Projet

Général

Profil

Paste
Télécharger (17,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / themes / bootstrap / includes / registry.inc @ b220caf9

1
<?php
2
/**
3
 * @file
4
 * List of functions used to alter the theme registry in Bootstrap based themes.
5
 */
6

    
7
/**
8
 * @addtogroup registry
9
 * @{
10
 */
11

    
12
// Define additional sub-groups for creating lists for all the theme files.
13
/**
14
 * @defgroup theme_functions Theme Functions (.func.php)
15
 *
16
 * List of theme functions used in the Drupal Bootstrap base theme.
17
 *
18
 * View the parent topic for additional documentation.
19
 */
20
/**
21
 * @defgroup theme_preprocess Theme Preprocess Functions (.vars.php)
22
 *
23
 * List of theme preprocess functions used in the Drupal Bootstrap base theme.
24
 *
25
 * View the parent topic for additional documentation.
26
 */
27
/**
28
 * @defgroup theme_process Theme Process Functions (.vars.php)
29
 *
30
 * List of theme process functions used in the Drupal Bootstrap base theme.
31
 *
32
 * View the parent topic for additional documentation.
33
 */
34
/**
35
 * @defgroup templates Theme Templates (.tpl.php)
36
 *
37
 * List of theme templates used in the Drupal Bootstrap base theme.
38
 *
39
 * View the parent topic for additional documentation.
40
 */
41

    
42
/**
43
 * Stub implementation for bootstrap_theme().
44
 *
45
 * This base-theme's custom theme hook implementations. Never define "path"
46
 * or "template" as these are detected and automatically added.
47
 *
48
 * @see bootstrap_theme_registry_alter()
49
 * @see bootstrap_hook_theme_complete()
50
 * @see bootstrap_theme()
51
 * @see hook_theme()
52
 */
53
function _bootstrap_theme(&$existing, $type, $theme, $path) {
54
  // Bootstrap Carousels.
55
  $hooks['bootstrap_carousel'] = array(
56
    'variables' => array(
57
      'attributes' => array(),
58
      'items' => array(),
59
      'start_index' => 0,
60
      'controls' => TRUE,
61
      'indicators' => TRUE,
62
      'interval' => 5000,
63
      'pause' => 'hover',
64
      'wrap' => TRUE,
65
    ),
66
  );
67

    
68
  // Bootstrap Dropdowns.
69
  $hooks['bootstrap_dropdown'] = array(
70
    'render element' => 'element',
71
  );
72

    
73
  // Bootstrap Modals.
74
  $hooks['bootstrap_modal'] = array(
75
    'variables' => array(
76
      'heading' => '',
77
      'body' => '',
78
      'footer' => '',
79
      'dialog_attributes' => array(),
80
      'attributes' => array(),
81
      'size' => '',
82
      'html_heading' => FALSE,
83
    ),
84
  );
85

    
86
  // Bootstrap Panels.
87
  $hooks['bootstrap_panel'] = array(
88
    'render element' => 'element',
89
  );
90

    
91
  // Bootstrap search form wrapper.
92
  // @todo Remove this as it's not really needed and should use suggestions.
93
  $hooks['bootstrap_search_form_wrapper'] = array(
94
    'render element' => 'element',
95
  );
96
  return $hooks;
97
}
98

    
99
/**
100
 * Implements hook_theme_registry_alter().
101
 */
102
function bootstrap_theme_registry_alter(&$registry) {
103
  // Retrieve the active theme names.
104
  $themes = _bootstrap_get_base_themes(NULL, TRUE);
105

    
106
  // Return the theme registry unaltered if it is not Bootstrap based.
107
  if (!in_array('bootstrap', $themes)) {
108
    return;
109
  }
110

    
111
  // Inject the "footer" variable default in the existing "table" hook.
112
  // @see https://drupal.org/node/806982
113
  // @todo Make this discoverable in some way instead of a manual injection.
114
  $registry['table']['variables']['footer'] = NULL;
115

    
116
  // Process registered hooks in the theme registry.
117
  _bootstrap_process_theme_registry($registry, $themes);
118

    
119
  // Process registered hooks in the theme registry to add necessary theme hook
120
  // suggestion phased function invocations. This must be run after separately
121
  // and after all includes have been loaded.
122
  _bootstrap_process_theme_registry_suggestions($registry, $themes);
123

    
124
  // Post-process theme registry. This happens after all altering has occurred.
125
  foreach ($registry as $hook => $info) {
126
    // Ensure uniqueness.
127
    if (!empty($registry[$hook]['includes'])) {
128
      $registry[$hook]['includes'] = array_unique($info['includes']);
129
    }
130
    if (!empty($registry[$hook]['preprocess functions'])) {
131
      $registry[$hook]['preprocess functions'] = array_unique($info['preprocess functions']);
132
    }
133
    if (!empty($registry[$hook]['process functions'])) {
134
      $registry[$hook]['process functions'] = array_unique($info['process functions']);
135
    }
136

    
137
    // Ensure "theme path" is set.
138
    if (!isset($registry[$hook]['theme path'])) {
139
      $registry[$hook]['theme path'] = $GLOBALS['theme_path'];
140
    }
141
  }
142
}
143

    
144
/**
145
 * Processes registered hooks in the theme registry against list of themes.
146
 *
147
 * Discovers and fills missing elements in the theme registry. This is similar
148
 * to _theme_process_registry(), however severely modified for Bootstrap based
149
 * themes.
150
 *
151
 * All additions or modifications must live in `./templates`, relative to the
152
 * base theme or sub-theme's base folder. These files can be organized in any
153
 * order using sub-folders as it searches recursively.
154
 *
155
 * Adds or modifies the following theme hook keys:
156
 *  - `includes`: When a variables file `*.vars.php` is found.
157
 *  - `includes`: When a function file `*.func.php` is found.
158
 *  - `function`: When a specific theme hook function override is found.
159
 *  - `template`: When a template file `*.tpl.php` is found in. Note, if both
160
 *    a function and a template are defined, a template implementation will
161
 *    always be used and the `function` will be unset.
162
 *  - `path`: When a template file `*.tpl.php` is found.
163
 *  - `preprocess functions`: When a specific theme hook suggestion function
164
 *    `hook_preprocess_HOOK__SUGGESTION` is found.
165
 *  - `process functions` When a specific theme hook suggestion function
166
 *    `hook_process_HOOK__SUGGESTION` is found.
167
 *
168
 * @param array $registry
169
 *   The theme registry array, passed by reference.
170
 * @param string|array $themes
171
 *   The name of the theme or list of theme names to process.
172
 *
173
 * @see bootstrap_theme_registry_alter()
174
 * @see _theme_process_registry()
175
 * @see _theme_build_registry()
176
 */
177
function _bootstrap_process_theme_registry(&$registry, $themes) {
178
  // Convert to an array if needed.
179
  if (is_string($themes)) {
180
    $themes = array();
181
  }
182

    
183
  // Processor functions work in two distinct phases with the process
184
  // functions always being executed after the preprocess functions.
185
  $variable_process_phases = array(
186
    'preprocess functions' => 'preprocess',
187
    'process functions'    => 'process',
188
  );
189

    
190
  // Iterate over each theme passed.
191
  // Iterate over the [pre]process phases.
192
  foreach ($variable_process_phases as $phase_key => $phase) {
193
    foreach ($themes as $theme) {
194
      // Get the theme's base path.
195
      $path = drupal_get_path('theme', $theme);
196

    
197
      // Find theme function overrides.
198
      foreach (drupal_system_listing('/\.(func|vars)\.php$/', $path, 'name', 0) as $name => $file) {
199
        // Strip off the extension.
200
        if (($pos = strpos($name, '.')) !== FALSE) {
201
          $name = substr($name, 0, $pos);
202
        }
203

    
204
        // Transform "-" in file names to "_" to match theme hook naming scheme.
205
        $hook = strtr($name, '-', '_');
206

    
207
        // File to be included by core's theme function when a theme hook is
208
        // invoked.
209
        if (isset($registry[$hook])) {
210
          if (!isset($registry[$hook][$phase_key])) {
211
            $registry[$hook][$phase_key] = array();
212
          }
213
          if (!isset($registry[$hook]['includes'])) {
214
            $registry[$hook]['includes'] = array();
215
          }
216

    
217
          // Include the file now so functions can be discovered below.
218
          include_once DRUPAL_ROOT . '/' . $file->uri;
219
          if (!in_array($file->uri, $registry[$hook]['includes'])) {
220
            $registry[$hook]['includes'][] = $file->uri;
221
          }
222
        }
223
      }
224

    
225
      // Process core's normal functionality.
226
      _theme_process_registry($registry, $theme, $GLOBALS['theme_key'] === $theme ? 'theme' : 'base_theme', $theme, $path);
227

    
228
      // Find necessary templates in the theme.
229
      $registry = drupal_array_merge_deep($registry, drupal_find_theme_templates($registry, '.tpl.php', $path));
230

    
231
      // Iterate over each registered hook.
232
      foreach ($registry as $hook => $info) {
233
        // Remove function callbacks if a template was found.
234
        if (isset($info['function']) && isset($info['template'])) {
235
          unset($registry[$hook]['function']);
236
        }
237

    
238
        // Correct template theme paths.
239
        if (!isset($info['theme path'])) {
240
          $registry[$hook]['theme path'] = $path;
241
        }
242

    
243
        // Correct the type that is implementing this override.
244
        $registry[$hook]['type'] = $GLOBALS['theme_path'] === $registry[$hook]['theme path'] ? 'theme' : 'base_theme';
245

    
246
        // Sort the phase functions.
247
        // @see https://www.drupal.org/node/2098551
248
        _bootstrap_registry_sort_phase_functions($registry[$hook][$phase_key], $hook, $phase, $themes);
249

    
250
        // Setup a default "context" variable. This allows #context to be passed
251
        // to every template and theme function.
252
        // @see https://drupal.org/node/2035055
253
        if (isset($info['variables']) && !isset($info['variables']['context'])) {
254
          $registry[$hook]['variables']['context'] = array();
255
        }
256

    
257
        // Setup a default "icon" variable. This allows #icon to be passed
258
        // to every template and theme function.
259
        // @see https://drupal.org/node/2219965
260
        if (isset($info['variables']) && !isset($info['variables']['icon'])) {
261
          $registry[$hook]['variables']['icon'] = NULL;
262
        }
263
        if (isset($info['variables']) && !isset($info['variables']['icon_position'])) {
264
          $registry[$hook]['variables']['icon_position'] = 'before';
265
        }
266
      }
267
    }
268
  }
269
}
270

    
271
/**
272
 * Ensures the phase functions are invoked in the correct order.
273
 *
274
 * @param array $functions
275
 *   The phase functions to iterate over.
276
 * @param string $hook
277
 *   The current hook being processed.
278
 * @param string $phase
279
 *   The current phase being processed.
280
 * @param array $themes
281
 *   An indexed array of current themes.
282
 *
283
 * @see https://www.drupal.org/node/2098551
284
 */
285
function _bootstrap_registry_sort_phase_functions(&$functions, $hook, $phase, $themes) {
286
  // Immediately return if there is nothing to sort.
287
  if (count($functions) < 2) {
288
    return;
289
  }
290

    
291
  // Create an associative array of theme functions to ensure sort order.
292
  $theme_functions = array_fill_keys($themes, array());
293

    
294
  // Iterate over all the themes.
295
  foreach ($themes as $theme) {
296
    // Only add the function to the array of theme functions if it currently
297
    // exists in the $functions array.
298
    $function = $theme . '_' . $phase . '_' . $hook;
299
    $key = array_search($function, $functions);
300
    if ($key !== FALSE) {
301
      // Save the theme function to be added later, but sorted.
302
      $theme_functions[$theme][] = $function;
303

    
304
      // Remove it from the current $functions array.
305
      unset($functions[$key]);
306
    }
307
  }
308

    
309
  // Iterate over all the captured theme functions and place them back into
310
  // the phase functions array.
311
  foreach ($theme_functions as $array) {
312
    $functions = array_merge($functions, $array);
313
  }
314
}
315

    
316
/**
317
 * Processes registered hooks in the theme registry against list of themes.
318
 *
319
 * This is used to add the necessary phased functions to theme hook suggestions.
320
 * Because it uses get_defined_functions(), it must be invoked after all
321
 * includes have been detected and loaded. This is similar to
322
 * drupal_find_theme_functions(), however severely modified for Bootstrap based
323
 * themes.
324
 *
325
 * @param array $registry
326
 *   The theme registry array, passed by reference.
327
 * @param string|array $themes
328
 *   The name of the theme or list of theme names to process.
329
 *
330
 * @return array
331
 *   The functions found, suitable for returning from hook_theme;
332
 *
333
 * @see https://drupal.org/node/939462
334
 * @see drupal_find_theme_functions()
335
 */
336
function _bootstrap_process_theme_registry_suggestions(&$registry, $themes) {
337
  // Convert to an array if needed.
338
  if (is_string($themes)) {
339
    $themes = array();
340
  }
341

    
342
  // Merge in normal core detections first.
343
  $registry = drupal_array_merge_deep($registry, drupal_find_theme_functions($registry, $themes));
344

    
345
  // Processor functions work in two distinct phases with the process
346
  // functions always being executed after the preprocess functions.
347
  $variable_process_phases = array(
348
    'preprocess functions' => 'preprocess',
349
    'process functions'    => 'process',
350
  );
351

    
352
  $grouped_functions = drupal_group_functions_by_prefix();
353

    
354
  // Iterate over each theme passed.
355
  foreach ($themes as $theme) {
356
    // Iterate over each registered hook.
357
    foreach ($registry as $hook => $info) {
358
      // The pattern to match.
359
      $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
360

    
361
      // Only process hooks that have not explicitly "turned off" patterns.
362
      if (empty($pattern)) {
363
        continue;
364
      }
365

    
366
      // Iterate over the [pre]process phases.
367
      foreach ($variable_process_phases as $phase_key => $phase) {
368
        // Find functions matching the specific theme and phase prefix.
369
        $prefix = $theme . '_' . $phase;
370

    
371
        // Grep only the functions which are within the prefix group.
372
        list($first_prefix,) = explode('_', $prefix, 2);
373
        if (isset($grouped_functions[$first_prefix]) && ($matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $grouped_functions[$first_prefix]))) {
374
          foreach ($matches as $match) {
375
            // Determine the current theme implementation.
376
            $hook = substr($match, strlen($prefix) + 1);
377
            $base_hook = $hook;
378

    
379
            // If there's no current theme implementation, keep checking for
380
            // more generic base hooks. If there's still no implementation,
381
            // one must be created using the last found implementation
382
            // information.
383
            if (!isset($registry[$base_hook]) || isset($registry[$base_hook]['base hook'])) {
384
              // Iteratively strip everything after the last '__' delimiter,
385
              // until an implementation is found.
386
              while ($pos = strrpos($base_hook, '__')) {
387
                $base_hook = substr($base_hook, 0, $pos);
388
                if (isset($registry[$base_hook])) {
389
                  break;
390
                }
391
              }
392

    
393
              // No base hook was found, this allows the implementation to be
394
              // ignored in the next steps.
395
              if (!isset($registry[$base_hook])) {
396
                $base_hook = FALSE;
397
              }
398
            }
399

    
400
            // Process specific base hook implementations if necessary.
401
            if ($base_hook) {
402
              // The matched theme implementation does not exist in the
403
              // registry, one must be created if base hook information was
404
              // found, otherwise it will be ignored.
405
              if (!isset($registry[$hook])) {
406
                $registry[$base_hook] += array(
407
                  'type' => 'theme',
408
                  'preprocess functions' => array(),
409
                  'process functions' => array(),
410
                );
411
                $hook_type = isset($registry[$base_hook]['function']) ? 'function' : 'template';
412
                $arg_name = isset($registry[$base_hook]['variables']) ? 'variables' : 'render element';
413
                $registry[$hook] = array(
414
                  $hook_type => $registry[$base_hook][$hook_type],
415
                  $arg_name => $registry[$base_hook][$arg_name],
416
                  'base hook' => $base_hook,
417
                  'type' => $registry[$base_hook]['type'],
418
                  'preprocess functions' => array(),
419
                  'process functions' => array(),
420
                );
421
                if (isset($registry[$base_hook]['path'])) {
422
                  $registry[$hook]['path'] = $registry[$base_hook]['path'];
423
                }
424
                if (isset($registry[$base_hook]['theme path'])) {
425
                  $registry[$hook]['theme path'] = $registry[$base_hook]['theme path'];
426
                }
427
              }
428
            }
429

    
430
            // If the hook exists, merge in the functions. Otherwise ignore it
431
            // since there was no base hook found and a new implementation
432
            // could not be created.
433
            if (isset($registry[$hook])) {
434
              $registry[$hook] = drupal_array_merge_deep($registry[$hook], array(
435
                $phase_key => array($match),
436
              ));
437

    
438
              // Due to how theme() functions, if a base hook implements
439
              // preprocess or process functions, then the base hook info is
440
              // used to invoke the necessary phase functions instead of the
441
              // suggestion hook info. To get around this, a helper function
442
              // must be appended to the base hook info so it can call the
443
              // theme suggestion implementation's phase function.
444
              $function = '_bootstrap_' . $phase . '_theme_suggestion';
445
              if (!in_array($function, $registry[$base_hook][$phase_key])) {
446
                $registry[$base_hook][$phase_key][] = $function;
447
              }
448
            }
449
          }
450
        }
451
      }
452
    }
453
  }
454
}
455

    
456
/**
457
 * Performance gain.
458
 *
459
 * Do not remove from 7.x. This function is not available in every core version.
460
 *
461
 * @see https://www.drupal.org/node/2339447
462
 */
463
if (!function_exists('drupal_group_functions_by_prefix')) {
464
  /**
465
   * Group all user functions by word before first underscore.
466
   *
467
   * @return array
468
   *   Functions grouped by the first prefix.
469
   */
470
  function drupal_group_functions_by_prefix() {
471
    $functions = get_defined_functions();
472
    $grouped_functions = array();
473

    
474
    // Splitting user defined functions into groups by the first prefix.
475
    foreach ($functions['user'] as $function) {
476
      list($first_prefix,) = explode('_', $function, 2);
477
      $grouped_functions[$first_prefix][] = $function;
478
    }
479

    
480
    return $grouped_functions;
481
  }
482
}
483

    
484
/**
485
 * @} End of "addtogroup registry".
486
 */