provision_drupal.module 14.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?php
/**
 * @file
 * Drupal specific functions for the provisioning framework.
 *
 * This module is responsible for the creation and maintenance of the drupal settings.php file, the sites directory structure
 * and all the install api code. 
 */

/**
 * @ingroup provisionui
 * @{
 */
 
/**
 * Implementation of hook_provision_service()
 */
function provision_drupal_provision_service() {
19
  return array('drupal' => t("Drupal sites"));
20 21 22 23
}

/**
 * Implentation of hook_provision_configure()
24
 * @TODO: PROPER profile support
25
function provision_drupal_provision_configure() {
26

27 28 29 30
  $profiles = file_scan_directory('./profiles', '\.profile$', array('.', '..', 'CVS'), 0, TRUE, 'name', 0);
  // Don't need to choose profile if only one available.
  if (sizeof($profiles) == 1) {
    $profile = array_pop($profiles);
31
    $form['default_profile'] = array('#type' => 'value', '#value' => $profile->name);
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
  }
  elseif (sizeof($profiles) > 1) {
    foreach ($profiles as $profile) {
      if ($_POST['profile'] == $profile->name) {
        $options[$profile->name] = $profile->name;
      }
    }
    $form['provision_default_profile'] = array(
      '#type' => 'radios',
      '#title' => t('Default install profile'),
      '#description' => t('New sites will be created with the following install profile'),
      '#options' => $options,
      '#default_value' => variable_get('provision_default_profile', 'default'),
    );
    
  }

49

50 51 52
  
  return $form;
}
53 54 55 56 57 58 59
//*/
/**
 * Implementation of hook_provision_templates
 */
function provision_drupal_provision_templates() {
  // settings template
}
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

/**
 * @} End "ingroup provisionui"
*/

/**
 * 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");
}

Adrian Rossouw's avatar
Adrian Rossouw committed
77 78 79 80 81 82 83 84
function _provision_drupal_site_installed($url) {
  if (_provision_drupal_site_exists($url)) {
    if ($data = provision_load_site_data($url)) {
      return isset($data['site_installed']) ? $data['site_installed'] : FALSE;
    }
  }
  return false;
}
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

/**
 * 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) {
    provision_log("error", "Could not back up sites directory for drupal");
    provision_set_error(PROVISION_FRAMEWORK_ERROR);
  }
}

100 101 102 103 104 105 106
/**
 * The default template to use while generating config files.
 *
 * @return
 *   The default template for the config file
 */
function _provision_drupal_default_template() {
107
  return file_get_contents(drupal_get_path('module', 'provision_drupal') . '/provision_drupal_settings.tpl.php');
108 109 110 111 112 113 114 115 116 117 118 119
}

/**
 * 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) {
120
  provision_log('notice', t("Generate settings.php file"));
121
  if (provision_path("exists", "sites/$url/settings.php")) {
122 123 124
    provision_path("chmod", "sites/$url/settings.php", 0750,
      t('Changed permissions of settings.php to @confirm'),
      t('Could not change permissions of settings.php to @confirm'));
125 126
  }

127
  $fp = fopen("sites/$url/settings.php", "w");
128 129
  $text = variable_get('provision_drupal_settings_template', _provision_drupal_default_template());
  fwrite($fp, "<?php\n" . provision_render_config($text, $data));
130
  fclose($fp);
131

132
  # Change the permissions of the file
133 134 135 136 137 138
  provision_path("chmod", "sites/$url/settings.php", 0550,
    t('Changed permissions of settings.php to @confirm'),
    t('Could not change permissions of settings.php to @confirm'));
  provision_path("chgrp", "sites/$url/settings.php", PROVISION_WEB_GROUP,
    t('Change group ownership of settings.php to @confirm'),
    t('Could not change group ownership of settings.php to @confirm'));
139 140 141 142 143 144 145
}

/**
 * Create the directories needed to host a drupal site
 * 
 * Also maintains permissions on existing directories.
 */
146
function _provision_drupal_create_directories($url, $profile = null) {  
147
  $paths = array(
148
    "sites/$url"                 => 0750,
149 150 151 152 153
    "sites/$url/files"           => 02750,
    "sites/$url/files/tmp"       => 02770,
    "sites/$url/files/images"    => 02770,
    "sites/$url/files/pictures"  => 02770,
    "sites/$url/themes"          => 02750,
Adrian Rossouw's avatar
Adrian Rossouw committed
154
    "sites/$url/modules"         => 02750,  
155 156 157 158
  );

  foreach ($paths as $path => $perm) {
    if (!is_dir($path)) {
159 160 161
      provision_path("mkdir", $path, true, 
        t("Created <code>@path</code>"),
        t("Could not create <code>@path</code>"),
162
        PROVISION_PERM_ERROR | PROVISION_INSTALL_ERROR );
163
    }
164
    provision_path("chown", $path, PROVISION_SCRIPT_USER, 
165 166 167
      t("Changed ownership of <code>@path</code>"),
      t("Could not change ownership <code>@path</code>"),
      PROVISION_PERM_ERROR | PROVISION_INSTALL_ERROR );
168
    provision_path("chgrp", $path, PROVISION_WEB_GROUP,
169 170 171 172 173 174 175
      t("Changed group ownership of <code>@path</code>"),
      t("Could not change group ownership <code>@path</code>"));

    provision_path("chmod", $path, $perm, 
      t("Changed permissions of <code>@path</code> to @confirm"),
      t("Could not change permissions <code>@path</code> to @confirm"),
      PROVISION_PERM_ERROR | PROVISION_INSTALL_ERROR );
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
  }
}

/**
 * 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:
    $drupal_base_url = parse_url($url);
    $_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'];
    $_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;
206
    global  $cookie_domain, $conf, $profile, $profile, $db_prefix;
207 208
 
    # This is just for backup, to be able to restore to the old DRUSH system.
209
    $backups = compact("active_db", "base_url", "base_path", "db_prefix", "cookie_domain", "conf", "profile");
210 211 212 213 214 215 216 217 218 219 220 221 222 223
    
    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 = '/';
     }
224
     provision_set_active_db($db_url);
225

226
  }
227 228 229 230 231 232 233 234 235 236 237 238 239 240
  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'];
    $_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
241
    global $db_prefix, $cookie_domain, $conf, $profile;
242 243 244

    # This is just for backup, to be able to restore to the old DRUSH system.
    extract($backups, EXTR_OVERWRITE);
245
    provision_set_active_db();
246 247 248
  }
}

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
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);
}


270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
/**
 * Force drupal to load the modules it expects to find on an uninstalled site
 */
function _provision_drupal_force_load_modules($url = null) {
  static $backup_list;
  if ($url) {
    $backup_list = module_list();
    require_once './modules/system/system.install';
    require_once './includes/file.inc';
    require_once './includes/install.inc';
    // Load module basics (needed for hook invokes).
     include_once './includes/module.inc';
     $module_list['system']['filename'] = 'modules/system/system.module';
     $module_list['filter']['filename'] = 'modules/filter/filter.module';
     module_list(TRUE, FALSE, FALSE, $module_list);
     drupal_load('module', 'system');
     drupal_load('module', 'filter');
     #should i load all the other modules? i don't know .=\
  }
  else {
    module_list(TRUE, FALSE, FALSE, $backup_list);
  }
}

/**
 * Install the drupal schema and install profile
 */
297
function _provision_drupal_install_schema($profile) {
298
  $GLOBALS['profile'] = $profile;
Adrian Rossouw's avatar
Adrian Rossouw committed
299
  provision_log("install", t("Installing Drupal schema"));
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
  // Load the profile.
  require_once "./profiles/$profile/$profile.profile";

  $requirements = drupal_check_profile($profile);
  $severity = drupal_requirements_severity($requirements);

  // If there are issues, report them.
  if ($severity == REQUIREMENT_ERROR) {
    foreach ($requirements as $requirement) {
      if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
        drupal_set_message($requirement['description'] .' ('. st('Currently using !item !version', array('!item' => $requirement['title'], '!version' => $requirement['value'])) .')', 'error');
      }
    }

    return false;
  }

  // Verify existence of all required modules.
  $modules = drupal_verify_profile($profile, null);

  if (!$modules) {
321
    provision_set_error(PROVISION_FRAMEWORK_ERROR);
322 323
    return false;
  }
324

Adrian Rossouw's avatar
Adrian Rossouw committed
325
  
326 327 328 329 330 331 332 333 334 335 336
  // Perform actual installation defined in the profile.
  drupal_install_profile($profile, $modules);

  // Show profile finalization info.
  $function = $profile .'_profile_final';
  if (function_exists($function)) {
    // More steps required
    $profile_message = $function();
  } 
}

337 338 339
/**
 * implementation of provision_verify
 */
340
function provision_drupal_provision_verify($url, &$data) {
341
  provision_path("writable", "sites", true, t("Drupal sites directory is writable by the provisioning script"),
342
    t("Drupal sites directory is not writable by the provisioning script"), PROVISION_PERM_ERROR);
343
  $exists = _provision_create_dir(PROVISION_DRUSHRC_PATH, t('Drush configuration path'), 0700);
Adrian Rossouw's avatar
Adrian Rossouw committed
344 345 346
  $data['modules'] = _provision_drupal_get_cvs_versions(module_rebuild_cache());
  // Find theme engines
  $data['engines'] = drupal_system_listing('\.engine$', 'themes/engines');
347
  $data['profiles'] = _provision_find_profiles();
Adrian Rossouw's avatar
Adrian Rossouw committed
348 349
  $data['themes'] = system_theme_data();
  
350 351 352
  $sites = provision_drupal_find_sites(); 

  $data['sites'] = array_keys($sites); // return list of hosted sites. used to determine whether or not to import. 
Adrian Rossouw's avatar
Adrian Rossouw committed
353 354
}

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
/**
 * Find available profiles on this platform.
 */
function _provision_find_profiles() {
  include_once('includes/install.inc');

  $profiles = file_scan_directory('./profiles', '\.profile$', array('.', '..', 'CVS', '.svn'), 0, TRUE, 'name', 0);
  
  foreach ($profiles as $key => $profile) {
    require_once($profile->filename);
    $func = $profile->name . "_profile_details";
    if (function_exists($func)) {
      $profile->info =  $func();
    }
    $return[$key] = $profile;
  }
  return $return;

}

375
/**
376 377
 * Remove any directories for the site in sites
 * This can't be rolled back. so won't even try.
378 379
 */
function provision_drupal_provision_delete($url, $data) {
380
  return _provision_recursive_delete("sites/$url");
381
}
382 383

function provision_drupal_find_sites() {
384 385 386 387 388 389 390
  $sitephp = file_scan_directory('./sites', 'settings\.php$', array('.', '..', 'CVS', '.svn'), 0, TRUE, 'filename', 0);
  foreach ($sitephp as $file => $info) {
    $path = explode("/", $file);
    array_pop($path);
    $sites[array_pop($path)] = $file;
  }
  return $sites;
391 392
}

Adrian Rossouw's avatar
Adrian Rossouw committed
393 394
function _provision_drupal_get_cvs_versions($files) {
  foreach ($files as $modulename => $file) {
395 396 397 398 399 400 401 402
      $project = array();
      $project['filename'] = $file->filename;
      $project['name'] = $file->name;
      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;
Adrian Rossouw's avatar
Adrian Rossouw committed
403
      $files[$name] = $file; 
404
  }
405

Adrian Rossouw's avatar
Adrian Rossouw committed
406
  return $files;
407
}
408 409 410 411 412 413 414 415 416 417 418 419

function provision_drupal_provision_import($url, &$data) {
  $sites = provision_drupal_find_sites();
  foreach ($sites as $site => $file) {
    if ($site != 'default') {
      $info = _provision_drupal_import_site($site);
      if ($info['site_installed']) {
        provision_log("notice", "Returning information for $site");
        $data['sites'][$site] = $info;
      }
    }
  }
420 421 422
  //Just make sure our ini settings and the like aren't wiped out.
  include(conf_path() . "/settings.php");

423 424 425 426 427 428
}

function _provision_drupal_import_site($url) {
  if (!($data = provision_get_site_data($url))) {
    $data = array(); // initialize site data to empty array
  }
429

430
  include("sites/$url/settings.php");
431 432 433 434 435 436
  if ($parts = @parse_url($db_url)) {
    $data['site_db_type'] = $parts['scheme'];
    $data['site_db_user'] = $parts['user'];
    $data['site_db_host'] = $parts['host'];
    $data['site_db_passwd'] = $parts['pass'];
    $data['site_db_name'] = substr($parts['path'], 1);
437 438 439 440 441 442 443 444 445

    /**
     * We need to be running AS the site to be able to see what profile it is installed with.
     *
     * This information is no longer stored in settings.php
     */
    _provision_drupal_switch_active_site($url);
    $data['site_profile'] = variable_get('install_profile', 'default');
    _provision_drupal_switch_active_site();
446
    $data['site_installed'] = TRUE;
447
  }
448
  provision_save_site_data($url, $data);
449

450 451
  return $data;
}
452