Projet

Général

Profil

Paste
Télécharger (15,9 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / themes / bootstrap / includes / alter.inc @ 05237dd8

1
<?php
2
/**
3
 * @file
4
 * alter.inc
5
 *
6
 * Contains various implementations of hook_*_alter().
7
 */
8

    
9
/**
10
 * Include #pre_render callbacks for elements.
11
 */
12
bootstrap_include('bootstrap', 'includes/pre-render.inc');
13

    
14
/**
15
 * Include #process callbacks for elements.
16
 */
17
bootstrap_include('bootstrap', 'includes/process.inc');
18

    
19
/**
20
 * Implements hook_css_alter().
21
 */
22
function bootstrap_css_alter(&$css) {
23
  $theme_path = drupal_get_path('theme', 'bootstrap');
24

    
25
  // Exclude specified CSS files from theme.
26
  $excludes = bootstrap_get_theme_info(NULL, 'exclude][css');
27

    
28
  // Add CDN assets, if any.
29
  $provider = bootstrap_setting('cdn_provider');
30
  if ($cdn_assets = bootstrap_get_cdn_assets('css', $provider)) {
31
    $cdn_weight = -2.99;
32
    foreach ($cdn_assets as $cdn_asset) {
33
      $cdn_weight += .01;
34
      $css[$cdn_asset] = array(
35
        'data' => $cdn_asset,
36
        'type' => 'external',
37
        'every_page' => TRUE,
38
        'media' => 'all',
39
        'preprocess' => FALSE,
40
        'group' => CSS_THEME,
41
        'browsers' => array('IE' => TRUE, '!IE' => TRUE),
42
        'weight' => $cdn_weight,
43
      );
44
    }
45

    
46
    // Add a specific version and theme CSS overrides file.
47
    $version = bootstrap_setting('cdn_' . $provider . '_version');
48
    if (!$version) {
49
      $version = BOOTSTRAP_VERSION;
50
    }
51
    $theme = bootstrap_setting('cdn_' . $provider . '_theme');
52
    if (!$theme) {
53
      $theme = 'bootstrap';
54
    }
55
    $theme = $theme === 'bootstrap' || $theme === 'bootstrap_theme' ? '' : "-$theme";
56
    $overrides = "$theme_path/css/$version/overrides$theme.min.css";
57
    if (file_exists($overrides)) {
58
      $css[$overrides] = array(
59
        'data' => $overrides,
60
        'type' => 'file',
61
        'every_page' => TRUE,
62
        'media' => 'all',
63
        'preprocess' => TRUE,
64
        'group' => CSS_THEME,
65
        'browsers' => array('IE' => TRUE, '!IE' => TRUE),
66
        'weight' => -1,
67
      );
68
    }
69
  }
70
  if (!empty($excludes)) {
71
    $excludes = array_merge($excludes, str_replace('.css', '-rtl.css', $excludes));
72
    $css = array_diff_key($css, drupal_map_assoc($excludes));
73
  }
74
}
75

    
76
/**
77
 * Implements hook_element_info_alter().
78
 */
79
function bootstrap_element_info_alter(&$info) {
80
  global $theme_key;
81

    
82
  $cid = "theme_registry:bootstrap:element_info";
83
  $cached = array();
84
  if (($cache = cache_get($cid)) && !empty($cache->data)) {
85
    $cached = $cache->data;
86
  }
87

    
88
  $themes = _bootstrap_get_base_themes($theme_key, TRUE);
89
  foreach ($themes as $theme) {
90
    if (!isset($cached[$theme])) {
91
      $cached[$theme] = array();
92
      foreach (array_keys($info) as $type) {
93
        $element = array();
94

    
95
        // Replace fieldset theme implementations with bootstrap_panel.
96
        if (!empty($info[$type]['#theme']) && $info[$type]['#theme'] === 'fieldset') {
97
          $element['#bootstrap_replace']['#theme'] = 'bootstrap_panel';
98
        }
99
        if (!empty($info[$type]['#theme_wrappers']) && array_search('fieldset', $info[$type]['#theme_wrappers']) !== FALSE) {
100
          $element['#bootstrap_replace']['#theme_wrappers']['fieldset'] = 'bootstrap_panel';
101
        }
102

    
103
        // Setup a default "icon" variable. This allows #icon to be passed
104
        // to every template and theme function.
105
        // @see https://drupal.org/node/2219965
106
        $element['#icon'] = NULL;
107
        $element['#icon_position'] = 'before';
108

    
109
        $properties = array(
110
          '#process' => array(
111
            'form_process',
112
            'form_process_' . $type,
113
          ),
114
          '#pre_render' => array(
115
            'pre_render',
116
            'pre_render_' . $type,
117
          ),
118
        );
119
        foreach ($properties as $property => $callbacks) {
120
          foreach ($callbacks as $callback) {
121
            $function = $theme . '_' . $callback;
122
            if (function_exists($function)) {
123
              // Replace direct core function correlation.
124
              if (!empty($info[$type][$property]) && array_search($callback, $info[$type][$property]) !== FALSE) {
125
                $element['#bootstrap_replace'][$property][$callback] = $function;
126
              }
127
              // Check for a "form_" prefix instead (for #pre_render).
128
              elseif (!empty($info[$type][$property]) && array_search('form_' . $callback, $info[$type][$property]) !== FALSE) {
129
                $element['#bootstrap_replace'][$property]['form_' . $callback] = $function;
130
              }
131
              // Otherwise, append the function.
132
              else {
133
                $element[$property][] = $function;
134
              }
135
            }
136
          }
137
        }
138
        $cached[$theme][$type] = $element;
139
      }
140

    
141
      // Cache the element information.
142
      cache_set($cid, $cached);
143
    }
144

    
145
    // Merge in each theme's cached element info.
146
    $info = _bootstrap_element_info_array_merge($info, $cached[$theme]);
147
  }
148
}
149

    
150
/**
151
 * Merges the cached element information into the runtime array.
152
 *
153
 * @param array $info
154
 *   The element info array to merge data into.
155
 * @param array $cached
156
 *   The cached element info data array to merge from.
157
 *
158
 * @return array
159
 *   The altered element info array.
160
 */
161
function _bootstrap_element_info_array_merge($info, $cached) {
162
  foreach ($cached as $type => $element) {
163
    $replacement_data = isset($element['#bootstrap_replace']) ? $element['#bootstrap_replace'] : array();
164
    unset($element['#bootstrap_replace']);
165
    foreach ($element as $property => $data) {
166
      if (is_array($data)) {
167
        if (!isset($info[$type][$property])) {
168
          $info[$type][$property] = array();
169
        }
170
        // Append the values if not already in the array.
171
        foreach ($data as $key => $value) {
172
          if (!in_array($value, $info[$type][$property])) {
173
            $info[$type][$property][] = $value;
174
          }
175
        }
176
      }
177
      // Create the property, if not already set.
178
      elseif (!isset($info[$type][$property])) {
179
        $info[$type][$property] = $data;
180
      }
181
    }
182
    // Replace data, if necessary.
183
    foreach ($replacement_data as $property => $data) {
184
      if (is_array($data)) {
185
        foreach ($data as $needle => $replacement) {
186
          if (!empty($info[$type][$property]) && ($key = array_search($needle, $info[$type][$property])) !== FALSE) {
187
            $info[$type][$property][$key] = $replacement;
188
          }
189
        }
190
      }
191
      // Replace the property with the new data.
192
      else {
193
        $info[$type][$property] = $data;
194
      }
195
    }
196
  }
197

    
198
  // Return the altered element info array.
199
  return $info;
200
}
201

    
202
/**
203
 * Implements hook_field_widget_form_alter().
204
 */
205
function bootstrap_field_widget_form_alter(&$element, &$form_state, $context) {
206
  $widget_type = isset($context['instance']['widget']['type']) ? $context['instance']['widget']['type'] : NULL;
207
  if ($widget_type === 'image_image') {
208
    foreach (element_children($element) as $child) {
209
      $element[$child]['#process'][] = '_bootstrap_image_field_widget_process';
210
    }
211
  }
212
}
213

    
214
/**
215
 * Implements above #process callback.
216
 */
217
function _bootstrap_image_field_widget_process($element, &$form_state, $form) {
218
  // Core explicitly sets #theme_wrappers to an empty array for the upload
219
  // element (perhaps for styling reasons?). Thus, bootstrap_form_element() is
220
  // invoked, preventing any necessary logic from executing. To achieve the
221
  // same goal and keep backwards compatibility, reset the theme wrapper back
222
  // and indicating that the wrapper shouldn't be printed.
223
  $element['upload']['#theme_wrappers'][] = 'form_element__image_widget';
224
  $element['upload']['#form_element_wrapper'] = FALSE;
225

    
226
  // Unfortunately, core also doesn't set #access on the appropriate elements
227
  // until way too late (ironically, because of #ajax). Instead of calling
228
  // file_managed_file_pre_render(), just mimic the same #access logic, but
229
  // using #default_value instead of #value since the ajax request populates
230
  // #value.
231
  $value = empty($element['#default_value']['fid']);
232
  if (!$value) {
233
    $element['upload']['#access'] = FALSE;
234
    $element['upload_button']['#access'] = FALSE;
235
  }
236
  // If we don't already have a file, there is nothing to remove.
237
  else {
238
    $element['remove_button']['#access'] = FALSE;
239
  }
240

    
241
  // Make the upload file element an input group with a button.
242
  $element['upload']['#input_group_button'] = $value;
243

    
244
  return $element;
245
}
246

    
247
/**
248
 * Implements hook_form_alter().
249
 */
250
function bootstrap_form_alter(array &$form, array &$form_state = array(), $form_id = NULL) {
251
  if ($form_id) {
252
    switch ($form_id) {
253
      case 'system_theme_settings':
254
        // Create vertical tabs for global settings (provided by core or other
255
        // contrib modules).
256
        if (!isset($form['global'])) {
257
          $form['global'] = array(
258
            '#type' => 'vertical_tabs',
259
            '#weight' => -9,
260
          );
261
          if (!empty($form_state['build_info']['args'][0])) {
262
            $form['global']['#prefix'] = '<h2><small>' . t('Override Global Settings') . '</small></h2>';
263
          }
264
        }
265

    
266
        // Iterate over all child elements and check to see if they should be
267
        // moved in the global vertical tabs.
268
        $global_children = element_children($form);
269
        foreach ($global_children as $child) {
270
          if (isset($form[$child]['#type']) && $form[$child]['#type'] === 'fieldset' && !isset($form[$child]['#group'])) {
271
            $form[$child]['#group'] = 'global';
272
          }
273
        }
274
        break;
275

    
276
      case 'search_form':
277
        // Add a clearfix class so the results don't overflow onto the form.
278
        $form['#attributes']['class'][] = 'clearfix';
279

    
280
        // Remove container-inline from the container classes.
281
        $form['basic']['#attributes']['class'] = array();
282

    
283
        // Hide the default button from display.
284
        $form['basic']['submit']['#attributes']['class'][] = 'element-invisible';
285

    
286
        // Implement a theme wrapper to add a submit button containing a search
287
        // icon directly after the input element.
288
        $form['basic']['keys']['#theme_wrappers'] = array('bootstrap_search_form_wrapper');
289
        $form['basic']['keys']['#title'] = '';
290
        $form['basic']['keys']['#attributes']['placeholder'] = t('Search');
291
        break;
292

    
293
      case 'search_block_form':
294
        $form['#attributes']['class'][] = 'form-search';
295

    
296
        $form['search_block_form']['#title'] = '';
297
        $form['search_block_form']['#attributes']['placeholder'] = t('Search');
298

    
299
        // Hide the default button from display and implement a theme wrapper
300
        // to add a submit button containing a search icon directly after the
301
        // input element.
302
        $form['actions']['submit']['#attributes']['class'][] = 'element-invisible';
303
        $form['search_block_form']['#theme_wrappers'] = array('bootstrap_search_form_wrapper');
304

    
305
        // Apply a clearfix so the results don't overflow onto the form.
306
        $form['#attributes']['class'][] = 'content-search';
307
        break;
308

    
309
      case 'image_style_form':
310
        $form['effects']['new']['new']['#input_group_button'] = TRUE;
311
        break;
312

    
313
      case 'path_admin_filter_form':
314
        $form['basic']['filter']['#input_group_button'] = TRUE;
315
        break;
316
    }
317

    
318
  }
319
}
320

    
321
/**
322
 * Implements hook_js_alter().
323
 */
324
function bootstrap_js_alter(&$js) {
325
  // Exclude specified JavaScript files from theme.
326
  $excludes = bootstrap_get_theme_info(NULL, 'exclude][js');
327

    
328
  $theme_path = drupal_get_path('theme', 'bootstrap');
329

    
330
  // Add or replace JavaScript files when matching paths are detected.
331
  // Replacement files must begin with '_', like '_node.js'.
332
  $files = _bootstrap_file_scan_directory($theme_path . '/js', '/\.js$/');
333
  foreach ($files as $file) {
334
    $path = str_replace($theme_path . '/js/', '', $file->uri);
335
    // Detect if this is a replacement file.
336
    $replace = FALSE;
337
    if (preg_match('/^[_]/', $file->filename)) {
338
      $replace = TRUE;
339
      $path = dirname($path) . '/' . preg_replace('/^[_]/', '', $file->filename);
340
    }
341
    $matches = array();
342
    if (preg_match('/^modules\/([^\/]*)/', $path, $matches)) {
343
      if (!module_exists($matches[1])) {
344
        continue;
345
      }
346
      else {
347
        $path = str_replace('modules/' . $matches[1], drupal_get_path('module', $matches[1]), $path);
348
      }
349
    }
350
    // Path should always exist to either add or replace JavaScript file.
351
    if (!empty($js[$path])) {
352
      // Replace file.
353
      if ($replace) {
354
        $js[$file->uri] = $js[$path];
355
        $js[$file->uri]['data'] = $file->uri;
356
        unset($js[$path]);
357
      }
358
      // Add file.
359
      else {
360
        $js[$file->uri] = drupal_js_defaults($file->uri);
361
        $js[$file->uri]['group'] = JS_THEME;
362
      }
363
    }
364
  }
365

    
366
  // Ensure jQuery Once is always loaded.
367
  // @see https://drupal.org/node/2149561
368
  if (empty($js['misc/jquery.once.js'])) {
369
    $jquery_once = drupal_get_library('system', 'jquery.once');
370
    $js['misc/jquery.once.js'] = $jquery_once['js']['misc/jquery.once.js'];
371
    $js['misc/jquery.once.js'] += drupal_js_defaults('misc/jquery.once.js');
372
  }
373

    
374
  // Always add bootstrap.js last.
375
  $bootstrap = $theme_path . '/js/bootstrap.js';
376
  $js[$bootstrap] = drupal_js_defaults($bootstrap);
377
  $js[$bootstrap]['group'] = JS_THEME;
378
  $js[$bootstrap]['scope'] = 'footer';
379

    
380
  if (!empty($excludes)) {
381
    $js = array_diff_key($js, drupal_map_assoc($excludes));
382
  }
383

    
384
  // Add Bootstrap settings.
385
  $js['settings']['data'][]['bootstrap'] = array(
386
    'anchorsFix' => bootstrap_setting('anchors_fix'),
387
    'anchorsSmoothScrolling' => bootstrap_setting('anchors_smooth_scrolling'),
388
    'formHasError' => (int) bootstrap_setting('forms_has_error_value_toggle'),
389
    'popoverEnabled' => bootstrap_setting('popover_enabled'),
390
    'popoverOptions' => array(
391
      'animation' => (int) bootstrap_setting('popover_animation'),
392
      'html' => (int) bootstrap_setting('popover_html'),
393
      'placement' => bootstrap_setting('popover_placement'),
394
      'selector' => bootstrap_setting('popover_selector'),
395
      'trigger' => implode(' ', array_filter(array_values((array) bootstrap_setting('popover_trigger')))),
396
      'triggerAutoclose' => (int) bootstrap_setting('popover_trigger_autoclose'),
397
      'title' => bootstrap_setting('popover_title'),
398
      'content' => bootstrap_setting('popover_content'),
399
      'delay' => (int) bootstrap_setting('popover_delay'),
400
      'container' => bootstrap_setting('popover_container'),
401
    ),
402
    'tooltipEnabled' => bootstrap_setting('tooltip_enabled'),
403
    'tooltipOptions' => array(
404
      'animation' => (int) bootstrap_setting('tooltip_animation'),
405
      'html' => (int) bootstrap_setting('tooltip_html'),
406
      'placement' => bootstrap_setting('tooltip_placement'),
407
      'selector' => bootstrap_setting('tooltip_selector'),
408
      'trigger' => implode(' ', array_filter(array_values((array) bootstrap_setting('tooltip_trigger')))),
409
      'delay' => (int) bootstrap_setting('tooltip_delay'),
410
      'container' => bootstrap_setting('tooltip_container'),
411
    ),
412
  );
413

    
414
  // Add CDN assets, if any.
415
  if ($cdn_assets = bootstrap_get_cdn_assets('js')) {
416
    $cdn_weight = -99.99;
417
    foreach ($cdn_assets as $cdn_asset) {
418
      $cdn_weight += .01;
419
      $js[$cdn_asset] = drupal_js_defaults($cdn_asset);
420
      $js[$cdn_asset]['type'] = 'external';
421
      $js[$cdn_asset]['every_page'] = TRUE;
422
      $js[$cdn_asset]['weight'] = $cdn_weight;
423
    }
424
  }
425
}
426

    
427
/**
428
 * Implements hook_icon_bundle_list_alter().
429
 */
430
function bootstrap_icon_bundle_list_alter(&$build, $bundle) {
431
  if (bootstrap_setting('tooltip_enabled')) {
432
    foreach ($build as &$icon) {
433
      $icon['#attributes']['data-toggle'] = 'tooltip';
434
      $icon['#attributes']['data-placement'] = 'bottom';
435
    }
436
  }
437
}
438

    
439
/**
440
 * Implements hook_menu_local_tasks_alter().
441
 */
442
function bootstrap_menu_local_tasks_alter(&$data, &$router_item, $root_path) {
443
  if (!empty($data['actions']['output'])) {
444
    $items = array();
445
    foreach ($data['actions']['output'] as $item) {
446
      $items[] = array(
447
        'data' => $item,
448
      );
449
    }
450
    $data['actions']['output'] = array(
451
      '#theme' => 'item_list__action_links',
452
      '#items' => $items,
453
      '#attributes' => array(
454
        'class' => array('action-links'),
455
      ),
456
    );
457
  }
458
}
459

    
460
/**
461
 * Implements hook_js_callback_filter_xss_alter().
462
 */
463
function bootstrap_js_callback_filter_xss_alter(array &$allowed_tags = array()) {
464
  $allowed_tags[] = 'button';
465
}