diff --git a/README.txt b/README.txt index c83edfc..b7b8a1d 100644 --- a/README.txt +++ b/README.txt @@ -15,7 +15,7 @@ This version of the module only works with Drupal 7.28 and newer. Features ------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- The primary features include: * The current supported basic meta tags are ABSTRACT, DESCRIPTION, CANONICAL, @@ -25,7 +25,9 @@ The primary features include: * Multi-lingual support using the Entity Translation module. -* Translation support using the Internationalization (i18n) module. +* Translation support using the Internationalization (i18n) module of the global + configurations, the values for all three submodules (Metatag:Context, + Metatag:Panels, Metatag:Views), and the final meta tags being output. * Full support for entity revisions and workflows based upon revision editing, including compatibility with the Revisioning and Workbench Moderation modules. @@ -121,7 +123,7 @@ The primary features include: Configuration ------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- 1. On the People Permissions administration page ("Administer >> People >> Permissions") you need to assign: @@ -157,19 +159,58 @@ Configuration admin/structure/types/manage/article/display/token -Internationalization: i18n.module ------------------------------------------------------------------------------- -All default configurations may be translated using the Internationalization -(i18n) module. The custom strings that are assigned to e.g., the "Global: Front -page" configuration will show up in the Translate Interface admin page -(admin/config/regional/translate/translate) and may be customized per language. - -Additionally, the custom pager may be translated using the Variable Translation -submodule. +Internationalization with the Translation (core) and Entity Translation modules +-------------------------------------------------------------------------------- +The module works with the core Translation module, allowing the meta tags for a +specific entity (node, term, etc) to be tied to a specific language. It also +supports the Entity Translation module, which may work better thank the basic +Translation module depending upon the site's desired functionality. This +integration means that content creators can customize an entity's meta tags for +each language supported on the site, and that the correct meta tags should +always be displayed for each locale. + + +Internationalization with the i18n modules +-------------------------------------------------------------------------------- +Using the String Translation (i18n_string) submodule of the Internationalization +(i18n) module package it is possible to translate meta tags: + +* All default configurations (admin/config/search/metatag) are translatable. + When a configuration is created or updated it will pass the values to the + i18n_string system. Additionally it is possible to bulk update them via the + string translation page (admin/config/regional/translate/i18n_string). + +* Meta tags for all submodules (Metatag:Context, Metatag:Panels, Metatag:Views) + are translatable. Similar to the default configurations, these meta tags are + made available when they are created and/or update, and may also be bulk + updated. + +* Meta tags from entities (nodes, terms, etc) are not directly translatable. + +* The final output meta tags are passed through the translation system when the + page is being loaded. It is not possible to use the strings bulk updater to + spool all pages on the site, to do so it would be necessary to spool the page + using a separate script or tool. + +Additionally, certain variables are available for translation using the Variable +Translation submodule of the i18n package: + +* metatag_pager_string - The custom pager string. + + +Internationalization with the Smartling module +-------------------------------------------------------------------------------- +The Smartling translation service may be used with the Metatag module provide an +improved UX around the meta tag translation process. In order to do this, the +Smartling Interface Translation (smartling_interface_translation) module must +be enabled. + +For further details see the module's project page: + https://www.drupal.org/project/smartling Fine Tuning & Suggestions ------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- * There are many options available on the settings page to control how Metatag works: admin/config/search/metatags/settings @@ -207,7 +248,7 @@ Fine Tuning & Suggestions Developers ------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- Full API documentation is available in metatag.api.php. It is not necessary to control Metatag via the entity API, any entity that has @@ -221,7 +262,7 @@ via drupal_render(), or examining to identify the actual text values. Troubleshooting / Known Issues ------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- * When using custom page template files, e.g., page--front.tpl.php, it is important to ensure that the following code is present in the template file: @@ -256,7 +297,7 @@ Troubleshooting / Known Issues Related modules ------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- Some modules are available that extend Metatag with additional functionality: * Transliteration @@ -302,7 +343,7 @@ Some modules are available that extend Metatag with additional functionality: Credits / Contact ------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- Currently maintained by Damien McKenna [1] and Dave Reid [2]; all initial development was by Dave Reid. @@ -315,7 +356,7 @@ request, a feature request or a bug report, in the project issue queue: References ------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- 1: https://www.drupal.org/u/damienmckenna 2: https://www.drupal.org/u/dave-reid 3: http://www.mediacurrent.com/ diff --git a/metatag.admin.inc b/metatag.admin.inc index e169644..b71ab10 100644 --- a/metatag.admin.inc +++ b/metatag.admin.inc @@ -134,6 +134,12 @@ function metatag_config_overview() { 'title' => t('Export'), 'href' => 'admin/config/search/metatags/config/' . $config->instance . '/export', ); + if (module_exists('i18n_string')) { + $operations['translate'] = array( + 'title' => t('Translate'), + 'href' => 'admin/config/search/metatags/config/' . $config->instance . '/translate', + ); + } $row['data']['operations'] = array( 'data' => array( '#theme' => 'links', @@ -306,7 +312,7 @@ function metatag_config_edit_form_submit($form, &$form_state) { $config = (object) $form_state['values']; // @todo Consider renaming the config field from 'config' to 'metatags' $config->config = $config->metatags[LANGUAGE_NONE]; - unset($config->metatags[LANGUAGE_NONE]); + unset($config->metatags); metatag_config_save($config); $label = metatag_config_instance_label($config->instance); @@ -590,6 +596,22 @@ function metatag_admin_settings_form() { '#default_value' => variable_get('metatag_pager_string', 'Page PAGER | '), ); + $form['i18n'] = array( + '#type' => 'fieldset', + '#title' => t('Internationalization options'), + '#description' => t('Additional options are available when using the Internationalization module.'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + + $form['i18n']['metatag_i18n_translate_output'] = array( + '#title' => t('Translate output'), + '#type' => 'checkbox', + '#description' => t('Optionally translate the final page output.'), + '#default_value' => variable_get('metatag_i18n_translate_output', FALSE), + '#disabled' => !module_exists('i18n_string'), + ); + $form['advanced'] = array( '#type' => 'fieldset', '#title' => t('Advanced settings'), @@ -639,15 +661,6 @@ function metatag_admin_settings_form() { '#default_value' => variable_get('metatag_cache_output', TRUE), ); - if (module_exists('i18n_string')) { - $form['advanced']['metatag_translate_final_values'] = array( - '#type' => 'checkbox', - '#title' => t('Translate final meta tag output'), - '#description' => t('By default the string meta tags are made available for translation before tokens are processed, the idea being that the tokens will probably have already been translated because they come from content that is translated elsewhere. This allows for an optional second-stage translation for sites that want to translate the final output too.'), - '#default_value' => variable_get('metatag_translate_final_values', FALSE), - ); - } - $form['advanced']['metatag_page_region'] = array( '#type' => 'select', '#title' => t("Page region to use"), diff --git a/metatag.api.php b/metatag.api.php index dd18082..9c8c3bc 100644 --- a/metatag.api.php +++ b/metatag.api.php @@ -54,6 +54,12 @@ function hook_metatag_config_default() { } /** + * Allow the exported configurations to be changed prior to being cached. + */ +function hook_metatag_config_default_alter(&$config) { +} + +/** * Internal hook for adding further configuration values in bundled submodules. * * The defaults provided by the main Metatag module need to be extended by the @@ -69,19 +75,10 @@ function hook_metatag_bundled_config_alter(&$config) { } /** + * Triggered when a Metatag configuration is created. * - */ -function hook_metatag_config_default_alter(&$config) { -} - -/** - * - */ -function hook_metatag_config_delete($entity_type, $entity_ids, $revision_ids, $langcode) { -} - -/** - * + * @param object $config + * The configuration object that was created. */ function hook_metatag_config_insert($config) { } @@ -118,12 +115,24 @@ function hook_metatag_config_presave($config) { } /** + * Triggered when a Metatag configuration is updated. * + * @param object $config + * The configuration object that was modified. */ function hook_metatag_config_update($config) { } /** + * Triggered when a Metatag configuration is removed. + * + * @param object $config + * The configuration object that was removed. + */ +function hook_metatag_config_delete($config) { +} + +/** * Definition of the meta tags and groups. * * @return array @@ -358,3 +367,24 @@ function hook_metatag_views_post_render_get_entity($view) { return 'my_entity'; } } + +/** + * Allow the context string being passed to i18n_string to be changed before + * it is used. + * + * If the string is set to an empty value it will cause this meta tag to not + * be translated. + * + * @param string $context + * The context string being passed into i18n_string. Will usually be in the + * format "[category]:[path-identifier]", e.g. "[node:123]", "[page:contact]", + * etc. + * @param string $tag_name + * The name of the meta tag being translated. + */ +function hook_metatag_i18n_context_alter(&$context, $tag_name) { + // Don't bother translating the canonical URL. + if ($tag_name == 'canonical') { + $context = ''; + } +} diff --git a/metatag.i18n.inc b/metatag.i18n.inc index 194d14c..1a2637a 100644 --- a/metatag.i18n.inc +++ b/metatag.i18n.inc @@ -8,11 +8,138 @@ * Implements hook_i18n_string_info(). */ function metatag_i18n_string_info() { + // Text groups. $groups['metatag'] = array( 'title' => t('Metatag'), - 'description' => t('Configurable metatags.'), + 'description' => t('Configurable meta tags.'), + + // This group does not have strings with text formats. 'format' => FALSE, - 'list' => FALSE, + + // This group can list all strings. + // @todo What does this actually do? + 'list' => TRUE, ); return $groups; } + +/** + * Implements hook_i18n_object_info(). + */ +function metatag_i18n_object_info() { + // Compile all of the tags to add to the translation stack. + $meta_tag_info = metatag_get_info(); + $groups = $meta_tag_info['groups']; + $config_properties = $output_properties = array(); + foreach ($meta_tag_info['tags'] as $tag_name => $tag_info) { + // Ignore certain field types that aren't translatable, mostly fields that + // list predetermined options in various forms. + if (!empty($tag_info['class']) && $tag_info['class'] == 'DrupalListMetaTag') { + continue; + } + elseif (!empty($tag_info['form']['#type']) && $tag_info['form']['#type'] == 'select') { + continue; + } + elseif (!empty($tag_info['form']['#options'])) { + continue; + } + + // Build a suitable structure for this meta tag. + if (!empty($tag_info['group']) && !empty($groups[$tag_info['group']]['label'])) { + $group_label = $groups[$tag_info['group']]['label'] . ': '; + } + elseif (!empty($tag_info['group'])) { + $group_label = $tag_info['group'] . ': '; + } + else { + $group_label = ''; + } + + // Configuration items. + $config_properties[$tag_name] = array( + 'title' => $group_label . $tag_info['label'], + 'field' => "config.{$tag_name}.value", + ); + + // Output items. + $output_properties[$tag_name] = array( + 'title' => $group_label . ': ' . $tag_info['label'], + 'field' => "data.{$tag_name}.value", + ); + } + + // The main Metatag configuration items. + $info['metatag_config'] = array( + 'title' => t('Metatag configuration'), + // Callback to load all config objects. + 'list callback' => 'metatag_i18n_list_metatag_config', + // The object load callback. + 'load callback' => 'metatag_config_load', + // @todo Custom i18n object overrides. + // 'class' => 'metatag_views_i18n_metatag', + // @todo Is this needed? What does it do? + // 'translation set' => TRUE, + + // The object key field. + 'key' => 'instance', + // Placeholders for automatic paths. This connects the 'key' to strings in + // the paths listed below. + 'placeholders' => array( + '%instance' => 'instance', + ), + // To produce edit links automatically. + 'edit path' => 'admin/config/search/metatags/config/%instance', + // Auto-generate a 'translate' tab. + 'translate tab' => 'admin/config/search/metatags/config/%instance/translate', + + // Properties for string translation. + 'string translation' => array( + // The textgroup, type and (below) name will be concatenated into a single + // string as the {locales_source} context. + 'textgroup' => 'metatag', + 'type' => 'metatag_config', + // Table where the object is stored, to automate string lists. + 'table' => 'metatag_config', + // Translatable properties of these objects. + 'properties' => $config_properties, + // The path to translate individual strings. + 'translate path' => 'admin/config/search/metatags/config/%instance/translate/%i18n_language', + ), + ); + + // The final meta tags being output on the page. + if (variable_get('metatag_i18n_translate_output', FALSE)) { + $info['metatag_output'] = array( + 'title' => t('Metatag final tag output'), + + // Properties for string translation. + 'string translation' => array( + // The textgroup, type and (below) name will be concatenated into a + // single string as the {locales_source} context. + 'textgroup' => 'metatag', + 'type' => 'metatag_output', + // Translatable properties of these objects. + 'properties' => $output_properties, + ), + ); + } + + return $info; +} + +/** + * List callback for {metatag_config} strings. + */ +function metatag_i18n_list_metatag_config() { + ctools_include('export'); + $configs = ctools_export_crud_load_all('metatag_config'); + if (!empty($configs)) { + // Unserialize the config array. + foreach ($configs as &$config) { + if (is_string($config->config)) { + $config->config = unserialize($config->config); + } + } + return $configs; + } +} diff --git a/metatag.inc b/metatag.inc index 95510b6..fba453d 100644 --- a/metatag.inc +++ b/metatag.inc @@ -1,4 +1,8 @@ tidyValue($this->data['value']); + $value = $this->tidyValue($this->data['value']); + + // Translate the final output string prior to output. Use the + // 'metatag_output' i18n_string object type, and pass along the meta tag's + // options as the context so it can be handled appropriately. + $value = metatag_translate_metatag($value, $this->info['name'], $options, NULL, TRUE); + + return $value; } + /** + * Get the HTML tag for this meta tag. + * + * @return array + * A render array for this meta tag. + */ public function getElement(array $options = array()) { $value = $this->getValue($options); if (strlen($value) === 0) { @@ -136,9 +166,6 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface { // doesn't end up with a bunch of a-circumflex characters. $value = str_replace(' ', ' ', $value); - // Convert any HTML entities into regular characters. - $value = decode_entities($value); - // Remove any HTML code that might have been included. $value = strip_tags($value); @@ -150,6 +177,7 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface { return $value; } + } /** @@ -157,6 +185,9 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface { */ class DrupalTextMetaTag extends DrupalDefaultMetaTag { + /** + * {@inheritdoc} + */ public function getForm(array $options = array()) { $options += array( 'token types' => array(), @@ -210,6 +241,9 @@ class DrupalTextMetaTag extends DrupalDefaultMetaTag { return $form; } + /** + * {@inheritdoc} + */ public function getValue(array $options = array()) { $options += array( 'instance' => '', @@ -222,9 +256,7 @@ class DrupalTextMetaTag extends DrupalDefaultMetaTag { 'raw' => FALSE, ); - // Translate the meta tag value prior to the tokens being replace, this - // allows for different tokens to be inserted for each locale/language. - $value = metatag_translate($this->data['value'], $this->info['name']); + $value = $this->data['value']; if (empty($options['raw'])) { // Give other modules the opportunity to use hook_metatag_pattern_alter() @@ -259,14 +291,14 @@ class DrupalTextMetaTag extends DrupalDefaultMetaTag { $value = $this->tidyValue($value); - // Optionally translate the strings after they've been processed for tokens - // and images. - if (variable_get('metatag_translate_final_values', FALSE)) { - $value = metatag_translate($value, $this->info['name']); - } + // Translate the final output string prior to output. Use the + // 'metatag_output' i18n_string object type, and pass along the meta tag's + // options as the context so it can be handled appropriately. + $value = metatag_translate_metatag($value, $this->info['name'], $options, NULL, TRUE); return $value; } + } /** @@ -274,6 +306,9 @@ class DrupalTextMetaTag extends DrupalDefaultMetaTag { */ class DrupalLinkMetaTag extends DrupalTextMetaTag { + /** + * {@inheritdoc} + */ public function getElement(array $options = array()) { $element = isset($this->info['element']) ? $this->info['element'] : array(); @@ -301,6 +336,7 @@ class DrupalLinkMetaTag extends DrupalTextMetaTag { '#attached' => array('drupal_add_html_head' => array(array($element, $element['#id']))), ); } + } /** @@ -312,15 +348,16 @@ class DrupalLinkMetaTag extends DrupalTextMetaTag { class DrupalTitleMetaTag extends DrupalTextMetaTag { /** - * + * {@inheritdoc} */ public function getElement(array $options = array()) { $element = array(); - $value = htmlentities($this->getValue($options), ENT_QUOTES); + $value = $this->getValue($options); $element['#attached']['metatag_set_preprocess_variable'][] = array('html', 'head_title', $value); $element['#attached']['metatag_set_preprocess_variable'][] = array('html', 'head_array', array('title' => $value)); return $element; } + } /** @@ -328,6 +365,9 @@ class DrupalTitleMetaTag extends DrupalTextMetaTag { */ class DrupalListMetaTag extends DrupalDefaultMetaTag { + /** + * {@inheritdoc} + */ function __construct(array $info, array $data = NULL) { // Ensure that the $data['value] argument is an array. if (empty($data['value'])) { @@ -338,6 +378,9 @@ class DrupalListMetaTag extends DrupalDefaultMetaTag { parent::__construct($info, $data); } + /** + * {@inheritdoc} + */ public function getForm(array $options = array()) { $form['value'] = isset($this->info['form']) ? $this->info['form'] : array(); @@ -351,12 +394,23 @@ class DrupalListMetaTag extends DrupalDefaultMetaTag { return $form; } + /** + * {@inheritdoc} + */ public function getValue(array $options = array()) { $values = array_keys(array_filter($this->data['value'])); sort($values); $value = implode(', ', $values); - return $this->tidyValue($value); + $value = $this->tidyValue($value); + + // Translate the final output string prior to output. Use the + // 'metatag_output' i18n_string object type, and pass along the meta tag's + // options as the context so it can be handled appropriately. + $value = metatag_translate_metatag($value, $this->info['name'], $options, NULL, TRUE); + + return $value; } + } /** @@ -364,6 +418,9 @@ class DrupalListMetaTag extends DrupalDefaultMetaTag { */ class DrupalDateIntervalMetaTag extends DrupalDefaultMetaTag { + /** + * {@inheritdoc} + */ public function getForm(array $options = array()) { $form['value'] = array( '#type' => 'textfield', @@ -389,6 +446,9 @@ class DrupalDateIntervalMetaTag extends DrupalDefaultMetaTag { return $form; } + /** + * {@inheritdoc} + */ public function getValue(array $options = array()) { $value = ''; if (!empty($this->data['value'])) { @@ -398,6 +458,13 @@ class DrupalDateIntervalMetaTag extends DrupalDefaultMetaTag { $value = format_plural($interval, '@count ' . $period, '@count ' . $period . 's'); } } + + // Translate the final output string prior to output. Use the + // 'metatag_output' i18n_string object type, and pass along the meta tag's + // options as the context so it can be handled appropriately. + $value = metatag_translate_metatag($value, $this->info['name'], $options, NULL, TRUE); + return $value; } + } diff --git a/metatag.info b/metatag.info index c90cd20..5f2b672 100644 --- a/metatag.info +++ b/metatag.info @@ -12,7 +12,8 @@ dependencies[] = ctools ; Must have this release of Token to avoid a bug with tokens that contain the ; colon symbol, as used in OG meta tags. -dependencies[] = token (>= 1.6) +dependencies[] = token +; (>= 1.6) configure = admin/config/search/metatags @@ -28,10 +29,15 @@ files[] = tests/metatag.user.test ; String handling files[] = tests/metatag.string_handling.test files[] = tests/metatag.string_handling_with_i18n.test +; Internationalization & translation. +files[] = tests/metatag.locale.test +files[] = tests/metatag.with_i18n_output.test +files[] = tests/metatag.with_i18n_config.test +files[] = tests/metatag.with_i18n_node.test ; Basic integration with Panels. -files[] = tests/metatag.panels.test +files[] = tests/metatag.with_panels.test ; Basic integration with Views. -files[] = tests/metatag.views.test +files[] = tests/metatag.with_views.test ; This is required for testing image handling. test_dependencies[] = devel diff --git a/metatag.install b/metatag.install index 2061c82..7632d2e 100644 --- a/metatag.install +++ b/metatag.install @@ -382,12 +382,12 @@ function metatag_uninstall() { // Optionally disables the output cache. variable_del('metatag_cache_output'); - // Optionally translate all output strings. - variable_del('metatag_translate_final_values'); - // Customizable pager string. variable_del('metatag_pager_string'); + // Optionally enable translations of final output. + variable_del('metatag_i18n_translate_output'); + // Remove all possible 'enable' variables. foreach (entity_get_info() as $entity_type => $entity_info) { variable_del('metatag_enable_' . $entity_type); @@ -1851,3 +1851,59 @@ function metatag_update_7040(&$sandbox) { function metatag_update_7041(&$sandbox) { metatag_update_7018($sandbox); } + +/** + * Delete a deprecated variable and clear all Metatag caches. + */ +function metatag_update_7100() { + variable_del('metatag_translate_final_values'); + + // Clear all caches so that i18n support can be activated, if necessary. + cache_clear_all('*', 'cache_metatag', TRUE); +} + +/** + * Update i18n strings for all meta tags to use the new format. + */ +function metatag_update_7101() { + if (module_exists('i18n')) { + // Loop through each entity type. Not going to bother filtering this list, + // it'll only be a difference of a few entities anyway and the queries + // should be fairly quick. + foreach (entity_get_info() as $entity_type => $entity_info) { + // Entities must have bundles. + if (empty($entity_info['bundles'])) { + $entity_info['bundles'] = array( + $entity_type => $entity_type, + ); + } + foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) { + // Update {locales_source}, replace 'metatag:ENTITYTYPE:BUNDLE:tag' with + // 'metatag:metatag_config:ENTITYTYPE:BUNDLE:tag'. + db_query("UPDATE {locales_source} + SET location = REPLACE(location, 'metatag:{$entity_name}:{$bundle_name}:', 'metatag:metatag_config:{$entity_name}:{$bundle_name}:'), + context = REPLACE(context, '{$entity_name}:{$bundle_name}:', '{$entity_name}:{$bundle_name}:') + WHERE textgroup = 'metatag' + AND location LIKE 'metatag:{$entity_name}:{$bundle_name}:%'"); + + // Update {locales_source}, replace 'metatag:ENTITYTYPE:tag' with + // 'metatag:metatag_config:ENTITYTYPE:tag'. + db_query("UPDATE {locales_source} + SET location = REPLACE(location, 'metatag:{$entity_name}:', 'metatag:metatag_config:{$entity_name}:'), + context = REPLACE(context, '{$entity_name}:', '{$entity_name}:') + WHERE textgroup = 'metatag' + AND location LIKE 'metatag:{$entity_name}:%'"); + } + } + // Update {locales_source}, replace 'metatag:metatag:' with + // 'metatag:metatag_output:'. + db_query("UPDATE {locales_source} + SET location = REPLACE(location, 'metatag:metatag:', 'metatag:metatag_output:'), + context = REPLACE(context, 'metatag:', 'metatag_output:') + WHERE textgroup = 'metatag' + AND location LIKE 'metatag:metatag:%'"); + } + else { + return t('The Internationalization (i18n) module is not enabled, so there is no need to generate the missing translation strings.'); + } +} diff --git a/metatag.module b/metatag.module index 4eec4ac..bef3bcb 100644 --- a/metatag.module +++ b/metatag.module @@ -213,6 +213,25 @@ function metatag_menu() { 'file' => 'metatag.admin.inc', ); + // Optional integration with the i18n_string module for translating the + // configurations. + if (module_exists('i18n_string')) { + $items['admin/config/search/metatags/config/%metatag_config/translate'] = array( + 'title' => 'Translate', + 'access arguments' => array('administer meta tags'), + 'page callback' => 'i18n_string_object_translate_page', + 'page arguments' => array('metatag_config', 5), + 'type' => MENU_LOCAL_TASK, + ); + $items['admin/config/search/metatags/config/%metatag_config/translate/%i18n_language'] = array( + 'title' => 'Translate', + 'access arguments' => array('administer meta tags'), + 'page callback' => 'i18n_string_object_translate_page', + 'page arguments' => array('metatag_config', 5, 7), + 'type' => MENU_CALLBACK, + ); + } + return $items; } @@ -236,8 +255,12 @@ function metatag_flush_caches() { function metatag_config_load_with_defaults($instance, $include_global = TRUE) { $defaults = &drupal_static(__FUNCTION__, array()); + // Use the current page's locale. + $langcode = $GLOBALS['language_content']->language; + // Statically cache defaults since they can include multiple levels. - $cid = "config:{$instance}" . ($include_global ? ':withglobal' : ':withoutglobal'); + $cid = "config:{$instance}:{$langcode}" . ($include_global ? ':withglobal' : ':withoutglobal'); + if (!isset($defaults[$cid])) { if ($cache = metatag_cache_get($cid)) { $defaults[$cid] = $cache->data; @@ -284,7 +307,7 @@ function metatag_config_load_multiple(array $instances) { // "Fix" any records that might be using old values. Ideally these will be // permanently fixed by being re-saved or re-exported. foreach (metatag_config_get_replacements() as $old_tag => $new_tag) { - foreach ($configs as $config_name => $config) { + foreach ($configs as $instance => $config) { if (isset($config->config[$old_tag])) { $config->config[$new_tag] = $config->config[$old_tag]; unset($config->config[$old_tag]); @@ -292,6 +315,17 @@ function metatag_config_load_multiple(array $instances) { } } + // Translate the configuration. + if (module_exists('i18n_string')) { + foreach ($configs as $instance => &$config) { + foreach ($config->config as $tag => &$value) { + if (isset($value['value']) && is_string($value['value'])) { + $value['value'] = i18n_string_translate(array('metatag', 'metatag_config', $instance, $tag), $value['value']); + } + } + } + } + return $configs; } @@ -329,16 +363,6 @@ function metatag_config_save($config) { // 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); @@ -368,6 +392,10 @@ function metatag_config_delete($instance) { ->condition('instance', $instance) ->execute(); + // Allow modules to act upon the record deletion using + // hook_metatag_config_delete(). + module_invoke_all('metatag_config_delete', $config); + // Clear any caches. metatag_config_cache_clear(); } @@ -666,6 +694,11 @@ function metatag_metatags_delete_multiple($entity_type, array $entity_ids, array // Clear the caches for these entities. entity_get_controller($entity_type)->resetCache($entity_ids); + // Update i18n. + if (module_exists('i18n_string')) { + i18n_string_object_remove('metatag_config', $new_metatags); + } + return TRUE; } catch (Exception $e) { @@ -1085,12 +1118,17 @@ function metatags_get_entity_metatags($entity_id, $entity_type, $langcode = NULL 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 (empty($options['language'])) { + $options['language'] = $GLOBALS['language_content']; + } + // If there are any tags, determine the translation to display. if (!empty($metatags)) { // Get the display language; default to the entity's language. @@ -2287,112 +2325,6 @@ function metatag_ctools_render_alter(&$info, $page, $context) { } /** - * 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); - $revision_id = intval($revision_id); - - // Delete the translation. - metatag_metatags_delete($entity_type, $entity_id, $revision_id, $langcode); -} - -/** - * Translates the metatag if i18n_string is enabled. - * - * @param string $string - * String in default language or array of strings to be translated. - * @param string $tag_name - * The internal name of the meta tag being translated. - * @param $options - * An associative array of additional options. - * - * @return string - * The translated string if i18n_string is enabled, otherwise just returns the - * original string. - * - * @see i18n_string_translate() - */ -function metatag_translate($string, $tag_name, $langcode = NULL, $update = FALSE) { - if (function_exists('i18n_string_translate')) { - $options = array( - 'update' => $update, - ); - - // If the langcode was passed in, add it to the options passed to i18n. - if (!empty($langcode)) { - $options['langcode'] = $langcode; - } - - // The 'name' is split up by i18n_string into two components - the textgroup - // is the first item, the others are joined together with a ':' separator - // to make the context. In order to have the contexts show with "metatag" as - // the first part of the context, it has to be added twice to the name. - $name = array( - 'metatag', - 'metatag', - $tag_name, - ); - - // Return the string that's translated through the i18n_string system. - return i18n_string_translate($name, $string, $options); - } - - // If the i18n_string module isn't enabled then just pass back the string - // as-is. - else { - return $string; - } -} - -/** - * Translate a set of metatags to the current language. - * - * @param array $metatags - * List of meta tags to be translated. - */ -function metatag_translate_metatags(&$metatags) { - global $language; - - if (!empty($metatags[LANGUAGE_NONE])) { - $translated_metatags = array(); - foreach ($metatags[LANGUAGE_NONE] as $key => $data) { - if (!empty($data['value']) && is_string($data['value'])) { - $translated_metatags[$key] = array( - 'value' => metatag_translate($data['value'], $key), - ); - } - } - $metatags[$language->language] = $translated_metatags; - } -} - -/** - * Update the translated definitions of meta tags. - * - * @param array $metatags - * List of meta tags to have their translations updated. - */ -function metatag_update_translations(&$metatags) { - global $language; - - // Update the i18n string. - if (function_exists('i18n_string_update')) { - foreach ($metatags as $key => $data) { - if (!empty($data['value']) && is_string($data['value'])) { - // Add 'metatag' twice so that it's also added to the string context. - i18n_string_update("metatag:metatag:{$key}", $data['value']); - } - } - } -} - -/** * Checks if this entity is the default revision (published). * * @param object $entity @@ -2675,3 +2607,248 @@ function _metatag_config_instance_sort($a, $b) { } } } + +/** + * Translations & internationalization (i18n). + */ + +/** + * 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); + $revision_id = intval($revision_id); + + // Delete the translation. + metatag_metatags_delete($entity_type, $entity_id, $revision_id, $langcode); +} + +/** + * Translates the metatag if i18n_string is enabled. + * + * @param string $string + * String in default language or array of strings to be translated. + * @param string $tag_name + * The internal name of the meta tag being translated. + * @param string $context + * A context to use with i18n, defaults to the string "metatag_output" + * indicating it is translating the final rendered output strings. + * @param string $langcode + * The language code to submit instead of the current page's language. + * @param bool $update + * Whether or not to create/update records in {locales_source}. + * + * @return string + * The translated string if i18n_string is enabled, otherwise just returns the + * original string. + * + * @see i18n_string_translate() + */ +function metatag_translate_metatag($string, $tag_name, $context = 'metatag_output', $langcode = NULL, $update = TRUE) { + if (module_exists('i18n_string')) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + static $page_is_admin; + if (is_null($page_is_admin)) { + $page_is_admin = FALSE; + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + $page_is_admin = TRUE; + } + } + if ($page_is_admin) { + return $string; + } + + // Optionally disable output translations. + static $process_output; + if ($context == 'metatag_output') { + if (is_null($process_output)) { + $process_output = (bool) variable_get('metatag_i18n_translate_output', FALSE); + } + if (!$process_output) { + return; + } + } + + // Automatically create/update the {locales_source} record if one wasn't + // found. + $options = array( + 'update' => $update, + ); + + // If the langcode was passed in, add it to the options passed to i18n. + if (!empty($langcode)) { + $options['langcode'] = $langcode; + } + + // If the context is an array then it is the $options from the meta tag + // generator and needs some custom tailoring. Doing it this way to avoid an + // unnecessary entity_extract_ids() call when i18n isn't being used. + if (is_array($context)) { + $new_context = 'metatag:'; + if (drupal_is_front_page()) { + $new_context .= 'frontpage'; + } + // If this is an entity page, use the entity as the context. + elseif (!empty($context['entity_type']) && !empty($context['entity'])) { + list($entity_id, $revision_id, $bundle) = entity_extract_ids($context['entity_type'], $context['entity']); + $new_context .= $context['entity_type'] . ':' . $entity_id . ':' . $revision_id; + } + // Otherwise, use the page URL. + else { + $new_context .= 'page:' . current_path(); + } + $context = $new_context; + } + + // Triggers hook_metatag_i18n_context_alter() - allows the i18n string to + // be altered before being used. + drupal_alter('metatag_i18n_context', $context, $tag_name); + + // If the context was erased just send back the original string - it's + // unlikely, but it could happen. + if (empty($context)) { + return $string; + } + + // The 'name' is split up by i18n_string into two components - the textgroup + // is the first item, the others are joined together with a ':' separator + // to make the context. In order to have the contexts show with "metatag" as + // the first part of the context, it has to be added twice to the name. + $name = array( + 'metatag', + $context, + $tag_name, + ); + + // Notify i18n of the string, and obtain a translation if one is available. + return i18n_string($name, $string, $options); + } + + // If the i18n_string module isn't enabled then just pass back the string + // as-is. + else { + return $string; + } +} + +/** + * Translate a set of metatags to the current language. + * + * @param array $metatags + * List of meta tags to be translated. + * @param string $context + * An optional context to use with i18n. + */ +function metatag_translate_metatags(&$metatags, $context = NULL) { + if (!empty($metatags)) { + foreach ($metatags as $key => $data) { + if (!empty($data['value']) && is_string($data['value'])) { + $metatags[$key] = array( + 'value' => metatag_translate_metatag($data['value'], $key, $context), + ); + } + } + } +} + +/** + * Update the translated definitions of meta tags. + * + * @param array $metatags + * List of meta tags to have their translations updated. + * @param string $context + * The string that will be used to group strings in the translation UI. + */ +function metatag_translations_update($metatags, $context) { + // Store the context as it was originally provided. + $original_context = $context; + + // Update the i18n string. + if (module_exists('i18n_string')) { + foreach ($metatags as $tag_name => $data) { + // Revert the context, so that it can be changed if needed. + $context = $original_context; + + if (!empty($data['value']) && is_string($data['value'])) { + // Triggers hook_metatag_i18n_context_alter() - allows the i18n string + // to be altered before being used. + drupal_alter('metatag_i18n_context', $context, $tag_name); + + // Don't do anything if the context was erased - it's unlikely, but it + // could happen. + if (empty($context)) { + continue; + } + + // The textgroup is the first part of the string. + i18n_string_update("metatag:{$context}:{$tag_name}", $data['value']); + } + } + } +} + +/** + * Remove the translated definitions of meta tags. + * + * @param array $metatags + * List of meta tags to have their translations updated. + * @param string $context + * The string that will be used to group strings in the translation UI. + */ +function metatag_translations_delete($metatags, $context) { + // Store the context as it was originally provided. + $original_context = $context; + + // Update the i18n string. + if (module_exists('i18n_string')) { + foreach ($metatags as $tag_name => $data) { + // Revert the context, so that it can be changed if needed. + $context = $original_context; + + if (!empty($data['value']) && is_string($data['value'])) { + // Triggers hook_metatag_i18n_context_alter() - allows the i18n string + // to be altered before being used. + drupal_alter('metatag_i18n_context', $context, $tag_name); + + // Don't do anything if the context was erased - it's unlikely, but it + // could happen. + if (empty($context)) { + continue; + } + + // The textgroup is the first part of the string. + i18n_string_remove("metatag:{$context}:{$tag_name}", $data['value']); + } + } + } +} + +/** + * Implements hook_metatag_config_insert() on behalf of i18n_string. + */ +function i18n_string_metatag_config_insert($config) { + $context = 'metatag_config:' . $config->instance; + metatag_translations_update($config->config, $context); +} + +/** + * Implements hook_metatag_config_update() on behalf of i18n_string. + */ +function i18n_string_metatag_config_update($config) { + // Defer to the 'insert' function. + i18n_string_metatag_config_insert($config); +} + +/** + * Implements hook_metatag_config_delete() on behalf of i18n_string. + */ +function i18n_string_metatag_config_delete($config) { + $context = 'metatag_config:' . $config->instance; + metatag_translations_delete($config->config, $context); +} diff --git a/metatag_context/metatag_context.admin.inc b/metatag_context/metatag_context.admin.inc index 2c80774..474f90e 100644 --- a/metatag_context/metatag_context.admin.inc +++ b/metatag_context/metatag_context.admin.inc @@ -87,7 +87,9 @@ function metatag_context_config_add_form_submit($form, &$form_state) { context_save($context); // Update the i18n strings. - metatag_update_translations($context->reactions['metatag_context_reaction']['metatags']); + if (!empty($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE])) { + metatag_translations_update($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE], 'metatag_context:' . $context->name); + } $form_state['redirect'] = 'admin/config/search/metatags/context/' . $context->name; } @@ -181,7 +183,9 @@ function metatag_context_config_edit_form_submit($form, &$form_state) { context_save($context); // Update the i18n strings. - metatag_update_translations($context->reactions['metatag_context_reaction']['metatags']); + if (!empty($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE])) { + metatag_translations_update($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE], 'metatag_context:' . $context->name); + } $form_state['redirect'] = 'admin/config/search/metatags/context'; } diff --git a/metatag_context/metatag_context.context.inc b/metatag_context/metatag_context.context.inc index ca30c2b..05238bf 100644 --- a/metatag_context/metatag_context.context.inc +++ b/metatag_context/metatag_context.context.inc @@ -114,7 +114,7 @@ class metatag_context_reaction extends context_reaction { } // Translate all of the meta tags using i18n. - metatag_translate_metatags($metadata_array); + metatag_translate_metatags($metadata_array, 'metatag_context:' . $context->name); // Add the meta tags to the output. foreach ($metadata_array as $langcode => $tags) { diff --git a/metatag_context/metatag_context.i18n.inc b/metatag_context/metatag_context.i18n.inc new file mode 100644 index 0000000..4bd82d4 --- /dev/null +++ b/metatag_context/metatag_context.i18n.inc @@ -0,0 +1,133 @@ + t('Metatag:Context configurations'), + // Callback to load all config objects. + 'list callback' => 'metatag_context_i18n_list', + // The object load callback. + 'load callback' => 'metatag_context_i18n_load', + // @todo Custom i18n object overrides. + // 'class' => 'metatag_views_i18n_metatag', + // @todo Is this needed? What does it do? + // 'translation set' => TRUE, + + // The object key field. + // 'key' => 'name', + // Placeholders for automatic paths. This connects the 'key' to strings in + // the paths listed below. + // 'placeholders' => array( + // '%name' => 'name', + // ), + // To produce edit links automatically. + // 'edit path' => 'admin/config/search/metatags/config/%instance', + // Auto-generate a 'translate' tab. + // 'translate tab' => 'admin/config/search/metatags/config/%instance/translate', + + // Properties for string translation. + 'string translation' => array( + // The textgroup, type and (below) name will be concatenated into a single + // string as the {locales_source} context. + 'textgroup' => 'metatag', + 'type' => 'metatag_context', + // Table where the object is stored, to automate string lists. + 'table' => 'context', + // Translatable properties of these objects, this will be added later. + 'properties' => array(), + // The path to translate individual strings. + // 'translate path' => 'admin/config/search/metatags/config/%instance/translate/%i18n_language', + ), + ); + + // Compile all of the tags to add to the translation stack. + $meta_tag_info = metatag_get_info(); + $groups = $meta_tag_info['groups']; + foreach ($meta_tag_info['tags'] as $tag_info) { + // Ignore certain field types that aren't translatable, mostly fields that + // list predetermined options in various forms. + if (!empty($tag_info['class']) && $tag_info['class'] == 'DrupalListMetaTag') { + continue; + } + elseif (!empty($tag_info['form']['#type']) && $tag_info['form']['#type'] == 'select') { + continue; + } + elseif (!empty($tag_info['form']['#options'])) { + continue; + } + + // Build a suitable structure for this meta tag. + $tag_name = $tag_info['name']; + $tag_group = $tag_info['group']; + $group_label = isset($groups[$tag_group]['label']) ? $groups[$tag_group]['label'] : $tag_group; + $info['metatag_context']['string translation']['properties'][$tag_name] = array( + 'title' => $group_label . ': ' . $tag_info['label'], + 'field' => "reactions.metatag_context_reaction.und.{$tag_name}.value", + ); + } + + return $info; +} + +/** + * Implements hook_i18n_string_list(). + * + * @todo Functionality to delete translation records when Panels are deleted. + */ +function metatag_context_i18n_string_list($group) { + if ($group == 'metatag' || $group == 'all') { + $strings = array(); + + foreach (context_context_list() as $context_name) { + $context = context_load($context_name); + if (!empty($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE])) { + $new_strings = array(); + foreach ($context->reactions['metatag_context_reaction']['metatags'][LANGUAGE_NONE] as $name => $value) { + if (isset($value['value'])) { + // Don't translate meta tags that are arrays. + if (is_array($value['value'])) { + continue; + } + // Collapse the array down one level. + else { + $new_strings[$name] = $value['value']; + } + } + } + $strings['metatag']['metatag_context'][$context->name] = $new_strings; + } + } + + return $strings; + } +} + +/** + * List callback. + */ +function metatag_context_i18n_list() { + ctools_include('export'); + $configs = ctools_export_crud_load_all('metatag_config'); + if (!empty($configs)) { + // Unserialize the config array. + foreach ($configs as &$config) { + if (is_string($config->config)) { + $config->config = unserialize($config->config); + } + } + return $configs; + } +} + +/** + * Load callback. + */ +function metatag_context_i18n_load() { + // dpm(func_get_args()); +} diff --git a/metatag_context/metatag_context.info b/metatag_context/metatag_context.info index 244c371..3a85fd6 100644 --- a/metatag_context/metatag_context.info +++ b/metatag_context/metatag_context.info @@ -2,7 +2,12 @@ name = Metatag: Context description = "Assigned Metatag using Context definitions, allowing them to be assigned by path and other criteria." package = SEO core = 7.x -dependencies[] = context -dependencies[] = metatag -files[] = metatag_context.test + configure = admin/config/search/metatags/context + +dependencies[] = metatag +dependencies[] = context + +; Tests. +files[] = tests/metatag_context.test +files[] = tests/metatag_context.i18n.test diff --git a/metatag_context/metatag_context.module b/metatag_context/metatag_context.module index dfa9b82..6b2d58f 100644 --- a/metatag_context/metatag_context.module +++ b/metatag_context/metatag_context.module @@ -85,6 +85,12 @@ function metatag_context_context_page_reaction() { * Implements hook_page_build(). */ function metatag_context_page_build(&$page) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + return; + } + // Load the meta tags that have been generated for this page. $metatags = drupal_static('metatag_context', array()); @@ -99,6 +105,12 @@ function metatag_context_page_build(&$page) { * Implements hook_preprocess_html(). */ function metatag_context_preprocess_html(&$variables) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + return; + } + $metadata = drupal_static('metatag_context'); if (isset($metadata['metadata_title'])) { diff --git a/metatag_context/metatag_context.test b/metatag_context/metatag_context.test deleted file mode 100644 index d641fa6..0000000 --- a/metatag_context/metatag_context.test +++ /dev/null @@ -1,160 +0,0 @@ - 'Metatag:Context tests', - 'description' => 'Test basic Metatag:Context functionality.', - 'group' => 'Metatag', - ); - } - - /** - * Prepares the testing environment - */ - public function setUp(array $modules = array()) { - $modules[] = 'context'; - $modules[] = 'metatag_context'; - parent::setUp($modules); - - // Create user. - $perms = array( - 'bypass node access', - ); - $this->adminUser = $this->createAdminUser($perms); - $this->drupalLogin($this->adminUser); - - // Create a content type, with underscores. - $type_name = strtolower($this->randomName(8)) . '_test'; - $type = $this->createContentType($type_name, $type_name); - $this->type = $type->type; - - // Store a valid URL name, with hyphens instead of underscores. - $this->hyphen_type = str_replace('_', '-', $this->type); - } - - /** - * Performs the basic tests. - */ - public function testMetatagContextBasic() { - // Create content type node. - $this->drupalPost('node/add/' . $this->hyphen_type, array('title' => $this->randomName(8)), t('Save')); - $this->context_name = drupal_strtolower($this->randomName(8)); - - // Generate metatags and check content. - $this->metatag_pages['node'] = $this->createMetatagObject('node/1', 'node_metatags'); - $this->metatag_pages['page'] = $this->createMetatagObject('', 'frontpage_metatags'); - foreach ($this->metatag_pages as $page) { - $this->generateMetatag($page); - $this->editMetatag($page); - $this->checkMetatags($page); - } - - // Edit metatag and check content. - $this->metatag_pages['node']->title = 'New title'; - $this->metatag_pages['node']->description = ''; - $this->editMetatag($this->metatag_pages['node']); - $this->checkMetatags($this->metatag_pages['node']); - } - - /** - * Creates a metatag object which can be used for generate and check - * the metatag_context module behavior. - * - * @param $path - * Path where generate metatags. - * @param $identifier - * Custom test to identify metatags in source code. - * - * @return $metatag_object - * Metatag mapping object. - */ - function createMetatagObject($path, $identifier) { - $metatag_object = new stdClass(); - $metatag_object->name = $identifier; - $metatag_object->path = $path; - $metatag_object->title = "My $identifier title"; - $metatag_object->description = "My $identifier description"; - $metatag_object->abstract = "My $identifier abstract"; - $metatag_object->keywords = "My $identifier keywords"; - - return $metatag_object; - } - - /** - * Generates metatags by path from a metatag_object instance. - * - * @return $metatag_object - * Metatag mapping object. - */ - function generateMetatag($metatag_object) { - // Verify the "add context" page works. - $this->drupalGet('admin/config/search/metatags/context'); - $this->assertResponse(200); - $this->assertText(t('Add a meta tag by path')); - - // Verify the "add context" page works. - $this->drupalGet('admin/config/search/metatags/context/add'); - $this->assertResponse(200); - $this->assertText(t('The unique ID for this metatag path context rule. This must contain only lower case letters, numbers and underscores.')); - - // Add new Metatag object for this configuration. - $values = array( - 'name' => $metatag_object->name, - ); - - $this->drupalPost('admin/config/search/metatags/context/add', $values, t('Add and configure')); - } - - /** - * Edits metatags by path from a metatag_object instance. - * - * @return $metatag_object - * Metatag mapping object. - */ - function editMetatag($metatag_object) { - $edit_metatag = array( - 'paths' => $metatag_object->path, - 'metatags[und][title][value]' => $metatag_object->title, - 'metatags[und][description][value]' => $metatag_object->description, - 'metatags[und][abstract][value]' => $metatag_object->abstract, - 'metatags[und][keywords][value]' => $metatag_object->keywords, - ); - $this->drupalPost('admin/config/search/metatags/context/' . $metatag_object->name, $edit_metatag, t('Save')); - } - - /** - * Checks if metatags has been added correctly from a metatag_object instance. - * - * @return $metatag_object - * Metatag mapping object. - */ - function checkMetatags($metatag_object) { - $options = array('description', 'abstract', 'keywords'); - $this->drupalGet($metatag_object->path); - - foreach ($options as $option) { - if (!empty($metatag_object->{$option})) { - $this->assertRaw($metatag_object->{$option}, $option . ' found in ' . $metatag_object->path); - } - else { - $this->assertNoRaw('saveTranslationString($lid, $i18n_context, 'fr', $object_en->$tag, $object_fr->$tag); + } + + // Confirm the configuration still works. + $this->checkByPathConfig($object_en); + + // Confirm the French configuration works too. + $this->checkByPathConfig($object_fr); + } + + // @todo Test translations on an exported display. +} diff --git a/metatag_context/tests/metatag_context.test b/metatag_context/tests/metatag_context.test new file mode 100644 index 0000000..4d60025 --- /dev/null +++ b/metatag_context/tests/metatag_context.test @@ -0,0 +1,84 @@ + 'Metatag:Context tests', + 'description' => 'Test basic Metatag:Context functionality.', + 'group' => 'Metatag', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'context'; + $modules[] = 'metatag_context'; + parent::setUp($modules); + + // Create user. + $perms = array( + 'bypass node access', + ); + $this->adminUser = $this->createAdminUser($perms); + $this->drupalLogin($this->adminUser); + + // Create a content type, with underscores. + $type_name = strtolower($this->randomName(8)) . '_test'; + $type = $this->createContentType($type_name, $type_name); + $this->type = $type->type; + + // Store a valid URL name, with hyphens instead of underscores. + $this->hyphen_type = str_replace('_', '-', $this->type); + } + + /** + * Test handling a node. + */ + public function testNode() { + // Create a node. + $this->drupalPost('node/add/' . $this->hyphen_type, array('title' => $this->randomName(8)), t('Save')); + $this->assertResponse(200); + + // Generate metatags and check content. + $test_object = $this->createTestObject('node_metatags', 'node/1'); + $this->generateByPathConfig($test_object); + $this->editByPathConfig($test_object); + $this->checkByPathConfig($test_object); + + // Edit metatag and check content. + $test_object->title = 'New title'; + $test_object->description = ''; + $this->editByPathConfig($test_object); + $this->checkByPathConfig($test_object); + } + + /** + * Test handling the front page. + */ + public function testFrontPage() { + // Generate metatags and check content. + $test_object = $this->createTestObject('frontpage_metatags', ''); + $this->generateByPathConfig($test_object); + $this->editByPathConfig($test_object); + $this->checkByPathConfig($test_object); + + // Edit metatag and check content. + $test_object->title = 'A different title'; + $test_object->description = ''; + $this->editByPathConfig($test_object); + $this->checkByPathConfig($test_object); + } + +} diff --git a/metatag_panels/metatag_panels.i18n.inc b/metatag_panels/metatag_panels.i18n.inc new file mode 100644 index 0000000..40b9649 --- /dev/null +++ b/metatag_panels/metatag_panels.i18n.inc @@ -0,0 +1,153 @@ + t('Metatag:Panels configurations'), + // Callback to load all config objects. + 'list callback' => 'metatag_panels_i18n_list', + // The object load callback. + 'load callback' => 'metatag_panels_i18n_load', + // @todo Custom i18n object overrides. + // 'class' => 'metatag_views_i18n_metatag', + // @todo Is this needed? What does it do? + // 'translation set' => TRUE, + + // The object key field. + // 'key' => 'did', + // Placeholders for automatic paths. This connects the 'key' to strings in + // the paths listed below. + // 'placeholders' => array( + // '%did' => 'did', + // ), + // To produce edit links automatically. + // 'edit path' => 'admin/config/search/metatags/config/%instance', + // Auto-generate a 'translate' tab. + // 'translate tab' => 'admin/config/search/metatags/config/%instance/translate', + + // Properties for string translation. + 'string translation' => array( + // The textgroup, type and (below) name will be concatenated into a single + // string as the {locales_source} context. + 'textgroup' => 'metatag', + 'type' => 'metatag_panels', + // Table where the object is stored, to automate string lists. + 'table' => 'page_manager_handlers', + // Translatable properties of these objects, this will be added later. + 'properties' => array(), + // The path to translate individual strings. + // 'translate path' => 'admin/config/search/metatags/config/%instance/translate/%i18n_language', + ), + ); + + // Compile all of the tags to add to the translation stack. + $meta_tag_info = metatag_get_info(); + $groups = $meta_tag_info['groups']; + foreach ($meta_tag_info['tags'] as $tag_info) { + // Ignore certain field types that aren't translatable, mostly fields that + // list predetermined options in various forms. + if (!empty($tag_info['class']) && $tag_info['class'] == 'DrupalListMetaTag') { + continue; + } + elseif (!empty($tag_info['form']['#type']) && $tag_info['form']['#type'] == 'select') { + continue; + } + elseif (!empty($tag_info['form']['#options'])) { + continue; + } + + // Build a suitable structure for this meta tag. + $tag_name = $tag_info['name']; + $tag_group = $tag_info['group']; + $group_label = isset($groups[$tag_group]['label']) ? $groups[$tag_group]['label'] : $tag_group; + $info['metatag_panels']['string translation']['properties'][$tag_name] = array( + 'title' => $group_label . ': ' . $tag_info['label'], + 'field' => "conf.metatag_panels.metatags.{$tag_name}.value", + ); + } + + return $info; +} + +/** + * Implements hook_i18n_string_list(). + * + * @todo Functionality to delete translation records when Panels are deleted. + */ +// function metatag_panels_i18n_string_list($group) { +// if ($group == 'metatag' || $group == 'all') { +// // Get a list of all strings from the exported configurations. +// ctools_include('export'); +// $handlers = ctools_export_crud_load_all('page_manager_handlers'); +// +// // Look through the handlers for meta tags. +// if (!empty($handlers)) { +// $strings = array(); +// +// foreach ($handlers as $handler) { +// if (!empty($handler)) { +// $config = $handler->conf; +// if (is_string($config)) { +// $config = unserialize($config); +// } +// if (!empty($config['metatag_panels']['enabled']) && !empty($config['metatag_panels']['metatags'])) { +// $new_strings = array(); +// foreach ($config['metatag_panels']['metatags'] as $name => $value) { +// if (isset($value['value'])) { +// // Don't translate meta tags that are arrays. +// if (is_array($value['value'])) { +// continue; +// } +// // Collapse the array down one level. +// else { +// $new_strings[$name] = $value['value']; +// } +// } +// } +// $strings['metatag']['metatag_panels'][$handler->name] = $new_strings; +// } +// } +// } +// +// return $strings; +// } +// } +// } + +/** + * List callback. + */ +function metatag_panels_i18n_list() { + // Load all of the CTools objects. + ctools_include('export'); + $handlers = ctools_export_crud_load_all('page_manager_handlers'); + + if (!empty($handlers)) { + // Unserialize the config array. + foreach ($handlers as $key => $handler) { + if (!empty($handler)) { + if (is_string($handler->conf)) { + $handler->conf = unserialize($handler->conf); + } + if (empty($handler->conf['metatag_panels']['enabled']) || empty($handler->conf['metatag_panels']['metatags'])) { + unset($handlers[$key]); + } + } + } + } + + return $handlers; +} + +/** + * Load callback. + */ +function metatag_panels_i18n_load() { + // dpm(func_get_args()); +} diff --git a/metatag_panels/metatag_panels.info b/metatag_panels/metatag_panels.info index 5b322f1..d84502b 100644 --- a/metatag_panels/metatag_panels.info +++ b/metatag_panels/metatag_panels.info @@ -3,10 +3,9 @@ description = Provides Metatag integration within the Panels interface. package = SEO core = 7.x -dependencies[] = ctools dependencies[] = metatag dependencies[] = panels -dependencies[] = token ; Tests. files[] = tests/metatag_panels.test +files[] = tests/metatag_panels.i18n.test diff --git a/metatag_panels/metatag_panels.module b/metatag_panels/metatag_panels.module index ecf5f6f..e3da100 100644 --- a/metatag_panels/metatag_panels.module +++ b/metatag_panels/metatag_panels.module @@ -53,7 +53,8 @@ function metatag_panels_form($form, $form_state) { '#default_value' => isset($handler->conf['metatag_panels']['enabled']) ? $handler->conf['metatag_panels']['enabled'] : FALSE, ); - // Don't set any metatag instance name as the configuration data is managed locally within panels. + // Don't set any metatag instance name as the configuration data is managed + // locally within panels. $instance = ''; $options = array('token types' => $token_types); $metatags = empty($handler->conf['metatag_panels']) ? array() : $handler->conf['metatag_panels']['metatags']; @@ -112,12 +113,18 @@ function metatag_panels_form($form, $form_state) { function metatag_panels_form_submit($form, $form_state) { $conf = array( 'enabled' => $form_state['values']['metatags_enabled'], - 'metatags' => $form_state['values']['metatags'][LANGUAGE_NONE], + 'metatags' => array(), ); - // Update the i18n strings. - metatag_update_translations($conf['metatags']); + // Only bother saving the meta tags if they were enabled. + if ($conf['enabled']) { + $conf['metatags'] = $form_state['values']['metatags'][LANGUAGE_NONE]; + // Translate the meta tags. + metatag_translations_update($conf['metatags'], 'metatag_panels:' . $form_state['handler']->name); + } + + // Save the values for later. $form_state['handler']->conf['metatag_panels'] = $conf; } @@ -125,6 +132,12 @@ function metatag_panels_form_submit($form, $form_state) { * Implements hook_ctools_render_alter(). */ function metatag_panels_ctools_render_alter($info, $page, $context) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + return; + } + $output = &drupal_static('metatag_panels'); $handler = $context['handler']; @@ -145,7 +158,7 @@ function metatag_panels_ctools_render_alter($info, $page, $context) { } // Translate all of the meta tags using i18n. - metatag_translate_metatags($metatags); + metatag_translate_metatags($metatags[LANGUAGE_NONE], 'metatag_panels:' . $handler->name); // Append global defaults. $all_metatags = array(); @@ -221,6 +234,12 @@ function metatag_panels_ctools_render_alter($info, $page, $context) { * @see metatag_panels_ctools_render_alter() */ function metatag_panels_page_build(&$page) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + return; + } + $metatags = drupal_static('metatag_panels'); if (!empty($metatags)) { diff --git a/metatag_panels/tests/metatag_panels.i18n.test b/metatag_panels/tests/metatag_panels.i18n.test new file mode 100644 index 0000000..03943f3 --- /dev/null +++ b/metatag_panels/tests/metatag_panels.i18n.test @@ -0,0 +1,45 @@ + 'Metatag:Panels i18n tests', + 'description' => 'Test Metatag integration via the Metatag:Panels module.', + 'group' => 'Metatag', + ); + } + + /** + * {@inheritdoc} + */ + function setUp(array $modules = array()) { + $modules[] = 'panels'; + $modules[] = 'metatag_panels'; + + // Can't really do anything without Page Manager. + $modules[] = 'page_manager'; + + // Needed for translations. + $modules[] = 'locale'; + $modules[] = 'i18n'; + $modules[] = 'i18n_string'; + + parent::setUp($modules); + } + + /** + * Test the Metatag:Panels translations. + */ + public function testSomething() { + } + + // @todo Test translations on an exported page. + // @todo Test translations on an in-db page. +} diff --git a/metatag_panels/metatag_panels.test b/metatag_panels/tests/metatag_panels.test similarity index 85% rename from metatag_panels/metatag_panels.test rename to metatag_panels/tests/metatag_panels.test index 012e844..1c9fdf8 100644 --- a/metatag_panels/metatag_panels.test +++ b/metatag_panels/tests/metatag_panels.test @@ -6,18 +6,11 @@ class MetatagPanelsTest extends MetatagTestHelper { /** - * Admin user. - * - * @var \StdClass - */ - protected $adminUser; - - /** * {@inheritdoc} */ public static function getInfo() { return array( - 'name' => 'Metatag Panels tests', + 'name' => 'Metatag:Panels tests', 'description' => 'Test Metatag integration via the Metatag:Panels module.', 'group' => 'Metatag', ); @@ -29,6 +22,9 @@ class MetatagPanelsTest extends MetatagTestHelper { function setUp(array $modules = array()) { $modules[] = 'panels'; + // Can't really do anything without Page Manager. + $modules[] = 'page_manager'; + parent::setUp($modules); } @@ -50,4 +46,6 @@ class MetatagPanelsTest extends MetatagTestHelper { // $this->assertEqual($defaults, $new_values); // } + // @todo Test an exported page. + // @todo Test an in-db page. } diff --git a/metatag_panels/tests/metatag_panels_tests.info b/metatag_panels/tests/metatag_panels_tests.info new file mode 100644 index 0000000..f126afd --- /dev/null +++ b/metatag_panels/tests/metatag_panels_tests.info @@ -0,0 +1,10 @@ +name = Metatag:Panels Tests +description = Helper module for testing metatag_panels.module. +core = 7.x + +; Don't show this on the modules admin page. +hidden = TRUE + +dependencies[] = metatag +dependencies[] = metatag_panels +dependencies[] = panels diff --git a/metatag_panels/tests/metatag_panels_tests.module b/metatag_panels/tests/metatag_panels_tests.module new file mode 100644 index 0000000..e69de29 diff --git a/metatag_views/metatag_views.i18n.inc b/metatag_views/metatag_views.i18n.inc new file mode 100644 index 0000000..f29e653 --- /dev/null +++ b/metatag_views/metatag_views.i18n.inc @@ -0,0 +1,103 @@ + t('Metatag:Views configurations'), + // Callback to load all config objects. + 'list callback' => 'metatag_views_i18n_list', + // The object load callback. + 'load callback' => 'metatag_views_i18n_load', + // @todo Custom i18n object overrides. + // 'class' => 'metatag_views_i18n_metatag', + // @todo Is this needed? What does it do? + // 'translation set' => TRUE, + + // The object key field. + // 'key' => 'did', + // Placeholders for automatic paths. This connects the 'key' to strings in + // the paths listed below. + // 'placeholders' => array( + // '%did' => 'did', + // ), + // To produce edit links automatically. + // 'edit path' => 'admin/config/search/metatags/config/%instance', + // Auto-generate a 'translate' tab. + // 'translate tab' => 'admin/config/search/metatags/config/%instance/translate', + + // Properties for string translation. + 'string translation' => array( + // The textgroup, type and (below) name will be concatenated into a single + // string as the {locales_source} context. + 'textgroup' => 'metatag', + 'type' => 'metatag_views', + // Table where the object is stored, to automate string lists. + 'table' => 'views_display', + // Translatable properties of these objects, this will be added later. + 'properties' => array(), + // The path to translate individual strings. + // 'translate path' => 'admin/config/search/metatags/config/%instance/translate/%i18n_language', + ), + ); + + // Compile all of the tags to add to the translation stack. + $meta_tag_info = metatag_get_info(); + $groups = $meta_tag_info['groups']; + foreach ($meta_tag_info['tags'] as $tag_info) { + // Ignore certain field types that aren't translatable, mostly fields that + // list predetermined options in various forms. + if (!empty($tag_info['class']) && $tag_info['class'] == 'DrupalListMetaTag') { + continue; + } + elseif (!empty($tag_info['form']['#type']) && $tag_info['form']['#type'] == 'select') { + continue; + } + elseif (!empty($tag_info['form']['#options'])) { + continue; + } + + // Build a suitable structure for this meta tag. + $tag_name = $tag_info['name']; + $tag_group = $tag_info['group']; + $group_label = isset($groups[$tag_group]['label']) ? $groups[$tag_group]['label'] : $tag_group; + $info['metatag_views']['string translation']['properties'][$tag_name] = array( + 'title' => $group_label . ': ' . $tag_info['label'], + 'field' => "display_options.metatags.und.{$tag_name}.value", + ); + } + + return $info; +} + +/** + * List callback. + */ +function metatag_views_i18n_list() { + $displays = array(); + + foreach (views_get_all_views() as $view_id => $view) { + foreach ($view->display as $display) { + if (!empty($display->display_options['metatags'][LANGUAGE_NONE])) { + foreach ($display->display_options['metatags'][LANGUAGE_NONE] as $name => $value) { + $name = $view_id . ':' . $display->id; + $displays[$name] = $display; + } + } + } + } + + return $displays; +} + +/** + * Load callback. + */ +function metatag_views_i18n_load($id) { + // dpm(func_get_args()); +} diff --git a/metatag_views/metatag_views.info b/metatag_views/metatag_views.info index 9229dd8..b5f9394 100644 --- a/metatag_views/metatag_views.info +++ b/metatag_views/metatag_views.info @@ -10,3 +10,4 @@ files[] = metatag_views_plugin_display_extender_metatags.inc ; Tests. files[] = tests/metatag_views.test +files[] = tests/metatag_views.i18n.test diff --git a/metatag_views/metatag_views.module b/metatag_views/metatag_views.module index 4250e53..8b2335f 100644 --- a/metatag_views/metatag_views.module +++ b/metatag_views/metatag_views.module @@ -65,6 +65,12 @@ function metatag_views_views_preview_info_alter(&$rows, $view) { * Implements hook_page_alter(). */ function metatag_views_page_alter(&$page) { + // By default do not add meta tags to admin pages. To enable meta tags on + // admin pages set the 'metatag_tag_admin_pages' variable to TRUE. + if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) { + return; + } + $view = views_get_page_view(); // Check if Views metatags are enabled. @@ -92,28 +98,29 @@ function metatag_views_page_alter(&$page) { // Load the meta tags for this view. $metatags = $view->display_handler->get_option('metatags'); - if (!is_array($metatags) || empty($metatags)) { - $metatags = array(); - } - // If meta tags were found but they're not nested for the language, fix it. - // This leaves some possibility for future versions to support translation. - if (!empty($metatags) && !isset($metatags[LANGUAGE_NONE])) { - $metatags = array(LANGUAGE_NONE => $metatags); - } + // Only proceed if there's something to work with. + if (!empty($metatags) && is_array($metatags)) { + // If meta tags were found but they're not nested for the language, fix + // it. This leaves some possibility for future versions to support + // translation. + if (!isset($metatags[LANGUAGE_NONE])) { + $metatags = array(LANGUAGE_NONE => $metatags); + } - // Translate all of the meta tags using i18n. - metatag_translate_metatags($metatags); + // Translate all of the meta tags using i18n. + metatag_translate_metatags($metatags[LANGUAGE_NONE], 'metatag_views:' . $view->name . '_' . $view->current_display); - // Build options for meta tag rendering. - $options = array(); - $options['token data']['view'] = $view; - $options['language'] = $language->language; + // Build options for meta tag rendering. + $options = array(); + $options['token data']['view'] = $view; + $options['language'] = $language->language; - // The page region can be changed. - $region = variable_get('metatag_page_region', 'content'); + // The page region can be changed. + $region = variable_get('metatag_page_region', 'content'); - // Add the metatags. - $page[$region]['metatags'][$instance] = metatag_metatags_view($instance, $metatags, $options); + // Add the metatags. + $page[$region]['metatags'][$instance] = metatag_metatags_view($instance, $metatags, $options); + } } } diff --git a/metatag_views/metatag_views_plugin_display_extender_metatags.inc b/metatag_views/metatag_views_plugin_display_extender_metatags.inc index 91f90f9..14e40b7 100644 --- a/metatag_views/metatag_views_plugin_display_extender_metatags.inc +++ b/metatag_views/metatag_views_plugin_display_extender_metatags.inc @@ -67,9 +67,9 @@ class metatag_views_plugin_display_extender_metatags extends views_plugin_displa $this->display->set_option('metatags', $metatags); - if (!empty($metatags[LANGUAGE_NONE]) && $this->definition['enabled'] && function_exists('i18n_string_update')) { - // Update the i18n strings. - metatag_update_translations($metatags[LANGUAGE_NONE]); + // Update the i18n strings. + if (!empty($metatags[LANGUAGE_NONE]) && $this->definition['enabled'] && module_exists('i18n_string')) { + metatag_translations_update($metatags[LANGUAGE_NONE], 'metatag_views:' . $this->view->name . '_' . $this->display->plugin_name); } } } diff --git a/metatag_views/metatag_views.test b/metatag_views/tests/metatag_views.i18n.test similarity index 75% rename from metatag_views/metatag_views.test rename to metatag_views/tests/metatag_views.i18n.test index 6028720..66e7fde 100644 --- a/metatag_views/metatag_views.test +++ b/metatag_views/tests/metatag_views.i18n.test @@ -4,20 +4,13 @@ * Tests for the Metatag module for the direct Views integration. */ -class MetatagViewsTest extends MetatagTestHelper { - /** - * Admin user. - * - * @var \StdClass - */ - protected $adminUser; - +class MetatagViewsI18nTest extends MetatagTestHelper { /** * {@inheritdoc} */ public static function getInfo() { return array( - 'name' => 'Metatag Views tests', + 'name' => 'Metatag:Views i18n tests', 'description' => 'Test Metatag integration via the Metatag:Views module.', 'group' => 'Metatag', ); @@ -28,6 +21,12 @@ class MetatagViewsTest extends MetatagTestHelper { */ function setUp(array $modules = array()) { $modules[] = 'views'; + $modules[] = 'metatag_views'; + + // Needed for translations. + $modules[] = 'locale'; + $modules[] = 'i18n'; + $modules[] = 'i18n_string'; parent::setUp($modules); } @@ -50,4 +49,6 @@ class MetatagViewsTest extends MetatagTestHelper { // $this->assertEqual($defaults, $new_values); // } + // @todo Test translations on an exported display. + // @todo Test translations on an in-db display. } diff --git a/metatag_views/tests/metatag_views.test b/metatag_views/tests/metatag_views.test new file mode 100644 index 0000000..ecabbf1 --- /dev/null +++ b/metatag_views/tests/metatag_views.test @@ -0,0 +1,36 @@ + 'Metatag:Views tests', + 'description' => 'Test Metatag integration via the Metatag:Views module.', + 'group' => 'Metatag', + ); + } + + /** + * {@inheritdoc} + */ + function setUp(array $modules = array()) { + $modules[] = 'views'; + + parent::setUp($modules); + } + + /** + * Test the Metatag:Views translations. + */ + public function testSomething() { + } + + // @todo Test the master display. + // @todo Test separate displays. +} diff --git a/metatag_views/tests/metatag_views_tests.info b/metatag_views/tests/metatag_views_tests.info new file mode 100644 index 0000000..ad0df53 --- /dev/null +++ b/metatag_views/tests/metatag_views_tests.info @@ -0,0 +1,10 @@ +name = Metatag:Views Tests +description = Helper module for testing metatag_views.module. +core = 7.x + +; Don't show this on the modules admin page. +hidden = TRUE + +dependencies[] = metatag +dependencies[] = metatag_panels +dependencies[] = panels diff --git a/metatag_views/tests/metatag_views_tests.module b/metatag_views/tests/metatag_views_tests.module new file mode 100644 index 0000000..e69de29 diff --git a/tests/metatag.helper.test b/tests/metatag.helper.test index 368b6f4..fc00c68 100644 --- a/tests/metatag.helper.test +++ b/tests/metatag.helper.test @@ -1,10 +1,17 @@ drupalGet('admin/config/development/performance'); + $this->assertResponse(200); + $this->assertText(t('Performance')); + $this->assertText(t('Clear cache')); + $this->drupalPost(NULL, array(), t('Clear all caches')); + $this->assertResponse(200); + $this->assertText(t('Caches cleared')); + } + + /** * Create a content type for the tests. */ function createContentType($machine_name, $label) { @@ -53,10 +75,12 @@ class MetatagTestHelper extends DrupalWebTestCase { * A user object. */ function createAdminUser($extra_permissions = array()) { - // Basic permissions for the module. $permissions = array( + // Basic permissions for the module. 'administer meta tags', 'edit meta tags', + // General admin access. + 'access administration pages', ); // Reset the static variable used to identify permissions, otherwise it's @@ -102,7 +126,7 @@ class MetatagTestHelper extends DrupalWebTestCase { * @return object * A vocabulary object. */ - function createVocabulary($vocab_name = NULL, $content_type = 'article') { + function createVocabulary($vocab_name = NULL, $content_type = NULL) { if (empty($vocab_name)) { $vocab_name = $this->randomName(); } @@ -113,8 +137,10 @@ class MetatagTestHelper extends DrupalWebTestCase { $vocabulary->description = $vocab_name; $vocabulary->machine_name = drupal_strtolower($vocab_name); $vocabulary->help = ''; - $vocabulary->nodes = array($content_type => $content_type); $vocabulary->weight = mt_rand(0, 10); + if (!empty($content_type)) { + $vocabulary->nodes = array($content_type => $content_type); + } taxonomy_vocabulary_save($vocabulary); // Enable meta tags for this new vocabulary. @@ -153,29 +179,407 @@ class MetatagTestHelper extends DrupalWebTestCase { return $term; } + + /** + * Return an list of default values. + * + * This should cover all of the default meta tags provided for a test:foo + * entity. + * + * @todo Expand to cover more meta tags. + * + * @see metatag_test_metatag_config_default() + */ + function getTestDefaults() { + return array( + // Basic meta tags. + 'title' => array('value' => 'Test altered title'), + 'description' => array('value' => 'Test foo description'), + 'abstract' => array('value' => 'Test foo abstract'), + // 'keywords' => array('value' => ''), + + // Advanced meta tags. + // 'robots' => array('value' => ''), + // 'news_keywords' => array('value' => ''), + // 'standout' => array('value' => ''), + // 'robots' => array('value' => ''), + // 'standout' => array('value' => ''), + 'generator' => array('value' => 'Drupal 7 (http://drupal.org)'), + // 'standout' => array('value' => ''), + // 'image_src' => array('value' => ''), + 'canonical' => array('value' => '[current-page:url:absolute]'), + 'shortlink' => array('value' => '[current-page:url:unaliased]'), + // 'publisher' => array('value' => ''), + // 'author' => array('value' => ''), + // 'original-source' => array('value' => ''), + // 'revisit-after' => array('value' => ''), + // 'content-language' => array('value' => ''),' + + // Dublin Core meta tags. + 'dcterms.format' => array('value' => 'text/html'), + 'dcterms.identifier' => array('value' => '[current-page:url:absolute]'), + 'dcterms.title' => array('value' => '[current-page:title]'), + 'dcterms.type' => array('value' => 'Text'), + + // Google+ meta tags. + 'itemprop:name' => array('value' => '[current-page:title]'), + + // Open Graph meta tags. + 'og:site_name' => array('value' => '[site:name]'), + 'og:title' => array('value' => '[current-page:title]'), + 'og:type' => array('value' => 'article'), + 'og:url' => array('value' => '[current-page:url:absolute]'), + + // Twitter Cards meta tags. + 'twitter:card' => array('value' => 'summary'), + 'twitter:title' => array('value' => '[current-page:title]'), + 'twitter:url' => array('value' => '[current-page:url:absolute]'), + ); + } + + /** + * Add a locale to the site. + * + * This assumes the Locale module is enabled. + */ + public function addSiteLanguage($langcode) { + // Load the language-add page. + $this->drupalGet('admin/config/regional/language/add'); + $this->assertResponse(200, 'Loaded the language-add admin page.'); + + // Submit the language-add form. + $args = array( + 'langcode' => $langcode, + ); + $this->drupalPost(NULL, $args, t('Add language')); + $this->assertResponse(200); + + // Verify that the browser was returned to the main languages admin page. + $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Redirected back to the main languages admin page.'); + + // Clear the language list cache so it can be reloaded. + drupal_static_reset('language_list'); + + // Get all language definitions. + $languages = language_list(); + $language = $languages[$langcode]->name; + $this->assertText(strip_tags(t('The language %language has been created and can now be used. More information is available on the help screen.', array('%language' => t($language), '@locale-help' => url('admin/help/locale')))), 'A new language has been added.'); + } + + /** + * Set up a basic starting point for the locales. + * + * This assumes the Locale module is enabled. + */ + public function setupLocales() { + // If there isn't an admin user already, create one. + if (empty($this->adminUser)) { + $perms = array( + 'administer languages', + 'translate interface', + 'access administration pages', + ); + $this->adminUser = $this->createAdminUser($perms); + } + + // Log in as the admin user. + $this->drupalLogin($this->adminUser); + + // Load the admin page, just to have a point of reference. + $this->drupalGet('admin'); + $this->assertResponse(200, 'Loaded the main admin page.'); + + // Add French. + $this->addSiteLanguage('fr'); + + // Add Spanish. + $this->addSiteLanguage('es'); + + // Enable URL language detection and selection. + $edit = array('language[enabled][locale-url]' => '1'); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + $this->assertResponse(200); + } + + /** + * Get the {locales_source} lid value for a specific context. + * + * @param string $context + * The context string to search for. + * + * @return integer + * The {locales_source}.lid value for this string. + */ + function getTranslationLid($context) { + // Extra debug output. + $this->debugLocalesSourcesByContext($context); + + // Look for the string that's actually being requested. + return (int) db_query("SELECT lid + FROM {locales_source} + WHERE textgroup = 'metatag' + AND context = :context", + array(':context' => $context)) + ->fetchField(); + } + + /** + * Generate a debug dump of the {locales_source} records for a specific context. + * + * @param string $context + * The translation context to search against. + */ + function debugLocalesSourcesByContext($context) { + // Get a dump of all i18n strings for Metatag. + $records = db_query("SELECT lid, location, textgroup, source, context, version + FROM {locales_source} + WHERE textgroup = 'metatag'") + ->fetchAllAssoc('lid'); + foreach ($records as $key => $record) { + $records[$key] = (array) $record; + } + $args = array( + 'caption' => 'i18n source check for . ' . $context, + 'header' => array( + 'lid', + 'location', + 'textgroup', + 'source', + 'context', + 'version', + ), + 'rows' => $records, + ); + $this->verbose(theme('table', $args)); + } + + /** + * Save a {locales_target} translation string to the database. + * + * @param int $lid + * The {locales_source}.lid primary key. + * @param string $context + * The {locales_source}.context value for this string. + * @param string $langcode + * The language the string is being translated into. + * @param string $string_source + * The string that is being translated. + * @param string $string_target + * The destination string. + */ + function saveTranslationString($lid, $context, $langcode, $string_source, $string_target) { + // Load the translation page for the front page's title tag. + $this->drupalGet('admin/config/regional/translate/edit/' . $lid); + $this->assertResponse(200, 'Loaded the front page title tag string translation page.'); + $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/edit/' . $lid, array('absolute' => TRUE))); + + // Confirm that the permission-check text is not found. + $this->assertNoText(t('This is a user-defined string. You are not allowed to translate these strings.')); + + // Look for the existing string. The string gets mungled by the Locale + // module, so need to replicate its behaviour. + $this->assertText(check_plain(wordwrap($string_source, 0))); + + // Look for the context value. + $this->assertText($context); + + // Confirm that the destination strings exist. + $this->assertField('translations[fr]', 'Found the French translation string field.'); + $this->assertField('translations[es]', 'Found the Spanish translation string field.'); + + // Translate the string. + $edit = array( + "translations[{$langcode}]" => $string_target, + ); + $this->drupalPost(NULL, $edit, t('Save translations')); + $this->assertResponse(200); + + // Confirm the save worked. + $this->assertText(t('The string has been saved.')); + $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE))); + + // Debug output. + $this->debugLocalesTargetsByContext($context); + + // Clear the Metatag caches. + metatag_flush_caches(); + } + + /** + * Generate a debug dump of the {locales_target} records for a specific context. + * + * @param string $context + * The translation context to search against. + */ + function debugLocalesTargetsByContext($context) { + $records = db_query("SELECT lt.lid, lt.translation, lt.language, lt.plid, lt.plural, lt.i18n_status + FROM {locales_target} lt + INNER JOIN {locales_source} ls + ON lt.lid = ls.lid + WHERE ls.textgroup = 'metatag' + AND ls.context = :context", + array(':context' => $context)) + ->fetchAllAssoc('lid'); + foreach ($records as $key => $record) { + $records[$key] = (array) $record; + } + $args = array( + 'caption' => 'i18n target check for . ' . $context, + 'header' => array( + 'lid', + 'translation', + 'language', + 'plid', + 'plural', + 'i18n_status', + ), + 'rows' => $records, + ); + $this->verbose(theme('table', $args)); + } + + /** + * Creates an object which can be used for generating and checking behavior. + * + * @param string $identifier + * The machine name to identify this object in source code. + * @param string $path + * Path where generate metatags. + * + * @return object + * A mapping object. + */ + function createTestObject($identifier, $path) { + $test_object = new stdClass(); + $test_object->name = $identifier; + $test_object->path = $path; + $test_object->title = "My $identifier title"; + $test_object->description = "My $identifier description"; + $test_object->abstract = "My $identifier abstract"; + $test_object->keywords = "My $identifier keywords"; + + return $test_object; + } + + /** + * Generates meta tags by path from a test_object. + * + * @return $test_object + * Metatag mapping object. + */ + function generateByPathConfig($test_object) { + // Verify the "add context" page works. + $this->drupalGet('admin/config/search/metatags/context'); + $this->assertResponse(200); + $this->assertText(t('Add a meta tag by path')); + + // Verify the "add context" page works. + $this->drupalGet('admin/config/search/metatags/context/add'); + $this->assertResponse(200); + $this->assertText(t('The unique ID for this metatag path context rule. This must contain only lower case letters, numbers and underscores.')); + + // Add new Metatag object for this configuration. + $values = array( + 'name' => $test_object->name, + ); + + $this->drupalPost('admin/config/search/metatags/context/add', $values, t('Add and configure')); + $this->assertResponse(200); + } + + /** + * Edits meta tags by path from a test_object. + * + * @return $test_object + * Metatag mapping object. + */ + function editByPathConfig($test_object) { + $edit = array( + 'paths' => $test_object->path, + 'metatags[und][title][value]' => $test_object->title, + 'metatags[und][description][value]' => $test_object->description, + 'metatags[und][abstract][value]' => $test_object->abstract, + 'metatags[und][keywords][value]' => $test_object->keywords, + ); + $this->drupalPost('admin/config/search/metatags/context/' . $test_object->name, $edit, t('Save')); + $this->assertResponse(200); + } + + /** + * Checks if meta tags have been added correctly from a test_object. + * + * @return $test_object + * Metatag mapping object. + */ + function checkByPathConfig($test_object) { + $this->drupalGet($test_object->path); + $this->assertResponse(200); + + // Manually test the page title. + if (!empty($test_object->title)) { + $this->assertTitle($test_object->title, 'Title found in ' . $test_object->path); + } + + // Test the other meta tags. + $tags = array('description', 'abstract', 'keywords'); + foreach ($tags as $tag) { + if (!empty($test_object->{$tag})) { + $this->assertRaw($test_object->{$tag}, $tag . ' found in ' . $test_object->path); + } + else { + $this->assertNoRaw('assertTrue($count >= 1, 'Found Metatag strings to translate.'); + $this->assertNoText(t('No strings available.')); + $xpath = $this->xpath("//body//div[@class='content']//table//tbody//tr"); + $this->verbose("Found {$count} items to translate."); + if (!empty($string)) { + $this->assertText($context); + $this->assertText('metatag:' . $context); + } + } + else { + $this->assertTrue($count == 1, "Did not find the requested meta tag available to translate, '{$string}', as expected."); + $this->assertText(t('No strings available.')); + $this->assertNoText($context); + $this->assertNoText('metatag:' . $context); + } + } } -// TODO: Test each meta tag. -// TODO: Scenarios. -// -// 1. Node -// * No language assignment. -// * First save. -// -// 2. Node -// * No language assignment. -// * Edit existing revision. -// -// 3. Node -// * No language assignment. -// * Create new revision. -// * Publish new revision. -// -// 4. Node -// * No language assignment. -// * Create new revision. -// * Delete new revision. +// TODO: Scenarios to test. // // 5. Node + Translation // * No language assignment @@ -298,24 +702,12 @@ class MetatagTestHelper extends DrupalWebTestCase { // * Delete new revision. // // -// 30. Node + i18n -// -// -// 50. Term -// * Create term. -// -// 51. Term -// * Create term. -// * Change values. -// -// -// 60. User -// * Create user. -// -// 61. User -// * Create user. -// * Change values. -// -// -// 70. Custom path -// * Defaults loaded. +// 30. Node + i18n? +// +// i18n output? +// Views +// Metatag:Views +// Metatag:Views + i18n +// Panels +// Metatag:Panels +// Metatag:Panels + i18n diff --git a/tests/metatag.locale.test b/tests/metatag.locale.test new file mode 100644 index 0000000..56fa79c --- /dev/null +++ b/tests/metatag.locale.test @@ -0,0 +1,31 @@ + 'Metatag core tests for Locale', + 'description' => 'Test Metatag integration with the locale module.', + 'group' => 'Metatag', + ); + } + + /** + * {@inheritdoc} + */ + function setUp(array $modules = array()) { + // Need Locale for the multiple languages. + $modules[] = 'locale'; + + parent::setUp($modules); + } + + // @todo Make sure the meta tag fields are displayed in the correct locale. + // @todo Make sure the node records are stored with the correct language. +} diff --git a/tests/metatag.node.test b/tests/metatag.node.test index 83e378c..c61d072 100644 --- a/tests/metatag.node.test +++ b/tests/metatag.node.test @@ -1,23 +1,16 @@ 'Metatag UI tests for nodes', + 'name' => 'Metatag core tests for nodes', 'description' => 'Test Metatag edit functionality for nodes.', 'group' => 'Metatag', ); @@ -56,15 +49,16 @@ class MetatagNodeTest extends MetatagTestHelper { // Load the "add default configuration" page. $this->drupalGet('admin/config/search/metatags/config/add'); + $this->assertResponse(200); // Verify the page loaded correct. - $this->assertResponse(200); $this->assertText(t('Select the type of default meta tags you would like to add.')); // Submit the initial form to select an entity bundle. $this->drupalPost(NULL, array( 'instance' => 'node:' . $content_type, ), t('Add and configure')); + $this->assertResponse(200); // Verify the page loaded correct. $this->assertText('Node: ' . $label); @@ -78,17 +72,21 @@ class MetatagNodeTest extends MetatagTestHelper { 'metatags[und][dcterms.identifier][value]' => '[current-page:url:absolute]', 'metatags[und][dcterms.language][value]' => '[node:language]', ), t('Save')); + $this->assertResponse(200); // Verify the page loaded correct. $this->assertText(strip_tags(t('The meta tag defaults for @label have been saved.', array('@label' => 'Node: ' . $label)))); + // Verify that the user was redirected to the settings page again. + $this->assertEqual($this->getUrl(), url('admin/config/search/metatags', array('absolute' => TRUE))); + // Create a test node. // Load the node form. $this->drupalGet('node/add/' . $content_type_path); + $this->assertResponse(200); // Verify the page loaded correctly. - $this->assertResponse(200); // @todo Update this to extract the page title. $this->assertText(strip_tags(t('Create @name', array('@name' => $label)))); @@ -97,6 +95,7 @@ class MetatagNodeTest extends MetatagTestHelper { 'metatags[und][dcterms.subject][value]' => '[node:title] ponies', 'title' => 'Who likes magic', ), t('Save')); + $this->assertResponse(200); // Verify that the node saved correctly. // $xpath = $this->xpath("//h1"); @@ -127,7 +126,7 @@ class MetatagNodeTest extends MetatagTestHelper { // This shouldn't happen, it indicates a problem. else { - $this->fail(t('Could not determine the ID for created node.')); + $this->fail(t('Could not determine the ID for the created node.')); } // Verify the title is using the custom default for this content type. @@ -253,6 +252,7 @@ class MetatagNodeTest extends MetatagTestHelper { $this->assertResponse(200, 'Loaded the form to delete the second revision of this node.'); // Try submitting the form. $this->drupalPost(NULL, array(), t('Delete')); + $this->assertResponse(200); // Verify that the revisions page no longer loads because there's only one // revision now. $this->drupalGet('node/' . $node->nid . '/revisions'); diff --git a/tests/metatag.panels.test b/tests/metatag.panels.test deleted file mode 100644 index b24261f..0000000 --- a/tests/metatag.panels.test +++ /dev/null @@ -1,54 +0,0 @@ - 'Metatag UI tests for Panels', - 'description' => 'Test Metatag integration with the Panels module.', - 'group' => 'Metatag', - ); - } - - /** - * {@inheritdoc} - */ - function setUp(array $modules = array()) { - $modules[] = 'panels'; - - parent::setUp($modules); - } - - /** - * Test the metatag_config_load_with_defaults() function. - */ - // public function testConfigLoadDefaults() { - // // Load the global defaults, inc the fake entity. - // $defaults = metatag_config_load_with_defaults('test:foo'); - // - // // Load the example values and verify they're the same as the globals. - // $extra_tags = array( - // // Fake meta tag. - // 'test:foo' => array('value' => 'foobar'), - // ); - // $new_values = array_merge($extra_tags, $this->getTestDefaults()); - // - // // Confirm that the values are equal. - // $this->assertEqual($defaults, $new_values); - // } - -} diff --git a/tests/metatag.string_handling.test b/tests/metatag.string_handling.test index f759c40..eb158a0 100644 --- a/tests/metatag.string_handling.test +++ b/tests/metatag.string_handling.test @@ -20,7 +20,7 @@ class MetatagStringHandlingTest extends MetatagTestHelper { */ public static function getInfo() { return array( - 'name' => 'Metatag string tests', + 'name' => 'Metatag core tests for string handling', 'description' => "Tests Metatag's string handling.", 'group' => 'Metatag', ); @@ -91,6 +91,7 @@ class MetatagStringHandlingTest extends MetatagTestHelper { 'metatags[und][description][value]' => $desc_original, ); $this->drupalPost('admin/config/search/metatags/config/node', $edit, 'Save'); + $this->assertResponse(200); // Load the configuration object. $result = db_select('metatag_config', 'mc') @@ -112,6 +113,7 @@ class MetatagStringHandlingTest extends MetatagTestHelper { 'title' => $title_original, )); $this->drupalGet('node/' . $node->nid); + $this->assertResponse(200); // assertTitle() uses xpath, which parses the HTML, so all of the HTML // entities will be converted automagically. diff --git a/tests/metatag.string_handling_with_i18n.test b/tests/metatag.string_handling_with_i18n.test index 2f37e1a..145f7c9 100644 --- a/tests/metatag.string_handling_with_i18n.test +++ b/tests/metatag.string_handling_with_i18n.test @@ -14,7 +14,7 @@ class MetatagStringHandlingWithI18nTest extends MetatagStringHandlingTest { */ public static function getInfo() { return array( - 'name' => 'Metatag string tests w i18n', + 'name' => 'Metatag core tests for string handling w i18n', 'description' => "Tests Metatag's string handling when i18n is enabled.", 'group' => 'Metatag', ); @@ -25,21 +25,6 @@ class MetatagStringHandlingWithI18nTest extends MetatagStringHandlingTest { */ function setUp(array $modules = array()) { parent::setUp(array('i18n', 'i18n_string')); - - $content_type = 'page'; - - // Create an admin user and log them in. - $perms = array( - // Needed for the content type. - 'create ' . $content_type . ' content', - 'delete any ' . $content_type . ' content', - 'edit any ' . $content_type . ' content', - - // This permission is required in order to create new revisions. - 'administer nodes', - ); - $this->adminUser = $this->createAdminUser($perms); - $this->drupalLogin($this->adminUser); } } diff --git a/tests/metatag.term.test b/tests/metatag.term.test index f9ba99d..5361c0b 100644 --- a/tests/metatag.term.test +++ b/tests/metatag.term.test @@ -1,23 +1,16 @@ 'Metatag UI tests for terms', + 'name' => 'Metatag core tests for terms', 'description' => 'Test Metatag edit functionality for terms.', 'group' => 'Metatag', ); @@ -80,9 +73,8 @@ class MetatagTermTest extends MetatagTestHelper { $this->drupalPost(NULL, array( 'instance' => 'taxonomy_term:' . $vocabulary->name, ), t('Add and configure')); - - // Verify the page loaded correct. $this->assertResponse(200); + // @todo Update this to extract the H1 tag. $this->assertText(strip_tags('Taxonomy term: ' . $vocabulary->name)); @@ -92,16 +84,18 @@ class MetatagTermTest extends MetatagTestHelper { 'metatags[und][dcterms.format][value]' => 'text/html', 'metatags[und][dcterms.identifier][value]' => '[current-page:url:absolute]', ), t('Save')); + $this->assertResponse(200); // Verify the page loaded correct. $this->assertText(strip_tags(t('The meta tag defaults for @label have been saved.', array('@label' => 'Taxonomy term: ' . $vocabulary->name)))); + // Verify that the user was redirected to the settings page again. + $this->assertEqual($this->getUrl(), url('admin/config/search/metatags', array('absolute' => TRUE))); + // Create a test term. // Load the term form. $this->drupalGet('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add'); - - // Verify the page loaded and didn't give a 404 error. $this->assertResponse(200); // Verify the page loaded correctly. @@ -114,6 +108,7 @@ class MetatagTermTest extends MetatagTestHelper { 'name' => $term_name, 'path[alias]' => $term_path, ), t('Save')); + $this->assertResponse(200); // Verify that the node saved correctly. $t_args = array('%name' => $term_name); @@ -125,8 +120,6 @@ class MetatagTermTest extends MetatagTestHelper { // Verify the term data saved correctly. $this->drupalGet($term_path); - - // Verify the page loaded and didn't give a 404 error. $this->assertResponse(200); // Try to extract the 'edit' link. diff --git a/tests/metatag.unit.test b/tests/metatag.unit.test index 251528d..b15ea87 100644 --- a/tests/metatag.unit.test +++ b/tests/metatag.unit.test @@ -1,9 +1,12 @@ assertEqual($defaults, array( + + // Load the example values and verify they're the same as the globals. + $extra_tags = array( // Fake meta tag. 'test:foo' => array('value' => 'foobar'), + ); + $new_values = array_merge($extra_tags, $this->getTestDefaults()); - // Basic meta tags. - 'title' => array('value' => 'Test altered title'), - 'description' => array('value' => 'Test foo description'), - 'abstract' => array('value' => 'Test foo abstract'), - // 'keywords' => array('value' => ''), - - // Advanced meta tags. - // 'robots' => array('value' => ''), - // 'news_keywords' => array('value' => ''), - // 'standout' => array('value' => ''), - // 'robots' => array('value' => ''), - // 'standout' => array('value' => ''), - 'generator' => array('value' => 'Drupal 7 (http://drupal.org)'), - // 'standout' => array('value' => ''), - // 'image_src' => array('value' => ''), - 'canonical' => array('value' => '[current-page:url:absolute]'), - 'shortlink' => array('value' => '[current-page:url:unaliased]'), - // 'publisher' => array('value' => ''), - // 'author' => array('value' => ''), - // 'original-source' => array('value' => ''), - // 'revisit-after' => array('value' => ''), - // 'content-language' => array('value' => ''),' - - // Dublin Core meta tags. - 'dcterms.format' => array('value' => 'text/html'), - 'dcterms.identifier' => array('value' => '[current-page:url:absolute]'), - 'dcterms.title' => array('value' => '[current-page:title]'), - 'dcterms.type' => array('value' => 'Text'), - - // Google+ meta tags. - 'itemprop:name' => array('value' => '[current-page:title]'), - - // Open Graph meta tags. - 'og:site_name' => array('value' => '[site:name]'), - 'og:title' => array('value' => '[current-page:title]'), - 'og:type' => array('value' => 'article'), - 'og:url' => array('value' => '[current-page:url:absolute]'), - - // Twitter Cards meta tags. - 'twitter:card' => array('value' => 'summary'), - 'twitter:title' => array('value' => '[current-page:title]'), - 'twitter:url' => array('value' => '[current-page:url:absolute]'), - )); + // Confirm that the values are equal. + $this->assertEqual($defaults, $new_values); } public function testEntitySupport() { diff --git a/tests/metatag.user.test b/tests/metatag.user.test index e184849..8d17602 100644 --- a/tests/metatag.user.test +++ b/tests/metatag.user.test @@ -1,23 +1,16 @@ 'Metatag UI tests for users', + 'name' => 'Metatag core tests for users', 'description' => 'Test Metatag edit functionality for users.', 'group' => 'Metatag', ); @@ -35,9 +28,9 @@ class MetatagUserTest extends MetatagTestHelper { // Load the page for overriding the User configuration. $this->drupalGet('admin/config/search/metatags/config/user'); + $this->assertResponse(200); // Verify the page loaded correct. - $this->assertResponse(200); // @todo Update this to extract the H1 tag. $this->assertText(strip_tags('User')); @@ -45,14 +38,16 @@ class MetatagUserTest extends MetatagTestHelper { $this->drupalPost(NULL, array( 'metatags[und][dcterms.subject][value]' => '[user:name]', ), t('Save')); + $this->assertResponse(200); // Verify the page loaded correct. $this->assertText(strip_tags(t('The meta tag defaults for @label have been saved.', array('@label' => 'User')))); + // Verify that the user was redirected to the settings page again. + $this->assertEqual($this->getUrl(), url('admin/config/search/metatags', array('absolute' => TRUE))); + // Load the user's edit form. $this->drupalGet('user/' . $this->adminUser->uid . '/edit'); - - // Verify the page loaded and didn't give a 404 error. $this->assertResponse(200); // Verify the page loaded correctly. @@ -63,6 +58,7 @@ class MetatagUserTest extends MetatagTestHelper { $this->drupalPost(NULL, array( 'metatags[und][dcterms.subject][value]' => '[user:name] ponies', ), t('Save')); + $this->assertResponse(200); // Verify that the node saved correctly. $t_args = array('%name' => $this->adminUser->name); @@ -88,8 +84,6 @@ class MetatagUserTest extends MetatagTestHelper { // Load the user's profile page. $this->drupalGet('user/' . $this->adminUser->uid); - - // Verify the page loaded and didn't give a 404 error. $this->assertResponse(200); // Verify the title is using the custom default for this vocabulary. diff --git a/tests/metatag.views.test b/tests/metatag.views.test deleted file mode 100644 index 34df15e..0000000 --- a/tests/metatag.views.test +++ /dev/null @@ -1,54 +0,0 @@ - 'Metatag UI tests for Views', - 'description' => 'Test Metatag integration with the Views module.', - 'group' => 'Metatag', - ); - } - - /** - * {@inheritdoc} - */ - function setUp(array $modules = array()) { - $modules[] = 'views'; - - parent::setUp($modules); - } - - /** - * Test the metatag_config_load_with_defaults() function. - */ - // public function testConfigLoadDefaults() { - // // Load the global defaults, inc the fake entity. - // $defaults = metatag_config_load_with_defaults('test:foo'); - // - // // Load the example values and verify they're the same as the globals. - // $extra_tags = array( - // // Fake meta tag. - // 'test:foo' => array('value' => 'foobar'), - // ); - // $new_values = array_merge($extra_tags, $this->getTestDefaults()); - // - // // Confirm that the values are equal. - // $this->assertEqual($defaults, $new_values); - // } - -} diff --git a/tests/metatag.with_i18n_config.test b/tests/metatag.with_i18n_config.test new file mode 100644 index 0000000..c546aba --- /dev/null +++ b/tests/metatag.with_i18n_config.test @@ -0,0 +1,152 @@ + 'Metatag core tests with i18n: configs', + 'description' => 'Test Metatag configuration integration with the i18n module.', + 'group' => 'Metatag', + ); + } + + /** + * {@inheritdoc} + */ + function setUp(array $modules = array()) { + // Need the i18n and i18n_strings modules. + $modules[] = 'i18n'; + $modules[] = 'i18n_string'; + + // Enable all of the modules that are needed. + parent::setUp($modules); + + // Set up the locales. + $perms = array( + 'administer languages', + 'translate interface', + // From i18n. + 'translate admin strings', + 'translate user-defined strings', + ); + $this->adminUser = $this->createAdminUser($perms); + $this->setupLocales($perms); + + // Reload the translations. + drupal_flush_all_caches(); + module_load_include('admin.inc', 'i18n_string'); + i18n_string_refresh_group('metatag'); + } + + /** + * Test translation functionality with i18n on config defaults. + */ + public function testI18nDefaultConfig() { + // Plan out the different translation string tests. + $string_en = 'Drupal 7 (http://drupal.org)'; + $string_fr = 'French Drupal'; + $config_name = 'metatag_config:global:generator'; + + // Confirm the translation page exists and has some Metatag strings. + $this->searchTranslationPage('', $config_name); + + // Load the front page. + $this->drupalGet(''); + $this->assertResponse(200, 'Loaded the homepage.'); + + // Confirm the page's title is what we set it to. + $xpath = $this->xpath("//meta[@name='generator']"); + $this->assertEqual(count($xpath), 1, 'Exactly one generator meta tag found.'); + $this->assertEqual($xpath[0]['content'], $string_en); + + // Confirm the string is present. + $this->searchTranslationPage($string_en, $config_name); + + // Get the translation string lid for the generator tag. + $lid = $this->getTranslationLid($config_name); + $this->assertNotEqual($lid, 0, 'Found the locales_source string for the generator tag.'); + + // Save the translation string. + $this->saveTranslationString($lid, $config_name, 'fr', $string_en, $string_fr); + + // Load the English front page again. + $this->drupalGet(''); + $this->assertResponse(200, 'Loaded the default homepage.'); + + // Confirm the page's title is what we set it to. + $xpath = $this->xpath("//meta[@name='generator']"); + $this->assertEqual(count($xpath), 1, 'Exactly one generator meta tag found.'); + $this->assertEqual($xpath[0]['content'], $string_en); + + // Load the French front page. + $this->drupalGet('fr'); + $this->assertResponse(200, 'Loaded the French homepage.'); + + // Confirm the generator string was translated. + $xpath = $this->xpath("//meta[@name='generator']"); + $this->assertEqual(count($xpath), 1, 'Exactly one generator meta tag found.'); + $this->assertEqual($xpath[0]['content'], $string_fr); + } + + /** + * Test translations of the custom configurations. + */ + public function testI18nCustomConfig() { + // Plan out the different translation string tests. + $string_en = 'Welcome to the homepage!'; + $string_fr = 'Bonjour pour le homepage!'; + $config_name = 'metatag_config:global:frontpage:title'; + + // Confirm the translation page exists and has some Metatag strings. + $this->searchTranslationPage('', $config_name); + + // Confirm the string is not present yet. + $this->searchTranslationPage($string_en, $config_name, FALSE); + + // Load the front page's meta tags. + $instance = 'global:frontpage'; + $config = metatag_config_load($instance); + + // Set something specific as the homepage. + $config->config['title']['value'] = $string_en; + metatag_config_save($config); + + // Confirm the string is present now. + $this->searchTranslationPage($string_en, $config_name); + + // Load the front page. + $this->drupalGet(''); + $this->assertResponse(200, 'Loaded the homepage.'); + + // Confirm the page's title is what we set it to. + $this->assertTitle($string_en); + + // Get the translation string lid for the front page's title. + $lid = $this->getTranslationLid($config_name); + $this->assertNotEqual($lid, 0, 'Found the locales_source string for the front page title tag.'); + + // Save the translation string. + $this->saveTranslationString($lid, $config_name, 'fr', $string_en, $string_fr); + + // Load the English front page again. + $this->drupalGet(''); + $this->assertResponse(200, 'Loaded the default homepage.'); + + // Confirm the page's title is what we set it to. + $this->assertTitle($string_en); + + // Load the French front page. + $this->drupalGet('fr'); + $this->assertResponse(200, 'Loaded the French homepage.'); + + // Confirm the page's title is what we set it to. + $this->assertTitle($string_fr); + } + +} diff --git a/tests/metatag.with_i18n_node.test b/tests/metatag.with_i18n_node.test new file mode 100644 index 0000000..16eab6c --- /dev/null +++ b/tests/metatag.with_i18n_node.test @@ -0,0 +1,107 @@ + 'Metatag core tests with i18n: node', + 'description' => 'Test Metatag node config integration with the i18n module.', + 'group' => 'Metatag', + ); + } + + /** + * {@inheritdoc} + */ + function setUp(array $modules = array()) { + // Need the i18n and i18n_strings modules. + $modules[] = 'i18n'; + $modules[] = 'i18n_string'; + + // Enable all of the modules that are needed. + parent::setUp($modules); + + // Set up the locales. + $perms = array( + 'administer languages', + 'translate interface', + // From i18n. + 'translate admin strings', + 'translate user-defined strings', + ); + $this->adminUser = $this->createAdminUser($perms); + $this->setupLocales($perms); + + // Reload the translations. + drupal_flush_all_caches(); + module_load_include('admin.inc', 'i18n_string'); + i18n_string_refresh_group('metatag'); + } + + /** + * Test translations of the main node configuration. + */ + public function testI18nNodeConfig() { + // Plan out the different translation string tests. + $string_en = 'It is a node page!'; + $config_name = 'metatag_config:node:title'; + + // Confirm the translation page exists and has some Metatag strings. + $this->searchTranslationPage('', $config_name); + + // Confirm the string is not present yet. + $this->searchTranslationPage($string_en, $config_name, FALSE); + + // Load the meta tags. + $instance = 'node'; + $config = metatag_config_load($instance); + + // Set something specific as the homepage. + $config->config['title']['value'] = $string_en; + metatag_config_save($config); + + // Confirm the string is present now. + $this->searchTranslationPage($string_en, $config_name); + + // Get the translation string lid for the node's title. + $lid = $this->getTranslationLid($config_name); + $this->assertNotEqual($lid, 0, 'Found the locales_source string for the node title tag.'); + } + + /** + * Test translations of the 'page' content type configuration. + */ + public function testI18nNodePageConfig() { + // Plan out the different translation string tests. + $string_en = 'It is a node page page!'; + $config_name = 'metatag_config:node:page:title'; + + // Confirm the translation page exists and has some Metatag strings. + $this->searchTranslationPage('', $config_name); + + // Confirm the string is not present yet. + $this->searchTranslationPage($string_en, $config_name, FALSE); + + // Create a new config object for the node:page structure. + $config = new StdClass(); + $config->instance = 'node:page'; + + // Set an example tag. + $config->config['title']['value'] = $string_en; + metatag_config_save($config); + + // Confirm the string is present now. + $this->searchTranslationPage($string_en, $config_name); + + // Get the translation string lid for the node's title. + $lid = $this->getTranslationLid($config_name); + $this->assertNotEqual($lid, 0, 'Found the locales_source string for the node title tag.'); + } + +} diff --git a/tests/metatag.with_i18n_output.test b/tests/metatag.with_i18n_output.test new file mode 100644 index 0000000..273d550 --- /dev/null +++ b/tests/metatag.with_i18n_output.test @@ -0,0 +1,101 @@ + 'Metatag core tests with i18n: output', + 'description' => 'Test Metatag integration with the i18n module.', + 'group' => 'Metatag', + ); + } + + /** + * {@inheritdoc} + */ + function setUp(array $modules = array()) { + // Need the i18n and i18n_strings modules. + $modules[] = 'i18n'; + $modules[] = 'i18n_string'; + + // Enable all of the modules that are needed. + parent::setUp($modules); + + // Set up the locales. + $perms = array( + 'administer languages', + 'translate interface', + // From i18n. + 'translate admin strings', + 'translate user-defined strings', + ); + $this->adminUser = $this->createAdminUser($perms); + $this->setupLocales($perms); + + // Reload the translations. + drupal_flush_all_caches(); + module_load_include('admin.inc', 'i18n_string'); + i18n_string_refresh_group('metatag'); + } + + /** + * Test translations of the output tags. + */ + public function testI18nOutput() { + // Plan out the different translation string tests. + $string_en = 'Welcome to the homepage!'; + $string_fr = 'Bonjour pour le homepage!'; + $config_name = 'metatag:frontpage:title'; + + $this->searchTranslationPage('', $config_name); + + // Confirm the string is not present yet. + $this->searchTranslationPage($string_en, $config_name, FALSE); + + // Load the front page's meta tags. + $instance = 'global:frontpage'; + $config = metatag_config_load($instance); + + // Set something specific as the homepage. + $config->config['title']['value'] = $string_en; + metatag_config_save($config); + + // Load the front page. + $this->drupalGet(''); + $this->assertResponse(200, 'Loaded the homepage.'); + + // Confirm the page's title is what we set it to. + $this->assertTitle($string_en); + + // Confirm the string is translatable. + $this->searchTranslationPage($string_en, $config_name); + + // Get the translation string lid for the front page's title. + $lid = $this->getTranslationLid($config_name); + $this->assertNotEqual($lid, 0, 'Found the locales_source string for the front page output title tag.'); + + // Save the translation string. + $this->saveTranslationString($lid, $config_name, 'fr', $string_en, $string_fr); + + // Load the English front page again. + $this->drupalGet(''); + $this->assertResponse(200, 'Loaded the default homepage.'); + + // Confirm the page's title is what we set it to. + $this->assertTitle($string_en); + + // Load the French front page. + $this->drupalGet('fr'); + $this->assertResponse(200, 'Loaded the French homepage.'); + + // Confirm the page's title is what we set it to. + $this->assertTitle($string_fr); + } + +} diff --git a/tests/metatag.with_panels.test b/tests/metatag.with_panels.test new file mode 100644 index 0000000..8fa5fbc --- /dev/null +++ b/tests/metatag.with_panels.test @@ -0,0 +1,176 @@ + 'Metatag core tests with Panels', + 'description' => 'Test Metatag integration with the Panels module.', + 'group' => 'Metatag', + ); + } + + /** + * {@inheritdoc} + */ + function setUp(array $modules = array()) { + // We'll be using Panels but most of the work is actually in Page Manager. + $modules[] = 'page_manager'; + $modules[] = 'panels'; + + parent::setUp($modules); + } + + /** + * Test out a node_view Panels display with Metatag. + */ + public function testPanelsNodeView() { + $content_type = 'metatag_test'; + $content_type_path = str_replace('_', '-', $content_type); + $label = 'Test'; + + // Create a content type. + $this->createContentType($content_type, $label); + + // Create an admin user and log them in. + $perms = array( + // Needed for the content type. + 'create ' . $content_type . ' content', + 'delete any ' . $content_type . ' content', + 'edit any ' . $content_type . ' content', + + // Needed for Panels et al. + 'use page manager', + 'administer page manager', + ); + $this->adminUser = $this->createAdminUser($perms); + $this->drupalLogin($this->adminUser); + + // Create a test node. + + // Load the node form. + $this->drupalGet('node/add/' . $content_type_path); + $this->assertResponse(200); + + // Verify the page loaded correctly. + // @todo Update this to extract the page title. + $this->assertText(strip_tags(t('Create @name', array('@name' => $label)))); + + // Verify that it's possible to submit values to the form. + $this->drupalPost(NULL, array( + 'metatags[und][dcterms.subject][value]' => '[node:title] ponies', + 'title' => 'Who likes magic', + ), t('Save')); + $this->assertResponse(200); + + // Verify that the node saved correctly. + // $xpath = $this->xpath("//h1"); + $t_args = array('@type' => 'Test', '%title' => 'Who likes magic'); + // This doesn't work for some reason, it seems the HTML is stripped off + // during output so the %title's standard Drupal wrappers don't match. + // $this->assertText(t('@type %title has been created.', $t_args)); + // .. so this has to be done instead. + $this->assertText(strip_tags(t('@type %title has been created.', $t_args))); + + // Save the node URL for later. + $node_url = $this->getUrl(); + + // Confirm the tags load correctly. + $this->confirmNodeTags(); + + // Enable the Page Manager display for nodes. + variable_set('page_manager_node_view_disabled', FALSE); + + // Confirm that the main Panels page loads correctly. + $this->drupalGet('admin/structure/pages'); + $this->assertResponse(200); + + // Confirm that the Panels node_view page loads. + $this->drupalGet('admin/structure/pages/edit/node_view'); + $this->assertResponse(200); + + // Confirm that a new variant can be added. + $this->drupalGet('admin/structure/pages/nojs/operation/node_view/actions/add'); + $this->assertResponse(200); + + // Create a variant. This is done as a series of form submissions. + $args = array( + 'title' => 'Test', + 'name' => 'test', + 'handler' => 'panel_context', + ); + $this->drupalPost('admin/structure/pages/nojs/operation/node_view/actions/add', $args, t('Create variant')); + $this->assertResponse(200); + $args = array( + 'layout' => 'flexible', + ); + $this->drupalPost(NULL, $args, t('Continue')); + $this->assertResponse(200); + $args = array(); + $this->drupalPost(NULL, $args, t('Continue')); + $this->assertResponse(200); + $args = array(); + $this->drupalPost(NULL, $args, t('Create variant')); + $this->assertResponse(200); + $this->assertText(t('The page has been updated. Changes will not be permanent until you save.')); + $args = array(); + $this->drupalPost(NULL, $args, t('Save')); + $this->assertResponse(200); + + // Confirm the process completed successfully. + $this->assertEqual($this->getUrl(), url('admin/structure/pages/edit/node_view', array('absolute' => TRUE))); + $this->assertText(t('Panel') . ': Test'); + + // Clear all caches, so we can start off properly. + drupal_flush_all_caches(); + + // Load the node page again. + $this->confirmNodeTags($node_url); + } + + /** + * Confirm that the meta tags for the requested node load properly. + * + * @param string $path + * Optional path to load, if not present the current path will be used. + */ + function confirmNodeTags($path = NULL) { + if (!empty($path)) { + $this->drupalGet($path); + $this->assertResponse(200); + } + + // Verify the title is using the custom default for this content type. + $xpath = $this->xpath("//meta[@name='dcterms.subject']"); + $this->assertEqual(count($xpath), 1, 'Exactly one dcterms.subject meta tag found.'); + $this->assertEqual($xpath[0]['content'], 'Who likes magic ponies'); + } + + /** + * Test out a term_view Panels display with Metatag. + * + * @todo Write this. + */ + // public function testPanelsTermView() { + // // Enable the Page Manager display for terms. + // variable_set('page_manager_term_view_disabled', FALSE); + // } + + /** + * Test out a user_view Panels display with Metatag. + * + * @todo Write this. + */ + // public function testPanelsUserView() { + // // Enable the Page Manager display for users. + // variable_set('page_manager_user_view_disabled', FALSE); + // } + +} diff --git a/tests/metatag.term.test b/tests/metatag.with_views.test similarity index 71% copy from tests/metatag.term.test copy to tests/metatag.with_views.test index f9ba99d..00d5c3c 100644 --- a/tests/metatag.term.test +++ b/tests/metatag.with_views.test @@ -1,24 +1,18 @@ 'Metatag UI tests for terms', - 'description' => 'Test Metatag edit functionality for terms.', + 'name' => 'Metatag core tests with Views', + 'description' => 'Test Metatag integration with the Views module.', 'group' => 'Metatag', ); } @@ -27,34 +21,30 @@ class MetatagTermTest extends MetatagTestHelper { * {@inheritdoc} */ function setUp(array $modules = array()) { + $modules[] = 'views'; + $modules[] = 'views_ui'; + // Need the Path module to set a alias which can then be loaded. $modules[] = 'path'; + parent::setUp($modules); } /** - * Tests creation of a standard entity. + * Test the taxonomy term page still works when displayed via Views. */ - public function testEntityCreationWorkflow() { - $content_type = 'metatag_test'; - $content_type_path = str_replace('_', '-', $content_type); - $content_type_label = 'Test'; + public function testTermViews() { $vocab_name = 'test_vocab'; $term_name = 'Who likes magic'; $term_path = 'term-test'; - // Create a content type. - $this->createContentType($content_type, $content_type_label); - // Create a vocabulary. - $vocabulary = $this->createVocabulary($vocab_name, $content_type); + $vocabulary = $this->createVocabulary($vocab_name); // Create an admin user and log them in. $perms = array( - // Needed for the content type. - 'create ' . $content_type . ' content', - 'delete any ' . $content_type . ' content', - 'edit any ' . $content_type . ' content', + // Global admin access. + 'administer site configuration', // Needed for the vocabulary. 'administer taxonomy', @@ -63,12 +53,13 @@ class MetatagTermTest extends MetatagTestHelper { // Needed for the Path module. 'create url aliases', + + // Needed for Views. + 'administer views', ); $this->adminUser = $this->createAdminUser($perms); $this->drupalLogin($this->adminUser); - // Assign default values for the new vocabulary. - // Load the "add default configuration" page. $this->drupalGet('admin/config/search/metatags/config/add'); $this->assertResponse(200); @@ -80,9 +71,8 @@ class MetatagTermTest extends MetatagTestHelper { $this->drupalPost(NULL, array( 'instance' => 'taxonomy_term:' . $vocabulary->name, ), t('Add and configure')); - - // Verify the page loaded correct. $this->assertResponse(200); + // @todo Update this to extract the H1 tag. $this->assertText(strip_tags('Taxonomy term: ' . $vocabulary->name)); @@ -92,16 +82,18 @@ class MetatagTermTest extends MetatagTestHelper { 'metatags[und][dcterms.format][value]' => 'text/html', 'metatags[und][dcterms.identifier][value]' => '[current-page:url:absolute]', ), t('Save')); + $this->assertResponse(200); // Verify the page loaded correct. $this->assertText(strip_tags(t('The meta tag defaults for @label have been saved.', array('@label' => 'Taxonomy term: ' . $vocabulary->name)))); + // Verify that the user was redirected to the settings page again. + $this->assertEqual($this->getUrl(), url('admin/config/search/metatags', array('absolute' => TRUE))); + // Create a test term. // Load the term form. $this->drupalGet('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add'); - - // Verify the page loaded and didn't give a 404 error. $this->assertResponse(200); // Verify the page loaded correctly. @@ -114,6 +106,7 @@ class MetatagTermTest extends MetatagTestHelper { 'name' => $term_name, 'path[alias]' => $term_path, ), t('Save')); + $this->assertResponse(200); // Verify that the node saved correctly. $t_args = array('%name' => $term_name); @@ -125,10 +118,32 @@ class MetatagTermTest extends MetatagTestHelper { // Verify the term data saved correctly. $this->drupalGet($term_path); + $this->assertResponse(200); + + // Confirm the page is managed by the default taxonomy term page. + $this->assertText(t('There is currently no content classified with this term.')); - // Verify the page loaded and didn't give a 404 error. + // Enable the taxonomy_term default display. + $view = views_get_view('taxonomy_term'); + ctools_include('export'); + ctools_export_set_object_status($view, 0); + $this->clearAllCaches(); + + $this->drupalGet('admin/structure/views'); $this->assertResponse(200); + $vars = variable_get('views_defaults'); + $this->verbose(print_r($vars, TRUE)); + $this->assertFalse($vars['taxonomy_term'], 'Taxonomy term page is enabled.'); + + // Load the page again. + $this->drupalGet($term_path); + $this->assertResponse(200); + + // Confirm this page is managed by Views - if it's managed by the default + // taxonomy term page this text will show. + $this->assertNoText(t('There is currently no content classified with this term.')); + // Try to extract the 'edit' link. $xpaths = $this->xpath("//ul/li/a"); $matches = array(); @@ -169,4 +184,5 @@ class MetatagTermTest extends MetatagTestHelper { $this->assertEqual(count($xpath), 1, 'Exactly one dcterms.subject meta tag found.'); $this->assertEqual($xpath[0]['content'], "Who likes magic ponies"); } + } diff --git a/tests/metatag_test.info b/tests/metatag_test.info index 2d03b1e..0ebc21d 100644 --- a/tests/metatag_test.info +++ b/tests/metatag_test.info @@ -1,5 +1,8 @@ name = Meta Tag Test -description = Testing module for metatag.module +description = Helper module for testing metatag.module. core = 7.x -dependencies[] = metatag + +; Don't show this on the modules admin page. hidden = TRUE + +dependencies[] = metatag diff --git a/tests/metatag_test.metatag.inc b/tests/metatag_test.metatag.inc index fd7e0d4..98f7993 100644 --- a/tests/metatag_test.metatag.inc +++ b/tests/metatag_test.metatag.inc @@ -2,6 +2,10 @@ /** * Implements hook_metatag_config_default(). + * + * @todo Expand to cover more meta tags. + * + * @see MetatagTestHelper::getTestDefaults() */ function metatag_test_metatag_config_default() { $configs = array(); @@ -20,9 +24,9 @@ function metatag_test_metatag_config_default() { $config->api_version = 1; $config->disabled = FALSE; $config->config = array( - 'description' => array('value' => 'Test foo description'), - 'abstract' => array('value' => 'Test foo abstract'), 'title' => array('value' => 'Test title'), + 'abstract' => array('value' => 'Test foo abstract'), + 'description' => array('value' => 'Test foo description'), 'test:foo' => array('value' => 'foobar'), ); $configs[$config->instance] = $config;