$cdn_asset, 'type' => 'external', 'every_page' => TRUE, 'media' => 'all', 'preprocess' => FALSE, 'group' => CSS_THEME, 'browsers' => array('IE' => TRUE, '!IE' => TRUE), 'weight' => $cdn_weight, ); } // Add Drupal Bootstrap Styles. // @see https://github.com/unicorn-fail/drupal-bootstrap-styles $version = bootstrap_setting('cdn_' . $provider . '_version') ?: BOOTSTRAP_VERSION; $theme = bootstrap_setting('cdn_' . $provider . '_theme') ?: 'bootstrap'; if ($url = _bootstrap_cdn_get_drupal_bootstrap_styles_url($version, $theme)) { $external = url_is_external($url); $css[$url] = array( 'data' => $url, 'type' => $external ? 'external' : 'file', 'every_page' => TRUE, 'media' => 'all', 'preprocess' => $external ? FALSE : TRUE, 'group' => CSS_THEME, 'browsers' => array('IE' => TRUE, '!IE' => TRUE), 'weight' => -1, ); } } // Exclude specified CSS files from theme. if ($excludes = bootstrap_get_theme_info(NULL, 'exclude][css')) { $excludes = array_merge($excludes, str_replace('.css', '-rtl.css', $excludes)); $css = array_diff_key($css, drupal_map_assoc($excludes)); } } /** * Implements hook_element_info_alter(). */ function bootstrap_element_info_alter(&$info) { global $theme_key; $cid = "theme_registry:bootstrap:element_info"; $cached = array(); if (($cache = cache_get($cid)) && !empty($cache->data)) { $cached = $cache->data; } $themes = _bootstrap_get_base_themes($theme_key, TRUE); foreach ($themes as $theme) { if (!isset($cached[$theme])) { $cached[$theme] = array(); foreach (array_keys($info) as $type) { $element = array(); // Replace fieldset theme implementations with bootstrap_panel. if (!empty($info[$type]['#theme']) && $info[$type]['#theme'] === 'fieldset') { $element['#bootstrap_replace']['#theme'] = 'bootstrap_panel'; } if (!empty($info[$type]['#theme_wrappers']) && array_search('fieldset', $info[$type]['#theme_wrappers']) !== FALSE) { $element['#bootstrap_replace']['#theme_wrappers']['fieldset'] = 'bootstrap_panel'; } // Setup a default "icon" variable. This allows #icon to be passed // to every template and theme function. // @see https://www.drupal.org/node/2219965 $element['#icon'] = NULL; $element['#icon_position'] = 'before'; $properties = array( '#process' => array( 'form_process', 'form_process_' . $type, ), '#pre_render' => array( 'pre_render', 'pre_render_' . $type, ), ); foreach ($properties as $property => $callbacks) { foreach ($callbacks as $callback) { $function = $theme . '_' . $callback; if (function_exists($function)) { // Replace direct core function correlation. if (!empty($info[$type][$property]) && array_search($callback, $info[$type][$property]) !== FALSE) { $element['#bootstrap_replace'][$property][$callback] = $function; } // Check for a "form_" prefix instead (for #pre_render). elseif (!empty($info[$type][$property]) && array_search('form_' . $callback, $info[$type][$property]) !== FALSE) { $element['#bootstrap_replace'][$property]['form_' . $callback] = $function; } // Otherwise, append the function. else { $element[$property][] = $function; } } } } $cached[$theme][$type] = $element; } // Cache the element information. cache_set($cid, $cached); } // Merge in each theme's cached element info. $info = _bootstrap_element_info_array_merge($info, $cached[$theme]); } } /** * Merges the cached element information into the runtime array. * * @param array $info * The element info array to merge data into. * @param array $cached * The cached element info data array to merge from. * * @return array * The altered element info array. */ function _bootstrap_element_info_array_merge(array $info, array $cached) { foreach ($cached as $type => $element) { $replacement_data = isset($element['#bootstrap_replace']) ? $element['#bootstrap_replace'] : array(); unset($element['#bootstrap_replace']); foreach ($element as $property => $data) { if (is_array($data)) { if (!isset($info[$type][$property])) { $info[$type][$property] = array(); } // Append the values if not already in the array. foreach ($data as $key => $value) { if (!in_array($value, $info[$type][$property])) { $info[$type][$property][] = $value; } } } // Create the property, if not already set. elseif (!isset($info[$type][$property])) { $info[$type][$property] = $data; } } // Replace data, if necessary. foreach ($replacement_data as $property => $data) { if (is_array($data)) { foreach ($data as $needle => $replacement) { if (!empty($info[$type][$property]) && ($key = array_search($needle, $info[$type][$property])) !== FALSE) { $info[$type][$property][$key] = $replacement; } } } // Replace the property with the new data. else { $info[$type][$property] = $data; } } } // Return the altered element info array. return $info; } /** * Implements hook_field_widget_form_alter(). */ function bootstrap_field_widget_form_alter(&$element, &$form_state, $context) { $widget_type = isset($context['instance']['widget']['type']) ? $context['instance']['widget']['type'] : NULL; if ($widget_type === 'image_image') { foreach (element_children($element) as $child) { $element[$child]['#process'][] = '_bootstrap_image_field_widget_process'; } } } /** * Implements above #process callback. */ function _bootstrap_image_field_widget_process($element, &$form_state, $form) { // Core explicitly sets #theme_wrappers to an empty array for the upload // element (perhaps for styling reasons?). Thus, bootstrap_form_element() is // invoked, preventing any necessary logic from executing. To achieve the // same goal and keep backwards compatibility, reset the theme wrapper back // and indicating that the wrapper shouldn't be printed. $element['upload']['#theme_wrappers'][] = 'form_element__image_widget'; $element['upload']['#form_element_wrapper'] = FALSE; // Unfortunately, core also doesn't set #access on the appropriate elements // until way too late (ironically, because of #ajax). Instead of calling // file_managed_file_pre_render(), just mimic the same #access logic, but // using #default_value instead of #value since the ajax request populates // #value. $value = empty($element['#default_value']['fid']); if (!$value) { $element['upload']['#access'] = FALSE; $element['upload_button']['#access'] = FALSE; } // If we don't already have a file, there is nothing to remove. else { $element['remove_button']['#access'] = FALSE; } // Make the upload file element an input group with a button. $element['upload']['#input_group_button'] = $value; return $element; } /** * Implements hook_form_alter(). */ function bootstrap_form_alter(array &$form, array &$form_state = array(), $form_id = NULL) { if ($form_id) { switch ($form_id) { case 'system_theme_settings': // Create vertical tabs for global settings (provided by core or other // contrib modules). if (!isset($form['global'])) { $form['global'] = array( '#type' => 'vertical_tabs', '#weight' => -9, ); if (!empty($form_state['build_info']['args'][0])) { $form['global']['#prefix'] = '