Browse Source

updated context, metatag, imagestyleflush, browscap

Bachir Soussi Chiadmi 7 years ago
parent
commit
3413d81bb8
100 changed files with 6022 additions and 1561 deletions
  1. 3 3
      sites/all/modules/contrib/admin/context/context.info
  2. 1 1
      sites/all/modules/contrib/admin/context/context.install
  3. 55 5
      sites/all/modules/contrib/admin/context/context.module
  4. 2 2
      sites/all/modules/contrib/admin/context/context.plugins.inc
  5. 3 3
      sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info
  6. 3 3
      sites/all/modules/contrib/admin/context/context_ui/context_ui.info
  7. 2 2
      sites/all/modules/contrib/admin/context/context_ui/context_ui.js
  8. 8 12
      sites/all/modules/contrib/admin/context/context_ui/context_ui.module
  9. 2 2
      sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js
  10. 11 11
      sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php
  11. 1 1
      sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test
  12. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc
  13. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc
  14. 9 5
      sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc
  15. 4 5
      sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js
  16. 1 0
      sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc
  17. 3 1
      sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc
  18. 1 1
      sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc
  19. 0 22
      sites/all/modules/contrib/admin/context/tests/context.conditions.test
  20. 3 4
      sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc
  21. 18 6
      sites/all/modules/contrib/files/imagestyleflush/README.txt
  22. 4 5
      sites/all/modules/contrib/files/imagestyleflush/imagestyleflush.info
  23. 5 5
      sites/all/modules/contrib/files/imagestyleflush/imagestyleflush.module
  24. 21 3
      sites/all/modules/contrib/responsive/browscap/README.txt
  25. 2 6
      sites/all/modules/contrib/responsive/browscap/browscap.admin.inc
  26. 64 0
      sites/all/modules/contrib/responsive/browscap/browscap.drush.inc
  27. 3 3
      sites/all/modules/contrib/responsive/browscap/browscap.info
  28. 26 6
      sites/all/modules/contrib/responsive/browscap/browscap.install
  29. 2 5
      sites/all/modules/contrib/responsive/browscap/browscap.module
  30. 100 40
      sites/all/modules/contrib/responsive/browscap/import.inc
  31. 454 1
      sites/all/modules/contrib/seo/metatag/CHANGELOG.txt
  32. 3 0
      sites/all/modules/contrib/seo/metatag/CODE_OF_CONDUCT.txt
  33. 218 110
      sites/all/modules/contrib/seo/metatag/README.txt
  34. 74 33
      sites/all/modules/contrib/seo/metatag/metatag.admin.inc
  35. 164 99
      sites/all/modules/contrib/seo/metatag/metatag.api.php
  36. 7 0
      sites/all/modules/contrib/seo/metatag/metatag.features.inc
  37. 17 8
      sites/all/modules/contrib/seo/metatag/metatag.feeds.inc
  38. 131 2
      sites/all/modules/contrib/seo/metatag/metatag.i18n.inc
  39. 200 13
      sites/all/modules/contrib/seo/metatag/metatag.inc
  40. 65 6
      sites/all/modules/contrib/seo/metatag/metatag.info
  41. 836 161
      sites/all/modules/contrib/seo/metatag/metatag.install
  42. 192 30
      sites/all/modules/contrib/seo/metatag/metatag.metatag.inc
  43. 23 19
      sites/all/modules/contrib/seo/metatag/metatag.migrate.inc
  44. 497 259
      sites/all/modules/contrib/seo/metatag/metatag.module
  45. 78 0
      sites/all/modules/contrib/seo/metatag/metatag.search_api.inc
  46. 0 261
      sites/all/modules/contrib/seo/metatag/metatag.test
  47. 25 5
      sites/all/modules/contrib/seo/metatag/metatag.theme.inc
  48. 142 10
      sites/all/modules/contrib/seo/metatag/metatag.tokens.inc
  49. 14 0
      sites/all/modules/contrib/seo/metatag/metatag.variable.inc
  50. 40 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/README.txt
  51. 15 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.info
  52. 162 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.metatag.inc
  53. 15 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.module
  54. 44 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/tests/metatag_app_links.test
  55. 3 3
      sites/all/modules/contrib/seo/metatag/metatag_context/README.txt
  56. 25 11
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.admin.inc
  57. 82 27
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.context.inc
  58. 126 0
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.i18n.inc
  59. 11 6
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.info
  60. 13 0
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.metatag.inc
  61. 30 13
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.module
  62. 0 145
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.test
  63. 133 0
      sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context.i18n.test
  64. 109 0
      sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context.test
  65. 56 0
      sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context_tests.context.inc
  66. 17 0
      sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context_tests.info
  67. 35 0
      sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context_tests.module
  68. 6 6
      sites/all/modules/contrib/seo/metatag/metatag_dc/README.txt
  69. 6 3
      sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.info
  70. 12 0
      sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.install
  71. 39 104
      sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.metatag.inc
  72. 3 2
      sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.module
  73. 44 0
      sites/all/modules/contrib/seo/metatag/metatag_dc/tests/metatag_dc.test
  74. 52 0
      sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/README.txt
  75. 16 0
      sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/metatag_dc_advanced.info
  76. 268 0
      sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/metatag_dc_advanced.metatag.inc
  77. 14 0
      sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/metatag_dc_advanced.module
  78. 44 0
      sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/tests/metatag_dc_advanced.test
  79. 7 4
      sites/all/modules/contrib/seo/metatag/metatag_devel/metatag_devel.info
  80. 6 1
      sites/all/modules/contrib/seo/metatag/metatag_devel/metatag_devel.module
  81. 44 0
      sites/all/modules/contrib/seo/metatag/metatag_devel/tests/metatag_devel.test
  82. 6 3
      sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.info
  83. 11 1
      sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.metatag.inc
  84. 11 4
      sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.module
  85. 44 0
      sites/all/modules/contrib/seo/metatag/metatag_facebook/tests/metatag_facebook.test
  86. 18 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.info
  87. 16 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.install
  88. 104 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.mask-icon.class.inc
  89. 281 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.metatag.inc
  90. 158 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.module
  91. 23 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/tests/druplicon-vector.svg
  92. 210 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/tests/metatag_favicons.test
  93. 9 25
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/README.txt
  94. 6 3
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.info
  95. 24 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.install
  96. 9 12
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.metatag.inc
  97. 13 7
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.module
  98. 44 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/tests/metatag_google_plus.test
  99. 39 0
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/README.txt
  100. 20 0
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.info

+ 3 - 3
sites/all/modules/contrib/admin/context/context.info

@@ -8,9 +8,9 @@ files[] = tests/context.test
 files[] = tests/context.conditions.test
 files[] = tests/context.reactions.test
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-05-18
+version = "7.x-3.7"
 core = "7.x"
 project = "context"
-datestamp = "1420573188"
+datestamp = "1463605446"
 

+ 1 - 1
sites/all/modules/contrib/admin/context/context.install

@@ -103,7 +103,7 @@ function context_update_7000() {
     }
   }
   if (empty($updated)) {
-    $ret = t('No contexts requiring migration detected');
+    $ret = t('No contexts requiring migration detected.');
   }
   else {
     $ret = t('The following contexts had theme reaction data migrated: @names', array('@names' => join(', ', $updated)));

+ 55 - 5
sites/all/modules/contrib/admin/context/context.module

@@ -192,11 +192,61 @@ function context_init() {
  * theme('menu_link') for the menu rendering to html.
  */
 function context_preprocess_menu_link(&$variables) {
-  if($contexts = context_active_contexts()){
-    foreach($contexts as $context){
-      if((isset($context->reactions['menu']))){
-        if ($variables['element']['#href'] == $context->reactions['menu']) {
-          $variables['element']['#localized_options']['attributes']['class'][] = "active";
+  if ($contexts = context_active_contexts()) {
+    foreach ($contexts as $context) {
+      if (isset($context->reactions['menu'])) {
+        // In context module < v3.2 the url was a string. In version 3.3+ this is
+        // an array of urls. Implement interims BC layer.
+        //
+        // Examples:
+        // - OLD < v3.2 context reaction structure:
+        // array('menu' => 'taxonomy/term/1')
+        //
+        // - NEW 3.3+ context reaction structure:
+        // array(
+        //   'menu' => array(
+        //     0 => 'navigation:taxonomy/term/1'
+        //     1 => 'foo-menu:taxonomy/term/1'
+        //   )
+        // )
+        $reactions_menu = is_array($context->reactions['menu']) ? array_values($context->reactions['menu']) : array($context->reactions['menu']);
+
+        // Get everything after the first ':' character (if found) as the url to
+        // match against element '#href'.
+        $urls = array();
+        foreach ($reactions_menu as $url) {
+          if (strpos($url, ':') !== FALSE) {
+            // Get unique menu name 'navigation' from 'navigation:taxonomy/term/1'
+            $reaction_menu = explode(':', $url);
+            $path = $reaction_menu[1];
+            $urls[$path] = $reaction_menu[0];
+          }
+          else {
+            // BC layer for menu contexts that have not re-saved. This is for
+            // urls like 'taxonomy/term/1'. We need to add a fake menu key
+            // 'bc-context-menu-layer' or the BC link get's removed by
+            // array_intersect below.
+            //
+            // @TODO: Remove BC layer in 4.x
+            $urls[$url] = 'context-reaction-menu-bc-layer';
+          }
+        }
+
+        // Filter urls by the menu name of the current link. The link reaction
+        // can be configured per menu link in specific menus and the contect
+        // reaction should not applied to other menus with the same menu link.
+        $menu_name = $variables['element']['#original_link']['menu_name'];
+        $menu_paths = array_intersect($urls, array($menu_name, 'context-reaction-menu-bc-layer'));
+        $reaction_menu_paths = array_keys($menu_paths);
+
+        // - If menu href and context reaction menu url match, add the 'active'
+        //   css class to the link of this menu.
+        // - Do not add class twice on current page.
+        if (in_array($variables['element']['#href'], $reaction_menu_paths) && $variables['element']['#href'] != $_GET['q']) {
+          // Do not add the 'active' class twice in views tabs.
+          if (!in_array('active', $variables['element']['#localized_options']['attributes']['class'])) {
+            $variables['element']['#localized_options']['attributes']['class'][] = 'active';
+          }
         }
       }
     }

+ 2 - 2
sites/all/modules/contrib/admin/context/context.plugins.inc

@@ -37,7 +37,7 @@ function _context_context_registry() {
       'plugin' => 'context_condition_path',
     ),
     'query_string' => array(
-      'title' => t('Query String'),
+      'title' => t('Query string'),
       'description' => t('Set this context when any of the query strings above match the page query string. Put each query string on a separate line. You can use the "*" character as a wildcard and <code>~</code> to exclude one or more query strings.'),
       'plugin' => 'context_condition_query_string',
     ),
@@ -119,7 +119,7 @@ function _context_context_registry() {
       'plugin' => 'context_reaction_template_suggestions',
     ),
     'theme' => array(
-      'title' => t('Theme Page'),
+      'title' => t('Theme page'),
       'description' => t('Control page theme variables using context.'),
       'plugin' => 'context_reaction_theme',
     ),

+ 3 - 3
sites/all/modules/contrib/admin/context/context_layouts/context_layouts.info

@@ -6,9 +6,9 @@ core = 7.x
 
 files[] = plugins/context_layouts_reaction_block.inc
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-05-18
+version = "7.x-3.7"
 core = "7.x"
 project = "context"
-datestamp = "1420573188"
+datestamp = "1463605446"
 

+ 3 - 3
sites/all/modules/contrib/admin/context/context_ui/context_ui.info

@@ -8,9 +8,9 @@ configure = admin/structure/context
 files[] = context.module
 files[] = tests/context_ui.test
 
-; Information added by Drupal.org packaging script on 2015-01-06
-version = "7.x-3.6"
+; Information added by Drupal.org packaging script on 2016-05-18
+version = "7.x-3.7"
 core = "7.x"
 project = "context"
-datestamp = "1420573188"
+datestamp = "1463605446"
 

+ 2 - 2
sites/all/modules/contrib/admin/context/context_ui/context_ui.js

@@ -12,11 +12,11 @@ function DrupalContextPlugins(form) {
     $('.context-plugin-list > li', this.form).each(function() {
       var plugin = $(this).attr('class').split('context-plugin-')[1].split(' ')[0];
       if ($(this).is('.disabled')) {
-        $('.context-plugin-selector select option[value='+plugin+']', this.form).show();
+        $('.context-plugin-selector select option[value="'+plugin+'"]', this.form).show();
       }
       else {
         state.push(plugin);
-        $('.context-plugin-selector select option[value='+plugin+']', this.form).hide();
+        $('.context-plugin-selector select option[value="'+plugin+'"]', this.form).hide();
       }
     });
     // Set the hidden plugin list state.

+ 8 - 12
sites/all/modules/contrib/admin/context/context_ui/context_ui.module

@@ -81,7 +81,7 @@ function context_ui_permission() {
     'description' => 'Associate menus, views, blocks, etc. with different contexts to structure your site.'
   );
   $permissions['context ajax block access'] = array(
-    'title' => t('Access All Blocks'),
+    'title' => t('Access all blocks'),
     'description' => t('Allows users to access all rendered blocks via an AJAX callback. If you have some blocks that should not be rendered for some users but need those users to be able to use context UI, then implement hook_context_allow_ajax_block_access with the necessary logic.'),
   );
   return $permissions;
@@ -143,7 +143,7 @@ function context_ui_editor($form, &$form_state, $contexts) {
 
   $form['title'] = array(
     '#prefix' => '<h2 class="context-editor-title">',
-    '#markup' => t('Select the Context/Layer to Edit'),
+    '#markup' => t('Select the context/layer to edit'),
     '#suffix' => '</h2>',
     '#weight' => -2,
   );
@@ -151,10 +151,7 @@ function context_ui_editor($form, &$form_state, $contexts) {
   //add some help text to the top of the form
   $form['help'] = array (
     '#prefix' => '<p class="context-help help">',
-    '#markup' => t('Select which context, or layer of blocks, to edit.
-                    Each context is configured to appear on different sets of pages so read the description carefully.
-                    When you are done editing click Done and save your changes.
-                    You may use the Stop Editing Layout link to close the editor.'),
+    '#markup' => t('Select which context, or layer of blocks, to edit. Each context is configured to appear on different sets of pages so read the description carefully. When you are done editing click Done and save your changes. You may use the Stop Editing Layout link to close the editor.'),
     '#suffix' => '</p>',
     '#weight' => -1,
   );
@@ -166,10 +163,9 @@ function context_ui_editor($form, &$form_state, $contexts) {
     $edit = l(t('Edit'), $_GET['q'], array('fragment' => $context->name, 'attributes' => array('class' => array('edit'))));
     $done = l(t('Done'), $_GET['q'], array('fragment' => $context->name, 'attributes' => array('class' => array('done'))));
     $readable_name = ucwords(str_replace('_', ' ', $context->name));
-    $description = empty($context->description) ? '' :
-                        "<br/><div class='label bottom'>".check_plain($context->description)."</div>";
+    $description = empty($context->description) ? '' : '<br/><div class="label bottom">' . check_plain($context->description) . '</div>';
     $items[] = array(
-      'data' => "<div class='label top'>" . $readable_name. "</div><div class='links'>{$edit} {$done}</div>" . $description,
+      'data' => '<div class="label top">' . $readable_name. "</div><div class='links'>{$edit} {$done}</div>" . $description,
       'class' => array('context-editable clearfix'),
       'id' => "context-editable-trigger-{$context->name}",
     );
@@ -306,9 +302,9 @@ function context_ui_settings($form, &$form_state) {
   }
   $form['context_ui_dialog_enabled'] = array(
     '#type' => 'checkbox',
-    '#title' => t('Use Context Editor Dialog'),
+    '#title' => t('Use context editor dialog'),
     '#default_value' => context_ui_dialog_is_enabled(),
-    '#description' => t('When enabled all contextual links will have a Edit Layout link that will refresh the page with the context editor in a dialog box.'),
+    '#description' => t('When enabled all contextual links will have a <em>Edit layout</em> link that will refresh the page with the context editor in a dialog box.'),
   );
   $form = system_settings_form($form);
   $form['#submit'][] = 'context_ui_settings_submit';
@@ -374,7 +370,7 @@ function context_ui_menu_contextual_links_alter(&$links, $router_item, $root_pat
      !context_isset('context_ui', 'context_ui_editor_present') && user_access('administer contexts')) {
     $links['layout'] = array(
       'href' => 'context-ui/activate',
-      'title' => t('Configure Layout'),
+      'title' => t('Configure layout'),
       'localized_options' => array(
         'query' =>  array('destination'=> $_GET['q']),
         'options' => array('html' => FALSE, 'attributes' => array()),

+ 2 - 2
sites/all/modules/contrib/admin/context/context_ui/context_ui_dialog.js

@@ -9,11 +9,11 @@
         selector.detach();
         $('#page').prepend(selector);
 
-        var labelOpen = Drupal.t('Select Context');
+        var labelOpen = Drupal.t('Select context');
         var labelClose = Drupal.t('Hide');
 
         // Create a tab to show/hide our edit area
-        var tab = $('<a href="javascript:" class="context-ui-dialog-open" title="Show Context Selector">'+labelClose+'</a>');
+        var tab = $('<a href="javascript:" class="context-ui-dialog-open" title="' + Drupal.t('Show context selector') + '">'+labelClose+'</a>');
         selector.append(tab);
 
         selector.toggled = false;

+ 11 - 11
sites/all/modules/contrib/admin/context/context_ui/export_ui/context_export_ui.class.php

@@ -22,7 +22,7 @@ class context_export_ui extends ctools_export_ui {
   function list_render(&$form_state) {
     $table = array(
       'header' => $this->list_table_header(),
-      'rows' => $this->rows, 
+      'rows' => $this->rows,
       'attributes' => array(
         'class' => array('context-admin'),
         'id' => 'ctools-export-ui-list-items',
@@ -104,17 +104,17 @@ class context_export_ui extends ctools_export_ui {
  * @param $form_state
  *   Form state array
  */
-function context_ui_form(&$form, &$form_state) {  
+function context_ui_form(&$form, &$form_state) {
   $conditions = array_keys(context_conditions());
   sort($conditions);
   $reactions = array_keys(context_reactions());
   sort($reactions);
-    
+
   $context = $form_state['item'];
   if (!empty($form_state['input'])) {
     $context = _context_ui_rebuild_from_input($context, $form_state['input'], $conditions, $reactions);
   }
-  
+
   $form['#base'] = 'context_ui_form';
   $form['#theme'] = 'context_ui_form';
 
@@ -131,7 +131,7 @@ function context_ui_form(&$form, &$form_state) {
     '#required' => FALSE,
     '#maxlength' => 255,
     '#default_value' => isset($context->tag) ? $context->tag : '',
-    '#description' => t('Example: <code>theme</code>') .'<br/>'. t('A tag to group this context with others.'),
+    '#description' => t('Example: <code>theme</code>') . '<br/>' . t('A tag to group this context with others.'),
   );
 
   $form['info']['description'] = array(
@@ -155,11 +155,11 @@ function context_ui_form(&$form, &$form_state) {
   $form['conditions'] = array(
     '#theme' => 'context_ui_plugins',
     '#title' => t('Conditions'),
-    '#description' => t('Trigger the activation of this context'),
+    '#description' => t('Trigger the activation of this context.'),
     '#tree' => TRUE,
     'selector' => array(
       '#type' => 'select',
-      '#options' => array(0 => '<'. t('Add a condition') .'>'),
+      '#options' => array(0 => '<' . t('Add a condition') . '>'),
       '#default_value' => 0,
     ),
     'state' => array(
@@ -185,11 +185,11 @@ function context_ui_form(&$form, &$form_state) {
   $form['reactions'] = array(
     '#theme' => 'context_ui_plugins',
     '#title' => t('Reactions'),
-    '#description' => t('Actions to take when this context is active'),
+    '#description' => t('Actions to take when this context is active.'),
     '#tree' => TRUE,
     'selector' => array(
       '#type' => 'select',
-      '#options' => array(0 => '<'. t('Add a reaction') .'>'),
+      '#options' => array(0 => '<' . t('Add a reaction') . '>'),
       '#default_value' => 0,
     ),
     'state' => array(
@@ -225,7 +225,7 @@ function context_ui_form(&$form, &$form_state) {
  *   A context object
  */
 function _context_ui_rebuild_from_input($context, $input, $conditions, $reactions) {
-  $condition_defaults = array();  
+  $condition_defaults = array();
   foreach ($conditions as $condition) {
     if ($plugin = context_get_plugin('condition', $condition)) {
       $condition_defaults[$condition] = array(
@@ -235,7 +235,7 @@ function _context_ui_rebuild_from_input($context, $input, $conditions, $reaction
     }
   }
   $input['conditions']['plugins'] = array_merge($condition_defaults, $input['conditions']['plugins']);
-  
+
   $reaction_defaults = array();
   foreach ($reactions as $reaction) {
     if ($plugin = context_get_plugin('reaction', $reaction)) {

+ 1 - 1
sites/all/modules/contrib/admin/context/context_ui/tests/context_ui.test

@@ -48,7 +48,7 @@ class ContextUiTestCase extends DrupalWebTestCase {
       'description' => $context->description,
       'tag' => $context->tag,
       'conditions[plugins][node][values][blog]' => 'blog',
-      'reactions[plugins][menu]' => 'node/add/blog',
+      'reactions[plugins][menu][]' => 'navigation:node/add/blog',
     );
     $this->drupalPost('admin/structure/context/add', $edit, 'Save');
     $this->assertText($context->name . ' has been created.', 'Context saved.');

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_condition_bookroot.inc

@@ -8,7 +8,7 @@ class context_condition_bookroot extends context_condition_node {
     if ($this->condition_used() && !empty($node->book['bid'])) {
       $type = db_select('node')
         ->fields('node', array('type'))
-        ->condition('nid', $node->book['nid'])
+        ->condition('nid', $node->book['bid'])
         ->execute()
         ->fetchField();
       $book = new stdClass();

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_condition_menu.inc

@@ -14,7 +14,7 @@ class context_condition_menu extends context_condition {
       foreach ($menus as $key => $name) {
         $id = explode(':', $key);
         if ($id[1] == '0') {
-          $root_menus[$id[0]] = check_plain($name);
+          $root_menus[$id[0]] = $name;
         }
         else {
           $link = menu_link_load($id[1]);

+ 9 - 5
sites/all/modules/contrib/admin/context/plugins/context_condition_node_taxonomy.inc

@@ -45,24 +45,28 @@ class context_condition_node_taxonomy extends context_condition_node {
     $check_fields = array();
     foreach ($instance_fields as $key => $field_info) {
       if ($fields[$key]['type'] == 'taxonomy_term_reference') {
-        $check_fields[] = $key;
+        $check_fields[$key] = 'tid';
+      }
+      else if ($fields[$key]['type'] == 'entityreference' &&
+               $fields[$key]['settings']['target_type'] == 'taxonomy_term') {
+        $check_fields[$key] = 'target_id';
       }
     }
 
     if ($this->condition_used() && !empty($check_fields)) {
-      foreach ($check_fields as $field) {
+      foreach ($check_fields as $field => $term_id_key) {
         if ($terms = field_get_items('node', $node, $field)) {
           foreach ($terms as $term) {
-            foreach ($this->get_contexts($term['tid']) as $context) {
+            foreach ($this->get_contexts($term[$term_id_key]) as $context) {
               // Check the node form option.
               if ($op === 'form') {
                 $options = $this->fetch_from_context($context, 'options');
                 if (!empty($options['node_form'])) {
-                  $this->condition_met($context, $term['tid']);
+                  $this->condition_met($context, $term[$term_id_key]);
                 }
               }
               else {
-                $this->condition_met($context, $term['tid']);
+                $this->condition_met($context, $term[$term_id_key]);
               }
             }
           }

+ 4 - 5
sites/all/modules/contrib/admin/context/plugins/context_reaction_block.js

@@ -69,7 +69,7 @@ DrupalContextBlockForm = function(blockForm) {
     // Hide enabled blocks from selector that are used
     $('table.context-blockform-region tr').each(function() {
       var bid = $(this).attr('id');
-      $('div.context-blockform-selector input[value='+bid+']').parents('div.form-item').eq(0).hide();
+      $('div.context-blockform-selector input[value="'+bid+'"]').parents('div.form-item').eq(0).hide();
     });
     // Show blocks in selector that are unused
     $('div.context-blockform-selector input').each(function() {
@@ -159,7 +159,7 @@ DrupalContextBlockForm = function(blockForm) {
           $(this).removeAttr('checked');
         });
         if (weight_warn) {
-          alert(Drupal.t('Desired block weight exceeds available weight options, please check weights for blocks before saving'));
+          alert(Drupal.t('Desired block weight exceeds available weight options, please check weights for blocks before saving.'));
         }
       }
       return false;
@@ -426,9 +426,8 @@ DrupalContextBlockEditor.prototype = {
 
     $('.editing-context-label').remove();
     var label = $('#context-editable-trigger-'+context+' .label').text();
-    label = Drupal.t('Now Editing: ') + label;
-    editor.parent().parent()
-      .prepend('<div class="editing-context-label">'+ label + '</div>');
+    label = Drupal.t('Now editing: @label', {'@label': label});
+    editor.parent().parent().prepend('<div class="editing-context-label">' + label + '</div>');
 
     // First pass, enable sortables on all regions.
     $(this.regions).each(function() {

+ 1 - 0
sites/all/modules/contrib/admin/context/plugins/context_reaction_breadcrumb.inc

@@ -8,6 +8,7 @@ class context_reaction_breadcrumb extends context_reaction_menu {
    * Overrides set_active_trail_from_link to set the breadcrumb instead of the menu path.
    */
   function set_active_trail_from_link($item) {
+    $breadcrumb = array(l(t('Home'), '<front>'));
     $result = db_select('menu_links')
       ->fields('menu_links', array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8'))
       ->condition('hidden', 0)

+ 3 - 1
sites/all/modules/contrib/admin/context/plugins/context_reaction_menu.inc

@@ -22,19 +22,21 @@ class context_reaction_menu extends context_reaction {
           $link = menu_link_load($mlid);
           $identifier = $link['link_path'];
           $root_menu = $menu_names[$menu_name];
-          while (isset($options[$root_menu][$identifier])) {
+          while (isset($options[$root_menu][$menu_name . ':' . $identifier])) {
             $identifier .= "'";
           }
           $options[$root_menu][$menu_name . ':' . $identifier] = $title;
         }
       }
     }
+    $menu_count = count($options, COUNT_RECURSIVE);
     return array(
       '#title' => $this->title,
       '#description' => $this->description,
       '#options' => $options,
       '#type' => 'select',
       '#multiple' => TRUE,
+      '#size' => $menu_count > 20 ? 20 : $menu_count,
       '#default_value' => $this->fetch_from_context($context),
     );
   }

+ 1 - 1
sites/all/modules/contrib/admin/context/plugins/context_reaction_template_suggestions.inc

@@ -14,7 +14,7 @@ class context_reaction_template_suggestions extends context_reaction {
     return array(
       '#title' => t('Template suggestions'),
       '#type' => 'textarea',
-      '#description' => t('Enter template suggestions such as "page__front", one per line, in order of preference (using underscores instead of hyphens).  For more information, please visit ') . l(t('Drupal 7 Template (Theme Hook) Suggestions'), 'http://drupal.org/node/1089656', array(array('target' => '_blank'), 'html' => TRUE,)) . '.',
+      '#description' => t('Enter template suggestions such as "page__front", one per line, in order of preference (using underscores instead of hyphens). For more information, please visit <a href="@template-suggestions">Drupal 7 Template (Theme Hook) Suggestions</a>.', array('@template-suggestions' => 'http://drupal.org/node/1089656')),
       '#default_value' => is_string($default_value) ? $default_value : '',
     );
   }

+ 0 - 22
sites/all/modules/contrib/admin/context/tests/context.conditions.test

@@ -36,13 +36,6 @@ class ContextConditionUserTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    user_delete($this->user1->uid);
-    user_delete($this->user2->uid);
-  }
-
   function test() {
     // User 1 triggers the context.
     $this->drupalLogin($this->user1);
@@ -82,14 +75,6 @@ class ContextConditionUserPageTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    $edit = array();
-    user_delete($this->user1->uid);
-    user_delete($this->user2->uid);
-  }
-
   function test() {
     // Viewing any user profile triggers context.
     $this->drupalLogin($this->user1);
@@ -157,13 +142,6 @@ class ContextConditionNodeTaxonomyTest extends DrupalWebTestCase {
     $this->assertTrue($saved, "Context 'testcontext' saved.");
   }
 
-  function tearDown() {
-    parent::tearDown();
-    context_delete($this->context);
-    taxonomy_term_delete($this->terms['apples']->tid);
-    taxonomy_term_delete($this->terms['oranges']->tid);
-  }
-
   function test() {
     // Apples does trigger the context.
     $edit = array(

+ 3 - 4
sites/all/modules/contrib/admin/context/theme/context_reaction_block.theme.inc

@@ -52,7 +52,7 @@ function theme_context_block_regions_form($vars) {
     $output .= "<div class='label context-blockform-regionlabel-{$region}'>";
     $output .= l(t('+') . ' ' . t('Add'), $_GET['q'], array('fragment' => $region, 'attributes' => array('class' => array('add-block'))));
     $output .= $form[$region]['#title'];
-    $output .= "</div>";
+    $output .= '</div>';
     $output .= theme('table', array('rows' => $rows, 'attributes' => $attr));
   }
   return $output;
@@ -100,14 +100,13 @@ function template_preprocess_context_block_browser(&$vars) {
   //add help text to tell people how to use the block browser
   $help_text = array(
     '#prefix' => '<div class="context_ui-help-text">',
-    '#markup' => t('To add a block to the current region, simply click on the block.  You may use the category filter to filter by
-      block type or the search filter to find the block that you wish to add.'),
+    '#markup' => t('To add a block to the current region, simply click on the block. You may use the category filter to filter by block type or the search filter to find the block that you wish to add.'),
     '#suffix' => '</div>',
   );
 
   $filter_label = array(
     '#prefix' => '<div class="filter-label">',
-    '#markup' => t('Search Filter'),
+    '#markup' => t('Search filter'),
     '#suffix' => '</div>',
   );
 

+ 18 - 6
sites/all/modules/contrib/files/imagestyleflush/README.txt

@@ -1,37 +1,49 @@
+
 Image style flush
 ==================
 
-This module will allow Drupal to flush all image styles at once or flush each image style individually right from administrative interface.
+This module will allow Drupal to flush all image styles at once or flush each
+individual image style right from the administrative interface.
+
 
 Features
 --------
 
   - Flush all image styles
-  - Flush each image style individually
+  - Flush each individual image style
 
-This module was written by Stepan Kuzmin.
 
 Dependencies
 ------------
 
 The image (core) module.
 
+
 Install
 -------
 
-1) Copy the imagestyleflush folder to the modules folder in your installation. Usually
-   this is sites/all/modules.
+1) Copy the imagestyleflush folder to the modules folder in your installation.
+   Usually this is sites/all/modules.
    Or use the UI and install it via admin/modules/install.
 
 2) In your Drupal site, enable the module under Administration -> Modules
    (/admin/modules).
 
+
 Usage
 -----
 
-  You can flush image styles under Administration -> Configuration -> Media -> Image styles
+You can flush image styles under Administration -> Configuration -> Media
+-> Image styles
+
 
 Known problems
 --------------
 
 Private file image styles can't be flushed with this module.
+
+
+Credit
+------
+
+This module was written by Stepan Kuzmin and is maintained by Hargobind Khalsa.

+ 4 - 5
sites/all/modules/contrib/files/imagestyleflush/imagestyleflush.info

@@ -1,13 +1,12 @@
 name = Image style flush
-description = Flush image styles in Drupal 7.
+description = Adds the ability to flush image styles.
 package = Media
 core = 7.x
-Image styles
 configure = admin/config/media/image-styles
 
-; Information added by drupal.org packaging script on 2013-05-21
-version = "7.x-1.2"
+; Information added by Drupal.org packaging script on 2015-11-07
+version = "7.x-1.3"
 core = "7.x"
 project = "imagestyleflush"
-datestamp = "1369122408"
+datestamp = "1446884340"
 

+ 5 - 5
sites/all/modules/contrib/files/imagestyleflush/imagestyleflush.module

@@ -48,7 +48,7 @@ function imagestyleflush_image_style_list($variables) {
   $rows = array();
   foreach ($styles as $style) {
     $row = array();
-    $row[] = l($style['name'], 'admin/config/media/image-styles/edit/' . $style['name']);
+    $row[] = l($style['label'], 'admin/config/media/image-styles/edit/' . $style['name']);
     $link_attributes = array(
       'attributes' => array(
         'class' => array('image-style-link'),
@@ -79,7 +79,7 @@ function imagestyleflush_image_style_list($variables) {
     $rows[] = array(array(
       'colspan' => 4,
       'data' => t('There are currently no styles. <a href="!url">Add a new one</a>.', array('!url' => url('admin/config/media/image-styles/add'))),
-      ));
+    ));
   }
 
   return theme('table', array('header' => $header, 'rows' => $rows));
@@ -103,7 +103,7 @@ function imagestyleflush_form($form, &$form_state, $style = NULL) {
           '#value' => $style['name'],
         ),
       ),
-      t('Are you sure you want to flush @style image style?', array('@style' => $style['name'])),
+      t('Are you sure you want to flush the %style image style?', array('%style' => $style['label'])),
       'admin/config/media/image-styles',
        t('This action cannot be undone.'),
       t('Flush'), t('Cancel')
@@ -151,10 +151,10 @@ function imagestyleflush_form_submit($form, &$form_state) {
  */
 function imagestyleflush_batch_finished($success, $results, $operations) {
   if ($success) {
-    drupal_set_message(t('Image styles was successfully flushed.'));
+    drupal_set_message(t('Image styles were successfully flushed.'));
   }
   else {
-    drupal_set_message(t('An error occurred while flushing the image caches.', 'error'));
+    drupal_set_message(t('An error occurred while flushing the image caches.'), 'error');
   }
   drupal_goto('admin/config/media/image-styles');
 }

+ 21 - 3
sites/all/modules/contrib/responsive/browscap/README.txt

@@ -20,25 +20,43 @@ to retrieve and update its user agent database.
 
 Note: Some hosting companies have this capability blocked.
 
+
+Recommendations
+---------------
+
+If you are using MySQL, it is recommended to use InnoDB and not MyISAM as the
+table storage engine. (InnoDB is the default for Drupal 7.) MyISAM does not
+support transactions, and without transactions Browscap may not return correct
+browser information while new Browscap data is being loaded.
+
+
 Installation
 ------------
 
 Browscap can be installed via the standard Drupal installation process.
-http://drupal.org/node/895232
+https://drupal.org/node/895232
+
 
 API
 ---
 
 Modules can make use of browscap data by calling browscap_get_browser()
 anywhere they would otherwise call the PHP get_browser()
-(http://us3.php.net/manual/en/function.get-browser.php) function.
+(https://secure.php.net/manual/en/function.get-browser.php) function.
 
 Note: browser_name_regex is not returned.
 
+
+Drush integration
+-----------------
+
+To import the data via drush, use `drush browscap-import`.
+
+
 Credits
 -------
 
-Development of Browscap is sponsored by Acquia (http://www.acquia.com) and the
+Development of Browscap is sponsored by Acquia (https://www.acquia.com) and the
 Ontario Ministry of Northern Development and Mines (http://www.mndm.gov.on.ca).
 
 A special thanks goes out to Gary Keith (http://www.garykeith.com) who provides

+ 2 - 6
sites/all/modules/contrib/responsive/browscap/browscap.admin.inc

@@ -22,8 +22,8 @@ function browscap_settings_form($form, &$form_state) {
   $form['browscap_enable_automatic_updates'] = array(
     '#type' => 'checkbox',
     '#title' => t('Enable automatic updates'),
-    '#default_value' => variable_get('browscap_enable_automatic_updates', TRUE),
-    '#description' => t('Automatically update the user agent detection information.'),
+    '#default_value' => variable_get('browscap_enable_automatic_updates', FALSE),
+    '#description' => t('Automatically update the user agent detection information. <em>Warning: This may require several seconds and possibly up to a minute or more during cron.</em>'),
   );
   $form['browscap_automatic_updates_timer'] = array(
     '#type' => 'select',
@@ -54,9 +54,5 @@ function browscap_settings_form($form, &$form_state) {
  * Submit handler for the refresh browscap button.
  */
 function browscap_refresh_submit($form, &$form_state) {
-  // Update the browscap information.
   _browscap_import(FALSE);
-
-  // Record when the browscap information was updated.
-  variable_set('browscap_imported', REQUEST_TIME);
 }

+ 64 - 0
sites/all/modules/contrib/responsive/browscap/browscap.drush.inc

@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Browscap Drush integration.
+ */
+
+/**
+ * Implements hook_drush_command().
+ */
+function browscap_drush_command() {
+  $items['browscap-import'] = array(
+    'description' => 'Imports/updates browscap data via HTTP.',
+  );
+  $items['browscap-get-browser'] = array(
+    'description' => dt('Show browser information for a given user agent.'),
+    'arguments' => array(
+      'user agent' => 'Quote-enclosed user agent',
+    ),
+    'required-arguments' => TRUE,
+    'examples' => array("drush browscap-get-browser 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9700; en) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.448 Mobile Safari/534.8+'"),
+    'outputformat' => array(
+      'default' => 'json',
+    ),
+  );
+  return $items;
+}
+
+/**
+ * Imports/updates browscap data via HTTP.
+ */
+function drush_browscap_import() {
+  drush_log(dt('Importing Browscap data...'), 'ok');
+  switch (_browscap_import()) {
+    case BROWSCAP_IMPORT_VERSION_ERROR:
+      drush_set_error('BROWSCAP', dt('Could not check current available Browscap data version.'));
+      break;
+
+    case BROWSCAP_IMPORT_NO_NEW_VERSION:
+      drush_log(dt('No new version of Browscap data to import.'), 'ok');
+      break;
+
+    case BROWSCAP_IMPORT_DATA_ERROR:
+      drush_set_error('BROWSCAP', dt('Failed to retrieve or load Browscap data.'));
+      break;
+
+    case BROWSCAP_IMPORT_OK:
+      drush_log(dt('Imported Browscap data successfully.'), 'ok');
+      break;
+  }
+}
+
+/**
+ * Gets browser information for Drush output.
+ *
+ * @param string $user_agent
+ *   User agent.
+ *
+ * @return array
+ *   An array of information about the user agent.
+ */
+function drush_browscap_get_browser($user_agent) {
+  return browscap_get_browser($user_agent);
+}

+ 3 - 3
sites/all/modules/contrib/responsive/browscap/browscap.info

@@ -3,9 +3,9 @@ description = Provides a replacement for PHPs get_browser() function.
 core = 7.x
 configure = admin/config/system/browscap
 
-; Information added by Drupal.org packaging script on 2014-07-09
-version = "7.x-2.2"
+; Information added by Drupal.org packaging script on 2016-03-23
+version = "7.x-2.3"
 core = "7.x"
 project = "browscap"
-datestamp = "1404915228"
+datestamp = "1458745750"
 

+ 26 - 6
sites/all/modules/contrib/responsive/browscap/browscap.install

@@ -29,14 +29,34 @@ function browscap_schema() {
 }
 
 /**
- * Implements hook_install().
+ * Implements hook_requirements().
  */
-function browscap_install() {
-  // Update the browscap information.
-  _browscap_import();
+function browscap_requirements($phase) {
+  $requirements = array();
+  $t = get_t();
+  if ($phase == 'runtime' && user_access('administer browscap')) {
+    $requirement = array(
+      'value' => variable_get('browscap_version', 0) === 0 ? $t('Not installed') : l(variable_get('browscap_version', 0), 'admin/config/system/browscap'),
+      'title' => $t('Browscap version'),
+    );
 
-  // Record when the browscap information was updated.
-  variable_set('browscap_imported', REQUEST_TIME);
+    if (variable_get('browscap_version', 0) === 0) {
+      $requirement += array(
+        'severity' => REQUIREMENT_ERROR,
+        'description' => $t('Browscap data is not imported! See <a href="!settings_url">Browscap settings</a> to import manually.', array(
+          '!settings_url' => url('admin/config/system/browscap'),
+        )),
+      );
+    }
+    else {
+      $requirement += array(
+        'severity' => REQUIREMENT_OK,
+      );
+    }
+
+    $requirements['browscap_version'] = $requirement;
+  }
+  return $requirements;
 }
 
 /**

+ 2 - 5
sites/all/modules/contrib/responsive/browscap/browscap.module

@@ -48,21 +48,18 @@ function browscap_help($path, $arg) {
  * Implements hook_cron().
  */
 function browscap_cron() {
-  if (variable_get('browscap_enable_automatic_updates', TRUE) == TRUE) {
+  if (variable_get('browscap_enable_automatic_updates', FALSE)) {
     // Check the current update timer.
     $automatic_update_timer = variable_get('browscap_automatic_updates_timer', 604800);
 
     // Check when the last update occurred.
-    $last_imported = variable_get('browscap_imported', REQUEST_TIME);
+    $last_imported = variable_get('browscap_imported', 0);
 
     // Update the browscap data if the amount of time specified by the update
     // timer has passed.
     if (($last_imported + $automatic_update_timer) < REQUEST_TIME) {
       // Update the browscap information.
       _browscap_import();
-
-      // Record when the browscap information was updated.
-      variable_set('browscap_imported', REQUEST_TIME);
     }
   }
 }

+ 100 - 40
sites/all/modules/contrib/responsive/browscap/import.inc

@@ -4,19 +4,33 @@
  * Browscap data import functions.
  */
 
+define('BROWSCAP_IMPORT_OK', 0);
+define('BROWSCAP_IMPORT_VERSION_ERROR', 1);
+define('BROWSCAP_IMPORT_NO_NEW_VERSION', 2);
+define('BROWSCAP_IMPORT_DATA_ERROR', 3);
+
 /**
  * Helper function to update the browscap data.
  *
  * @param bool $cron
  *   Optional import environment. If false, display status messages to the user
  *   in addition to logging information with the watchdog.
+ *
+ * @return int
+ *   A code indicating the result:
+ *   - BROWSCAP_IMPORT_OK: New data was imported.
+ *   - BROWSCAP_IMPORT_NO_NEW_VERSION: No new data version was available.
+ *   - BROWSCAP_IMPORT_VERSION_ERROR: Checking the current data version failed.
+ *   - BROWSCAP_IMPORT_DATA_ERROR: The data could not be downloaded or parsed.
  */
 function _browscap_import($cron = TRUE) {
   // Check the local browscap data version number.
   $local_version = variable_get('browscap_version', 0);
 
+  watchdog('browscap', 'Checking for new browscap version...');
+
   // Retrieve the current browscap data version number using HTTP.
-  $current_version = drupal_http_request('http://www.browscap.org/version-number');
+  $current_version = drupal_http_request('https://www.browscap.org/version-number');
 
   // Log an error if the browscap version number could not be retrieved.
   if (isset($current_version->error)) {
@@ -28,7 +42,7 @@ function _browscap_import($cron = TRUE) {
       drupal_set_message(t("Couldn't check version: %error", array('%error' => $current_version->error)), 'error');
     }
 
-    return;
+    return BROWSCAP_IMPORT_VERSION_ERROR;
   }
 
   // Sanitize the returned version number.
@@ -45,7 +59,7 @@ function _browscap_import($cron = TRUE) {
       drupal_set_message(t('No new version of browscap to import'));
     }
 
-    return;
+    return BROWSCAP_IMPORT_NO_NEW_VERSION;
   }
 
   // Set options for downloading data with or without compression.
@@ -61,7 +75,7 @@ function _browscap_import($cron = TRUE) {
   }
 
   // Retrieve the browscap data using HTTP.
-  $browscap_data = drupal_http_request('http://www.browscap.org/stream?q=PHP_BrowsCapINI', $options);
+  $browscap_data = drupal_http_request('https://www.browscap.org/stream?q=PHP_BrowsCapINI', $options);
 
   // Log an error if the browscap data could not be retrieved.
   if (isset($browscap_data->error) || empty($browscap_data)) {
@@ -73,7 +87,7 @@ function _browscap_import($cron = TRUE) {
       drupal_set_message(t("Couldn't retrieve updated browscap: %error", array('%error' => $browscap_data->error)), 'error');
     }
 
-    return;
+    return BROWSCAP_IMPORT_DATA_ERROR;
   }
 
   // Decompress the downloaded data if it is compressed.
@@ -123,46 +137,15 @@ function _browscap_import($cron = TRUE) {
     // The version information is the first entry in the array.
     $version = array_shift($browscap_data);
 
-    // Store the data available for each user agent.
-    foreach ($browscap_data as $key => $values) {
-      // Store the current value.
-      $e = $values;
-
-      // Create an array to hold the last parent.
-      $last_parent = array();
-
-      // Recurse through the available user agent information.
-      while (isset($values['Parent']) && $values['Parent'] !== $last_parent) {
-        $values = isset($browscap_data[$values['Parent']]) ? $browscap_data[$values['Parent']] : array();
-        $e = array_merge($values, $e);
-        $last_parent = $values;
-      }
-
-      // Replace '*?' with '%_'.
-      $user_agent = strtr($key, '*?', '%_');
-
-      // Change all array keys to lowercase.
-      $e = array_change_key_case($e);
-
-      // Delete all data about the current user agent from the database.
-      db_delete('browscap')
-        ->condition('useragent', $user_agent)
-        ->execute();
-
-      // Insert all data about the current user agent into the database.
-      db_insert('browscap')
-        ->fields(array(
-         'useragent' => $user_agent,
-         'data' => serialize($e),
-        ))
-        ->execute();
-    }
+    // Save parsed Browscap data.
+    _browscap_save_parsed_data($browscap_data);
 
     // Clear the browscap data cache.
     cache_clear_all('*', 'cache_browscap', TRUE);
 
-    // Update the browscap version.
+    // Update the browscap version and imported time.
     variable_set('browscap_version', $current_version);
+    variable_set('browscap_imported', REQUEST_TIME);
 
     // Log a message with the watchdog.
     watchdog('browscap', 'New version of browscap imported: %version', array('%version' => $current_version));
@@ -171,5 +154,82 @@ function _browscap_import($cron = TRUE) {
     if ($cron == FALSE) {
       drupal_set_message(t('New version of browscap imported: %version', array('%version' => $current_version)));
     }
+
+    return BROWSCAP_IMPORT_OK;
+  }
+
+  return BROWSCAP_IMPORT_DATA_ERROR;
+}
+
+/**
+ * Saves parsed Browscap data.
+ *
+ * The purpose of this function is to perform the queries on the {browscap}
+ * table as a transaction. This vastly improves performance with database
+ * engines such as InnoDB and ensures that queries will work while new data
+ * is being imported.
+ *
+ * @param array $browscap_data
+ *   Browscap data that has been parsed with parse_ini_string() or
+ *   parse_ini_file().
+ */
+function _browscap_save_parsed_data(&$browscap_data) {
+  // Start a transaction. The variable is unused. That's on purpose.
+  $transaction = db_transaction();
+
+  // Delete all data from database.
+  db_delete('browscap')->execute();
+
+  // Prepare the data for insertion.
+  $import_data = array();
+  foreach ($browscap_data as $key => $values) {
+    // Store the current value.
+    $original_values = $values;
+
+    // Replace '*?' with '%_'.
+    $user_agent = strtr($key, '*?', '%_');
+
+    // Remove trailing spaces to prevent "duplicate entry" errors. Databases
+    // such as MySQL do not preserve trailing spaces when storing VARCHARs.
+    $user_agent = rtrim($user_agent);
+
+    // Change all array keys to lowercase.
+    $original_values = array_change_key_case($original_values);
+
+    // Add to array of data to import.
+    $import_data[$user_agent] = $original_values;
+
+    // Remove processed data to reduce memory usage.
+    unset($browscap_data[$key]);
+  }
+
+  // Get user agents to insert, removing case-insensitive duplicates.
+  $user_agents = array_keys($import_data);
+  $user_agents = array_intersect_key($user_agents, array_unique(array_map('strtolower', $user_agents)));
+
+  // Insert all data about user agents into database in chunks.
+  $user_agents_chunks = array_chunk($user_agents, 1000);
+  foreach ($user_agents_chunks as $user_agents_chunk) {
+    $query = db_insert('browscap')
+      ->fields(array('useragent', 'data'));
+    foreach ($user_agents_chunk as $user_agent) {
+      $values = $import_data[$user_agent];
+
+      // Recurse through the available user agent information.
+      $previous_parent = NULL;
+      $parent = isset($values['parent']) ? $values['parent'] : FALSE;
+      while ($parent && $parent !== $previous_parent) {
+        $parent_values = isset($import_data[$parent]) ? $import_data[$parent] : array();
+        $values = array_merge($parent_values, $values);
+        $previous_parent = $parent;
+        $parent = isset($parent_values['parent']) ? $parent_values['parent'] : FALSE;
+      }
+
+      $query->values(array(
+        'useragent' => $user_agent,
+        'data' => serialize($values),
+      ));
+    }
+    $query->execute();
   }
 }

+ 454 - 1
sites/all/modules/contrib/seo/metatag/CHANGELOG.txt

@@ -1,3 +1,453 @@
+Metatag 7.x-1.17, 2016-06-30
+----------------------------
+#2748627 by jalpesh: Corrected twitter:app:id:googleplay description.
+#2752319 by DamienMcKenna: Fixed output of the mask-icon meta tag.
+#2752319 by DamienMcKenna: Added the mask-icon 'color' attribute, the new 'url'
+  option for meta tag definitions, and tests for the mask-icon meta tag.
+#2754263 by DamienMcKenna: Meta tags with multiple values should still be
+  returned as a string from metatag_get_value().
+#1953080 by DamienMcKenna: Don't output language/locale meta tags if they are
+  equal to LANGUAGE_NONE/"und".
+#2659906 by DamienMcKenna: Rewrote update 7040 to use less memory and work
+  better.
+#2720723 by IRuslan: Allow config instance names to be changed.
+#2669592 by DamienMcKenna: Fix for possible problem with removal of meta tags
+  that Metatag is already outputting.
+#2162397 by DamienMckenna: Added test dependency for Profile2.
+#2162397 by roderick, DamienMckenna, hideaway: Fixed loading multiple entities
+  on the same path when at least one of them is not enabled for use with
+  Metatag and gets in the way of the one which is enabled.
+
+
+Metatag 7.x-1.16, 2016-06-03
+----------------------------
+#2742063 by jalpesh, DamienMcKenna: Fix syntax error in verification module.
+#2742123 by DamienMcKenna: Added test for all submodules to ensure they could be
+  enabled.
+
+
+Metatag 7.x-1.15, 2016-06-03
+----------------------------
+#2700217 by DamienMcKenna: Added Media module as a test dependency.
+#2700217 by DamienMcKenna: Automatically filter out Media embed codes from meta
+  tags when the Media WYSIWYG module is installed.
+#2709985 by alzz: Added fb:pages meta tag for Facebook Instant Pages.
+#2703081 by DylanUnderwood: Added apple-mobile-web-app-title meta tag.
+#2704805 by jcnventura: Fixed the hreflang meta tag.
+#2678004 by alvar0hurtad0: Improvements to the API documentation.
+By DamienMcKenna: Corrected some messages in metatag_update_7104.
+By DamienMcKenna: Incorrect indenting in metatag_favicons.metatag.inc.
+#2687309 by das-peter, skein, DamienMcKenna, k_zoltan: Fixed output translations
+  so it's actually disabled when it's turned off.
+#2712815 by Chalk, DamienMcKenna: Rewrote update scripts that rename meta tags
+  to use two new shared functions, and always use a sandbox for it.
+#2720463 by DamienMcKenna: Rename the icon_any favicon to mask-icon.
+#2276855 by ben.kyriakou: Properly handle the 'disabled' state when featurized
+  configs are reverted.
+#2687309 by das-peter, DamienMcKenna: Submodules shouldn't update translations
+  during page load.
+#2725251 by DamienMcKenna, Jim.M: Added validation tags for Alexa, Baidu, Norton
+  Safe Web and Yahoo.
+#2725741 by DamienMcKenna: Ensure the Locale module is enabled before running db
+  queries against the {locales_source} table.
+By DamienMcKenna: Updated the docblock comment for metatag_metatags_save().
+#2728919 by DYdave: Workaround for CTools bug that causes the Views popup to be
+  tiny.
+#2728933 by DYdave, DamienMcKenna: Trigger hook_metatag_token_types() from
+  metatag_metatags_form() instead of metatag_field_attach_form() so it works
+  with all submodules too.
+#2710377 by DamienMcKenna: Updated the message for og:image:url to make it
+  clear it might be better to not use it.
+#2539388 by DamienMcKenna: All image meta tags will be output with absolute URLs
+  and spaces will be replaced with '%20'.
+#2692877 by stefan.r, DamienMcKenna, Drunkey Monkey: Search API integration.
+#2720221 by dnmurray: Allow sanitizing of values through token_replace(); for
+  advanced usage only, may lead to other issues.
+#2497043 by marcelovani, uzlov: Allow Metatag:Context configs to be sorted by a
+  'weight' value.
+
+
+Metatag 7.x-1.14, 2016-04-02
+----------------------------
+By DamienMcKenna: Corrected return code from update 7107.
+#2664624 by DamienMcKenna: Added a space in front of the Google Plus schemaorg
+  variable to avoid HTML validation errors because of the lack of space.
+#2658902 by DamienMcKenna, swentel: Renamed the 'Add a Metatag default' link to
+  match the D8 wording.
+#2665206 by DamienMcKenna: Don't add an index in 7029 if it already exists.
+#2670842 by Ambient.Impact: Fixed permissions in the Importer submodule.
+#2613598 by DamienMcKenna: Removed the Contributor Covenant, replaced it with a
+  link to https://www.drupal.org/dcoc.
+#2692933 by doostinharrell, DamienMcKenna: $metatags variable in
+  metatag_field_attach_form() may not be an array in certain circumstances.
+#2658808 by tommik: Rewrote query in update 7040 so it works better.
+#2688963 by DamienMcKenna: Added a note about the Yoast SEO module.
+#2687847 by DamienMcKenna: Added a note about the Parse.ly module.
+#2696445 by Simon Georges: Added the twitter:image:alt meta tag.
+#2678896 by Dave Reid: Fixed double-encoding of form fields.
+#2667214 by DamienMcKenna: Improved tests for string encoding.
+#2667214 by joelstein, DamienMcKenna: Decode HTML entities prior to rendering
+  meta tags.
+
+
+Metatag 7.x-1.13, 2016-03-04
+----------------------------
+#2662952 by DamienMcKenna: Fixed logic on hook_requirements for checking the
+  version of Token that is installed.
+
+
+Metatag 7.x-1.12, 2016-03-04
+----------------------------
+#2644742 by kev5873, DamienMcKenna: Variable scoping issue with Metatag:Panels
+  could lead to incorrect meta tag output.
+#2309017 by DamienMcKenna, rodrigoaguilera: Tidied up the install file a little.
+#2658808 by DamienMcKenna: Improved query to skip update 7040 if it isn't
+  needed.
+#2658262 by Devin Carlson, DamienMcKenna: Fixed hook_metatag_config_delete(),
+  this time with tests.
+#2661378 by DamienMcKenna: No longer require Token v7.x-1.6, just highly
+  recommend it.
+#2661408 by DamienMcKenna: Removed the status message when core is up-to-date.
+#2661412 by DamienMcKenna: Improve hook_requirements check for Entity
+  Translation.
+#2661434 by DamienMcKenna: Removed the status message about alt_hreflang.
+#2652120 by DamienMcKenna: Fixes for old config translations that weren't
+  fixed with previous updates.
+
+
+Metatag 7.x-1.11, 2016-01-26
+----------------------------
+#2568499 by DamienMcKenna, DD 85: Removed an errant space.
+#2655582 by DamienMcKenna, MihaiMiculescu: Fixed automatic image parsing.
+By DamienMcKenna: Minor changes to the README.txt file.
+#2655614 by DamienMcKenna: Added tests for image handling.
+#2655662 by DamienMcKenna: Fixed handling of output translation on pages with
+  really long paths.
+
+
+Metatag 7.x-1.10, 2016-01-22
+----------------------------
+#2654838 by Plazik, DamienMcKenna: Disabling output caching lead to Undefined
+  Variable errors.
+#2654638 by DamienMcKenna, noah, marleythedog: Allow updates 7104 and 7105 to
+  run if Locale is not enabled.
+#2654530 by DamienMcKenna, Adenn, deja711: Twitter Cards update 7100 and
+  OpenGraph update 7103 cannot run until Metatag update 7100 has finished.
+
+
+Metatag 7.x-1.9, 2016-01-21
+---------------------------
+#2652772 by DamienMcKenna: Fix update 7101 and rerun it to correct translation
+  data.
+#2651812 by DamienMcKenna: Added an option to disable the i18n integration.
+#2420489 by Les Lim, DamienMcKenna: New caching process for entities.
+#2652260 by DamienMcKenna: Remove the revision_id from translation string IDs.
+#2509246 by DamienMcKenna, zniki.ru: Remove meta tags added by core, with option
+  to leave them as-is.
+#2652294 by DamienMcKenna: Incorrect string context was being used for meta tag
+  output translation.
+#2653434 by DamienMcKenna, reinchek: Fixed metatag_metatags_load().
+#2653446 by DamienMcKenna: Wrong variable name passed to
+  metatag_config_delete().
+#1858540 by DamienMcKenna, friera: Added the pragma, cache-control and expires
+  meta tags.
+
+
+Metatag 7.x-1.8, 2016-01-14
+---------------------------
+#2546636 by DamienMcKenna: Fixes to the custom Panels pane for editing tags.
+#2113501 by DamienMcKenna: Shortened i18n translation context to just
+  "metatag:METATAGNAME" to make translations easier.
+#2552829 by DamienMcKenna: Move the basic meta tags to a group.
+#2552827 by DamienMcKenna: Group meta tag tokens so they're less confusing.
+#2552849 by DamienMcKenna: Meta tag tokens for user entities.
+#1986032 by DamienMcKenna, izus, yang_yi_cn, Placinta, maijs, Tharna, stijndmd:
+  Improved i18n compatibility for the Views, Panels and Context submodules.
+#2518690 by manikaprasanth: Added support for editing Commerce product entities.
+#2550001 by DamienMcKenna: Renamed twitter:image:src back to twitter:image. LOL.
+#2559359 by labboy0276: Errors if the Views integration can't find the expected
+  display object.
+#2560649 by DamienMcKenna: Added optional second-stage translation for meta tag
+  output.
+#2556741 by DamienMcKenna: Fixed robots handling on Nodewords importer; moved
+  Nodewords importer into a separate file to keep the Importer module more
+  general.
+#1809652 by DamienMcKenna: Added a new [current-page:pager] token for inserting
+  a pager into meta tags.
+#1809652 by DamienMcKenna: Update default node title to insert the pager.
+#2518690 by DamienMcKenna: Improvements to Commerce Product handling.
+#2568499 by DamienMcKenna: Added new Apple-specific meta tags to the Mobile
+  submodule.
+#2568463 by DamienMcKenna: Added the android-app deeplink meta tag.
+#2569093 by DamienMcKenna: Added the ios-app deeplink meta tag.
+#2568955 by DamienMcKenna: Don't output meta tags that only contain a pager.
+#2556741 by DamienMcKenna: BatchAPI call wasn't updated after the Nodewords
+  importer file was renamed.
+#2514916 by DamienMcKenna: Split up the tests file, moved them into the tests
+  directory, added new tests for node revisions.
+#2567621 by hass: Allow the custom pager to be translated using the i18n
+  Variable module.
+#2572371 by Dave Reid: Remove support for comments.
+#2572291 by Dave Reid: Always display config items in the correct order - global
+  items first, then sorted alphabetically.
+#2567677 by Perignon, DamienMcKenna: og:video meta tag renamed.
+#2556741 by DamienMcKenna: Follow-up to fix imported robots meta tags.
+#2556741 by DamienMcKenna: Updated the final commit message to show the correct
+  count.
+#2498213 by makangus: Added the Android manifest meta tag.
+#2573869 by DamienMcKenna: Fixed variable bug in update 7040.
+#2579871 by DamienMcKenna: Added more test dependencies.
+#2579201 by lesonunique, DamienMcKenna: Missing 'secure' option on
+  og:audio:secure_url.
+#2580623 by marcvangend: Incorrect check for admin_language.
+#2580523 by DamienMcKenna: Add a note to hook_requirements if hreflang module
+  is not installed and there are multiple locales enabled on the site.
+#2587369 by DamienMcKenna: Added two placeholder files for storing tests for the
+  Views integration.
+#2587371 by DamienMcKenna: Added two placeholder files for storing tests for the
+  Panels integration.
+#2376857 by DamienMcKenna: Work around entity definitions that don't have a
+  'bundles' attribute.
+#2455777 by DamienMcKenna: Don't try to save any {metatag} records if the entity
+  or bundle is not supported.
+#2597301 by DamienMcKenna: New submodule for handling the hreflang meta tag.
+#2603058 by FluxSauce: Fixed problem loading migrate.inc file if Migrate is not
+  enabled.
+#1957358 by pjonckiere, DamienMcKenna: Add tests to ensure that the meta tag
+  string encoding works correctly.
+By DamienMcKenna: Some commit messages were in the wrong place.
+#2603458 by Frando: Fixed update 7018 for sites using Entity Translation, and
+  re-run it.
+#1355788 by DamienMcKenna: Allow use of 'public://' file scheme for images.
+#2550001 by Dave Reid: Blank out metatag_update_7024() and metatag_update_7030()
+  because the 'twitter:image:src' meta tag was renamed back to 'twitter:image'
+  so these updates don't need to ever run.
+#1957358 by DamienMcKenna: Added tests to confirm that HTML entities work
+  correctly in the page title.
+#2619438 by DamienMcKenna: Improve documentation on the mobile subtheme.
+#2597301 by DamienMcKenna: Only use the new locale-URL tokens when translations
+  modules are enabled.
+#2180031 by DamienMcKenna: Fixed double-encoding of tokens.
+#2635144 by DamienMcKenna: Added the apple-itunes-app meta tag.
+#1904542 by DamienMcKenna: Added a bunch of Windows and IE -focused meta tags.
+#2636132 by SpaghettiBolognese: Inconsistency in submodule naming.
+#2637026 by matthewordie, DamienMcKenna: Mention in the og:image meta tag how
+  Facebook will handle multiple images (It defaults to the largest one).
+#2564483 by DamienMcKenna, drupov, mas0h, das-peter, pwiniacki, sylus, webflo,
+  Gábor Hojtsy, pjonckiere, k_zoltan: Fixed i18n integration for core module and
+  all submodules; added over 1,000 new test assertions in major expansion of the
+  test suite.
+#2644156 by DamienMcKenna: Default value of image_src meta tag for user entity
+  was set incorrectly.
+#2639170 by anthonyleach: Use the correct hooks to add the RDF namespaces.
+#2628558 by marcelovani: Renamed the bundled Context definitions to avoid
+  conflicts.
+By DamienMcKenna: Fixed an accidental removal of the Token v1.6 requirement.
+#2622662 by rollsd: Adjusted logic to fix problem with entity types that only
+  have one bundle.
+#2648804 by DamienMcKenna: Removed some unfinished code from
+  metatag_metatags_delete_multiple().
+#2587725 by DamienMcKenna: Fixed display of meta tag labels in localized entity
+  & config forms.
+#2389929 by michee.lengronne, scor, DamienMcKenna, sint: Fixed Google+ HTML
+  head declaration so it validates, but a change to the site's html.tpl.php is
+  now required.
+#2184857 by DamienMcKenna, mikeytown2: Expanded metatag_metatags_load() to allow
+  meta tags to be loaded by revision ID.
+#1838554 by dimchich, jcnventura, Bao Truong, marcelovani, DamienMcKenna: Allow
+  entity tokens to be properly used on entity pages via Metatag:Context i.e.
+  'by path'.
+#2361343 by marcelovani: Allow by-path definitions to override the page title of
+  all pages.
+#2493689 by DamienMcKenna: Removed two arguments from metatag_metatags_save()
+  that weren't actually being used.
+#2613598 by DamienMcKenna: Added a copy of the Contributor Covenant in the
+  CODE_OF_CONDUCT.txt file.
+#2335015 by DamienMcKenna: Remove og:type 'blog'.
+#2649816 by DamienMcKenna: Added a copy of the GPL 2.0 license to the
+  repository.
+#2338211 by DamienMcKenna: Only output the first item of a token for image meta
+  tags that only support one value.
+
+
+Metatag 7.x-1.7, 2015-07-24
+---------------------------
+#2537738 by deepak_zyxware: Incorrect path to fb_social settings page.
+#2535178 by DamienMcKenna: 'multiple' option on Viewport causes problems with
+  the meta tag's intended values.
+#2524460 by DamienMcKenna, adriancotter, gbirch, jrb: Remove custom wrangling
+  for Views-based custom entity displays, added new hook to allow other modules
+  to customize as needed (hook_metatag_views_post_render_get_entity).
+#2199533 by Adrian Richardson, DamienMcKenna, mairi: Don't reload entities when
+  processing tokens, it causes problems with content workflows.
+#2513892 by DamienMcKenna: Tests for user objects.
+#1658970 by DamienMcKenna, stefan.r, subhojit777, HyperGlide, jenlampton: Drush
+  script to convert data from the Page Title module.
+
+
+Metatag 7.x-1.6, 2015-06-30
+---------------------------
+#2503089 by DamienMcKenna: Added support for the "any" favicon, used for SVG
+  files in Safari 9.
+#2499865 by DamienMcKenna: Improvements to entity selection, all sites will now
+  automatically start off supporting news, terms and users.
+#2503097 by DamienMcKenna: Added the theme-color meta tag.
+#2503089 by jdanthinne, DamienMcKenna: Improved wording of the SVG favicon
+  description.
+#2499737 by DamienMcKenna: Moved the Dublin Core Additional Tags meta tags into
+  a new submodule, metatag_dc_advanced.
+#2499739 by DamienMcKenna: Moved the Open Graph Products meta tags into a new
+  submodule, metatag_opengraph_products.
+#2498173 by DamienMcKenna: Clarified the touch icon meta tags available by
+  adding separate primary vs precomposed tags.
+#2499739 by DamienMcKenna: Follow-up to fix a missing variable.
+#2507025 by DamienMcKenna: Fixed Panels/Panelizer support for entities after
+  changes in 1.5.
+#2505051 by DamienMcKenna: Automatically check for image URLs in image meta
+  tags; added a new 'image' attribute to meta tag specifications.
+#2504561 by hanoii: Remove unused metatag_load_entity_from_path() function and
+  corresponding hook.
+#2222711 by hanoii, DamienMcKenna, andyg5000: Fixed Views support for entities
+  after API changes in 1.5.
+#2467587 by DamienMcKenna: Clear the Metatag cache when a node's state is
+  changed via Workbench Moderation.
+#2449425 by DamienMcKenna: Only process string values for token replacement.
+#2265453 by zd123, DamienMcKenna: CTools keyword substitution for Panels
+  integration.
+#2512284 by DamienMcKenna: Missing token browser link on main Metatag fieldset.
+#2513890 by DamienMcKenna: Added tests for taxonomy term integration.
+#1404270 by JStanton, DamienMcKenna: Added the Refresh meta tag.
+#2384673 by etroid, DamienMcKenna: Added the shortcut icon meta tag.
+#2514852 by rrfegade: Spelling mistakes in README.txt files.
+#2514878 by DamienMcKenna: Ignore admin pages on Views/CTools-driven entity
+  pages.
+#2514812 by david_garcia: Fix Views integration for ECK entities.
+#2514572 by DamienMcKenna: Don't check if records exist when deleting them,
+  just run the deletion query.
+
+
+Metatag 7.x-1.5, 2015-05-29
+---------------------------
+#2442183 by DamienMcKenna, jwilson: Mention the Image URL Formatter module in
+  the README.txt file.
+#2451231 by DamienMcKenna: Fixed the Devel:Generate integration.
+By DamienMcKenna: Rearranged og:type select_or_other integration code to be
+  after the og:type tag definition.
+By DamienMcKenna: Removed duplicate description for video:writer meta tag.
+By DamienMcKenna: Standardized structure of all theme functions.
+#2452985 by DamienMcKenna: Added 39 additional Dublin Core meta tags.
+By DamienMcKenna: Removed duplicate dcterms.rights tag.
+By DamienMcKenna: Updated new 'date' dcterms tags to use 'date' generator.
+#2460791 by DamienMcKenna: Allow the page region used to trigger output to be
+  changed; see the advanced settings page for details.
+#2462117 by DamienMcKenna: Allow the included default configurations to be
+  disabled.
+#2454499 by Dmitriy.trt: metatag_config_is_enabled() returned FALSE for empty
+  config, when checked with defaults.
+#2407477 by greggles: Provide support for Twitter app tags without having to use
+  an "app" type.
+By DamienMcKenna: Fixed a small mistake in output of metatag_update_7011().
+#2429091 by deviantintegral, DamienMcKenna: Add support for applinks.org tags.
+#2417155 by dobe: Feeds import fails because of placement of entity_type.
+By DamienMcKenna: Minor text improvements per D8 branch.
+By DamienMcKenna: Clarified compatibility with Workbench Moderation.
+#2473459 by DamienMcKenna: Updated all links to d.o.
+#2479325 by DamienMcKenna: Require Token 1.6.
+#1491562 by jonathan_hunt, knalstaaf: Add instructions to README.txt explaining
+  how to configure meta tags for entity bundles.
+#2449425 by DamienMcKenna: Refactored select_or_other usage, API addition.
+#2487179 by DamienMcKenna: Allow longer cache IDs to reduce conflicts.
+#2473107 by DamienMcKenna: Added a warning about having more og:image:type
+  values than there are og:image values, it can lead to Facebook validation
+  errors.
+#2474427 by DamienMcKenna: Added an advanced option to disable the output cache.
+#2432517 by undertext, DamienMcKenna: Check all CTools contexts, not just the
+  first one.
+#2449425 by DamienMcKenna: Follow-up for select_or_other bug.
+#2415983 by ciss, DamienMcKenna: Core elements not removed when no metatag
+  substitute provided.
+#2490846 by DamienMcKenna: Using [metatag] tokens failed if the value was empty.
+#2466629 by DamienMcKenna, rupertj: Ensure entity is an object before checking
+  its language code.
+#2081717 by DamienMcKenna: Added Admin Menu item for flush the Metatag caches.
+By DamienMcKenna: Noted that the 'shortlink' meta tag replaces 'shorturl'.
+#2493711 by akoe, DamienMcKenna: Added geo.position, geo.placename, geo.region
+  and icbm meta tags.
+#2493395 by das-peter: Google+ itemtype meta tag malformatted.
+#1915926 by DamienMcKenna: Allow multiple fb:admins values.
+#2494271 by DamienMcKenna: og:street_address, og:postal_code, og:country_name
+  are incorrect.
+#1498762 by DamienMcKenna: Added the Rating meta tag.
+#2451271 by DamienMcKenna: Added the Referrer meta tag.
+#1285946 by DamienMcKenna: Added metatag_mobile submodule with a few mobile
+  -related meta tags.
+#2475147 by MatthewHager, DamienMcKenna: Fixed Feeds integration after its API
+  changed.
+#2070821 by DamienMcKenna, pounard: Major re-architecture to how supported
+  entities are handled.
+#2495877 by DamienMcKenna: Added a Context global config instance.
+#2495875 by DamienMcKenna: Added a Panels global config instance.
+#1281138 by jantoine, DamienMcKenna, drupalninja99, stuart.crouch, subhojit777,
+  KarlShea: Metatag:Importer submodule for importing data from Nodewords (D6).
+#2376921 by DamienMcKenna: Trigger an entity cache clear when meta tags are
+  saved or deleted.
+#2496487 by DamienMcKenna: The function is called entity_get_info(), not
+  entity_info().
+#2103321 by mistermoper, DamienMcKenna: Added 24 more Open Graph meta tags for
+  managing product information.
+#2085747 by DamienMcKenna: Added twelve favicon varieties to a new submodule,
+  Metatag: Favicons.
+By DamienMcKenna: Removed message from metatag_opengraph_install() warning about
+  compatibility with the RDF module, which is no longer applicable.
+#2408211 by infinet, MatthewHager: Context substitution added to Metatag: Panels
+  output.
+#2156653 by mitsuroseba, undertext, asgorobets, DamienMcKenna: Added a custom
+  pane for adding the meta tags fieldset to a node form customized via Panels.
+#2496487 by DamienMcKenna: Follow-up on previous commit, remove unneeded
+  function_exists() call.
+
+
+Metatag 7.x-1.5-beta1, 2015-02-02
+---------------------------------
+#2362639 by DamienMcKenna: Improved defaults for Google+ meta tags.
+#2318985 by DamienMcKenna: Indicate that Open Graph tags are used on Pinterest.
+#2362639 by DamienMcKenna: Added itemtype default values for Google+ meta tags.
+#2358137 by DamienMcKenna: Added a submodule for managing site verification
+  meta tags, the first of which is for Google.
+#2358131 by DamienMcKenna: Support for the Pinterest verification meta tag.
+#1848338 by larowlan, DamienMcKenna: Added more tests, especially one for
+  checking the editorial process on a node.
+#2362893 by ipo4ka704: Don't assume the first Panels context is an object.
+#2363591 by DamienMcKenna: Added a default for the 'image' meta tag on user
+  entity pages.
+#1967856 by duozersk: Fixed a minor mistake in the previous commit.
+#2370943 by Simon George: Removed redundant comment.
+#2373189 by nmillin: Added support for the Bing verification code.
+#2358139 by nmillin: Added support for the Yandex verification code.
+#2378127 by DamienMcKenna: Support for rel="alternate" hreflang="x" link tag.
+#2376915 by jenlampton, DamienMcKenna: Added the og:image:url meta tag.
+#1978708 by DamienMcKenna, scor: Updated warning about compatibility with the
+  RDF module in Drupal core before 7.33.
+#2385265 by mikemiles86: Correctly flatten Metatag form fieldsets in Context
+  integration.
+#2370439 by potop, DamienMcKenna: Work around hook_entity_load() problems by
+  loading entity info in metatag_entity_supports_metatags() on demand.
+#1868460 by preshetin: Added the rel=prev and rel=next meta tags.
+#2388339 by das-peter: Fix select_or_other integration for Metatag:GooglePlus,
+  add the missing element_validator.
+#2391975 by Spleshka: Support scenarios where the entity is possibly blank or
+  has no entity_id assigned yet, e.g. Profile2 pages.
+#2400241 by greggles: Typo in description of robots-notranslate option.
+#2400529 by greggles: Add support for OG product price:amount, price:currency.
+#2411607 by liberatr, DamienMcKenna: README.txt note about using the
+  field_multiple_types module to control how many items are output.
+#2415025 by DamienMcKenna: l() in metatag_metatag_info() creates recursion bug.
+#2411477 by betz, DamienMcKenna: $form[#entity] doesn't work for all entities.
+#2411549 by maijs: Language is lost during migration.
+#2198669 by D2ev: Using metatag tokens can easily cause an infinite loop.
+#2411477 by DamienMcKenna, betz: Follow-up to last change.
+
+
 Metatag 7.x-1.4, 2014-10-09
 ---------------------------
 #2353079 by DamienMcKenna: Fixed Views integration, for real this time.
@@ -70,6 +520,7 @@ By DamienMcKenna: Corrected the namespace prefix for OG video meta tags.
 #2186155 by DamienMcKenna: Follow-up to fix a number of scenarios.
 
 
+
 Metatag 7.x-1.0-rc2, 2014-08-05
 -------------------------------
 #1904266 by mvwensen, DamienMcKenna: Added the dcterms.modified meta tag.
@@ -95,6 +546,8 @@ Metatag 7.x-1.0-rc2, 2014-08-05
   didn't work correctly on Panels pages.
 #2153977 by paolomainardi, DamienMcKenna: Fix for translations of base entity
   type configuration when there is no bundle configuration.
+#2572891 by DamienMcKenna: Removed most 'fine tuning' items from the README.txt
+  file, added notes again about using Imagecache_Token to improve images.
 
 
 Metatag 7.x-1.0-rc1, 2014-07-12
@@ -272,7 +725,7 @@ By DamienMcKenna: Moved hook_requirements to the top of metatag.install.
   primary keys.
 #1864306 by DamienMcKenna: Follow-on to only export the $config->disabled
   setting if it exists.
-#2173271 by deetergp: Spelling and grammer fixes for README.txt.
+#2173271 by deetergp: Spelling and grammar fixes for README.txt.
 #2172433 by fabsor, DamienMcKenna: Ensure that update 7015 runs early enough to
   avoid data corruption or errors during other updates.
 #2156261 by plopesc, DamienMcKenna: Allow meta tags for 403/404 error pages to

+ 3 - 0
sites/all/modules/contrib/seo/metatag/CODE_OF_CONDUCT.txt

@@ -0,0 +1,3 @@
+Contributor Code of Conduct
+---------------------------
+See: https://www.drupal.org/dcoc

+ 218 - 110
sites/all/modules/contrib/seo/metatag/README.txt

@@ -8,26 +8,35 @@ meta tags may help improve your site's & pages' ranking, thus may aid with
 achieving a more prominent display of your content within search engine
 results. Additionally, using meta tags can help control the summary content
 that is used within social networks when visitors link to your site,
-particularly the Open Graph submodule for use with Facebook, LinkedIn, etc (see
-below).
+particularly the Open Graph submodule for use with Facebook, Pinterest,
+LinkedIn, etc (see below).
 
 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,
-  GENERATOR, IMAGE_SRC, KEYWORDS, PUBLISHER, REVISIT-AFTER, RIGHTS, ROBOTS,
-  SHORTLINK and the page's TITLE tag.
+  GENERATOR, GEO.PLACENAME, GEO.POSITION, GEO.REGION, ICBM IMAGE_SRC, KEYWORDS,
+  PUBLISHER, REFRESH, REVISIT-AFTER, RIGHTS, ROBOTS, SHORTLINK, and the page's
+  TITLE tag.
 
 * 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,
-  e.g., Revisioning module.
+  including compatibility with the Revisioning and Workbench Moderation modules.
+
+* Automatically extracts URLs from image fields, no need for extra modules.
+
+* A custom pager string may be added to meta tags by inserting the token
+  [current-page:pager] into e.g. page titles, description tags, etc. The
+  replacement string may be customized from the settings page.
 
 * Per-path control over meta tags using the "Metatag: Context" submodule
   (requires the Context module).
@@ -42,8 +51,14 @@ The primary features include:
 * The fifteen Dublin Core Basic Element Set 1.1 meta tags may be added by
   enabling the "Metatag: Dublin Core" submodule.
 
-* The Open Graph Protocol meta tags, as used by Facebook, LinkedIn and other
-  sites, may be added by enabling the "Metatag: Open Graph" submodule.
+* Forty additional Dublin Core meta tags may be added by enabling the "Metatag:
+  Dublin Core Advanced" submodule.
+
+* The Open Graph Protocol meta tags, as used by Facebook, Pinterest, LinkedIn
+  and other sites, may be added by enabling the "Metatag: Open Graph" submodule.
+
+* Twenty six additional Open Graph Protocol meta tags are provided for
+  describing products in the "Metatag: Open Graph Products" submodule.
 
 * The Twitter Cards meta tags may be added by enabling the "Metatag: Twitter
   Cards" submodule.
@@ -51,11 +66,29 @@ The primary features include:
 * Certain meta tags used by Google+ may be added by enabling the "Metatag:
   Google+" submodule.
 
-* Facebook's fb:app_id and fb:admins meta tags may be added by enabling the
-  "Metatag: Facebook" submodule. These are useful for sites which are using
-  Facebook widgets or are building custom integration with Facebook's APIs,
-  but they are not needed by most sites and have no bearing on the Open Graph
-  meta tags.
+* Facebook's fb:app_id, fb:admins and fb:pages meta tags may be added by
+  enabling the "Metatag: Facebook" submodule. These are useful for sites which
+  are using Facebook widgets or are building custom integration with Facebook's
+  APIs, but they are not needed by most sites and have no bearing on the
+  Open Graph meta tags.
+
+* The App Links meta tags may be added by enabling the Metatag: App Links
+  submodule.
+
+* Site verification meta tags can be added, e.g. as used by the Google search
+  engine to confirm ownership of the site; see the "Metatag: Verification"
+  submodule.
+
+* The Metatag: Mobile & UI Adjustments submodule adds the MobileOptimized,
+  HandheldFriendly, viewport, cleartype, theme-color, format-detection,
+  apple-mobile-web-app-capable, apple-mobile-web-app-status-bar-style, the
+  android-app and ios-app alternative link meta tags, and the Android manifest
+  tag.
+
+* The hreflang meta tags are available via the Metatag:hreflang submodule.
+
+* A variety of favicon sizes and styles can be added to the global configuration
+  using the Metatag: Favicons submodule.
 
 * An API allowing for additional meta tags to be added, beyond what is provided
   by this module - see metatag.api.php for full details.
@@ -66,20 +99,36 @@ The primary features include:
 * Support for the Feeds module for importing data from external data sources or
   file uploads.
 
+* Support for the Search API module for indexing of keywords.
+
 * Integrates with Devel_Generate, part of the Devel module, to automatically
   generate meta tags for generated nodes, via the Metatag:Devel submodule.
 
 * Integrates with Workbench Moderation (both v1 and v2) allowing meta tags on
   nodes to be managed through the workflow process.
 
-* The Transliteration and Imagecache Token modules (see below) are highly
-  recommended when using image meta tags, e.g. og:image.
+* The Transliteration module (see below) is highly recommended when using image
+  meta tags, e.g. og:image, to ensure that filenames are HTML-safe.
+
+* Adds an extra item to the "Flush all caches" menu for the Admin Menu module,
+  allowing for a quick way to clear the Metatag module's custom caches.
 
-* Several advanced options may be controlled via the Advanced Settings page.
+* A custom pane, called "Node form meta tags", is available for adding the meta
+  tags fieldset when the node_edit page is customized using Panels; the
+  Metatag: Panels submodule does not need to be enabled in order for this to
+  work.
+
+* Several advanced options may be controlled via the Settings page.
+
+* An import script is provided in the Metatag:Importer submodule for D6 sites
+  that used Nodewords and need to migrate the data.
+
+* If the Media module (v2) is installed, the Media WYSIWYG submodule will be
+  used to automatically filter out Media's embed codes.
 
 
 Configuration
-------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
  1. On the People Permissions administration page ("Administer >> People
     >> Permissions") you need to assign:
 
@@ -94,13 +143,20 @@ Configuration
     assigned specifically for the front page:
       admin/config/search/metatags
 
- 3. Each supported entity object (nodes, terms, users) will have a set of meta
+ 3. The list of supported entity types (nodes, taxonomy terms, etc) and bundles
+    (content types, vocabularies, etc) may be controlled from the Settings page:
+      admin/config/search/metatags/settings
+
+ 4. In order to provide a specific configuration per entity bundle (content
+    type, vocabulary, etc), click "Add default meta tags".
+
+ 5. Each supported entity object (nodes, terms, users) will have a set of meta
     tag fields available for customization on their respective edit page, these
     will inherit their values from the defaults assigned in #2 above. Any
     values that are not overridden per object will automatically update should
     the defaults be updated.
 
- 4. As the meta tags are output using Tokens, it may be necessary to customize
+ 6. As the meta tags are output using Tokens, it may be necessary to customize
     the token display for the site's entities (content types, vocabularies,
     etc). To do this go to e.g., admin/structure/types/manage/article/display,
     in the "Custom Display Settings" section ensure that "Tokens" is checked
@@ -108,18 +164,73 @@ 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.
+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.
 
-Fine Tuning
-------------------------------------------------------------------------------
-All of these may be controlled from the advanced settings page:
-admin/config/search/metatags/settings
+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
+
+
+Search API integration
+--------------------------------------------------------------------------------
+Entity meta tag values can be made searchable using the Search API module
+(https://www.drupal.org/project/search_api).
+
+ 1. Select "Meta tags" under "Data alterations" in the filters for the
+    index:
+      admin/config/search/search_api/index/INDEX NAME/workflow
+ 2. Meta tag fields will now appear under "Fields" and can be enabled there:
+      admin/config/search/search_api/index/INDEX NAME/fields
+
+
+Fine tuning & suggestions
+--------------------------------------------------------------------------------
+* There are many options available on the settings page to control how Metatag
+  works:
+    admin/config/search/metatags/settings
 
 * It is possible to "disable" the meta tags provided by Drupal core, i.e.
   "generator", "canonical URL" and "shortlink", though it may not be completely
@@ -128,62 +239,38 @@ admin/config/search/metatags/settings
   that is necessary is to clear the default value for that tag, e.g. on the
   global settings for nodes, which will result in the tag not being output for
   those pages.
-* By default Metatag will load the global default values for all pages that do
-  not have meta tags assigned via the normal entity display or via Metatag
-  Context. This may be disabled by setting the variable 'metatag_load_all_pages'
-  to FALSE through one of the following methods:
-  * Use Drush to set the value:
-    drush vset metatag_load_all_pages FALSE
-  * Hardcode the value in the site's settings.php file:
-    $conf['metatag_load_all_pages'] = FALSE;
-  To re-enable this option simply set the value to TRUE.
-* By default users will be able to edit meta tags on forms based on the 'Edit
-  meta tags' permission. The 'metatag_extended_permissions' variable may be set
-  to TRUE to give each individual meta tag a separate permission. This allows
-  fine-tuning of the site's editorial control, and for rarely-used fields to be
-  hidden from most users. Note: The 'Edit meta tags' permission is still
-  required otherwise none of the meta tag fields will display at all. The
-  functionality may be disabled again by either removing the variable or
-  setting it to FALSE.
-* It's possible to disable Metatag integration for certain entity types or
-  bundles using variables. To disable an entity just assigning a variable
-  'metatag_enable_{$entity_type}' or 'metatag_enable_{$entity_type}__{$bundle}'
-  the value FALSE, e.g.:
-    // Disable metatags for file_entity.
-    $conf['metatag_enable_file'] = FALSE;
-    // Disable metatags for carousel nodes.
-    $conf['metatag_enable_node__carousel'] = FALSE;
-  To enable the entity and/or bundle simply set the value to TRUE or remove the
-  settings.php line. Note that the Metatag cache will need to be cleared after
-  changing these settings, specifically the 'info' records, e.g., 'info:en'; a
-  quick version of doing this is to clear the site caches using either Drush,
-  Admin Menu (flush all caches), or the "Clear all caches" button on
-  admin/config/development/performance.
-* By default Metatag will not display meta tags on admin pages. To enable meta
-  tags on admin pages simply set the 'metatag_tag_admin_pages' variable to TRUE
-  through one of the following methods:
-  * Use Drush to set the value:
-    drush vset metatag_tag_admin_pages TRUE
-  * Hardcode the value in the site's settings.php file:
-    $conf['metatag_tag_admin_pages'] = TRUE;
-  To re-enable this option simply set the value to FALSE or delete the
-  settings.php line.
-* When loading an entity with multiple languages for a specific language the
-  meta tag values saved for that language will be used if they exist, otherwise
-  values assigned to the entity's default language will be used. This
-  may be disabled using the enabling the "Don't load entity's default language
-  values if no languages match" option on the Advanced Settings page, which will
-  cause default values to be used should there not be any values assigned for
-  the current requested language.
+
+* When using Features to export Metatag configurations, it is suggested to
+  override all of the default configurations and then disable the default
+  configurations via the advanced settings page; doing so will avoid potential
+  conflicts of the same configurations being loaded by both the Metatag module
+  and the new Features-based modules.
+
+* Using fields to automatically fill in values for image meta tags is the
+  recommended way of inserting images - the module will automatically extract
+  the URL from the value. However, by default this forces social networks,
+  search engines and certain browsers to download the original version of the
+  image, which could be multiple megabytes. The alternative is to use the
+  Imagecache_Token module to instead load meta tags via a specific image style.
+  As an example, in order to load an image from a node field named
+  "field_meta_tag_image" using the "seo_thumbnail" style, the following token
+  would be used:
+    [node:field_meta_tag_image:seo_thumbnail:uri]
+  or
+    [node:field_meta_tag_image:seo_thumbnail]
+  (They give the same results)
+  Additionally, dimensions of the image may be obtained from the following:
+    [node:field_meta_tag_image:seo_thumbnail:width]
+    [node:field_meta_tag_image:seo_thumbnail:height]
 
 
 Developers
-------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
 Full API documentation is available in metatag.api.php.
 
-To enable Metatag support in custom entities, add 'metatag' => TRUE to either
-the entity or bundle definition in hook_entity_info(); see metatag.api.php for
-further details and example code.
+It is not necessary to control Metatag via the entity API, any entity that has
+view modes defined and is not a configuration entity is automatically suitable
+for use.
 
 The meta tags for a given entity object (node, etc) can be obtained as follows:
   $metatags = metatags_get_entity_metatags($entity_id, $entity_type, $langcode);
@@ -191,18 +278,18 @@ The result will be a nested array of meta tag structures ready for either output
 via drupal_render(), or examining to identify the actual text values.
 
 
-Troubleshooting / Known Issues
-------------------------------------------------------------------------------
-* Image fields do not output very easily in meta tags, e.g. for og:image,
-  without use of the Imagecache Token module (see below). This also provides a
-  way of using an image style to resize the original images first, rather than
-  requiring visitors download multi-megabyte original images.
+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:
     <?php render($page['content']); ?>
   or
     <?php render($page['content']['metatags']); ?>
   Without one of these being present the meta tags will not be displayed.
+* An alternative method to fixing the missing-tags problem is to change the page
+  region used to output the meta tags. The region used may be controlled from
+  the settings page, it is recommended to test different options to identify the
+  one that works best for a specific site.
 * Versions of Drupal older than v7.17 were missing necessary functionality for
   taxonomy term pages to work correctly.
 * Using Metatag with values assigned for the page title and the Page Title
@@ -217,10 +304,9 @@ Troubleshooting / Known Issues
     <title>"The page title | My cool site"</title>
   The solution is to remove the browser plugin - the page's actual output is not
   affected, it is just a problem in the browser.
-* The core RDF module is known to cause validation problems for Open Graph meta
-  tags output by the Metatag:OpenGraph module. Unless it is actually needed for
-  the site, it may be worthwhile to disable the RDF module to avoid any
-  possible problems for the Open Graph integration.
+* Drupal core versions before v7.33 had a bug which caused validation problems
+  in the Open Graph output if the RDF module was also enabled. The solution is
+  to update to core v7.33 or newer.
 * If the Administration Language (admin_language) module is installed, it is
   recommended to disable the "Force language neutral aliases" setting on the
   Admin Language settings page, i.e. set the "admin_language_force_neutral"
@@ -228,20 +314,27 @@ Troubleshooting / Known Issues
 
 
 Related modules
-------------------------------------------------------------------------------
-Some modules are available that extend Metatag with additional functionality:
-
-* Imagecache Token
-  https://www.drupal.org/project/imagecache_token
-  Provides additional tokens for image fields that can be used in e.g. the
-  og:image meta tag; ultimately makes it possible to actually use image meta
-  tags without writing custom code.
+--------------------------------------------------------------------------------
+Some modules are available that extend Metatag with additional or complimentary
+functionality:
 
 * Transliteration
   https://drupal.org/project/transliteration
   Tidies up filenames for uploaded files, e.g. it can remove commas from
   filenames that could otherwise break certain meta tags.
 
+* Imagecache Token
+  https://www.drupal.org/project/imagecache_token
+  Use tokens to load images via image styles, rather than forcing meta tags to
+  use the original image.
+
+* Alternative hreflang
+  https://www.drupal.org/project/hreflang
+  An alternative to the Metatag:hreflang module. Automatically outputs
+  <link rel="alternate" hreflang="x" href="http://" /> meta tags on every page
+  for each language/locale available on the site. Also does not provide any way
+  of overriding the values or setting the x-default value.
+
 * Domain Meta Tags
   https://drupal.org/project/domain_meta
   Integrates with the Domain Access module, so each site of a multi-domain
@@ -249,8 +342,8 @@ Some modules are available that extend Metatag with additional functionality:
 
 * Select or Other
   https://drupal.org/project/select_or_other
-  Enhances the user experience of the metatag_opengraph submodule by allowing
-  the creation of custom Open Graph types.
+  Enhances the user experience of the metatag_google_plus and metatag_opengraph
+  submodules by allowing the creation of custom itemtype and og:types values.
 
 * Node Form Panes
   https://drupal.org/project/node_form_panes
@@ -260,24 +353,39 @@ Some modules are available that extend Metatag with additional functionality:
   https://drupal.org/project/textimage
   Supports using Textimage's custom tokens in meta tag fields.
 
+* Field Multiple Limit
+  https://drupal.org/project/field_multiple_limit
+  Allows control over how many items are output in a multi-item field, useful
+  with meta tags that only allow for one item but which are assigned from fields
+  which accept multiple items, e.g. og:audio and og:video.
+
+* Yoast SEO
+  https://www.drupal.org/project/yoast_seo
+  Adds integration with the Yoast service (https://yoast.com/).
+
+* Parse.ly Publishing Analytics
+  https://www.drupal.org/project/parsely
+  Automatically generates meta tags for the Parse.ly service.
+
 
-Credits / Contact
-------------------------------------------------------------------------------
+Credits / contact
+--------------------------------------------------------------------------------
 Currently maintained by Damien McKenna [1] and Dave Reid [2]; all initial
 development was by Dave Reid.
 
-Ongoing development is sponsored by Mediacurrent [3] and Palantir.net [4]. All
-initial development was sponsored by Acquia [5] and Palantir.net.
+Ongoing development is sponsored by Mediacurrent [3] and Lullabot [4]. All
+initial development was sponsored by Acquia [5] and Palantir.net [6].
 
 The best way to contact the authors is to submit an issue, be it a support
 request, a feature request or a bug report, in the project issue queue:
-  http://drupal.org/project/issues/metatag
+  https://www.drupal.org/project/issues/metatag
 
 
 References
-------------------------------------------------------------------------------
-1: http://drupal.org/user/108450
-2: http://drupal.org/user/53892
+--------------------------------------------------------------------------------
+1: https://www.drupal.org/u/damienmckenna
+2: https://www.drupal.org/u/dave-reid
 3: http://www.mediacurrent.com/
-4: http://www.palantir.net/
+4: http://www.lullabot.com/
 5: http://www.acquia.com/
+6: http://www.palantir.net/

File diff suppressed because it is too large
+ 74 - 33
sites/all/modules/contrib/seo/metatag/metatag.admin.inc


+ 164 - 99
sites/all/modules/contrib/seo/metatag/metatag.api.php

@@ -4,67 +4,6 @@
  * API documentation for the Metatag module.
  */
 
-/**
- * To enable Metatag support in custom entities, add 'metatags' => TRUE to the
- * entity definition in hook_entity_info(), e.g.:
- * 
- * /**
- *  * Implements hook_entity_info().
- *  *
- *  * Taken from the Examples module.
- *  * /
- * function entity_example_entity_info() {
- *   $info['entity_example_basic'] = array(
- *     'label' => t('Example Basic Entity'),
- *     'controller class' => 'EntityExampleBasicController',
- *     'base table' => 'entity_example_basic',
- *     'uri callback' => 'entity_example_basic_uri',
- *     'fieldable' => TRUE,
- *     'metatags' => TRUE,
- *     'entity keys' => array(
- *       'id' => 'basic_id' , // The 'id' (basic_id here) is the unique id.
- *       'bundle' => 'bundle_type' // Bundle will be determined by the 'bundle_type' field
- *     ),
- *     'bundle keys' => array(
- *       'bundle' => 'bundle_type',
- *     ),
- *     'static cache' => TRUE,
- *     'bundles' => array(
- *       'first_example_bundle' => array(
- *         'label' => 'First example bundle',
- *         'admin' => array(
- *           'path' => 'admin/structure/entity_example_basic/manage',
- *           'access arguments' => array('administer entity_example_basic entities'),
- *         ),
- *       ),
- *     ),
- *     'view modes' => array(
- *       'tweaky' => array(
- *         'label' => t('Tweaky'),
- *         'custom settings' =>  FALSE,
- *       ),
- *     )
- *   );
- * 
- *   return $info;
- * }
- *
- * The definition of each bundle may be handled separately, thus support may be
- * disabled for the entity as a whole but enabled for individual bundles. This
- * is handled via the 'metatags' value on the bundle definition, e.g.:
- *
- *     'bundles' => array(
- *       'first_example_bundle' => array(
- *         'label' => 'First example bundle',
- *         'metatags' => TRUE,
- *         'admin' => array(
- *           'path' => 'admin/structure/entity_example_basic/manage',
- *           'access arguments' => array('administer entity_example_basic entities'),
- *         ),
- *       ),
- *     ),
- */
-
 /**
  * Provides a default configuration for Metatag intances.
  *
@@ -80,7 +19,7 @@
  * The $config->disabled boolean attribute indicates whether the Metatag
  * instance should be enabled (FALSE) or disabled (TRUE) by default.
  *
- * @return
+ * @return array
  *   An associative array containing the structures of Metatag instances, as
  *   generated from the Export tab, keyed by the Metatag config name.
  *
@@ -114,6 +53,12 @@ function hook_metatag_config_default() {
   return $configs;
 }
 
+/**
+ * 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.
  *
@@ -128,60 +73,105 @@ function hook_metatag_config_default() {
  */
 function hook_metatag_bundled_config_alter(&$config) {
 }
-  
+
 /**
- * 
+ * Allow modules to act upon the record insertion.
  */
-function hook_metatag_config_default_alter(&$config) {
+function hook_metatag_config_instance_info() {
+  return array();
 }
 
 /**
- * 
+ * Alter record insertion provided by modules with the previous hook.
+ *
+ * @see hook_metatag_config_instance_info()
  */
-function hook_metatag_config_delete($entity_type, $entity_ids, $revision_ids, $langcode) {
+function hook_metatag_config_instance_info_alter(&$info) {
 }
 
 /**
- * 
+ * Allow the Metatag config instance name to be changed.
+ *
+ * By default, Metatag module define config instance per entity bundle, making
+ * it impossible to store set of meta tags based on other criteria. Combining
+ * this hook with hook_metatag_config_instance_info_alter() it is possible to
+ * alter the logic of how instances are named based on any custom criteria.
+ *
+ * @param string $instance
+ *   The name of the instance generated by default.
+ * @param object $entity
+ *   The entity object to generate the Metatag instance name for.
+ * @param string $entity_type
+ *   The entity type of the entity.
+ * @param string $bundle
+ *   The bundle of the entity.
  */
-function hook_metatag_config_insert($config) {
+function hook_metatag_get_entity_metatags_instance_alter(&$instance, $entity, $entity_type, $bundle) {
+  if ($entity_type == 'user') {
+    // Split config instances based on user roles.
+    foreach (array_reverse(user_roles(), TRUE) as $rid => $role) {
+      if ($rid == DRUPAL_AUTHENTICATED_RID) {
+        continue;
+      }
+
+      if (isset($entity->roles[$rid])) {
+        $instance = 'user:' . $role;
+        break;
+      }
+    }
+  }
 }
 
 /**
- * 
+ * Never triggered?
+ *
+ * @todo Confirm whether this still exists.
  */
-function hook_metatag_config_instance_info() {
-  return array();
+function hook_metatag_config_load() {
 }
 
 /**
- * 
+ * Never triggered?
+ *
+ * @todo Confirm whether this still exists.
  */
-function hook_metatag_config_instance_info_alter(&$info) {
+function hook_metatag_config_load_presave() {
 }
 
 /**
- * 
+ * Allow a Metatag configuration to be modified prior to being saved.
+ *
+ * @param object $config
+ *   The configuration object that is about to be saved.
  */
-function hook_metatag_config_load() {
+function hook_metatag_config_presave($config) {
 }
 
 /**
- * 
+ * Triggered when a Metatag configuration is created.
+ *
+ * @param object $config
+ *   The configuration object that was created.
  */
-function hook_metatag_config_load_presave() {
+function hook_metatag_config_insert($config) {
 }
 
 /**
- * 
+ * Triggered when a Metatag configuration is updated.
+ *
+ * @param object $config
+ *   The configuration object that was modified.
  */
-function hook_metatag_config_presave($config) {
+function hook_metatag_config_update($config) {
 }
 
 /**
- * 
+ * Triggered when a Metatag configuration is removed.
+ *
+ * @param object $config
+ *   The name of the configuration object that was removed.
  */
-function hook_metatag_config_update($config) {
+function hook_metatag_config_delete($config) {
 }
 
 /**
@@ -215,6 +205,18 @@ function hook_metatag_config_update($config) {
  *       one meta tag can replace several others, only one of the possible
  *       values will be used, the others will be silently purged upon the next
  *       configuration/object save.
+ *     'multiple' - If set to TRUE the output will be comma-separated and output
+ *       as multiple tags.
+ *     'image' - If set to TRUE some additional effort will be added to attempt
+ *       extracting image URLs from the value. Currently limited to matching
+ *       the default output of core image theming, i.e. the following string:
+ *         src="[URL]" width=
+ *     'url' - If set to TRUE, relative paths will be converted to absolute.
+ *     'is_language' - If set to TRUE, will not allow the Drupal default
+ *       language value "und" to be output.
+ *     'select_or_other' - If set to TRUE, form[#type] is set to 'select' and
+ *       the "select_or_other" module is available, that module will be used to
+ *       provide a text field to manually insert another option.
  *     'form' - Optional items to be passed directly to the form; uses standard
  *       Form API values.
  *     'devel_generate' - Optional values to be passed to the Devel Generate
@@ -252,24 +254,20 @@ function hook_metatag_config_update($config) {
  *   Note: 'label', 'description', and any text strings passed in 'form', should
  *   be translated.
  *
- * @see metatag_metatag_info().
+ * @see metatag_metatag_info()
  */
 function hook_metatag_info() {
   return array();
 }
 
 /**
- * 
+ * Alter record insertion provided by modules with the previous hook.
+ *
+ * @see hook_metatag_info()
  */
 function hook_metatag_info_alter(&$info) {
 }
 
-/**
- * 
- */
-function hook_metatag_load_entity_from_path_alter(&$path, $result) {
-}
-
 /**
  * Alter metatags before being cached.
  *
@@ -291,13 +289,13 @@ function hook_metatag_metatags_view_alter(&$output, $instance, $options) {
 }
 
 /**
- * 
+ * Allow other modules to customize the data to generate the cache ID.
  */
 function hook_metatag_page_cache_cid_parts_alter(&$cid_parts) {
 }
 
 /**
- * 
+ * Allow other modules to alter the meta tags prior to saving.
  */
 function hook_metatag_presave(&$metatags, $entity_type, $entity_id, $revision_id, $langcode) {
 }
@@ -320,16 +318,16 @@ function hook_metatag_presave(&$metatags, $entity_type, $entity_id, $revision_id
  * @see metatag_field_attach_form()
  */
 function hook_metatag_token_types_alter(&$options) {
-  // Watchout: $options['token types'] might be empty
+  // Watchout: $options['token types'] might be empty.
   if (!isset($options['token types'])) {
     $options['token types'] = array();
   }
 
-  if ($options['context'] == 'config1'){
-    $options['token types'] += array('token_type1','token_type2');
+  if ($options['context'] == 'config1') {
+    $options['token types'] += array('token_type1', 'token_type2');
   }
-  elseif ($options['context'] == 'config2'){
-    $options['token types'] += array('token_type3','token_type4');
+  elseif ($options['context'] == 'config2') {
+    $options['token types'] += array('token_type3', 'token_type4');
   }
 }
 
@@ -343,11 +341,11 @@ function hook_metatag_token_types_alter(&$options) {
  *
  * See facetapi and facetapi_bonus modules for an example of implementation.
  *
- * @param $pattern
+ * @param string $pattern
  *   A string potentially containing replaceable tokens. The pattern could also
  *   be altered by reference, allowing modules to implement further logic, such
  *   as tokens lists or masks/filters.
- * @param $types
+ * @param array $types
  *   Corresponds to the 'token data' property of the $options object.
  *   (optional) An array of keyed objects. For simple replacement scenarios
  *   'node', 'user', and others are common keys, with an accompanying node or
@@ -370,3 +368,70 @@ function hook_metatag_pattern_alter(&$pattern, &$types, $tag_name) {
     $pattern = str_replace('[token_type3]', '', $pattern);
   }
 }
+
+/**
+ * Allow modules to override whether entity types are enabled for use.
+ *
+ * By default the system only support entities that are not configuration
+ * entities, have multiple view modes (excluding those created by the ical,
+ * diff and token modules), are fieldable, and are not one of the following:
+ * - field_collection_item (from the Field Collection module)
+ * - paragraphs_item (from the Paragraphs module)
+ *
+ * @param bool $suitable
+ *   Whether or not the entity type is enabled for use with Metatag.
+ * @param string $entity_type
+ *   The machine name of the entity type.
+ * @param array $entity_info
+ *   The full specifications for this entity type, as returned by
+ *   entity_get_info().
+ */
+function hook_metatag_entity_type_is_supported_alter(&$suitable, $entity_type, $entity_info) {
+  // Enable Metatag support for a custom entity that might otherwise be
+  // ignored, e.g. it doesn't allow fields.
+  if ($entity_type == 'my_entity') {
+    $suitable = TRUE;
+  }
+}
+
+/**
+ * Identify the entity type provided by a specific view.
+ *
+ * When a view is used to display a page it can be difficult to identify what
+ * entity type is being managed. Use this hook to inform Metatag what type of
+ * entity is being displayed.
+ *
+ * @param object $view
+ *   The view object being displayed.
+ *
+ * @return string|NULL
+ *   Should return a string indicating an entity type that will be paired with
+ *   the views' first argument ($view->args[0]) to load that entity.
+ */
+function hook_metatag_views_post_render_get_entity($view) {
+  $display = $view->display[$view->current_display];
+  if ($display->display_options['path'] == 'coolpage/%') {
+    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 = '';
+  }
+}

+ 7 - 0
sites/all/modules/contrib/seo/metatag/metatag.features.inc

@@ -67,6 +67,13 @@ function metatag_features_revert($module) {
       $object->instance = $config;
       $object->config = $feature_conf[$config]['config'];
       metatag_config_save($object);
+
+      if (!empty($feature_conf[$config]['disabled'])) {
+        ctools_export_crud_disable('metatag_config', $config);
+      }
+      else {
+        ctools_export_crud_enable('metatag_config', $config);
+      }
     }
   }
 }

+ 17 - 8
sites/all/modules/contrib/seo/metatag/metatag.feeds.inc

@@ -12,7 +12,7 @@ function metatag_feeds_processor_targets_alter(&$targets, $entity_type, $bundle)
     $info = metatag_get_info();
     foreach ($info['tags'] as $name => $tag) {
       $targets['meta_' . $name] = array(
-        'name' => 'Meta tag: ' . check_plain($tag['label']),
+        'name' => t('Meta tag: !label', array('!label' => $tag['label'])),
         'callback' => 'metatag_feeds_set_target',
         'description' => $tag['description'],
       );
@@ -23,19 +23,27 @@ function metatag_feeds_processor_targets_alter(&$targets, $entity_type, $bundle)
 /**
  * Callback function to set value of a metatag tag.
  */
-function metatag_feeds_set_target($source, $entity, $target, $value) {
+function metatag_feeds_set_target($source, $entity, $target, $values) {
   // Don't do anything if we weren't given any data.
-  if (empty($value)) {
+  if (empty($values)) {
     return;
   }
 
+  // The Feed API was changed so that the $values are passed as an array rather
+  // than a single value. For backwards compatibility, support both.
+  if (!is_array($values)) {
+    $values = array($values);
+  }
+
   // Strip the prefix that was added above.
   $name = str_replace('meta_', '', $target);
 
+  // Identify the type of entity being imported.
+  $entity_type = $entity->feeds_item->entity_type;
+
   // Check for any existing data that may not have been loaded already.
   if (!isset($entity->metatags) && !empty($entity->feeds_item->entity_id) && is_numeric($entity->feeds_item->entity_id)) {
     $entity->metatags = array();
-    $entity_type = $entity->feeds_item->entity_type;
     $entity_id = $entity->feeds_item->entity_id;
 
     // Load the existing entity.
@@ -43,11 +51,12 @@ function metatag_feeds_set_target($source, $entity, $target, $value) {
     if (!empty($stored_entities)) {
       $stored_entity = reset($stored_entities);
       if (!empty($stored_entity)) {
+        // Only load meta tags for the current revision.
+        list(, $revision_id) = entity_extract_ids($entity_type, $stored_entity);
+
         // This function returns all values for all revisions.
-        $metatags = metatag_metatags_load($entity_type, $entity_id);
+        $metatags = metatag_metatags_load($entity_type, $entity_id, $revision_id);
         if (!empty($metatags)) {
-          // Only load meta tags for the current revision.
-          list(, $revision_id) = entity_extract_ids($entity_type, $stored_entity);
           if (!empty($metatags[$revision_id])) {
             // Just copy all meta tags for all languages.
             $entity->metatags = $metatags[$revision_id];
@@ -59,5 +68,5 @@ function metatag_feeds_set_target($source, $entity, $target, $value) {
 
   // Assign the value.
   $langcode = metatag_entity_get_language($entity_type, $entity);
-  $entity->metatags[$langcode][$name]['value'] = $value;
+  $entity->metatags[$langcode][$name]['value'] = $values[0];
 }

+ 131 - 2
sites/all/modules/contrib/seo/metatag/metatag.i18n.inc

@@ -8,11 +8,140 @@
  * 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_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['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' => '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;
+}

+ 200 - 13
sites/all/modules/contrib/seo/metatag/metatag.inc

@@ -1,4 +1,8 @@
 <?php
+/**
+ * @file
+ * Metatag primary classes.
+ */
 
 interface DrupalMetaTagInterface {
 
@@ -23,6 +27,9 @@ interface DrupalMetaTagInterface {
   function getElement();
 
   function tidyValue($value);
+
+  function convertUrlToAbsolute($url);
+
 }
 
 class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
@@ -39,7 +46,7 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
   }
 
   /**
-   * Calculate the weight or this meta tag.
+   * Calculate the weight of this meta tag.
    *
    * @return integer
    */
@@ -55,14 +62,39 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
     return $weight + ($counter++ * 0.1);
   }
 
+  /**
+   * Build the form for this meta tag.
+   *
+   * @return array
+   *   A standard FormAPI array.
+   */
   public function getForm(array $options = array()) {
     return array();
   }
 
+  /**
+   * Get the string value of this meta tag.
+   *
+   * @return string
+   *   The value of this meta tag.
+   */
   public function getValue(array $options = array()) {
-    return $this->tidyValue($this->data['value']);
+    $value = $this->tidyValue($this->data['value']);
+
+    // Translate the final output string prior to output. Use the
+    // '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) {
@@ -131,12 +163,17 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
    *   The meta tag value after it has been tidied up.
    */
   public function tidyValue($value) {
+    // Check for Media strings from the WYSIWYG submodule.
+    if (module_exists('media_wysiwyg') && strpos($value, '[[{') !== FALSE) {
+      $value = media_wysiwyg_filter($value);
+    }
+
     // Specifically replace encoded spaces, because some WYSIWYG editors are
     // silly. Do this before decoding the other HTML entities so that the output
     // doesn't end up with a bunch of a-circumflex characters.
     $value = str_replace('&nbsp;', ' ', $value);
 
-    // Convert any HTML entities into regular characters.
+    // Decode HTML entities.
     $value = decode_entities($value);
 
     // Remove any HTML code that might have been included.
@@ -150,6 +187,34 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
 
     return $value;
   }
+
+  /**
+   * Make sure a given URL is absolute.
+   *
+   * @param string $url
+   *   The URL to convert to an absolute URL.
+   *
+   * @return string
+   *   The argument converted to an absolute URL.
+   */
+  function convertUrlToAbsolute($url) {
+    // Convert paths relative to the hostname, that start with a slash, to
+    // ones that are relative to the Drupal root path.
+    if (strpos($url, base_path()) === 0) {
+      // Logic:
+      // * Get the length of the base_path(), 
+      // * Get a portion of the image's path starting from the position equal
+      //   to the base_path()'s length; this will result in a path relative
+      //   to the Drupal installation's base directory.
+      $len = strlen(base_path());
+      $url = substr($url, $len);
+    }
+
+    // Pass everything else through file_create_url(). The alternative is to
+    // use url() but it would insert '?q=' into the path.
+    return file_create_url($url);
+  }
+
 }
 
 /**
@@ -157,6 +222,9 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
  */
 class DrupalTextMetaTag extends DrupalDefaultMetaTag {
 
+  /**
+   * {@inheritdoc}
+   */
   public function getForm(array $options = array()) {
     $options += array(
       'token types' => array(),
@@ -174,10 +242,33 @@ class DrupalTextMetaTag extends DrupalDefaultMetaTag {
       '#maxlength' => 1024,
     );
 
+    // Optional handling for items that allow multiple values.
     if (!empty($this->info['multiple'])) {
       $form['value']['#description'] .= ' ' . t('Multiple values may be used, separated by a comma. Note: Tokens that return multiple values will be handled automatically.');
     }
 
+    // Optional handling for images.
+    if (!empty($this->info['image'])) {
+      $form['value']['#description'] .= ' ' . t('This will be able to extract the URL from an image field.');
+    }
+
+    // Optional handling for languages.
+    if (!empty($this->info['is_language'])) {
+      $form['value']['#description'] .= ' ' . t('This will not be displayed if it is set to the "Language neutral" (i.e. "und").');
+    }
+
+    // Optional support for select_or_other.
+    if ($form['value']['#type'] == 'select' && !empty($this->info['select_or_other']) && module_exists('select_or_other')) {
+      $form['value']['#type'] = 'select_or_other';
+      $form['value']['#other'] = t('Other (please type a value)');
+      $form['value']['#multiple'] = FALSE;
+      $form['value']['#other_unknown_defaults'] = 'other';
+      $form['value']['#other_delimiter'] = FALSE;
+      $form['value']['#theme'] = 'select_or_other';
+      $form['value']['#select_type'] = 'select';
+      $form['value']['#element_validate'] = array('select_or_other_element_validate');
+    }
+
     // Support for dependencies, using Form API's #states system.
     // @see metatag.api.php.
     // @see https://api.drupal.org/drupal_process_states
@@ -192,26 +283,83 @@ class DrupalTextMetaTag extends DrupalDefaultMetaTag {
     return $form;
   }
 
+  /**
+   * {@inheritdoc}
+   */
   public function getValue(array $options = array()) {
     $options += array(
       'instance' => '',
       'token data' => array(),
+      // Remove any remaining token after the string is parsed.
       'clear' => TRUE,
-      'sanitize' => TRUE,
+      'sanitize' => variable_get('metatag_token_sanitize', FALSE),
       'raw' => FALSE,
     );
 
-    $name = "metatag:" . $options["instance"] . ":" . $this->info["name"];
+    $value = $this->data['value'];
 
-    $value = metatag_translate($name, $this->data['value']);
     if (empty($options['raw'])) {
       // Give other modules the opportunity to use hook_metatag_pattern_alter()
       // to modify defined token patterns and values before replacement.
       drupal_alter('metatag_pattern', $value, $options['token data'], $this->info['name']);
       $value = token_replace($value, $options['token data'], $options);
     }
-    return $this->tidyValue($value);
+
+    // Special handling for language meta tags.
+    if (!empty($this->info['is_language'])) {
+      // If the meta tag value equals LANGUAGE_NONE, i.e. "und", then don't
+      // output it.
+      if (is_string($value) && $value == LANGUAGE_NONE) {
+        $value = '';
+      }
+    }
+
+    // Special handling for images and other URLs.
+    if (!empty($this->info['image']) || !empty($this->info['url'])) {
+      // Support multiple items, whether it's needed or not. Also remove the
+      // empty values.
+      $values = array_filter(explode(',', $value));
+
+      // If this meta tag does *not* allow multiple items, only keep the first
+      // one.
+      if (empty($this->info['multiple']) && !empty($values[0])) {
+        $values = array($values[0]);
+      }
+
+      foreach ($values as $key => &$image_value) {
+        // Remove any unwanted whitespace around the value.
+        $image_value = trim($image_value);
+
+        // If this contains embedded image tags, extract the image URLs.
+        if (!empty($this->info['image']) && strip_tags($image_value) != $image_value) {
+          $matches = array();
+          preg_match('/src="([^"]*)"/', $image_value, $matches);
+          if (!empty($matches[1])) {
+            $image_value = $matches[1];
+          }
+        }
+
+        // Convert the URL to an absolute URL.
+        $image_value = $this->convertUrlToAbsolute($image_value);
+
+        // Replace spaces the URL encoded entity to avoid validation problems.
+        $image_value = str_replace(' ', '%20', $image_value);
+      }
+
+      // Combine the multiple values into a single string.
+      $value = implode(',', $values);
+    }
+
+    $value = $this->tidyValue($value);
+
+    // Translate the final output string prior to output. Use the
+    // '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;
   }
+
 }
 
 /**
@@ -219,6 +367,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();
 
@@ -246,6 +397,7 @@ class DrupalLinkMetaTag extends DrupalTextMetaTag {
       '#attached' => array('drupal_add_html_head' => array(array($element, $element['#id']))),
     );
   }
+
 }
 
 /**
@@ -256,13 +408,18 @@ class DrupalLinkMetaTag extends DrupalTextMetaTag {
  */
 class DrupalTitleMetaTag extends DrupalTextMetaTag {
 
+  /**
+   * {@inheritdoc}
+   */
   public function getElement(array $options = array()) {
     $element = array();
-    $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));
+    if ($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;
   }
+
 }
 
 /**
@@ -270,6 +427,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'])) {
@@ -280,6 +440,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();
 
@@ -293,12 +456,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
+    // '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;
   }
+
 }
 
 /**
@@ -306,10 +480,13 @@ class DrupalListMetaTag extends DrupalDefaultMetaTag {
  */
 class DrupalDateIntervalMetaTag extends DrupalDefaultMetaTag {
 
+  /**
+   * {@inheritdoc}
+   */
   public function getForm(array $options = array()) {
     $form['value'] = array(
       '#type' => 'textfield',
-      '#title' => t('@title interval', array('@title' => $this->info['label'])),
+      '#title' => t('!title interval', array('!title' => $this->info['label'])),
       '#default_value' => isset($this->data['value']) ? $this->data['value'] : '',
       '#element_validate' => array('element_validate_integer_positive'),
       '#maxlength' => 4,
@@ -317,7 +494,7 @@ class DrupalDateIntervalMetaTag extends DrupalDefaultMetaTag {
     );
     $form['period'] = array(
       '#type' => 'select',
-      '#title' => t('@title interval type', array('@title' => $this->info['label'])),
+      '#title' => t('!title interval type', array('!title' => $this->info['label'])),
       '#default_value' => isset($this->data['period']) ? $this->data['period'] : '',
       '#options' => array(
         '' => t('- none -'),
@@ -331,6 +508,9 @@ class DrupalDateIntervalMetaTag extends DrupalDefaultMetaTag {
     return $form;
   }
 
+  /**
+   * {@inheritdoc}
+   */
   public function getValue(array $options = array()) {
     $value = '';
     if (!empty($this->data['value'])) {
@@ -340,6 +520,13 @@ class DrupalDateIntervalMetaTag extends DrupalDefaultMetaTag {
         $value = format_plural($interval, '@count ' . $period, '@count ' . $period . 's');
       }
     }
+
+    // Translate the final output string prior to output. Use the
+    // '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;
   }
+
 }

+ 65 - 6
sites/all/modules/contrib/seo/metatag/metatag.info

@@ -5,21 +5,80 @@ core = 7.x
 
 ; This requires Drupal 7.28 or newer as it fixes the [node:summary] token that
 ; was previously broken.
-dependencies[] = system (>=7.28)
+dependencies[] = system (>= 7.28)
 
-; CTools and Token are also required.
+; CTools is required.
 dependencies[] = ctools
+
+; Requires Token; v7.x-1.6 is highly recommended due to bugs with certain tags
+; when using older versions.
 dependencies[] = token
 
 configure = admin/config/search/metatags
 
 files[] = metatag.inc
 files[] = metatag.migrate.inc
-files[] = metatag.test
+files[] = metatag.search_api.inc
+
+; Tests.
+files[] = tests/metatag.helper.test
+; Basic configuration handling.
+files[] = tests/metatag.unit.test
+; Core entities.
+files[] = tests/metatag.node.test
+files[] = tests/metatag.term.test
+files[] = tests/metatag.user.test
+; Handling of core's default meta tags.
+files[] = tests/metatag.core_tag_removal.test
+; String handling.
+files[] = tests/metatag.string_handling.test
+files[] = tests/metatag.string_handling_with_i18n.test
+; Images need specia attention.
+files[] = tests/metatag.image.test
+; Internationalization & translation.
+files[] = tests/metatag.locale.test
+files[] = tests/metatag.with_i18n_output.test
+files[] = tests/metatag.with_i18n_disabled.test
+files[] = tests/metatag.with_i18n_config.test
+files[] = tests/metatag.with_i18n_node.test
+; Basic integration with Media.
+files[] = tests/metatag.with_media.test
+; Basic integration with Panels.
+files[] = tests/metatag.with_panels.test
+; Basic integration with Profile2.
+files[] = tests/metatag.with_profile2.test
+; Basic integration with Search API.
+files[] = tests/metatag.with_search_api.test
+; Basic integration with Views.
+files[] = tests/metatag.with_views.test
+
+; This is required for testing image handling.
+test_dependencies[] = devel
+test_dependencies[] = imagecache_token
+
+; These are required for the internationalization & translation functionality.
+test_dependencies[] = entity_translation
+test_dependencies[] = i18n
+
+; These are required for the submodules.
+test_dependencies[] = context
+test_dependencies[] = panels
+test_dependencies[] = views
+
+; These are required for the Media integration.
+test_dependencies[] = file_entity
+test_dependencies[] = media
+
+; These are required for the Search API integration.
+test_dependencies[] = entity
+test_dependencies[] = search_api
+
+; This is required for the Profile2-related tests.
+test_dependencies[] = profile2
 
-; Information added by Drupal.org packaging script on 2014-10-10
-version = "7.x-1.4"
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
 core = "7.x"
 project = "metatag"
-datestamp = "1412909330"
+datestamp = "1467306248"
 

+ 836 - 161
sites/all/modules/contrib/seo/metatag/metatag.install

@@ -54,6 +54,9 @@ function metatag_requirements($phase) {
     }
   }
   elseif ($phase == 'runtime') {
+    // Complete data dump of all installed modules, used later.
+    $module_data = system_rebuild_module_data();
+
     // Work out the release of D7 that is currently running.
     list($major, $minor) = explode('.', VERSION);
     // Strip off any suffixes on the version string, e.g. "17-dev".
@@ -71,15 +74,6 @@ function metatag_requirements($phase) {
         'description' => $t("This older version of Drupal core is missing functionality necessary for the module's multilingual support and contains a broken [node:summary] token, it must be upgraded to version 7.28 or newer."),
       );
     }
-    // Everything's OK.
-    else {
-      $requirements['metatag'] = array(
-        'severity' => REQUIREMENT_OK,
-        'title' => 'Metatag',
-        'value' => $t('Drupal core is compatible'),
-        'description' => $t('Older versions of Drupal core contained bugs that made them incompatible with Metatag, but this version will work correctly.'),
-      );
-    }
 
     // Add a note if Page Title is also installed.
     if (module_exists('page_title')) {
@@ -104,16 +98,16 @@ function metatag_requirements($phase) {
     }
 
     // Check that Entity_Translation is current.
-    if (module_exists('entity_translation')) {
-      $rev = db_query("SELECT schema_version FROM {system} WHERE name = :module", array(':module' => 'entity_translation'))->fetchColumn();
-      if ($rev < 7004) {
-        $requirements['metatag_et_old'] = array(
-          'severity' => REQUIREMENT_ERROR,
-          'title' => 'Metatag',
-          'value' => $t('<a href="@url">Entity_Translation</a> is out of date and requires updating', array('@url' => 'http://drupal.org/project/entity_translation')),
-          'description' => $t('The Entity_Translation module is out of date and needs to be updated in order to be compatible with Metatag.'),
-        );
-      }
+    if (module_exists('entity_translation')
+      && !empty($module_data['entity_translation'])
+      && !empty($module_data['entity_translation']->schema_version)
+      && $module_data['entity_translation']->schema_version < 7004) {
+      $requirements['metatag_et_version'] = array(
+        'severity' => REQUIREMENT_ERROR,
+        'title' => 'Metatag',
+        'value' => $t('<a href="@url">Entity_Translation</a> is out of date and requires updating', array('@url' => 'https://www.drupal.org/project/entity_translation')),
+        'description' => $t('The Entity_Translation module is out of date and needs to be updated in order to be compatible with Metatag.'),
+      );
     }
 
     // It's recommended to install the Transliteration module to clean up file
@@ -134,12 +128,12 @@ function metatag_requirements($phase) {
         'severity' => REQUIREMENT_INFO,
         'title' => 'Metatag',
         'value' => $t('The Imagecache Token module is recommended.'),
-        'description' => $t("It is recommended to install the <a href=\"@url\">Imagecache Token module</a> to make it easier to control image meta tags, e.g. og:image.", array('@url' => 'https://drupal.org/project/imagecache_token')),
+        'description' => $t("It is recommended to install the <a href=\"@url\">Imagecache Token module</a> to make it easier to control image meta tags, e.g. og:image. See the Metatag module's README.txt for details.", array('@url' => 'https://drupal.org/project/imagecache_token')),
       );
     }
 
     // The Admin Language module can cause problems.
-    if (!module_exists('admin_language') && variable_get('admin_language_force_neutral', 0)) {
+    if (module_exists('admin_language') && variable_get('admin_language_force_neutral', 0)) {
       $requirements['metatag_admin_language'] = array(
         'severity' => REQUIREMENT_WARNING,
         'title' => 'Metatag',
@@ -147,6 +141,46 @@ function metatag_requirements($phase) {
         'description' => $t("Using the \"@option\" with Metatag can lead to data loss, so it is recommended to <a href=\"@url\">disable that option</a>.", array('@option' => t('Force language neutral aliases'), '@url' => url('admin/config/regional/language/admin_language'))),
       );
     }
+
+    // Token v7.x-1.6 is *highly* recommended.
+    $token_module = $module_data['token'];
+    // If the version string is not present then it means the module is running
+    // from git, which means it can't be compared against. Alternatively, look
+    // for the test file, which was the last commit of the 1.6 release.
+    if (!empty($token_module->info['version'])
+      || empty($token_module->info['files'])) {
+      // If there's no test file then this is older than v1.6.
+      if (empty($token_module->info['files'])) {
+        $minor = 5;
+      }
+      // Otherwise check the version string.
+      else {
+        // Versions are in the format 7.x-1.y, so split the string up to find
+        // the 'y' portion.
+        $version = explode('-', $token_module->info['version']);
+        if (isset($version[1])) {
+          list($major, $minor) = explode('.', $version[1]);
+          // Strip off any suffixes on the version string, e.g. "17-dev".
+          if (strpos('-', $minor)) {
+            list($minor, $suffix) = explode('-', $minor);
+          }
+        }
+        // If the version string couldn't be extracted correctly, assume that
+        // an incorrect version is installed.
+        else {
+          $minor = 0;
+        }
+      }
+      // If v1.6 is not installed, give a warning.
+      if ($minor < 6) {
+        $requirements['metatag_token_version'] = array(
+          'severity' => REQUIREMENT_WARNING,
+          'title' => 'Metatag',
+          'value' => $t('Token module is out of date.'),
+          'description' => $t('It is highly recommended to install <a href="https://www.drupal.org/project/token">Token module</a> v7.x-1.6 or newer, otherwise there may be problems using certain meta tags.'),
+        );
+      }
+    }
   }
 
   return $requirements;
@@ -241,13 +275,21 @@ function metatag_schema() {
       ),
     ),
     'indexes' => array(
-      'type_revision' => array('entity_type','revision_id'),
+      'type_revision' => array(
+        'entity_type',
+        'revision_id',
+      ),
+    ),
+    'primary key' => array(
+      'entity_type',
+      'entity_id',
+      'revision_id',
+      'language',
     ),
-    'primary key' => array('entity_type', 'entity_id', 'revision_id', 'language'),
   );
 
   $schema['cache_metatag'] = drupal_get_schema_unprocessed('system', 'cache');
-  $schema['cache_metatag']['description'] = t('Cache table for the generated meta tag output.');
+  $schema['cache_metatag']['description'] = 'Cache table for the generated meta tag output.';
 
   return $schema;
 }
@@ -257,6 +299,86 @@ function metatag_schema() {
  */
 function metatag_install() {
   drupal_set_message(t("Thank you for installing the Metatag module. It is recommended to read the module's <a href=\"!url\" title=\"Read the Metatag module's documentation\">README.txt</a> file as there are some known issues that may affect this site.", array('!url' => url(drupal_get_path('module', 'metatag') . '/README.txt'))));
+
+  // Always enable the node, taxonomy term and user entities.
+  foreach (array('node', 'taxonomy_term', 'user') as $entity_type) {
+    // Enable the main entity type.
+    $variable_name = 'metatag_enable_' . $entity_type;
+    variable_set($variable_name, TRUE);
+
+    // Update each entity bundle too.
+    $entity_info = entity_get_info($entity_type);
+    if (!empty($entity_info['bundles'])) {
+      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+        $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;
+        variable_set($variable_name, TRUE);
+      }
+    }
+  }
+
+  // Possibly enable other entities, if they're specifically requested.
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    // Skip the three entity types that were already enabled.
+    if (in_array($entity_type, array('node', 'taxonomy_term', 'user'))) {
+      continue;
+    }
+
+    $variable_name = 'metatag_enable_' . $entity_type;
+
+    // Configuration entities are skipped.
+    if (isset($entity_info['configuration']) && $entity_info['configuration'] == TRUE) {
+      continue;
+    }
+
+    // Entities must have bundles.
+    if (empty($entity_info['bundles'])) {
+      continue;
+    }
+
+    // Entities must be fieldable.
+    elseif (empty($entity_info['fieldable'])) {
+      continue;
+    }
+
+    // Ignore some view modes that are automatically added by certain modules.
+    unset($entity_info['view modes']['ical']);
+    unset($entity_info['view modes']['diff_standard']);
+    unset($entity_info['view modes']['token']);
+
+    // Entities without view modes are skipped.
+    if (empty($entity_info['view modes'])) {
+      continue;
+    }
+
+    // At this point, disable the entity by default.
+    $entity_enabled = FALSE;
+
+    // Anything that was specifically enabled via hook_entity_info() from older
+    // versions will be enabled if not configured already.
+    if (!empty($entity_info['metatag']) || !empty($entity_info['metatags'])) {
+      $entity_enabled = variable_get($variable_name, 'monkey');
+      if ($entity_enabled === 'monkey') {
+        $entity_enabled = TRUE;
+      }
+    }
+
+    variable_set($variable_name, $entity_enabled);
+
+    // Loop through the bundles, but only if the entity is enabled.
+    if ($entity_enabled) {
+      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+        $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;
+
+        // If it wasn't specifically disabled before, enable it.
+        $bundle_enabled = variable_get($variable_name, 'monkey');
+        if ($bundle_name != FALSE) {
+          variable_set($variable_name, TRUE);
+        }
+      }
+    }
+  }
+
+  drupal_set_message(t('It may be worth verifying on the <a href="@url">Settings page</a> which types of content on the site should allow meta tags.', array('@url' => url('admin/config/search/metatags/settings'))));
 }
 
 /**
@@ -280,6 +402,49 @@ function metatag_uninstall() {
   // Used to force an entity's default language values to be used if nothing
   // else matched.
   variable_del('metatag_entity_no_lang_default');
+
+  // Controls which page region is used to trigger output of the meta tags.
+  variable_del('metatag_page_region');
+
+  // Optionally disable the default configurations.
+  variable_del('metatag_load_defaults');
+
+  // Optionally disables the output cache.
+  variable_del('metatag_cache_output');
+
+  // Customizable pager string.
+  variable_del('metatag_pager_string');
+
+  // Optionally enable translations of final output.
+  variable_del('metatag_i18n_translate_output');
+
+  // Optionally enable the automatic watchdog logging of i18n strings.
+  variable_del('metatag_i18n_enable_watchdog');
+
+  // Optionally disable the i18n integration.
+  variable_del('metatag_i18n_disabled');
+
+  // Optionally output core's meta tags.
+  variable_del('metatag_leave_core_tags');
+
+  // Optionally enable individual permissions for each meta tag.
+  variable_del('metatag_extended_permissions');
+
+  // Optionally load meta tags on admin pages.
+  variable_del('metatag_load_all_pages');
+
+  // Sanitize token replacement output.
+  variable_del('metatag_token_sanitize');
+
+  // Remove all possible 'enable' variables.
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    variable_del('metatag_enable_' . $entity_type);
+    if (!empty($entity_info['bundles'])) {
+      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+        variable_del('metatag_enable_' . $entity_type . '__' . $bundle_name);
+      }
+    }
+  }
 }
 
 /**
@@ -290,13 +455,119 @@ function metatag_enable() {
 }
 
 /**
- * Implements hook_disable().
+ * Replace one meta tag with another in the entity records.
+ *
+ * @param array $sandbox
+ *   A Batch API sandbox, passed by reference.
+ * @param string $old_tag
+ *   The meta tag that is to be replaced.
+ * @param string $new_tag
+ *   The meta tag that replaces the old one.
  */
-// function metatag_disable() {
-// }
+function metatag_update_replace_meta_tag(&$sandbox, $old_tag, $new_tag) {
+  if (!isset($sandbox['progress'])) {
+    // Count of all {metatag} records that contained an entry for the old meta
+    // tag.
+    $records_count = db_select('metatag', 'm')
+      ->condition('m.data', '%' . db_like('"" . $old_tag . ""') . '%', 'LIKE')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+
+    if (empty($records_count)) {
+      return t('No Metatag entity records needed to have the "' . $old_tag . '" meta tag renamed.');
+    }
+
+    $sandbox['max'] = $records_count;
+    $sandbox['progress'] = 0;
+  }
+
+  // Count of rows that will be processed per iteration.
+  $limit = 100;
+
+  // Fetches a part of records.
+  $records = db_select('metatag', 'm')
+    ->fields('m', array())
+    ->condition('m.data', '%' . db_like('"' . $old_tag . '"') . '%', 'LIKE')
+    ->range(0, $limit)
+    ->execute();
+
+  $count = 0;
+  $keys = array('entity_type', 'entity_id', 'revision_id', 'language');
+
+  // Loop over the values and correct them.
+  foreach ($records as $record) {
+    $record->data = unserialize($record->data);
+
+    if (isset($record->data[$old_tag])) {
+      $record->data[$new_tag] = $record->data[$old_tag];
+      unset($record->data[$old_tag]);
+      drupal_write_record('metatag', $record, $keys);
+
+      // Clear the cache for the entity this belongs to.
+      entity_get_controller($record->entity_type)->resetCache(array($record->entity_id));
+    }
+
+    $count++;
+  }
+
+  if (!empty($count)) {
+    $sandbox['progress'] += $count;
+    $sandbox['#finished'] = min(0.99, $sandbox['progress'] / $sandbox['max']);
+  }
+  else {
+    $sandbox['#finished'] = 1;
+    return t('Converted the "' . $old_tag . '" meta tag for @count entity records to "' . $new_tag . '" meta tag.', array('@count' => $sandbox['progress']));
+  }
+}
 
 /**
- * Disable the deprecated metatag_ui module which has been merged into metatag.
+ * Replace one meta tag with another in the configs.
+ *
+ * @param string $old_tag
+ *   The meta tag that is to be replaced.
+ * @param string $new_tag
+ *   The meta tag that replaces the old one.
+ */
+function metatag_update_replace_config($old_tag, $new_tag) {
+  // Find all {metatag_config} records that contained an entry for the old meta
+  // tag.
+  $records = db_select('metatag_config', 'm')
+    ->fields('m', array('cid', 'config'))
+    ->condition('m.config', '%' . db_like('"' . $old_tag . '"') . '%', 'LIKE')
+    ->execute();
+  // This message will be returned if nothing needed to be updated.
+  $none_message = t('No Metatag configuration records needed to have the "og:video" meta tag fixed. That said, there may be other configurations elsewhere that do need updating.');
+
+  // Loop over the values and correct them.
+  if ($records->rowCount() == 0) {
+    drupal_set_message($none_message);
+  }
+  else {
+    $keys = array('cid');
+
+    // Loop over the values and correct them.
+    $counter = 0;
+    foreach ($records as $record) {
+      $record->config = unserialize($record->config);
+      if (isset($record->config[$old_tag])) {
+        $record->config[$new_tag] = $record->config[$old_tag];
+        unset($record->config['og:video']);
+        drupal_write_record('metatag_config', $record, $keys);
+        $counter++;
+      }
+    }
+    if ($counter == 0) {
+      drupal_set_message($none_message);
+    }
+    else {
+      drupal_set_message(t('Converted the "' . $old_tag . '" meta tag for @count configurations to the new "' . $new_tag . '" meta tag.', array('@count' => $counter)));
+    }
+  }
+}
+
+/**
+ * Disable the deprecated metatag_ui module, which has been merged into metatag.
  */
 function metatag_update_7000() {
   if (module_exists('metatag_ui')) {
@@ -658,7 +929,7 @@ function metatag_update_7011(&$sandbox) {
 
     // hook_update_N() may optionally return a string which will be displayed
     // to the user.
-    return t('Fixed the Metatag language values for @count nodes.', array('!count' => $sandbox['progress']));
+    return t('Fixed the Metatag language values for @count nodes.', array('@count' => $sandbox['progress']));
   }
 }
 
@@ -1166,6 +1437,13 @@ function metatag_update_7018(&$sandbox) {
     }
     $records = entity_load($entity_type, array($entity_id), $conditions);
 
+    // Try to fallback to default language if no record was found. This may
+    // happen when using entity_translation as the parent entity table only
+    // contains one record.
+    if (!empty($conditions) && empty($records)) {
+      $records = entity_load($entity_type, array($entity_id));
+    }
+
     // Fix this record.
     if (!empty($records)) {
       $entity = reset($records);
@@ -1348,40 +1626,10 @@ function metatag_update_7023() {
 }
 
 /**
- * Rename the 'twitter:image' meta tag to 'twitter:image:src', part 1.
+ * No-op update. Renaming twitter:image to twitter:image:src no longer needed.
  */
 function metatag_update_7024() {
-  // Find all {metatag} records that contained an entry for the old meta tag.
-  $records = db_query("SELECT entity_type, entity_id, revision_id, language, data
-    FROM {metatag}
-    WHERE data LIKE '%twitter:image%'");
-  // This message will be returned if nothing needed to be updated.
-  $none_message = t('No Metatag entity records needed to have the "twitter:image" meta tag fixed.');
-
-  if ($records->rowCount() == 0) {
-    drupal_set_message($none_message);
-  }
-  else {
-    $keys = array('entity_type', 'entity_id', 'revision_id', 'language');
-
-    // Loop over the values and correct them.
-    $counter = 0;
-    foreach ($records as $record) {
-      $record->data = unserialize($record->data);
-      if (isset($record->data['twitter:image'])) {
-        $record->data['twitter:image:src'] = $record->data['twitter:image'];
-        unset($record->data['twitter:image']);
-        drupal_write_record('metatag', $record, $keys);
-        $counter++;
-      }
-    }
-    if ($counter == 0) {
-      drupal_set_message($none_message);
-    }
-    else {
-      drupal_set_message(t('Converted the "twitter:image" meta tag for @count entity records to the correct "twitter:image:src" meta tag.', array('@count' => $counter)));
-    }
-  }
+  // Do nothing.
 }
 
 /**
@@ -1394,38 +1642,10 @@ function metatag_update_7025() {
 /**
  * Rename the 'copyright' meta tag to 'rights', part 1.
  */
-function metatag_update_7026() {
-  // Find all {metatag} records that contained an entry for the old meta tag.
-  $records = db_query("SELECT entity_type, entity_id, revision_id, language, data
-    FROM {metatag}
-    WHERE data LIKE '%copyright%'");
-  // This message will be returned if nothing needed to be updated.
-  $none_message = t('No Metatag entity records needed to have the "copyright" meta tag fixed.');
-
-  if ($records->rowCount() == 0) {
-    drupal_set_message($none_message);
-  }
-  else {
-    $keys = array('entity_type', 'entity_id', 'revision_id', 'language');
-
-    // Loop over the values and correct them.
-    $counter = 0;
-    foreach ($records as $record) {
-      $record->data = unserialize($record->data);
-      if (isset($record->data['copyright'])) {
-        $record->data['rights'] = $record->data['copyright'];
-        unset($record->data['copyright']);
-        drupal_write_record('metatag', $record, $keys);
-        $counter++;
-      }
-    }
-    if ($counter == 0) {
-      drupal_set_message($none_message);
-    }
-    else {
-      drupal_set_message(t('Converted the "copyright" meta tag for @count entity records to the correct "rights" meta tag.', array('@count' => $counter)));
-    }
-  }
+function metatag_update_7026(&$sandbox) {
+  $old_tag = 'copyright';
+  $new_tag = 'rights';
+  return metatag_update_replace_meta_tag($sandbox, $old_tag, $new_tag);
 }
 
 /**
@@ -1436,7 +1656,7 @@ function metatag_update_7027() {
 }
 
 /**
- * Clear the menu cache so the new Advanced Settings page will be picked up.
+ * Clear the menu cache so the new Settings page will be picked up.
  */
 function metatag_update_7028() {
   variable_set('menu_rebuild_needed', TRUE);
@@ -1446,106 +1666,561 @@ function metatag_update_7028() {
  * Add an index to the {metatag} table to speed up some queries.
  */
 function metatag_update_7029() {
-  db_add_index('metatag', 'type_revision', array('entity_type', 'revision_id'));
-  drupal_set_message(t('Added an index to the main Metatag table that will hopefully improve performance a little.'));
+  if (!db_index_exists('metatag', 'type_revision')) {
+    db_add_index('metatag', 'type_revision', array('entity_type', 'revision_id'));
+    return t('Added an index to the main Metatag table that will hopefully improve performance a little.');
+  }
+  else {
+    return t('Did not add the index, it already existed.');
+  }
 }
 
 /**
- * Rename the 'twitter:image' meta tag to 'twitter:image:src', part 2.
+ * No-op update. Renaming twitter:image to twitter:image:src no longer needed.
  */
 function metatag_update_7030() {
-  // Find all {metatag_config} records that contained an entry for the old meta
-  // tag.
-  $records = db_query("SELECT cid, config
-    FROM {metatag_config}
-    WHERE config LIKE '%twitter:image%'");
-  // This message will be returned if nothing needed to be updated.
-  $none_message = t('No Metatag configuration records needed to have the "twitter:image" meta tag fixed. That said, there may be other configurations elsewhere that do need updating.');
+  // Do nothing.
+}
 
-  // Loop over the values and correct them.
-  if ($records->rowCount() == 0) {
-    drupal_set_message($none_message);
-  }
-  else {
-    $keys = array('cid');
+/**
+ * Rename the 'copyright' meta tag to 'rights', part 2.
+ */
+function metatag_update_7031() {
+  $old_tag = 'copyright';
+  $new_tag = 'rights';
+  return metatag_update_replace_config($old_tag, $new_tag);
+}
 
-    // Loop over the values and correct them.
-    $counter = 0;
-    foreach ($records as $record) {
-      $record->config = unserialize($record->config);
-      if (isset($record->config['twitter:image'])) {
-        $record->config['twitter:image:src'] = $record->config['twitter:image'];
-        unset($record->config['twitter:image']);
-        drupal_write_record('metatag_config', $record, $keys);
-        $counter++;
-      }
+/**
+ * Clear the Metatag cache.
+ */
+function metatag_update_7032() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  return t('All Metatag caches cleared.');
+}
+
+/**
+ * These originally removed the 'author' meta tag, but it was subsequently
+ * decided that this was not the correct approach, that the meta tag should not
+ * be removed after all.
+ *
+ * @see https://www.drupal.org/node/2330823
+ */
+function metatag_update_7033() {
+}
+function metatag_update_7034() {
+}
+function metatag_update_7035() {
+}
+
+/**
+ * Update variables to indicate which entities should be supported.
+ */
+function metatag_update_7036() {
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    $variable_name = 'metatag_enable_' . $entity_type;
+
+    // Configuration entities are skipped.
+    if (isset($entity_info['configuration']) && $entity_info['configuration'] == TRUE) {
+      continue;
     }
-    if ($counter == 0) {
-      drupal_set_message($none_message);
+
+    // Entities without view modes are skipped.
+    elseif (empty($entity_info['view modes'])) {
+      continue;
     }
+
+    // Basic core entities or "normal" entities that have been enabled via the
+    // API.
+    elseif (in_array($entity_type, array('node', 'taxonomy_term', 'user')) ||
+      !empty($entity_info['metatag']) || !empty($entity_info['metatags'])) {
+      // Check if the entity type has been enabled or disabled previously; if
+      // the variable equals a junk value then it was not previously set,
+      // therefore we'll set a default.
+      if (variable_get($variable_name, 'monkey') == 'monkey') {
+        // By default these entity types are enabled.
+        variable_set($variable_name, TRUE);
+        // Check each entity bundle.
+        if (!empty($entity_info['bundles'])) {
+          foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+            $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;
+            // Check if the bundle has been enabled or disabled previously; if
+            // the variable equals a junk value then it was not previously set,
+            // therefore we'll set a default.
+            if (variable_get($variable_name, 'monkey') == 'monkey') {
+              if (!empty($bundle_info['metatag']) || !empty($bundle_info['metatags'])) {
+                variable_set($variable_name, TRUE);
+              }
+              else {
+                variable_set($variable_name, FALSE);
+              }
+            }
+            // This variable was set before.
+            else {
+              // Do nothing.
+            }
+          }
+        }
+      }
+      // This variable was set before.
+      else {
+        // Do nothing.
+      }
+    }
+
+    // Disable this entity type.
     else {
-      drupal_set_message(t('Converted the "twitter:image" meta tag for @count configurations to the correct "twitter:image:src" meta tag.', array('@count' => $counter)));
+      variable_set($variable_name, FALSE);
     }
   }
+
+  // Clear the caches.
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  drupal_static_reset('metatag_config_load_with_defaults');
+  drupal_static_reset('metatag_entity_supports_metatags');
+  ctools_include('export');
+  ctools_export_load_object_reset('metatag_config');
+
+  drupal_set_message(t('The way that Metatag tracks which entity types are compatible has changed. Please review the <a href="@url">Settings page</a> to ensure that all of the entity types are enabled correctly.', array('@url' => 'admin/config/search/metatags/settings')));
 }
 
 /**
- * Rename the 'copyright' meta tag to 'rights', part 2.
+ * Clear the menu cache so the renamed Settings page will be picked up.
  */
-function metatag_update_7031() {
-  // Find all {metatag_config} records that contained an entry for the old meta
-  // tag.
-  $records = db_query("SELECT cid, config
-    FROM {metatag_config}
-    WHERE config LIKE '%copyright%'");
-  // This message will be returned if nothing needed to be updated.
-  $none_message = t('No Metatag configuration records needed to have the "copyright" meta tag fixed. That said, there may be other configurations elsewhere that do need updating.');
+function metatag_update_7037() {
+  variable_set('menu_rebuild_needed', TRUE);
+}
 
-  // Loop over the values and correct them.
-  if ($records->rowCount() == 0) {
-    drupal_set_message($none_message);
+/**
+ * Manually enable all content types, vocabularies and the user entity to help
+ * resolve issues from 1.5's architecture change.
+ */
+function metatag_update_7038() {
+  foreach (array('node', 'taxonomy_term', 'user') as $entity_type) {
+    // Enable the main entity type.
+    $variable_name = 'metatag_enable_' . $entity_type;
+    variable_set($variable_name, TRUE);
+
+    // Update each entity bundle too.
+    $entity_info = entity_get_info($entity_type);
+    if (!empty($entity_info['bundles'])) {
+      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+        $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;
+        variable_set($variable_name, TRUE);
+      }
+    }
   }
-  else {
-    $keys = array('cid');
+}
 
-    // Loop over the values and correct them.
-    $counter = 0;
-    foreach ($records as $record) {
-      $record->config = unserialize($record->config);
-      if (isset($record->config['copyright'])) {
-        $record->config['rights'] = $record->config['copyright'];
-        unset($record->config['copyright']);
-        drupal_write_record('metatag_config', $record, $keys);
-        $counter++;
+/**
+ * Reload some meta tag caches.
+ */
+function metatag_update_7039() {
+  foreach (language_list() as $langcode => $language) {
+    cache_clear_all('info:' . $langcode, 'cache_metatag');
+  }
+}
+
+/**
+ * Fix robots meta tags that might have been broken when they were imported
+ * from Nodewords.
+ */
+function metatag_update_7040(&$sandbox) {
+  // Process records by groups of 10 (arbitrary value).
+  // When a group is processed, the batch update engine determines whether it
+  // should continue processing in the same request or provide progress
+  // feedback to the user and wait for the next request.
+  $limit = 10;
+  // When ran through Drush it's Ok to process a larger number of objects at a
+  // time.
+  if (drupal_is_cli()) {
+    $limit = 100;
+  }
+
+  // Use the sandbox at your convenience to store the information needed
+  // to track progression between successive calls to the function.
+  if (!isset($sandbox['progress'])) {
+    // The count of records visited so far.
+    $sandbox['progress'] = 0;
+
+    // Get a count of {metatag} records that have a robots meta tag definition
+    // that is not in the correct format.
+    $records = db_select('metatag', 'm')
+      ->fields('m')
+      ->condition('m.data', '%:6:"robots";%', 'LIKE')
+      ->condition('m.data', '%:6:"robots";a:1:{s:5:"value";%', 'NOT LIKE')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+
+    // If there's no data, don't bother with the extra work.
+    if (empty($records)) {
+      watchdog('metatag', 'Update 7040: No robots meta tags need to be fixed.', array(), WATCHDOG_INFO);
+      if (drupal_is_cli()) {
+        drupal_set_message(t('Update 7040: No robots meta tags need to be fixed.'));
       }
+      return t('No robots meta tags need to be fixed.');
     }
-    if ($counter == 0) {
-      drupal_set_message($none_message);
+
+    // Total records that must be visited.
+    $sandbox['max'] = $records;
+
+    // A place to store messages during the run.
+    $sandbox['messages'] = array();
+
+    // An initial record of the number of records to be updated.
+    watchdog('metatag', 'Update 7040: !count records to examine.', array('!count' => $sandbox['max']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7040: !count records to examine.', array('!count' => $sandbox['max'])));
     }
-    else {
-      drupal_set_message(t('Converted the "copyright" meta tag for @count configurations to the correct "rights" meta tag.', array('@count' => $counter)));
+
+    // Log how many records are fixed.
+    $sandbox['fixed'] = 0;
+
+    // Because a lot of other processing happens on the first iteration, just do
+    // ten.
+    $limit = 10;
+  }
+
+  // Get a list of records that need to be fixed.
+  $records = db_select('metatag', 'm')
+    ->fields('m')
+    ->condition('m.data', '%:6:"robots";%', 'LIKE')
+    ->condition('m.data', '%:6:"robots";a:1:{s:5:"value";%', 'NOT LIKE')
+    ->range(0, $limit)
+    ->execute();
+
+  // Set default values.
+  foreach ($records as $record) {
+    // Extract the record.
+    $record->data = unserialize($record->data);
+
+    // See if the record needs to be fixed.
+    if (!empty($record->data['robots']) && empty($record->data['robots']['value'])) {
+      // Fix the record.
+      $robots = $record->data['robots'];
+      $record->data['robots'] = array(
+        'value' => $robots,
+      );
+
+      // Update the database.
+      db_update('metatag')
+        ->fields(array('data' => serialize($record->data)))
+        ->condition('entity_type', $record->entity_type)
+        ->condition('entity_id', $record->entity_id)
+        ->condition('revision_id', $record->revision_id)
+        ->condition('language', $record->language)
+        ->execute();
+
+      // Clear the cache for this entity.
+      entity_get_controller($record->entity_type)->resetCache(array($record->entity_id));
+
+      // Update our progress information.
+      $sandbox['fixed']++;
+    }
+
+    // Update our progress information.
+    $sandbox['progress']++;
+  }
+
+  // Set the "finished" status, to tell batch engine whether this function
+  // needs to run again. If you set a float, this will indicate the progress of
+  // the batch so the progress bar will update.
+  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  // Only display a status message if process finished.
+  if ($sandbox['#finished'] === TRUE) {
+    // Clear all caches so the fixed data will be reloaded.
+    cache_clear_all('*', 'cache_metatag', TRUE);
+
+    // A final log of the number of records that were converted.
+    watchdog('metatag', 'Update 7040: !count records were fixed.', array('!count' => $sandbox['fixed']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7040: !count records were fixed.', array('!count' => $sandbox['fixed'])));
     }
+
+    // hook_update_N() may optionally return a string which will be displayed
+    // to the user.
+    return t('Fixed the Metatag robots values for @count nodes.', array('@count' => $sandbox['fixed']));
   }
 }
 
 /**
- * Clear the Metatag cache.
+ * Rerun update 7018 that was fixed to run correctly when using Entity
+ * Translation. This may take some time.
  */
-function metatag_update_7032() {
+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);
-  return t('All Metatag caches cleared.');
 }
 
 /**
- * These originally removed the 'author' meta tag, but it was subsequently
- * decided that this was not the correct approach, that the meta tag should not
- * be removed after all.
- *
- * @see https://www.drupal.org/node/2330823
+ * Update i18n strings for all meta tags to use the new format.
  */
-function metatag_update_7033() {
+function metatag_update_7101() {
+  if (!module_exists('locale') || !db_table_exists('locales_source')) {
+    return t('No translations to fix as the locale system is not enabled.');
+  }
+
+  // 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_type => $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_type}:{$bundle_type}:', 'metatag:metatag_config:{$entity_type}:{$bundle_type}:'),
+          context = REPLACE(context, '{$entity_type}:{$bundle_type}:', 'metatag_config:{$entity_type}:{$bundle_type}:')
+        WHERE textgroup = 'metatag'
+          AND location LIKE 'metatag:{$entity_type}:{$bundle_type}:%'");
+
+      // Update {locales_source}, replace 'metatag:ENTITYTYPE:tag' with
+      // 'metatag:metatag_config:ENTITYTYPE:tag'.
+      db_query("UPDATE {locales_source}
+        SET location = REPLACE(location, 'metatag:{$entity_type}:', 'metatag:metatag_config:{$entity_type}:'),
+          context = REPLACE(context, '{$entity_type}:', 'metatag_config:{$entity_type}:')
+        WHERE textgroup = 'metatag'
+          AND location LIKE 'metatag:{$entity_type}:%'");
+    }
+  }
+  // Update {locales_source}, replace 'metatag:metatag:' with
+  // 'metatag:output:'.
+  db_query("UPDATE {locales_source}
+    SET location = REPLACE(location, 'metatag:metatag:', 'metatag:output:'),
+      context = REPLACE(context, 'metatag:', 'output:')
+    WHERE textgroup = 'metatag'
+      AND location LIKE 'metatag:metatag:%'");
 }
-function metatag_update_7034() {
+
+/**
+ * Re-run update 7101.
+ */
+function metatag_update_7102() {
+  metatag_update_7101();
 }
-function metatag_update_7035() {
+
+/**
+ * Clear all metatag caches so the new entity caching structure takes over.
+ */
+function metatag_update_7103() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+}
+
+/**
+ * Remove the entity revision ID from the translation strings.
+ */
+function metatag_update_7104(&$sandbox) {
+  // Verify that locales are being used in the first place.
+  if (!module_exists('locale') || !db_table_exists('locales_source')) {
+    return t('Metatag: No translations to fix as the locale system is not enabled.');
+  }
+
+  // No need to do anything if output translation is disabled.
+  if (!variable_get('metatag_i18n_translate_output', FALSE)) {
+    return t('Metatag: Output translation is disabled, so no need to update anything.');
+  }
+
+  // Process records by groups of 10 (arbitrary value).
+  // When a group is processed, the batch update engine determines whether it
+  // should continue processing in the same request or provide progress
+  // feedback to the user and wait for the next request.
+  $limit = 10;
+  // When ran through Drush it's Ok to process a larger number of objects at a
+  // time.
+  if (drupal_is_cli()) {
+    $limit = 100;
+  }
+
+  // Use the sandbox at your convenience to store the information needed
+  // to track progression between successive calls to the function.
+  if (!isset($sandbox['progress'])) {
+    // The count of records visited so far.
+    $sandbox['progress'] = 0;
+
+    // Total records that must be visited.
+    $sandbox['max'] = db_query("SELECT COUNT(lid)
+      FROM {locales_source}
+      WHERE textgroup = 'metatag'
+      AND context LIKE 'output:%:%:%:%'")->fetchField();
+
+    // If there's no data, don't bother with the extra work.
+    if (empty($sandbox['max'])) {
+      watchdog('metatag', 'Update 7104: No nodes need the translation entity string fixed.', array(), WATCHDOG_INFO);
+      if (drupal_is_cli()) {
+        drupal_set_message(t('Update 7104: No nodes need the translation entity string fixed.'));
+      }
+      return t('No nodes need the Metatag language values fixed.');
+    }
+
+    // A place to store messages during the run.
+    $sandbox['messages'] = array();
+
+    // An initial record of the number of records to be updated.
+    watchdog('metatag', 'Update 7104: !count records to update.', array('!count' => $sandbox['max']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7104: !count records to update.', array('!count' => $sandbox['max'])));
+    }
+  }
+
+  // Get a batch of records that need to be fixed.
+  $records = db_query_range("SELECT lid, location, context
+    FROM {locales_source}
+    WHERE textgroup = 'metatag'
+    AND context LIKE 'output:%:%:%:%'", 0, $limit);
+
+  // Update each of the records.
+  foreach ($records as $record) {
+    $old_location = '/metatag:output:([^:]*):([^:]*):([^:]*):([^:]*)/';
+    $new_location = 'metatag:output:$1:$2:$4';
+    $location = preg_replace($old_location, $new_location, $record->location);
+    $old_context = '/output:([^:]*):([^:]*):([^:]*):([^:]*)/';
+    $new_context = 'output:$1:$2:$4';
+    $context = preg_replace($old_context, $new_context, $record->context);
+    drupal_set_message($location);
+    drupal_set_message($context);
+    db_update('locales_source')
+      ->fields(array(
+        'location' => $location,
+        'context' => $context,
+      ))
+      ->condition('lid', $record->lid)
+      ->execute();
+
+    // Update our progress information.
+    $sandbox['progress']++;
+  }
+
+  // Set the "finished" status, to tell batch engine whether this function
+  // needs to run again. If you set a float, this will indicate the progress of
+  // the batch so the progress bar will update.
+  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($sandbox['#finished']) {
+    // Clear all caches so the fixed data will be reloaded.
+    cache_clear_all('*', 'cache_metatag', TRUE);
+
+    // hook_update_N() may optionally return a string which will be displayed
+    // to the user.
+    return t('Fixed the Metatag language values for @count nodes.', array('@count' => $sandbox['progress']));
+  }
+}
+
+/**
+ * Fix the output translation strings.
+ */
+function metatag_update_7105() {
+  if (!module_exists('locale') || !db_table_exists('locales_source')) {
+    return t('No translations to fix as the locale system is not enabled.');
+  }
+
+  db_query("UPDATE {locales_source}
+    SET location = REPLACE(location, 'metatag:metatag:', 'metatag:output:'),
+      context = REPLACE(context, 'metatag:', 'output:')
+    WHERE textgroup = 'metatag'
+    AND location LIKE 'metatag:metatag:%'");
+
+  return t('Fixed the Metatag output strings.');
+}
+
+
+/**
+ * The output translation strings were renamed to something shorter, so rerun
+ * update 7105.
+ */
+function metatag_update_7106() {
+  return metatag_update_7105();
+}
+
+/**
+ * Fix the global config translation strings.
+ */
+function metatag_update_7107() {
+  if (!module_exists('locale') || !db_table_exists('locales_source')) {
+    return t('No translations to fix as the locale system is not enabled.');
+  }
+
+  db_query("UPDATE {locales_source}
+    SET location = REPLACE(location, 'metatag:global:', 'metatag:metatag_config:global:'),
+      context = REPLACE(context, 'global:', 'metatag_config:global:')
+    WHERE textgroup = 'metatag'
+    AND location LIKE 'metatag:global:%'");
+
+  return t('Fixed the Metatag global config string translations.');
+}
+
+/**
+ * Delete output translations if it's disabled.
+ */
+function metatag_update_7108() {
+  if (!module_exists('locale') || !db_table_exists('locales_source')) {
+    return t('No translations to fix as the locale system is not enabled.');
+  }
+
+  // If the output-translation option is enabled then don't delete anything.
+  if (variable_get('metatag_i18n_translate_output', FALSE)) {
+    return t("Metatag: Not deleting output translations because that option is enabled.");
+  }
+
+  $lids = db_select('locales_source', 'ls')
+    ->fields('ls', array('lid'))
+    ->condition('ls.textgroup', 'metatag')
+    ->condition('ls.context', 'output:%', 'LIKE')
+    ->execute()
+    ->fetchCol();
+
+  if (!empty($lids)) {
+    // Delete records in the tables in reverse order, so that if the query fails
+    // and has to be reran it'll still find records. But it should be ok.
+    if (db_table_exists('i18n_string')) {
+      db_delete('i18n_string')
+        ->condition('lid', $lids)
+        ->execute();
+    }
+    db_delete('locales_target')
+      ->condition('lid', $lids)
+      ->execute();
+    db_delete('locales_source')
+      ->condition('lid', $lids)
+      ->execute();
+    return t('Metatag: Removed @count output translation records that were not needed.', array('@count' => count($lids)));
+  }
+  else {
+    return t('Metatag; No output translation records needed to be removed.');
+  }
+}
+
+/**
+ * Rename the 'icon_any' meta tag to 'mask-icon' in the entity records.
+ */
+function metatag_update_7109(&$sandbox) {
+  module_load_include('install', 'metatag');
+  $old_tag = 'icon_any';
+  $new_tag = 'mask-icon';
+  return metatag_update_replace_meta_tag($sandbox, $old_tag, $new_tag);
+}
+
+/**
+ * Rename the 'icon_any' meta tag to 'mask-icon' in the configs.
+ */
+function metatag_update_7110() {
+  module_load_include('install', 'metatag');
+  $old_tag = 'icon_any';
+  $new_tag = 'mask-icon';
+  return metatag_update_replace_config($old_tag, $new_tag);
 }

+ 192 - 30
sites/all/modules/contrib/seo/metatag/metatag.metatag.inc

@@ -4,6 +4,11 @@
  * Implements hook_metatag_config_default().
  */
 function metatag_metatag_config_default() {
+  // Optionally skip loading the defaults.
+  if (!variable_get('metatag_load_defaults', TRUE)) {
+    return;
+  }
+
   $configs = array();
 
   $config = new stdClass();
@@ -11,7 +16,7 @@ function metatag_metatag_config_default() {
   $config->api_version = 1;
   $config->disabled = FALSE;
   $config->config = array(
-    'title' => array('value' => '[current-page:title] | [site:name]'),
+    'title' => array('value' => '[current-page:title] | [current-page:pager][site:name]'),
     'generator' => array('value' => 'Drupal 7 (http://drupal.org)'),
     'canonical' => array('value' => '[current-page:url:absolute]'),
     'shortlink' => array('value' => '[current-page:url:unaliased]'),
@@ -23,7 +28,7 @@ function metatag_metatag_config_default() {
   $config->api_version = 1;
   $config->disabled = FALSE;
   $config->config = array(
-    'title' => array('value' => variable_get('site_slogan') ? '[site:name] | [site:slogan]' : '[site:name]'),
+    'title' => array('value' => variable_get('site_slogan') ? '[site:name] | [current-page:pager][site:slogan]' : '[site:name] | [current-page:pager]'),
     'canonical' => array('value' => '[site:url]'),
     'shortlink' => array('value' => '[site:url]'),
   );
@@ -54,7 +59,7 @@ function metatag_metatag_config_default() {
   $config->api_version = 1;
   $config->disabled = FALSE;
   $config->config = array(
-    'title' => array('value' => '[node:title] | [site:name]'),
+    'title' => array('value' => '[node:title] | [current-page:pager][site:name]'),
     'description' => array('value' => '[node:summary]'),
   );
   $configs[$config->instance] = $config;
@@ -65,7 +70,7 @@ function metatag_metatag_config_default() {
     $config->api_version = 1;
     $config->disabled = FALSE;
     $config->config = array(
-      'title' => array('value' => '[term:name] | [site:name]'),
+      'title' => array('value' => '[term:name] | [current-page:pager][site:name]'),
       'description' => array('value' => '[term:description]'),
     );
     $configs[$config->instance] = $config;
@@ -78,6 +83,9 @@ function metatag_metatag_config_default() {
   $config->config = array(
     'title' => array('value' => '[user:name] | [site:name]'),
   );
+  if (variable_get('user_pictures')) {
+    $config->config['image_src'] = array('value' => '[user:picture:url]');
+  }
   $configs[$config->instance] = $config;
 
   // Before returning these, allow the bundled submodules to override them, thus
@@ -122,10 +130,17 @@ function metatag_metatag_config_instance_info() {
  * Implements hook_metatag_info().
  */
 function metatag_metatag_info() {
+  $info['groups']['basic'] = array(
+    'label' => t('Basic tags'),
+    'form' => array(
+      '#weight' => 1,
+      '#collapsed' => FALSE,
+    ),
+  );
   $info['groups']['advanced'] = array(
-    'label' => t('Advanced'),
+    'label' => t('Advanced tags'),
     'form' => array(
-      '#weight' => 100,
+      '#weight' => 2,
     ),
   );
 
@@ -136,6 +151,7 @@ function metatag_metatag_info() {
     'label' => t('Page title'),
     'description' => t("The text to display in the title bar of a visitor's web browser when they view this page. This meta tag may also be used as the title of the page when a visitor bookmarks or favorites this page."),
     'class' => 'DrupalTitleMetaTag',
+    'group' => 'basic',
     'weight' => ++$weight,
   );
 
@@ -143,6 +159,7 @@ function metatag_metatag_info() {
     'label' => t('Description'),
     'description' => t("A brief and concise summary of the page's content, preferably 150 characters or less. The description meta tag may be used by search engines to display a snippet about the page in search results."),
     'class' => 'DrupalTextMetaTag',
+    'group' => 'basic',
     'weight' => ++$weight,
     'form' => array(
       '#type' => 'textarea',
@@ -155,6 +172,7 @@ function metatag_metatag_info() {
     'label' => t('Abstract'),
     'description' => t("A brief and concise summary of the page's content, preferably 150 characters or less. The abstract meta tag may be used by search engines for archiving purposes."),
     'class' => 'DrupalTextMetaTag',
+    'group' => 'basic',
     'weight' => ++$weight,
     'form' => array(
       '#type' => 'textarea',
@@ -165,8 +183,9 @@ function metatag_metatag_info() {
 
   $info['tags']['keywords'] = array(
     'label' => t('Keywords'),
-    'description' => t("A comma-separated list of keywords about the page. This meta tag is <em>not</em> supported by most search engines."),
+    'description' => t("A comma-separated list of keywords about the page. This meta tag is <em>not</em> supported by most search engines anymore."),
     'class' => 'DrupalTextMetaTag',
+    'group' => 'basic',
     'weight' => ++$weight,
   );
 
@@ -185,10 +204,10 @@ function metatag_metatag_info() {
         'nofollow' => t('Prevents search engines from following links on this page.'),
         'noarchive' => t('Prevents cached copies of this page from appearing in search results.'),
         'nosnippet' => t('Prevents descriptions from appearing in search results, and prevents page caching.'),
-        'noodp' => t('Blocks the !opendirectory description from appearing in search results.', array('!opendirectory' => l('Open Directory Project', 'http://www.dmoz.org/'))),
+        'noodp' => t('Blocks the <a href="!opendirectory">Open Directory Project</a> description from appearing in search results.', array('!opendirectory' => 'http://www.dmoz.org/')),
         'noydir' => t('Prevents Yahoo! from listing this page in the <a href="@ydir">Yahoo! Directory</a>.', array('@ydir' => 'http://dir.yahoo.com/')),
         'noimageindex' => t('Prevent search engines from indexing images on this page.'),
-        'notranslate' => t('Prevent search engines from offering to translation this page in search results.'),
+        'notranslate' => t('Prevent search engines from offering to translate this page in search results.'),
       ),
     ),
   );
@@ -209,6 +228,46 @@ function metatag_metatag_info() {
     'weight' => ++$weight,
   );
 
+  $info['tags']['rating'] = array(
+    'label' => t('Content rating'),
+    'description' => t('Used to indicate the intended audience for the content.'),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'select_or_other' => TRUE,
+    'form' => array(
+      '#type' => 'select',
+      '#options' => array(
+        'general' => t('General'),
+        'mature' => t("Mature"),
+        'restricted' => t("Restricted"),
+        '14 years' => t("14 years or Older"),
+        'safe for kids' => t("Safe for kids"),
+      ),
+      '#empty_option' => t('- None -'),
+    ),
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['referrer'] = array(
+    'label' => t('Referrer policy'),
+    'description' => t('Indicate to search engines and other page scrapers whether or not links should be followed. See <a href="http://w3c.github.io/webappsec/specs/referrer-policy/">the W3C specifications</a> for further details.'),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'select_or_other' => TRUE,
+    'form' => array(
+      '#type' => 'select',
+      '#options' => array(
+        'no-referrer' => t('No Referrer'),
+        'origin' => t('Origin'),
+        'no-referrer-when-downgrade' => t('No Referrer When Downgrade'),
+        'origin-when-cross-origin' => t('Origin When Cross-Origin'),
+        'unsafe-url' => t('Unsafe URL'),
+      ),
+      '#empty_option' => t('- None -'),
+    ),
+    'weight' => ++$weight,
+  );
+
   $info['tags']['generator'] = array(
     'label' => t('Generator'),
     'description' => t("Describes the name and version number of the software or publishing tool used to create the page."),
@@ -236,6 +295,7 @@ function metatag_metatag_info() {
     'class' => 'DrupalLinkMetaTag',
     'group' => 'advanced',
     'weight' => ++$weight,
+    'image' => TRUE,
     'devel_generate' => array(
       'type' => 'image',
     ),
@@ -258,6 +318,9 @@ function metatag_metatag_info() {
     'class' => 'DrupalLinkMetaTag',
     'group' => 'advanced',
     'weight' => ++$weight,
+    'replaces' => array(
+      'shorturl',
+    ),
     'devel_generate' => array(
       'type' => 'shortlink',
     ),
@@ -297,27 +360,126 @@ function metatag_metatag_info() {
     ),
   );
 
-   $info['tags']['revisit-after'] = array(
-     'label' => t('Revisit After'),
-     'description' => t('Tell search engines when to index the page again. Very few search engines support this tag, it is more useful to use an <a href="@xmlsitemap">XML Sitemap</a> file.', array('@xmlsitemap' => 'http://drupal.org/project/xmlsitemap')),
-     'class' => 'DrupalDateIntervalMetaTag',
-     'group' => 'advanced',
-     'weight' => ++$weight,
-     'devel_generate' => array(
-       'type' => 'date',
-     ),
-   );
-
-   $info['tags']['content-language'] = array(
-     'label' => t('Content language'),
-     'description' => t("A deprecated meta tag for defining this page's two-letter language code(s)."),
-     'class' => 'DrupalTextMetaTag',
-     'group' => 'advanced',
-     'weight' => ++$weight,
-     'element' => array(
-       '#theme' => 'metatag_http_equiv',
-     ),
-   );
+  $info['tags']['prev'] = array(
+    'label' => t('Previous page URL'),
+    'description' => t('Used for paginated content. Meet Google recommendations to <a href="@google_pagination">indicate paginated content</a> by providing URL with rel="prev" link.', array('@google_pagination' => 'https://support.google.com/webmasters/answer/1663744')),
+    'class' => 'DrupalLinkMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  );
+
+  $info['tags']['next'] = array(
+    'label' => t('Next page URL'),
+    'description' => t('Used for paginated content. Meet Google recommendations to <a href="@google_pagination">indicate paginated content</a> by providing URL with rel="next" link.', array('@google_pagination' => 'https://support.google.com/webmasters/answer/1663744')),
+    'class' => 'DrupalLinkMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  );
+
+  $info['tags']['content-language'] = array(
+    'label' => t('Content language'),
+    'description' => t("A deprecated meta tag for defining this page's two-letter language code(s)."),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'is_language' => TRUE,
+    'element' => array(
+      '#theme' => 'metatag_http_equiv',
+    ),
+  );
+
+  $info['tags']['geo.position'] = array(
+    'label' => t('Geo position'),
+    'description' => t('Geo-spatial information in "latitude;longitude" format, e.g. "50.167958;-97.133185"; <a href="http://en.wikipedia.org/wiki/Geotagging#HTML_pages">see Wikipedia for details</a>.'),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['geo.placename'] = array(
+    'label' => t('Geo place name'),
+    'description' => t("A location's formal name."),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['geo.region'] = array(
+    'label' => t('Geo region'),
+    'description' => t("A location's two-letter international country code, with an optional two-letter region, e.g. \"US-NH\" for New Hampshire in the USA."),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['icbm'] = array(
+    'label' => t('ICBM'),
+    'description' => t('Geo-spatial information in "latitude, longitude" format, e.g. "50.167958, -97.133185"; <a href="http://en.wikipedia.org/wiki/ICBM">see Wikipedia for details</a>.'),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['refresh'] = array(
+    'label' => t('Refresh'),
+    'description' => t('The number of seconds to wait before refreshing the page. May also force redirect to another page using the format "5; url=http://example.com/", which would be triggered after five seconds.'),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'element' => array(
+      '#theme' => 'metatag_http_equiv',
+    ),
+  );
+
+  $info['tags']['revisit-after'] = array(
+    'label' => t('Revisit After'),
+    'description' => t('Tell search engines when to index the page again. Very few search engines support this tag, it is more useful to use an <a href="@xmlsitemap">XML Sitemap</a> file.', array('@xmlsitemap' => 'https://www.drupal.org/project/xmlsitemap')),
+    'class' => 'DrupalDateIntervalMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  );
+
+  $info['tags']['pragma'] = array(
+    'label' => t('Pragma'),
+    'description' => t('Used to control whether a browser caches a specific page locally. Little used today. Should be used in conjunction with the Cache-Control meta tag.'),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'element' => array(
+      '#theme' => 'metatag_http_equiv',
+    ),
+  );
+
+  $info['tags']['cache-control'] = array(
+    'label' => t('Cache-Control'),
+    'description' => t('Used to control whether a browser caches a specific page locally. Little used today. Should be used in conjunction with the Pragma meta tag.'),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'element' => array(
+      '#theme' => 'metatag_http_equiv',
+    ),
+  );
+
+  $info['tags']['expires'] = array(
+    'label' => t('Expires'),
+    'description' => t("Control when the browser's internal cache of the current page should expire. The date must to be an <a href=\"@rfc\">RFC-1123</a>-compliant date string that is represented in Greenwich Mean Time (GMT), e.g. '@date'. Set to '0' to stop the page being cached entirely.", array('@rfc' => 'http://www.csgnetwork.com/timerfc1123calc.html', '@date' => gmdate("D, d M Y H:i:s \G\M\T"))),
+    'class' => 'DrupalTextMetaTag',
+    'weight' => ++$weight,
+    'group' => 'advanced',
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  );
 
   return $info;
 }

+ 23 - 19
sites/all/modules/contrib/seo/metatag/metatag.migrate.inc

@@ -1,24 +1,27 @@
 <?php
-
 /**
  * @file
  * Metatag support for Migrate.
  */
 
+if (!class_exists('MigrateDestinationHandler')) {
+  return;
+}
+
 /**
  * Basic usage of the Migrate integration.
  *
  * This example assumes the custom module's name is "example_migrate".
- * 
+ *
  * example_migrate.inc:
- * 
+ *
  * class MetatagTestMigration extends DynamicMigration {
- * 
+ *
  *   public function __construct() {
  *     parent::__construct();
- * 
+ *
  *     $this->description = t('Migrate test.');
- * 
+ *
  *     $this->map = new MigrateSQLMap(
  *       $this->machineName,
  *       array(
@@ -31,22 +34,22 @@
  *       ),
  *       MigrateDestinationNode::getKeySchema()
  *     );
- * 
+ *
  *     $this->source = new MigrateSourceCSV(
  *       drupal_get_path('module', 'example_migrate') . '/sample.csv',
  *       array(),
  *       array('header_rows' => TRUE)
  *     );
- * 
+ *
  *     $this->destination = new MigrateDestinationNode('article');
- * 
+ *
  *     $this->addFieldMapping('metatag_description', 'description');
  *     $this->addFieldMapping('metatag_keywords', 'keywords');
  *   }
  * }
- * 
+ *
  * example_migrate.migrate.inc:
- * 
+ *
  * /**
  *  * Implements hook_migrate_api().
  *  * /
@@ -57,7 +60,7 @@
  *       'MetatagTest' => array('class_name' => 'MetatagTestMigration'),
  *     ),
  *   );
- * 
+ *
  *   return $api;
  * }
  */
@@ -81,13 +84,13 @@ function metatag_migrate_api() {
  */
 class MigrateMetatagHandler extends MigrateDestinationHandler {
 
+  /**
+   * Identify a list of supported entity types.
+   */
   public function __construct() {
-    $entity_types = array();
-    foreach (entity_get_info() as $entity_type => $entity_info) {
-      if (isset($entity_info['metatags']) && $entity_info['metatags']) {
-        $entity_types[] = $entity_type;
-      }
-    }
+    $entity_types = metatag_entity_supports_metatags();
+    $entity_types = array_filter($entity_types);
+    $entity_types = array_keys($entity_types);
 
     $this->registerTypes($entity_types);
   }
@@ -116,7 +119,8 @@ class MigrateMetatagHandler extends MigrateDestinationHandler {
     foreach ($elements['tags'] as $value) {
       $metatag_field = 'metatag_' . $value['name'];
       if (isset($entity->$metatag_field)) {
-        $entity->metatags[$value['name']]['value'] = $entity->$metatag_field;
+        $language = isset($entity->language) ? $entity->language : LANGUAGE_NONE;
+        $entity->metatags[$language][$value['name']]['value'] = $entity->$metatag_field;
         unset($entity->$metatag_field);
       }
     }

File diff suppressed because it is too large
+ 497 - 259
sites/all/modules/contrib/seo/metatag/metatag.module


+ 78 - 0
sites/all/modules/contrib/seo/metatag/metatag.search_api.inc

@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains MetatagSearchAlterCallback.
+ */
+
+/**
+ * Implements hook_search_api_alter_callback_info().
+ */
+function metatag_search_api_alter_callback_info() {
+  return array(
+    'search_api_metatag_alter_callback' => array(
+      'name' => t('Meta tags'),
+      'description' => t("Adds the item's meta tags to the indexed data."),
+      'class' => 'MetatagSearchAlterCallback',
+    ),
+  );
+}
+
+/**
+ * Adds meta tag values to the indexed items.
+ */
+class MetatagSearchAlterCallback extends SearchApiAbstractAlterCallback {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function supportsIndex(SearchApiIndex $index) {
+    // Only works on entities.
+    return (bool) $index->getEntityType();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alterItems(array &$items) {
+    $entity_type = $this->index->getEntityType();
+    $tags = metatag_get_info('tags');
+    foreach ($items as $id => $item) {
+      foreach (array_keys($tags) as $tag) {
+        $items[$id]->{'metatag_' . $tag} = NULL;
+        if (isset($item->language) && isset($item->metatags[$item->language][$tag])) {
+          $instance = metatag_get_instance($tag, $item->metatags[$item->language][$tag]);
+          $items[$id]->{'metatag_' . $tag} = $instance->getValue(array('token data' => array($entity_type => $item)));
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function propertyInfo() {
+    $properties = array();
+
+    // Get available meta tags.
+    $tags = metatag_get_info('tags');
+    foreach ($tags as $id => $tag) {
+      switch ($tag['class']) {
+        case 'DrupalLinkMetaTag':
+          $type = 'uri';
+          break;
+        default:
+          $type = 'text';
+          break;
+      }
+      $properties['metatag_' . $id] = array(
+        'label' => t('Meta tag: @label', array('@label' => $tag['label'])),
+        'description' => t('@label meta tag attached to an item.', array('@label' => $tag['label'])),
+        'type' => $type,
+      );
+    }
+
+    return $properties;
+  }
+
+}

+ 0 - 261
sites/all/modules/contrib/seo/metatag/metatag.test

@@ -1,261 +0,0 @@
-<?php
-
-class MetaTagsTestHelper extends DrupalWebTestCase {
-  function setUp(array $modules = array()) {
-    $modules[] = 'ctools';
-    $modules[] = 'token';
-    $modules[] = 'metatag';
-    $modules[] = 'metatag_test';
-    parent::setUp($modules);
-  }
-}
-
-class MetaTagsUnitTest extends MetaTagsTestHelper {
-  public static function getInfo() {
-    return array(
-      'name' => 'Meta tag unit tests',
-      'description' => 'Test basic meta tag functionality.',
-      'group' => 'Metatag',
-    );
-  }
-
-  /**
-   * Test the metatag_config_load_with_defaults() function.
-   */
-  public function testConfigLoadDefaults() {
-    $defaults = metatag_config_load_with_defaults('test:foo');
-    $this->assertEqual($defaults, array(
-      'description' => array('value' => 'Test foo description'),
-      'abstract' => array('value' => 'Test foo abstract'),
-      'title' => array('value' => 'Test altered title'),
-      'test:foo' => array('value' => 'foobar'),
-      'generator' => array('value' => 'Drupal 7 (http://drupal.org)'),
-      'canonical' => array('value' => '[current-page:url:absolute]'),
-      'shortlink' => array('value' => '[current-page:url:unaliased]'),
-    ));
-  }
-
-  public function testEntitySupport() {
-    $test_cases[1] = array('type' => 'node', 'bundle' => 'article', 'expected' => TRUE);
-    $test_cases[2] = array('type' => 'node', 'bundle' => 'page', 'expected' => TRUE);
-    $test_cases[3] = array('type' => 'node', 'bundle' => 'invalid-bundle', 'expected' => FALSE);
-    $test_cases[4] = array('type' => 'user', 'expected' => TRUE);
-    foreach ($test_cases as $test_case) {
-      $test_case += array('bundle' => NULL);
-      $this->assertMetatagEntityHasMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']);
-    }
-
-    variable_set('metatag_test_entity_info_disable', TRUE);
-    drupal_static_reset('metatag_entity_has_metatags');
-    drupal_static_reset('metatag_entity_supports_metatags');
-    entity_info_cache_clear();
-
-    $test_cases[2]['expected'] = FALSE;
-    $test_cases[4]['expected'] = FALSE;
-    foreach ($test_cases as $test_case) {
-      $test_case += array('bundle' => NULL);
-      $this->assertMetatagEntityHasMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']);
-    }
-  }
-
-  function assertMetatagEntityHasMetatags($entity_type, $bundle, $expected) {
-    $entity = entity_create_stub_entity($entity_type, array(0, NULL, $bundle));
-    return $this->assertEqual(
-      metatag_entity_has_metatags($entity_type, $entity),
-      $expected,
-      t("metatag_entity_has_metatags(:type, :entity) is :expected", array(
-        ':type' => var_export($entity_type, TRUE),
-        ':entity' => var_export($entity, TRUE),
-        ':expected' => var_export($expected, TRUE),
-      ))
-    );
-  }
-
-  /**
-   * Test the metatag_config_instance_label() function.
-   */
-  public function testConfigLabels() {
-    $test_cases = array(
-      'node' => 'Node',
-      'node:article' => 'Node: Article',
-      'node:article:c' => 'Node: Article: Unknown (c)',
-      'node:b' => 'Node: Unknown (b)',
-      'node:b:c' => 'Node: Unknown (b): Unknown (c)',
-      'a' => 'Unknown (a)',
-      'a:b' => 'Unknown (a): Unknown (b)',
-      'a:b:c' => 'Unknown (a): Unknown (b): Unknown (c)',
-      'a:b:c:d' => 'Unknown (a): Unknown (b): Unknown (c): Unknown (d)',
-    );
-
-    foreach ($test_cases as $input => $expected_output) {
-      drupal_static_reset('metatag_config_instance_label');
-      $actual_output = metatag_config_instance_label($input);
-      $this->assertEqual($actual_output, $expected_output);
-    }
-  }
-}
-
-
-// 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.
-//
-// 5. Node + Translation
-// * No language assignment
-// * Change language assignment.
-//   * Edit existing revision.
-//
-// 6. Node + Translation
-// * No language assignment
-// * Change language assignment.
-//   * Create new revision.
-// * Publish new revision.
-//
-// 7. Node + Translation
-// * No language assignment
-// * Change language assignment.
-//   * Create new revision.
-// * Delete new revision.
-//
-// 8. Node + Translation
-// * Initial language assignment
-//
-// 9. Node + Translation
-// * Initial language assignment
-// * Create new revision.
-// * Publish new revision.
-//
-// 10. Node + Translation
-// * Initial language assignment
-// * Create new revision.
-// * Delete new revision.
-//
-// 11. Node + Translation
-// * Initial language assignment
-// * Change language assignment.
-//   * Create new revision.
-// * Publish new revision.
-//
-// 12. Node + Translation
-// * Initial language assignment
-// * Change language assignment.
-//   * Create new revision.
-// * Delete new revision.
-//
-// 13. Node + Translation
-// * Initial language assignment
-// * Create translated node.
-//
-// 14. Node + Translation
-// * Initial language assignment
-// * Create new revision.
-// * Publish new revision.
-// * Create translated node.
-//
-// 15. Node + Translation
-// * Initial language assignment
-// * Create new revision.
-// * Publish new revision.
-// * Create translated node.
-// * Delete translated node.
-//
-// 16. Node + Translation
-// * Initial language assignment
-// * Create translated node.
-// * Delete original node.
-//
-// 17. Node + Translation
-// * Initial language assignment
-// * Create new revision.
-// * Publish new revision.
-// * Create translated node.
-// * Delete original node.
-//
-// 18. Node + entity_translation
-// * Initial language assignment
-// * Create translated node.
-//
-// 19. Node + entity_translation
-// * Initial language assignment
-// * Create translated node.
-// * Delete original.
-//
-// 20. Node + entity_translation
-// * Initial language assignment
-// * Create translated node.
-// * Create new revision.
-// * Publish new revision.
-//
-// 21. Node + entity_translation
-// * Initial language assignment
-// * Create translated node.
-// * Create new revision.
-// * Publish new revision.
-// * Delete new revision.
-//
-// 22. Node + entity_translation
-// * Initial language assignment
-// * Create translated node.
-// * Create new revision.
-// * Publish new revision.
-// * Delete original.
-//
-// 23. Node + entity_translation
-// * Initial language assignment
-// * Create translated node.
-// * Create new revision.
-// * Publish new revision.
-// * Delete original.
-//
-// 24. Node + entity_translation
-// * Initial language assignment
-// * Create new revision.
-// * Publish new revision.
-// * Create translated node.
-//
-// 25. Node + entity_translation
-// * Initial language assignment
-// * Create new revision.
-// * Publish new revision.
-// * Create translated node.
-// * 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.

+ 25 - 5
sites/all/modules/contrib/seo/metatag/metatag.theme.inc

@@ -13,7 +13,11 @@
  */
 function theme_metatag($variables) {
   $element = &$variables['element'];
-  element_set_attributes($element, array('name', '#value' => 'content'));
+  $args = array(
+    '#name'  => 'name',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
   unset($element['#value']);
   return theme('html_tag', $variables);
 }
@@ -26,7 +30,11 @@ function theme_metatag($variables) {
  */
 function theme_metatag_http_equiv($variables) {
   $element = &$variables['element'];
-  element_set_attributes($element, array('#name' => 'http-equiv', '#value' => 'content'));
+  $args = array(
+    '#name' => 'http-equiv',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
   unset($element['#value']);
   return theme('html_tag', $variables);
 }
@@ -39,7 +47,11 @@ function theme_metatag_http_equiv($variables) {
  */
 function theme_metatag_link_rel($variables) {
   $element = &$variables['element'];
-  element_set_attributes($element, array('#name' => 'rel', '#value' => 'href'));
+  $args = array(
+    '#name' => 'rel',
+    '#value' => 'href',
+  );
+  element_set_attributes($element, $args);
   unset($element['#value']);
   return theme('html_tag', $variables);
 }
@@ -52,7 +64,11 @@ function theme_metatag_link_rel($variables) {
  */
 function theme_metatag_link_rev($variables) {
   $element = &$variables['element'];
-  element_set_attributes($element, array('#name' => 'rev', '#value' => 'href'));
+  $args = array(
+    '#name' => 'rev',
+    '#value' => 'href',
+  );
+  element_set_attributes($element, $args);
   unset($element['#value']);
   return theme('html_tag', $variables);
 }
@@ -65,7 +81,11 @@ function theme_metatag_link_rev($variables) {
  */
 function theme_metatag_property($variables) {
   $element = &$variables['element'];
-  element_set_attributes($element, array('#name' => 'property', '#value' => 'content'));
+  $args = array(
+    '#name' => 'property',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
   unset($element['#value']);
   return theme('html_tag', $variables);
 }

+ 142 - 10
sites/all/modules/contrib/seo/metatag/metatag.tokens.inc

@@ -17,29 +17,51 @@ function metatag_token_info() {
 
   $metatag_info = metatag_get_info();
 
-  foreach($metatag_info['tags'] as $value) {
+  foreach ($metatag_info['tags'] as $value) {
+    if (isset($value['group'], $metatag_info['groups'][$value['group']], $metatag_info['groups'][$value['group']]['label'])) {
+      $label = t($metatag_info['groups'][$value['group']]['label']) . ': ' . t($value['label']);
+    }
+    else {
+      $label = t('Basic tags') . ': ' . t($value['label']);
+    }
     $info['tokens']['metatag'][$value['name']] = array(
-      'name' => $value['label'],
-      'description' => $value['description']
+      'name' => $label,
+      'description' => t($value['description']),
     );
   }
 
   if (module_exists('taxonomy')) {
     $info['tokens']['term']['metatag'] = array(
-      'name' => t('Metatag.'),
-      'description' => t('Metatag.'),
-      'type' => 'metatag'
+      'name' => t('Meta tags'),
+      'description' => t('Meta tags for this taxonomy term.'),
+      'type' => 'metatag',
     );
   }
 
   if (module_exists('node')) {
     $info['tokens']['node']['metatag'] = array(
-      'name' => t('Metatag.'),
-      'description' => t('Metatag.'),
-      'type' => 'metatag'
+      'name' => t('Meta tags'),
+      'description' => t('Meta tags for this node.'),
+      'type' => 'metatag',
+    );
+  }
+
+  if (module_exists('user')) {
+    $info['tokens']['user']['metatag'] = array(
+      'name' => t('Meta tags'),
+      'description' => t('Meta tags for this user.'),
+      'type' => 'metatag',
     );
   }
 
+  // A custom pager.
+  $pager = variable_get('metatag_pager_string', 'Page PAGER | ');
+  $page = str_replace('PAGER', 12, $pager);
+  $info['tokens']['current-page']['pager'] = array(
+    'name' => t('Custom pager'),
+    'description' => t('A custom pager (from the Metatag module). Currently set to "@pager" which would be output as e.g. "@page".', array('@pager' => $pager, '@page' => $page)),
+  );
+
   return $info;
 }
 
@@ -81,6 +103,30 @@ function metatag_tokens($type, $tokens, array $data = array(), array $options =
     }
   }
 
+  // User tokens.
+  if ($type == 'user' && !empty($data['user'])) {
+    $account = $data['user'];
+
+    if ($metatag_tokens = token_find_with_prefix($tokens, 'metatag')) {
+      $result = metatag_token_generate_array($account, 'user', 'user');
+      $replacements += token_generate('metatag', $metatag_tokens, array('metatag' => $result), $options);
+    }
+  }
+
+  // Custom pager.
+  if ($type == 'current-page') {
+    foreach ($tokens as $name => $original) {
+      switch ($name) {
+        case 'pager':
+          $pager = metatag_get_current_pager();
+          if (!empty($pager)) {
+            $replacements[$original] = $pager;
+          }
+          break;
+      }
+    }
+  }
+
   return $replacements;
 }
 
@@ -91,7 +137,7 @@ function metatag_token_generate_array($entity, $entity_type, $bundle) {
   if (metatag_entity_supports_metatags($entity_type, $bundle)) {
     $token_type = token_get_entity_mapping('entity', $entity_type);
 
-    $instance = "{$entity_type}:{$bundle}";
+    $instance = metatag_get_entity_metatags_instance($entity, $entity_type, $bundle);
     $options = array();
     $options['token data'][$token_type] = $entity;
     $options['entity'] = $entity;
@@ -104,6 +150,8 @@ function metatag_token_generate_array($entity, $entity_type, $bundle) {
       }
     }
     $metatags += metatag_config_load_with_defaults($instance);
+    // Process it for entity metatag replacement to avoid infinite recursion.
+    $metatags = _metatag_token_process_metatag($metatags, $token_type);
 
     $result = array();
     foreach ($metatags as $metatag => $data) {
@@ -116,3 +164,87 @@ function metatag_token_generate_array($entity, $entity_type, $bundle) {
 
   return NULL;
 }
+
+/**
+ * Loop through metatags to avoid recursion on entity tokens. It will replace
+ * entity metatag token to its actual entity metatag field value.
+ *
+ * @param array $metatags
+ *   An array of entity metatag tokens.
+ * @param string $token_type
+ *   The entity token type, such as 'node' or 'term'.
+ *
+ * @return array
+ *   Return metatags array with entity metatag tokens replaced.
+ */
+function _metatag_token_process_metatag($metatags, $token_type) {
+  foreach ($metatags as $metatag => $data) {
+    // Skip values that are not strings.
+    if (!is_string($data['value'])) {
+      continue;
+    }
+
+    // Explode all metatag token in field.
+    $data_tokens = token_scan($data['value']);
+    if (isset($data_tokens[$token_type])) {
+      foreach ($data_tokens[$token_type] as $key => $value) {
+        $metatag_parts = explode(':', $key);
+        // Check entity metatag token. Like [<entity_token_type>:metatag:<xyz>].
+        if ($metatag_parts[0] == 'metatag') {
+          $entity_field = implode(':', array_slice($metatag_parts, 1));
+          // If a value is not set here it will trigger an infinite loop.
+          $metatags[$metatag]['value'] = '';
+
+          // If the meta tag was defined then try parsing it.
+          if (!empty($metatags[$entity_field]['value'])) {
+            // Entity metatag field may contain other entity metatag token
+            // that need to be replaced too.
+            $replaced_value = metatag_token_entitymetatagtoken_replace($metatags, $metatags[$entity_field]['value'], $token_type);
+            $metatags[$metatag]['value'] = str_replace($value, $replaced_value, $metatags[$metatag]['value']);
+          }
+        }
+      }
+    }
+  }
+
+  return $metatags;
+}
+
+/**
+ * Replace entity metatag token with actual entity metatag field value.
+ *
+ * @param array $metatags
+ *   An array entity metatag tokens.
+ * @param string $token
+ *   A token to be replaced.
+ * @param string $token_type
+ *   The entity token type, such as 'node' or 'term'.
+ * @param array $search_tokens
+ *   An array of tokens to search for.
+ * @param array $replace_tokens
+ *   An array of tokens to be replaced.
+ *
+ * @return string
+ *   The replaced value of $token.
+ */
+function metatag_token_entitymetatagtoken_replace($metatags, $token, $token_type, $search_tokens = array(), $replace_tokens = array()) {
+  $data_tokens = token_scan($token);
+  // Check field has tokens.
+  if (isset($data_tokens[$token_type])) {
+    // Go through each token in field and find entity metatag tokens.
+    foreach ($data_tokens[$token_type] as $key => $value) {
+      $metatag_parts = explode(':', $key);
+      if ($metatag_parts[0] == 'metatag' && !in_array($value, $search_tokens)) {
+        // Entity metatag field value.
+        $entity_field = implode(':', array_slice($metatag_parts, 1));
+        $replaced_value = metatag_token_entitymetatagtoken_replace($metatags, $metatags[$entity_field]['value'], $token_type, $replace_tokens, $search_tokens);
+        $replace_tokens[] = $replaced_value;
+        $search_tokens[] = $value;
+      }
+    }
+    return str_replace($search_tokens, $replace_tokens, $token);
+  }
+  else {
+    return $token;
+  }
+}

File diff suppressed because it is too large
+ 14 - 0
sites/all/modules/contrib/seo/metatag/metatag.variable.inc


+ 40 - 0
sites/all/modules/contrib/seo/metatag/metatag_app_links/README.txt

@@ -0,0 +1,40 @@
+Metatag: App Links
+------------------
+This module adds the App Links meta tags, provided by the applinks.org [1].
+
+The following tags are provided:
+* al:android:package
+* al:android:url
+* al:android:class
+* al:android:app_name
+* al:ios:url
+* al:ios:app_store_id
+* al:ios:app_name
+* al:ipad:url
+* al:ipad:app_store_id
+* al:ipad:app_name
+* al:iphone:url
+* al:iphone:app_store_id
+* al:iphone:app_name
+* al:windows_phone:url
+* al:windows_phone:app_id
+* al:windows_phone:app_name
+* al:windows:url
+* al:windows:app_id
+* al:windows:app_name
+* al:windows_universal:url
+* al:windows_universal:app_id
+* al:windows_universal:app_name
+* al:web:url
+* al:web:should_fallback
+
+
+Credits
+------------------------------------------------------------------------------
+The initial development was by Andrew Berry [2].
+
+
+References
+------------------------------------------------------------------------------
+1: http://applinks.org/
+2: https://www.drupal.org/u/deviantintegral

+ 15 - 0
sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.info

@@ -0,0 +1,15 @@
+name = Metatag: App Links
+description = "Provides support for applinks.org meta tags."
+package = SEO
+core = 7.x
+dependencies[] = metatag
+
+; Tests.
+files[] = tests/metatag_app_links.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
+core = "7.x"
+project = "metatag"
+datestamp = "1467306248"
+

+ 162 - 0
sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.metatag.inc

@@ -0,0 +1,162 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the Metatag:App Links module.
+ */
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_app_links_metatag_info() {
+  $info['groups']['app_links'] = array(
+    'label' => t('App Links'),
+    'description' => t('Meta tags used to expose App Links for app deep linking. See <a href="@url">applinks.org</a> for details and documentation.', array('@url' => 'http://applinks.org')),
+    'form' => array(
+      '#weight' => 90,
+    ),
+  );
+
+  // Default values for each meta tag.
+  $defaults = array(
+    'description' => '',
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'app_links',
+    'element' => array(
+      '#theme' => 'metatag_property',
+    ),
+  );
+  // Stack these codes after most others.
+  $weight = 80;
+
+  $info['tags']['al:android:package'] = array(
+    'label' => t('Android App Package ID'),
+    'description' => t('A fully-qualified package name for intent generation. <strong>This attribute is required by the App Links specification.</strong>'),
+    'weight' => ++$weight,
+  ) + $defaults;
+
+  $info['tags']['al:android:url'] = array(
+    'label' => t('Android App URL scheme'),
+    'description' => t('A custom scheme for the Android app.'),
+  ) + $defaults;
+
+  $info['tags']['al:android:class'] = array(
+    'label' => t('Android App Activity Class'),
+    'description' => t('A fully-qualified Activity class name for intent generation.'),
+  ) + $defaults;
+
+  $info['tags']['al:android:app_name'] = array(
+    'label' => t('Android App Name'),
+    'description' => t('The name of the app (suitable for display)'),
+  ) + $defaults;
+
+  $info['tags']['al:ios:url'] = array(
+    'label' => t('iOS App URL scheme'),
+    'description' => t('A custom scheme for the iOS app. <strong>This attribute is required by the App Links specification.</strong>'),
+  ) + $defaults;
+
+  $info['tags']['al:ios:app_store_id'] = array(
+    'label' => t('iOS App Store ID'),
+    'description' => t('The app ID for the App Store.'),
+    'devel_generate' => array(
+      'type' => 'integer',
+    ),
+  ) + $defaults;
+
+  $info['tags']['al:ios:app_name'] = array(
+    'label' => t('iOS App Name'),
+    'description' => t('The name of the app (suitable for display)'),
+  ) + $defaults;
+
+  $info['tags']['al:ipad:url'] = array(
+    'label' => t('iPad App URL scheme'),
+    'description' => t('A custom scheme for the iPad app. <strong>This attribute is required by the App Links specification.</strong>'),
+  ) + $defaults;
+
+  $info['tags']['al:ipad:app_store_id'] = array(
+    'label' => t('iPad App Store ID'),
+    'description' => t('The app ID for the App Store.'),
+    'devel_generate' => array(
+      'type' => 'integer',
+    ),
+  ) + $defaults;
+
+  $info['tags']['al:ipad:app_name'] = array(
+    'label' => t('iPad App Name'),
+    'description' => t('The name of the app (suitable for display)'),
+  ) + $defaults;
+
+  $info['tags']['al:iphone:url'] = array(
+    'label' => t('iPhone App URL'),
+    'description' => t('A custom scheme for the iPhone app. <strong>This attribute is required by the App Links specification.</strong>'),
+  ) + $defaults;
+
+  $info['tags']['al:iphone:app_store_id'] = array(
+    'label' => t('iPhone App Store ID'),
+    'description' => t('The app ID for the App Store.'),
+    'devel_generate' => array(
+      'type' => 'integer',
+    ),
+  ) + $defaults;
+
+  $info['tags']['al:iphone:app_name'] = array(
+    'label' => t('iPhone App Name'),
+    'description' => t('The name of the app (suitable for display)'),
+  ) + $defaults;
+
+  $info['tags']['al:windows_phone:url'] = array(
+    'label' => t('Windows Phone App URL scheme'),
+    'description' => t('A custom scheme for the Windows Phone app. <strong>This attribute is required by the App Links specification.</strong>'),
+  ) + $defaults;
+
+  $info['tags']['al:windows_phone:app_id'] = array(
+    'label' => t('Windows Phone App GUID'),
+    'description' => t('The app ID (a GUID) for app store.'),
+  ) + $defaults;
+
+  $info['tags']['al:windows_phone:app_name'] = array(
+    'label' => t('Windows Phone App Name'),
+    'description' => t('The name of the app (suitable for display)'),
+  ) + $defaults;
+
+  $info['tags']['al:windows:url'] = array(
+    'label' => t('Windows App URL scheme'),
+    'description' => t('A custom scheme for the Windows app. <strong>This attribute is required by the App Links specification.</strong>'),
+  ) + $defaults;
+
+  $info['tags']['al:windows:app_id'] = array(
+    'label' => t('Windows App GUID'),
+    'description' => t('The app ID (a GUID) for app store.'),
+  ) + $defaults;
+
+  $info['tags']['al:windows:app_name'] = array(
+    'label' => t('Windows App Name'),
+    'description' => t('The name of the app (suitable for display)'),
+  ) + $defaults;
+
+  $info['tags']['al:windows_universal:url'] = array(
+    'label' => t('Windows Universal App URL scheme'),
+    'description' => t('A custom scheme for the Windows Universal app. <strong>This attribute is required by the App Links specification.</strong>'),
+  ) + $defaults;
+
+  $info['tags']['al:windows_universal:app_id'] = array(
+    'label' => t('Windows Universal App GUID'),
+    'description' => t('The app ID (a GUID) for app store.'),
+  ) + $defaults;
+
+  $info['tags']['al:windows_universal:app_name'] = array(
+    'label' => t('Windows Universal App Name'),
+    'description' => t('The name of the app (suitable for display)'),
+  ) + $defaults;
+
+  $info['tags']['al:web:url'] = array(
+    'label' => t('Web URL'),
+    'description' => t('The web URL; defaults to the URL for the content that contains this tag.'),
+  ) + $defaults;
+
+  $info['tags']['al:web:should_fallback'] = array(
+    'label' => t('Should fallback'),
+    'description' => t('Indicates if the web URL should be used as a fallback; defaults to "true".'),
+  ) + $defaults;
+
+  return $info;
+}

+ 15 - 0
sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.module

@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * @file
+ * Primary hook implementations for Metatag:App Links.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_app_links_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}

+ 44 - 0
sites/all/modules/contrib/seo/metatag/metatag_app_links/tests/metatag_app_links.test

@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * Tests for the Metatag AppLinks module.
+ */
+
+class MetatagAppLinksTest extends MetatagTestHelper {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag AppLinks tests',
+      'description' => 'Test the Metatag:AppLinks module.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_app_links';
+
+    parent::setUp($modules);
+
+    // Create an admin user and log them in.
+    $this->adminUser = $this->createAdminUser();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Confirm that it's possible to load the main admin page.
+   */
+  public function testAdminPage() {
+    $this->drupalGet('admin/config/search/metatags');
+    $this->assertResponse(200);
+
+    // Confirm the page is correct.
+    $this->assertText(t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.'));
+  }
+
+}

+ 3 - 3
sites/all/modules/contrib/seo/metatag/metatag_context/README.txt

@@ -25,7 +25,7 @@ development was by Marcin Pajdzik [3] (sponsored by Dennis Publishing [4]).
 
 References
 ------------------------------------------------------------------------------
-1: http://drupal.org/project/context
-2: http://drupal.org/project/context_metadata
-3: http://drupal.org/user/160555
+1: https://www.drupal.org/project/context
+2: https://www.drupal.org/project/context_metadata
+3: https://www.drupal.org/u/marcin-pajdzik
 4: http://www.dennis.co.uk/

+ 25 - 11
sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.admin.inc

@@ -9,10 +9,10 @@
  */
 function metatag_context_context_overview() {
   $contexts = context_enabled_contexts(TRUE);
-  $header = array(t('Name'), t('Paths'), t('Operations'));
-  $rows = array();
 
-  $caption = t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a> and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global')));
+  $header = array(t('Name'), t('Paths'), t('Weight'), t('Operations'));  $rows = array();
+
+  $caption = t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a>. Use the weight to specify the order the context meta tags run.', array('@url' => url('admin/config/search/metatags/config/global')));
 
   foreach ($contexts as $name => $context) {
     // Only show context items that are specifically selected to be "Shown on
@@ -23,12 +23,15 @@ function metatag_context_context_overview() {
         l(t('Delete'), 'admin/config/search/metatags/context/' . $context->name . '/delete', array('query' => array('destination' => 'admin/config/search/metatags/context'))),
       );
       $rows[] = array(
-        $context->name,
-        isset($context->conditions['path']) ? htmlspecialchars(implode(', ', $context->conditions['path']['values'])) : t('No path condition.'),
-        implode(' | ', $ops),
+        'name' => $context->name,
+        'path' => isset($context->conditions['path']) ? htmlspecialchars(implode(', ', $context->conditions['path']['values'])) : t('No path condition.'),
+        'weight' => isset($context->reactions['metatag_context_reaction']['weight']) ? $context->reactions['metatag_context_reaction']['weight'] : 0,
+        'ops' => implode(' | ', $ops),
       );
     }
   }
+  uasort($rows, 'drupal_sort_weight');
+
   return theme('table', array('header' => $header, 'rows' => $rows, 'caption' => $caption));
 }
 
@@ -85,6 +88,12 @@ function metatag_context_config_add_form_submit($form, &$form_state) {
   $context = metatag_context_load_default_context();
   $context->name = $form_state['values']['name'];
   context_save($context);
+
+  // Update the i18n strings.
+  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;
 }
 
@@ -99,7 +108,9 @@ function metatag_context_config_edit_form($form, &$form_state, $context) {
   // Don't care about the instance name, the data is being managed by Context
   // and not Metatag.
   $instance = "";
-  $options = array();
+  $options = array(
+    'token types' => array('node', 'term', 'user'),
+  );
 
   $metatags = $context->reactions['metatag_context_reaction']['metatags'];
   if (!isset($metatags[LANGUAGE_NONE])) {
@@ -138,14 +149,11 @@ function metatag_context_config_edit_form($form, &$form_state, $context) {
 
   $form['help'] = array(
     '#prefix' => '<hr /><p><em>',
-    '#markup' => t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a> and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))),
+    '#markup' => t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a>.', array('@url' => url('admin/config/search/metatags/config/global'))),
     '#suffix' => '</em></p>',
     '#weight' => -99,
   );
 
-  // Show all tokens.
-  $form['metatags']['tokens']['#token_types'] = 'all';
-
   $form['metatags']['#type'] = 'container';
   unset($form['metatags']['#collapsed']);
   unset($form['metatags']['#collapsible']);
@@ -175,6 +183,12 @@ function metatag_context_config_edit_form_submit($form, &$form_state) {
   $paths = array_combine($paths, $paths);
   $context->conditions['path']['values'] = $paths;
   context_save($context);
+
+  // Update the i18n strings.
+  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';
 }
 

+ 82 - 27
sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.context.inc

@@ -26,8 +26,10 @@ class metatag_context_reaction extends context_reaction {
       );
     }
 
-    // No options currently available.
-    $options = array();
+    // Provide token types.
+    $options = array(
+      'token types' => array('node', 'term', 'user'),
+    );
 
     $form['help'] = array(
       '#prefix' => '<p><em>',
@@ -52,9 +54,9 @@ class metatag_context_reaction extends context_reaction {
 
     // Flatten the fieldsets because otherwise the Context module will not save
     // them properly.
-    // TODO: Perhaps it can be done in a better way with #tree and #parents?
-    foreach (array('advanced', 'dublin-core', 'open-graph', 'twitter-cards') as $fieldset) {
-      if (isset($form['metatags'][LANGUAGE_NONE][$fieldset])) {
+    foreach (element_children($form['metatags'][LANGUAGE_NONE]) as $fieldset) {
+      $child = $form['metatags'][LANGUAGE_NONE][$fieldset];
+      if (isset($child['#type']) && $child['#type'] == 'fieldset') {
         $form['metatags'][LANGUAGE_NONE][$fieldset . '_heading'] = array(
           '#prefix' => '<hr /><h3>',
           '#markup' => $form['metatags'][LANGUAGE_NONE][$fieldset]['#title'],
@@ -90,6 +92,20 @@ class metatag_context_reaction extends context_reaction {
       '#default_value' => isset($data['metatag_admin']) ? $data['metatag_admin'] : '',
     );
 
+    // Add weight for current metatag_context.
+    $default_weight = 0;
+    if (!empty($context->reactions['metatag_context_reaction']['weight'])) {
+      $default_weight = (int) $context->reactions['metatag_context_reaction']['weight'];
+    }
+    $form['weight'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Weight'),
+      '#size' => 2,
+      '#default_value' => $default_weight,
+      '#description' => t('A higher weight will make this context be executed later, overriding other context meta tags.'),
+      '#weight' => -99,
+    );
+
     return $form;
   }
 
@@ -112,14 +128,23 @@ class metatag_context_reaction extends context_reaction {
           if (isset($metadata_array[LANGUAGE_NONE])) {
             $metadata_array = $metadata_array[LANGUAGE_NONE];
           }
-          foreach ($metadata_array as $key => $data) {
-            if (!empty($data['value'])) {
-              $metatags[$key] = $data;//t(check_plain($data['value']));
+
+          // Translate all of the meta tags using i18n, but don't update the
+          // strings.
+          metatag_translate_metatags($metadata_array, 'metatag_context:' . $context->name, NULL, FALSE);
+
+          // Add the meta tags to the output.
+          foreach ($metadata_array as $langcode => $tags) {
+            foreach ($tags as $name => $value) {
+              $metatags[$langcode][$name] = $value;
             }
           }
 
           // Add this context to the list of instances.
-          $instance_names[] = $context->name;
+          $weight = isset($context->reactions['metatag_context_reaction']['weight'])
+            ? $context->reactions['metatag_context_reaction']['weight']
+            : 0;
+          $instance_names[] = array('name' => $context->name, 'weight' => $weight);
         }
       }
 
@@ -128,15 +153,37 @@ class metatag_context_reaction extends context_reaction {
         // Load the global defaults.
         $metatags += metatag_config_load_with_defaults('');
 
+        // Sort by weight.
+        uasort($instance_names, 'drupal_sort_weight');
+
+        // Keep names only.
+        $instance_names = array_map('current', $instance_names);
+
         // Compile the identifier for this combination based on the context
         // names.
-        asort($instance_names);
         $instance = 'context:' . implode(',', $instance_names);
         $options['instance'] = $instance;
 
+        // If an entity & entity type were saved elsewhere, grab them for later.
+        // @see hook_entity_prepare_view().
+        $entities = drupal_static('metatag_context_entities');
+        if (!empty($entities) && (count($entities[1]) == 1)) {
+          $data = array_values($entities[1]);
+          $options['entity'] = $data[0];
+          $options['entity_type'] = $entities[0];
+          $options['token data'] = array($entities[0] => $data[0]);
+        }
+
+        // Don't output meta tags that only contain the pager.
+        $current_pager = metatag_get_current_pager();
+
         foreach ($metatags as $metatag => $data) {
           if ($metatag_instance = metatag_get_instance($metatag, $data)) {
-            $output[$metatag] = $metatag_instance->getElement($options);
+            $tag_output = $metatag_instance->getElement($options);
+            // Don't output the pager if that's all there is.
+            if ($tag_output != $current_pager) {
+              $output[$metatag] = $tag_output;
+            }
           }
         }
 
@@ -157,7 +204,7 @@ function metatag_context_context_default_contexts() {
   $context = new stdClass();
   $context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
   $context->api_version = 3;
-  $context->name = 'user_login';
+  $context->name = 'metatag_context_user_login';
   $context->description = 'A default Metatag:Context definition for the user login page. This needs to be enabled and then it can be customized as needed.';
   $context->tag = 'Metatag';
   $context->conditions = array(
@@ -176,9 +223,11 @@ function metatag_context_context_default_contexts() {
   $context->reactions = array(
     'metatag_context_reaction' => array(
       'metatags' => array(
-        'title' => array(
-          'value' => '[current-page:title] | [site:name]',
-          'default' => '[current-page:title] | [site:name]',
+        'und' => array(
+          'title' => array(
+            'value' => '[current-page:title] | [site:name]',
+            'default' => '[current-page:title] | [site:name]',
+          ),
         ),
       ),
       'metatag_admin' => 1,
@@ -190,7 +239,7 @@ function metatag_context_context_default_contexts() {
   $context = new stdClass();
   $context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
   $context->api_version = 3;
-  $context->name = 'user_register';
+  $context->name = 'metatag_context_user_register';
   $context->description = 'A default Metatag:Context definition for the user registration page. This needs to be enabled and then it can be customized as needed.';
   $context->tag = 'Metatag';
   $context->conditions = array(
@@ -208,9 +257,11 @@ function metatag_context_context_default_contexts() {
   $context->reactions = array(
     'metatag_context_reaction' => array(
       'metatags' => array(
-        'title' => array(
-          'value' => '[current-page:title] | [site:name]',
-          'default' => '[current-page:title] | [site:name]',
+        'und' => array(
+          'title' => array(
+            'value' => '[current-page:title] | [site:name]',
+            'default' => '[current-page:title] | [site:name]',
+          ),
         ),
       ),
       'metatag_admin' => 1,
@@ -223,7 +274,7 @@ function metatag_context_context_default_contexts() {
     $context = new stdClass();
     $context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
     $context->api_version = 3;
-    $context->name = 'forum';
+    $context->name = 'metatag_context_forum';
     $context->description = 'A default Metatag:Context definition for the main forum page. This needs to be enabled and then it can be customized as needed.';
     $context->tag = 'Metatag';
     $context->conditions = array(
@@ -236,9 +287,11 @@ function metatag_context_context_default_contexts() {
     $context->reactions = array(
       'metatag_context_reaction' => array(
         'metatags' => array(
-          'title' => array(
-            'value' => '[current-page:title] | [site:name]',
-            'default' => '[current-page:title] | [site:name]',
+          'und' => array(
+            'title' => array(
+              'value' => '[current-page:title] | [site:name]',
+              'default' => '[current-page:title] | [site:name]',
+            ),
           ),
         ),
         'metatag_admin' => 1,
@@ -252,7 +305,7 @@ function metatag_context_context_default_contexts() {
     $context = new stdClass();
     $context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
     $context->api_version = 3;
-    $context->name = 'blog';
+    $context->name = 'metatag_context_blog';
     $context->description = 'A default Metatag:Context definition for the main blog page. This needs to be enabled and then it can be customized as needed. Note: this does not cover the individual user blog pages, only the main blog page.';
     $context->tag = 'Metatag';
     $context->conditions = array(
@@ -265,9 +318,11 @@ function metatag_context_context_default_contexts() {
     $context->reactions = array(
       'metatag_context_reaction' => array(
         'metatags' => array(
-          'title' => array(
-            'value' => '[current-page:title] | [site:name]',
-            'default' => '[current-page:title] | [site:name]',
+          'und' => array(
+            'title' => array(
+              'value' => '[current-page:title] | [site:name]',
+              'default' => '[current-page:title] | [site:name]',
+            ),
           ),
         ),
         'metatag_admin' => 1,

+ 126 - 0
sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.i18n.inc

@@ -0,0 +1,126 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) hooks.
+ */
+
+/**
+ * Implements hook_i18n_object_info().
+ */
+function metatag_context_i18n_object_info() {
+  $info['metatag_context'] = array(
+    'title' => t('Metatag:Context configurations'),
+    // Callback to load all config objects.
+    'list callback' => 'metatag_context_i18n_list_contexts',
+    // The object load callback.
+    // 'load callback' => 'metatag_context_i18n_load',
+    // @todo Custom i18n object overrides.
+    // 'class' => 'metatag_context_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_contexts() {
+  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;
+  }
+}

+ 11 - 6
sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.info

@@ -2,14 +2,19 @@ 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
 
-; Information added by Drupal.org packaging script on 2014-10-10
-version = "7.x-1.4"
+dependencies[] = metatag
+dependencies[] = context
+
+; Tests.
+files[] = tests/metatag_context.test
+files[] = tests/metatag_context.i18n.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
 core = "7.x"
 project = "metatag"
-datestamp = "1412909330"
+datestamp = "1467306248"
 

+ 13 - 0
sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.metatag.inc

@@ -0,0 +1,13 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the metatag_context module.
+ */
+
+/**
+ * Implements hook_metatag_config_instance_info().
+ */
+function metatag_context_metatag_config_instance_info() {
+  $info['context'] = array('label' => t('Context'));
+  return $info;
+}

+ 30 - 13
sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.module

@@ -82,25 +82,30 @@ function metatag_context_context_page_reaction() {
 }
 
 /**
- * Implements hook_page_build().
+ * Implements hook_entity_prepare_view().
  */
-function metatag_context_page_build(&$page) {
-  // Load the meta tags that have been generated for this page.
-  $metatags = drupal_static('metatag_context', array());
-
-  if (!empty($metatags)) {
-    $page['content']['metatags']['global'] = $metatags;
-  }
+function metatag_context_entity_prepare_view($entities, $entity_type, $langcode = NULL) {
+  // Store the current entities.
+  drupal_static('metatag_context_entities', array($entity_type, $entities));
 }
 
 /**
- * Implements hook_preprocess_html().
+ * Implements hook_page_alter().
  */
-function metatag_context_preprocess_html(&$variables) {
-  $metadata = drupal_static('metatag_context');
+function metatag_context_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;
+  }
 
-  if (isset($metadata['metadata_title'])) {
-    $variables['head_title'] = token_replace($metadata['metadata_title']);
+  // Load the meta tags that have been generated for this page.
+  $metatags = drupal_static('metatag_context', array());
+
+  if (!empty($metatags)) {
+    // The page region can be changed.
+    $region = variable_get('metatag_page_region', 'content');
+    $page[$region]['metatags']['context'] = $metatags;
   }
 }
 
@@ -113,3 +118,15 @@ function metatag_context_ctools_plugin_api() {
     return array("version" => "3");
   }
 }
+
+/**
+ * Implements hook_module_implements_alter().
+ */
+function metatag_context_module_implements_alter(&$implementations, $hook) {
+  if ($hook == 'page_alter') {
+    // Run metatag_context_page_alter() last.
+    $group = $implementations['metatag_context'];
+    unset($implementations['metatag_context']);
+    $implementations['metatag_context'] = $group;
+  }
+}

+ 0 - 145
sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.test

@@ -1,145 +0,0 @@
-<?php
-/**
- * @file
- * Functional tests for the Metatag:Context module.
- */
-
-class MetatagContextTestCase extends DrupalWebTestCase {
-  /**
-   * The getInfo() method provides information about the test.
-   * In order for the test to be run, the getInfo() method needs
-   * to be implemented.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Meta tag context tests',
-      'description' => 'Test basic meta tag context functionality.',
-      'group' => 'Metatag',
-    );
-  }
-
-  /**
-   * Prepares the testing environment
-   */
-  public function setUp(array $modules = array()) {
-    $modules[] = 'metatag';
-    $modules[] = 'metatag_context';
-    parent::setUp($modules);
-    // Create user.
-    $this->privileged_user = $this->drupalCreateUser(array(
-      'bypass node access',
-      'administer content types',
-      'administer meta tags',
-    ));
-    $this->drupalLogin($this->privileged_user);
-    // Create content type, with underscores.
-    $type_name = strtolower($this->randomName(8)) . '_test';
-    $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $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('<front>', 'frontpage_metatags');
-    foreach ($this->metatag_pages as $page) {
-      $this->generateMetatag($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 = drupal_strtolower($this->randomName(10));
-    $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) {
-    //Add new Metatag object by path.
-    $edit = array(
-      'name' => $metatag_object->name,
-    );
-    $this->drupalPost('admin/config/search/metatags/context/add', $edit, t('Add and configure'));
-    $this->editMetatag($metatag_object);
-  }
-
-  /**
-   * 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('<meta name="' . $option, $option . ' not found in ' . $metatag_object->path);
-      }
-    }
-    if (!empty($metatag_object->title)) {
-      $this->assertRaw($metatag_object->title, 'Title found in ' . $metatag_object->path);
-    }
-  }
-}

+ 133 - 0
sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context.i18n.test

@@ -0,0 +1,133 @@
+<?php
+/**
+ * @file
+ * Tests the Metatag:Context module for i18n integration.
+ */
+
+class MetatagContextI18nTest extends MetatagTestHelper {
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag:Context tests, with i18n',
+      'description' => 'Test Metatag integration with the Context and i18n modules.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'context';
+    $modules[] = 'metatag_context';
+
+    // Enable the hidden submodule to manage some default configs.
+    $modules[] = 'metatag_context_tests';
+
+    // Needed for translations.
+    $modules[] = 'locale';
+    $modules[] = 'i18n';
+    $modules[] = 'i18n_string';
+
+    // Enable all of the modules that are needed.
+    parent::setUp($modules);
+
+    // Set up the locales.
+    $perms = array(
+      'translate admin strings',
+      'translate user-defined strings',
+      // Needed for the content type.
+      'administer languages',
+      'translate interface',
+      'bypass node access',
+    );
+    // This replaces the one from MetatagContextTest().
+    $this->adminUser = $this->createAdminUser($perms);
+    $this->setupLocales();
+    
+    // Reload the translations.
+    drupal_flush_all_caches();
+    module_load_include('admin.inc', 'i18n_string');
+    i18n_string_refresh_group('metatag');
+  }
+
+  /**
+   * Verify that strings are added to the translation system.
+   */
+  public function testContextI18n() {
+    // Generate a test object with English strings.
+    $object_en = $this->createTestObject('frontpage_metatags', '<front>');
+    // Generate a copy of that object only designed for loading on the French
+    // front page.
+    $object_fr = $this->createTestObject('frontpage_metatags', 'fr');
+    foreach (array('title', 'description', 'abstract', 'keywords') as $tag) {
+      $object_fr->$tag = $object_fr->$tag . ' in French';
+    }
+
+    // Create the English test object and check their content.
+    $this->generateByPathConfig($object_en);
+    $this->editByPathConfig($object_en);
+    $this->checkByPathConfig($object_en);
+
+    // Check each of the meta tags that were transmitted.
+    foreach (array('title', 'description', 'abstract', 'keywords') as $tag) {
+      $i18n_context = 'metatag_context:' . $object_en->name . ':' . $tag;
+      $lid = $this->getTranslationLidByContext($i18n_context);
+      $this->assertNotEqual($lid, 0, "Found the {locales_source} record for the {$tag} tag.");
+
+      // Save a translation for this tag.
+      $this->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);
+  }
+
+  /**
+   * Test the Metatag:Context translations for an exported configuration.
+   */
+  public function testExportedContext() {
+    // Plan out the different translation string tests.
+    $string_en = 'Metatag:Context test description tag.';
+    $string_fr = 'French page description';
+    $config_name = 'metatag_context:metatag_context_test:description';
+    $path = 'metatag-context-test';
+
+    // Confirm the string is present as it has been grabbed by the string-
+    // refresh triggered in $this->setUp().
+    $this->searchTranslationPage($string_en, $config_name);
+
+    // Get the translation string lid for the generator tag.
+    $lid = $this->getTranslationLidByContext($config_name);
+    $this->assertNotEqual($lid, 0, 'Found the locales_source string for the description tag.');
+
+    // Save the translation string.
+    $this->saveTranslationString($lid, $config_name, 'fr', $string_en, $string_fr);
+
+    // Load the English page again.
+    $this->drupalGet($path);
+    $this->assertResponse(200, 'Loaded the default test page again.');
+
+    // Confirm the page's description is what we set it to.
+    $xpath = $this->xpath("//meta[@name='description']");
+    $this->assertEqual(count($xpath), 1, 'Exactly one description meta tag found.');
+    $this->assertEqual($xpath[0]['content'], $string_en);
+    $this->assertNotEqual($xpath[0]['content'], $string_fr);
+
+    // Load the French page.
+    $this->drupalGet('fr/' . $path);
+    $this->assertResponse(200, 'Loaded the French test page.');
+
+    // Confirm the generator string was translated.
+    $xpath = $this->xpath("//meta[@name='description']");
+    $this->assertEqual(count($xpath), 1, 'Exactly one description meta tag found.');
+    $this->assertEqual($xpath[0]['content'], $string_fr);
+    $this->assertNotEqual($xpath[0]['content'], $string_en);
+  }
+
+}

+ 109 - 0
sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context.test

@@ -0,0 +1,109 @@
+<?php
+/**
+ * @file
+ * Functional tests for the Metatag:Context module.
+ */
+
+/**
+ * This extends the basic Metatag test class to reduce code duplication.
+ */
+class MetatagContextTest extends MetatagTestHelper {
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag:Context tests',
+      'description' => 'Test basic Metatag:Context functionality.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp(array $modules = array()) {
+    $modules[] = 'context';
+    $modules[] = 'metatag_context';
+
+    // Enable the hidden submodule to manage some default configs.
+    $modules[] = 'metatag_context_tests';
+
+    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', '<front>');
+    $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);
+  }
+
+  /**
+   * Test the Context integration.
+   */
+  public function testExportedPage() {
+    $this->drupalGet('metatag-context-test');
+    $this->assertResponse(200);
+
+    // Test the page title.
+    $this->assertTitle('Metatag:Context test page title tag');
+
+    // Test the description meta tag.
+    $xpath = $this->xpath("//meta[@name='description']");
+    $this->assertEqual(count($xpath), 1, 'Exactly one description meta tag found.');
+    $this->assertEqual($xpath[0]['content'], 'Metatag:Context test description tag.');
+
+    // Test the keywords meta tag.
+    $xpath = $this->xpath("//meta[@name='keywords']");
+    $this->assertEqual(count($xpath), 1, 'Exactly one keywords meta tag found.');
+    $this->assertEqual($xpath[0]['content'], 'Test, page, keywords');
+  }
+
+}

+ 56 - 0
sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context_tests.context.inc

@@ -0,0 +1,56 @@
+<?php
+/**
+ * @file
+ * Context reaction for Metatag:Context tests.
+ */
+
+/**
+ * Implements hook_context_default_contexts().
+ */
+function metatag_context_tests_context_default_contexts() {
+  $defaults = array();
+
+  $context = new stdClass();
+  $context->disabled = FALSE; /* Edit this to true to make a default context disabled initially */
+  $context->api_version = 3;
+  $context->name = 'metatag_context_test';
+  $context->description = 'A default Metatag:Context definition for a test page.';
+  $context->tag = 'Metatag';
+  $context->conditions = array(
+    'path' => array(
+      'values' => array(
+        'metatag-context-test' => 'metatag-context-test',
+      ),
+    ),
+  );
+  $context->reactions = array(
+    'metatag_context_reaction' => array(
+      'metatags' => array(
+        'und' => array(
+          'title' => array(
+            'value' => 'Metatag:Context test page title tag',
+          ),
+          'description' => array(
+            'value' => 'Metatag:Context test description tag.',
+          ),
+          'abstract' => array(
+            'value' => '',
+          ),
+          'keywords' => array(
+            'value' => 'Test, page, keywords',
+          ),
+        ),
+      ),
+      'metatag_admin' => 1,
+    ),
+  );
+  $context->condition_mode = 1;
+  $defaults[$context->name] = $context;
+
+  // Translatables
+  // Included for use with string extractors like potx.
+  t('A default Metatag:Context definition for a test page.');
+  t('Metatag');
+
+  return $defaults;
+}

+ 17 - 0
sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context_tests.info

@@ -0,0 +1,17 @@
+name = Metatag:Context Tests
+description = Helper module for testing metatag_context.module.
+core = 7.x
+
+; Don't show this on the modules admin page.
+hidden = TRUE
+
+dependencies[] = context
+dependencies[] = metatag
+dependencies[] = metatag_context
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
+core = "7.x"
+project = "metatag"
+datestamp = "1467306248"
+

+ 35 - 0
sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context_tests.module

@@ -0,0 +1,35 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag:Context tests.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_context_tests_ctools_plugin_api() {
+  list($module, $api) = func_get_args();
+  if ($module == "context" && $api == "context") {
+    return array("version" => "3");
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function metatag_context_tests_menu() {
+  $items['metatag-context-test'] = array(
+    'title' => t('Metatag:Context test page'),
+    'page callback' => 'metatag_context_tests_test_page',
+    'access callback' => TRUE,
+  );
+
+  return $items;
+}
+
+/**
+ * Menu callback for displaying the test page.
+ */
+function metatag_context_tests_test_page() {
+  return t('Hello');
+}

+ 6 - 6
sites/all/modules/contrib/seo/metatag/metatag_dc/README.txt

@@ -1,7 +1,8 @@
 Metatag: Dublin Core
 --------------------
-This module adds the fifteen Dublin Core Metadata Element Set [1] (plus one) to
-the available meta tags, as defined by the Dublin Core Metadata Institute [2].
+This module adds the fifteen Dublin Core Metadata Element Set [1] to the
+available meta tags, as defined by the Dublin Core Metadata Institute [2]. Forty
+additional tags may be added via the Metatag: Dublin Core Advanced submodule.
 
 The following tags are provided:
 * dcterms.contributor
@@ -12,7 +13,6 @@ The following tags are provided:
 * dcterms.format
 * dcterms.identifier
 * dcterms.language
-* dcterms.modified (additional, not part of the DCES)
 * dcterms.publisher
 * dcterms.relation
 * dcterms.rights
@@ -30,8 +30,8 @@ Haag. [4]), with contributions by many in the community [5].
 
 References
 ------------------------------------------------------------------------------
-1: http://dublincore.org/documents/dces/
+1: http://www.dublincore.org/documents/dces/
 2: http://www.dublincore.org/
-3: http://drupal.org/user/960720
+3: https://www.drupal.org/u/marty2081
 4: http://www.gemeentemuseum.nl/
-5: http://drupal.org/node/1491616
+5: https://www.drupal.org/node/1491616

+ 6 - 3
sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.info

@@ -4,9 +4,12 @@ package = SEO
 core = 7.x
 dependencies[] = metatag
 
-; Information added by Drupal.org packaging script on 2014-10-10
-version = "7.x-1.4"
+; Tests.
+files[] = tests/metatag_dc.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
 core = "7.x"
 project = "metatag"
-datestamp = "1412909330"
+datestamp = "1467306248"
 

+ 12 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.install

@@ -0,0 +1,12 @@
+<?php
+/**
+ * @file
+ * Installation and update scripts for metatag_dc_advanced.
+ */
+
+/**
+ * The Dublic Core Additional Tags meta tags are now in a new submodule.
+ */
+function metatag_dc_update_7100() {
+  drupal_set_message(t('The Dublic Core Additional Tags meta tags have been moved into the new "Metatag: Dublin Core Advanced" submodule.'));
+}

File diff suppressed because it is too large
+ 39 - 104
sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.metatag.inc


+ 3 - 2
sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.module

@@ -29,11 +29,12 @@ function metatag_dc_theme() {
  */
 function theme_metatag_dc($variables) {
   $element = &$variables['element'];
-  element_set_attributes($element, array(
+  $args = array(
     '#name' => 'name',
     '#schema' => 'schema',
-    '#value' => 'content')
+    '#value' => 'content',
   );
+  element_set_attributes($element, $args);
   unset($element['#value']);
   return theme('html_tag', $variables);
 }

+ 44 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc/tests/metatag_dc.test

@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * Tests for the Metatag DC module.
+ */
+
+class MetatagDcTest extends MetatagTestHelper {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag DC tests',
+      'description' => 'Test the Metatag:DC module.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_dc';
+
+    parent::setUp($modules);
+
+    // Create an admin user and log them in.
+    $this->adminUser = $this->createAdminUser();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Confirm that it's possible to load the main admin page.
+   */
+  public function testAdminPage() {
+    $this->drupalGet('admin/config/search/metatags');
+    $this->assertResponse(200);
+
+    // Confirm the page is correct.
+    $this->assertText(t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.'));
+  }
+
+}

+ 52 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/README.txt

@@ -0,0 +1,52 @@
+Metatag: Dublin Core Advanced
+-----------------------------
+This module adds the forty additional Dublin Core Metadata tags to the available
+meta tags, as defined by the Dublin Core Metadata Institute [1]; it also adds
+forty tags which may be useful for certain sites.
+
+Additional DCMI Metadata Terms meta tags:
+* dcterms.abstract
+* dcterms.accessRights
+* dcterms.accrualMethod
+* dcterms.accrualPeriodicity
+* dcterms.accrualPolicy
+* dcterms.alternative
+* dcterms.audience
+* dcterms.available
+* dcterms.bibliographicCitation
+* dcterms.conformsTo
+* dcterms.created
+* dcterms.dateAccepted
+* dcterms.dateCopyrighted
+* dcterms.dateSubmitted
+* dcterms.educationLevel
+* dcterms.extent
+* dcterms.hasFormat
+* dcterms.hasPart
+* dcterms.hasVersion
+* dcterms.instructionalMethod
+* dcterms.isFormatOf
+* dcterms.isPartOf
+* dcterms.isReferencedBy
+* dcterms.isReplacedBy
+* dcterms.isRequiredBy
+* dcterms.isVersionOf
+* dcterms.issued
+* dcterms.license
+* dcterms.mediator
+* dcterms.medium
+* dcterms.modified
+* dcterms.provenance
+* dcterms.references
+* dcterms.replaces
+* dcterms.requires
+* dcterms.rightsHolder
+* dcterms.spatial
+* dcterms.tableOfContents
+* dcterms.temporal
+* dcterms.valid
+
+
+References
+------------------------------------------------------------------------------
+1: http://www.dublincore.org/

+ 16 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/metatag_dc_advanced.info

@@ -0,0 +1,16 @@
+name = Metatag: Dublin Core Advanced
+description = Provides forty additional meta tags from the <a href="http://dublincore.org/">Dublin Core Metadata Institute</a>.
+package = SEO
+core = 7.x
+dependencies[] = metatag
+dependencies[] = metatag_dc
+
+; Tests.
+files[] = tests/metatag_dc_advanced.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
+core = "7.x"
+project = "metatag"
+datestamp = "1467306248"
+

+ 268 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/metatag_dc_advanced.metatag.inc

@@ -0,0 +1,268 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the metatag_dc module.
+ */
+
+/**
+ * Implements hook_metatag_bundled_config_alter().
+ */
+function metatag_dc_advanced_metatag_bundled_config_alter(array &$configs) {
+  foreach ($configs as &$config) {
+    switch ($config->instance) {
+      case 'node':
+        $config->config += array(
+          'dcterms.modified' => array('value' => '[node:changed:custom:Y-m-d\TH:iP]'),
+        );
+        break;
+    }
+  }
+}
+
+/**
+ * Implements hook_metatag_info().
+ * Dublin Core Elements taken from http://purl.org/dc/elements/1.1/.
+ */
+function metatag_dc_advanced_metatag_info() {
+  $info['groups']['dublin-core-additional'] = array(
+    'label' => t('Dublin Core Additional Tags'),
+    'description' => t('These tags are not part of the Metadata Element Set but may be useful for certain scenarios.'),
+    'form' => array(
+      '#weight' => 71,
+    ),
+  );
+
+  // Dublin Core meta tags stack after the Twitter Cards tags.
+  $weight = 80;
+
+  // Additional tags.
+  $defaults = array(
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'dublin-core-additional',
+    'element' => array(
+      '#type' => 'term',
+      '#theme' => 'metatag_dc',
+    ),
+  );
+
+  $info['tags']['dcterms.abstract'] = array(
+    'label' => t('Abstract'),
+    'description' => t('A summary of the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.accessRights'] = array(
+    'label' => t('Access rights'),
+    'description' => t('Information about who can access the resource or an indication of its security status.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.accrualMethod'] = array(
+    'label' => t('Accrual Method'),
+    'description' => t('The method by which items are added to a collection.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.accrualPeriodicity'] = array(
+    'label' => t('Accrual Periodicity'),
+    'description' => t('The frequency with which items are added to a collection.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.accrualPolicy'] = array(
+    'label' => t('Accrual Policy'),
+    'description' => t('The policy governing the addition of items to a collection.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.alternative'] = array(
+    'label' => t('Alternative Title'),
+    'description' => t('An alternative name for the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.audience'] = array(
+    'label' => t('Audience'),
+    'description' => t('A class of entity for whom the resource is intended or useful.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.available'] = array(
+    'label' => t('Date Available'),
+    'description' => t('Date (often a range) that the resource became or will become available.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  ) + $defaults;
+  $info['tags']['dcterms.bibliographicCitation'] = array(
+    'label' => t('Bibliographic Citation'),
+    'description' => t('A bibliographic reference for the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.conformsTo'] = array(
+    'label' => t('Conforms To'),
+    'description' => t('An established standard to which the described resource conforms.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.created'] = array(
+    'label' => t('Date Created'),
+    'description' => t('Date of creation of the resource.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  ) + $defaults;
+  $info['tags']['dcterms.dateAccepted'] = array(
+    'label' => t('Date Accepted'),
+    'description' => t('Date of acceptance of the resource.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  ) + $defaults;
+  $info['tags']['dcterms.dateCopyrighted'] = array(
+    'label' => t('Date Copyrighted'),
+    'description' => t('Date of copyright.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  ) + $defaults;
+  $info['tags']['dcterms.dateSubmitted'] = array(
+    'label' => t('Date Submitted'),
+    'description' => t('Date of submission of the resource.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  ) + $defaults;
+  $info['tags']['dcterms.educationLevel'] = array(
+    'label' => t('Audience Education Level'),
+    'description' => t('A class of entity, defined in terms of progression through an educational or training context, for which the described resource is intended.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.extent'] = array(
+    'label' => t('Extent'),
+    'description' => t('The size or duration of the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.hasFormat'] = array(
+    'label' => t('Has Format'),
+    'description' => t('A related resource that is substantially the same as the pre-existing described resource, but in another format.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.hasPart'] = array(
+    'label' => t('Has Part'),
+    'description' => t('A related resource that is included either physically or logically in the described resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.hasVersion'] = array(
+    'label' => t('Has Version'),
+    'description' => t('A related resource that is a version, edition, or adaptation of the described resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.instructionalMethod'] = array(
+    'label' => t('Instructional Method'),
+    'description' => t('A process, used to engender knowledge, attitudes and skills, that the described resource is designed to support.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.isFormatOf'] = array(
+    'label' => t('Is Format Of'),
+    'description' => t('A related resource that is substantially the same as the described resource, but in another format.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.isPartOf'] = array(
+    'label' => t('Is Part Of'),
+    'description' => t('A related resource in which the described resource is physically or logically included.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.isReferencedBy'] = array(
+    'label' => t('Is Referenced By'),
+    'description' => t('A related resource that references, cites, or otherwise points to the described resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.isReplacedBy'] = array(
+    'label' => t('Is Replaced by'),
+    'description' => t('A related resource that supplants, displaces, or supersedes the described resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.isRequiredBy'] = array(
+    'label' => t('Is Required By'),
+    'description' => t('A related resource that requires the described resource to support its function, delivery, or coherence.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.isVersionOf'] = array(
+    'label' => t('Is Version Of'),
+    'description' => t('A related resource of which the described resource is a version, edition, or adaptation.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.issued'] = array(
+    'label' => t('Date Issued'),
+    'description' => t('Date of formal issuance (e.g., publication) of the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.license'] = array(
+    'label' => t('License'),
+    'description' => t('A legal document giving official permission to do something with the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.mediator'] = array(
+    'label' => t('Mediator'),
+    'description' => t('An entity that mediates access to the resource and for whom the resource is intended or useful.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.medium'] = array(
+    'label' => t('Medium'),
+    'description' => t('The material or physical carrier of the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.modified'] = array(
+    'label' => t('Modified Date'),
+    'description' => t('Date on which the resource was changed.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  ) + $defaults;
+  $info['tags']['dcterms.provenance'] = array(
+    'label' => t('Provenance'),
+    'description' => t('A statement of any changes in ownership and custody of the resource since its creation that are significant for its authenticity, integrity, and interpretation.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.references'] = array(
+    'label' => t('References'),
+    'description' => t('A related resource that is referenced, cited, or otherwise pointed to by the described resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.replaces'] = array(
+    'label' => t('Replaces'),
+    'description' => t('A related resource that is supplanted, displaced, or superseded by the described resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.requires'] = array(
+    'label' => t('Requires'),
+    'description' => t('A related resource that is required by the described resource to support its function, delivery, or coherence.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.rightsHolder'] = array(
+    'label' => t('Rights Holder'),
+    'description' => t('A person or organization owning or managing rights over the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.spatial'] = array(
+    'label' => t('Spatial'),
+    'description' => t('Spatial characteristics of the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.tableOfContents'] = array(
+    'label' => t('Table Of Contents'),
+    'description' => t('A list of subunits of the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.temporal'] = array(
+    'label' => t('Temporal'),
+    'description' => t('Temporal characteristics of the resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['dcterms.valid'] = array(
+    'label' => t('Valid'),
+    'description' => t('Date (often a range) of validity of a resource.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+
+  return $info;
+}

+ 14 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/metatag_dc_advanced.module

@@ -0,0 +1,14 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the metatag_dc_advanced module.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_dc_advanced_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}

+ 44 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/tests/metatag_dc_advanced.test

@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * Tests for the Metatag DC Advanced module.
+ */
+
+class MetatagDcAdvancedTest extends MetatagTestHelper {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag DC Advanced tests',
+      'description' => 'Test the Metatag:DC Advanced module.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_dc_advanced';
+
+    parent::setUp($modules);
+
+    // Create an admin user and log them in.
+    $this->adminUser = $this->createAdminUser();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Confirm that it's possible to load the main admin page.
+   */
+  public function testAdminPage() {
+    $this->drupalGet('admin/config/search/metatags');
+    $this->assertResponse(200);
+
+    // Confirm the page is correct.
+    $this->assertText(t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.'));
+  }
+
+}

+ 7 - 4
sites/all/modules/contrib/seo/metatag/metatag_devel/metatag_devel.info

@@ -1,13 +1,16 @@
-name = Metatag:Devel
+name = Metatag: Devel
 description = Provides development / debugging functionality for the Metatag module. Integrates with Devel Generate.
 package = Development
 core = 7.x
 tags[] = developer
 dependencies[] = metatag
 
-; Information added by Drupal.org packaging script on 2014-10-10
-version = "7.x-1.4"
+; Tests.
+files[] = tests/metatag_devel.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
 core = "7.x"
 project = "metatag"
-datestamp = "1412909330"
+datestamp = "1467306248"
 

+ 6 - 1
sites/all/modules/contrib/seo/metatag/metatag_devel/metatag_devel.module

@@ -170,7 +170,12 @@ function metatag_devel_node_insert($node) {
       $count++;
     }
 
+    // Adjust the values for the nested language structure.
+    $metatags = array(
+      $node->language => $metatags,
+    );
+
     // Save the meta tags.
-    metatag_metatags_save('node', $node->nid, $node->vid, $metatags, $node->language);
+    metatag_metatags_save('node', $node->nid, $node->vid, $metatags);
   }
 }

+ 44 - 0
sites/all/modules/contrib/seo/metatag/metatag_devel/tests/metatag_devel.test

@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * Tests for the Metatag Devel module.
+ */
+
+class MetatagDevelest extends MetatagTestHelper {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag Devel tests',
+      'description' => 'Test the Metatag:Devel module.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_devel';
+
+    parent::setUp($modules);
+
+    // Create an admin user and log them in.
+    $this->adminUser = $this->createAdminUser();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Confirm that it's possible to load the main admin page.
+   */
+  public function testAdminPage() {
+    $this->drupalGet('admin/config/search/metatags');
+    $this->assertResponse(200);
+
+    // Confirm the page is correct.
+    $this->assertText(t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.'));
+  }
+
+}

+ 6 - 3
sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.info

@@ -4,9 +4,12 @@ package = SEO
 core = 7.x
 dependencies[] = metatag
 
-; Information added by Drupal.org packaging script on 2014-10-10
-version = "7.x-1.4"
+; Tests.
+files[] = tests/metatag_facebook.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
 core = "7.x"
 project = "metatag"
-datestamp = "1412909330"
+datestamp = "1467306248"
 

+ 11 - 1
sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.metatag.inc

@@ -33,6 +33,7 @@ function metatag_facebook_metatag_info() {
     'label' => t('Admins'),
     'description' => t('A comma-separated list of Facebook user IDs of people who are considered administrators or moderators of this page.'),
     'weight' => ++$weight,
+    'multiple' => TRUE,
   ) + $tag_info_defaults;
 
   $info['tags']['fb:app_id'] = array(
@@ -47,8 +48,17 @@ function metatag_facebook_metatag_info() {
   // If the FB_Social module is installed already, disable the app_id field.
   if (module_exists('fb_social')) {
     $info['tags']['fb:app_id']['form']['#disabled'] = TRUE;
-    $info['tags']['fb:app_id']['form']['#description'] = t('The FB_Social module will automatically output this meta tag, go to the <a href="!fb_social">FB_Social settings page</a> to customize it.', array('!fb_social' => url('admin/config/user-interface/fb_social')));
+    $info['tags']['fb:app_id']['form']['#description'] = t('The FB_Social module will automatically output this meta tag, go to the <a href="!fb_social">FB_Social settings page</a> to customize it.', array('!fb_social' => url('admin/structure/fbsocial')));
   }
 
+  $info['tags']['fb:pages'] = array(
+    'label' => t('Pages'),
+    'description' => t('Facebook Instant Articles claim URL token.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'integer',
+    ),
+  ) + $tag_info_defaults;
+
   return $info;
 }

+ 11 - 4
sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.module

@@ -17,10 +17,17 @@ function metatag_facebook_ctools_plugin_api($owner, $api) {
  * Implements hook_preprocess_html().
  */
 function metatag_facebook_preprocess_html(&$variables) {
+  // Fall back to hook_rdf_namespaces if the rdf module is enabled.
+  if (module_exists('rdf')) {
+    return;
+  }
+
   $variables['rdf_namespaces'] .= "\n  xmlns:fb=\"http://ogp.me/ns/fb#\"";
 }
 
-/*
-fb:admins
-fb:app_id
-*/
+/**
+ * Implements hook_rdf_namespaces().
+ */
+function metatag_facebook_hook_rdf_namespaces() {
+  return array('fb' => 'http://ogp.me/ns/fb#');
+}

+ 44 - 0
sites/all/modules/contrib/seo/metatag/metatag_facebook/tests/metatag_facebook.test

@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * Tests for the Metatag Facebook module.
+ */
+
+class MetatagFacebookTest extends MetatagTestHelper {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag Facebook tests',
+      'description' => 'Test the Metatag:Facebook module.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_facebook';
+
+    parent::setUp($modules);
+
+    // Create an admin user and log them in.
+    $this->adminUser = $this->createAdminUser();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Confirm that it's possible to load the main admin page.
+   */
+  public function testAdminPage() {
+    $this->drupalGet('admin/config/search/metatags');
+    $this->assertResponse(200);
+
+    // Confirm the page is correct.
+    $this->assertText(t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.'));
+  }
+
+}

+ 18 - 0
sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.info

@@ -0,0 +1,18 @@
+name = Metatag: favicons
+description = "Provides support for custom favicons."
+package = SEO
+core = 7.x
+dependencies[] = metatag
+
+; Custom class for mask-icon.
+files[] = metatag_favicons.mask-icon.class.inc
+
+; Tests.
+files[] = tests/metatag_favicons.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
+core = "7.x"
+project = "metatag"
+datestamp = "1467306248"
+

+ 16 - 0
sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.install

@@ -0,0 +1,16 @@
+<?php
+/**
+ * @file
+ * Update scripts, etc for the Metatag Favicons module.
+ */
+
+/**
+ * Implementations of hook_update_N().
+ */
+
+/**
+ * Clear the Metatag caches so the updated mask-icon spec can be used.
+ */
+function metatag_favicons_update_7100() {
+  metatag_config_cache_clear();
+}

+ 104 - 0
sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.mask-icon.class.inc

@@ -0,0 +1,104 @@
+<?php
+/**
+ * @file
+ * Custom class for the mask-icon meta tag's custom data.
+ */
+
+/**
+ * Mask icon meta tag controller.
+ */
+class DrupalMaskIconMetaTag extends DrupalTextMetaTag {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getForm(array $options = array()) {
+    $form['value'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->info['label'],
+      '#default_value' => isset($this->data['value']) ? $this->data['value'] : '',
+      '#description' => isset($this->info['description']) ? $this->info['description'] : '',
+      '#maxlength' => 1024,
+      '#weight' => $this->getWeight(),
+    );
+    $form['color'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Icon: SVG color'),
+      '#default_value' => isset($this->data['color']) ? $this->data['color'] : '',
+      '#description' => t('Provides a color for the SVG icon. Per <a href="@specs_url">Apple\'s specifications</a>, it may be a hexadecimal value (e.g. "#990000"), an RGB value (e.g. "rgb(153, 0, 0)"), or a recognized color-keyword (e.g. "red", "lime", "navy", etc). Will only be output if the SVG icon meta tag has a value.', array('@specs_url' => 'https://developer.apple.com/library/mac/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_0.html#//apple_ref/doc/uid/TP40014305-CH9-SW20')),
+      '#maxlength' => 10,
+      '#weight' => $form['value']['#weight'] + 0.01,
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue(array $options = array()) {
+    $value = array(
+      'value' => '',
+      'color' => '',
+    );
+
+    // The 'color' attribute will only be output if the 'value' is present.
+    if (!empty($this->data['value'])) {
+      // Pass the 'value' through the parent logic.
+      $value['value'] = parent::getValue($options);
+
+      // Get the optional 'color' attribute.
+      if (!empty($this->data['color'])) {
+        $value['color'] = check_plain($this->tidyValue($this->data['color']));
+      }
+    }
+
+    return $value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getElement(array $options = array()) {
+    $value = $this->getValue($options);
+
+    // This meta tag has two separate values.
+    $color = $value['color'];
+    $value = $value['value'];
+
+    // Don't bother proceeding if the 'value' is empty.
+    if (strlen($value) === 0) {
+      return array();
+    }
+
+    // The stack of elements that will be output.
+    $elements = array();
+
+    // Dynamically add each option to this setting.
+    $base_element = isset($this->info['element']) ? $this->info['element'] : array();
+
+    // Combine the base configuration for this meta tag with the value.
+    $element = $base_element + array(
+      '#theme' => 'metatag',
+      '#tag' => 'link',
+      '#id' => 'metatag_' . $this->info['name'] . '_' . 1,
+      '#rel' => $this->info['name'],
+      '#name' => $this->info['name'],
+      '#value' => $value,
+      '#color' => $color,
+      '#weight' => $this->getWeight(),
+    );
+
+    // Add header information if desired.
+    if (!empty($this->info['header'])) {
+      $element['#attached']['drupal_add_http_header'][] = array($this->info['header'], $value);
+    }
+
+    $elements[] = array($element, $element['#id']);
+
+    return array(
+      '#attached' => array('drupal_add_html_head' => $elements),
+    );
+  }
+
+}

+ 281 - 0
sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.metatag.inc

@@ -0,0 +1,281 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the Metatag:favicons module.
+ */
+
+/**
+ * Implements hook_metatag_bundled_config_alter().
+ */
+function metatag_favicons_metatag_bundled_config_alter(&$config) {
+  $favicon = metatag_favicons_get_theme_favicon();
+  if (!empty($favicon)) {
+    $config['global']->config['shortcut icon'] = array('value' => $favicon);
+  }
+}
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_favicons_metatag_info() {
+  $info['groups']['favicons'] = array(
+    'label' => t('Favicons & touch icons'),
+    'description' => t('Meta tags for displaying favicons of various sizes and types. All values should be either absolute or relative URLs. No effects are added to the "precomposed" icons.'),
+    'form' => array(
+      '#weight' => 100,
+    ),
+  );
+
+  // favicons meta tags stack after the simple tags.
+  $weight = 100;
+
+  // Default values for each meta tag.
+  $favicon_defaults = array(
+    'description' => '',
+    'class' => 'DrupalLinkMetaTag',
+    'group' => 'favicons',
+    'url' => TRUE,
+    'context' => array('global'),
+  );
+
+  $info['tags']['shortcut icon'] = array(
+    'label' => t('Default shortcut icon'),
+    'description' => t('The traditional favicon, must be either a GIF, ICO, JPG/JPEG or PNG image.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'shortcut icon',
+      '#theme' => 'metatag_shortcut_icon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['mask-icon'] = array(
+    'label' => t('Icon: SVG'),
+    'description' => t('A grayscale scalable vector graphic (SVG) file.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#theme' => 'metatag_mask_icon',
+    ),
+    'class' => 'DrupalMaskIconMetaTag',
+    'replaces' => array('icon_any'),
+  ) + $favicon_defaults;
+
+  $info['tags']['icon_16x16'] = array(
+    'label' => t('Icon: 16px x 16px'),
+    'description' => t('A PNG image that is 16px wide by 16px high.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'icon',
+      '#sizes' => '16x16',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['icon_32x32'] = array(
+    'label' => t('Icon: 32px x 32px'),
+    'description' => t('A PNG image that is 32px wide by 32px high.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'icon',
+      '#sizes' => '32x32',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['icon_96x96'] = array(
+    'label' => t('Icon: 96px x 96px'),
+    'description' => t('A PNG image that is 96px wide by 96px high.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'icon',
+      '#sizes' => '96x96',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['icon_192x192'] = array(
+    'label' => t('Icon: 192px x 192px'),
+    'description' => t('A PNG image that is 192px wide by 192px high.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'icon',
+      '#sizes' => '192x192',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon'] = array(
+    'label' => t('Apple touch icon: 60px x 60px'),
+    'description' => t('A PNG image that is 60px wide by 60px high. Used with the non-Retina iPhone, iPod Touch, and Android 2.1+ devices.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon_72x72'] = array(
+    'label' => t('Apple touch icon: 72px x 72px'),
+    'description' => t('A PNG image that is 72px wide by 72px high. Used with the iPad mini and the first- and second-generation iPad (@1x display) on iOS <= 6.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon',
+      '#sizes' => '72x72',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon_76x76'] = array(
+    'label' => t('Apple touch icon: 76px x 76px'),
+    'description' => t('A PNG image that is 76px wide by 76px high. Used with the iPad mini and the second-generation iPad (@1x display) on iOS >= 7.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon',
+      '#sizes' => '76x76',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon_114x114'] = array(
+    'label' => t('Apple touch icon: 114px x 114px'),
+    'description' => t('A PNG image that is 114px wide by 114px high. Used with iPhone with @2x display running iOS <= 6.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon',
+      '#sizes' => '114x114',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon_120x120'] = array(
+    'label' => t('Apple touch icon: 120px x 120px'),
+    'description' => t('A PNG image that is 120px wide by 120px high. Used with iPhone with @2x display running iOS >= 7.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon',
+      '#sizes' => '120x120',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon_144x144'] = array(
+    'label' => t('Apple touch icon: 144px x 144px'),
+    'description' => t('A PNG image that is 144px wide by 144px high. Used with iPad with @2x display running iOS <= 6.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon',
+      '#sizes' => '144x144',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon_152x152'] = array(
+    'label' => t('Apple touch icon: 152px x 152px'),
+    'description' => t('A PNG image that is 152px wide by 152px high. Used with iPad with @2x display running iOS >= 7.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon',
+      '#sizes' => '152x152',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon_180x180'] = array(
+    'label' => t('Apple touch icon: 180px x 180px'),
+    'description' => t('A PNG image that is 180px wide by 180px high. Used with iPhone 6 Plus with @3x display.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon',
+      '#sizes' => '180x180',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon-precomposed'] = array(
+    'label' => t('Apple touch icon (precomposed): 57px x 57px'),
+    'description' => t('A PNG image that is 57px wide by 57px high. Used with the non-Retina iPhone, iPod Touch, and Android 2.1+ devices.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon-precomposed',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon-precomposed_72x72'] = array(
+    'label' => t('Apple touch icon (precomposed): 72px x 72px'),
+    'description' => t('A PNG image that is 72px wide by 72px high. Used with the iPad mini and the first- and second-generation iPad (@1x display) on iOS <= 6.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon-precomposed',
+      '#sizes' => '72x72',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon-precomposed_76x76'] = array(
+    'label' => t('Apple touch icon (precomposed): 76px x 76px'),
+    'description' => t('A PNG image that is 76px wide by 76px high. Used with the iPad mini and the second-generation iPad (@1x display) on iOS >= 7.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon-precomposed',
+      '#sizes' => '76x76',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon-precomposed_114x114'] = array(
+    'label' => t('Apple touch icon (precomposed): 114px x 114px'),
+    'description' => t('A PNG image that is 114px wide by 114px high. Used with iPhone with @2x display running iOS <= 6.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon-precomposed',
+      '#sizes' => '114x114',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon-precomposed_120x120'] = array(
+    'label' => t('Apple touch icon (precomposed): 120px x 120px'),
+    'description' => t('A PNG image that is 120px wide by 120px high. Used with iPhone with @2x display running iOS >= 7.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon-precomposed',
+      '#sizes' => '120x120',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon-precomposed_144x144'] = array(
+    'label' => t('Apple touch icon (precomposed): 144px x 144px'),
+    'description' => t('A PNG image that is 144px wide by 144px high. Used with iPad with @2x display running iOS <= 6.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon-precomposed',
+      '#sizes' => '144x144',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon-precomposed_152x152'] = array(
+    'label' => t('Apple touch icon (precomposed): 152px x 152px'),
+    'description' => t('A PNG image that is 152px wide by 152px high. Used with iPad with @2x display running iOS >= 7.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon-precomposed',
+      '#sizes' => '152x152',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  $info['tags']['apple-touch-icon-precomposed_180x180'] = array(
+    'label' => t('Apple touch icon (precomposed): 180px x 180px'),
+    'description' => t('A PNG image that is 180px wide by 180px high. Used with iPhone 6 Plus with @3x display.'),
+    'weight' => ++$weight,
+    'element' => array(
+      '#rel' => 'apple-touch-icon-precomposed',
+      '#sizes' => '180x180',
+      '#theme' => 'metatag_favicon',
+    ),
+  ) + $favicon_defaults;
+
+  return $info;
+}

+ 158 - 0
sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.module

@@ -0,0 +1,158 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag:favicons.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_favicons_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function metatag_favicons_theme() {
+  $info['metatag_favicon'] = array(
+    'render element' => 'element',
+  );
+
+  $info['metatag_mask_icon'] = array(
+    'render element' => 'element',
+  );
+
+  $info['metatag_shortcut_icon'] = array(
+    'render element' => 'element',
+  );
+  return $info;
+}
+
+/**
+ * Theme callback for a favicon meta tag.
+ *
+ * The format is:
+ * <link rel="[rel]" href="[value]" sizes="[sizes]" />
+ */
+function theme_metatag_favicon($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#rel' => 'rel',
+    '#value' => 'href',
+    '#sizes' => 'sizes',
+    '#mask' => 'mask',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}
+
+/**
+ * Theme callback for the mask-icon meta tag.
+ *
+ * The format is:
+ * <link rel="mask-icon" href="[value]" sizes="[sizes]" />
+ */
+function theme_metatag_mask_icon($variables) {
+  $element = &$variables['element'];
+
+  $args = array(
+    '#rel' => 'rel',
+    '#value' => 'href',
+    '#color' => 'color',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}
+
+/**
+ * Theme callback for a shortcut icon meta tag.
+ *
+ * The format is:
+ * <link rel="[rel]" href="[value]" type="[type]" />
+ */
+function theme_metatag_shortcut_icon($variables) {
+  $element = &$variables['element'];
+
+  // Extract the MIME type.
+  $element['#type'] = metatag_favicons_get_mime_type($element['#value']);
+
+  $args = array(
+    '#rel' => 'rel',
+    '#value' => 'href',
+    '#type' => 'type',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}
+
+/**
+ * Helper function to get the theme's favicon URL.
+ *
+ * @return string
+ *  The absolute URL to the favicon, empty string if not found.
+ */
+function metatag_favicons_get_theme_favicon() {
+  $favicon_url = '';
+
+  // Is the favicon enabled?
+  if (theme_get_setting('toggle_favicon')) {
+    $favicon_url = theme_get_setting('favicon');
+  }
+
+  return $favicon_url;
+}
+
+/**
+ * Returns the correct MIME type for favicons.
+ *
+ * @param string $uri
+ *   The URI, or URL, of the favicon to be checked.
+ *
+ * @return string
+ *  The MIME type on success, an empty string on failure.
+ */
+function metatag_favicons_get_mime_type($uri) {
+  // Look for the last period in the URL.
+  $extension_dot = strrpos($uri, '.');
+  $type = '';
+
+  // URLs must have a file extension in order for this to work.
+  if ($extension_dot) {
+    $extension = strtolower(substr($uri, $extension_dot + 1));
+
+    // Work out the file's extension.
+    switch ($extension) {
+      case 'ico':
+        $type = 'vnd.microsoft.icon';
+        break;
+
+      // Rename JPEG images as JPG.
+      case 'jpeg':
+        $extension = 'jpeg';
+
+      // Basic image types.
+      case 'gif':
+      case 'jpg':
+      case 'png':
+        // Keep the extension as it is.
+        break;
+
+      // This shouldn't happen, only GIF, JPG, ICO or PNG files are supported.
+      default:
+        $extension = '';
+    }
+
+    // Only compile the MIME type if a supported extension was found.
+    if (!empty($extension)) {
+      $type = 'image/' . $extension;
+    }
+  }
+
+  return $type;
+}

File diff suppressed because it is too large
+ 23 - 0
sites/all/modules/contrib/seo/metatag/metatag_favicons/tests/druplicon-vector.svg


+ 210 - 0
sites/all/modules/contrib/seo/metatag/metatag_favicons/tests/metatag_favicons.test

@@ -0,0 +1,210 @@
+<?php
+/**
+ * @file
+ * Tests for the Metatag Favicons module.
+ */
+
+class MetatagFaviconsTest extends MetatagTestHelper {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag Favicons tests',
+      'description' => 'Test the Metatag:Favicons module.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_favicons';
+
+    parent::setUp($modules);
+
+    // Create an admin user and log them in.
+    $this->adminUser = $this->createAdminUser();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Confirm that it's possible to load the main admin page.
+   */
+  public function testAdminPage() {
+    $this->drupalGet('admin/config/search/metatags');
+    $this->assertResponse(200);
+
+    // Confirm the page is correct.
+    $this->assertText(t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.'));
+  }
+
+  /**
+   * Confirm the mask-icon meta tag works correctly.
+   */
+  public function testMaskIconValue() {
+    // The SVG image is copied from the archive obtained from
+    // https://www.drupal.org/about/media-kit/logos on 6/20/2016.
+    $svg_path = drupal_get_path('module', 'metatag_favicons') . '/tests/druplicon-vector.svg';
+    $relative_path = url($svg_path, array('absolute' => FALSE));
+    $absolute_path = url($svg_path, array('absolute' => TRUE));
+
+    // Testbot doesn't have "clean_url" enabled.
+    $relative_path = str_replace('?q=', '', $relative_path);
+    $absolute_path = str_replace('?q=', '', $absolute_path);
+
+    // Try filling in a path relative to the Drupal installation.
+    $config = metatag_config_load('global');
+    $config->config['mask-icon']['value'] = $svg_path;
+    metatag_config_save($config);
+
+    // Load the config page.
+    $this->drupalGet('admin/config/search/metatags');
+    $this->assertResponse(200);
+
+    // Confirm the meta tag's value is on the page.
+    $this->assertText(t('Icon: SVG') . ':');
+    $this->assertText($absolute_path);
+
+    // Load the front page.
+    $this->drupalGet('<front>');
+    $this->assertResponse(200);
+
+    // Confirm the meta tag is present.
+    $xpath = $this->xpath("//link[@rel='mask-icon']");
+    $this->assertEqual(count($xpath), 1, 'One mask-icon meta tag found.');
+    $this->assertNotEqual((string)$xpath[0]['href'], $svg_path);
+    $this->assertEqual((string)$xpath[0]['href'], $absolute_path);
+
+    // Before proceeding, clear the site's caches.
+    drupal_flush_all_caches();
+
+    // Try filling in a relative path.
+    $config = metatag_config_load('global');
+    $config->config['mask-icon']['value'] = $relative_path;
+    metatag_config_save($config);
+
+    // Load the front page.
+    $this->drupalGet('<front>');
+    $this->assertResponse(200);
+
+    // Confirm the meta tag is present.
+    $xpath = $this->xpath("//link[@rel='mask-icon']");
+    $this->assertEqual(count($xpath), 1, 'One mask-icon meta tag found.');
+    $this->assertNotEqual((string)$xpath[0]['href'], $svg_path);
+    $this->assertNotEqual((string)$xpath[0]['href'], $relative_path);
+
+    // Before proceeding, clear the site's caches.
+    drupal_flush_all_caches();
+
+    // Test filling in an absolute path.
+    $config = metatag_config_load('global');
+    $config->config['mask-icon']['value'] = $absolute_path;
+    metatag_config_save($config);
+
+    // Load the front page.
+    $this->drupalGet('<front>');
+    $this->assertResponse(200);
+
+    // Confirm the meta tag is present.
+    $xpath = $this->xpath("//link[@rel='mask-icon']");
+    $this->assertEqual(count($xpath), 1, 'One mask-icon meta tag found.');
+    $this->assertNotEqual((string)$xpath[0]['href'], $svg_path);
+    $this->assertEqual((string)$xpath[0]['href'], $absolute_path);
+
+    // Before proceeding, clear the site's caches.
+    drupal_flush_all_caches();
+
+    // Test filling in an absolute path for an external file
+    $path = 'https://www.example.com/example.svg';
+    $config = metatag_config_load('global');
+    $config->config['mask-icon']['value'] = $path;
+    metatag_config_save($config);
+
+    // Load the front page.
+    $this->drupalGet('<front>');
+    $this->assertResponse(200);
+
+    // Confirm the meta tag is present.
+    $xpath = $this->xpath("//link[@rel='mask-icon']");
+    $this->assertEqual(count($xpath), 1, 'One mask-icon meta tag found.');
+    $this->assertNotEqual((string)$xpath[0]['href'], $absolute_path);
+    $this->assertEqual((string)$xpath[0]['href'], $path);
+  }
+
+  /**
+   * Confirm the mask-icon meta tag 'color' attribute works correctly.
+   */
+  public function testMaskIconColor() {
+    // The SVG image is copied from the archive obtained from
+    // https://www.drupal.org/about/media-kit/logos on 6/20/2016.
+    $svg_path = drupal_get_path('module', 'metatag_favicons') . '/tests/druplicon-vector.svg';
+    $absolute_path = url($svg_path, array('absolute' => TRUE));
+
+    // Testbot doesn't have "clean_url" enabled.
+    $absolute_path = str_replace('?q=', '', $absolute_path);
+
+    // Try a color string.
+    $color = 'red';
+
+    // Update the global config.
+    $config = metatag_config_load('global');
+    $config->config['mask-icon']['value'] = $svg_path;
+    $config->config['mask-icon']['color'] = $color;
+    metatag_config_save($config);
+
+    // Load the front page.
+    $this->drupalGet('<front>');
+    $this->assertResponse(200);
+
+    // Confirm the meta tag is present.
+    $xpath = $this->xpath("//link[@rel='mask-icon']");
+    $this->assertEqual(count($xpath), 1, 'One mask-icon meta tag found.');
+    $this->assertEqual((string)$xpath[0]['href'], $absolute_path);
+    $this->assertEqual((string)$xpath[0]['color'], $color);
+
+    // Before proceeding, clear the site's caches.
+    drupal_flush_all_caches();
+
+    // Try a color hex code.
+    $color = '#990000';
+
+    // Update the global config.
+    $config = metatag_config_load('global');
+    $config->config['mask-icon']['value'] = $svg_path;
+    $config->config['mask-icon']['color'] = $color;
+    metatag_config_save($config);
+
+    // Load the front page.
+    $this->drupalGet('<front>');
+    $this->assertResponse(200);
+
+    // Confirm the meta tag is present.
+    $xpath = $this->xpath("//link[@rel='mask-icon']");
+    $this->assertEqual(count($xpath), 1, 'One mask-icon meta tag found.');
+    $this->assertEqual((string)$xpath[0]['href'], $absolute_path);
+    $this->assertEqual((string)$xpath[0]['color'], $color);
+
+    // Try a color RGB value.
+    $color = 'rgb(153, 0, 0)';
+
+    // Update the global config.
+    $config = metatag_config_load('global');
+    $config->config['mask-icon']['value'] = $svg_path;
+    $config->config['mask-icon']['color'] = $color;
+    metatag_config_save($config);
+
+    // Load the front page.
+    $this->drupalGet('<front>');
+    $this->assertResponse(200);
+
+    // Confirm the meta tag is present.
+    $xpath = $this->xpath("//link[@rel='mask-icon']");
+    $this->assertEqual(count($xpath), 1, 'One mask-icon meta tag found.');
+    $this->assertEqual((string)$xpath[0]['href'], $absolute_path);
+    $this->assertEqual((string)$xpath[0]['color'], $color);
+  }
+
+}

+ 9 - 25
sites/all/modules/contrib/seo/metatag/metatag_google_plus/README.txt

@@ -1,5 +1,5 @@
 Metatag: Google+
------------------
+----------------
 This module adds support for meta tag configuration for Google+ Snippet [1].
 
 The following Google+ tags are provided:
@@ -20,31 +20,15 @@ Page type (itemtype) provides default type options from the Google+ Snippet page
 Metatag hooks (see metatag.api.php).
 
 
-Known Issues
+Installation
 --------------------------------------------------------------------------------
-- When using Zen or its derived theme, the RDF Namespaces will be serialized
-  into an RDFa 1.1 prefix attribute, which means itemtype will be included in
-  prefix="...". To avoid this problem, this module will not add a itemtype
-  directly to $variable['rdf_namespaces'], instead, it will be necessary to add
-  code manually in the template.php or the custom theme.
-
-  Example code to use and adapt as needed:
-
-/**
- * Implements template_preprocess_html().
- *
- * Add itemtype code for Google+ in the 'html_attributes_array' which is only
- * available in Zen theme. Note Zen will convert rdf_namespaces to RDFa 1.1 with
- * prefix, so putting itemtype there will cause it to be added to the
- * prefix="itemtype=..." attribute.
- *
- * @see zen_preprocess_html()
- */
-function MYTHEME_preprocess_html(&$variables, $hook) {
-  if (module_exists('metatag_google_plus') && isset($variables['itemtype'])) {
-    $variables['html_attributes_array']['itemscope itemtype'] = "http://schema.org/{$variables['itemtype']}";
-  }
-}
+The $schemaorg variable must be appended to the <html> tag in the html.tpl.php
+file being used on the site, and it must be added after the $rdf_namespaces
+variable, e.g.:
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print
+  $language->language; ?>" version="XHTML+RDFa 1.0" dir="<?php print
+  $language->dir; ?>"<?php print $rdf_namespaces; ?><?php print $schemaorg; ?>>
 
 
 Credits / Contact

+ 6 - 3
sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.info

@@ -7,9 +7,12 @@ dependencies[] = metatag
 
 files[] = metatag_google_plus.inc
 
-; Information added by Drupal.org packaging script on 2014-10-10
-version = "7.x-1.4"
+; Tests.
+files[] = tests/metatag_google_plus.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
 core = "7.x"
 project = "metatag"
-datestamp = "1412909330"
+datestamp = "1467306248"
 

+ 24 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.install

@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * Various update scripts for Metatag: Google Plus.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function metatag_google_plus_install() {
+  // Notify the site builder that the html.tpl.php must be updated.
+  metatag_google_plus_update_7100();
+}
+
+/**
+ * Implementations of hook_update_N().
+ */
+
+/**
+ * Notify the site builder that the html.tpl.php file needs to be changed.
+ */
+function metatag_google_plus_update_7100() {
+  drupal_set_message(t("Note that the template's html.tpl.php must be updated in order for the Metatag: Google+ module to work correctly. Please see its README.txt file for details."));
+}

+ 9 - 12
sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.metatag.inc

@@ -18,7 +18,8 @@ function metatag_google_plus_metatag_bundled_config_alter(array &$configs) {
 
       case 'global:frontpage':
         $config->config += array(
-          'itemprop:name' => array('value' => '[current-page:title]'),
+          'itemprop:name' => array('value' => '[site:name]'),
+          'itemprop:description' => array('value' => '[site:slogan]'),
         );
         break;
 
@@ -26,14 +27,15 @@ function metatag_google_plus_metatag_bundled_config_alter(array &$configs) {
       case 'global:403':
       case 'global:404':
         $config->config += array(
-          'itemprop:name' => array('value' => '[current-page:title]'),
+          'itemprop:name' => array('value' => '[site:name]'),
         );
         break;
 
       case 'node':
         $config->config += array(
           'itemprop:description' => array('value' => '[node:summary]'),
-          'itemprop:name' => array('value' => '[current-page:title]'),
+          'itemprop:name' => array('value' => '[node:title]'),
+          'itemtype' => array('value' => 'Article'),
         );
         break;
 
@@ -47,6 +49,7 @@ function metatag_google_plus_metatag_bundled_config_alter(array &$configs) {
       case 'user':
         $config->config += array(
           'itemprop:name' => array('value' => '[user:name]'),
+          'itemtype' => array('value' => 'Person'),
         );
         if (variable_get('user_pictures')) {
           $config->config += array(
@@ -66,7 +69,7 @@ function metatag_google_plus_metatag_info() {
     'label' => t('Google+'),
     'description' => t('A set of meta tags specially for controlling the summaries displayed when content is shared on <a href="!url">Google+</a>.', array('!url' => 'https://plus.google.com/')),
     'form' => array(
-      '#weight' => 70,
+      '#weight' => 80,
     ),
   );
 
@@ -87,6 +90,7 @@ function metatag_google_plus_metatag_info() {
     'description' => t('Schema type. <a href="!url">More schema info</a>. If your page type does not exist in options above, please install <a href="!url2">select_or_other</a> module to enter page type manually.', array('!url' => 'http://schema.org/docs/schemas.html', '!url2' => 'https://drupal.org/project/select_or_other')),
     'class' => 'DrupalSchemaMetaTag',
     'weight' => ++$weight,
+    'select_or_other' => TRUE,
     'form' => array(
       '#type' => 'select',
       '#options' => array(
@@ -104,14 +108,6 @@ function metatag_google_plus_metatag_info() {
     ),
   ) + $defaults;
 
-  if (module_exists('select_or_other')) {
-    $info['tags']['itemtype']['form']['#type'] = 'select_or_other';
-    $info['tags']['itemtype']['form']['#other'] = 'Other (please type a value)';
-    $info['tags']['itemtype']['form']['#multiple'] = FALSE;
-    $info['tags']['itemtype']['form']['#other_unknown_defaults'] = 'other';
-    $info['tags']['itemtype']['form']['#select_type'] = 'select';
-  }
-
   $info['tags']['itemprop:name'] = array(
     'label' => t('Title'),
     'description' => t('A Google+ title for the page being shared. Keep keywords towards the front.'),
@@ -126,6 +122,7 @@ function metatag_google_plus_metatag_info() {
     'label' => t('Image URL'),
     'description' => t('The URL to a unique image representing the content of the page. Do not use a generic image such as your website logo, author photo, or other image that spans multiple pages. '),
     'weight' => ++$weight,
+    'image' => TRUE,
     'devel_generate' => array(
       'type' => 'image',
     ),

+ 13 - 7
sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.module

@@ -36,7 +36,11 @@ function theme_metatag_google_plus($variables) {
   // The format is e.g. 'itemprop:name'. Remove 'itemprop:' and store the rest
   // in '#itemprop'.
   $element['#itemprop'] = substr($element['#name'], 9);
-  element_set_attributes($element, array('#itemprop' => 'itemprop', '#value' => 'content'));
+  $args = array(
+    '#itemprop' => 'itemprop',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
   unset($element['#value']);
 
   return theme('html_tag', $variables);
@@ -47,14 +51,16 @@ function theme_metatag_google_plus($variables) {
  *
  * Add itemtype when available.
  *
- * We will not add itemtype in the rdf_namespaces when using Zen and its derived
- * themes as Zen will serialize RDF Namespaces into an RDFa 1.1 prefix
- * attribute, which means itemtype will be included in prefix="...".
- *
- * @see zen_preprocess_html()
+ * Note: The $schemaorg variable must be added to the html tag of the
+ *   html.tpl.php template after the $rdf_namespaces variable, see README.txt
+ *   for details.
  */
 function metatag_google_plus_preprocess_html(&$variables) {
+  if (!isset($variables['schemaorg'])) {
+    $variables['schemaorg'] = '';
+  }
+
   if (isset($variables['itemtype']) && !function_exists('zen_preprocess_html')) {
-    $variables['rdf_namespaces'] .= "\n  itemscope itemtype= \"http://schema.org/{$variables['itemtype']}\"";
+    $variables['schemaorg'] .= " itemscope itemtype=\"http://schema.org/{$variables['itemtype']}\"";
   }
 }

+ 44 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_plus/tests/metatag_google_plus.test

@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * Tests for the Metatag GooglePlus module.
+ */
+
+class MetatagGooglePlusTest extends MetatagTestHelper {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag GooglePlus tests',
+      'description' => 'Test the Metatag:GooglePlus module.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_google_plus';
+
+    parent::setUp($modules);
+
+    // Create an admin user and log them in.
+    $this->adminUser = $this->createAdminUser();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Confirm that it's possible to load the main admin page.
+   */
+  public function testAdminPage() {
+    $this->drupalGet('admin/config/search/metatags');
+    $this->assertResponse(200);
+
+    // Confirm the page is correct.
+    $this->assertText(t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.'));
+  }
+
+}

+ 39 - 0
sites/all/modules/contrib/seo/metatag/metatag_hreflang/README.txt

@@ -0,0 +1,39 @@
+Metatag: hreflang
+-----------------
+This module automatically adds hreflang meta tags for each locale currently
+enabled on the site. It also provides support for the hreflang=x-default meta
+tag.
+
+This is similar to the Alternative hreflang [1] module. That module
+automatically adds the hreflang tag to every page for every enabled locale,
+which may not be what a site needs. This module allows the site builder control
+over what tags are shown.
+
+The module also provides new tokens to output the URLs of each of the current
+node's translations, and assigns these as the defaults for the meta tags. As
+such, this module may not need additional configuration once it is enabled, but
+it's always worth confirming the output is as expected.
+
+This module works best when the Translation or Entity Translation modules are
+enabled and configured.
+
+
+Configuration
+--------------------------------------------------------------------------------
+ 1. By default if the hreflang="x-default" meta tag matches one of the
+    hreflang="LANGCODE" meta tags that hreflang="LANGCODE" meta tag will be
+    removed. It is possible to change this so that the meta tag is not removed
+    by enabling the "Allow hreflang tag that matches the x-default tag" option
+    on the main Metatag settings page:
+      admin/config/search/metatags/settings
+
+
+Credits / Contact
+--------------------------------------------------------------------------------
+Originally developed by Damien McKenna [2].
+
+
+References
+--------------------------------------------------------------------------------
+1: https://www.drupal.org/project/hreflang
+2: https://www.drupal.org/u/damienmckenna.

+ 20 - 0
sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.info

@@ -0,0 +1,20 @@
+name = Metatag: hreflang
+description = Provides support for the hreflang meta tag with some extra logic to simplify it.
+package = SEO
+core = 7.x
+
+dependencies[] = metatag
+
+; The locale module is required in order to have multiple languages/locales.
+dependencies[] = locale
+
+; Tests.
+files[] = tests/metatag_hreflang.test
+files[] = metatag_hreflang.with_entity_translation.test
+
+; Information added by Drupal.org packaging script on 2016-06-30
+version = "7.x-1.17"
+core = "7.x"
+project = "metatag"
+datestamp = "1467306248"
+

Some files were not shown because too many files changed in this diff