' . t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.') . '

'; } elseif ($path == 'admin/help#metatag') { return '

' . t('The Metatag module provides a options to let each page have customized meta data added to the "meta" tags in the HEAD section of the document.') . '

'; } elseif ($path == 'admin/config/search/metatags/bulk-revert') { return '

' . t('This form will wipe out all custom meta tags for the selected entities, reverting them to the default configuration assigned at the Defaults tab. For example, if the meta tags are changed for an article they will be removed if the "Node: Article" checkbox is selected.', array('@url' => url('admin/config/search/metatags'))) . '

'; } } /** * Implements hook_theme(). */ function metatag_theme() { $info['metatag'] = array( 'render element' => 'element', 'file' => 'metatag.theme.inc', ); $info['metatag_http_equiv'] = array( 'render element' => 'element', 'file' => 'metatag.theme.inc', ); $info['metatag_link_rel'] = array( 'render element' => 'element', 'file' => 'metatag.theme.inc', ); $info['metatag_link_rev'] = array( 'render element' => 'element', 'file' => 'metatag.theme.inc', ); return $info; } /** * Implements hook_ctools_plugin_api(). */ function metatag_ctools_plugin_api($owner, $api) { if ($owner == 'metatag' && $api == 'metatag') { return array('version' => 1); } } /** * Implements hook_hook_info(). */ function metatag_hook_info() { $hooks = array( 'metatag_config_default', 'metatag_config_default_alter', 'metatag_config_delete', 'metatag_config_insert', 'metatag_config_instance_info', 'metatag_config_instance_info_alter', 'metatag_config_load', 'metatag_config_load_presave', 'metatag_config_update', 'metatag_info', 'metatag_info_alter', ); return array_fill_keys($hooks, array('group' => 'metatag')); } /** * Implements hook_permission(). */ function metatag_permission() { $permissions['administer meta tags'] = array( 'title' => t('Administer meta tags'), 'restrict access' => TRUE, 'description' => t('Control the main settings pages and modify per-object meta tags.'), ); $permissions['edit meta tags'] = array( 'title' => t('Edit meta tags'), 'description' => t('Modify meta tags on individual entity records (nodes, terms, users, etc).'), ); // Optional extended edit permissions. if (variable_get('metatag_extended_permissions', FALSE)) { $permissions['edit meta tags']['description'] .= '
' . t('Extended Permissions has been enabled. Roles have the :admin permission will see all meta tags on edit forms, otherwise the permissions below will control which meta tags are available and are needed in addition to Edit meta tags.', array(':admin' => t('Administer meta tags'))); $metatags = metatag_get_info(); foreach ($metatags['tags'] as $metatag_name => $metatag) { $permissions['edit meta tag: ' . $metatag_name] = array( 'title' => t('Extended permission: Edit :tag meta tag', array(':tag' => $metatag['label'])), 'description' => t('Customize the :tag meta tag on individual forms.', array(':tag' => $metatag['label'])), ); } } return $permissions; } /** * Implements hook_menu(). */ function metatag_menu() { $items['admin/config/search/metatags'] = array( 'title' => 'Metatag', 'description' => 'Configure Metatag defaults.', 'page callback' => 'metatag_config_overview', 'access arguments' => array('administer meta tags'), 'file' => 'metatag.admin.inc', ); $items['admin/config/search/metatags/config'] = array( 'title' => 'Defaults', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/config/search/metatags/config/add'] = array( 'title' => 'Add a Metatag default', 'page callback' => 'drupal_get_form', 'page arguments' => array('metatag_config_add_form'), 'access arguments' => array('administer meta tags'), 'file' => 'metatag.admin.inc', 'type' => MENU_LOCAL_ACTION, ); $items['admin/config/search/metatags/config/%metatag_config'] = array( 'title callback' => 'metatag_config_title', 'title arguments' => array(5), 'page callback' => 'drupal_get_form', 'page arguments' => array('metatag_config_edit_form', 5), 'access arguments' => array('administer meta tags'), 'file' => 'metatag.admin.inc', ); $items['admin/config/search/metatags/config/%metatag_config/edit'] = array( 'title' => 'Edit', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/config/search/metatags/config/%metatag_config/enable'] = array( 'title' => 'Enable', 'page callback' => 'metatag_config_enable', 'page arguments' => array(5), 'access callback' => 'metatag_config_access', 'access arguments' => array('enable', 5), 'file' => 'metatag.admin.inc', ); $items['admin/config/search/metatags/config/%metatag_config/disable'] = array( 'title' => 'Disable', 'page callback' => 'metatag_config_disable', 'page arguments' => array(5), 'access callback' => 'metatag_config_access', 'access arguments' => array('disable', 5), 'file' => 'metatag.admin.inc', ); $items['admin/config/search/metatags/config/%metatag_config/revert'] = array( 'title' => 'Revert', 'page callback' => 'drupal_get_form', 'page arguments' => array('metatag_config_delete_form', 5), 'access callback' => 'metatag_config_access', 'access arguments' => array('revert', 5), 'file' => 'metatag.admin.inc', 'type' => MENU_LOCAL_TASK, ); $items['admin/config/search/metatags/config/%metatag_config/delete'] = array( 'title' => 'Delete', 'page callback' => 'drupal_get_form', 'page arguments' => array('metatag_config_delete_form', 5), 'access callback' => 'metatag_config_access', 'access arguments' => array('delete', 5), 'file' => 'metatag.admin.inc', ); $items['admin/config/search/metatags/config/%metatag_config/export'] = array( 'title' => 'Export', 'page callback' => 'metatag_config_export_form', 'page arguments' => array(5), 'access arguments' => array('administer meta tags'), 'file' => 'metatag.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 10, ); $items['admin/config/search/metatags/bulk-revert'] = array( 'title' => 'Bulk revert', 'page callback' => 'drupal_get_form', 'page arguments' => array('metatag_bulk_revert_form'), 'access arguments' => array('administer meta tags'), 'type' => MENU_LOCAL_TASK, 'weight' => 30, 'file' => 'metatag.admin.inc', ); return $items; } /** * Implements hook_flush_caches(). */ function metatag_flush_caches() { return array('cache_metatag'); } /** * Load a metatag configuration record with all the defaults merged in. * * For example, given the configuration instance 'node:article', this function * will load the configuration records for 'node:article', then 'node', and * then finally 'global', with each attempt using an array merge. * * The levels of defaults is arranged by splitting the $instance variable by * the colon character, and always using a 'global' instance at the end. */ function metatag_config_load_with_defaults($instance, $include_global = TRUE) { $defaults = &drupal_static(__FUNCTION__, array()); // Statically cache defaults since they can include multiple levels. $cid = "config:{$instance}" . ($include_global ? ':withglobal' : ':withoutglobal'); if (!isset($defaults[$cid])) { if ($cache = cache_get($cid, 'cache_metatag')) { $defaults[$cid] = $cache->data; } else { $defaults[$cid] = array(); $instances = metatag_config_get_parent_instances($instance, $include_global); $configs = metatag_config_load_multiple($instances); foreach ($instances as $key) { // Ignore disabled configurations. if (!isset($configs[$key]) || !empty($configs[$key]->disabled)) { continue; } // Add config to the defaults array. $defaults[$cid] += $configs[$key]->config; } cache_set($cid, $defaults[$cid], 'cache_metatag'); } } return $defaults[$cid]; } /** * Load a metatag configuration record. */ function metatag_config_load($instance) { $results = metatag_config_load_multiple(array($instance)); return !empty($results[$instance]) ? $results[$instance] : FALSE; } /** * Load multiple metatag configuration records. */ function metatag_config_load_multiple(array $instances) { ctools_include('export'); return ctools_export_load_object('metatag_config', 'names', $instances); } /** * Save a metatag configuration record to the database. */ function metatag_config_save($config) { $config->is_new = empty($config->cid); // Allow modules to alter the configuration before it is saved using // hook_metatag_config_presave(). module_invoke_all('metatag_config_presave', $config); // Update the i18n string if (function_exists('i18n_string_update')) { $instance = $config->instance; foreach ($config->config as $field => $item) { $name = "metatag:" . $instance . ":" . $field; i18n_string_update($name, $item['value']); } } if ($config->is_new) { drupal_write_record('metatag_config', $config); // Allow modules to act upon the record insertion using // hook_metatag_config_insert(). module_invoke_all('metatag_config_insert', $config); } else { drupal_write_record('metatag_config', $config, array('cid')); // Allow modules to act upon the record update using // hook_metatag_config_insert(). module_invoke_all('metatag_config_update', $config); } unset($config->is_new); // Clear any caches. metatag_config_cache_clear(); } /** * Delete a metatag configuration record. */ function metatag_config_delete($instance) { db_delete('metatag_config') ->condition('instance', $instance) ->execute(); // Clear any caches. metatag_config_cache_clear(); } /** * Clear the metatag configuration cache. */ function metatag_config_cache_clear() { cache_clear_all('*', 'cache_metatag', TRUE); drupal_static_reset('metatag_config_load_with_defaults'); drupal_static_reset('metatag_entity_has_metatags'); drupal_static_reset('metatag_entity_supports_metatags'); ctools_include('export'); ctools_export_load_object_reset('metatag_config'); } /** * Load an entity's tags. * * @param $entity_type * The entity type to load * @param $entity_id * The ID of the entity to load * @return * An array of tag data keyed by language. */ function metatag_metatags_load($entity_type, $entity_id) { $metatags = metatag_metatags_load_multiple($entity_type, array($entity_id)); return !empty($metatags) ? reset($metatags) : array(); } /** * Load tags for multiple entities. * * @param $entity_type * The entity type to load * @param $entity_ids * The list of entity IDs * @return * An array of tag data, keyed by ID. */ function metatag_metatags_load_multiple($entity_type, array $entity_ids, array $revision_ids) { // Double check entity IDs are numeric thanks to Entity API module. $entity_ids = array_filter($entity_ids, 'is_numeric'); if (empty($entity_ids)) { return array(); } // Also need to check if the metatag table exists since this condition could // fire before the table has been installed yet. if (!variable_get('metatag_schema_installed', FALSE)) { if (db_table_exists('metatag')) { variable_set('metatag_schema_installed', TRUE); } else { watchdog('metatag', 'The system tried to load metatag data before the schema was fully loaded.', array(), WATCHDOG_WARNING); return array(); } } // Get all translations of tag data for this entity. $result = db_query("SELECT entity_id, data, language FROM {metatag} WHERE (entity_type = :type) AND (entity_id IN (:ids)) AND (revision_id IN (:vids))", array( ':type' => $entity_type, ':ids' => $entity_ids, ':vids' => $revision_ids, )); // Marshal it into an array keyed by entity ID. Each value is an array of // translations keyed by language code. $metatags = array(); while ($record = $result->fetchObject()) { $metatags[$record->entity_id][$record->language] = unserialize($record->data); } return $metatags; } /** * Save an entity's tags. * * @param $entity_type * The entity type to load * @param $entity_id * The entity's ID * @param $revision_id * The entity's VID. * @param $metatags * All of the tag information * @param $language * The language of the translation set */ function metatag_metatags_save($entity_type, $entity_id, $revision_id, $metatags, $langcode) { // If no language assigned, use the has-no-language language. if (empty($langcode)) { $langcode = LANGUAGE_NONE; } // Check that $entity_id is numeric because of Entity API and string IDs. if (!is_numeric($entity_id)) { return; } // If the vid was not passed in, use the entity_id. if (empty($revision_id)) { $revision_id = $entity_id; } // Ensure the data saves during node_save(). if (isset($metatags[$langcode])) { // There are certain occasions when the old data and the new data are // *both* added to the $metatags array, in this case throw away the language // data. $lang_data = $metatags[$langcode]; unset($metatags[$langcode]); if (empty($metatags)) { $metatags = $lang_data; } } // Allow other modules to alter the meta tags prior to saving using // hook_metatag_presave(). foreach (module_implements('metatag_presave') as $module) { $function = "{$module}_metatag_presave"; $function($metatags, $entity_type, $entity_id, $revision_id, $langcode); } if (empty($metatags)) { // If the data array is empty, there is no data to actually save, so // just delete the record from the database. db_delete('metatag') ->condition('entity_type', $entity_type) ->condition('entity_id', $entity_id) ->condition('language', $langcode) ->execute(); } else { // Otherwise save the data for this entity. db_merge('metatag') ->key(array( 'entity_type' => $entity_type, 'entity_id' => $entity_id, 'language' => $langcode, 'revision_id' => $revision_id, )) ->fields(array( 'data' => serialize($metatags), )) ->execute(); } // Clear cached data. metatag_metatags_cache_clear($entity_type, $entity_id); } /** * Delete an entity's tags. * * @param $entity_type * The entity type * @param $entity_id * The entity's ID * @param $revision_id * The entity's VID. * @param $langcode * The language ID of the entry to delete. If left blank, all language * entries for this entity will be deleted. */ function metatag_metatags_delete($entity_type, $entity_id, $revision_id = NULL, $langcode = NULL) { return metatag_metatags_delete_multiple($entity_type, array($entity_id), array($revision_id), $langcode); } /** * Delete multiple entities' tags. * * @param $entity_type * The entity type * @param $entity_ids * The list of IDs * @param $revision_id * An optional list of VIDs, if omitted all revisions will be deleted. * @param $langcode * The language ID of the entities to delete. If left blank, all language * entries for the enities will be deleted. */ function metatag_metatags_delete_multiple($entity_type, array $entity_ids, array $revision_ids, $langcode = NULL) { // Double check entity IDs are numeric thanks to Entity API module. $entity_ids = array_filter($entity_ids, 'is_numeric'); if ($metatags = metatag_metatags_load_multiple($entity_type, $entity_ids, $revision_ids)) { $transaction = db_transaction(); try { // Let other modules know about the records being deleted using // hook_metatag_metatags_delete(). module_invoke_all('metatag_metatags_delete', $entity_type, $entity_ids, $revision_ids, $langcode); // Set the entity to delete. $query = db_delete('metatag') ->condition('entity_type', $entity_type) ->condition('entity_id', $entity_ids, 'IN'); // Optionally delete a specific revision. if (!empty($revision_ids)) { $query->condition('revision_id', $revision_ids, 'IN'); } // Specify a language if there is one. if (!empty($langcode)) { $query->condition('language', $langcode); } // Perform the deletion(s). $query->execute(); // Clear cached data. metatag_metatags_cache_clear($entity_type, $entity_ids); } catch (Exception $e) { $transaction->rollback(); watchdog_exception('metatag', $e); throw $e; } } } function metatag_metatags_cache_clear($entity_type, $entity_id = NULL) { if (empty($entity_id)) { cache_clear_all("output:$entity_type", 'cache_metatag', TRUE); } else { $entity_ids = (array) $entity_id; foreach ($entity_ids as $entity_id) { cache_clear_all("output:$entity_type:$entity_id", 'cache_metatag', TRUE); } } } /** * Implements hook_entity_load(). */ function metatag_entity_load($entities, $entity_type) { // get the revision_ids $revision_ids = array(); //since some entities do not have revisions, set the vid to the id foreach ($entities as $key => $entity) { list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); if (!$revision_id) { $revision_id = $entity_id; } $revision_ids[] = $revision_id; } // Wrap this in a try-catch block to work around occasions when the schema // hasn't been updated yet. try { if (metatag_entity_supports_metatags($entity_type)) { $metatags = metatag_metatags_load_multiple($entity_type, array_keys($entities), $revision_ids); foreach ($entities as $entity_id => $entity) { $entities[$entity_id]->metatags = isset($metatags[$entity_id]) ? $metatags[$entity_id] : array(); } } } catch (Exception $e) { watchdog('metatag', 'Error loading meta tag data, do the database updates need to be run? The error occurred when loading record(s) %ids for the %type entity type. The error message was: %error', array('@update' => base_path() . 'update.php', '%ids' => implode(', ', array_keys($entities)), '%type' => $entity_type, '%error' => $e->getMessage()), WATCHDOG_CRITICAL); // Don't display the same message twice for Drush. if (php_sapi_name() != 'cli') { drupal_set_message(t('Error loading meta tag data, do the database updates need to be run?', array('@update' => base_path() . 'update.php')), 'error'); } } } /** * Implements hook_entity_insert(). */ function metatag_entity_insert($entity, $entity_type) { if (isset($entity->metatags)) { list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); // Determine the entity's language. $langcode = entity_language($entity_type, $entity); // Unfortunately due to how core works, the normal entity_language() // function returns 'und' instead of the node's language during node // creation. if ((empty($langcode) || $langcode == LANGUAGE_NONE) && !empty($entity->language)) { $langcode = $entity->language; } // If no language was still found, use the 'no language' value. if (empty($langcode)) { $langcode = LANGUAGE_NONE; } metatag_metatags_save($entity_type, $entity_id, $revision_id, $entity->metatags, $langcode); } } /** * Implements hook_entity_update(). */ function metatag_entity_update($entity, $entity_type) { if (!metatag_entity_supports_metatags($entity_type)) { return; } list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); if (isset($entity->metatags)) { // Determine the entity's language. $new_language = metatag_entity_get_language($entity_type, $entity); // Determine the language for this entity object. if (isset($entity->original)) { $old_language = metatag_entity_get_language($entity_type, $entity->original); // If the language has changed then remove the old one. When a new // translation is being saved using Entity Translation both values will // be the same, so this is safe to do. if ($old_language != $new_language) { db_delete('metatag') ->condition('entity_type', $entity_type) ->condition('entity_id', $entity_id) ->condition('language', $old_language) ->execute(); } } // Save the record. metatag_metatags_save($entity_type, $entity_id, $revision_id, $entity->metatags, $new_language); } else { // Still ensure the meta tag output is cached. metatag_metatags_cache_clear($entity_type, $entity_id); } } /** * Implements hook_entity_delete(). */ function metatag_entity_delete($entity, $entity_type) { list($entity_id) = entity_extract_ids($entity_type, $entity); metatag_metatags_delete($entity_type, $entity_id); } /** * Implements hook_field_attach_delete_revision(). */ function metatag_field_attach_delete_revision($entity_type, $entity) { list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); metatag_metatags_delete($entity_type, $entity_id, $revision_id); } /** * Implements hook_taxonomy_term_view_alter(). */ function metatag_taxonomy_term_view_alter(&$build, &$entity_type) { // This is only needed if hook_entity_view has not been added to core. // @see http://drupal.org/node/1067120 if (isset($build['#term']) && !function_exists('taxonomy_term_view_multiple')) { $entity = taxonomy_term_load($build['#term']->tid); metatag_entity_view($entity, $entity_type, 'full', NULL, TRUE); } } /** * Implements hook_entity_view(). * * Provides additional argument to allow the display to be forced, to work * around problems elsewhere in the APIs. */ function metatag_entity_view($entity, $entity_type, $view_mode, $langcode, $force = FALSE) { // Only run this function once per page load. static $i_will_say_this_only_once = FALSE; // Only proceed if this entity object is the page being viewed. if (_metatag_entity_is_page($entity_type, $entity)) { // Some API calls need to force the data loading. if (!$force) { // Only run this function once per page load. if ($i_will_say_this_only_once) { return; } $i_will_say_this_only_once = TRUE; } // If this entity object isn't allowed meta tags, skip it. if (!metatag_entity_has_metatags($entity_type, $entity)) { return; } // Obbtain some details of the entity that are needed elsewhere. list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); $instance = "{$entity_type}:{$bundle}"; // Determine the language this entity actually uses. $entity_language = metatag_entity_get_language($entity_type, $entity); // The requested language is different to the entity's language, look for // a language elsewhere. if ($entity_language != $langcode) { // No language was defined for the entity. if ($entity_language == LANGUAGE_NONE) { $langcode = LANGUAGE_NONE; } else { $enabled_languages = field_content_languages(); foreach (field_language($entity_type, $entity) as $field => $lang) { // Only accept actual language values that are properly enabled. if ($lang != LANGUAGE_NONE && in_array($lang, $enabled_languages)) { $langcode = $lang; } } } } // All applicable pieces for this current page. $cid_parts = array( 'entity_type' => $entity_type, 'bundle' => $bundle, 'entity_id' => $entity_id, 'view_mode' => $view_mode, 'langcode' => $langcode, 'url' => $GLOBALS['base_url'] . base_path() . current_path(), ); // Allow each page in a sequence to have different values. if (isset($_GET['page'])) { $cid_parts['page'] = $_GET['page']; } // Allow other modules to alter the page parts using // hook_metatag_page_cache_cid_parts_alter(). drupal_alter('metatag_page_cache_cid_parts', $cid_parts); $cid = "output:{$entity_type}:{$entity_id}:{$revision_id}:{$langcode}:" . hash('sha256', serialize($cid_parts)); if ($cache = cache_get($cid, 'cache_metatag')) { $output = $cache->data; } else { // Separate the meta tags. $metatags = isset($entity->metatags) ? $entity->metatags : array(); // Build options for meta tag rendering. $options = array( 'entity' => $entity, 'entity_type' => $entity_type, 'view_mode' => $view_mode, ); // Ensure we actually pass a language object rather than language code. $languages = language_list(); if (isset($languages[$langcode])) { $options['language'] = $languages[$langcode]; } // Reload the entity object from cache as it may have been altered. $token_type = token_get_entity_mapping('entity', $entity_type); $entities = entity_load($entity_type, array($entity_id)); $options['token data'][$token_type] = $entities[$entity_id]; $options['entity'] = $entities[$entity_id]; // Render the metatags and save to the cache. $output = metatag_metatags_view($instance, $metatags, $options); cache_set($cid, $output, 'cache_metatag'); } // We need to register the term's metatags, so we can later fetch them. // @see metatag_page_build(). metatag_page_set_metatags($instance, $output); } } /** * Build a renderable array of meta tag output. * * @param string $instance * The configuration instance key of the meta tags to use, e.g. * "node:article". * @param array $metatags * An array of meta tag data. * @param array $options * (optional) An array of options including the following keys and values: * - language: A language object. * - token data: An array of data to pass into token_replace() during * meta tag value generation. */ function metatag_metatags_view($instance, array $metatags = array(), array $options = array()) { $output = array(); // Convert language codes to a language object. if (isset($options['language']) && is_string($options['language'])) { $languages = language_list(); $options['language'] = isset($languages[$options['language']]) ? $languages[$options['language']] : NULL; } // If there are any tags, determine the translation to display. if (!empty($metatags)) { // Get the display language; default to the entity's language. if (isset($options['language']) && isset($options['language']->language) && isset($metatags[$options['language']->language])) { $metatags = $metatags[$options['language']->language]; } // If no language requested, use the no-language value. elseif (!empty($metatags[LANGUAGE_NONE])) { $metatags = $metatags[LANGUAGE_NONE]; } else { $metatags = array(); } } // Add any default tags to the mix. $metatags += metatag_config_load_with_defaults($instance); $options['instance'] = $instance; foreach ($metatags as $metatag => $data) { if ((!empty($data['value']) || (isset($data['value']) && is_numeric($data['value']))) && $metatag_instance = metatag_get_instance($metatag, $data)) { $output[$metatag] = $metatag_instance->getElement($options); } } // Allow the output meta tags to be modified using // hook_metatag_metatags_view_alter(). drupal_alter('metatag_metatags_view', $output, $instance); return $output; } function metatag_metatags_values($instance, array $metatags = array(), array $options = array()) { $values = array(); // Apply defaults to the data for each language. foreach ($metatags as $language => $metatag) { $metatags[$language] += metatag_config_load_with_defaults($instance); } // Generate output only if we have a valid language. if (isset($options['language']) && is_string($options['language']) && isset($metatags[$options['language']])) { $language = $options['language']; // Convert language codes to a language object. $languages = language_list(); $options['language'] = isset($languages[$language]) ? $languages[$language] : NULL; $options['instance'] = $instance; // Get output elements. foreach ($metatags[$language] as $metatag => $data) { if ($metatag_instance = metatag_get_instance($metatag, $data)) { $values[$metatag] = $metatag_instance->getValue($options); } } } return array_filter($values, 'drupal_strlen'); } /** * Build a FAPI array for editing meta tags. * * @param array $form * The current FAPI array. * @param string $instance * The configuration instance key of the metatags to use, e.g. "node:article". * @param array $metatags * An array of metatag data. * @param array $options * (optional) An array of options including the following keys and values: * - token types: An array of token types to be passed to theme_token_tree(). */ function metatag_metatags_form(array &$form, $instance, array $metatags = array(), array $options = array()) { $info = metatag_get_info(); if (empty($info['tags'])) { return; } // Merge in the default options. $options += array( 'token types' => array(), 'defaults' => metatag_config_load_with_defaults($instance), 'instance' => $instance, ); $form['metatags'] = array( '#type' => 'fieldset', '#title' => t('Meta tags'), '#multilingual' => TRUE, '#collapsible' => TRUE, '#collapsed' => TRUE, '#tree' => TRUE, '#access' => user_access('edit meta tags') || user_access('administer meta tags'), '#weight' => 40, '#attributes' => array( 'class' => array('metatags-form'), ), '#metatag_defaults' => $options['defaults'], ); // Only support vertical tabs if there is a vertical tab element. foreach (element_children($form) as $key) { if (isset($form[$key]['#type']) && $form[$key]['#type'] == 'vertical_tabs') { $form['metatags']['#group'] = $key; $form['metatags']['#attached']['js']['vertical-tabs'] = drupal_get_path('module', 'metatag') . '/metatag.vertical-tabs.js'; break; } } // Merge in the default meta tag configurations. $metatags += $options['defaults']; // This will be used later. $group_metatag_access = array(); // Build the form for each metatag. foreach ($info['tags'] as $metatag => $metatag_info) { // @todo Replace context matching with hook_metatag_access(). if (isset($options['context']) && isset($metatag_info['context'])) { if (!in_array($options['context'], $metatag_info['context'])) { continue; } } $metatag_instance = metatag_get_instance($metatag, isset($metatags[$metatag]) ? $metatags[$metatag] : array()); if (empty($metatag_instance)) { continue; } // Get the form element from the meta tag class. $metatag_form = $metatag_instance->getForm($options); // Add a default value form element. if (isset($options['defaults'][$metatag]['value'])) { $metatag_form['default'] = array( '#type' => 'hidden', '#value' => $options['defaults'][$metatag]['value'], ); } // Optional extended edit permissions. if (variable_get('metatag_extended_permissions', FALSE)) { $metatag_form['#access'] = user_access('edit meta tag: ' . $metatag) || user_access('administer meta tags'); } else { $metatag_form['#access'] = $form['metatags']['#access']; } if (!empty($metatag_info['group'])) { $group_key = $metatag_info['group']; if (isset($info['groups'][$group_key]['label']) && !isset($form['metatags'][$group_key])) { $group = $info['groups'][$group_key] + array('form' => array(), 'description' => NULL); $form['metatags'][$group_key] = $group['form'] + array( '#type' => 'fieldset', '#title' => check_plain($group['label']), '#description' => filter_xss($group['description']), '#collapsible' => TRUE, '#collapsed' => TRUE, ); } $form['metatags'][$group_key][$metatag] = $metatag_form + array('#parents' => array('metatags', $metatag)); // Hide the fieldset itself if there is not at least one of the meta tag // fields visible. if (variable_get('metatag_extended_permissions', FALSE)) { $form['metatags'][$group_key]['#access'] = count(element_get_visible_children($form['metatags'][$group_key])) > 0; } else { $form['metatags'][$group_key]['#access'] = $form['metatags']['#access']; } // Structure the access parameter into this array, and make use of it // later when we move on. Besides, this foreach is getting heavy. $group_metatag_access[$group_key] = $form['metatags'][$group_key]['#access']; } else { $form['metatags'][$metatag] = $metatag_form; } } // Hide the fieldset itself if there is not at least one of the meta tag // fields visible; only bother checking this if the user had edit access in // the first place. if ($form['metatags']['#access'] && variable_get('metatag_extended_permissions', FALSE)) { $form['metatags']['#access'] = count(element_get_visible_children($form['metatags'])) > 0; } // Check the #access of each group. If it passed, we display options for // tokens. By this we update the #description of each group. if ($form['metatags']['#access']) { // Built the token list. $token_listing_link = theme('token_tree', array('token_types' => $options['token types'], 'dialog' => TRUE)); // Add the token list to the top of the fieldset. $form['metatags']['#description'] = $token_listing_link; // Check if each meta tag group is being displayed. if (!empty($group_metatag_access)) { foreach ($group_metatag_access as $group_key => $token_access) { if ($token_access) { // Update the description. if (isset($form['metatags'][$group_key]['#description'])) { $form['metatags'][$group_key]['#description'] .= '
'; } else { $form['metatags'][$group_key]['#description'] = ''; } $form['metatags'][$group_key]['#description'] .= $token_listing_link; } } } } // Add a submit handler to compare the submitted values against the deafult // values. $form += array('#submit' => array()); array_unshift($form['#submit'], 'metatag_metatags_form_submit'); } /** * Form submit handler; unset meta tag values that equal their default values. */ function metatag_metatags_form_submit($form, &$form_state) { if (!empty($form_state['values']['metatags']) && !empty($form['metatags']['#metatag_defaults'])) { metatag_filter_values_from_defaults($form_state['values']['metatags'], $form['metatags']['#metatag_defaults']); } } /** * Implements hook_field_extra_fields(). */ function metatag_field_extra_fields() { $extra = array(); foreach (entity_get_info() as $entity_type => $entity_info) { foreach (array_keys($entity_info['bundles']) as $bundle) { if (metatag_entity_supports_metatags($entity_type, $bundle)) { $extra[$entity_type][$bundle]['form']['metatags'] = array( 'label' => t('Meta tags'), 'description' => t('Meta tag module form elements.'), 'weight' => 40, ); } } } return $extra; } /** * Check if an individual entity has meta tags defined, or has defaults. * * @param string $entity_type * An entity type. * @param object $entity * An entity object. * * @return boolean * TRUE or FALSE if the entity should have a form for or process meta tags. */ function metatag_entity_has_metatags($entity_type, $entity) { // If an entity has custom meta tags assigned, then we should return TRUE. if (!empty($entity->metatags)) { return TRUE; } // Otherwise, check to see if there exists any enabed configuration for // either the entity type, or bundle (even if the configuration is empty). // If no configuration exists, then we should not be displaying the meta tag // forms or processing meta tags on entity view. $config_exists = &drupal_static(__FUNCTION__, array()); list( , , $bundle) = entity_extract_ids($entity_type, $entity); // Do not pretend to have metatags when the bundle does not support them. if (!metatag_entity_supports_metatags($entity_type, $bundle)) { return FALSE; } $instance = "{$entity_type}:{$bundle}"; if (!isset($config_exists[$instance])) { // Check if the intstance or its parents (excluding global) are enabled. $config_exists[$instance] = metatag_config_is_enabled($instance, TRUE, FALSE); } return isset($config_exists[$instance]); } /** * Check whether the requested entity type (and bundle) support metatag. * * By default this will be FALSE, support has to be specifically enabled by * assigning 'metatag' => TRUE within the hook_entity_info() definition for the * entity. */ function metatag_entity_supports_metatags($entity_type = NULL, $bundle = NULL) { $entity_types = &drupal_static(__FUNCTION__); if (!isset($entity_types)) { $entity_types = array(); foreach (entity_get_info() as $entity_type_key => $entity_info) { if (empty($entity_info['metatags'])) { $entity_types[$entity_type_key] = FALSE; continue; } $entity_types[$entity_type_key] = array(); foreach ($entity_info['bundles'] as $bundle_key => $bundle_info) { $entity_types[$entity_type_key][$bundle_key] = !isset($bundle_info['metatags']) || !empty($bundle_info['metatags']); } } } if (isset($entity_type) && isset($bundle)) { return isset($entity_types[$entity_type][$bundle]) ? $entity_types[$entity_type][$bundle] : FALSE; } elseif (isset($entity_type)) { return isset($entity_types[$entity_type]) ? ($entity_types[$entity_type] !== FALSE) : FALSE; } return $entity_types; } /** * Implements hook_entity_info_alter(). * * Enables Metatag support for the core entities. */ function metatag_entity_info_alter(&$info) { $defaults['node'] = array( 'path' => 'node/%node', 'metatags' => TRUE, ); $defaults['taxonomy_term'] = array( 'path' => 'taxonomy/term/%taxonomy_term', 'metatags' => TRUE, ); if (module_exists('forum') && ($revision_id = variable_get('forum_nav_vocabulary', 0)) && $vocabulary = taxonomy_vocabulary_load($revision_id)) { $defaults['taxonomy_term']['bundles'][$vocabulary->machine_name]['path'] = 'forum/%taxonomy_term'; } $defaults['user'] = array( 'path' => 'user/%user', 'metatags' => TRUE, ); foreach ($defaults as $key => $entity_defaults) { if (isset($info[$key])) { $info[$key] = drupal_array_merge_deep($entity_defaults, $info[$key]); } } } /** * Given a path determine if it is an entity default path. * * @param $path * The internal path. The id of the entity should be in the string as '[id]'. * @return * An array with the entity type and the loaded entity object. */ function metatag_load_entity_from_path($path) { $entity_paths = &drupal_static(__FUNCTION__); $result = FALSE; if (!isset($entity_paths)) { $entity_paths = array(); foreach (entity_get_info() as $entity_type => $entity_info) { if (isset($entity_info['default path'])) { $default_path = $entity_info['default path']; $default_path = preg_quote($default_path, '/'); $default_path = str_replace('\[id\]', '(\d+)', $default_path); $entity_paths[$entity_type] = $default_path; } } } foreach ($entity_paths as $entity_type => $default_path) { if (preg_match("/^{$default_path}$/", $path, $matches)) { if ($entity = entity_load($entity_type, array($matches[1]))) { $result = array('entity_type' => $entity_type, 'entity' => reset($entity)); } break; } } // Allow other modules to customize the data using // hook_metatag_load_entity_from_path_alter(). drupal_alter('metatag_load_entity_from_path', $path, $result); return $result; } /** * Add meta tags to be added later with metatag_page_build(). * * @param string $instance * The configuration instance key of the meta tags, e.g. "node:article". * @param array $metatags * An array of meta tags from metatag_metatags_view(). */ function metatag_page_set_metatags($instance, $metatags) { $page_metatags = &drupal_static(__FUNCTION__, array()); $page_metatags[$instance] = $metatags; } /** * Retrieve the array of met tags to be added with metatag_page_build(). */ function metatag_page_get_metatags() { // @todo Add alter to this result? return drupal_static('metatag_page_set_metatags', array()); } /** * Implements hook_page_build(). */ function metatag_page_build(&$page) { // Ensure these arrays exist, otherwise several use cases will fail. if (!isset($page['content']) || !is_array($page['content'])) { $page['content'] = array(); } if (!isset($page['content']['metatags']) || !is_array($page['content']['metatags'])) { $page['content']['metatags'] = array(); } // The front page has special consideration. $instance = 'global:frontpage'; if (drupal_is_front_page() && metatag_config_is_enabled($instance)) { $instance = 'global:frontpage'; // These two parts are sufficient given that the homepage is unique. $cid_parts = array( 'langcode' => $GLOBALS['language_content']->language, 'url' => $GLOBALS['base_url'] . base_path() . '', ); // Allow each page in a sequence to have different values. if (isset($_GET['page'])) { $cid_parts['page'] = $_GET['page']; } // Allow other modules to customize the data using // hook_metatag_page_cache_cid_parts_alter(). drupal_alter('metatag_page_cache_cid_parts', $cid_parts); $cid = "output:{$instance}:" . hash('sha256', serialize($cid_parts)); if ($cache = cache_get($cid, 'cache_metatag')) { $metatags = $cache->data; } else { $metatags = metatag_metatags_view($instance, array()); cache_set($cid, $metatags, 'cache_metatag'); } $page['content']['metatags'][$instance] = $metatags; } // Load any meta tags assigned via metatag_page_set_metatags(). Note: this // must include the necessary defaults. else { $page['content']['metatags'] += metatag_page_get_metatags(); } // If no meta tags were loaded, and this is not an admin path, at least load // the global defaults. This may be disabled, see README.txt for details. if (empty($page['content']['metatags']) && variable_get('metatag_load_all_pages', TRUE) && !path_is_admin(current_path())) { $instance = 'global'; // These two parts are sufficient given that the homepage is unique. $cid_parts = array( 'langcode' => $GLOBALS['language_content']->language, 'url' => $GLOBALS['base_url'] . request_uri(), ); // Allow each page in a sequence to have different values. if (isset($_GET['page'])) { $cid_parts['page'] = $_GET['page']; } // Allow other modules to customize the data using // hook_metatag_page_cache_cid_parts_alter(). drupal_alter('metatag_page_cache_cid_parts', $cid_parts); $cid = "output:{$instance}:" . hash('sha256', serialize($cid_parts)); if ($cache = cache_get($cid, 'cache_metatag')) { $metatags = $cache->data; } else { $metatags = metatag_metatags_view($instance, array()); cache_set($cid, $metatags, 'cache_metatag'); } $page['content']['metatags'][$instance] = $metatags; } } /** * Returns whether the current page is the page of the passed in entity. * * @param $entity_type * The entity type; e.g. 'node' or 'user'. * @param $entity * The entity object. * * @return * TRUE if the current page is the page of the specified entity, or FALSE * otherwise. */ function _metatag_entity_is_page($entity_type, $entity) { $uri = entity_uri($entity_type, $entity); return !empty($uri['path']) && current_path() == $uri['path']; } /** * Implements hook_field_attach_rename_bundle(). */ function metatag_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { $instance_old = $entity_type . ':' . $bundle_old; $instance_new = $entity_type . ':' . $bundle_new; if ($config = metatag_config_load($instance_old)) { $config->instance = $instance_new; metatag_config_save($config); metatag_config_delete($instance_old); } } /** * Implements hook_field_attach_delete_bundle(). */ function metatag_field_attach_delete_bundle($entity_type, $bundle) { $instance = $entity_type . ':' . $bundle; metatag_config_delete($instance); } /** * Implements hook_field_attach_form(). */ function metatag_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { if (!metatag_entity_has_metatags($entity_type, $entity)) { return; } // Entity_Translation will trigger this hook again, skip it. if (!empty($form_state['entity_translation']['is_translation'])) { return; } list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); $instance = "{$entity_type}:{$bundle}"; // Grab the meta tags for display in the form if there are any. if (!empty($entity->metatags)) { // Identify the language to use with this entity. $entity_language = metatag_entity_get_language($entity_type, $entity); // If this is a new translation using Entity Translation, load the meta // tags from the entity's original language. if (module_exists('entity_translation') && empty($form['#entity_translation_source_form']) && ($handler = entity_translation_entity_form_get_handler($form, $form_state)) && isset($entity->metatags[$handler->getSourceLanguage()])) { $metatags = $entity->metatags[$handler->getSourceLanguage()]; } // Determine from where we should get the tags. elseif (isset($entity->metatags[$langcode])) { // Set the tags to the translation set matching that of the form. $metatags = $entity->metatags[$langcode]; } // There is no translation for this entity's tags in the current // language. Instead, display tags in the language of the entity, the // source language of translations. The will provide translators with the // original text to translate. elseif (isset($entity->metatags[$entity_language])) { $metatags = $entity->metatags[$entity_language]; } // This is a preview so set the tags to the raw submission data. No // language has been set. else { $metatags = $entity->metatags; } } else { $metatags = array(); } $options['token types'] = array(token_get_entity_mapping('entity', $entity_type)); $options['context'] = $entity_type; // Allow hook_metatag_token_types_alter() to modify the defined tokens. drupal_alter('metatag_token_types', $options); // @todo Remove metatag_form_alter() when http://drupal.org/node/1284642 is fixed in core. //metatag_metatags_form($form, $instance, $metatags, $options); $form['#metatags'] = array( 'instance' => $instance, 'metatags' => $metatags, 'options' => $options, ); } /** * Implements hook_form_alter(). * * @todo Remove this when http://drupal.org/node/1284642 is fixed in core. */ function metatag_form_alter(&$form, $form_state, $form_id) { if (!empty($form['#metatags']) && !isset($form['metatags'])) { extract($form['#metatags']); metatag_metatags_form($form, $instance, $metatags, $options); unset($form['#metatags']); } } /** * Get the meta tag information array of a meta tag. * * @param $metatag * The meta tag name, e.g. description, for which the info shall be returned, * or NULL to return an array with info about all meta tags. */ function metatag_get_info($type = NULL, $name = NULL) { // Use the advanced drupal_static() pattern, since this is called very often. static $drupal_static_fast; if (!isset($drupal_static_fast)) { $drupal_static_fast['metatag_info'] = &drupal_static(__FUNCTION__); } $info = &$drupal_static_fast['metatag_info']; global $language; if (!isset($info)) { // hook_metatag_info() includes translated strings, so each language is cached // separately. $cid = 'info:' . $language->language; if ($cache = cache_get($cid, 'cache_metatag')) { $info = $cache->data; } else { // Obtain all metatag specs defined in other modules using // hook_metatag_info(). $info = module_invoke_all('metatag_info'); $info += array('tags' => array(), 'groups' => array()); // Merge in default values. foreach ($info['tags'] as $key => $data) { $info['tags'][$key] += array( // Merge in default values. 'name' => $key, 'class' => 'DrupalTextMetaTag', ); } // Let other modules alter the entity info using // hook_metatag_info_alter(). drupal_alter('metatag_info', $info); cache_set($cid, $info, 'cache_metatag'); } } if (isset($type) && isset($name)) { return isset($info[$type][$name]) ? $info[$type][$name] : FALSE; } elseif (isset($type)) { return isset($info[$type]) ? $info[$type] : array(); } else { return $info; } } function metatag_get_instance($metatag, array $data = array()) { $info = metatag_get_info('tags', $metatag); if (!empty($info['class']) && class_exists($info['class'])) { $class = $info['class']; return new $class($info, $data); } } /** * Return the string value of a meta tag. * * @param $metatag * The meta tag string. * @param $data * The array of data for the meta tag class instance. * @param $options * An optional array of additional options to pass to the getValue() method * of the meta tag class instance. * - raw: A boolean if TRUE will not perform token replacement. * * @return * A string value. */ function metatag_get_value($metatag, array $data, array $options = array()) { $value = ''; if ($metatag_instance = metatag_get_instance($metatag, $data)) { $options["instance"] = $metatag; $value = $metatag_instance->getValue($options); } return $value; } /** * Set a variable to be altered in metatag_preprocess_html(). * * @see metatag_get_preprocess_variables() * @see metatag_preprocess_html() * @see metatag_preprocess_maintenance_page() */ function metatag_set_preprocess_variable($hook, $variable, $value) { $variables = &drupal_static(__FUNCTION__, array()); $variables[$hook][$variable] = $value; } /** * Return an array of variables to be altered in preprocess functions. * * @see metatag_set_preprocess_variable() * @see metatag_preprocess_html() * @see metatag_preprocess_maintenance_page() */ function metatag_get_preprocess_variables($hook) { $variables = drupal_static('metatag_set_preprocess_variable', array()); return isset($variables[$hook]) ? $variables[$hook] : array(); } /** * Implements hook_preprocess_html(). */ function metatag_preprocess_html(&$variables) { foreach (metatag_get_preprocess_variables('html') as $variable => $value) { $variables[$variable] = $value; } } /** * Implements hook_preprocess_maintenance_page(). */ function metatag_preprocess_maintenance_page(&$variables) { foreach (metatag_get_preprocess_variables('html') as $variable => $value) { $variables[$variable] = $value; } } /** * Implements hook_html_head_alter(). */ function metatag_html_head_alter(&$elements) { // Remove duplicate link tags if found. $metatags = metatag_get_info('tags'); foreach (array_keys($metatags) as $name) { if (!isset($elements['metatag_' . $name]) || $elements['metatag_' . $name]['#tag'] != 'link') { // Only check for link tags added by the metatags module. continue; } foreach (array_keys($elements) as $key) { if (strpos($key, 'drupal_add_html_head_link:' . $name . ':') === 0) { unset($elements[$key]); } } } // Remove the default generator meta tag. unset($elements['system_meta_generator']); } function metatag_metatag_get_form($metatag, array $data = array(), array $options = array()) { $instance = metatag_get_instance($metatag, $data); return $instance->getForm($options); } function metatag_config_instance_info($instance = NULL) { global $language; $info = &drupal_static(__FUNCTION__); // hook_metatag_info() includes translated strings, so each language is cached // separately. $cid = 'metatag:config:instance:info:' . $language->language; if (!isset($info)) { if ($cache = cache_get($cid, 'cache_metatag')) { $info = $cache->data; } else { // Allow modules to act upon the record insertion using // hook_metatag_config_instance_info(). $info = module_invoke_all('metatag_config_instance_info'); // Allow other modules to customize the data using // hook_metatag_config_instance_info_alter(). drupal_alter('metatag_config_instance_info', $info); cache_set($cid, $info, 'cache_metatag'); } } if (isset($instance)) { return isset($info[$instance]) ? $info[$instance] : FALSE; } else { return $info; } } /** * Filter out meta tag values that equal the default values. * * @todo Use information in $values[$metatag]['default'] rather than a $defaults parameter. */ function metatag_filter_values_from_defaults(array &$values, array $defaults = array()) { foreach ($values as $metatag => $data) { $default = isset($data['default']) ? $data['default'] : (isset($defaults[$metatag]['value']) ? $defaults[$metatag]['value'] : NULL); if (isset($default) && isset($data['value']) && $default === $data['value']) { // Meta tag has a default, and it matches user-submitted value. unset($values[$metatag]); } elseif (!isset($default) && (is_string($data['value']) && !drupal_strlen($data['value']) || (is_array($data['value']) && !array_filter($data['value'])))) { // Metatag does not have a default, and user did not submit a value. unset($values[$metatag]); } if (isset($values[$metatag]['default'])) { // Unset the default hidden value. unset($values[$metatag]['default']); } } } /** * Return all the parents of a given configuration instance. * * @param $instance * A meta tag configuration instance. * * @return * An array of instances starting with the $instance parameter, with the end * of the array being the global instance. */ function metatag_config_get_parent_instances($instance, $include_global = TRUE) { $parents = array(); $segments = explode(':', $instance); while (count($segments) > 0) { $parents[] = implode(':', $segments); array_pop($segments); } if ($include_global && end($parents) !== 'global') { $parents[] = 'global'; } reset($parents); return $parents; } /** * Get the proper label of a configuration instance. * * @param $instance * A meta tag configuration instance. */ function metatag_config_instance_label($instance) { $labels = &drupal_static(__FUNCTION__, array()); if (!isset($labels[$instance])) { $instance_parts = explode(':', $instance); $instance_part = array_pop($instance_parts); if ($context = metatag_config_instance_info($instance)) { $labels[$instance] = $context['label']; } else { $labels[$instance] = t('Unknown (@instance)', array('@instance' => $instance_part)); } // Normally the following would use metatag_config_get_parent_instances() // but since we already sliced the instance by separator and removed the // last segment, putting the array back together gives us this instance's // parent. if (!empty($instance_parts)) { $labels[$instance] = metatag_config_instance_label(implode(':', $instance_parts)) . ': ' . $labels[$instance]; } } return $labels[$instance]; } /** * Title callback for meta tag configuration instances. */ function metatag_config_title($config) { return metatag_config_instance_label($config->instance); } /** * Access callback for meta tag configuration instances. */ function metatag_config_access($op, $config = NULL) { if (!user_access('administer meta tags')) { return FALSE; } if ($op == 'enable') { return !empty($config->disabled); } elseif ($op == 'disable') { return empty($config->disabled); } elseif ($op == 'delete') { return ($config->export_type & EXPORT_IN_DATABASE) && !($config->export_type & EXPORT_IN_CODE); } elseif ($op == 'revert') { return ($config->export_type & EXPORT_IN_DATABASE) && ($config->export_type & EXPORT_IN_CODE); } return FALSE; } /** * Checks if a metatag configuration record is enabled. * * @param string $instance * The configuration instance machine name. * * @return bool * TRUE if the configuration is enabled, or FALSE otherwise. */ function metatag_config_is_enabled($instance, $include_defaults = FALSE, $include_global = TRUE) { if ($include_defaults) { return (bool) metatag_config_load_with_defaults($instance, $include_global); } else { $config = metatag_config_load($instance); return !empty($config) && empty($config->disabled); } } /** * Wrapper around entity_language() to use LANGUAGE_NONE if the entity does not * have a language assigned. * * @param $entity_type * An entity type's machine name. * @param $entity * The entity to review; * * @return * A string indicating the language code to be used. */ function metatag_entity_get_language($entity_type, $entity) { // Determine the entity's language. $langcode = entity_language($entity_type, $entity); // If no matching language was found, which will happen for e.g. terms and // users, it is normally recommended to use the system default language. // However, as the system default language can change, this could potentially // cause data loss / confusion problems; as a result use the system "no // language" value to avoid any potential problems. if (empty($langcode)) { $langcode = LANGUAGE_NONE; } return $langcode; } /** * Implements hook_features_api(). */ function metatag_features_api() { $components = array( 'metatag' => array( 'name' => t('Metatag'), 'feature_source' => TRUE, 'default_hook' => 'metatag_export_default', 'default_file' => FEATURES_DEFAULTS_INCLUDED, 'file' => drupal_get_path('module', 'metatag') . '/metatag.features.inc', ), ); return $components; } /** * Implements hook_views_post_render(). */ function metatag_views_post_render(&$view, &$output, &$cache) { // Build a shortcut to the current display object. $display = $view->display[$view->current_display]; // Only proceed if this view is a full page, don't process block or other // Views display objects. if ($display->display_plugin == 'page' && isset($display->display_options['path'])) { // Check if this is an entity display page, if so trigger // hook_entity_view(). foreach (entity_get_info() as $entity_name => $entity_type) { // Entity paths will include an auto-loader that matches the entity's // name, thus the path will be 'some/path/%entity_name'. if (isset($entity_type['path']) && ($display->display_options['path'] . $entity_name) == $entity_type['path']) { // Only proceed if this entity type supports meta tags. if (metatag_entity_supports_metatags($entity_name)) { // There must be at least one argument and the first argument must be // numerical. if (!empty($view->args) && is_numeric($view->args[0])) { // Only the first argument is used. $entities = entity_load($entity_name, array($view->args[0])); $entity = array_pop($entities); metatag_entity_view($entity, $entity_name, 'full', NULL, TRUE); } } } } } } /** * Implements hook_ctools_render_alter(). * * Temporary solution to load meta tags on entity pages that are driven by * CTools display handlers. */ function metatag_ctools_render_alter(&$info, $page, $context) { // Only proceed if this is a full page (don't process individual panes) and // there's an 'admin path' for the current task. if ($page && !empty($context['task']['admin path'])) { // Check if this is an entity display page, if so trigger // hook_entity_view(). foreach (entity_get_info() as $entity_name => $entity_type) { // Entity paths will include an auto-loader that matches the entity's // name, thus the path will be 'some/path/%entity_name'. if (isset($entity_type['path']) && $context['task']['admin path'] == $entity_type['path']) { // Only proceed if this entity type supports meta tags. if (metatag_entity_supports_metatags($entity_name)) { // There must be at least one argument and the first argument must be // numerical. if (!empty($context['args']) && is_numeric($context['args'][0])) { // Only the first argument is used. $entities = entity_load($entity_name, array($context['args'][0])); $entity = array_pop($entities); metatag_entity_view($entity, $entity_name, 'full', NULL, TRUE); } } } } } } /** * Implements hook_entity_translation_delete(). * * Required for content translations being handled via Entity_Translation to * remove the appropriate record when a translation is removed without the * corresponding entity record also being removed. */ function metatag_entity_translation_delete($entity_type, $entity, $langcode) { // Get the entity's ID. list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); // Delete the translation. metatag_metatags_delete($entity_type, $entity_id, $revision_id, $langcode); } /** * Translates the metatag if i18n_string is enabled. * @param $name * Array or string concatenated with ':' that contains textgroup and string context * @param string $string * String in default language or array of strings to be translated * @param $options * An associative array of additional options. @see i18n_string_translate() */ function metatag_translate($name, $string, $langcode = NULL, $update = FALSE) { if (function_exists('i18n_string_translate')) { $options = array( 'langcode' => $langcode, 'update' => $update, ); return i18n_string_translate($name, $string, $options); } else { return $string; } }