plus-8.x-4.x-dev/plus.7.php
plus.7.php
<?php /** * @file * Drupal 7 module hooks/alters for the Drupal+ module. */ use Drupal\plus\Utility\Element; /** * Implements hook_boot(). */ function plus_boot() { // Because drupal_theme_initialize() is invoked before hook_init(), it adds // any theme JavaScript using drupal_add_js(), which in turn invokes // drupal_add_library() to add jQuery to the page. In order to prevent any // premature invocation of drupal_add_library(), set the static now and // just go ahead and define the default drupalSettings which is a backport // from Drupal 8. $javascript = &drupal_static('drupal_add_js', []); drupal_static('drupal_add_js:jquery_added', TRUE); $javascript = drupal_array_merge_deep($javascript, [ 'settings' => [ 'data' => [], 'type' => 'setting', 'scope' => 'header', 'scope_lock' => TRUE, 'group' => JS_CORE, 'every_page' => TRUE, ], ]); } /** * Implements hook_page_delivery_callback_alter(). * * {@inheritdoc} */ function plus_page_delivery_callback_alter(&$callback) { if (variable_get('plus_deliver_html_drupal8', TRUE)) { switch ($callback) { case 'drupal_deliver_html_page': $callback = 'plus_deliver_html_page'; break; case 'ajax_deliver': $callback = 'plus_ajax_deliver'; break; } } } /** * Delivers an HTML response. * * @param $page_callback_result * The page callback result. * * @todo Use Drupal 8 HTML delivery to take advantage of render array and * library/asset improvements. */ function plus_deliver_html_page($page_callback_result) { drupal_deliver_html_page($page_callback_result); } /** * Delivers an Ajax response. * * @param $page_callback_result * The page callback result. * * @todo Use Drupal 8 ajax delivery to take advantage of render array and * library/asset improvements. */ function plus_ajax_deliver($page_callback_result) { ajax_deliver($page_callback_result); } /** * Callback for dynamically invoking Drupal 7 #pre_render callbacks. * * @param array $element * The render array element. * * @return array * The modified render array element. * * @see \Drupal\plus\Utility\Element::addCallback() */ function plus_element_pre_render_callback(array $element) { $callbacks = Element::reference($element)->getProperty('plus_pre_render', []); foreach ($callbacks as $callback) { $element = call_user_func_array($callback, [$element]); } return $element; } /** * Implements hook_js_settings_alter(). */ function plus_js_settings_alter(array &$settings) { global $language; global $theme; // url() generates the prefix using hook_url_outbound_alter(). Instead of // running the hook_url_outbound_alter() again here, extract the prefix // from url(). $path_prefix = ''; url('', ['prefix' => &$path_prefix]); $base_path = base_path(); $current_path = current_path(); $path_settings = [ 'baseUrl' => $base_path, 'pathPrefix' => $path_prefix, 'currentPath' => $current_path, 'currentPathIsAdmin' => path_is_admin($current_path), 'isFront' => drupal_is_front_page(), 'currentLanguage' => $language->language, ]; // Only set values that haven't been set already. foreach ($path_settings as $key => $value) { if (!isset($settings['path'][$key])) { $settings['path'][$key] = $value; } } // Add the theme token to ajaxPageState, ensuring the database is available // before doing so. Also add the loaded libraries to ajaxPageState. $libraries = plus_get_loaded_libraries(); if (isset($settings['ajaxPageState']) || in_array('system/drupal.ajax', $libraries)) { // Provide the page with information about the theme that's used, so that // a later AJAX request can be rendered using the same theme. $settings['ajaxPageState']['theme'] = $theme; // The theme token is only validated when the theme requested is not the // default, so don't generate it unless necessary. if (!defined('MAINTENANCE_MODE') && $theme !== variable_get('theme_default', 'bartik')) { $settings['ajaxPageState']['theme_token'] = drupal_get_token($theme); } $settings['ajaxPageState']['libraries'] = $libraries; } } function plus_init_libraries() { // Unfortunately, in Drupal 7, hook_library() and hook_library_alter() do not // conform to the "normal" hook build/alter pattern. Nor are these hook info // definitions cached (at all). Every time either the drupal_add_library() or // drupal_get_library() functions are invoked, it starts the manual process // of retrieving and altering each module's libraries separately. This can // cause serious issues if a module needs to do some heavy computational // building or alters of library definitions. It also prevents a module from // altering all libraries, regardless of where they were defined, without // recursion issues (e.g. calling drupal_get_library() inside alters). $cid = 'plus:library_info'; $libraries = &drupal_static('drupal_get_library', []); // Immediately set cached library info and return, if it exists. if (($cache = cache_get($cid)) && isset($cache->data) && is_array($cache->data)) { $libraries = $cache->data; return; } // Get all module libraries. foreach (module_implements('library') as $module) { // Skip AdvAgg's implementation as it's not really a hook implementation. // @see https://www.drupal.org/project/advagg/issues/2946751 if ($module === 'advagg') { continue; } $libraries[$module] = module_invoke($module, 'library'); if (empty($libraries[$module])) { $libraries[$module] = []; } } // Perform each individual module library info alters (like core does). foreach ($libraries as $module => &$module_libraries) { drupal_alter('library', $module_libraries, $module); } // Now let modules perform an alter for all defined libraries. drupal_alter('all_libraries', $libraries); // Add default elements to allow for easier processing. foreach ($libraries as $module => &$module_libraries) { foreach ($module_libraries as $key => $data) { if (is_array($data)) { $module_libraries[$key] += [ 'dependencies' => [], 'js' => [], 'css' => [], ]; foreach ($module_libraries[$key]['js'] as $file => $options) { $module_libraries[$key]['js'][$file]['version'] = $module_libraries[$key]['version']; } } } } // Cache the library info definitions. cache_set($cid, $libraries); } /** * Implements hook_init(). */ function plus_init() { // Reset JavaScript's drupalSettings. During the bootstrap phase, the // drupal_theme_initialize() function adds the theme and theme token in the // "ajaxPageState" prematurely. These are added back, correctly, if so needed // in plus_js_settings_alter(). $javascript = &drupal_static('drupal_add_js', []); $javascript['settings']['data'] = []; // Drupal::service('library_info'); // Initialize libraries. plus_init_libraries(); // Add jQuery and the Drupal libraries if the site should always use theme. if (variable_get('javascript_always_use_jquery', TRUE)) { drupal_add_library('system', 'jquery'); drupal_add_library('system', 'jquery.once'); drupal_add_library('system', 'drupal'); } } /** * Implements hook_all_libraries_alter(). * * @see plus_init() */ function plus_all_libraries_alter(&$libraries) { $module_path = drupal_get_path('module', 'plus'); // Create a Drupal 8 backport of the "core/drupal" library, but using this // module's replacement instead. $libraries['system']['drupal'] = [ 'title' => 'Drupal+', 'version' => '1.0.0', 'es6' => FALSE, 'js' => [ $module_path . '/js/Drupal.js' => [ 'es6' => FALSE, 'group' => JS_CORE, ], ], ]; // Add the new Drupal library as a dependency for all core libraries that // start with "drupal.". foreach ($libraries['system'] as $key => &$info) { if (strpos($key, 'drupal.') === 0) { if (!isset($info['dependencies'])) { $info['dependencies'] = []; } array_unshift($info['dependencies'], ['system', 'drupal']); } } // Add a default placeholder for ajaxPageState. $libraries['system']['drupal.ajax']['js'][] = [ 'type' => 'setting', 'data' => ['ajaxPageState' => []], ]; // Add a jQuery On/Off shim if necessary. $jquery_on_off_shim = TRUE; if (module_exists('jquery_update')) { $jquery_versions = explode('.', variable_get('jquery_update_jquery_version', '1.10')); if ((int) $jquery_versions[0] > 1 || ((int) $jquery_versions[0] === 1 && (int) $jquery_versions[1] > 6)) { $jquery_on_off_shim = FALSE; } } if ($jquery_on_off_shim) { $libraries['system']['jquery']['js'][$module_path . '/js/jquery.on.off.shim.js'] = $libraries['system']['jquery']['js']['misc/jquery.js']; unset($libraries['system']['jquery']['js'][$module_path . '/js/jquery.on.off.shim.js']['data']); } foreach ($libraries as $module => &$module_libraries) { foreach ($module_libraries as $key => &$info) { if (is_array($info) && !empty($info['js']) && _plus_is_library_es6($info, $libraries)) { // Add a dependency on the Drupal+ Asset Manager, but only if the module // being processed is not this module, since doing so can cause // dependency recursion. if ($module !== 'plus') { $info['dependencies'][] = ['plus', 'drupal.asset.manager']; } // Mark all JavaScript files as ES6 since this the whole library is ES6 // (which may be because of an ES6 dependency). foreach ($info['js'] as &$data) { $data['es6'] = TRUE; } } } } } function _plus_is_library_es6(array &$info, array &$libraries) { // Immediately return if ES6 support was already determined or explicitly set. if (isset($info['es6'])) { return $info['es6']; } $info['es6'] = FALSE; if (!empty($info['dependencies'])) { foreach ($info['dependencies'] as $dependency) { list($dependency_module, $dependency_name) = $dependency; $dependency = isset($libraries[$dependency_module][$dependency_name]) ? $libraries[$dependency_module][$dependency_name] : []; $info['es6'] = $dependency ? _plus_is_library_es6($dependency, $libraries) : FALSE; if ($info['es6']) { break; } } } // If ES6 couldn't be determined by dependencies, check if this library's JS // has explicitly set "es6" or the filename ends with ".es6.js". if (!$info['es6'] && !empty($info['js'])) { foreach ($info['js'] as $file => &$data) { $file = (!isset($data['type']) || $data['type'] === 'file') && isset($data['data']) ? $data['data'] : $file; if (!empty($data['es6']) || preg_match('/\.es6\.js/', $file)) { $info['es6'] = TRUE; break; } } } return $info['es6']; } function plus_core_js_files() { $module_path = drupal_get_path('module', 'plus'); $files = [ 'misc/jquery.js', $module_path . '/js/jquery.on.off.shim.js', 'misc/jquery.once.js', $module_path . '/js/Drupal.js', 'misc/ajax.js', ]; return $files; } /** * Implements hook_element_info_alter(). */ function plus_element_info_alter(&$types) { if (!isset($types['scripts']['#pre_render'])) { $types['scripts']['#pre_render'] = []; } foreach ($types as $type => &$info) { if (!isset($info['#pre_render'])) { $info['#pre_render'] = []; } if ($type === 'scripts') { array_unshift($types['scripts']['#pre_render'], 'plus_pre_render_scripts'); } else { array_unshift($types['scripts']['#pre_render'], 'plus_pre_render_drupal_settings'); } } } function plus_pre_render_drupal_settings(array $element) { // Immediately return if there are no attachments. if (!isset($element['#attached'])) { return $element; } $settings = isset($element['#attached']['drupalSettings']) ? $element['#attached']['drupalSettings'] : []; unset($element['#attached']['drupalSettings']); if (isset($element['#attached']['js'])) { foreach ($element['#attached']['js'] as $key => $info) { if (is_array($info) && isset($info['type']) && $info['type'] === 'setting') { $settings = drupal_array_merge_deep($settings, drupal_array_merge_deep_array($info['data'])); unset($element['#attached']['js'][$key]); } } } if ($settings) { $element['#attached']['js'][] = [ 'type' => 'setting', 'data' => $settings, ]; } return $element; } /** * Implements hook_library(). */ function plus_library() { $module_path = drupal_get_path('module', 'plus'); $version = '1.0.0'; $libraries['drupal.asset'] = [ 'title' => 'Drupal+ Asset', 'version' => $version, 'es6' => TRUE, 'js' => [ $module_path . '/js/Drupal.Asset.es6.js' => [ 'es6' => TRUE, 'group' => JS_CORE, ], ], 'dependencies' => [ ['system', 'drupal'], ['plus', 'drupal.url'], ], ]; $libraries['drupal.asset.manager'] = [ 'title' => 'Drupal+ Asset Manager', 'version' => $version, 'es6' => TRUE, 'js' => [ $module_path . '/js/Drupal.AssetManager.es6.js' => [ 'es6' => TRUE, 'group' => JS_CORE, ], ], 'dependencies' => [ ['plus', 'drupal.asset'], ], ]; $libraries['drupal.async.queue'] = [ 'title' => 'Drupal+ Asynchronous Queue', 'version' => $version, 'es6' => TRUE, 'js' => [ $module_path . '/js/Drupal.AsyncQueue.es6.js' => [ 'group' => JS_CORE, ], ], 'dependencies' => [ ['system', 'drupal'], ], ]; $libraries['drupal.storage'] = [ 'title' => 'Drupal+ Storage', 'version' => $version, 'es6' => TRUE, 'js' => [ $module_path . '/js/Drupal.Storage.es6.js' => [ 'es6' => TRUE, 'group' => JS_LIBRARY, ], ], 'dependencies' => [ ['system', 'drupal'], ], ]; $libraries['drupal.url'] = [ 'title' => 'Drupal+ URL', 'version' => $version, 'es6' => TRUE, 'js' => [ $module_path . '/js/Drupal.Url.es6.js' => [ 'es6' => TRUE, 'group' => JS_LIBRARY, ], ], 'dependencies' => [ ['system', 'drupal'], ], ]; return $libraries; } function &_plus_find_element_by_key(&$elements, $key) { $element = NULL; foreach (element_children($elements) as $child) { if ($child === $key) { $element = &$elements[$child]; break; } if ($element = &_plus_find_element_by_key($elements[$child], $key)) { break; } } return $element; } /** * Implements hook_module_implements_alter(). */ function plus_module_implements_alter(&$implementations, $hook) { // Move plus to the bottom of the following hooks. $alter_hooks = [ 'init' => 'before', 'element_info_alter' => 'after', 'js_alter' => 'after', ]; foreach ($alter_hooks as $alter_hook => $position) { if ($hook === $alter_hook && array_key_exists('plus', $implementations)) { $item = $implementations['plus']; unset($implementations['plus']); if ($position === 'after') { $implementations['plus'] = $item; } else { $implementations = ['plus' => $item] + $implementations; } } } } /** * Implements hook_form_alter(). */ function plus_form_alter(&$form, &$form_state, $form_id) { // Immediately return if advagg settings shouldn't be managed. if (!variable_get('plus_manage_advagg_mod_settings', TRUE)) { return; } // Map the form identifier to the manage variable. $map = [ 'advagg_admin_settings_form' => 'plus_manage_advagg_settings', 'advagg_bundler_admin_settings_form' => 'plus_manage_advagg_bundler_settings', 'advagg_js_compress_admin_settings_form' => 'plus_manage_advagg_compressor_settings', 'advagg_mod_admin_settings_form' => 'plus_manage_advagg_mod_settings', ]; if (isset($map[$form_id]) && variable_get($map[$form_id], TRUE)) { $recommended = t('(recommended)'); $managed = t('(managed by <a href="!plus">Drupal+</a>)', [ // @todo Change URL to an admin UI page. '!plus' => url('https://www.drupal.org/project/plus'), ]); $settings = plus_manage_advagg_settings(); foreach ($settings as $name => $value) { if ($element = &_plus_find_element_by_key($form, $name)) { if (strpos($element['#title'], $recommended) !== FALSE) { $element['#title'] = str_replace($recommended, $managed, $element['#title']); } else { $element['#title'] .= ' ' . $managed; } $element['#default_value'] = $value; $element['#disabled'] = TRUE; unset($element['#states']); } } } } /** * Implements hook_advagg_mod_get_lists_alter(). */ function plus_advagg_mod_get_lists_alter(&$data) { // AdvAgg doesn't really provide a decent way to alter $all_in_footer_list. $all_in_footer_list = &$data[6]; // Ensure all the core files are scope locked to the header. foreach (plus_core_js_files() as $file) { if (!isset($all_in_footer_list[$file])) { $all_in_footer_list[$file] = [$file]; } } } /** * Provides AdvAgg setting overrides. * * Forcefully sets runtime AdvAgg settings that are not conducive with this * module's operation. Typically, these settings are configured incorrectly * anyway due to the lack of understanding and knowledge of how JavaScript * execution actually works in a browser. * * Thus, AdvAgg does a lot of unnecessary "wrapping" and "onload" event * modifications to the scripts in an effort to be a "catch all" for said * site developer. * * This is counter productive though and causes issues with this module which * aims to standardize everything using backports from Drupal 8 and new * libraries like Drupal.AssetManager and Drupal.AsyncBehaviors. * * @return array * An array of the overridden AdvAgg settings. */ function plus_manage_advagg_settings() { global $conf; $settings = []; // AdvAgg JavaScript Settings. if (variable_get('plus_manage_advagg_settings', TRUE)) { $settings = array_merge($settings, [ 'advagg_core_groups' => FALSE, ]); } // AdvAgg JavaScript Bundler Settings. if (variable_get('plus_manage_advagg_bundler_settings', TRUE)) { $settings = array_merge($settings, [ 'advagg_bundler_active' => TRUE, ]); } // AdvAgg JavaScript Compressor Settings. if (variable_get('plus_manage_advagg_compressor_settings', TRUE)) { $settings = array_merge($settings, [ 'advagg_js_compressor' => 1, 'advagg_js_compress_inline' => 1, 'advagg_js_compress_add_license' => 3, ]); } // AdvAgg JavaScript Modification Settings. if (variable_get('plus_manage_advagg_mod_settings', TRUE)) { $settings = array_merge($settings, [ 'advagg_mod_ga_inline_to_file' => FALSE, 'advagg_mod_prefetch' => FALSE, 'advagg_mod_js_adjust_sort_browsers' => FALSE, 'advagg_mod_js_adjust_sort_external' => FALSE, 'advagg_mod_js_adjust_sort_inline' => FALSE, 'advagg_mod_js_async' => FALSE, 'advagg_mod_js_async_in_header' => FALSE, 'advagg_mod_js_async_shim' => FALSE, 'advagg_mod_js_defer' => 0, 'advagg_mod_js_defer_inline_alter' => FALSE, 'advagg_mod_js_footer' => 3, 'advagg_mod_js_footer_inline_alter' => FALSE, 'advagg_mod_js_head_extract' => TRUE, 'advagg_mod_js_no_ajaxpagestate' => FALSE, 'advagg_mod_js_preprocess' => TRUE, 'advagg_mod_js_remove_unused' => FALSE, ]); } // Override runtime config. foreach ($settings as $name => $value) { $conf[$name] = $value; } return $settings; } /** * Implements hook_js_alter(). */ function plus_js_alter(&$javascript) { // Use the advanced drupal_static() pattern, since this is called very often. static $drupal_static_fast; if (!isset($drupal_static_fast)) { $drupal_static_fast['es6'] = &drupal_static(__FUNCTION__ . ':es6', []); $drupal_static_fast['settings'] = &drupal_static(__FUNCTION__ . ':settings', []); // Override AdvAgg settings (only needs to be invoked once). plus_manage_advagg_settings(); } $es6 = &$drupal_static_fast['es6']; $settings = &$drupal_static_fast['settings']; // Immediately return if this is rendering the ES6 files. if (!empty($javascript['es6_rendering'])) { unset($javascript['es6_rendering']); return; } // Capture all Drupal settings and remove them from the $javascript array. // They will be handled separately in plus_render_drupal_settings() as a // backport of Drupal 8's drupalSettings. foreach ($javascript as $key => $item) { if (isset($item['type']) && $item['type'] === 'setting') { $settings = array_merge($settings, isset($item['data']) && is_array($item['data']) ? $item['data'] : []); unset($javascript[$key]); } } $core_files = plus_core_js_files(); // Find all ES6 JavaScript. foreach ($javascript as $key => $info) { $core_file = in_array($key, $core_files); // Move core files to their own group. if ($core_file) { $javascript[$key]['group'] = JS_CORE; } // Otherwise, defer the rest. elseif ($javascript[$key]['type'] === 'file' || $javascript[$key]['type'] === 'external') { $javascript[$key]['defer'] = TRUE; } // Remove ES6 files since this must must be conditionally loaded if the // browser supports ES6. if (!$core_file && (!empty($info['es6']) || ($info['type'] === 'file' && preg_match('/\.es6\.js/', $info['data'])))) { if (!isset($es6[$key])) { $es6[$key] = []; } $es6[$key] = drupal_array_merge_deep($es6[$key], $info); unset($javascript[$key]); } } } /** * Implements hook_page_alter(). */ function plus_page_alter(&$page) { $element = [ '#type' => 'html_tag', '#tag' => 'script', '#attributes' => [ 'type' => 'application/json', 'data-drupal-selector' => 'drupal-settings-json', ], '#pre_render' => ['plus_render_drupal_settings'], '#value' => '{}', ]; drupal_add_html_head($element, 'drupalSettings'); // Implement a custom #attached callback to the page. This is necessary in // case any hook_page_build() or hook_page_alter() implementations attach // additional assets/libraries to the render array. It's also the last step // in the rendering process of drupal_render(). $page['page_bottom']['plus']['#attached']['_plus_page_alter'] = [[]]; } function plus_get_loaded_libraries() { // Backport of D8's indication of which libraries have been loaded. $libraries = []; foreach (drupal_static('drupal_add_library', []) as $module => $library) { foreach (array_keys(array_filter($library)) as $name) { $libraries[] = "$module/$name"; } } natsort($libraries); return array_values($libraries); } /** * Callback for attaching ES6 files as a Drupal setting to the page. */ function _plus_page_alter() { $settings = []; $options = ['absolute' => TRUE]; // Create a list of common files to load. $module_path = drupal_get_path('module', 'plus'); $settings['loaderFiles'] = [ // Drupal.Loader.es6.js must always be first! url($module_path . '/js/Drupal.Loader.es6.js', $options), url($module_path . '/js/Drupal.AsyncQueue.es6.js', $options), url($module_path . '/js/Drupal.Url.es6.js', $options), url($module_path . '/js/Drupal.Asset.es6.js', $options), url($module_path . '/js/Drupal.AssetManager.es6.js', $options), url($module_path . '/js/Drupal.Storage.es6.js', $options), ]; // Backport of D8's indication of which libraries have been loaded. $settings['ajaxPageState']['library'] = plus_get_loaded_libraries(); // Immediately return if ES6 support has been disabled. if ($es6 = drupal_static('plus_js_alter:es6', [])) { // Indicate that this is in the rendering phase so the alter doesn't recurse. $es6['es6_rendering'] = TRUE; // Generate the output for the files (absolute paths + aggregation). preg_match_all('/src="([^"]+)"/', drupal_get_js('header', $es6), $es6_matches); // Merge in any additional es6 files that should be loaded. $settings['loaderFiles'] = array_unique(array_merge($settings['loaderFiles'], $es6_matches[1])); } // Add the settings data. drupal_add_js($settings, 'setting'); } function plus_pre_render_scripts(array $elements) { foreach ($elements['#items'] as $key => $info) { if (isset($info['type']) && $info['type'] === 'drupalSettings') { $elements[] = [ '#type' => 'html_tag', '#tag' => 'script', '#attributes' => [ 'type' => 'application/json', 'data-drupal-selector' => 'drupal-settings-json', ], '#context' => [ 'drupalSettings' => TRUE, 'info' => $info, ], '#attached' => [ 'plus_render_drupal_settings' => [[]], ], '#value' => '{}', ]; unset($elements['#items'][$key]); } } return $elements; } function plus_render_drupal_settings(array $element) { global $theme; global $language; drupal_get_js(); // Backport similar JS cache id from Drupal 8, but just for drupalSettings. $libraries_to_load = plus_get_loaded_libraries(); $runtime_settings = drupal_array_merge_deep_array(drupal_static('plus_js_alter:settings', [])); $cid = 'plus:js:drupalSettings:' . $theme . ':' . $language->language . ':' . drupal_hash_base64(serialize($libraries_to_load)); if (($cache = cache_get($cid)) && isset($cache->data) && is_array($cache->data)) { $settings = $cache->data; } else { // Pass the current runtime settings to the hooks, but only cache the // changes that were made to it as the entire runtime settings are likely // to change per page due to the nature of asset management in Drupal 7. $build_settings = $runtime_settings; foreach (module_implements('js_settings_build') as $module) { $function = $module . '_' . 'js_settings_build'; $function($build_settings); } $settings = drupal_array_diff_assoc_recursive($build_settings, $runtime_settings); cache_set($cid, $settings); } // Merge built/cached settings with runtime settings. $settings = drupal_array_merge_deep($settings, $runtime_settings); // Allow modules and themes to alter the JS settings. drupal_alter('js_settings', $settings); if ($settings) { $element['#context']['drupalSettings'] = $settings; $element['#value'] = function_exists('advagg_json_encode') ? advagg_json_encode($settings) : drupal_json_encode($settings); // Gzip and base64 encode the settings. if (variable_get('plus_drupal_settings_gzip', TRUE) && variable_get('js_gzip_compression', TRUE) && extension_loaded('zlib')) { $element['#attributes']['data-drupal-gzip'] = 'true'; $element['#value'] = base64_encode(gzencode($element['#value'], 9)); } } return $element; } /** * Implements hook_theme(). */ function plus_theme($existing, $type, $theme, $path) { return [ 'file_link__plus' => [ 'base hook' => 'file_link', 'variables' => ['file' => NULL, 'icon_directory' => NULL, 'icon' => TRUE], ], 'no_file_icon' => [], ]; } /** * Do not show file icons, which can take up valuable space. */ function theme_no_file_icon() { return ''; } /** * Override theme hook. */ function plus_preprocess_file_link(&$variables) { $variables['theme_hook_suggestions'][] = 'file_link__plus'; } /** * Override file_link theme hook. */ function theme_file_link__plus($variables) { $file = $variables['file']; $url = file_create_url($file->uri); // Show icon. $icon = ''; if ($variables['icon']) { // Human-readable names, for use as text-alternatives to icons. $mime_name = array( 'application/msword' => t('Microsoft Office document icon'), 'application/vnd.ms-excel' => t('Office spreadsheet icon'), 'application/vnd.ms-powerpoint' => t('Office presentation icon'), 'application/pdf' => t('PDF icon'), 'video/quicktime' => t('Movie icon'), 'audio/mpeg' => t('Audio icon'), 'audio/wav' => t('Audio icon'), 'image/jpeg' => t('Image icon'), 'image/png' => t('Image icon'), 'image/gif' => t('Image icon'), 'application/zip' => t('Package icon'), 'text/html' => t('HTML icon'), 'text/plain' => t('Plain text icon'), 'application/octet-stream' => t('Binary Data'), ); $mimetype = file_get_mimetype($file->uri); $icon = theme('file_icon', array( 'file' => $file, 'icon_directory' => $variables['icon_directory'], 'alt' => !empty($mime_name[$mimetype]) ? $mime_name[$mimetype] : t('File'), )); } if (!isset($file->extension)) { $info = pathinfo($file->filename); $file->extension = $info['extension']; } // Set options as per anchor format described at // http://microformats.org/wiki/file-format-examples $options = array( 'attributes' => array( 'data-basename' => basename($file->filename, '.' . $file->extension), 'data-extension' => $file->extension, 'data-fid' => $file->fid, 'data-filename' => $file->filename, 'data-filesize' => $file->filesize, 'data-uid' => $file->uid, 'type' => $file->filemime . '; length=' . $file->filesize, ), ); // Use the description as the link text if available. if (empty($file->description)) { $link_text = $file->filename; } else { $link_text = $file->description; $options['attributes']['title'] = check_plain($file->filename); } return '<span class="file">' . $icon . l($link_text, $url, $options) . '</span>'; }