~spreadubuntu/spreadubuntu/devel-drupal6

« back to all changes in this revision

Viewing changes to includes/module.inc

  • Committer: ruben
  • Date: 2009-06-08 09:38:49 UTC
  • Revision ID: ruben@captive-20090608093849-s1qtsyctv2vwp1x1
SpreadUbuntu moving to Drupal6. Based on ubuntu-drupal theme and adding our modules

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
// $Id: module.inc,v 1.115.2.1 2009/02/16 10:32:10 goba Exp $
 
3
 
 
4
/**
 
5
 * @file
 
6
 * API for loading and interacting with Drupal modules.
 
7
 */
 
8
 
 
9
/**
 
10
 * Load all the modules that have been enabled in the system table.
 
11
 */
 
12
function module_load_all() {
 
13
  foreach (module_list(TRUE, FALSE) as $module) {
 
14
    drupal_load('module', $module);
 
15
  }
 
16
}
 
17
 
 
18
/**
 
19
 * Call a function repeatedly with each module in turn as an argument.
 
20
 */
 
21
function module_iterate($function, $argument = '') {
 
22
  foreach (module_list() as $name) {
 
23
    $function($name, $argument);
 
24
  }
 
25
}
 
26
 
 
27
/**
 
28
 * Collect a list of all loaded modules. During the bootstrap, return only
 
29
 * vital modules. See bootstrap.inc
 
30
 *
 
31
 * @param $refresh
 
32
 *   Whether to force the module list to be regenerated (such as after the
 
33
 *   administrator has changed the system settings).
 
34
 * @param $bootstrap
 
35
 *   Whether to return the reduced set of modules loaded in "bootstrap mode"
 
36
 *   for cached pages. See bootstrap.inc.
 
37
 * @param $sort
 
38
 *   By default, modules are ordered by weight and filename, settings this option
 
39
 *   to TRUE, module list will be ordered by module name.
 
40
 * @param $fixed_list
 
41
 *   (Optional) Override the module list with the given modules. Stays until the
 
42
 *   next call with $refresh = TRUE.
 
43
 * @return
 
44
 *   An associative array whose keys and values are the names of all loaded
 
45
 *   modules.
 
46
 */
 
47
function module_list($refresh = FALSE, $bootstrap = TRUE, $sort = FALSE, $fixed_list = NULL) {
 
48
  static $list, $sorted_list;
 
49
 
 
50
  if ($refresh || $fixed_list) {
 
51
    $list = array();
 
52
    $sorted_list = NULL;
 
53
    if ($fixed_list) {
 
54
      foreach ($fixed_list as $name => $module) {
 
55
        drupal_get_filename('module', $name, $module['filename']);
 
56
        $list[$name] = $name;
 
57
      }
 
58
    }
 
59
    else {
 
60
      if ($bootstrap) {
 
61
        $result = db_query("SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 AND bootstrap = 1 ORDER BY weight ASC, filename ASC");
 
62
      }
 
63
      else {
 
64
        $result = db_query("SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC");
 
65
      }
 
66
      while ($module = db_fetch_object($result)) {
 
67
        if (file_exists($module->filename)) {
 
68
          // Determine the current throttle status and see if the module should be
 
69
          // loaded based on server load. We have to directly access the throttle
 
70
          // variables, since throttle.module may not be loaded yet.
 
71
          $throttle = ($module->throttle && variable_get('throttle_level', 0) > 0);
 
72
          if (!$throttle) {
 
73
            drupal_get_filename('module', $module->name, $module->filename);
 
74
            $list[$module->name] = $module->name;
 
75
          }
 
76
        }
 
77
      }
 
78
    }
 
79
  }
 
80
  if ($sort) {
 
81
    if (!isset($sorted_list)) {
 
82
      $sorted_list = $list;
 
83
      ksort($sorted_list);
 
84
    }
 
85
    return $sorted_list;
 
86
  }
 
87
  return $list;
 
88
}
 
89
 
 
90
/**
 
91
 * Rebuild the database cache of module files.
 
92
 *
 
93
 * @return
 
94
 *   The array of filesystem objects used to rebuild the cache.
 
95
 */
 
96
function module_rebuild_cache() {
 
97
  // Get current list of modules
 
98
  $files = drupal_system_listing('\.module$', 'modules', 'name', 0);
 
99
 
 
100
  // Extract current files from database.
 
101
  system_get_files_database($files, 'module');
 
102
 
 
103
  ksort($files);
 
104
 
 
105
  // Set defaults for module info
 
106
  $defaults = array(
 
107
    'dependencies' => array(),
 
108
    'dependents' => array(),
 
109
    'description' => '',
 
110
    'version' => NULL,
 
111
    'php' => DRUPAL_MINIMUM_PHP,
 
112
  );
 
113
 
 
114
  foreach ($files as $filename => $file) {
 
115
    // Look for the info file.
 
116
    $file->info = drupal_parse_info_file(dirname($file->filename) .'/'. $file->name .'.info');
 
117
 
 
118
    // Skip modules that don't provide info.
 
119
    if (empty($file->info)) {
 
120
      unset($files[$filename]);
 
121
      continue;
 
122
    }
 
123
    // Merge in defaults and save.
 
124
    $files[$filename]->info = $file->info + $defaults;
 
125
 
 
126
    // Invoke hook_system_info_alter() to give installed modules a chance to
 
127
    // modify the data in the .info files if necessary.
 
128
    drupal_alter('system_info', $files[$filename]->info, $files[$filename]);
 
129
 
 
130
    // Log the critical hooks implemented by this module.
 
131
    $bootstrap = 0;
 
132
    foreach (bootstrap_hooks() as $hook) {
 
133
      if (module_hook($file->name, $hook)) {
 
134
        $bootstrap = 1;
 
135
        break;
 
136
      }
 
137
    }
 
138
 
 
139
    // Update the contents of the system table:
 
140
    if (isset($file->status) || (isset($file->old_filename) && $file->old_filename != $file->filename)) {
 
141
      db_query("UPDATE {system} SET info = '%s', name = '%s', filename = '%s', bootstrap = %d WHERE filename = '%s'", serialize($files[$filename]->info), $file->name, $file->filename, $bootstrap, $file->old_filename);
 
142
    }
 
143
    else {
 
144
      // This is a new module.
 
145
      $files[$filename]->status = 0;
 
146
      $files[$filename]->throttle = 0;
 
147
      db_query("INSERT INTO {system} (name, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, serialize($files[$filename]->info), 'module', $file->filename, 0, 0, $bootstrap);
 
148
    }
 
149
  }
 
150
  $files = _module_build_dependencies($files);
 
151
  return $files;
 
152
}
 
153
 
 
154
/**
 
155
 * Find dependencies any level deep and fill in dependents information too.
 
156
 *
 
157
 * If module A depends on B which in turn depends on C then this function will
 
158
 * add C to the list of modules A depends on. This will be repeated until
 
159
 * module A has a list of all modules it depends on. If it depends on itself,
 
160
 * called a circular dependency, that's marked by adding a nonexistent module,
 
161
 * called -circular- to this list of modules. Because this does not exist,
 
162
 * it'll be impossible to switch module A on.
 
163
 *
 
164
 * Also we fill in a dependents array in $file->info. Using the names above,
 
165
 * the dependents array of module B lists A.
 
166
 *
 
167
 * @param $files
 
168
 *   The array of filesystem objects used to rebuild the cache.
 
169
 * @return
 
170
 *   The same array with dependencies and dependents added where applicable.
 
171
 */
 
172
function _module_build_dependencies($files) {
 
173
  do {
 
174
    $new_dependency = FALSE;
 
175
    foreach ($files as $filename => $file) {
 
176
      // We will modify this object (module A, see doxygen for module A, B, C).
 
177
      $file = &$files[$filename];
 
178
      if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
 
179
        foreach ($file->info['dependencies'] as $dependency_name) {
 
180
          // This is a nonexistent module.
 
181
          if ($dependency_name == '-circular-' || !isset($files[$dependency_name])) {
 
182
            continue;
 
183
          }
 
184
          // $dependency_name is module B (again, see doxygen).
 
185
          $files[$dependency_name]->info['dependents'][$filename] = $filename;
 
186
          $dependency = $files[$dependency_name];
 
187
          if (isset($dependency->info['dependencies']) && is_array($dependency->info['dependencies'])) {
 
188
            // Let's find possible C modules.
 
189
            foreach ($dependency->info['dependencies'] as $candidate) {
 
190
              if (array_search($candidate, $file->info['dependencies']) === FALSE) {
 
191
                // Is this a circular dependency?
 
192
                if ($candidate == $filename) {
 
193
                  // As a module name can not contain dashes, this makes
 
194
                  // impossible to switch on the module.
 
195
                  $candidate = '-circular-';
 
196
                  // Do not display the message or add -circular- more than once.
 
197
                  if (array_search($candidate, $file->info['dependencies']) !== FALSE) {
 
198
                    continue;
 
199
                  }
 
200
                  drupal_set_message(t('%module is part of a circular dependency. This is not supported and you will not be able to switch it on.', array('%module' => $file->info['name'])), 'error');
 
201
                }
 
202
                else {
 
203
                  // We added a new dependency to module A. The next loop will
 
204
                  // be able to use this as "B module" thus finding even
 
205
                  // deeper dependencies.
 
206
                  $new_dependency = TRUE;
 
207
                }
 
208
                $file->info['dependencies'][] = $candidate;
 
209
              }
 
210
            }
 
211
          }
 
212
        }
 
213
      }
 
214
      // Don't forget to break the reference.
 
215
      unset($file);
 
216
    }
 
217
  } while ($new_dependency);
 
218
  return $files;
 
219
}
 
220
 
 
221
/**
 
222
 * Determine whether a given module exists.
 
223
 *
 
224
 * @param $module
 
225
 *   The name of the module (without the .module extension).
 
226
 * @return
 
227
 *   TRUE if the module is both installed and enabled.
 
228
 */
 
229
function module_exists($module) {
 
230
  $list = module_list();
 
231
  return array_key_exists($module, $list);
 
232
}
 
233
 
 
234
/**
 
235
 * Load a module's installation hooks.
 
236
 */
 
237
function module_load_install($module) {
 
238
  // Make sure the installation API is available
 
239
  include_once './includes/install.inc';
 
240
 
 
241
  module_load_include('install', $module);
 
242
}
 
243
 
 
244
/**
 
245
 * Load a module include file.
 
246
 *
 
247
 * @param $type
 
248
 *   The include file's type (file extension).
 
249
 * @param $module
 
250
 *   The module to which the include file belongs.
 
251
 * @param $name
 
252
 *   Optionally, specify the file name. If not set, the module's name is used.
 
253
 */
 
254
function module_load_include($type, $module, $name = NULL) {
 
255
  if (empty($name)) {
 
256
    $name = $module;
 
257
  }
 
258
 
 
259
  $file = './'. drupal_get_path('module', $module) ."/$name.$type";
 
260
 
 
261
  if (is_file($file)) {
 
262
    require_once $file;
 
263
  }
 
264
  else {
 
265
    return FALSE;
 
266
  }
 
267
}
 
268
 
 
269
/**
 
270
 * Load an include file for each of the modules that have been enabled in
 
271
 * the system table.
 
272
 */
 
273
function module_load_all_includes($type, $name = NULL) {
 
274
  $modules = module_list();
 
275
  foreach ($modules as $module) {
 
276
    module_load_include($type, $module, $name);
 
277
  }
 
278
}
 
279
 
 
280
/**
 
281
 * Enable a given list of modules.
 
282
 *
 
283
 * @param $module_list
 
284
 *   An array of module names.
 
285
 */
 
286
function module_enable($module_list) {
 
287
  $invoke_modules = array();
 
288
  foreach ($module_list as $module) {
 
289
    $existing = db_fetch_object(db_query("SELECT status FROM {system} WHERE type = '%s' AND name = '%s'", 'module', $module));
 
290
    if ($existing->status == 0) {
 
291
      module_load_install($module);
 
292
      db_query("UPDATE {system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s'", 1, 0, 'module', $module);
 
293
      drupal_load('module', $module);
 
294
      $invoke_modules[] = $module;
 
295
    }
 
296
  }
 
297
 
 
298
  if (!empty($invoke_modules)) {
 
299
    // Refresh the module list to include the new enabled module.
 
300
    module_list(TRUE, FALSE);
 
301
    // Force to regenerate the stored list of hook implementations.
 
302
    module_implements('', FALSE, TRUE);
 
303
  }
 
304
 
 
305
  foreach ($invoke_modules as $module) {
 
306
    module_invoke($module, 'enable');
 
307
    // Check if node_access table needs rebuilding.
 
308
    // We check for the existence of node_access_needs_rebuild() since
 
309
    // at install time, module_enable() could be called while node.module
 
310
    // is not enabled yet.
 
311
    if (function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
 
312
      node_access_needs_rebuild(TRUE);
 
313
    }
 
314
  }
 
315
}
 
316
 
 
317
/**
 
318
 * Disable a given set of modules.
 
319
 *
 
320
 * @param $module_list
 
321
 *   An array of module names.
 
322
 */
 
323
function module_disable($module_list) {
 
324
  $invoke_modules = array();
 
325
  foreach ($module_list as $module) {
 
326
    if (module_exists($module)) {
 
327
      // Check if node_access table needs rebuilding.
 
328
      if (!node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
 
329
        node_access_needs_rebuild(TRUE);
 
330
      }
 
331
 
 
332
      module_load_install($module);
 
333
      module_invoke($module, 'disable');
 
334
      db_query("UPDATE {system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s'", 0, 0, 'module', $module);
 
335
      $invoke_modules[] = $module;
 
336
    }
 
337
  }
 
338
 
 
339
  if (!empty($invoke_modules)) {
 
340
    // Refresh the module list to exclude the disabled modules.
 
341
    module_list(TRUE, FALSE);
 
342
    // Force to regenerate the stored list of hook implementations.
 
343
    module_implements('', FALSE, TRUE);
 
344
  }
 
345
 
 
346
  // If there remains no more node_access module, rebuilding will be
 
347
  // straightforward, we can do it right now.
 
348
  if (node_access_needs_rebuild() && count(module_implements('node_grants')) == 0) {
 
349
    node_access_rebuild();
 
350
  }
 
351
}
 
352
 
 
353
/**
 
354
 * @defgroup hooks Hooks
 
355
 * @{
 
356
 * Allow modules to interact with the Drupal core.
 
357
 *
 
358
 * Drupal's module system is based on the concept of "hooks". A hook is a PHP
 
359
 * function that is named foo_bar(), where "foo" is the name of the module (whose
 
360
 * filename is thus foo.module) and "bar" is the name of the hook. Each hook has
 
361
 * a defined set of parameters and a specified result type.
 
362
 *
 
363
 * To extend Drupal, a module need simply implement a hook. When Drupal wishes to
 
364
 * allow intervention from modules, it determines which modules implement a hook
 
365
 * and call that hook in all enabled modules that implement it.
 
366
 *
 
367
 * The available hooks to implement are explained here in the Hooks section of
 
368
 * the developer documentation. The string "hook" is used as a placeholder for
 
369
 * the module name is the hook definitions. For example, if the module file is
 
370
 * called example.module, then hook_help() as implemented by that module would be
 
371
 * defined as example_help().
 
372
 */
 
373
 
 
374
/**
 
375
 * Determine whether a module implements a hook.
 
376
 *
 
377
 * @param $module
 
378
 *   The name of the module (without the .module extension).
 
379
 * @param $hook
 
380
 *   The name of the hook (e.g. "help" or "menu").
 
381
 * @return
 
382
 *   TRUE if the module is both installed and enabled, and the hook is
 
383
 *   implemented in that module.
 
384
 */
 
385
function module_hook($module, $hook) {
 
386
  return function_exists($module .'_'. $hook);
 
387
}
 
388
 
 
389
/**
 
390
 * Determine which modules are implementing a hook.
 
391
 *
 
392
 * @param $hook
 
393
 *   The name of the hook (e.g. "help" or "menu").
 
394
 * @param $sort
 
395
 *   By default, modules are ordered by weight and filename, settings this option
 
396
 *   to TRUE, module list will be ordered by module name.
 
397
 * @param $refresh
 
398
 *   For internal use only: Whether to force the stored list of hook
 
399
 *   implementations to be regenerated (such as after enabling a new module,
 
400
 *   before processing hook_enable).
 
401
 * @return
 
402
 *   An array with the names of the modules which are implementing this hook.
 
403
 */
 
404
function module_implements($hook, $sort = FALSE, $refresh = FALSE) {
 
405
  static $implementations;
 
406
 
 
407
  if ($refresh) {
 
408
    $implementations = array();
 
409
    return;
 
410
  }
 
411
 
 
412
  if (!isset($implementations[$hook])) {
 
413
    $implementations[$hook] = array();
 
414
    $list = module_list(FALSE, TRUE, $sort);
 
415
    foreach ($list as $module) {
 
416
      if (module_hook($module, $hook)) {
 
417
        $implementations[$hook][] = $module;
 
418
      }
 
419
    }
 
420
  }
 
421
 
 
422
  // The explicit cast forces a copy to be made. This is needed because
 
423
  // $implementations[$hook] is only a reference to an element of
 
424
  // $implementations and if there are nested foreaches (due to nested node
 
425
  // API calls, for example), they would both manipulate the same array's
 
426
  // references, which causes some modules' hooks not to be called.
 
427
  // See also http://www.zend.com/zend/art/ref-count.php.
 
428
  return (array)$implementations[$hook];
 
429
}
 
430
 
 
431
/**
 
432
 * Invoke a hook in a particular module.
 
433
 *
 
434
 * @param $module
 
435
 *   The name of the module (without the .module extension).
 
436
 * @param $hook
 
437
 *   The name of the hook to invoke.
 
438
 * @param ...
 
439
 *   Arguments to pass to the hook implementation.
 
440
 * @return
 
441
 *   The return value of the hook implementation.
 
442
 */
 
443
function module_invoke() {
 
444
  $args = func_get_args();
 
445
  $module = $args[0];
 
446
  $hook = $args[1];
 
447
  unset($args[0], $args[1]);
 
448
  $function = $module .'_'. $hook;
 
449
  if (module_hook($module, $hook)) {
 
450
    return call_user_func_array($function, $args);
 
451
  }
 
452
}
 
453
/**
 
454
 * Invoke a hook in all enabled modules that implement it.
 
455
 *
 
456
 * @param $hook
 
457
 *   The name of the hook to invoke.
 
458
 * @param ...
 
459
 *   Arguments to pass to the hook.
 
460
 * @return
 
461
 *   An array of return values of the hook implementations. If modules return
 
462
 *   arrays from their implementations, those are merged into one array.
 
463
 */
 
464
function module_invoke_all() {
 
465
  $args = func_get_args();
 
466
  $hook = $args[0];
 
467
  unset($args[0]);
 
468
  $return = array();
 
469
  foreach (module_implements($hook) as $module) {
 
470
    $function = $module .'_'. $hook;
 
471
    $result = call_user_func_array($function, $args);
 
472
    if (isset($result) && is_array($result)) {
 
473
      $return = array_merge_recursive($return, $result);
 
474
    }
 
475
    else if (isset($result)) {
 
476
      $return[] = $result;
 
477
    }
 
478
  }
 
479
 
 
480
  return $return;
 
481
}
 
482
 
 
483
/**
 
484
 * @} End of "defgroup hooks".
 
485
 */
 
486
 
 
487
/**
 
488
 * Array of modules required by core.
 
489
 */
 
490
function drupal_required_modules() {
 
491
  return array('block', 'filter', 'node', 'system', 'user');
 
492
}