Project

General

Profile

Paste
Download (18.2 KB) Statistics
| Branch: | Revision:

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

1
<?php
2

    
3
/**
4
 * @file
5
 * List of functions used to alter the theme registry in Bootstrap based themes.
6
 */
7

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

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

    
43
/**
44
 * Stub implementation for bootstrap_theme().
45
 *
46
 * This base-theme's custom theme hook implementations. Never define "path"
47
 * or "template" as these are detected and automatically added.
48
 *
49
 * @see bootstrap_theme_registry_alter()
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://www.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
  // Merge both "html" and "page" theme hooks into "maintenance_page". Create
125
  // a fake "page" variable to satisfy both "html" and "page" preprocess/process
126
  // functions. Whether or not they stumble over each other doesn't matter since
127
  // the "maintenance_page" theme hook uses the "content" variable instead.
128
  $registry['maintenance_page']['variables']['page'] = array(
129
    '#show_messages' => TRUE,
130
    '#children' => NULL,
131
    'page_bottom' => array(),
132
    'page_top' => array(),
133
  );
134
  foreach (array('html', 'page') as $theme_hook) {
135
    foreach (array('includes', 'preprocess functions', 'process functions') as $property) {
136
      if (!isset($registry['maintenance_page'][$property])) {
137
        $registry['maintenance_page'][$property] = array();
138
      }
139
      if (!isset($registry[$theme_hook][$property])) {
140
        $registry[$theme_hook][$property] = array();
141
      }
142
      $registry['maintenance_page'][$property] = array_merge($registry['maintenance_page'][$property], $registry[$theme_hook][$property]);
143
    }
144
  }
145

    
146
  // Post-process theme registry. This happens after all altering has occurred.
147
  foreach ($registry as $hook => $info) {
148
    // Ensure uniqueness.
149
    if (!empty($registry[$hook]['includes'])) {
150
      $registry[$hook]['includes'] = array_unique($info['includes']);
151
    }
152
    if (!empty($registry[$hook]['preprocess functions'])) {
153
      $registry[$hook]['preprocess functions'] = array_unique($info['preprocess functions']);
154
    }
155
    if (!empty($registry[$hook]['process functions'])) {
156
      $registry[$hook]['process functions'] = array_unique($info['process functions']);
157
    }
158

    
159
    // Ensure "theme path" is set.
160
    if (!isset($registry[$hook]['theme path'])) {
161
      $registry[$hook]['theme path'] = $GLOBALS['theme_path'];
162
    }
163
  }
164
}
165

    
166
/**
167
 * Processes registered hooks in the theme registry against list of themes.
168
 *
169
 * Discovers and fills missing elements in the theme registry. This is similar
170
 * to _theme_process_registry(), however severely modified for Bootstrap based
171
 * themes.
172
 *
173
 * All additions or modifications must live in `./templates`, relative to the
174
 * base theme or sub-theme's base folder. These files can be organized in any
175
 * order using sub-folders as it searches recursively.
176
 *
177
 * Adds or modifies the following theme hook keys:
178
 *  - `includes`: When a variables file `*.vars.php` is found.
179
 *  - `includes`: When a function file `*.func.php` is found.
180
 *  - `function`: When a specific theme hook function override is found.
181
 *  - `template`: When a template file `*.tpl.php` is found in. Note, if both
182
 *    a function and a template are defined, a template implementation will
183
 *    always be used and the `function` will be unset.
184
 *  - `path`: When a template file `*.tpl.php` is found.
185
 *  - `preprocess functions`: When a specific theme hook suggestion function
186
 *    `hook_preprocess_HOOK__SUGGESTION` is found.
187
 *  - `process functions` When a specific theme hook suggestion function
188
 *    `hook_process_HOOK__SUGGESTION` is found.
189
 *
190
 * @param array $registry
191
 *   The theme registry array, passed by reference.
192
 * @param string|array $themes
193
 *   The name of the theme or list of theme names to process.
194
 *
195
 * @see bootstrap_theme_registry_alter()
196
 * @see _theme_process_registry()
197
 * @see _theme_build_registry()
198
 */
199
function _bootstrap_process_theme_registry(array &$registry, $themes) {
200
  // Convert to an array if needed.
201
  if (is_string($themes)) {
202
    $themes = array();
203
  }
204

    
205
  // Processor functions work in two distinct phases with the process
206
  // functions always being executed after the preprocess functions.
207
  $variable_process_phases = array(
208
    'preprocess functions' => 'preprocess',
209
    'process functions'    => 'process',
210
  );
211

    
212
  // Iterate over each theme passed.
213
  // Iterate over the [pre]process phases.
214
  foreach ($variable_process_phases as $phase_key => $phase) {
215
    foreach ($themes as $theme) {
216
      // Get the theme's base path.
217
      $path = drupal_get_path('theme', $theme);
218

    
219
      // Find theme function overrides.
220
      foreach (drupal_system_listing('/\.(func|vars)\.php$/', $path, 'name', 0) as $name => $file) {
221
        // Strip off the extension.
222
        if (($pos = strpos($name, '.')) !== FALSE) {
223
          $name = substr($name, 0, $pos);
224
        }
225

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

    
229
        // File to be included by core's theme function when a theme hook is
230
        // invoked.
231
        if (isset($registry[$hook])) {
232
          if (!isset($registry[$hook]['includes'])) {
233
            $registry[$hook]['includes'] = array();
234
          }
235

    
236
          // Include the file now so functions can be discovered below.
237
          include_once DRUPAL_ROOT . '/' . $file->uri;
238
          if (!in_array($file->uri, $registry[$hook]['includes'])) {
239
            $registry[$hook]['includes'][] = $file->uri;
240
          }
241
        }
242
      }
243

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

    
247
      // Find necessary templates in the theme.
248
      $registry = drupal_array_merge_deep($registry, drupal_find_theme_templates($registry, '.tpl.php', $path));
249

    
250
      // Iterate over each registered hook.
251
      foreach ($registry as $hook => $info) {
252
        // Ensure the current phase callback functions array exists.
253
        if (!isset($registry[$hook][$phase_key])) {
254
          $registry[$hook][$phase_key] = array();
255
        }
256

    
257
        // Remove function callbacks if a template was found.
258
        if (isset($info['function']) && isset($info['template'])) {
259
          unset($registry[$hook]['function']);
260
        }
261

    
262
        // Correct template theme paths.
263
        if (!isset($info['theme path'])) {
264
          $registry[$hook]['theme path'] = $path;
265
        }
266

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

    
270
        // Sort the phase functions.
271
        // @see https://www.drupal.org/node/2098551
272
        _bootstrap_registry_sort_phase_functions($registry[$hook][$phase_key], $hook, $phase, $themes);
273

    
274
        // Setup a default "context" variable. This allows #context to be passed
275
        // to every template and theme function.
276
        // @see https://www.drupal.org/node/2035055
277
        if (isset($info['variables']) && !isset($info['variables']['context'])) {
278
          $registry[$hook]['variables']['context'] = array();
279
        }
280

    
281
        // Setup a default "icon" variable. This allows #icon to be passed
282
        // to every template and theme function.
283
        // @see https://www.drupal.org/node/2219965
284
        if (isset($info['variables']) && !isset($info['variables']['icon'])) {
285
          $registry[$hook]['variables']['icon'] = NULL;
286
        }
287
        if (isset($info['variables']) && !isset($info['variables']['icon_position'])) {
288
          $registry[$hook]['variables']['icon_position'] = 'before';
289
        }
290
      }
291
    }
292
  }
293
}
294

    
295
/**
296
 * Ensures the phase functions are invoked in the correct order.
297
 *
298
 * @param array $functions
299
 *   The phase functions to iterate over.
300
 * @param string $hook
301
 *   The current hook being processed.
302
 * @param string $phase
303
 *   The current phase being processed.
304
 * @param array $themes
305
 *   An indexed array of current themes.
306
 *
307
 * @see https://www.drupal.org/node/2098551
308
 */
309
function _bootstrap_registry_sort_phase_functions(array &$functions, $hook, $phase, array $themes) {
310
  // Immediately return if there is nothing to sort.
311
  if (count($functions) < 2) {
312
    return;
313
  }
314

    
315
  // Create an associative array of theme functions to ensure sort order.
316
  $theme_functions = array_fill_keys($themes, array());
317

    
318
  // Iterate over all the themes.
319
  foreach ($themes as $theme) {
320
    // Only add the function to the array of theme functions if it currently
321
    // exists in the $functions array.
322
    $function = $theme . '_' . $phase . '_' . $hook;
323
    $key = array_search($function, $functions);
324
    if ($key !== FALSE) {
325
      // Save the theme function to be added later, but sorted.
326
      $theme_functions[$theme][] = $function;
327

    
328
      // Remove it from the current $functions array.
329
      unset($functions[$key]);
330
    }
331
  }
332

    
333
  // Iterate over all the captured theme functions and place them back into
334
  // the phase functions array.
335
  foreach ($theme_functions as $array) {
336
    $functions = array_merge($functions, $array);
337
  }
338
}
339

    
340
/**
341
 * Processes registered hooks in the theme registry against list of themes.
342
 *
343
 * This is used to add the necessary phased functions to theme hook suggestions.
344
 * Because it uses get_defined_functions(), it must be invoked after all
345
 * includes have been detected and loaded. This is similar to
346
 * drupal_find_theme_functions(), however severely modified for Bootstrap based
347
 * themes.
348
 *
349
 * @param array $registry
350
 *   The theme registry array, passed by reference.
351
 * @param string|array $themes
352
 *   The name of the theme or list of theme names to process.
353
 *
354
 * @see https://www.drupal.org/node/939462
355
 * @see drupal_find_theme_functions()
356
 */
357
function _bootstrap_process_theme_registry_suggestions(array &$registry, $themes) {
358
  // Convert to an array if needed.
359
  if (is_string($themes)) {
360
    $themes = array();
361
  }
362

    
363
  // Merge in normal core detections first.
364
  $registry = drupal_array_merge_deep($registry, drupal_find_theme_functions($registry, $themes));
365

    
366
  // Processor functions work in two distinct phases with the process
367
  // functions always being executed after the preprocess functions.
368
  $variable_process_phases = array(
369
    'preprocess functions' => 'preprocess',
370
    'process functions'    => 'process',
371
  );
372

    
373
  $grouped_functions = drupal_group_functions_by_prefix();
374

    
375
  // Iterate over each theme passed.
376
  foreach ($themes as $theme) {
377
    // Iterate over each registered hook.
378
    foreach ($registry as $hook => $info) {
379
      // The pattern to match.
380
      $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
381

    
382
      // Only process hooks that have not explicitly "turned off" patterns.
383
      if (empty($pattern)) {
384
        continue;
385
      }
386

    
387
      // Iterate over the [pre]process phases.
388
      foreach ($variable_process_phases as $phase_key => $phase) {
389
        // Find functions matching the specific theme and phase prefix.
390
        $prefix = $theme . '_' . $phase;
391

    
392
        // Grep only the functions which are within the prefix group.
393
        list($first_prefix,) = explode('_', $prefix, 2);
394
        if (isset($grouped_functions[$first_prefix]) && ($matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $grouped_functions[$first_prefix]))) {
395
          foreach ($matches as $match) {
396
            // Determine the current theme implementation.
397
            $hook = substr($match, strlen($prefix) + 1);
398
            $base_hook = $hook;
399

    
400
            // If there's no current theme implementation, keep checking for
401
            // more generic base hooks. If there's still no implementation,
402
            // one must be created using the last found implementation
403
            // information.
404
            if (!isset($registry[$base_hook]) || isset($registry[$base_hook]['base hook'])) {
405
              // Iteratively strip everything after the last '__' delimiter,
406
              // until an implementation is found.
407
              while ($pos = strrpos($base_hook, '__')) {
408
                $base_hook = substr($base_hook, 0, $pos);
409
                if (isset($registry[$base_hook])) {
410
                  break;
411
                }
412
              }
413

    
414
              // No base hook was found, this allows the implementation to be
415
              // ignored in the next steps.
416
              if (!isset($registry[$base_hook])) {
417
                $base_hook = FALSE;
418
              }
419
            }
420

    
421
            // Process specific base hook implementations if necessary.
422
            if ($base_hook) {
423
              // The matched theme implementation does not exist in the
424
              // registry, one must be created if base hook information was
425
              // found, otherwise it will be ignored.
426
              if (!isset($registry[$hook])) {
427
                $registry[$base_hook] += array(
428
                  'type' => 'theme',
429
                  'preprocess functions' => array(),
430
                  'process functions' => array(),
431
                );
432
                $hook_type = isset($registry[$base_hook]['function']) ? 'function' : 'template';
433
                $arg_name = isset($registry[$base_hook]['variables']) ? 'variables' : 'render element';
434
                $registry[$hook] = array(
435
                  $hook_type => $registry[$base_hook][$hook_type],
436
                  $arg_name => $registry[$base_hook][$arg_name],
437
                  'base hook' => $base_hook,
438
                  'type' => $registry[$base_hook]['type'],
439
                  'preprocess functions' => array(),
440
                  'process functions' => array(),
441
                );
442
                if (isset($registry[$base_hook]['path'])) {
443
                  $registry[$hook]['path'] = $registry[$base_hook]['path'];
444
                }
445
                if (isset($registry[$base_hook]['theme path'])) {
446
                  $registry[$hook]['theme path'] = $registry[$base_hook]['theme path'];
447
                }
448
              }
449
            }
450

    
451
            // If the hook exists, merge in the functions. Otherwise ignore it
452
            // since there was no base hook found and a new implementation
453
            // could not be created.
454
            if (isset($registry[$hook])) {
455
              $registry[$hook] = drupal_array_merge_deep($registry[$hook], array(
456
                $phase_key => array($match),
457
              ));
458

    
459
              // Due to how theme() functions, if a base hook implements
460
              // preprocess or process functions, then the base hook info is
461
              // used to invoke the necessary phase functions instead of the
462
              // suggestion hook info. To get around this, a helper function
463
              // must be appended to the base hook info so it can call the
464
              // theme suggestion implementation's phase function.
465
              $function = '_bootstrap_' . $phase . '_theme_suggestion';
466
              if (!in_array($function, $registry[$base_hook][$phase_key])) {
467
                $registry[$base_hook][$phase_key][] = $function;
468
              }
469
            }
470
          }
471
        }
472
      }
473
    }
474
  }
475
}
476

    
477
/**
478
 * Performance gain.
479
 *
480
 * Do not remove from 7.x. This function is not available in every core version.
481
 *
482
 * @see https://www.drupal.org/node/2339447
483
 */
484
if (!function_exists('drupal_group_functions_by_prefix')) {
485

    
486
  /**
487
   * Group all user functions by word before first underscore.
488
   *
489
   * @return array
490
   *   Functions grouped by the first prefix.
491
   */
492
  function drupal_group_functions_by_prefix() {
493
    $functions = get_defined_functions();
494
    $grouped_functions = array();
495

    
496
    // Splitting user defined functions into groups by the first prefix.
497
    foreach ($functions['user'] as $function) {
498
      list($first_prefix,) = explode('_', $function, 2);
499
      $grouped_functions[$first_prefix][] = $function;
500
    }
501

    
502
    return $grouped_functions;
503
  }
504

    
505
}
506

    
507
/**
508
 * @} End of "addtogroup registry".
509
 */