From 49a9514d9e21a64898af2c0c3e5446b55df935bf Mon Sep 17 00:00:00 2001 From: Colan Schwartz Date: Tue, 24 Jul 2012 15:45:42 -0400 Subject: [PATCH] Issue #1688286 by colan: Add language-specific tags. --- metatag.install | 43 +++++++++++++- metatag.module | 182 +++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 205 insertions(+), 20 deletions(-) diff --git a/metatag.install b/metatag.install index 47518f9..8128b5f 100644 --- a/metatag.install +++ b/metatag.install @@ -78,8 +78,15 @@ function metatag_schema() { 'not null' => TRUE, 'serialize' => TRUE, ), + 'language' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language of the tag', + ), ), - 'primary key' => array('entity_type', 'entity_id'), + 'primary key' => array('entity_type', 'entity_id', 'language'), ); $schema['cache_metatag'] = drupal_get_schema_unprocessed('system', 'cache'); @@ -110,3 +117,37 @@ function metatag_update_7001() { ); db_change_field('metatag_config', 'cid', 'cid', $field); } + +/** + * Add the {metatag}.language field. + */ +function metatag_update_7002() { + + // Set the target table and field name. + $table_name = 'metatag'; + $field_name = 'language'; + + // Don't add the new field if it already exists. + if (!db_field_exists($table_name, $field_name)) { + + // Describe the new field. + $field_definition = array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language of the tag', + ); + + // Add it and update the primary key. + db_add_field($table_name, $field_name, $field_definition); + db_drop_primary_key($table_name); + db_add_primary_key($table_name, array('entity_type', 'entity_id', 'language')); + + // Set default values. + db_update($table_name) + ->fields(array($field_name => language_default('language'))) + ->execute(); + } +} + diff --git a/metatag.module b/metatag.module index 9899f5e..2d7e610 100644 --- a/metatag.module +++ b/metatag.module @@ -271,12 +271,33 @@ function metatag_config_cache_clear() { ctools_export_load_object_reset('metatag_config'); } +/** + * Load an entity's tags. + * + * @param $type + * The entity type to load + * @param $id + * The ID of the entity to load + * @return + * An array of tag data keyed by language. + */ function metatag_metatags_load($type, $id) { $metatags = metatag_metatags_load_multiple($type, array($id)); return !empty($metatags) ? reset($metatags) : array(); } +/** + * Load tags for multiple entities. + * + * @param $type + * The entity type to load + * @param $ids + * The list of entity IDs + * @return + * An array of tag data, keyed by ID. + */ function metatag_metatags_load_multiple($type, array $ids) { + // Double check entity IDs are numeric thanks to Entity API module. $ids = array_filter($ids, 'is_numeric'); if (empty($ids)) { @@ -289,15 +310,42 @@ function metatag_metatags_load_multiple($type, array $ids) { return array(); } - $metatags = db_query("SELECT entity_id, data FROM {metatag} WHERE entity_type = :type AND entity_id IN (:ids)", 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))", array( ':type' => $type, ':ids' => $ids, - ))->fetchAllKeyed(); - $metatags = array_map('unserialize', $metatags); + )); + + // 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; } -function metatag_metatags_save($type, $id, $metatags) { +/** + * Save an entity's tags. + * + * @param $type + * The entity type to load + * @param $id + * The entity's ID + * @param $metatags + * All of the tag information + * @param $language + * The language of the translation set + */ +function metatag_metatags_save($type, $id, $metatags, $language) { + + // Use the default content language if the entity doesn't have language + // support. + if (!$language) { + $language = $GLOBALS['language_content']->language; + } + // Check that $id is numeric because of Entity API and string IDs. if (!is_numeric($id)) { return; @@ -306,7 +354,7 @@ function metatag_metatags_save($type, $id, $metatags) { // Allow other modules to alter the metatags prior to saving. foreach (module_implements('metatag_presave') as $module) { $function = "{$module}_metatag_presave"; - $function($metatags, $type, $id); + $function($metatags, $type, $id, $language); } if (empty($metatags)) { @@ -315,6 +363,7 @@ function metatag_metatags_save($type, $id, $metatags) { db_delete('metatag') ->condition('entity_type', $type) ->condition('entity_id', $id) + ->condition('language', $language) ->execute(); } else { @@ -323,6 +372,7 @@ function metatag_metatags_save($type, $id, $metatags) { ->key(array( 'entity_type' => $type, 'entity_id' => $id, + 'language' => $language, )) ->fields(array( 'data' => serialize($metatags), @@ -334,11 +384,28 @@ function metatag_metatags_save($type, $id, $metatags) { metatag_metatags_cache_clear($type, $id); } +/** + * Delete an entity's tags. + * + * @param $type + * The entity type + * @param $id + * The entity's ID + */ function metatag_metatags_delete($type, $id) { return metatag_metatags_delete_multiple($type, array($id)); } +/** + * Delete multiple entities' tags. + * + * @param $type + * The entity type + * @param $ids + * The list of IDs + */ function metatag_metatags_delete_multiple($type, array $ids) { + // Double check entity IDs are numeric thanks to Entity API module. $ids = array_filter(array_keys($ids, 'is_numeric')); @@ -348,6 +415,7 @@ function metatag_metatags_delete_multiple($type, array $ids) { // Let other modules know about the metatags being deleted. module_invoke_all('metatag_metatags_delete', $type, $ids); + // Delete all translations of the entities' tag data. db_delete('metatag') ->condition('entity_type', $type) ->condition('entity_id', $ids, 'IN') @@ -380,6 +448,7 @@ function metatag_metatags_cache_clear($type, $id = NULL) { * Implements hook_entity_load(). */ function metatag_entity_load($entities, $type) { + $metatags = metatag_metatags_load_multiple($type, array_keys($entities)); foreach ($entities as $id => $entity) { $entities[$id]->metatags = isset($metatags[$id]) ? $metatags[$id] : array(); @@ -392,7 +461,12 @@ function metatag_entity_load($entities, $type) { function metatag_entity_insert($entity, $entity_type) { if (isset($entity->metatags)) { list($id) = entity_extract_ids($entity_type, $entity); - metatag_metatags_save($entity_type, $id, $entity->metatags); + + // Determine the language as per http://drupal.org/node/1626346. + $language = function_exists('entity_language') ? + entity_language($entity_type, $entity) : $entity->language; + + metatag_metatags_save($entity_type, $id, $entity->metatags, $language); } } @@ -403,7 +477,12 @@ function metatag_entity_update($entity, $entity_type) { list($id) = entity_extract_ids($entity_type, $entity); if (isset($entity->metatags)) { - metatag_metatags_save($entity_type, $id, $entity->metatags); + + // Determine the language as per http://drupal.org/node/1626346. + $language = function_exists('entity_language') ? + entity_language($entity_type, $entity) : $entity->language; + + metatag_metatags_save($entity_type, $id, $entity->metatags, $language); } else { // Still ensure the meta tag output is cached. @@ -482,7 +561,7 @@ function metatag_field_attach_view_alter(&$output, $context) { * @param string $instance * The configuration instance key of the metatags to use, e.g. "node:article". * @param array $metatags - * An arary of metatag data. + * An array of metatag data. * @param array $options * (optional) An array of options including the following keys and values: * - language: A language object. @@ -491,7 +570,6 @@ function metatag_field_attach_view_alter(&$output, $context) { */ function metatag_metatags_view($instance, array $metatags = array(), array $options = array()) { $output = array(); - $metatags += metatag_config_load_with_defaults($instance); // Convert language codes to a language object. if (isset($options['language']) && is_string($options['language'])) { @@ -499,6 +577,30 @@ function metatag_metatags_view($instance, array $metatags = array(), array $opti $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. + if (isset($options['language']->language)) { + // Use the passed-in option. + $translation = $options['language']->language; + } + else if (isset($metatags[$GLOBALS['language_content']->language])) { + // We weren't given a language; use the global content one. + $translation = $GLOBALS['language_content']->language; + } + else { + // The language is not defined. + $translation = LANGUAGE_NONE; + } + + // Choose the derived translation (or nothing if it doesn't exist). + $metatags = isset($metatags[$translation]) ? $metatags[$translation] : array(); + } + + // Add any default tags to the mix. + $metatags += metatag_config_load_with_defaults($instance); + foreach ($metatags as $metatag => $data) { if ($metatag_instance = metatag_get_instance($metatag, $data)) { $output[$metatag] = $metatag_instance->getElement($options); @@ -512,17 +614,25 @@ function metatag_metatags_view($instance, array $metatags = array(), array $opti function metatag_metatags_values($instance, array $metatags = array(), array $options = array()) { $values = array(); - $metatags += metatag_config_load_with_defaults($instance); - // 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; + // Apply defaults to the data for each language. + foreach ($metatags as $language => $metatag) { + $metatags[$language] += metatag_config_load_with_defaults($instance); } - foreach ($metatags as $metatag => $data) { - if ($metatag_instance = metatag_get_instance($metatag, $data)) { - $values[$metatag] = $metatag_instance->getValue($options); + // Generate output only if we've got a valid language. + $language = $options['language']; + if (isset($language) && is_string($language) && isset($metatags[$language])) { + + // Convert language codes to a language object. + $languages = language_list(); + $options['language'] = isset($languages[$language]) ? $languages[$language] : NULL; + + // Get output elements. + foreach ($metatags[$language] as $metatag => $data) { + if ($metatag_instance = metatag_get_instance($metatag, $data)) { + $values[$metatag] = $metatag_instance->getValue($options); + } } } @@ -537,7 +647,7 @@ function metatag_metatags_values($instance, array $metatags = array(), array $op * @param string $instance * The configuration instance key of the metatags to use, e.g. "node:article". * @param array $metatags - * An arary of metatag data. + * 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(). @@ -826,13 +936,47 @@ function metatag_field_attach_delete_bundle($entity_type, $bundle) { function metatag_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); + // Don't do anything if this entity doesn't support meta tags. if (!metatag_entity_supports_metatags($entity_type, $bundle)) { return; } $instance = "{$entity_type}:{$bundle}"; - $metatags = isset($entity->metatags) ? $entity->metatags : array(); + // Grab the meta tags for display in the form if there are any. + if (isset($entity->metatags)) { + + // Determine the entity language as per http://drupal.org/node/1626346. + $entity_language = function_exists('entity_language') ? + entity_language($entity_type, $entity) : $entity->language; + + // Determine from where we should get the tags. + if (!(isset($entity->metatags[$langcode]) || isset($entity->metatags[$entity_language]))) { + + // This is a preview so set the tags to the raw submission data. No + // language has been set. + $metatags = $entity->metatags; + + } else if (isset($entity->metatags[$langcode])) { + + // Set the tags to the translation set matching that of the form. + $metatags = $entity->metatags[$langcode]; + } + else { + + /* + * 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. + */ + $metatags = $entity->metatags[$entity_language]; + } + } + else { + $metatags = array(); + } + $options['token types'] = array(token_get_entity_mapping('entity', $entity_type)); $options['context'] = $entity_type; -- 1.7.0.4