Projet

Général

Profil

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

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

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_theme()
50
 * @see hook_theme()
51
 */
52
function _bootstrap_theme(&$existing, $type, $theme, $path) {
53
  // Bootstrap Carousels.
54
  $hooks['bootstrap_carousel'] = array(
55
    'variables' => array(
56
      'attributes' => array(),
57
      'items' => array(),
58
      'start_index' => 0,
59
      'controls' => TRUE,
60
      'indicators' => TRUE,
61
      'interval' => 5000,
62
      'pause' => 'hover',
63
      'wrap' => TRUE,
64
    ),
65
  );
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
351
  $grouped_functions = drupal_group_functions_by_prefix();
352

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

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

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

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

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

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

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

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

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

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

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

    
479
    return $grouped_functions;
480
  }
481
}
482

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