/[drupal]/drupal/includes/install.inc
ViewVC logotype

Contents of /drupal/includes/install.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.115 - (show annotations) (download) (as text)
Tue Oct 13 21:16:43 2009 UTC (6 weeks, 1 day ago) by dries
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10, HEAD
Changes since 1.114: +2 -2 lines
File MIME type: text/x-php
- Patch #601570 by effulgentsia: hook_exit() and other cleanup needs to happen for AJAX requests too.
1 <?php
2 // $Id: install.inc,v 1.114 2009/10/13 05:26:57 webchick Exp $
3
4 /**
5 * Indicates that a module has not been installed yet.
6 */
7 define('SCHEMA_UNINSTALLED', -1);
8
9 /**
10 * Indicates that a module has been installed.
11 */
12 define('SCHEMA_INSTALLED', 0);
13
14 /**
15 * Requirement severity -- Informational message only.
16 */
17 define('REQUIREMENT_INFO', -1);
18
19 /**
20 * Requirement severity -- Requirement successfully met.
21 */
22 define('REQUIREMENT_OK', 0);
23
24 /**
25 * Requirement severity -- Warning condition; proceed but flag warning.
26 */
27 define('REQUIREMENT_WARNING', 1);
28
29 /**
30 * Requirement severity -- Error condition; abort installation.
31 */
32 define('REQUIREMENT_ERROR', 2);
33
34 /**
35 * File permission check -- File exists.
36 */
37 define('FILE_EXIST', 1);
38
39 /**
40 * File permission check -- File is readable.
41 */
42 define('FILE_READABLE', 2);
43
44 /**
45 * File permission check -- File is writable.
46 */
47 define('FILE_WRITABLE', 4);
48
49 /**
50 * File permission check -- File is executable.
51 */
52 define('FILE_EXECUTABLE', 8);
53
54 /**
55 * File permission check -- File does not exist.
56 */
57 define('FILE_NOT_EXIST', 16);
58
59 /**
60 * File permission check -- File is not readable.
61 */
62 define('FILE_NOT_READABLE', 32);
63
64 /**
65 * File permission check -- File is not writable.
66 */
67 define('FILE_NOT_WRITABLE', 64);
68
69 /**
70 * File permission check -- File is not executable.
71 */
72 define('FILE_NOT_EXECUTABLE', 128);
73
74 /**
75 * Initialize the update system by loading all installed module's .install files.
76 */
77 function drupal_load_updates() {
78 foreach (drupal_get_installed_schema_version(NULL, FALSE, TRUE) as $module => $schema_version) {
79 if ($schema_version > -1) {
80 module_load_install($module);
81 }
82 }
83 }
84
85 /**
86 * Returns an array of available schema versions for a module.
87 *
88 * @param $module
89 * A module name.
90 * @return
91 * If the module has updates, an array of available updates sorted by version.
92 * Otherwise, FALSE.
93 */
94 function drupal_get_schema_versions($module) {
95 $updates = &drupal_static(__FUNCTION__, NULL);
96 if (!isset($updates[$module])) {
97 $updates = array();
98 // Prepare regular expression to match all possible defined hook_update_N().
99 $regexp = '/^(?P<module>.+)_update_(?P<version>\d+)$/';
100 $functions = get_defined_functions();
101 // Narrow this down to functions ending with an integer, since all
102 // hook_update_N() functions end this way, and there are other
103 // possible functions which match '_update_'. We use preg_grep() here
104 // instead of foreaching through all defined functions, since the loop
105 // through all PHP functions can take significant page execution time
106 // and this function is called on every administrative page via
107 // system_requirements().
108 foreach (preg_grep('/_\d+$/', $functions['user']) as $function) {
109 // If this function is a module update function, add it to the list of
110 // module updates.
111 if (preg_match($regexp, $function, $matches)) {
112 $updates[$matches['module']][] = $matches['version'];
113 }
114 }
115 // Ensure that updates are applied in numerical order.
116 foreach ($updates as &$module_updates) {
117 sort($module_updates, SORT_NUMERIC);
118 }
119 }
120 return isset($updates[$module]) ? $updates[$module] : FALSE;
121 }
122
123 /**
124 * Returns the currently installed schema version for a module.
125 *
126 * @param $module
127 * A module name.
128 * @param $reset
129 * Set to TRUE after modifying the system table.
130 * @param $array
131 * Set to TRUE if you want to get information about all modules in the
132 * system.
133 * @return
134 * The currently installed schema version.
135 */
136 function drupal_get_installed_schema_version($module, $reset = FALSE, $array = FALSE) {
137 static $versions = array();
138
139 if ($reset) {
140 $versions = array();
141 }
142
143 if (!$versions) {
144 $versions = array();
145 $result = db_query("SELECT name, schema_version FROM {system} WHERE type = :type", array(':type' => 'module'));
146 foreach ($result as $row) {
147 $versions[$row->name] = $row->schema_version;
148 }
149 }
150
151 return $array ? $versions : $versions[$module];
152 }
153
154 /**
155 * Update the installed version information for a module.
156 *
157 * @param $module
158 * A module name.
159 * @param $version
160 * The new schema version.
161 */
162 function drupal_set_installed_schema_version($module, $version) {
163 db_update('system')
164 ->fields(array('schema_version' => $version))
165 ->condition('name', $module)
166 ->execute();
167 }
168
169 /**
170 * Loads the install profile definition, extracting its defined name.
171 *
172 * @return
173 * The name defined in the profile's _profile_details() hook.
174 */
175 function drupal_install_profile_name() {
176 global $install_state;
177
178 if (isset($install_state['profile_info']['name'])) {
179 $name = $install_state['profile_info']['name'];
180 }
181 else {
182 $name = 'Drupal';
183 }
184
185 return $name;
186 }
187
188 /**
189 * Auto detect the base_url with PHP predefined variables.
190 *
191 * @param $file
192 * The name of the file calling this function so we can strip it out of
193 * the URI when generating the base_url.
194 * @return
195 * The auto-detected $base_url that should be configured in settings.php
196 */
197 function drupal_detect_baseurl($file = 'install.php') {
198 $proto = $_SERVER['HTTPS'] ? 'https://' : 'http://';
199 $host = $_SERVER['SERVER_NAME'];
200 $port = ($_SERVER['SERVER_PORT'] == 80 ? '' : ':' . $_SERVER['SERVER_PORT']);
201 $uri = preg_replace("/\?.*/", '', $_SERVER['REQUEST_URI']);
202 $dir = str_replace("/$file", '', $uri);
203
204 return "$proto$host$port$dir";
205 }
206
207 /**
208 * Detect all supported databases that are compiled into PHP.
209 *
210 * @return
211 * An array of database types compiled into PHP.
212 */
213 function drupal_detect_database_types() {
214 $databases = array();
215
216 // We define a driver as a directory in /includes/database that in turn
217 // contains a database.inc file. That allows us to drop in additional drivers
218 // without modifying the installer.
219 // Because we have no registry yet, we need to also include the install.inc
220 // file for the driver explicitly.
221 require_once DRUPAL_ROOT . '/includes/database/database.inc';
222 foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', array('recurse' => FALSE)) as $file) {
223 include_once "{$file->uri}/install.inc";
224 include_once "{$file->uri}/database.inc";
225 $drivers[$file->filename] = $file->uri;
226 }
227
228 foreach ($drivers as $driver => $file) {
229 $class = 'DatabaseTasks_' . $driver;
230 $installer = new $class();
231 if ($installer->installable()) {
232 $databases[$driver] = $installer->name();
233 }
234 }
235
236 // Usability: unconditionally put the MySQL driver on top.
237 if (isset($databases['mysql'])) {
238 $mysql_database = $databases['mysql'];
239 unset($databases['mysql']);
240 $databases = array('mysql' => $mysql_database) + $databases;
241 }
242
243 return $databases;
244 }
245
246 /**
247 * Database installer structure.
248 *
249 * Defines basic Drupal requirements for databases.
250 */
251 abstract class DatabaseTasks {
252
253 /**
254 * Structure that describes each task to run.
255 *
256 * @var array
257 *
258 * Each value of the tasks array is an associative array defining the function
259 * to call (optional) and any arguments to be passed to the function.
260 */
261 protected $tasks = array(
262 array(
263 'arguments' => array(
264 'CREATE TABLE drupal_install_test (id int NULL)',
265 'Drupal can use CREATE TABLE database commands.',
266 'Failed to <strong>CREATE</strong> a test table on your %name database server with the command %query. %name reports the following message: %error.<p>Are you sure the configured username has the necessary %name permissions to create tables in the database?</p>',
267 TRUE,
268 ),
269 ),
270 array(
271 'arguments' => array(
272 'INSERT INTO drupal_install_test (id) VALUES (1)',
273 'Drupal can use INSERT database commands.',
274 'Failed to <strong>INSERT</strong> a value into a test table on your %name database server. We tried inserting a value with the command %query and %name reported the following error: %error.',
275 ),
276 ),
277 array(
278 'arguments' => array(
279 'UPDATE drupal_install_test SET id = 2',
280 'Drupal can use UPDATE database commands.',
281 'Failed to <strong>UPDATE</strong> a value in a test table on your %name database server. We tried updating a value with the command %query and %name reported the following error: %error.',
282 ),
283 ),
284 array(
285 'arguments' => array(
286 'DELETE FROM drupal_install_test',
287 'Drupal can use DELETE database commands.',
288 'Failed to <strong>DELETE</strong> a value from a test table on your %name database server. We tried deleting a value with the command %query and %name reported the following error: %error.',
289 ),
290 ),
291 array(
292 'arguments' => array(
293 'DROP TABLE drupal_install_test',
294 'Drupal can use DROP TABLE database commands.',
295 'Failed to <strong>DROP</strong> a test table from your %name database server. We tried dropping a table with the command %query and %name reported the following error %error.',
296 ),
297 ),
298 );
299 /**
300 * Results from tasks.
301 *
302 * @var array
303 */
304 protected $results = array();
305
306 /**
307 * Ensure the PDO driver is supported by the version of PHP in use.
308 */
309 protected function hasPdoDriver() {
310 return in_array($this->pdoDriver, PDO::getAvailableDrivers());
311 }
312
313 /**
314 * Assert test as failed.
315 */
316 protected function fail($message) {
317 $this->results[$message] = FALSE;
318 }
319
320 /**
321 * Assert test as a pass.
322 */
323 protected function pass($message) {
324 $this->results[$message] = TRUE;
325 }
326
327 /**
328 * Check whether Drupal is installable on the database.
329 */
330 public function installable() {
331 return $this->hasPdoDriver() && empty($this->error);
332 }
333
334 abstract public function name();
335
336 /**
337 * Run database tasks and tests to see if Drupal can run on the database.
338 */
339 public function runTasks() {
340 // We need to establish a connection before we can run tests.
341 if ($this->connect()) {
342 foreach ($this->tasks as $task) {
343 if (!isset($task['function'])) {
344 $task['function'] = 'runTestQuery';
345 }
346 if (method_exists($this, $task['function'])) {
347 // Returning false is fatal. No other tasks can run.
348 if (FALSE === call_user_func_array(array($this, $task['function']), $task['arguments'])) {
349 break;
350 }
351 }
352 else {
353 throw new DatabaseTaskException(st("Failed to run all tasks against the database server. The task %task wasn't found.", array('%task' => $task['function'])));
354 }
355 }
356 }
357 // Check for failed results and compile message
358 $message = '';
359 foreach ($this->results as $result => $success) {
360 if (!$success) {
361 $message .= '<p class="error">' . $result . '</p>';
362 }
363 }
364 if (!empty($message)) {
365 $message = '<p>In order for Drupal to work, and to continue with the installation process, you must resolve all issues reported below. For more help with configuring your database server, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what any of this means you should probably contact your hosting provider.</p>' . $message;
366 throw new DatabaseTaskException($message);
367 }
368 }
369
370 /**
371 * Check if we can connect to the database.
372 */
373 protected function connect() {
374 try {
375 // This doesn't actually test the connection.
376 db_set_active();
377 // Now actually do a check.
378 Database::getConnection();
379 $this->pass('Drupal can CONNECT to the database ok.');
380 }
381 catch (Exception $e) {
382 $this->fail(st('Failed to connect to your %name database server. %name reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => $e->getMessage(), '%name' => $this->name())));
383 return FALSE;
384 }
385 return TRUE;
386 }
387
388 /**
389 * Run SQL tests to ensure the database can execute commands with the current user.
390 */
391 protected function runTestQuery($query, $pass, $fail, $fatal = FALSE) {
392 try {
393 db_query($query);
394 $this->pass(st($pass));
395 }
396 catch (Exception $e) {
397 $this->fail(st($fail, array('%query' => $query, '%error' => $e->getMessage(), '%name' => $this->name())));
398 return !$fatal;
399 }
400 }
401 }
402 /**
403 * @class Exception class used to throw error if the DatabaseInstaller fails.
404 */
405 class DatabaseTaskException extends Exception {
406 }
407
408 /**
409 * Replace values in settings.php with values in the submitted array.
410 *
411 * @param $settings
412 * An array of settings that need to be updated.
413 */
414 function drupal_rewrite_settings($settings = array(), $prefix = '') {
415 $default_settings = 'sites/default/default.settings.php';
416 drupal_static_reset('conf_path');
417 $settings_file = conf_path(FALSE) . '/' . $prefix . 'settings.php';
418
419 // Build list of setting names and insert the values into the global namespace.
420 $keys = array();
421 foreach ($settings as $setting => $data) {
422 $GLOBALS[$setting] = $data['value'];
423 $keys[] = $setting;
424 }
425
426 $buffer = NULL;
427 $first = TRUE;
428 if ($fp = fopen(DRUPAL_ROOT . '/' . $default_settings, 'r')) {
429 // Step line by line through settings.php.
430 while (!feof($fp)) {
431 $line = fgets($fp);
432 if ($first && substr($line, 0, 5) != '<?php') {
433 $buffer = "<?php\n\n";
434 }
435 $first = FALSE;
436 // Check for constants.
437 if (substr($line, 0, 7) == 'define(') {
438 preg_match('/define\(\s*[\'"]([A-Z_-]+)[\'"]\s*,(.*?)\);/', $line, $variable);
439 if (in_array($variable[1], $keys)) {
440 $setting = $settings[$variable[1]];
441 $buffer .= str_replace($variable[2], " '" . $setting['value'] . "'", $line);
442 unset($settings[$variable[1]]);
443 unset($settings[$variable[2]]);
444 }
445 else {
446 $buffer .= $line;
447 }
448 }
449 // Check for variables.
450 elseif (substr($line, 0, 1) == '$') {
451 preg_match('/\$([^ ]*) /', $line, $variable);
452 if (in_array($variable[1], $keys)) {
453 // Write new value to settings.php in the following format:
454 // $'setting' = 'value'; // 'comment'
455 $setting = $settings[$variable[1]];
456 $buffer .= '$' . $variable[1] . " = " . var_export($setting['value'], TRUE) . ";" . (!empty($setting['comment']) ? ' // ' . $setting['comment'] . "\n" : "\n");
457 unset($settings[$variable[1]]);
458 }
459 else {
460 $buffer .= $line;
461 }
462 }
463 else {
464 $buffer .= $line;
465 }
466 }
467 fclose($fp);
468
469 // Add required settings that were missing from settings.php.
470 foreach ($settings as $setting => $data) {
471 if ($data['required']) {
472 $buffer .= "\$$setting = " . var_export($data['value'], TRUE) . ";\n";
473 }
474 }
475
476 $fp = fopen(DRUPAL_ROOT . '/' . $settings_file, 'w');
477 if ($fp && fwrite($fp, $buffer) === FALSE) {
478 throw new Exception(st('Failed to modify %settings, please verify the file permissions.', array('%settings' => $settings_file)));
479 }
480 }
481 else {
482 throw new Exception(st('Failed to open %settings, please verify the file permissions.', array('%settings' => $default_settings)));
483 }
484 }
485
486 /**
487 * Get list of all .install files.
488 *
489 * @param $module_list
490 * An array of modules to search for their .install files.
491 */
492 function drupal_get_install_files($module_list = array()) {
493 $installs = array();
494 foreach ($module_list as $module) {
495 $installs = array_merge($installs, drupal_system_listing('/' . $module . '.install$/', 'modules'));
496 }
497 return $installs;
498 }
499
500
501 /**
502 * Verify an install profile for installation.
503 *
504 * @param $install_state
505 * An array of information about the current installation state.
506 * @return
507 * The list of modules to install.
508 */
509 function drupal_verify_profile($install_state) {
510 $profile = $install_state['parameters']['profile'];
511 $locale = $install_state['parameters']['locale'];
512
513 include_once DRUPAL_ROOT . '/includes/file.inc';
514 include_once DRUPAL_ROOT . '/includes/common.inc';
515
516 $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile";
517
518 if (!isset($profile) || !file_exists($profile_file)) {
519 throw new Exception(install_no_profile_error());
520 }
521 $info = $install_state['profile_info'];
522
523 // Get a list of modules that exist in Drupal's assorted subdirectories.
524 $present_modules = array();
525 foreach (drupal_system_listing('/\.module$/', 'modules', 'name', 0) as $present_module) {
526 $present_modules[] = $present_module->name;
527 }
528
529 // The install profile is also a module, which needs to be installed after all the other dependencies
530 // have been installed.
531 $present_modules[] = drupal_get_profile();
532
533 // Verify that all of the profile's required modules are present.
534 $missing_modules = array_diff($info['dependencies'], $present_modules);
535
536 $requirements = array();
537
538 if (count($missing_modules)) {
539 $modules = array();
540 foreach ($missing_modules as $module) {
541 $modules[] = '<span class="admin-missing">' . drupal_ucfirst($module) . '</span>';
542 }
543 $requirements['required_modules'] = array(
544 'title' => st('Required modules'),
545 'value' => st('Required modules not found.'),
546 'severity' => REQUIREMENT_ERROR,
547 'description' => st('The following modules are required but were not found. Please move them into the appropriate modules subdirectory, such as <em>sites/all/modules</em>. Missing modules: !modules', array('!modules' => implode(', ', $modules))),
548 );
549 }
550 return $requirements;
551 }
552
553 /**
554 * Calls the install function for a given list of modules.
555 *
556 * @param $module_list
557 * The modules to install.
558 * @param $disable_modules_installed_hook
559 * Normally just testing wants to set this to TRUE.
560 */
561 function drupal_install_modules($module_list = array(), $disable_modules_installed_hook = FALSE) {
562 $files = system_rebuild_module_data();
563 $module_list = array_flip(array_values($module_list));
564 do {
565 $moved = FALSE;
566 foreach ($module_list as $module => $weight) {
567 $file = $files[$module];
568 if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
569 foreach ($file->info['dependencies'] as $dependency) {
570 if (isset($module_list[$dependency]) && $module_list[$module] < $module_list[$dependency] +1) {
571 $module_list[$module] = $module_list[$dependency] +1;
572 $moved = TRUE;
573 }
574 }
575 }
576 }
577 } while ($moved);
578 asort($module_list);
579 $module_list = array_keys($module_list);
580 module_enable($module_list, $disable_modules_installed_hook);
581 }
582
583 /**
584 * Callback to install an individual install profile module.
585 *
586 * Used during installation to install modules one at a time and then
587 * enable them, or to install a number of modules at one time
588 * from admin/config/modules.
589 *
590 * @param $module
591 * The machine name of the module to install.
592 * @return
593 * TRUE if the module got installed.
594 */
595 function _drupal_install_module($module) {
596 if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
597 drupal_load('module', $module);
598 drupal_install_schema($module);
599 // Now allow the module to perform install tasks.
600 module_invoke($module, 'install');
601 $versions = drupal_get_schema_versions($module);
602 drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
603 return TRUE;
604 }
605 }
606
607 /**
608 * Manually include all files for the active database.
609 *
610 * Because we have no registry yet, we need to manually include the
611 * necessary database include files.
612 */
613 function drupal_install_initialize_database() {
614 static $included = FALSE;
615
616 if (!$included) {
617 $connection_info = Database::getConnectionInfo();
618 $driver = $connection_info['default']['driver'];
619 require_once DRUPAL_ROOT . '/includes/database/query.inc';
620 require_once DRUPAL_ROOT . '/includes/database/select.inc';
621 require_once DRUPAL_ROOT . '/includes/database/schema.inc';
622 foreach (glob(DRUPAL_ROOT . '/includes/database/' . $driver . '/*.inc') as $include_file) {
623 require_once $include_file;
624 }
625 $included = TRUE;
626 }
627 }
628
629 /**
630 * Callback to install the system module.
631 *
632 * Separated from the installation of other modules so core system
633 * functions can be made available while other modules are installed.
634 */
635 function drupal_install_system() {
636 $system_path = dirname(drupal_get_filename('module', 'system', NULL));
637 require_once DRUPAL_ROOT . '/' . $system_path . '/system.install';
638 drupal_install_initialize_database();
639 module_invoke('system', 'install');
640
641 $system_versions = drupal_get_schema_versions('system');
642 $system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED;
643 db_insert('system')
644 ->fields(array('filename', 'name', 'type', 'owner', 'status', 'schema_version', 'bootstrap'))
645 ->values(array(
646 'filename' => $system_path . '/system.module',
647 'name' => 'system',
648 'type' => 'module',
649 'owner' => '',
650 'status' => 1,
651 'schema_version' => $system_version,
652 'bootstrap' => 0,
653 ))
654 ->execute();
655 // Now that we've installed things properly, bootstrap the full Drupal environment
656 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
657 system_rebuild_module_data();
658 }
659
660 /**
661 * Calls the uninstall function and updates the system table for a given module.
662 *
663 * @param $module_list
664 * The modules to uninstall.
665 */
666 function drupal_uninstall_modules($module_list = array()) {
667 foreach ($module_list as $module) {
668 // First, retrieve all the module's menu paths from db.
669 drupal_load('module', $module);
670 $paths = module_invoke($module, 'menu');
671
672 // Uninstall the module.
673 module_load_install($module);
674 module_invoke($module, 'uninstall');
675 drupal_uninstall_schema($module);
676
677 watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO);
678
679 // Now remove the menu links for all paths declared by this module.
680 if (!empty($paths)) {
681 $paths = array_keys($paths);
682 // Clean out the names of load functions.
683 foreach ($paths as $index => $path) {
684 $parts = explode('/', $path, MENU_MAX_PARTS);
685 foreach ($parts as $k => $part) {
686 if (preg_match('/^%[a-z_]*$/', $part)) {
687 $parts[$k] = '%';
688 }
689 }
690 $paths[$index] = implode('/', $parts);
691 }
692
693 $result = db_select('menu_links')
694 ->fields('menu_links')
695 ->condition('router_path', $paths, 'IN')
696 ->condition('external', 0)
697 ->orderBy('depth')
698 ->execute();
699 // Remove all such items. Starting from those with the greatest depth will
700 // minimize the amount of re-parenting done by menu_link_delete().
701 foreach ($result as $item) {
702 _menu_delete_item($item, TRUE);
703 }
704 }
705
706 drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
707 }
708
709 if (!empty($module_list)) {
710 // Call hook_module_uninstall to let other modules act
711 module_invoke_all('modules_uninstalled', $module_list);
712 }
713 }
714
715 /**
716 * Verify the state of the specified file.
717 *
718 * @param $file
719 * The file to check for.
720 * @param $mask
721 * An optional bitmask created from various FILE_* constants.
722 * @param $type
723 * The type of file. Can be file (default), dir, or link.
724 * @return
725 * TRUE on success or FALSE on failure. A message is set for the latter.
726 */
727 function drupal_verify_install_file($file, $mask = NULL, $type = 'file') {
728 $return = TRUE;
729 // Check for files that shouldn't be there.
730 if (isset($mask) && ($mask & FILE_NOT_EXIST) && file_exists($file)) {
731 return FALSE;
732 }
733 // Verify that the file is the type of file it is supposed to be.
734 if (isset($type) && file_exists($file)) {
735 $check = 'is_' . $type;
736 if (!function_exists($check) || !$check($file)) {
737 $return = FALSE;
738 }
739 }
740
741 // Verify file permissions.
742 if (isset($mask)) {
743 $masks = array(FILE_EXIST, FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
744 foreach ($masks as $current_mask) {
745 if ($mask & $current_mask) {
746 switch ($current_mask) {
747 case FILE_EXIST:
748 if (!file_exists($file)) {
749 if ($type == 'dir') {
750 drupal_install_mkdir($file, $mask);
751 }
752 if (!file_exists($file)) {
753 $return = FALSE;
754 }
755 }
756 break;
757 case FILE_READABLE:
758 if (!is_readable($file) && !drupal_install_fix_file($file, $mask)) {
759 $return = FALSE;
760 }
761 break;
762 case FILE_WRITABLE:
763 if (!is_writable($file) && !drupal_install_fix_file($file, $mask)) {
764 $return = FALSE;
765 }
766 break;
767 case FILE_EXECUTABLE:
768 if (!is_executable($file) && !drupal_install_fix_file($file, $mask)) {
769 $return = FALSE;
770 }
771 break;
772 case FILE_NOT_READABLE:
773 if (is_readable($file) && !drupal_install_fix_file($file, $mask)) {
774 $return = FALSE;
775 }
776 break;
777 case FILE_NOT_WRITABLE:
778 if (is_writable($file) && !drupal_install_fix_file($file, $mask)) {
779 $return = FALSE;
780 }
781 break;
782 case FILE_NOT_EXECUTABLE:
783 if (is_executable($file) && !drupal_install_fix_file($file, $mask)) {
784 $return = FALSE;
785 }
786 break;
787 }
788 }
789 }
790 }
791 return $return;
792 }
793
794 /**
795 * Create a directory with specified permissions.
796 *
797 * @param $file
798 * The name of the directory to create;
799 * @param $mask
800 * The permissions of the directory to create.
801 * @param $message
802 * (optional) Whether to output messages. Defaults to TRUE.
803 * @return
804 * TRUE/FALSE whether or not the directory was successfully created.
805 */
806 function drupal_install_mkdir($file, $mask, $message = TRUE) {
807 $mod = 0;
808 $masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
809 foreach ($masks as $m) {
810 if ($mask & $m) {
811 switch ($m) {
812 case FILE_READABLE:
813 $mod += 444;
814 break;
815 case FILE_WRITABLE:
816 $mod += 222;
817 break;
818 case FILE_EXECUTABLE:
819 $mod += 111;
820 break;
821 }
822 }
823 }
824
825 if (@drupal_mkdir($file, intval("0$mod", 8))) {
826 return TRUE;
827 }
828 else {
829 return FALSE;
830 }
831 }
832
833 /**
834 * Attempt to fix file permissions.
835 *
836 * The general approach here is that, because we do not know the security
837 * setup of the webserver, we apply our permission changes to all three
838 * digits of the file permission (i.e. user, group and all).
839 *
840 * To ensure that the values behave as expected (and numbers don't carry
841 * from one digit to the next) we do the calculation on the octal value
842 * using bitwise operations. This lets us remove, for example, 0222 from
843 * 0700 and get the correct value of 0500.
844 *
845 * @param $file
846 * The name of the file with permissions to fix.
847 * @param $mask
848 * The desired permissions for the file.
849 * @param $message
850 * (optional) Whether to output messages. Defaults to TRUE.
851 * @return
852 * TRUE/FALSE whether or not we were able to fix the file's permissions.
853 */
854 function drupal_install_fix_file($file, $mask, $message = TRUE) {
855 // If $file does not exist, fileperms() issues a PHP warning.
856 if (!file_exists($file)) {
857 return FALSE;
858 }
859
860 $mod = fileperms($file) & 0777;
861 $masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
862
863 // FILE_READABLE, FILE_WRITABLE, and FILE_EXECUTABLE permission strings
864 // can theoretically be 0400, 0200, and 0100 respectively, but to be safe
865 // we set all three access types in case the administrator intends to
866 // change the owner of settings.php after installation.
867 foreach ($masks as $m) {
868 if ($mask & $m) {
869 switch ($m) {
870 case FILE_READABLE:
871 if (!is_readable($file)) {
872 $mod |= 0444;
873 }
874 break;
875 case FILE_WRITABLE:
876 if (!is_writable($file)) {
877 $mod |= 0222;
878 }
879 break;
880 case FILE_EXECUTABLE:
881 if (!is_executable($file)) {
882 $mod |= 0111;
883 }
884 break;
885 case FILE_NOT_READABLE:
886 if (is_readable($file)) {
887 $mod &= ~0444;
888 }
889 break;
890 case FILE_NOT_WRITABLE:
891 if (is_writable($file)) {
892 $mod &= ~0222;
893 }
894 break;
895 case FILE_NOT_EXECUTABLE:
896 if (is_executable($file)) {
897 $mod &= ~0111;
898 }
899 break;
900 }
901 }
902 }
903
904 // chmod() will work if the web server is running as owner of the file.
905 // If PHP safe_mode is enabled the currently executing script must also
906 // have the same owner.
907 if (@chmod($file, $mod)) {
908 return TRUE;
909 }
910 else {
911 return FALSE;
912 }
913 }
914
915
916 /**
917 * Send the user to a different installer page.
918 *
919 * This issues an on-site HTTP redirect. Messages (and errors) are erased.
920 *
921 * @param $path
922 * An installer path.
923 */
924 function install_goto($path) {
925 global $base_url;
926 header('Location: ' . $base_url . '/' . $path);
927 header('Cache-Control: no-cache'); // Not a permanent redirect.
928 drupal_exit();
929 }
930
931 /**
932 * Functional equivalent of t(), used when some systems are not available.
933 *
934 * Used during the install process, when database, theme, and localization
935 * system is possibly not yet available.
936 *
937 * @see t()
938 */
939 function st($string, $args = array()) {
940 static $locale_strings = NULL;
941 global $install_state;
942
943 if (!isset($locale_strings)) {
944 $locale_strings = array();
945 if (isset($install_state['parameters']['profile']) && isset($install_state['parameters']['locale'])) {
946 $filename = 'profiles/' . $install_state['parameters']['profile'] . '/translations/' . $install_state['parameters']['locale'] . '.po';
947 if (file_exists(DRUPAL_ROOT . '/' . $filename)) {
948 require_once DRUPAL_ROOT . '/includes/locale.inc';
949 $file = (object) array('uri' => $filename);
950 _locale_import_read_po('mem-store', $file);
951 $locale_strings = _locale_import_one_string('mem-report');
952 }
953 }
954 }
955
956 require_once DRUPAL_ROOT . '/includes/theme.inc';
957 // Transform arguments before inserting them
958 foreach ($args as $key => $value) {
959 switch ($key[0]) {
960 // Escaped only
961 case '@':
962 $args[$key] = check_plain($value);
963 break;
964 // Escaped and placeholder
965 case '%':
966 default:
967 $args[$key] = '<em>' . check_plain($value) . '</em>';
968 break;
969 // Pass-through
970 case '!':
971 }
972 }
973 return strtr((!empty($locale_strings[$string]) ? $locale_strings[$string] : $string), $args);
974 }
975
976 /**
977 * Check an install profile's requirements.
978 *
979 * @param $profile
980 * Name of install profile to check.
981 * @return
982 * Array of the install profile's requirements.
983 */
984 function drupal_check_profile($profile) {
985 include_once DRUPAL_ROOT . '/includes/file.inc';
986
987 $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile";
988
989 if (!isset($profile) || !file_exists($profile_file)) {
990 throw new Exception(install_no_profile_error());
991 }
992
993 $info = install_profile_info($profile);
994
995 // Get a list of all .install files.
996 $installs = drupal_get_install_files($info['dependencies']);
997
998 // Collect requirement testing results
999 $requirements = array();
1000 foreach ($installs as $install) {
1001 require_once DRUPAL_ROOT . '/' . $install->uri;
1002 $function = $install->name . '_requirements';
1003 if (function_exists($function)) {
1004 $requirements = array_merge($requirements, $function('install'));
1005 }
1006 }
1007 return $requirements;
1008 }
1009
1010 /**
1011 * Extract highest severity from requirements array.
1012 *
1013 * @param $requirements
1014 * An array of requirements, in the same format as is returned by
1015 * hook_requirements().
1016 * @return
1017 * The highest severity in the array.
1018 */
1019 function drupal_requirements_severity(&$requirements) {
1020 $severity = REQUIREMENT_OK;
1021 foreach ($requirements as $requirement) {
1022 if (isset($requirement['severity'])) {
1023 $severity = max($severity, $requirement['severity']);
1024 }
1025 }
1026 return $severity;
1027 }
1028
1029 /**
1030 * Check a module's requirements.
1031 *
1032 * @param $module
1033 * Machine name of module to check.
1034 * @return
1035 * TRUE/FALSE depending on the requirements are in place.
1036 */
1037 function drupal_check_module($module) {
1038 // Include install file
1039 $install = drupal_get_install_files(array($module));
1040 if (isset($install[$module])) {
1041 require_once DRUPAL_ROOT . '/' . $install[$module]->uri;
1042
1043 // Check requirements
1044 $requirements = module_invoke($module, 'requirements', 'install');
1045 if (is_array($requirements) && drupal_requirements_severity($requirements) == REQUIREMENT_ERROR) {
1046 // Print any error messages
1047 foreach ($requirements as $requirement) {
1048 if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
1049 $message = $requirement['description'];
1050 if (isset($requirement['value']) && $requirement['value']) {
1051 $message .= ' (' . t('Currently using !item !version', array('!item' => $requirement['title'], '!version' => $requirement['value'])) . ')';
1052 }
1053 drupal_set_message($message, 'error');
1054 }
1055 }
1056 return FALSE;
1057 }
1058 }
1059 return TRUE;
1060 }
1061
1062 /**
1063 * Retrieve info about an install profile from its .info file.
1064 *
1065 * Information stored in the profile.info file:
1066 * - name: The real name of the install profile for display purposes.
1067 * - description: A brief description of the profile.
1068 * - dependencies: An array of shortnames of other modules this install profile requires.
1069 *
1070 * Example of .info file:
1071 * @verbatim
1072 * name = Drupal (minimal)
1073 * description = Create a Drupal site with only required modules enabled.
1074 * dependencies[] = block
1075 * dependencies[] = dblog
1076 * @endverbatim
1077 *
1078 * @param profile
1079 * Name of profile.
1080 * @param locale
1081 * Name of locale used (if any).
1082 * @return
1083 * The info array.
1084 */
1085 function install_profile_info($profile, $locale = 'en') {
1086 $cache = &drupal_static(__FUNCTION__, array());
1087
1088 if (!isset($cache[$profile])) {
1089 // Set defaults for module info.
1090 $defaults = array(
1091 'dependencies' => array(),
1092 'description' => '',
1093 'version' => NULL,
1094 'php' => DRUPAL_MINIMUM_PHP,
1095 );
1096 $info = drupal_parse_info_file("profiles/$profile/$profile.info") + $defaults;
1097 $info['dependencies'] = array_unique(array_merge(
1098 drupal_required_modules(),
1099 $info['dependencies'],
1100 ($locale != 'en' && !empty($locale) ? array('locale') : array()))
1101 );
1102
1103 // drupal_required_modules() includes the current profile as a dependency.
1104 // Since a module can't depend on itself we remove that element of the array.
1105 array_shift($info['dependencies']);
1106
1107 $cache[$profile] = $info;
1108 }
1109 return $cache[$profile];
1110 }
1111
1112 /**
1113 * Ensures the environment for a Drupal database on a predefined connection.
1114 *
1115 * This will run tasks that check that Drupal can perform all of the functions
1116 * on a database, that Drupal needs. Tasks include simple checks like CREATE
1117 * TABLE to database specfic functions like stored procedures and client
1118 * encoding.
1119 */
1120 function db_run_tasks($driver) {
1121 $task_class = 'DatabaseTasks_' . $driver;
1122 $DatabaseTasks = new $task_class();
1123 $DatabaseTasks->runTasks();
1124 return true;
1125 }

  ViewVC Help
Powered by ViewVC 1.1.2