t("Drupal sites")); } /** * Test to see if the site settings.php exists * * @param url * The url of the site to check * @return * If the file exists, return TRUE, else return FALSE. */ function _provision_drupal_site_exists($url) { return file_exists("sites/$url/settings.php"); } function _provision_drupal_site_installed($url) { if (_provision_drupal_site_exists($url)) { if ($data = provision_load_site_data($url)) { return isset($data['installed']) ? $data['installed'] : FALSE; } } return FALSE; } /** * Implentation of hook_provision_backup() */ function provision_drupal_provision_backup($url, $data) { // Adds the site directory into the backup file provision_log("backup", "Adding sites directory to $data[backup_file].gz"); $result = provision_shell_exec("cd %s; tar -rf %s * ", "sites/$url", $data['backup_file']); if (!$result) { $log = join('\n', _drush_shell_exec_output_set()); provision_log("error", t("Could not back up sites directory for drupal, error: @log", array('@log' => $log))); provision_set_error(PROVISION_FRAMEWORK_ERROR); } } /** * The default template to use while generating config files. * * @return * The default template for the config file */ function _provision_drupal_default_template() { return file_get_contents(dirname(__FILE__) .'/provision_drupal_settings.tpl.php'); } /** * Generate a settings file for the site. * * @param url * The url of the site being invoked. * @param data * A reference to the associated array containing the data for the site. This needs to be a reference, * because the modules might provide additional information about the site. */ function _provision_drupal_create_settings_file($url, &$data) { provision_log('notice', t("Generate settings.php file")); if (provision_path("exists", "sites/$url/settings.php")) { provision_path("chmod", "sites/$url/settings.php", 0640, t('Changed permissions of settings.php to @confirm'), t('Could not change permissions of settings.php to @confirm')); } $data['extra_config'] = "# Extra configuration from modules:\n"; foreach (module_implements('provision_drupal_config') as $module) { $data['extra_config'] .= "# -- $module settings --\n"; $data['extra_config'] .= module_invoke($module, 'provision_drupal_config', $url, $data) . "\n"; } $fp = fopen("sites/$url/settings.php", "w"); $text = variable_get('provision_drupal_settings_template', _provision_drupal_default_template()); fwrite($fp, " 0755, "sites/$url/files" => 02770, "sites/$url/files/tmp" => 02770, "sites/$url/files/images" => 02770, "sites/$url/files/pictures" => 02770, "sites/$url/themes" => 0755, "sites/$url/modules" => 0755, ); $grps = array( "sites/$url/files", "sites/$url/files/tmp", "sites/$url/files/images", "sites/$url/files/pictures", ); foreach ($paths as $path => $perm) { if (!is_dir($path)) { provision_path("mkdir", $path, TRUE, t("Created @path"), t("Could not create @path"), PROVISION_PERM_ERROR | PROVISION_INSTALL_ERROR ); } if (in_array($path, $grps)) { provision_path("chown", $path, PROVISION_SCRIPT_USER, t("Changed ownership of @path"), t("Could not change ownership @path"), PROVISION_PERM_ERROR | PROVISION_INSTALL_ERROR ); provision_path("chgrp", $path, PROVISION_WEB_GROUP, t("Changed group ownership of @path"), t("Could not change group ownership @path")); } provision_path("chmod", $path, $perm, t("Changed permissions of @path to @confirm"), t("Could not change permissions @path to @confirm"), PROVISION_PERM_ERROR | PROVISION_INSTALL_ERROR ); } } /** * Switch the active database to the newly created database * * This function tricks Drupal into thinking that it's running on an uninstalled site, * so that it can cleanly install the database schema. It also handles switching back to the * main provisioning site. */ function _provision_drupal_switch_active_site($url = NULL) { static $backups; if ($url) { /* Pretend to be the site being installed */ // Fake the necessary HTTP headers that Drupal needs: $_SERVER['HTTP_HOST'] = $url; $_SERVER['PHP_SELF'] = '/index.php'; $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF']; // Coder generates a warning. But this is correct. $_SERVER['REMOTE_ADDR'] = NULL; $_SERVER['REQUEST_METHOD'] = NULL; /** * This code is sourced from bootstrap.inc. I am trying to avoid patching core, but it might * be smarter to make a small patch to allow core to re-initialize itself more easily */ // Export the following settings.php variables to the global namespace global $base_url, $base_path, $base_root; global $cookie_domain, $conf, $profile, $profile, $db_prefix; // This is just for backup, to be able to restore to the old DRUSH system. $backups = compact("active_db", "base_url", "base_path", "db_prefix", "cookie_domain", "conf", "profile"); include_once $_SERVER['DOCUMENT_ROOT'] .'sites/'. $url .'/settings.php'; // Create base URL $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; $base_url = $base_root .= '://'. preg_replace('/[^a-z0-9-:._]/i', '', $_SERVER['HTTP_HOST']); if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) { $base_path = "/$dir"; $base_url .= $base_path; $base_path .= '/'; } else { $base_path = '/'; } provision_set_active_db($db_url); } else { /** * Restore everything to the way it was before we switched sites. */ // Fake the necessary HTTP headers that Drupal needs: $drupal_base_url = parse_url(DRUSH_URI); $_SERVER['HTTP_HOST'] = $drupal_base_url['host']; $_SERVER['PHP_SELF'] = $drupal_base_url['path'] .'/index.php'; $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF']; // Coder generates a warning, but this is correct. $_SERVER['REMOTE_ADDR'] = NULL; $_SERVER['REQUEST_METHOD'] = NULL; global $base_url, $base_path, $base_root; // Export the following settings.php variables to the global namespace global $db_prefix, $cookie_domain, $conf, $profile; // This is just for backup, to be able to restore to the old DRUSH system. extract($backups, EXTR_OVERWRITE); provision_set_active_db(); } } function provision_drupal_provision_post_restore($url, $data) { _provision_drupal_create_settings_file($url, $data); } function provision_drupal_provision_restore($url, $data) { $old = PROVISION_SITES_PATH ."/$url.restore"; $new = PROVISION_SITES_PATH ."/$url"; provision_path("switch_paths", $old, $new , t('Swapping out the @path and @confirm directories was successful.'), t('Swapping the @path and @confirm directories has failed.'), PROVISION_PERM_ERROR); // make sure it has the latest site data available provision_save_site_data($url, $data); } // Luckily this is reversable =) function provision_drupal_provision_restore_rollback($url, $data) { provision_drupal_provision_restore($url, $data); } function provision_drupal_provision_pre_install($url, &$data) { // This is the actual drupal provisioning requirements. _provision_drupal_create_directories($url, $data['profile']); // Requires at least the database settings to complete. _provision_drupal_create_settings_file($url, $data); } function provision_drupal_provision_install($url, &$data) { $cmd = sprintf("php %s/install.php %s %s %s %s", dirname(__FILE__), escapeshellarg($url), escapeshellarg($data['profile']), escapeshellarg($data['language']), escapeshellarg($data['client_email'])); if (provision_exec($cmd, $data)) { $data['installed'] = TRUE; // Create symlinks for site aliases (if any) _provision_drupal_maintain_aliases($url, $data); } } function provision_drupal_provision_post_install($url, &$data) { if ($url) { provision_path("chmod", "./sites/$url/settings.php", 0440, t("Secured settings.php with safe permissions")); _provision_drupal_rebuild_caches($url, $data); } } /** * implementation of provision_verify */ function provision_drupal_provision_verify($url, &$data) { if (!$url) { provision_path("writable", "sites", TRUE, t("Drupal sites directory is writable by the provisioning script"), t("Drupal sites directory is not writable by the provisioning script"), PROVISION_PERM_ERROR); $data['modules'] = _provision_drupal_get_cvs_versions(module_rebuild_cache()); // Find theme engines $data['engines'] = drupal_system_listing('\.engine$', 'themes/engines'); $data['profiles'] = _provision_find_profiles(); $data['themes'] = system_theme_data(); $data['platform'] = array('short_name' => 'drupal', 'version' => VERSION); provision_log('notice', t("This platform is running @short_name @version", array('@short_name' => 'drupal', '@version' => VERSION))); $sites = provision_drupal_find_sites(); $data['sites'] = array_keys($sites); // return list of hosted sites. used to determine whether or not to import. } else { // This is the actual drupal provisioning requirements. _provision_drupal_create_directories($url, $data['profile']); _provision_drupal_maintain_aliases($url, $data); // Requires at least the database settings to complete. _provision_drupal_create_settings_file($url, $data); } } /** * Implementation of hook_provision_post_verify */ function provision_drupal_provision_post_verify($url, &$data) { if ($url) { _provision_drupal_rebuild_caches($url, $data); } } /** * Runs an external script to reload all the various drupal caches */ function _provision_drupal_rebuild_caches($url, &$data) { if ($url) { $cmd = sprintf("php %s/provision_drupal_clear.php %s", dirname(__FILE__), escapeshellarg($url)); provision_exec($cmd, $data); } } /** * Find available profiles on this platform. */ function _provision_find_profiles() { include_once('includes/install.inc'); if (!$dir = opendir("./profiles")) { provision_log('error', t("Cannot find profiles directory")); return FALSE; } while (FALSE !== ($name = readdir($dir))) { $languages = array(); $file = "./profiles/$name/$name.profile"; if ($name == '..' || $name == '.' || !file_exists($file)) { continue; } $profile = new stdClass(); $profile->name = $name; $profile->filename = $file; require_once($profile->filename); $func = $profile->name . "_profile_details"; if (function_exists($func)) { $profile->info = $func(); } $languages['en'] = 1; // Find languages available $files = array_keys(file_scan_directory('./profiles/' . $name . '/translations', '\.po$', array('.', '..', 'CVS'), 0, FALSE, 'filepath')); if (is_array($files)) { foreach ($files as $file) { if (preg_match('!(/|\.)([^\./]+)\.po$!', $file, $langcode)) { $languages[$langcode[2]] = 1; // use the language name as an index to weed out duplicates } } } $profile->info['languages'] = array_keys($languages); $return[$name] = $profile; provision_log('notice', t('found install profile %name', array('%name' => $name))); } return $return; } /** * Remove any directories for the site in sites * This can't be rolled back. so won't even try. */ function provision_drupal_provision_delete($url, $data) { if ($old_data = provision_load_site_data($url)) { if (sizeof($old_aliases = $old_data['aliases'])) { _provision_drupal_delete_aliases($old_aliases); } } return _provision_recursive_delete("sites/$url"); } function provision_drupal_find_sites() { if ($dir = opendir("./sites")) { while (FALSE !== ($subdir = readdir($dir))) { $file = "./sites/$subdir/settings.php"; if (file_exists("$file") && ($subdir != 'default') && !is_link("./sites/$subdir")) { $sites[$subdir] = $file; } } closedir($dir); } else { provision_log("error", t("Cannot find sites directory")); $sites = FALSE; } return $sites; } function _provision_drupal_get_cvs_versions($files) { foreach ($files as $modulename => $file) { $project = array(); $project['filename'] = $file->filename; $project['name'] = $file->name; $file->info['description'] = str_replace("\n", "", $file->info['description']); if (empty($project['project'])) { $project['project'] = cvs_deploy_get_project_name($project); } _cvs_deploy_version_alter($file->info['version'], $project); $name = ($project['project']) ? $project['project'] : $modulename; $files[$name] = $file; } return $files; } function provision_drupal_provision_import($url, &$data) { $cmd = sprintf("php %s/provision_drupal_import.php %s", dirname(__FILE__), escapeshellarg($url)); if (provision_exec($cmd, $data)) { $data['installed'] = TRUE; provision_log("notice", "Returning information for $url"); } else { $data['installed'] = FALSE; } return TRUE; } /** * Retrieve drupal variable from database * * This is different to the normal variable_get in that it doesn't * do any caching whatsoever. */ function _provision_drupal_variable_get($name, $default) { $value = db_result(db_query("SELECT value FROM {variable} WHERE name='%s'", $name)); return ($value) ? unserialize($value) : $default; } /** * Create and remove symlinks for each of the possible domain aliases of an existing site */ function _provision_drupal_maintain_aliases($url, $data) { if ($url) { $old_aliases = array(); if ($old_data = provision_load_site_data($url)) { if (is_array($old_data['aliases'])) { // Flip the array to make it easier to remove duplicates $old_aliases = array_flip($old_data['aliases']); } } if (is_array($data['aliases'])) { foreach($data['aliases'] as $alias) { if (isset($old_aliases[$alias])) { // We have already created an alias for this site. unset($old_aliases[$alias]); } provision_path("symlink", $url, PROVISION_DOCROOT_PATH . "/sites/" . $alias, t("Created symlink for alias @alias", array("@alias" => $alias)), t("Could not create symlink for alias @alias", array("@alias" => $alias))); } // Delete existing aliases that are no longer present _provision_drupal_delete_aliases(array_keys($old_aliases)); } } } /** * Delete a list of aliases */ function _provision_drupal_delete_aliases($aliases) { foreach ($aliases as $alias) { provision_path("unlink", PROVISION_DOCROOT_PATH . "/sites/" . $alias, TRUE, t("Removed symlink for alias @alias", array("@alias" => $alias)), t("Could not remove symlink for alias @alias", array("@alias" => $alias))); } }