Commit 83f57f6e authored by Adrian Rossouw's avatar Adrian Rossouw Committed by adrian

In progress commit. Remove dependency on date module. Lots of help additions....

In progress commit. Remove dependency on date module. Lots of help additions. Added verify step so the back end (mostly) configures itself. Switched templating to use php instead of tokens (needed loops in templates). Turned options to use underscore instead of dash for separation (now consistent). Lots of validation and logging, including strict permission checking.
parent 6d99b240
...@@ -81,7 +81,7 @@ function provision_invoke($hook, $url, &$data, $rollback = false) { ...@@ -81,7 +81,7 @@ function provision_invoke($hook, $url, &$data, $rollback = false) {
* @param extra * @param extra
* An associative array containing additional data to be returned from the command. @see provision_stats_stats() * An associative array containing additional data to be returned from the command. @see provision_stats_stats()
*/ */
function provision_output($url, $data, $extra = null) { function provision_output($url = null, $data = array(), $extra = null) {
$return = $extra; $return = $extra;
$return['site'] = $data; $return['site'] = $data;
$return['error_status'] = provision_get_error(); // error code being returned $return['error_status'] = provision_get_error(); // error code being returned
...@@ -91,14 +91,15 @@ function provision_output($url, $data, $extra = null) { ...@@ -91,14 +91,15 @@ function provision_output($url, $data, $extra = null) {
print serialize($return); print serialize($return);
} }
else { else {
foreach (provision_get_log() as $entry) { if ($return) {
printf("%10s|%10s|%10%s", $entry['timestamp'], $entry['type'], $entry['severity'], $entry['message']); if ($output = theme("provision_" . $data['action_type'] . "_output", $url, $return)) {
} return $output;
if ($data) { } else {
/** TODO : return a cleanly formatted display of all the necessary information */ /** TODO : return a cleanly formatted display of all the necessary information */
print_r($return); print_r($return);
} }
} }
}
exit(provision_get_error()); exit(provision_get_error());
} }
...@@ -245,22 +246,22 @@ function provision_get_site_data($url) { ...@@ -245,22 +246,22 @@ function provision_get_site_data($url) {
global $args; global $args;
#TODO: Accept serialized string via unix pipe. #TODO: Accept serialized string via unix pipe.
foreach ($args['options'] as $key => $value) { foreach ($args['options'] as $key => $value) {
if (preg_match("/^site-/", $key)) { if (preg_match("/^site_/", $key)) {
$site_data[$key] = $value; $site_data[$key] = $value;
} }
} }
$site_data['site-url'] = $url; $site_data['site_url'] = $url;
$site_data['site-action-type'] = $args['commands'][1]; $site_data['site_action_type'] = $args['commands'][1];
$docroot = variable_get('provision_root', ''); $docroot = drush_get_option("r", $_SERVER['PWD']);
$site_data['site-document-root'] = ($docroot) ? $docroot . '/webroot' : $_SERVER['DOCUMENT_ROOT']; $site_data['site_document_root'] = ($docroot) ? $docroot . '/webroot' : $_SERVER['DOCUMENT_ROOT'];
$site_data['site-temporary-url'] = str_replace('.', '-', $url) . '.' . variable_get('provision_tempurl_base', $_SERVER['HTTP_HOST']); $site_data['site_profile'] = ($site_data['site_profile']) ? $site_data['site_profile'] : variable_get('provision_default_profile', 'default');
$site_data['site-profile'] = ($site_data['site-profile']) ? $site_data['site-profile'] : variable_get('provision_default_profile', 'default'); $site_data['site_ip'] = variable_get('provision_apache_server_ip', '127.0.0.1');
$site_data['site-ip'] = variable_get('provision_apache_server_ip', '127.0.0.1'); $site_data['site_port'] = variable_get('provision_apache_server_ip', 80);
$site_data['site-port'] = variable_get('provision_apache_server_ip', 80);
if ($old_data = provision_load_site_data($url)) { if ($old_data = provision_load_site_data($url)) {
# Merge previously saved data with the new data. This way, old parameters overwrite new ones. # Merge previously saved data with the new data. This way, old parameters overwrite new ones.
$site_data = array_merge(provision_load_site_data($url), $site_data); $site_data = array_merge(provision_load_site_data($url), $site_data);
} }
return $site_data; return $site_data;
} }
...@@ -303,9 +304,9 @@ function provision_save_site_data($url, $data) { ...@@ -303,9 +304,9 @@ function provision_save_site_data($url, $data) {
} }
else { else {
fwrite($fp, "<?php\n"); fwrite($fp, "<?php\n");
$action = array('id' => $data['action-id'], $action = array('id' => $data['action_id'],
'timestamp' => mktime(), 'timestamp' => mktime(),
'action' => $data['site-action-type'], 'action' => $data['site_action_type'],
'success' => $data['success']); 'success' => $data['success']);
$line = "\n\n\$actions[] = " . var_export($action, TRUE) . ';'; $line = "\n\n\$actions[] = " . var_export($action, TRUE) . ';';
fwrite($fp, $line); fwrite($fp, $line);
...@@ -342,16 +343,16 @@ function provision_save_site_data($url, $data) { ...@@ -342,16 +343,16 @@ function provision_save_site_data($url, $data) {
* A keyed array listing the substitution tokens. Elements should be * A keyed array listing the substitution tokens. Elements should be
* in the form of: $list[$type][$token] = $description * in the form of: $list[$type][$token] = $description
*/ */
function provision_token_values($type, $object = null) { #function provision_token_values($type, $object = null) {
global $args; # global $args;
switch ($type) { # switch ($type) {
case 'site': # case 'site':
$values = array_merge(provision_get_site_data($args['commands'][2]), $object) ; # $values = array_merge(provision_get_site_data($args['commands'][2]), $object) ;
break; # break;
} # }
return (array) $values; # return (array) $values;
#}
}
/** /**
* Implementation of hook_token_list(). * Implementation of hook_token_list().
* *
...@@ -365,18 +366,30 @@ function provision_token_values($type, $object = null) { ...@@ -365,18 +366,30 @@ function provision_token_values($type, $object = null) {
function provision_token_list($type = 'all') { function provision_token_list($type = 'all') {
if ($type == 'site') { if ($type == 'site') {
/** TODO: Complete the token list to allow the front end to more easily edit the settings. */ /** TODO: Complete the token list to allow the front end to more easily edit the settings. */
$tokens['site']['site-url'] = t("The domain name used to access the site. This is defaulted to the value used on the command line."); $tokens['site']['site_url'] = t("The domain name used to access the site. This is defaulted to the value used on the command line.");
$tokens['site']['site-db-type'] = t("The type of database server used"); $tokens['site']['site_db_type'] = t("The type of database server used");
$tokens['site']['site-db-username'] = t("Username to access database for site"); $tokens['site']['site_db_username'] = t("Username to access database for site");
$tokens['site']['site-db-password'] = t("Password to access database for site"); $tokens['site']['site_db_password'] = t("Password to access database for site");
$tokens['site']['site-db-name'] = t("Database name for the site"); $tokens['site']['site_db_name'] = t("Database name for the site");
$tokens['site']['site-profile'] = t("Install profile of site"); $tokens['site']['site_profile'] = t("Install profile of site");
$tokens['site']['site-action-type'] = t("What type of action has been used. Only used in conjuction with hosting front end"); $tokens['site']['site_action_type'] = t("What type of action has been used. Only used in conjuction with hosting front end");
$tokens['site']['site-action-type'] = t("What type of action has been used. Only used in conjuction with hosting front end"); $tokens['site']['site_action_type'] = t("What type of action has been used. Only used in conjuction with hosting front end");
} }
return $tokens; return $tokens;
} }
/**
* Generate the text for a config file using php
*/
function provision_render_config($template, $variables) {
extract($variables, EXTR_SKIP); // Extract the variables to a local namespace
ob_start(); // Start output buffering
eval("?>" . $template); // Generate content
$contents = ob_get_contents(); // Get the contents of the buffer
ob_end_clean(); // End buffering and discard
return $contents; // Return the contents
}
/** /**
* @} End of "defgroup provisiontokens". * @} End of "defgroup provisiontokens".
*/ */
...@@ -388,11 +401,6 @@ function provision_token_list($type = 'all') { ...@@ -388,11 +401,6 @@ function provision_token_list($type = 'all') {
*/ */
function provision_confirm_drush() { function provision_confirm_drush() {
#confirm that code is running through the command line.
if (php_isapi_name() != 'cli') {
drupal_set_message(t("This function should only be called via the command line Drush utility."));
return false;
}
return true; return true;
} }
...@@ -405,28 +413,32 @@ function _provision_backup_path() { ...@@ -405,28 +413,32 @@ function _provision_backup_path() {
return variable_get('provision_backup_path', implode("/" , $parts) . '/backups'); return variable_get('provision_backup_path', implode("/" , $parts) . '/backups');
} }
/** /**
* Get the drushrc path of the Provision installation * Get the base configuration path for the system
*/ */
function _provision_drushrc_path() { function _provision_config_path() {
$parts = explode("/", $_SERVER['DOCUMENT_ROOT']); $path = ($_SERVER['PWD']) ? $_SERVER['PWD'] : $_SERVER['DOCUMENT_ROOT'];
$parts = explode("/", $path);
array_pop($parts); array_pop($parts);
return variable_get('provision_drushrc_path', implode("/" , $parts) . '/platform.d'); return variable_get('provision_apache_config_path', implode("/" , $parts) . '/config') ;
} }
/** /**
* Get the vhost path of the Provision installation * Get the vhost path of the Provision installation
*/ */
function _provision_vhost_path() { function _provision_vhost_path() {
$parts = explode("/", $_SERVER['DOCUMENT_ROOT']); return _provision_config_path() . '/vhost.d';
array_pop($parts);
return variable_get('provision_vhost_path', implode("/" , $parts) . '/vhost.d');
} }
function _provision_path_can_be_created($path) { /**
* Get the drushrc path of the Provision installation
*/
function _provision_drushrc_path() {
return _provision_config_path() . '/drush';
} }
/** /**
* Wrapper around drush_shell_exec to provide sprintf functionality with some more safety. * Wrapper around drush_shell_exec to provide sprintf functionality with some more safety.
*/ */
...@@ -456,11 +468,13 @@ function provision_set_active_db($new_db_url = null) { ...@@ -456,11 +468,13 @@ function provision_set_active_db($new_db_url = null) {
#initialize static #initialize static
if (!$old_db_url) { if (!$old_db_url) {
$old_db_url = $db_url; $old_db_url = $db_url;
$db_url = array();
$db_url['default'] = $old_db_url;
} }
if ($new_db_url) { if ($new_db_url) {
$db_url = $new_db_url; $db_url['new'] = $new_db_url;
db_set_active(); db_set_active('new');
} }
else { else {
$db_url = $old_db_url; $db_url = $old_db_url;
...@@ -486,3 +500,57 @@ function provision_get_group_name() { ...@@ -486,3 +500,57 @@ function provision_get_group_name() {
$info = posix_getgrgid(posix_getgid()); $info = posix_getgrgid(posix_getgid());
return variable_get('provision_group', $info['name']); return variable_get('provision_group', $info['name']);
} }
/**
* Perform tests on directory.
*
* Perform test on path, and log error messages / codes on failure.
*/
function provision_check_path($path, $type, $test = true, $succeed_message = '', $fail_message = '', $error_codes = null) {
# The code style is a bit weird here, but it's a bit easier to read this way.
switch ($type) {
case "writeable" :
case "writable" : $value = is_writable($path);
break;
case "exists" : $value = file_exists($path);
break;
case "readable" : $value = is_readable($path);
break;
case "is_dir" : $value = is_dir($path);
break;
case "owner" : $value = fileowner($path);
break;
case "group" : $value = filegroup($path);
break;
case "mkdir" : $value = mkdir($path, 0770, true);
break;
case "chmod" : chmod($path, $test);
$value = sprintf("%o", fileperms($path));
break;
case "chown" : chown($path, $test);
$value = fileowner($path);
break;
case "chgrp" : chgrp($path, $test);
$value = filegroup($path);
break;
case "unlink" : $value = (file_exists($path)) ? unlink($path) : false;
break;
case "rmdir" : $value = (file_exists($path) && is_dir($path)) ? rmdir($path) : false;
break;
}
$status = ($value == $test) ? true : false;
if ($status) {
if ($succeed_message) {
provision_log("message", $succeed_message);
}
}
else {
if ($error_codes) {
provision_set_error($error_codes);
}
if ($fail_message) {
provision_log("error", $fail_message);
}
}
return $status;
}
\ No newline at end of file
...@@ -37,10 +37,50 @@ ...@@ -37,10 +37,50 @@
/** Include the provisioning API. */ /** Include the provisioning API. */
include_once('provision.inc'); include_once('provision.inc');
/**
* Implementation of hook_provision_service
*/
function provision_provision_service() { function provision_provision_service() {
return t("Basic configuration"); return t("Basic configuration");
} }
/**
* Implementation of hook_perm().
*/
function provision_perm() {
return array("administer provisioning");
}
/**
* @defgroup provisionui Configure provisioning framework.
* @{
*/
/**
* Implementation of hook_menu().
*/
function provision_menu($may_cache = true) {
if ($may_cache) {
$items[] = array(
'path' => 'admin/settings/provision',
'title' => t('Provisioning'),
'description' => t("Configure how new Drupal sites will be provisioned."),
'callback' => 'drupal_get_form',
'callback arguments' => array('provision_configure'),
'access' => user_access('administer provisioning'),
);
$items[] = array(
'path' => 'admin/help/provision/requirements',
'title' => t('Provisioning requirements'),
'description' => t("Information of how to configure the provisioning system."),
'callback' => 'provision_help_requirements',
'type' => MENU_CALLBACK
);
}
return $items;
}
/** /**
* Implementation of hook_help() * Implementation of hook_help()
*/ */
...@@ -60,7 +100,9 @@ function provision_help($section) { ...@@ -60,7 +100,9 @@ function provision_help($section) {
$output .= "<dl>"; $output .= "<dl>";
foreach ($commands as $command => $info) { foreach ($commands as $command => $info) {
if (preg_match('/^provision/', $command)) { if (preg_match('/^provision/', $command)) {
if (sizeof($info['arguments'])) {
$command .= ' ' . implode(' ', (array) key($info['arguments'])); $command .= ' ' . implode(' ', (array) key($info['arguments']));
}
if (sizeof($info['optional arguments'])) { if (sizeof($info['optional arguments'])) {
$command .= ' [' . implode('] [', (array) key($info['optional arguments'])) . ']'; $command .= ' [' . implode('] [', (array) key($info['optional arguments'])) . ']';
} }
...@@ -105,20 +147,23 @@ EOF; ...@@ -105,20 +147,23 @@ EOF;
username to "<code>@username</code>" and the group to "<code>@group</code>", username to "<code>@username</code>" and the group to "<code>@group</code>",
but you can change these in the <a href="@provision_link">provisioning section</a>.</p>', array("@username" => $username, "@group" => $group, "@provision_link" => url('admin/settings/provision'))) . '</li>'; but you can change these in the <a href="@provision_link">provisioning section</a>.</p>', array("@username" => $username, "@group" => $group, "@provision_link" => url('admin/settings/provision'))) . '</li>';
$output .= '<li>' . t('<strong>Write access to a directory to store backups.</strong> $output .= '<li>' . t('<p><strong>Write access to a directory to store backups.</strong>
The drush user needs to be able to maintain the backups repository to ensure that your site is backed up successfully. The drush user needs to be able to maintain the backups repository to ensure that your site is backed up successfully.
It is incredibly important that this path is not accessible via the web server, so that no undesirables can get their It is incredibly important that this path is not accessible via the web server, so that no undesirables can get their
hands on your database. The recommended path is directly above your platform path, but it can be anywhere.<br /> hands on your database. The recommended path is directly above your platform path, but it can be anywhere.</p>
Based on your server configuration we have determined that your path should be <code>@backup_path</code>, <p>Based on your server configuration we have determined that your path should be <code>@backup_path</code>,
but you can change it change them in the <a href="@provision_link">provisioning section</a><br /> but you can change this in the <a href="@provision_link">provisioning section</a>.</p>
To create and configure this directory correctly, please enter the following commands : <strong>To configure: </strong> Please enter the following commands :
<pre>@mkdir_cmd</pre>',$mkdir_cmd) . '</li>'; <pre>@mkdir_cmd</pre>',$mkdir_cmd) . '</li>';
$output .= "</ol>"; $output .= "</ol>";
return $output; return $output;
} }
} }
/**
* Page callback with in depth requirement documentation
*/
function provision_help_requirements() { function provision_help_requirements() {
$output .= t('<p>Unfortunately, due to the requirements of some of the functionality, significantly more access than is usually allowed on a shared hosting solution is required, $output .= t('<p>Unfortunately, due to the requirements of some of the functionality, significantly more access than is usually allowed on a shared hosting solution is required,
and as such, a virtual server or dedicated hosting system will be required to run this system.</p>'); and as such, a virtual server or dedicated hosting system will be required to run this system.</p>');
...@@ -128,7 +173,7 @@ function provision_help_requirements() { ...@@ -128,7 +173,7 @@ function provision_help_requirements() {
$name = module_invoke($module, 'provision_service'); $name = module_invoke($module, 'provision_service');
$help = module_invoke($module, 'help', 'admin/help/provision#requirements'); $help = module_invoke($module, 'help', 'admin/help/provision#requirements');
if ($name && $help) { if ($name && $help) {
$output .= '<h3>' . t($name) . '</h3>'; $output .= '<a href="requirements-$module"></a><h3>' . t($name) . '</h3>';
$output .= $help; $output .= $help;
} }
} }
...@@ -136,42 +181,6 @@ function provision_help_requirements() { ...@@ -136,42 +181,6 @@ function provision_help_requirements() {
return $output; return $output;
} }
/**
* Implementation of hook_perm().
*/
function provision_perm() {
return array("administer provisioning");
}
/**
* @defgroup provisionui Configure provisioning framework.
* @{
*/
/**
* Implementation of hook_menu().
*/
function provision_menu($may_cache = true) {
if ($may_cache) {
$items[] = array(
'path' => 'admin/settings/provision',
'title' => t('Provisioning'),
'description' => t("Configure how new Drupal sites will be provisioned."),
'callback' => 'drupal_get_form',
'callback arguments' => array('provision_configure'),
'access' => user_access('administer provisioning'),
);
$items[] = array(
'path' => 'admin/help/provision/requirements',
'title' => t('Provisioning requirements'),
'description' => t("Information of how to configure the provisioning system."),
'callback' => 'provision_help_requirements',
'type' => MENU_CALLBACK
);
}
return $items;
}
/** /**
* Menu callback. * Menu callback.
...@@ -197,15 +206,7 @@ function provision_configure() { ...@@ -197,15 +206,7 @@ function provision_configure() {
* Implementation of provision_configure * Implementation of provision_configure
*/ */
function provision_provision_configure() { function provision_provision_configure() {
$form['provision_tempurl_base'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#title' => t('Temporary URL base'),
'#description' => t('Each new domain that gets created gets created with a temporary url base that allows it to be immediately accessible.'),
'#default_value' => variable_get('provision_tempurl_base', $_SERVER['HTTP_HOST']),
'#size' => 40,
'#maxlength' => 255,
);
$form['provision_user'] = array( $form['provision_user'] = array(
'#type' => 'textfield', '#type' => 'textfield',
...@@ -224,7 +225,15 @@ function provision_provision_configure() { ...@@ -224,7 +225,15 @@ function provision_provision_configure() {
'#size' => 40, '#size' => 40,
'#maxlength' => 255, '#maxlength' => 255,
); );
$form['provision_backup_path'] = array(
'#type' => 'textfield',
'#required' => TRUE,
'#title' => t('Path to backup directory'),
'#description' => t('The file system location where backups will be stored.'),
'#default_value' => _provision_backup_path(),
'#size' => 40,
'#maxlength' => 255,
);
return $form; return $form;
} }
...@@ -241,6 +250,7 @@ function provision_provision_configure() { ...@@ -241,6 +250,7 @@ function provision_provision_configure() {
* Implementation of hook_drush_command(). * Implementation of hook_drush_command().
*/ */
function provision_drush_command() { function provision_drush_command() {
$items['provision install'] = array( $items['provision install'] = array(
'callback' => 'provision_install_cmd', 'callback' => 'provision_install_cmd',
'arguments' => array('domain.com' => t('The domain of the site to install.')), 'arguments' => array('domain.com' => t('The domain of the site to install.')),
...@@ -252,11 +262,13 @@ function provision_drush_command() { ...@@ -252,11 +262,13 @@ function provision_drush_command() {
'arguments' => array('domain.com' => t('The domain of the site to synch.')), 'arguments' => array('domain.com' => t('The domain of the site to synch.')),
'description' => t('Regenerate all the config files for a site.') 'description' => t('Regenerate all the config files for a site.')
); );
$items['provision import'] = array( $items['provision import'] = array(
'callback' => '_provision_import', 'callback' => '_provision_import',
'arguments' => array('domain.com' => t('The domain of the site to import.')), 'arguments' => array('domain.com' => t('The domain of the site to import.')),
'description' => t('Turn an already running site into a provisioned site.') 'description' => t('Turn an already running site into a provisioned site.')
); );
$items['provision backup'] = array( $items['provision backup'] = array(
'callback' => '_provision_backup', 'callback' => '_provision_backup',
'arguments' => array('domain.com' => t('The domain of the site to back up.')), 'arguments' => array('domain.com' => t('The domain of the site to back up.')),
...@@ -274,6 +286,12 @@ function provision_drush_command() { ...@@ -274,6 +286,12 @@ function provision_drush_command() {
'arguments' => array('domain.com' => t('The domain of the site to disable (only if disabled).')), 'arguments' => array('domain.com' => t('The domain of the site to disable (only if disabled).')),
'description' => 'Disable a site.' 'description' => 'Disable a site.'
); );
$items['provision verify'] = array(
'callback' => '_provision_verify',
# 'arguments' => array('domain.com' => t('The domain of the site to disable (only if disabled).')),
'description' => 'Verify that the provisioning framework is correctly installed.'
);
/* /*
// Not implemented yet. // Not implemented yet.
$items['provision delete'] = array( $items['provision delete'] = array(
...@@ -322,7 +340,7 @@ function provision_install_cmd($url) { ...@@ -322,7 +340,7 @@ function provision_install_cmd($url) {
print "Usage: drush.php provision install DOMAIN [OPTIONS]\n"; print "Usage: drush.php provision install DOMAIN [OPTIONS]\n";
print "Install a new site for the domain DOMAIN.\n"; print "Install a new site for the domain DOMAIN.\n";
print "Example: drush.php provision install mydomain.com --site-db-host localhost\n"; print "Example: drush.php provision install mydomain.com --site-db-host localhost\n";
provision_set_log("error", "Incorrect usage of the provisioning framework"); provision_log("error", "Incorrect usage of the provisioning framework");
drupal_set_error(PROVISION_FRAMEWORK_ERROR); drupal_set_error(PROVISION_FRAMEWORK_ERROR);
provision_output($url, $data); provision_output($url, $data);
} }
...@@ -389,17 +407,17 @@ function _provision_pre_install($url, &$data) { ...@@ -389,17 +407,17 @@ function _provision_pre_install($url, &$data) {
* Boolean denoting whether the provision_invoke rolled back changes made. * Boolean denoting whether the provision_invoke rolled back changes made.
*/ */
function _provision_install($url, &$data) { function _provision_install($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return;
$rolled_back = provision_invoke("install", $url, $data); $rolled_back = provision_invoke("install", $url, $data);
# if (!$rolled_back) { if (!$rolled_back) {
_provision_drupal_switch_active_site($url); # Change headers and db info, also backs up _provision_drupal_switch_active_site($url); # Change headers and db info, also backs up
_provision_drupal_force_load_modules($url); _provision_drupal_force_load_modules($url);
_provision_drupal_install_schema($data['site-profile']); _provision_drupal_install_schema($data['site_profile']);