Browse Source

applied security contrib modules updates
didn't repatched view module, keep it in mind, may be necessary

Bachir Soussi Chiadmi 7 years ago
parent
commit
dbd7b88639
100 changed files with 3706 additions and 578 deletions
  1. 3 3
      sites/all/modules/contrib/fields/autocomplete_deluxe/autocomplete_deluxe.info
  2. 29 4
      sites/all/modules/contrib/fields/autocomplete_deluxe/autocomplete_deluxe.js
  3. 25 1
      sites/all/modules/contrib/fields/title/CHANGELOG.txt
  4. 53 12
      sites/all/modules/contrib/fields/title/tests/title.test
  5. 3 3
      sites/all/modules/contrib/fields/title/tests/title_test.info
  6. 39 6
      sites/all/modules/contrib/fields/title/title.core.inc
  7. 12 2
      sites/all/modules/contrib/fields/title/title.field.inc
  8. 3 3
      sites/all/modules/contrib/fields/title/title.info
  9. 5 17
      sites/all/modules/contrib/fields/title/title.module
  10. 2 3
      sites/all/modules/contrib/fields/title/views/title.views.inc
  11. 2 2
      sites/all/modules/contrib/fields/title/views/views_handler_title_field.inc
  12. 25 0
      sites/all/modules/contrib/seo/metatag/.codeclimate.yml
  13. 2 0
      sites/all/modules/contrib/seo/metatag/.csslintrc
  14. 1 0
      sites/all/modules/contrib/seo/metatag/.eslintignore
  15. 213 0
      sites/all/modules/contrib/seo/metatag/.eslintrc
  16. 117 0
      sites/all/modules/contrib/seo/metatag/CHANGELOG.txt
  17. 24 9
      sites/all/modules/contrib/seo/metatag/README.txt
  18. 91 15
      sites/all/modules/contrib/seo/metatag/metatag.admin.inc
  19. 17 0
      sites/all/modules/contrib/seo/metatag/metatag.api.php
  20. 7 3
      sites/all/modules/contrib/seo/metatag/metatag.inc
  21. 59 30
      sites/all/modules/contrib/seo/metatag/metatag.info
  22. 404 45
      sites/all/modules/contrib/seo/metatag/metatag.install
  23. 3 25
      sites/all/modules/contrib/seo/metatag/metatag.metatag.inc
  24. 178 92
      sites/all/modules/contrib/seo/metatag/metatag.module
  25. 45 43
      sites/all/modules/contrib/seo/metatag/metatag.search_api.inc
  26. 4 4
      sites/all/modules/contrib/seo/metatag/metatag.tokens.inc
  27. 5 4
      sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.info
  28. 33 33
      sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.metatag.inc
  29. 85 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/tests/metatag_app_links.tags.test
  30. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_app_links/tests/metatag_app_links.test
  31. 5 5
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.info
  32. 9 3
      sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context.i18n.test
  33. 5 5
      sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context.test
  34. 6 6
      sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context_tests.info
  35. 5 4
      sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.info
  36. 76 0
      sites/all/modules/contrib/seo/metatag/metatag_dc/tests/metatag_dc.tags.test
  37. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_dc/tests/metatag_dc.test
  38. 6 5
      sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/metatag_dc_advanced.info
  39. 80 0
      sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/tests/metatag_dc_advanced.tags.test
  40. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/tests/metatag_dc_advanced.test
  41. 4 4
      sites/all/modules/contrib/seo/metatag/metatag_devel/metatag_devel.info
  42. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_devel/tests/metatag_devel.test
  43. 5 4
      sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.info
  44. 48 0
      sites/all/modules/contrib/seo/metatag/metatag_facebook/tests/metatag_facebook.tags.test
  45. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_facebook/tests/metatag_facebook.test
  46. 6 5
      sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.info
  47. 1 1
      sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.mask-icon.class.inc
  48. 21 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/metatag_favicons.module
  49. 246 0
      sites/all/modules/contrib/seo/metatag/metatag_favicons/tests/metatag_favicons.tags.test
  50. 20 2
      sites/all/modules/contrib/seo/metatag/metatag_favicons/tests/metatag_favicons.test
  51. 22 0
      sites/all/modules/contrib/seo/metatag/metatag_google_cse/README.txt
  52. 16 0
      sites/all/modules/contrib/seo/metatag/metatag_google_cse/metatag_google_cse.info
  53. 5 0
      sites/all/modules/contrib/seo/metatag/metatag_google_cse/metatag_google_cse.install
  54. 62 0
      sites/all/modules/contrib/seo/metatag/metatag_google_cse/metatag_google_cse.metatag.inc
  55. 47 0
      sites/all/modules/contrib/seo/metatag/metatag_google_cse/metatag_google_cse.module
  56. 52 0
      sites/all/modules/contrib/seo/metatag/metatag_google_cse/tests/metatag_google_cse.tags.test
  57. 43 0
      sites/all/modules/contrib/seo/metatag/metatag_google_cse/tests/metatag_google_cse.test
  58. 2 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/README.txt
  59. 4 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.inc
  60. 6 5
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.info
  61. 23 1
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.metatag.inc
  62. 118 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/tests/metatag_google_plus.tags.test
  63. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/tests/metatag_google_plus.test
  64. 11 6
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.info
  65. 34 0
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.install
  66. 1 1
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.metatag.inc
  67. 17 13
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.module
  68. 23 0
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.tokens.inc
  69. 108 0
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/tests/metatag_hreflang.tags.test
  70. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/tests/metatag_hreflang.test
  71. 249 4
      sites/all/modules/contrib/seo/metatag/metatag_hreflang/tests/metatag_hreflang.with_entity_translation.test
  72. 4 4
      sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.info
  73. 2 2
      sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.page_title.inc
  74. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_importer/tests/metatag_importer.test
  75. 29 28
      sites/all/modules/contrib/seo/metatag/metatag_mobile/README.txt
  76. 5 4
      sites/all/modules/contrib/seo/metatag/metatag_mobile/metatag_mobile.info
  77. 58 39
      sites/all/modules/contrib/seo/metatag/metatag_mobile/metatag_mobile.metatag.inc
  78. 24 2
      sites/all/modules/contrib/seo/metatag/metatag_mobile/metatag_mobile.module
  79. 220 0
      sites/all/modules/contrib/seo/metatag/metatag_mobile/tests/metatag_mobile.tags.test
  80. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_mobile/tests/metatag_mobile.test
  81. 5 4
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.info
  82. 2 2
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.install
  83. 2 0
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.metatag.inc
  84. 178 0
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/tests/metatag_opengraph.tags.test
  85. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/tests/metatag_opengraph.test
  86. 7 6
      sites/all/modules/contrib/seo/metatag/metatag_opengraph_products/metatag_opengraph_products.info
  87. 1 1
      sites/all/modules/contrib/seo/metatag/metatag_opengraph_products/metatag_opengraph_products.metatag.inc
  88. 83 0
      sites/all/modules/contrib/seo/metatag/metatag_opengraph_products/tests/metatag_opengraph_products.tags.test
  89. 1 3
      sites/all/modules/contrib/seo/metatag/metatag_opengraph_products/tests/metatag_opengraph_products.test
  90. 5 5
      sites/all/modules/contrib/seo/metatag/metatag_panels/metatag_panels.info
  91. 9 3
      sites/all/modules/contrib/seo/metatag/metatag_panels/tests/metatag_panels.i18n.test
  92. 3 2
      sites/all/modules/contrib/seo/metatag/metatag_panels/tests/metatag_panels.test
  93. 7 7
      sites/all/modules/contrib/seo/metatag/metatag_panels/tests/metatag_panels_tests.info
  94. 5 4
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.info
  95. 2 2
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.install
  96. 1 1
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc
  97. 137 0
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/tests/metatag_twitter_cards.tags.test
  98. 1 2
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/tests/metatag_twitter_cards.test
  99. 5 4
      sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.info
  100. 25 0
      sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.install

+ 3 - 3
sites/all/modules/contrib/fields/autocomplete_deluxe/autocomplete_deluxe.info

@@ -5,9 +5,9 @@ core = 7.x
 files[] = autocomplete_deluxe.module
 dependencies[] = taxonomy
 
-; Information added by Drupal.org packaging script on 2015-03-16
-version = "7.x-2.1"
+; Information added by Drupal.org packaging script on 2017-01-11
+version = "7.x-2.2"
 core = "7.x"
 project = "autocomplete_deluxe"
-datestamp = "1426503185"
+datestamp = "1484128687"
 

+ 29 - 4
sites/all/modules/contrib/fields/autocomplete_deluxe/autocomplete_deluxe.js

@@ -82,6 +82,22 @@
     return this;
   };
 
+  /**
+   * Unescapes the given string.
+   */
+  Drupal.autocomplete_deluxe.unescape = function (input) {
+    // Unescaping is done via a textarea, since the text inside of it is never
+    // executed. This method also allows us to support older browsers like
+    // IE 9 and below.
+    var textArea = document.createElement('textarea');
+    textArea.innerHTML = input;
+    var decoded = textArea.value;
+    if ('remove' in Element.prototype) {
+      textArea.remove();
+    }
+    return decoded;
+  };
+
   /**
    * If there is no result this label will be shown.
    * @type {{label: string, value: string}}
@@ -177,7 +193,7 @@
       return result;
     };
 
-    var cache = {}
+    var cache = {};
     var lastXhr = null;
 
     this.source = function(request, response) {
@@ -296,14 +312,17 @@
     }
 
     this.value = item.value;
-    this.element = $('<span class="autocomplete-deluxe-item">' + item.label + '</span>');
+    this.element = $('<span class="autocomplete-deluxe-item"></span>');
+    this.element.text(item.label);
     this.widget = widget;
     this.item = item;
     var self = this;
 
     var close = $('<a class="autocomplete-deluxe-item-delete" href="javascript:void(0)"></a>').appendTo(this.element);
     // Use single quotes because of the double quote encoded stuff.
-    var input = $('<input type="hidden" value=\'' + this.value + '\'/>').appendTo(this.element);
+    var input = $('<input type="hidden"/>')
+    input.val(this.value);
+    input.appendTo(this.element);
 
     close.mousedown(function() {
       self.remove(item);
@@ -381,7 +400,13 @@
     });
 
     jqObject.bind("autocompleteselect", function(event, ui) {
-      self.addValue(ui.item);
+      // JQuery ui autocomplete needs the terms escaped, otherwise it would be
+      // open to XSS issues. Drupal.autocomplete.Item also escapes on rendering
+      // the DOM elements. Thus we have to unescape the label here before adding
+      // the new item.
+      var item = ui.item;
+      item.label = Drupal.autocomplete_deluxe.unescape(item.label);
+      self.addValue(item);
       jqObject.width(25);
       // Return false to prevent setting the last term as value for the jqObject.
       return false;

+ 25 - 1
sites/all/modules/contrib/fields/title/CHANGELOG.txt

@@ -1,9 +1,33 @@
 
 Title 7.x-1.x, xxxx-xx-xx
 -------------------------
+#2757739 by Pol, jyraya, alexverb, plach, ademarco, dxvargas: Added text format
+  support to the title text field.
+
+
+Title 7.x-1.0-alpha9, 2017-01-13
+--------------------------------
+#2757739 by dewalt, Pol: Token value is not sanitized, when replaced from title
+  field.
+#2813673 by plach, czigor, Stevel: Tests broken since new permission in drupal
+  core.
+
+
+Title 7.x-1.0-alpha8, 2016-03-28
+--------------------------------
+#2465141 by DuaelFr, Matthijs, jfrederick: Used entity_uri() options if given.
+#2602568 by sdstyles: Fixed title_entity_label() should be documented as
+  callback implementation.
+#2605040 by ShaxA, joelpittet, sylus: Removed recursive call to
+  entity_get_info().
+#2040055 by flux423, cs_shadow, visabhishek, plach, OlyN: Fixed Notice:
+  Undefined index: safe_value in title_field_formatter_view().
+#2426105 by Peacog, jcisio: Fixed Views "Link this field to the original entity"
+  doesn't work when using relationship.
 #2286147 by plach: Language fallback does not work when an entity translation is
   unpublished.
-#2286145 by plach: Prevent empty translations from being synced into the legacy field.
+#2286145 by plach: Prevent empty translations from being synced into the legacy
+  field.
 #1772116 by duellj, GaëlG | f4o: Fixed Menu link title is not getting node title
   by default.
 #1779268 by ndobromirov | brycesenz: Undefined index: field_name in

+ 53 - 12
sites/all/modules/contrib/fields/title/tests/title.test

@@ -9,6 +9,7 @@
  * Tests for legacy field replacement.
  */
 class TitleFieldReplacementTestCase extends DrupalWebTestCase {
+
   public static function getInfo() {
     return array(
       'name' => 'Field replacement',
@@ -17,14 +18,17 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
     );
   }
 
-  function setUp() {
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
     parent::setUp('entity', 'field_test', 'title', 'title_test');
   }
 
   /**
    * Test field replacement API and workflow.
    */
-  function testFieldReplacementWorkflow() {
+  public function testFieldReplacementWorkflow() {
     $info = entity_get_info('test_entity');
     $label_key = $info['entity keys']['label'];
     $field_name = $label_key . '_field';
@@ -78,7 +82,7 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
     // and view.
     $entity = title_test_entity_test_load($entity);
     title_test_phase_check('after_load', $entity);
-    $build = entity_view('test_entity', array($entity->ftid => $entity));
+    entity_view('test_entity', array($entity->ftid => $entity));
 
     foreach (title_test_phase_store() as $phase => $value) {
       $this->assertTrue($value, t('Field synchronization is correctly performed on %phase.', array('%phase' => $phase)));
@@ -95,8 +99,16 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
   /**
    * Test field replacement UI.
    */
-  function testFieldReplacementUI() {
-    $admin_user = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer content types', 'administer taxonomy', 'administer comments'));
+  public function testFieldReplacementUI() {
+    $permissions = array(
+      'access administration pages',
+      'view the administration theme',
+      'administer content types',
+      'administer taxonomy',
+      'administer comments',
+      'administer fields',
+    );
+    $admin_user = $this->drupalCreateUser($permissions);
     $this->drupalLogin($admin_user);
 
     foreach (entity_get_info() as $entity_type => $entity_info) {
@@ -147,12 +159,14 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
       }
     }
   }
+
 }
 
 /**
  * Tests for legacy field replacement.
  */
 class TitleAdminSettingsTestCase extends DrupalWebTestCase {
+
   public static function getInfo() {
     return array(
       'name' => 'Admin settings',
@@ -161,7 +175,10 @@ class TitleAdminSettingsTestCase extends DrupalWebTestCase {
     );
   }
 
-  function setUp() {
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
     parent::setUp('field_test', 'title', 'title_test');
     $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer taxonomy'));
     $this->drupalLogin($admin_user);
@@ -170,15 +187,18 @@ class TitleAdminSettingsTestCase extends DrupalWebTestCase {
   /**
    * Check for automated title_field attachment.
    */
-  function testAutomatedFieldAttachement() {
-    $this->assertAutomatedFieldAttachement(TRUE);
-    $this->assertAutomatedFieldAttachement(FALSE);
+  public function testAutomatedFieldAttachment() {
+    $this->doTestAutomatedFieldAttachment(TRUE);
+    $this->doTestAutomatedFieldAttachment(FALSE);
   }
 
   /**
-   * Check that the fields are replaced or skipped depdening on the given value.
+   * Check that the fields are replaced or skipped depending on the given value.
+   *
+   * @param bool $enabled
+   *   Whether replacement is enabled or not.
    */
-  function assertAutomatedFieldAttachement($enabled) {
+  public function doTestAutomatedFieldAttachment($enabled) {
     $edit = array(
       'title_taxonomy_term[auto_attach][name]' => $enabled,
       'title_taxonomy_term[auto_attach][description]' => $enabled,
@@ -198,6 +218,7 @@ class TitleAdminSettingsTestCase extends DrupalWebTestCase {
     $this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, 'name') == $enabled, 'Name field correctly processed.');
     $this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, 'description') == $enabled, 'Description field correctly processed.');
   }
+
 }
 
 /**
@@ -213,11 +234,22 @@ class TitleTranslationTestCase extends DrupalWebTestCase {
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp('locale', 'entity_translation', 'title', 'field_test', 'title_test');
 
     // Create a power user.
-    $admin_user = $this->drupalCreateUser(array('administer modules',  'view the administration theme', 'administer languages', 'administer taxonomy', 'administer entity translation', 'translate any entity'));
+    $permissions = array(
+      'administer modules',
+      'view the administration theme',
+      'administer languages',
+      'administer taxonomy',
+      'administer entity translation',
+      'translate any entity',
+    );
+    $admin_user = $this->drupalCreateUser($permissions);
     $this->drupalLogin($admin_user);
 
     // Enable a translation language.
@@ -242,6 +274,7 @@ class TitleTranslationTestCase extends DrupalWebTestCase {
     $edit = array(
       'name' => $this->randomString(),
       'machine_name' => $name,
+      'entity_translation_taxonomy' => 1,
     );
     $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
     $this->vocabulary = taxonomy_vocabulary_machine_name_load($name);
@@ -406,6 +439,14 @@ class TitleTranslationTestCase extends DrupalWebTestCase {
 
   /**
    * Loads a term using the given language as active language.
+   *
+   * @param int $tid
+   *   The term identifier.
+   * @param string|null $langcode
+   *   (optional) The active language to be set. Defaults to none.
+   *
+   * @return object|bool
+   *   A term object.
    */
   protected function termLoad($tid, $langcode = NULL) {
     drupal_static_reset();

+ 3 - 3
sites/all/modules/contrib/fields/title/tests/title_test.info

@@ -7,9 +7,9 @@ dependencies[] = title
 dependencies[] = entity
 dependencies[] = entity_translation
 
-; Information added by Drupal.org packaging script on 2015-03-23
-version = "7.x-1.0-alpha7+14-dev"
+; Information added by Drupal.org packaging script on 2017-01-13
+version = "7.x-1.0-alpha9+1-dev"
 core = "7.x"
 project = "title"
-datestamp = "1427069882"
+datestamp = "1484304486"
 

+ 39 - 6
sites/all/modules/contrib/fields/title/title.core.inc

@@ -41,6 +41,9 @@ function title_entity_info() {
           'label' => t('Title'),
           'description' => '',
         ) + $instance,
+        'additional keys' => array(
+          'format' => 'format',
+        ),
       ),
     ),
     'efq bundle conditions' => TRUE,
@@ -122,12 +125,31 @@ function title_field_term_description_submit($entity_type, $entity, $legacy_fiel
  */
 function title_field_text_sync_get($entity_type, $entity, $legacy_field, $info, $langcode) {
   $value = NULL;
+  $format = 'plain_text';
+
+  $info += array(
+    'additional keys' => array(
+      'format' => 'format',
+    ),
+  );
+
+  $format_key = $info['additional keys']['format'];
   $field_name = $info['field']['field_name'];
+  // Return values only if there is any available to process for the current
+  // language.
   if (!empty($entity->{$field_name}[$langcode]) && is_array($entity->{$field_name}[$langcode])) {
-    $items = $entity->{$field_name}[$langcode];
-    $value = !empty($items[0]['value']) ? $items[0]['value'] : NULL;
+    $item = $entity->{$field_name}[$langcode][0] + array(
+      'value' => NULL,
+      'format' => NULL,
+    );
+    $value = $item['value'];
+    $format = $item['format'];
   }
-  return array($legacy_field => $value);
+
+  return array(
+    $legacy_field => $value,
+    $format_key => $format,
+  );
 }
 
 /**
@@ -143,15 +165,26 @@ function title_field_text_sync_set($entity_type, $entity, $legacy_field, $info,
 function title_field_text_with_summary_sync_get($entity_type, $entity, $legacy_field, $info, $langcode) {
   $value = NULL;
   $format = NULL;
+
+  $info += array(
+    'additional keys' => array(
+      'format' => 'format',
+    ),
+  );
+
   $format_key = $info['additional keys']['format'];
   $field_name = $info['field']['field_name'];
   // Return values only if there is any available to process for the current
   // language.
   if (!empty($entity->{$field_name}[$langcode]) && is_array($entity->{$field_name}[$langcode])) {
-    $items = $entity->{$field_name}[$langcode];
-    $value = !empty($items[0]['value']) ? $items[0]['value'] : NULL;
-    $format = $entity->{$field_name}[$langcode][0]['format'];
+    $item = $entity->{$field_name}[$langcode][0] + array(
+      'value' => NULL,
+      'format' => NULL,
+    );
+    $value = $item['value'];
+    $format = $item['format'];
   }
+
   return array(
     $legacy_field => $value,
     $format_key => $format,

+ 12 - 2
sites/all/modules/contrib/fields/title/title.field.inc

@@ -97,13 +97,23 @@ function title_field_formatter_settings_summary($field, $instance, $view_mode) {
  */
 function title_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
   $settings = $display['settings'];
-  $output = isset($items[0]) ? $items[0]['safe_value'] : '';
+  $output = '';
+  if (isset($items[0]['safe_value'])) {
+    $output = $items[0]['safe_value'];
+  }
+  elseif (isset($items[0]['value'])) {
+    $output = _text_sanitize($instance, $langcode, $items[0], 'value');
+  }
   $element = array();
 
   if (!empty($output)) {
     if ($settings['title_link'] == 'content') {
       $uri = entity_uri($entity_type, $entity);
-      $output = l($output, $uri['path'], array('html' => TRUE));
+      $options = array('html' => TRUE);
+      if (!empty($uri['options'])) {
+        $options = array_merge($options, $uri['options']);
+      }
+      $output = l($output, $uri['path'], $options);
     }
 
     $wrap_tag = empty($settings['title_style']) ? '_none' : $settings['title_style'];

+ 3 - 3
sites/all/modules/contrib/fields/title/title.info

@@ -9,9 +9,9 @@ files[] = title.module
 files[] = views/views_handler_title_field.inc
 files[] = tests/title.test
 
-; Information added by Drupal.org packaging script on 2015-03-23
-version = "7.x-1.0-alpha7+14-dev"
+; Information added by Drupal.org packaging script on 2017-01-13
+version = "7.x-1.0-alpha9+1-dev"
 core = "7.x"
 project = "title"
-datestamp = "1427069882"
+datestamp = "1484304486"
 

+ 5 - 17
sites/all/modules/contrib/fields/title/title.module

@@ -105,17 +105,7 @@ function title_field_replacement_info($entity_type, $legacy_field = NULL) {
 }
 
 /**
- * Return an entity label value.
- *
- * @param $entity
- *   The entity whose label has to be displayed.
- * @param $type
- *   The name of the entity type.
- * @param $langcode
- *   (Optional) The language the entity label has to be displayed in.
- *
- * @return
- *   The entity label as a string value.
+ * Implements callback_entity_info_label().
  */
 function title_entity_label($entity, $type, $langcode = NULL) {
   $entity_info = entity_get_info($type);
@@ -754,6 +744,7 @@ function title_tokens_alter(array &$replacements, array $context) {
       $entity = $context['data'][$context['type']];
       list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
       $options = $context['options'];
+      $sanitize = !empty($options['sanitize']);
 
       // Since Title tokens are mostly used in storage contexts we default to
       // the current working language, that is the entity language. Modules
@@ -766,13 +757,10 @@ function title_tokens_alter(array &$replacements, array $context) {
           if (title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) {
             if (isset($context['tokens'][$legacy_field])) {
               $langcode = field_language($entity_type, $entity, $info['field']['field_name'], $langcode);
-              $values = $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode);
-              $item = $values[$legacy_field];
+              $item = $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode);
               if (!empty($item)) {
-                if (is_array($item)) {
-                  $item = reset($item);
-                }
-                $replacements[$context['tokens'][$legacy_field]] = $item;
+                list($value, $format) = array_values($item);
+                $replacements[$context['tokens'][$legacy_field]] = check_markup($value, $format, $langcode);
               }
             }
           }

+ 2 - 3
sites/all/modules/contrib/fields/title/views/title.views.inc

@@ -6,9 +6,8 @@
  */
 function title_field_views_data_alter(&$data) {
   foreach (entity_get_info() as $entity_type => $entity_info) {
-    $replacements = title_field_replacement_info($entity_type);
-    if ($replacements) {
-      foreach ($replacements as $replacement) {
+    if (!empty($entity_info['field replacement'])) {
+      foreach ($entity_info['field replacement'] as $replacement) {
         if (isset($replacement['field']['field_name'])) {
           $field = field_info_field($replacement['field']['field_name']);
           $table = _field_sql_storage_tablename($field);

+ 2 - 2
sites/all/modules/contrib/fields/title/views/views_handler_title_field.inc

@@ -36,13 +36,13 @@ class views_handler_title_field extends views_handler_field_field {
     if (!empty($this->options['link_to_entity'])) {
       $values = $this->original_values;
       $entity_type = $this->definition['entity_tables'][$this->base_table];
-      $entity_info = entity_get_info($entity_type);
-      $key = $entity_info['entity keys']['id'];
+      $key = $this->field_alias;
       if (!empty($values->_field_data[$key]['entity'])) {
         $entity = $values->_field_data[$key]['entity'];
         $uri = entity_uri($entity_type, $entity);
         $this->options['alter']['make_link'] = TRUE;
         $this->options['alter']['path'] = $uri['path'];
+        $this->options['alter']['options'] = !empty($uri['options']) ? $uri['options'] : array();
       }
     }
     return parent::render_item($count, $item);

+ 25 - 0
sites/all/modules/contrib/seo/metatag/.codeclimate.yml

@@ -0,0 +1,25 @@
+---
+engines:
+  csslint:
+    enabled: true
+  duplication:
+    enabled: true
+    config:
+      languages:
+      - javascript
+      - php
+  eslint:
+    enabled: true
+  fixme:
+    enabled: true
+  phpmd:
+    enabled: true
+ratings:
+  paths:
+  - "**.css"
+  - "**.inc"
+  - "**.install"
+  - "**.js"
+  - "**.module"
+  - "**.php"
+  - "**.test"

+ 2 - 0
sites/all/modules/contrib/seo/metatag/.csslintrc

@@ -0,0 +1,2 @@
+--exclude-exts=.min.css
+--ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes

+ 1 - 0
sites/all/modules/contrib/seo/metatag/.eslintignore

@@ -0,0 +1 @@
+**/*{.,-}min.js

+ 213 - 0
sites/all/modules/contrib/seo/metatag/.eslintrc

@@ -0,0 +1,213 @@
+ecmaFeatures:
+  modules: true
+  jsx: true
+
+env:
+  amd: true
+  browser: true
+  es6: true
+  jquery: true
+  node: true
+
+# http://eslint.org/docs/rules/
+rules:
+  # Possible Errors
+  comma-dangle: [2, never]
+  no-cond-assign: 2
+  no-console: 0
+  no-constant-condition: 2
+  no-control-regex: 2
+  no-debugger: 2
+  no-dupe-args: 2
+  no-dupe-keys: 2
+  no-duplicate-case: 2
+  no-empty: 2
+  no-empty-character-class: 2
+  no-ex-assign: 2
+  no-extra-boolean-cast: 2
+  no-extra-parens: 0
+  no-extra-semi: 2
+  no-func-assign: 2
+  no-inner-declarations: [2, functions]
+  no-invalid-regexp: 2
+  no-irregular-whitespace: 2
+  no-negated-in-lhs: 2
+  no-obj-calls: 2
+  no-regex-spaces: 2
+  no-sparse-arrays: 2
+  no-unexpected-multiline: 2
+  no-unreachable: 2
+  use-isnan: 2
+  valid-jsdoc: 0
+  valid-typeof: 2
+
+  # Best Practices
+  accessor-pairs: 2
+  block-scoped-var: 0
+  complexity: [2, 6]
+  consistent-return: 0
+  curly: 0
+  default-case: 0
+  dot-location: 0
+  dot-notation: 0
+  eqeqeq: 2
+  guard-for-in: 2
+  no-alert: 2
+  no-caller: 2
+  no-case-declarations: 2
+  no-div-regex: 2
+  no-else-return: 0
+  no-empty-label: 2
+  no-empty-pattern: 2
+  no-eq-null: 2
+  no-eval: 2
+  no-extend-native: 2
+  no-extra-bind: 2
+  no-fallthrough: 2
+  no-floating-decimal: 0
+  no-implicit-coercion: 0
+  no-implied-eval: 2
+  no-invalid-this: 0
+  no-iterator: 2
+  no-labels: 0
+  no-lone-blocks: 2
+  no-loop-func: 2
+  no-magic-number: 0
+  no-multi-spaces: 0
+  no-multi-str: 0
+  no-native-reassign: 2
+  no-new-func: 2
+  no-new-wrappers: 2
+  no-new: 2
+  no-octal-escape: 2
+  no-octal: 2
+  no-proto: 2
+  no-redeclare: 2
+  no-return-assign: 2
+  no-script-url: 2
+  no-self-compare: 2
+  no-sequences: 0
+  no-throw-literal: 0
+  no-unused-expressions: 2
+  no-useless-call: 2
+  no-useless-concat: 2
+  no-void: 2
+  no-warning-comments: 0
+  no-with: 2
+  radix: 2
+  vars-on-top: 0
+  wrap-iife: 2
+  yoda: 0
+
+  # Strict
+  strict: 0
+
+  # Variables
+  init-declarations: 0
+  no-catch-shadow: 2
+  no-delete-var: 2
+  no-label-var: 2
+  no-shadow-restricted-names: 2
+  no-shadow: 0
+  no-undef-init: 2
+  no-undef: 0
+  no-undefined: 0
+  no-unused-vars: 0
+  no-use-before-define: 0
+
+  # Node.js and CommonJS
+  callback-return: 2
+  global-require: 2
+  handle-callback-err: 2
+  no-mixed-requires: 0
+  no-new-require: 0
+  no-path-concat: 2
+  no-process-exit: 2
+  no-restricted-modules: 0
+  no-sync: 0
+
+  # Stylistic Issues
+  array-bracket-spacing: 0
+  block-spacing: 0
+  brace-style: 0
+  camelcase: 0
+  comma-spacing: 0
+  comma-style: 0
+  computed-property-spacing: 0
+  consistent-this: 0
+  eol-last: 0
+  func-names: 0
+  func-style: 0
+  id-length: 0
+  id-match: 0
+  indent: 0
+  jsx-quotes: 0
+  key-spacing: 0
+  linebreak-style: 0
+  lines-around-comment: 0
+  max-depth: 0
+  max-len: 0
+  max-nested-callbacks: 0
+  max-params: 0
+  max-statements: [2, 30]
+  new-cap: 0
+  new-parens: 0
+  newline-after-var: 0
+  no-array-constructor: 0
+  no-bitwise: 0
+  no-continue: 0
+  no-inline-comments: 0
+  no-lonely-if: 0
+  no-mixed-spaces-and-tabs: 0
+  no-multiple-empty-lines: 0
+  no-negated-condition: 0
+  no-nested-ternary: 0
+  no-new-object: 0
+  no-plusplus: 0
+  no-restricted-syntax: 0
+  no-spaced-func: 0
+  no-ternary: 0
+  no-trailing-spaces: 0
+  no-underscore-dangle: 0
+  no-unneeded-ternary: 0
+  object-curly-spacing: 0
+  one-var: 0
+  operator-assignment: 0
+  operator-linebreak: 0
+  padded-blocks: 0
+  quote-props: 0
+  quotes: 0
+  require-jsdoc: 0
+  semi-spacing: 0
+  semi: 0
+  sort-vars: 0
+  space-after-keywords: 0
+  space-before-blocks: 0
+  space-before-function-paren: 0
+  space-before-keywords: 0
+  space-in-parens: 0
+  space-infix-ops: 0
+  space-return-throw-case: 0
+  space-unary-ops: 0
+  spaced-comment: 0
+  wrap-regex: 0
+
+  # ECMAScript 6
+  arrow-body-style: 0
+  arrow-parens: 0
+  arrow-spacing: 0
+  constructor-super: 0
+  generator-star-spacing: 0
+  no-arrow-condition: 0
+  no-class-assign: 0
+  no-const-assign: 0
+  no-dupe-class-members: 0
+  no-this-before-super: 0
+  no-var: 0
+  object-shorthand: 0
+  prefer-arrow-callback: 0
+  prefer-const: 0
+  prefer-reflect: 0
+  prefer-spread: 0
+  prefer-template: 0
+  require-yield: 0

+ 117 - 0
sites/all/modules/contrib/seo/metatag/CHANGELOG.txt

@@ -1,3 +1,120 @@
+Metatag 7.x-1.21, 2017-02-15
+----------------------------
+#2844504 by DamienMcKenna: Add project names to all dependencies.
+By DamienMcKenna: Backported some minor text changes from the D8 branch.
+#2821713 by WidgetsBurritos: Don't output the mask-icon 'color' attribute if
+  it's empty.
+By DamienMcKenna, Michelle: Minor updates to README.txt.
+#2852260 by DamienMcKenna: Output caching wasn't fully disabled for the front
+  page or non-entity pages that used the global defaults.
+#2852737 by DamienMcKenna: Added CodeClimate config files.
+By DamienMcKenna, klausi: Information disclosure issue under certain
+  circumstances.
+
+
+Metatag 7.x-1.20, 2017-01-18
+----------------------------
+#2840500 by DamienMcKenna: Bring back compatibility with Workbench Moderation
+  v1.
+#2841064 by klausi, DamienMcKenna: Fix sandbox-based update scripts so they
+  don't run into infinite loops.
+
+
+Metatag 7.x-1.19, 2017-01-01
+----------------------------
+#2832427 by dmitry.kazberovich, e2thex, DamienMcKenna: Allow the metatag cache
+  expiration time to be modified.
+#2780025 by DamienMcKenna: Backported output tests from D8. Also fixes the
+  output of the shortcut icon, ios-app, android-app, author, publisher, and made
+  the Google CSE thumbnail tag an 'image'. Left some others to be fixed later.
+#2832476 by czigor; Added the 'product.group' and 'place' og:type options.
+#2835614 by drumm: metatag_metatags_load_multiple() doesn't need to sort the
+  results.
+By DamienMcKenna: Noted in the metatag_google_plus info file and README.txt that
+  it includes the Author and Publisher meta tags.
+By DamienMcKenna: Tweaked the favicons module description.
+By DamienMcKenna: Updated main tests to match the latest coding standards.
+By DamienMcKenna: Minor updates to various Google CSE labels.
+By DamienMcKenna: A string in metatag_opengraph_products wasn't using an
+  argument that was being passed to it.
+By DamienMcKenna: Minor adjustment to metatag_mobile strings.
+By DamienMcKenna: Updated submodule tests to match the latest coding standards.
+#2838198 by DamienMcKenna, Mixologic: Test dependency changes to workaround
+  changes in the DrupalCI platform.
+#2831073 by dxvargas, DamienMcKenna: Remove old workarounds due to Workbench
+  Moderation 3.x API changes, warn if older version installed.
+
+
+Metatag 7.x-1.18, 2016-11-30
+----------------------------
+#2761817 by DamienMcKenna: Fixed metatag_update_replace_config() so it isn't
+  hardcoded to only work with og:video.
+#2759843 by DamienMcKenna: Removed the Alexa verification tag.
+#2759855 by DamienMcKenna: Removed the Yahoo verification tag.
+By DamienMcKenna: Clear Metatag's caches after deleting or renaming a meta tag.
+#2763499 by DamienMcKenna: Don't use entity_load() when saving Metatag data,
+  it can cause anomolies.
+#2750705 by jalpesh, susannecoates: Updated description of the Google Play app
+  ID meta tag.
+#2771603 by FeyP: Fixed incorrect argument name to metatag_metatag_save().
+#2745177 by DamienMcKenna: Tests to confirm each meta tag can be filled in and
+  added to the global settings.
+#2773839 by DamienMcKenna: Remove 'metatag_ui' from the {system} table.
+#2773465 by DamienMcKenna: Because Page Title is now fully deprecated, promote
+  converting its settings and uninstalling it.
+#1944862 by FeyP, DamienMcKenna: Allow control over which meta tags and
+  languages are reverted on the Bulk Revert page.
+#2766315 by recrit, DamienMcKenna: metatag_entity_type_enable() would
+  incorrectly change settings in certain circumstances.
+#2774859 by DamienMcKenna: Refactored main tests to not use the submodules by
+  default.
+#2678896 by nmalinoski: Fixed redundant t() calls to fix double translation.
+#2787189 by DamienMcKenna: Added some tests to confirm that the different node
+  preview options don't interfere with saving meta tag values.
+By DamienMcKenna: Added a Known Issue for problems with Entity Token.
+#2790967 by DamienMcKenna: Added tests for taxonomy term config translations
+  using i18n.
+#2795255 by lazysoundsystem: 'disabled' was misspelled.
+By DamienMcKenna: Updated the description of content-language to clarify its
+  usage and the fact that Bing may still use it.
+#1865228 by DamienMcKenna: Moved Author meta tag to GooglePlus submodule.
+#1343914 by DamienMcKenna: Moved Publisher meta tag to GooglePlus submodule.
+#2784879 by sorinb, DamienMcKenna: Change metatag_update_7108 to use a sandbox.
+#2791963 by ttkaminski, DamienMcKenna: Don't change protocol-relative URLs in
+  image values.
+#2797069 by Internet, DamienMcKenna: Corrected the URL to Wikipedia's ICBM page.
+#2799317 by mdooley: Use a static date example for the Expires meta tag's
+  description to avoid flooding the {locales_source} table.
+#2800479 by DamienMcKenna, david.gil: Avoid showing errors if Search API is not
+  installed.
+By DamienMcKenna: Slight reordering of the main info file.
+#2663208 by DamienMcKenna, geertvd: Don't load meta tags on the /user/me page,
+  avoid problems when the Me module is installed.
+#2813429 by DamienMcKenna: Added tests for metatag_mobile.
+#2813427 by DamienMcKenna: Added support for the amphtml link tag.
+#2811735 by Stevel, DamienMcKenna: Added dependencies to all tests so that tests
+  will only be listed if those dependencies are also available.
+By stimalsina: Minor improvements to the amphtml meta tag's description.
+#2823367 by DamienMcKenna: Fixed tests after internal API change in Media.
+#2826023 by renatog, DamienMcKenna, gfcamilo: Coding standards fixes for
+  metatag.module.
+#2831030 by prince_zyxware: Fixed some minor coding standard bugs, spacing
+ issues.
+#2759461 by DamienMcKenna: hreflang=x-default is no longer removed when another
+  hreflang meta tag has the same URL, instead the other tag is removed as it
+  was supposed to be. Added a new [node:url-original] token for showing the
+  URL for the source node for translations; updated the default value for
+  the hreflang=x-default meta tag to use the new token instead of
+  [node:source:url]. Updates to many tests to allow these changes.
+#2532588 by cebasqueira, renatog, DamienMcKenna: Added new meta tags for Google
+  CSE.
+#2796701 by DrupalDano, DamienMcKenna: Some XSS tests for meta tag handling.
+#2831073 by DamienMcKenna: Added Workbench Moderation as a test dependency, for
+  future use.
+#2831822 by DamienMcKenna: Added support for the handheld mobile alternate link
+  tag, supported by Google.
+
+
 Metatag 7.x-1.17, 2016-06-30
 ----------------------------
 #2748627 by jalpesh: Corrected twitter:app:id:googleplay description.

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

@@ -13,6 +13,9 @@ LinkedIn, etc (see below).
 
 This version of the module only works with Drupal 7.28 and newer.
 
+For additional information, see the online documentation:
+  https://www.drupal.org/docs/7/modules/metatag
+
 
 Features
 --------------------------------------------------------------------------------
@@ -87,6 +90,9 @@ The primary features include:
 
 * The hreflang meta tags are available via the Metatag:hreflang submodule.
 
+* Support for meta tags specific to Google Custom Search Appliance are available
+  in the "Metatag: Google Custom Search Engine (CSE)" submodule.
+
 * A variety of favicon sizes and styles can be added to the global configuration
   using the Metatag: Favicons submodule.
 
@@ -104,8 +110,9 @@ The primary features include:
 * 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.
+* Integrates with Workbench Moderation (v1) allowing meta tags on nodes to be
+  managed through the workflow process; this custom support is not needed in
+  Workbench Moderation v3 so the extra logic is automatically ignored.
 
 * The Transliteration module (see below) is highly recommended when using image
   meta tags, e.g. og:image, to ensure that filenames are HTML-safe.
@@ -293,7 +300,10 @@ Troubleshooting / known issues
 * 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
-  module simultaneously can cause conflicts and unexpected results.
+  module simultaneously can cause conflicts and unexpected results. It is
+  strongly recommended to convert the Page Title settings to Metatag and just
+  uninstall Page Title entirely. See https://www.drupal.org/node/2774833 for
+  further details.
 * When customizing the meta tags for user pages, it is strongly recommended to
   not use the [current-user] tokens, these pertain to the person *viewing* the
   page and not e.g., the person who authored a page.
@@ -311,6 +321,10 @@ Troubleshooting / known issues
   recommended to disable the "Force language neutral aliases" setting on the
   Admin Language settings page, i.e. set the "admin_language_force_neutral"
   variable to FALSE. Failing to do so can lead to data loss in Metatag.
+* If Entity Token is installed (a dependency for Rules, Commerce and others) it
+  is possible that the token browser may not work correctly and may either
+  timeout or give an error instead of a browsable list of tokens. This is a
+  limitation of the token browser.
 
 
 Related modules
@@ -359,9 +373,10 @@ functionality:
   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
+* Real-time SEO for Drupal
   https://www.drupal.org/project/yoast_seo
-  Adds integration with the Yoast service (https://yoast.com/).
+  Uses the YoastSEO.js library and service (https://yoast.com/) to provide
+  realtime feedback on the meta tags.
 
 * Parse.ly Publishing Analytics
   https://www.drupal.org/project/parsely
@@ -385,7 +400,7 @@ References
 --------------------------------------------------------------------------------
 1: https://www.drupal.org/u/damienmckenna
 2: https://www.drupal.org/u/dave-reid
-3: http://www.mediacurrent.com/
-4: http://www.lullabot.com/
-5: http://www.acquia.com/
-6: http://www.palantir.net/
+3: https://www.mediacurrent.com/
+4: https://www.lullabot.com/
+5: https://www.acquia.com/
+6: https://www.palantir.net/

+ 91 - 15
sites/all/modules/contrib/seo/metatag/metatag.admin.inc

@@ -342,7 +342,7 @@ function metatag_config_disable($config) {
   ctools_export_crud_disable('metatag_config', $config);
 
   $label = metatag_config_instance_label($config->instance);
-  drupal_set_message(t('The meta tag defaults for @label have been disabed.', array('@label' => $label)));
+  drupal_set_message(t('The meta tag defaults for @label have been disabled.', array('@label' => $label)));
   drupal_goto();
 }
 
@@ -391,8 +391,7 @@ function metatag_bulk_revert_form() {
   foreach (entity_get_info() as $entity_type => $entity_info) {
     foreach (array_keys($entity_info['bundles']) as $bundle) {
       if (metatag_entity_supports_metatags($entity_type, $bundle)) {
-        $options[$entity_type . ':' . $bundle] =
-          $entity_info['label'] . ': ' . $entity_info['bundles'][$bundle]['label'];
+        $options[$entity_type . ':' . $bundle] = $entity_info['label'] . ': ' . $entity_info['bundles'][$bundle]['label'];
       }
     }
   }
@@ -406,6 +405,41 @@ function metatag_bulk_revert_form() {
     '#description' => t('All meta tags will be removed for all content of the selected entities.'),
   );
 
+  $metatags = metatag_get_info();
+  $options = array();
+  foreach ($metatags['tags'] as $tag_name => $tag) {
+    $options[$tag_name] = t('@group_label: @tag_label', array(
+      '@group_label' => $metatags['groups'][$tag['group']]['label'],
+      '@tag_label' => $tag['label'],
+    ));
+  }
+
+  if (count($options) > 0) {
+    $form['tags'] = array(
+      '#type' => 'checkboxes',
+      '#required' => FALSE,
+      '#title' => t('Select the tags to revert'),
+      '#description' => t('If you select any tags, only those tags will be reverted.'),
+      '#options' => $options,
+    );
+  }
+
+  $languages = language_list();
+  $options = array(
+    LANGUAGE_NONE => t('Language neutral'),
+  );
+  foreach ($languages as $language) {
+    $options[$language->language] = $language->name;
+  }
+
+  $form['languages'] = array(
+    '#type' => 'checkboxes',
+    '#required' => FALSE,
+    '#title' => t('Select the languages to revert'),
+    '#description' => t('If you select any languages, only tags for those languages will be reverted.'),
+    '#options' => $options,
+  );
+
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Revert'),
@@ -428,10 +462,13 @@ function metatag_bulk_revert_form_submit($form, &$form_state) {
     'file' => drupal_get_path('module', 'metatag') . '/metatag.admin.inc',
   );
 
+  $tags = isset($form_state['values']['tags']) ? array_filter($form_state['values']['tags']) : array();
+  $languages = isset($form_state['values']['languages']) ? array_filter($form_state['values']['languages']) : array();
+
   // Set a batch operation per entity:bundle.
   foreach (array_filter($form_state['values']['update']) as $option) {
     list($entity_type, $bundle) = explode(':', $option);
-    $batch['operations'][] = array('metatag_bulk_revert_batch_operation', array($entity_type, $bundle));
+    $batch['operations'][] = array('metatag_bulk_revert_batch_operation', array($entity_type, $bundle, $tags, $languages));
   }
 
   batch_set($batch);
@@ -440,7 +477,7 @@ function metatag_bulk_revert_form_submit($form, &$form_state) {
 /**
  * Batch callback: delete custom metatags for selected bundles.
  */
-function metatag_bulk_revert_batch_operation($entity_type, $bundle, &$context) {
+function metatag_bulk_revert_batch_operation($entity_type, $bundle, $tags, $languages, &$context) {
   if (!isset($context['sandbox']['current'])) {
     $context['sandbox']['count'] = 0;
     $context['sandbox']['current'] = 0;
@@ -478,14 +515,53 @@ function metatag_bulk_revert_batch_operation($entity_type, $bundle, &$context) {
   foreach ($entity_ids as $entity_id) {
     $metatags = metatag_metatags_load($entity_type, $entity_id);
     if (!empty($metatags)) {
-      db_delete('metatag')->condition('entity_type', $entity_type)
-        ->condition('entity_id', $entity_id)
-        ->execute();
-      metatag_metatags_cache_clear($entity_type, $entity_id);
-      $context['results'][] = t('Reverted metatags for @bundle with id @id.', array(
-        '@bundle' => $entity_type . ': ' . $bundle,
-        '@id' => $entity_id,
-      ));
+      $reset = FALSE;
+      if (empty($tags)) {
+        // All tags should be reset, so we just delete any records from the db.
+        $query = db_delete('metatag')
+          ->condition('entity_type', $entity_type)
+          ->condition('entity_id', $entity_id);
+        if (!empty($languages)) {
+          $query->condition('language', $languages, 'IN');
+        }
+        $query->execute();
+        metatag_metatags_cache_clear($entity_type, $entity_id);
+        $reset = TRUE;
+      }
+      else {
+        // Iterate over tags and unset those, that we want to reset.
+        $needs_reset = FALSE;
+        foreach ($metatags as $metatags_language => $metatags_tags) {
+          if (empty($languages) || in_array($metatags_language, $languages)) {
+            foreach ($metatags_tags as $metatags_tag => $metatags_value) {
+              if (in_array($metatags_tag, $tags)) {
+                unset($metatags[$metatags_language][$metatags_tag]);
+                $needs_reset = TRUE;
+              }
+            }
+          }
+        }
+        // Save modified metatags.
+        if ($needs_reset) {
+          // We don't have a revision id, so we'll get the active one.
+          // Unfortunately, the only way of getting the active revision ID is to
+          // first load the entity, and then extract the ID. This is a bit
+          // performance intensive, but it seems to be the only way of doing it.
+          $entities = entity_load($entity_type, array($entity_id));
+          if (!empty($entities[$entity_id])) {
+            // We only care about the revision_id.
+            list(, $revision_id, ) = entity_extract_ids($entity_type, $entities[$entity_id]);
+          }
+          metatag_metatags_save($entity_type, $entity_id, $revision_id, $metatags, $bundle);
+          $reset = TRUE;
+        }
+      }
+      if ($reset) {
+        $context['results'][] = t('Reverted metatags for @bundle with id @id.', array(
+          '@bundle' => $entity_type . ': ' . $bundle,
+          '@id' => $entity_id,
+        ));
+      }
     }
   }
 
@@ -674,8 +750,8 @@ function metatag_admin_settings_form() {
   $form['advanced']['metatag_cache_output'] = array(
     '#type' => 'checkbox',
     '#title' => t('Cache meta tag output'),
-    '#description' => t('Disabling this will cause all meta tag output to be generated uniquely for each page load. Currently only affects entities. Note: the entity configuration and output for other types of pages will still be cached, but this can stop the {cache_metatag} table from growing out of control in some scenarios.'),
-    '#default_value' => variable_get('metatag_cache_output', TRUE),
+    '#description' => t('Enabling this will cause all meta tag output to be cached for each page, which may aid with site performance in some circumstances. Should not be used if there are paths which include user-specific meta tags for the <em>current user</em>, they may lead to information about the user being accidentally leaked; this problem does not affect the "user/[uid]" pages. It may also cause the {cache_metatag} cache table to grow to be extremely large in certain circumstances.'),
+    '#default_value' => variable_get('metatag_cache_output', FALSE),
   );
 
   $form['advanced']['metatag_leave_core_tags'] = array(

+ 17 - 0
sites/all/modules/contrib/seo/metatag/metatag.api.php

@@ -435,3 +435,20 @@ function hook_metatag_i18n_context_alter(&$context, $tag_name) {
     $context = '';
   }
 }
+
+/**
+ * Allow modules to overide the expiration of metatag caches.
+ *
+ * By default Metatag caches everything as CACHE_PERMANENT, this alter allows to
+ * change that.
+ *
+ * @param $expire
+ *   The expire value to change.
+ * @param $cid
+ *   The cid about to be cached.
+ * @param $data
+ *   The data to be cached.
+ */
+function hook_metatag_cache_set_expire_alter(&$expire, $cid, $data) {
+  $expire = CACHE_TEMPORARY;
+}

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

@@ -165,7 +165,10 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
   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);
+      // In https://www.drupal.org/node/2129273 media_wysiwyg_filter() was
+      // changed to require several additional arguments.
+      $langcode = language_default('language');
+      $value = media_wysiwyg_filter($value, NULL, NULL, $langcode, NULL, NULL);
     }
 
     // Specifically replace encoded spaces, because some WYSIWYG editors are
@@ -199,8 +202,9 @@ class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
    */
   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) {
+    // ones that are relative to the Drupal root path; ignore protocol-relative
+    // URLs.
+    if (strpos($url, base_path()) === 0 && strpos($url, '//') !== 0) {
       // Logic:
       // * Get the length of the base_path(), 
       // * Get a portion of the image's path starting from the position equal

+ 59 - 30
sites/all/modules/contrib/seo/metatag/metatag.info

@@ -5,80 +5,109 @@ 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[] = drupal:system (>= 7.28)
 
 ; CTools is required.
-dependencies[] = ctools
+dependencies[] = ctools:ctools
 
 ; Requires Token; v7.x-1.6 is highly recommended due to bugs with certain tags
 ; when using older versions.
-dependencies[] = token
+dependencies[] = token:token
 
 configure = admin/config/search/metatags
 
+; The main classes.
 files[] = metatag.inc
+
+; Defines the basic meta tags.
 files[] = metatag.migrate.inc
+
+; Search API integration.
 files[] = metatag.search_api.inc
 
+
 ; Tests.
 files[] = tests/metatag.helper.test
+
 ; Basic configuration handling.
 files[] = tests/metatag.unit.test
+
+; Basic tag testing.
+files[] = tests/metatag.tags_helper.test
+files[] = tests/metatag.tags.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
+
+; The custom Bulk Revert functionality.
+files[] = tests/metatag.bulk_revert.test
+
 ; String handling.
 files[] = tests/metatag.string_handling.test
 files[] = tests/metatag.string_handling_with_i18n.test
+
+; XSS testing.
+files[] = tests/metatag.xss.test
+
+; Output caching.
+files[] = tests/metatag.output_caching.test
+
 ; Images need specia attention.
+test_dependencies[] = devel:devel
+test_dependencies[] = imagecache_token:imagecache_token
 files[] = tests/metatag.image.test
+
 ; Internationalization & translation.
+test_dependencies[] = entity_translation:entity_translation
+test_dependencies[] = i18n:i18n
 files[] = tests/metatag.locale.test
+files[] = tests/metatag.node.with_i18n.test
+files[] = tests/metatag.term.with_i18n.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 Me.
+test_dependencies[] = me:me
+files[] = tests/metatag.with_me.test
+
 ; Basic integration with Media.
+test_dependencies[] = file_entity:file_entity
+test_dependencies[] = media:media (>= 2.0, < 3.0)
 files[] = tests/metatag.with_media.test
+
 ; Basic integration with Panels.
+test_dependencies[] = panels:panels
 files[] = tests/metatag.with_panels.test
+
 ; Basic integration with Profile2.
+test_dependencies[] = profile2:profile2
 files[] = tests/metatag.with_profile2.test
+
 ; Basic integration with Search API.
+test_dependencies[] = entity:entity
+test_dependencies[] = search_api: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
+; Integration with Workbench Moderation.
+test_dependencies[] = workbench_moderation:workbench_moderation
+files[] = tests/metatag.with_workbench_moderation.test
 
-; 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
+; Basic integration with Views.
+test_dependencies[] = views:views
+files[] = tests/metatag.with_views.test
 
-; This is required for the Profile2-related tests.
-test_dependencies[] = profile2
+; Other test dependencies.
+test_dependencies[] = context:context
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 404 - 45
sites/all/modules/contrib/seo/metatag/metatag.install

@@ -78,10 +78,10 @@ function metatag_requirements($phase) {
     // Add a note if Page Title is also installed.
     if (module_exists('page_title')) {
       $requirements['metatag_page_title'] = array(
-        'severity' => REQUIREMENT_INFO,
+        'severity' => REQUIREMENT_WARNING,
         'title' => 'Metatag',
-        'value' => $t('Possible conflicts with Page Title module'),
-        'description' => $t('The Metatag module is able to customize page titles so running the Page Title module simultaneously can lead to complications.'),
+        'value' => $t('Page Title module should be removed'),
+        'description' => $t('The Metatag module is able to customize page titles, so running the Page Title module simultaneously can lead to complications. Please follow the instructions to <a href="@page">convert the Page Title settings</a> and uninstall the module.', array('@page' => 'https://www.drupal.org/node/2774833')),
       );
     }
 
@@ -181,6 +181,36 @@ function metatag_requirements($phase) {
         );
       }
     }
+
+    // If Workbench Moderation is installed, show a message if it is out of
+    // date.
+    if (module_exists('workbench_moderation')) {
+      $wm_module = $module_data['workbench_moderation'];
+      // If the version string is not present then it means the module is
+      // running from git, which means it can't be compared against.
+      if (!empty($wm_module->info['version'])) {
+        // Versions are in the format 7.x-1.y, so split the string up to find
+        // the 'y' portion.
+        $version = explode('-', $wm_module->info['version']);
+        if (isset($version[1])) {
+          list($major, $minor) = explode('.', $version[1]);
+        }
+        // If the version string couldn't be extracted correctly, assume that
+        // an incorrect version is installed.
+        else {
+          $major = 0;
+        }
+      }
+      // If v3.x is not installed, give a message.
+      if ($major < 3) {
+        $requirements['metatag_wm_version'] = array(
+          'severity' => REQUIREMENT_INFO,
+          'title' => 'Metatag',
+          'value' => $t('Workbench Moderation module is out of date.'),
+          'description' => $t('It is recommended to use <a href="https://www.drupal.org/project/workbench_moderation">Workbench Moderation module</a> v7.x-3.0 or newer.'),
+        );
+      }
+    }
   }
 
   return $requirements;
@@ -455,7 +485,7 @@ function metatag_enable() {
 }
 
 /**
- * Replace one meta tag with another in the entity records.
+ * Replace one meta tag's with another in the entity records.
  *
  * @param array $sandbox
  *   A Batch API sandbox, passed by reference.
@@ -464,18 +494,18 @@ function metatag_enable() {
  * @param string $new_tag
  *   The meta tag that replaces the old one.
  */
-function metatag_update_replace_meta_tag(&$sandbox, $old_tag, $new_tag) {
+function metatag_update_replace_entity_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')
+      ->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.');
+      return t('No Metatag entity records needed to have the "@tag" meta tag renamed.', array('@tag' => $old_tag));
     }
 
     $sandbox['max'] = $records_count;
@@ -513,11 +543,102 @@ function metatag_update_replace_meta_tag(&$sandbox, $old_tag, $new_tag) {
 
   if (!empty($count)) {
     $sandbox['progress'] += $count;
-    $sandbox['#finished'] = min(0.99, $sandbox['progress'] / $sandbox['max']);
+    // In some cases the query yields results that cannot be fixed and we would
+    // run into an infinite loop. Stop immediately if we processed all records.
+    if ($sandbox['progress'] >= $sandbox['max']) {
+      $sandbox['#finished'] = TRUE;
+    }
+    else {
+      $sandbox['#finished'] = $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']));
+    $sandbox['#finished'] = TRUE;
+    return t('Converted the "@old_tag" meta tag for @count entity records to "@new_tag" meta tag.', array('@old_tag' => $old_tag, '@new_tag' => $new_tag, '@count' => $sandbox['progress']));
+  }
+}
+
+/**
+ * Replace one meta tag's value in the entity records.
+ *
+ * @param array $sandbox
+ *   A Batch API sandbox, passed by reference.
+ * @param string $meta_tag
+ *   The meta tag that needs its value replaced.
+ * @param string $old_value
+ *   The meta tag value that is to be replaced.
+ * @param string $new_value
+ *   The meta tag value that replaces the old one.
+ */
+function metatag_update_replace_entity_value(&$sandbox, $meta_tag, $old_value, $new_value) {
+  // The condition used for both queries.
+  $db_and = db_and();
+  $db_and->condition('m.data', '%' . db_like('"' . $meta_tag . '"') . '%', 'LIKE');
+  $db_and->condition('m.data', '%' . db_like('"' . $old_value . '"') . '%', 'LIKE');
+
+  if (!isset($sandbox['progress'])) {
+    // Count of all {metatag} records that contained an entry for the old meta
+    // tag value
+    $records_count = db_select('metatag', 'm')
+      ->condition($db_and)
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+
+    if (empty($records_count)) {
+      return t('No Metatag entity records needed to have the "@tag" meta tag "@old_value" value replaced.', array('@tag' => $meta_tag, '@old_value' => $old_value));
+    }
+
+    $sandbox['max'] = $records_count;
+    $sandbox['progress'] = 0;
+
+    // Keep track of the number of replaced values separately.
+    $sandbox['count'] = 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($db_and)
+    ->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[$meta_tag])) {
+      $record->data[$meta_tag]['value'] = str_replace($old_value, $new_value, $record->data[$meta_tag]['value']);
+      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));
+
+      $sandbox['count']++;
+    }
+
+    $sandbox['progress']++;
+  }
+
+  if (!empty($count)) {
+    // In some cases the query yields results that cannot be fixed and we would
+    // run into an infinite loop. Stop immediately if we processed all records.
+    if ($sandbox['progress'] >= $sandbox['max']) {
+      $sandbox['#finished'] = TRUE;
+    }
+    else {
+      $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+    }
+  }
+  else {
+    $sandbox['#finished'] = TRUE;
+    return t('Replaced the value of @count entity records for the "@meta_tag" meta tag.', array('@meta_tag' => $meta_tag, '@count' => $sandbox['count']));
   }
 }
 
@@ -529,7 +650,7 @@ function metatag_update_replace_meta_tag(&$sandbox, $old_tag, $new_tag) {
  * @param string $new_tag
  *   The meta tag that replaces the old one.
  */
-function metatag_update_replace_config($old_tag, $new_tag) {
+function metatag_update_replace_config_tag($old_tag, $new_tag) {
   // Find all {metatag_config} records that contained an entry for the old meta
   // tag.
   $records = db_select('metatag_config', 'm')
@@ -537,11 +658,11 @@ function metatag_update_replace_config($old_tag, $new_tag) {
     ->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.');
+  $none_message = t('No Metatag configuration records needed to have the "@tag" meta tag fixed. That said, there may be other configurations elsewhere that do need updating.', array('@tag' => $old_tag));
 
   // Loop over the values and correct them.
   if ($records->rowCount() == 0) {
-    drupal_set_message($none_message);
+    $message = $none_message;
   }
   else {
     $keys = array('cid');
@@ -552,18 +673,137 @@ function metatag_update_replace_config($old_tag, $new_tag) {
       $record->config = unserialize($record->config);
       if (isset($record->config[$old_tag])) {
         $record->config[$new_tag] = $record->config[$old_tag];
-        unset($record->config['og:video']);
+        unset($record->config[$old_tag]);
         drupal_write_record('metatag_config', $record, $keys);
         $counter++;
       }
     }
+    if ($counter == 0) {
+      $message = $none_message;
+    }
+    else {
+      $message = t('Converted the "@old_tag" meta tag for @count configurations to the new "@new_tag" meta tag.', array('@old_tag' => $old_tag, '@new_tag' => $new_tag, '@count' => $counter));
+    }
+  }
+
+  // Clear all Metatag caches.
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  drupal_static_reset('metatag_config_load_with_defaults');
+  drupal_static_reset('metatag_entity_supports_metatags');
+  drupal_static_reset('metatag_config_instance_info');
+  drupal_static_reset('metatag_get_info');
+  ctools_include('export');
+  ctools_export_load_object_reset('metatag_config');
+
+  return $message;
+}
+
+/**
+ * Replace one meta tag with another in the configs.
+ *
+ * @param string $meta_tag
+ *   The meta tag that needs its value replaced.
+ * @param string $old_value
+ *   The meta tag value that is to be replaced.
+ * @param string $new_value
+ *   The meta tag value that replaces the old one.
+ */
+function metatag_update_replace_config_value($meta_tag, $old_value, $new_value) {
+  // Find all {metatag_config} records that contained an entry for the old meta
+  // tag.
+  $db_and = db_and();
+  $db_and->condition('m.config', '%' . db_like('"' . $meta_tag . '"') . '%', 'LIKE');
+  $db_and->condition('m.config', '%' . db_like('"' . $old_value . '"') . '%', 'LIKE');
+  $records = db_select('metatag_config', 'm')
+    ->fields('m', array('cid', 'config'))
+    ->condition($db_and)
+    ->execute();
+  // This message will be returned if nothing needed to be updated.
+  $none_message = t('No Metatag configuration records needed to have the "@tag" meta tag values updated. That said, there may be other configurations elsewhere that do need updating.', array('@tag' => $meta_tag));
+
+  // Loop over the values and correct them.
+  if ($records->rowCount() == 0) {
+    $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[$meta_tag])) {
+        $record->config[$meta_tag]['value'] = str_replace($old_value, $new_value, $record->config[$meta_tag]['value']);
+        drupal_write_record('metatag_config', $record, $keys);
+        $counter++;
+      }
+    }
+    if ($counter == 0) {
+      $message = $none_message;
+    }
+    else {
+      $message = t('Replaced the value of @count entity records for the "@meta_tag" meta tag.', array('@meta_tag' => $meta_tag, '@count' => $counter));
+    }
+  }
+
+  // Clear all Metatag caches.
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  drupal_static_reset('metatag_config_load_with_defaults');
+  drupal_static_reset('metatag_entity_supports_metatags');
+  drupal_static_reset('metatag_config_instance_info');
+  drupal_static_reset('metatag_get_info');
+  ctools_include('export');
+  ctools_export_load_object_reset('metatag_config');
+
+  return $message;
+}
+
+/**
+ * Remove a specific meta tag from all configs.
+ *
+ * @param string $$tag_name
+ *   The meta tag that is to be removed.
+ */
+function metatag_update_delete_config($tag_name) {
+  // Find all {metatag_config} records that contained an entry for the meta tag.
+  $records = db_select('metatag_config', 'm')
+    ->fields('m', array('cid', 'config'))
+    ->condition('m.config', '%' . db_like('"' . $tag_name . '"') . '%', 'LIKE')
+    ->execute();
+  // This message will be returned if nothing needed to be updated.
+  $none_message = t('No Metatag configuration records needed to have the "@tag" meta tag removed.', array('@tag' => $tag_name));
+
+  // Loop over the values and correct them.
+  if ($records->rowCount() == 0) {
+    drupal_set_message($none_message);
+  }
+  else {
+    // Loop over the values and correct them.
+    $counter = 0;
+    foreach ($records as $record) {
+      $record->config = unserialize($record->config);
+      if (isset($record->config[$tag_name])) {
+        unset($record->config[$tag_name]);
+        drupal_write_record('metatag_config', $record, array('cid'));
+        $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)));
+      drupal_set_message(t('Removed the "@tag" meta tag for @count configurations.', array('@tag' => $tag_name, '@count' => $counter)));
     }
   }
+
+  // Clear all Metatag caches.
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  drupal_static_reset('metatag_config_load_with_defaults');
+  drupal_static_reset('metatag_entity_supports_metatags');
+  drupal_static_reset('metatag_config_instance_info');
+  drupal_static_reset('metatag_get_info');
+  ctools_include('export');
+  ctools_export_load_object_reset('metatag_config');
 }
 
 /**
@@ -915,7 +1155,12 @@ function metatag_update_7011(&$sandbox) {
   // 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['progress'] >= $sandbox['max']) {
+    $sandbox['#finished'] = TRUE;
+  }
+  else {
+    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+  }
 
   if ($sandbox['#finished']) {
     // Clear all caches so the fixed data will be reloaded.
@@ -1073,7 +1318,12 @@ function metatag_update_7013(&$sandbox) {
   // 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['progress'] >= $sandbox['max']) {
+    $sandbox['#finished'] = TRUE;
+  }
+  else {
+    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+  }
 
   if ($sandbox['#finished']) {
     // Clear all caches so the fixed data will be reloaded.
@@ -1558,7 +1808,12 @@ function metatag_update_7018(&$sandbox) {
   // 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['progress'] >= $sandbox['max']) {
+    $sandbox['#finished'] = TRUE;
+  }
+  else {
+    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+  }
 
   if ($sandbox['#finished']) {
     // Clear all caches so the fixed data will be reloaded.
@@ -1645,7 +1900,7 @@ function metatag_update_7025() {
 function metatag_update_7026(&$sandbox) {
   $old_tag = 'copyright';
   $new_tag = 'rights';
-  return metatag_update_replace_meta_tag($sandbox, $old_tag, $new_tag);
+  return metatag_update_replace_entity_tag($sandbox, $old_tag, $new_tag);
 }
 
 /**
@@ -1688,7 +1943,7 @@ function metatag_update_7030() {
 function metatag_update_7031() {
   $old_tag = 'copyright';
   $new_tag = 'rights';
-  return metatag_update_replace_config($old_tag, $new_tag);
+  return metatag_update_replace_config_tag($old_tag, $new_tag);
 }
 
 /**
@@ -1926,7 +2181,12 @@ function metatag_update_7040(&$sandbox) {
   // 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['progress'] >= $sandbox['max']) {
+    $sandbox['#finished'] = TRUE;
+  }
+  else {
+    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+  }
 
   // Only display a status message if process finished.
   if ($sandbox['#finished'] === TRUE) {
@@ -2109,7 +2369,12 @@ function metatag_update_7104(&$sandbox) {
   // 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['progress'] >= $sandbox['max']) {
+    $sandbox['#finished'] = TRUE;
+  }
+  else {
+    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+  }
 
   if ($sandbox['#finished']) {
     // Clear all caches so the fixed data will be reloaded.
@@ -2167,7 +2432,7 @@ function metatag_update_7107() {
 /**
  * Delete output translations if it's disabled.
  */
-function metatag_update_7108() {
+function metatag_update_7108(&$sandbox) {
   if (!module_exists('locale') || !db_table_exists('locales_source')) {
     return t('No translations to fix as the locale system is not enabled.');
   }
@@ -2177,31 +2442,85 @@ function metatag_update_7108() {
     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();
+  $limit = 100;
 
-  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();
+  // When ran through Drush it's Ok to process a larger number of objects at a
+  // time.
+  if (drupal_is_cli()) {
+    $limit = 500;
+  }
+
+  // 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;
+
+    $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 7108: No nodes need the translation entity string fixed.', array(), WATCHDOG_INFO);
+      if (drupal_is_cli()) {
+        drupal_set_message(t('Update 7108: No nodes need the translation entity string fixed.'));
+      }
+      return t('No nodes need the Metatag language values fixed.');
     }
-    db_delete('locales_target')
-      ->condition('lid', $lids)
-      ->execute();
-    db_delete('locales_source')
-      ->condition('lid', $lids)
+
+    // 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 7108: !count records to update.', array('!count' => $sandbox['max']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7108: !count records to update.', array('!count' => $sandbox['max'])));
+    }
+  }
+
+  // Get a batch of records that need to be fixed.
+  $records = db_query_range("SELECT lid
+    FROM {locales_source}
+    WHERE textgroup = 'metatag'
+    AND context LIKE 'output:%'", 0, $limit);
+
+  $lids = $records->fetchCol();
+  $count = count($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, 'IN')
       ->execute();
-    return t('Metatag: Removed @count output translation records that were not needed.', array('@count' => count($lids)));
+  }
+  db_delete('locales_target')
+    ->condition('lid', $lids, 'IN')
+    ->execute();
+  db_delete('locales_source')
+    ->condition('lid', $lids, 'IN')
+    ->execute();
+
+  $sandbox['progress'] = $sandbox['progress'] + $count;
+
+  // 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.
+  if ($sandbox['progress'] >= $sandbox['max']) {
+    $sandbox['#finished'] = TRUE;
   }
   else {
-    return t('Metatag; No output translation records needed to be removed.');
+    $sandbox['#finished'] = $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('Deleted output translation if disabled for  @count items.', array('@count' => $sandbox['progress']));
   }
 }
 
@@ -2212,7 +2531,7 @@ 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);
+  return metatag_update_replace_entity_tag($sandbox, $old_tag, $new_tag);
 }
 
 /**
@@ -2222,5 +2541,45 @@ 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);
+  return metatag_update_replace_config_tag($old_tag, $new_tag);
+}
+
+/**
+ * Remove the "metatag_ui" record from the {system} table.
+ */
+function metatag_update_7111() {
+  db_delete('system')
+    ->condition('name', 'metatag_ui')
+    ->execute();
+}
+
+/**
+ * The Publisher meta tag is now part of the Google Plus submodule.
+ */
+function metatag_update_7112() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  drupal_set_message(t('The Publisher meta tag is now part of the Google Plus submodule.'));
+}
+
+/**
+ * Clear all metatag output caches if output caching is disabled.
+ */
+function metatag_update_7113() {
+  if (variable_get('metatag_cache_output', TRUE) == FALSE) {
+    cache_clear_all('output:', 'cache_metatag', TRUE);
+  }
+}
+
+/**
+ * Disable output caching.
+ */
+function metatag_update_7114() {
+  variable_del('metatag_cache_output');
+}
+
+/**
+ * Clear all metatag output caches, it will be rebuild if needed.
+ */
+function metatag_update_7115() {
+  cache_clear_all('output:', 'cache_metatag', TRUE);
 }

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

@@ -326,28 +326,6 @@ function metatag_metatag_info() {
     ),
   );
 
-  $info['tags']['publisher'] = array(
-    'label' => t('Publisher URL'),
-    'description' => '',
-    'class' => 'DrupalLinkMetaTag',
-    'group' => 'advanced',
-    'weight' => ++$weight,
-    'devel_generate' => array(
-      'type' => 'url',
-    ),
-  );
-
-  $info['tags']['author'] = array(
-    'label' => t('Author URL'),
-    'description' => t("Used by some search engines to confirm authorship of the content on a page. Should be either the full URL for the author's Google+ profile page or a local page with information about the author."),
-    'class' => 'DrupalLinkMetaTag',
-    'group' => 'advanced',
-    'weight' => ++$weight,
-    'devel_generate' => array(
-      'type' => 'url',
-    ),
-  );
-
   $info['tags']['original-source'] = array(
     'label' => t('Original Source'),
     'description' => '',
@@ -384,7 +362,7 @@ function metatag_metatag_info() {
 
   $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)."),
+    'description' => t("Used to define this page's language code. May be the two letter language code, e.g. \"de\" for German, or the two letter code with a dash and the two letter ISO country code, e.g. \"de-AT\" for German in Austria. Still used by Bing."),
     'class' => 'DrupalTextMetaTag',
     'group' => 'advanced',
     'weight' => ++$weight,
@@ -420,7 +398,7 @@ function metatag_metatag_info() {
 
   $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>.'),
+    'description' => t('Geo-spatial information in "latitude, longitude" format, e.g. "50.167958, -97.133185"; <a href="https://en.wikipedia.org/wiki/ICBM_address">see Wikipedia for details</a>.'),
     'class' => 'DrupalTextMetaTag',
     'group' => 'advanced',
     'weight' => ++$weight,
@@ -472,7 +450,7 @@ function metatag_metatag_info() {
 
   $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"))),
+    '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. 'Thu, 01 Sep 2016 00:12:56 GMT'. Set to '0' to stop the page being cached entirely.", array('@rfc' => 'http://www.csgnetwork.com/timerfc1123calc.html')),
     'class' => 'DrupalTextMetaTag',
     'weight' => ++$weight,
     'group' => 'advanced',

+ 178 - 92
sites/all/modules/contrib/seo/metatag/metatag.module

@@ -1,4 +1,5 @@
 <?php
+
 /**
  * @file
  * Primary hook implementations for Metatag.
@@ -329,7 +330,14 @@ function metatag_config_load_multiple(array $instances) {
     foreach ($configs as $instance => &$config) {
       foreach ($config->config as $tag => &$value) {
         if (isset($value['value']) && is_string($value['value'])) {
-          $value['value'] = i18n_string_translate(array('metatag', 'metatag_config', $instance, $tag), $value['value'], $options);
+          $value['value'] = i18n_string_translate(array(
+            'metatag',
+            'metatag_config',
+            $instance,
+            $tag,
+          ),
+          $value['value'],
+          $options);
         }
       }
     }
@@ -432,7 +440,7 @@ function metatag_config_cache_clear() {
  * @param mixed $revision_id
  *   Optional revision ID to load instead of the entity ID.
  *
- * @return
+ * @return array
  *   An array of tag data keyed by language for the entity's current active
  *   revision.
  */
@@ -445,7 +453,7 @@ function metatag_metatags_load($entity_type, $entity_id, $revision_id = NULL) {
     $entities = entity_load($entity_type, array($entity_id));
     if (!empty($entities[$entity_id])) {
       // We only care about the revision_id.
-      list(, $revision_id, ) = entity_extract_ids($entity_type, $entities[$entity_id]);
+      list(, $revision_id,) = entity_extract_ids($entity_type, $entities[$entity_id]);
     }
   }
 
@@ -470,10 +478,10 @@ function metatag_metatags_load($entity_type, $entity_id, $revision_id = NULL) {
  *   The entity type to load.
  * @param array $entity_ids
  *   The list of entity IDs.
- * @param array $revision_id
+ * @param array $revision_ids
  *   Optional revision ID to load instead of the entity ID.
  *
- * @return
+ * @return array
  *   An array of tag data, keyed by entity ID, revision ID and language.
  */
 function metatag_metatags_load_multiple($entity_type, array $entity_ids, array $revision_ids = array()) {
@@ -518,9 +526,7 @@ function metatag_metatags_load_multiple($entity_type, array $entity_ids, array $
   // Get all translations of tag data for this entity.
   $query = db_select('metatag', 'm')
     ->fields('m', array('entity_id', 'revision_id', 'language', 'data'))
-    ->condition('m.entity_type', $entity_type)
-    ->orderBy('entity_id')
-    ->orderBy('revision_id');
+    ->condition('m.entity_type', $entity_type);
   // Filter by revision_ids if they are available. If not, filter by entity_ids.
   if (!empty($revision_ids)) {
     $query->condition('m.revision_id', $revision_ids, 'IN');
@@ -575,9 +581,11 @@ function metatag_metatags_load_multiple($entity_type, array $entity_ids, array $
  *         'value' => "[node:field_thumbnail]",
  *       ),
  *     ),
- *   );
+ *   );.
+ * @param string|null $bundle
+ *   The bundle of the entity that is being saved. Optional.
  */
-function metatag_metatags_save($entity_type, $entity_id, $revision_id, $metatags) {
+function metatag_metatags_save($entity_type, $entity_id, $revision_id, $metatags, $bundle = NULL) {
   // Check that $entity_id is numeric because of Entity API and string IDs.
   if (!is_numeric($entity_id)) {
     return;
@@ -588,17 +596,17 @@ function metatag_metatags_save($entity_type, $entity_id, $revision_id, $metatags
     return;
   }
 
-  // Verify that the entity can be loaded.
-  $entity = entity_load($entity_type, array($entity_id));
-  if (empty($entity)) {
-    return;
+  // Verify the entity bundle is supported, if not available just check the
+  // entity type.
+  if (!empty($bundle)) {
+    if (!metatag_entity_supports_metatags($entity_type, $bundle)) {
+      return;
+    }
   }
-  $entity = reset($entity);
-  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
-
-  // Don't do anything if the entity bundle is not supported.
-  if (!metatag_entity_supports_metatags($entity_type, $bundle)) {
-    return;
+  else {
+    if (!metatag_entity_supports_metatags($entity_type)) {
+      return;
+    }
   }
 
   // The revision_id must be a numeric value; some entities use NULL for the
@@ -672,13 +680,13 @@ function metatag_metatags_save($entity_type, $entity_id, $revision_id, $metatags
 /**
  * Delete an entity's tags.
  *
- * @param $entity_type
- *   The entity type
- * @param $entity_id
- *   The entity's ID
- * @param $revision_id
+ * @param string $entity_type
+ *   The entity type.
+ * @param int $entity_id
+ *   The entity's ID.
+ * @param int $revision_id
  *   The entity's VID.
- * @param $langcode
+ * @param string $langcode
  *   The language ID of the entry to delete. If left blank, all language
  *   entries for this entity will be deleted.
  */
@@ -699,11 +707,11 @@ function metatag_metatags_delete($entity_type, $entity_id, $revision_id = NULL,
  *   The list of IDs.
  * @param array $revision_ids
  *   An optional list of revision IDs; if omitted all revisions will be deleted.
- * @param $langcode
+ * @param string $langcode
  *   The language ID of the entities to delete. If left blank, all language
  *   entries for the enities will be deleted.
  *
- * @return boolean
+ * @return bool
  *   If any problems were encountered will return FALSE, otherwise TRUE.
  */
 function metatag_metatags_delete_multiple($entity_type, array $entity_ids, array $revision_ids = array(), $langcode = NULL) {
@@ -829,7 +837,15 @@ function metatag_entity_load($entities, $entity_type) {
     }
   }
   catch (Exception $e) {
-    watchdog('metatag', 'Error loading meta tag data, do the <a href="@update">database updates</a> need to be run? The error occurred when loading record(s) %ids for the %type entity type. The error message was: %error', array('@update' => base_path() . 'update.php', '%ids' => implode(', ', array_keys($entities)), '%type' => $entity_type, '%error' => $e->getMessage()), WATCHDOG_WARNING);
+    watchdog('metatag', 'Error loading meta tag data, do the <a href="@update">database updates</a> need to be run? The error occurred when loading record(s) %ids for the %type entity type. The error message was: %error',
+      array(
+        '@update' => base_path() . 'update.php',
+        '%ids' => implode(', ', array_keys($entities)),
+        '%type' => $entity_type,
+        '%error' => $e->getMessage(),
+      ),
+    WATCHDOG_WARNING);
+
     // Don't display the same message twice for Drush.
     if (drupal_is_cli()) {
       drupal_set_message(t('Run your updates, like drush updb.'));
@@ -877,12 +893,12 @@ function metatag_entity_insert($entity, $entity_type) {
       unset($entity->metatags[LANGUAGE_NONE]);
     }
 
-    // Support for Workbench Moderation v1.
+    // Support for Workbench Moderation.
     if ($entity_type == 'node' && _metatag_isdefaultrevision($entity)) {
       return;
     }
 
-    metatag_metatags_save($entity_type, $entity_id, $revision_id, $entity->metatags);
+    metatag_metatags_save($entity_type, $entity_id, $revision_id, $entity->metatags, $bundle);
   }
 }
 
@@ -937,13 +953,13 @@ function metatag_entity_update($entity, $entity_type) {
       }
     }
 
-    // Support for Workbench Moderation v1.
+    // Support for Workbench Moderation.
     if ($entity_type == 'node' && _metatag_isdefaultrevision($entity)) {
       return;
     }
 
     // Save the record.
-    metatag_metatags_save($entity_type, $entity_id, $revision_id, $entity->metatags);
+    metatag_metatags_save($entity_type, $entity_id, $revision_id, $entity->metatags, $bundle);
   }
   else {
     // Still ensure the meta tag output is cached.
@@ -973,10 +989,8 @@ function metatag_field_attach_delete_revision($entity_type, $entity) {
  *
  * @param object $entity
  *   The entity object to generate the metatags instance name for.
- *
  * @param string $entity_type
  *   The entity type of the entity.
- *
  * @param string $bundle
  *   The bundle of the entity.
  *
@@ -1050,8 +1064,9 @@ function metatag_entity_view($entity, $entity_type, $view_mode, $langcode, $forc
  *   TRUE if metatags can be loaded from and saved to the cache. FALSE if the
  *   cache should be bypassed.
  *
- * @return array
+ * @return mixed
  *   A renderable array of metatags for the given entity.
+ *   If this entity object isn't allowed meta tags, return FALSE (empty).
  */
 function metatag_generate_entity_metatags($entity, $entity_type, $langcode = NULL, $view_mode = 'full', $cached = TRUE) {
   // Obtain some details of the entity that are needed elsewhere.
@@ -1107,7 +1122,7 @@ function metatag_generate_entity_metatags($entity, $entity_type, $langcode = NUL
   $metatag_variants = array();
 
   // Caching is enabled.
-  if ($cached && variable_get('metatag_cache_output', TRUE)) {
+  if ($cached && variable_get('metatag_cache_output', FALSE)) {
     // All possible variants of the metatags for this entity are stored in a
     // single cache entry.
     $cid = "output:$entity_type:$entity_id";
@@ -1210,7 +1225,6 @@ function metatags_get_entity_metatags($entity_id, $entity_type, $langcode = NULL
 function metatag_metatags_view($instance, array $metatags = array(), array $options = array()) {
   $output = array();
 
-
   // Convert language codes to a language object.
   if (isset($options['language']) && is_string($options['language'])) {
     $languages = language_list();
@@ -1265,7 +1279,7 @@ function metatag_metatags_view($instance, array $metatags = array(), array $opti
 /**
  * Get the pager string for the current page.
  *
- * return @string
+ * @return string
  *   Returns a string based upon the 'metatag_pager_string' variable and the
  *   current page number.
  */
@@ -1279,6 +1293,9 @@ function metatag_get_current_pager() {
   }
 }
 
+/**
+ * Returns metatags values.
+ */
 function metatag_metatags_values($instance, array $metatags = array(), array $options = array()) {
   $values = array();
 
@@ -1441,13 +1458,15 @@ function metatag_metatags_form(array &$form, $instance, array $metatags = array(
         $group = $info['groups'][$group_key] + array('form' => array(), 'description' => NULL);
         $form['metatags'][$langcode][$group_key] = $group['form'] + array(
           '#type' => 'fieldset',
-          '#title' => t($group['label']),
-          '#description' => !empty($group['description']) ? t($group['description']) : '',
+          '#title' => $group['label'],
+          '#description' => !empty($group['description']) ? $group['description'] : '',
           '#collapsible' => TRUE,
           '#collapsed' => TRUE,
         );
       }
-      $form['metatags'][$langcode][$group_key][$metatag] = $metatag_form + array('#parents' => array('metatags', $langcode, $metatag));
+      $form['metatags'][$langcode][$group_key][$metatag] = $metatag_form + array(
+        '#parents' => array('metatags', $langcode, $metatag),
+      );
 
       // Hide the fieldset itself if there is not at least one of the meta tag
       // fields visible.
@@ -1551,7 +1570,7 @@ function metatag_metatags_form_submit($form, &$form_state) {
 /**
  * Form API submission callback for Commerce product.
  *
- * Unlike metatag_metatags_form_submit
+ * Unlike metatag_metatags_form_submit.
  *
  * @see metatag_metatags_save()
  */
@@ -1565,8 +1584,11 @@ function metatag_commerce_product_form_submit($form, &$form_state) {
   $entity_id = $product->product_id;
   $revision_id = $product->revision_id;
 
+  // Get the full entity details.
+  list(, , $bundle) = entity_extract_ids($entity_type, $product);
+
   // Update the meta tags for this entity type.
-  metatag_metatags_save($entity_type, $entity_id, $revision_id, $form_state['values']['metatags']);
+  metatag_metatags_save($entity_type, $entity_id, $revision_id, $form_state['values']['metatags'], $bundle);
 }
 
 /**
@@ -1597,7 +1619,7 @@ function metatag_field_extra_fields() {
  * enabled during installation. If an entity type is enabled it is assumed that
  * the entity bundles will also be enabled by default.
  *
- * @see metatag_entity_type_is_suitable().
+ * @see metatag_entity_type_is_suitable()
  */
 function metatag_entity_supports_metatags($entity_type = NULL, $bundle = NULL) {
   $entity_types = &drupal_static(__FUNCTION__);
@@ -1616,7 +1638,7 @@ function metatag_entity_supports_metatags($entity_type = NULL, $bundle = NULL) {
         // 'metatag_enable_{$entity_type}' the value FALSE, e.g.:
         //
         // // Disable metatags for file_entity.
-        // $conf['metatag_enable_file'] = FALSE;
+        // $conf['metatag_enable_file'] = FALSE;.
         //
         // @see Settings page.
         if (variable_get('metatag_enable_' . $entity_name, FALSE) == FALSE) {
@@ -1638,7 +1660,7 @@ function metatag_entity_supports_metatags($entity_type = NULL, $bundle = NULL) {
             // 'metatag_enable_{$entity_type}__{$bundle}' the value FALSE, e.g.:
             //
             // // Disable metatags for carousel nodes.
-            // $conf['metatag_enable_node__carousel'] = FALSE;
+            // $conf['metatag_enable_node__carousel'] = FALSE;.
             //
             // @see Settings page.
             if (variable_get('metatag_enable_' . $entity_name . '__' . $bundle_name, TRUE) == FALSE) {
@@ -1674,30 +1696,51 @@ function metatag_entity_supports_metatags($entity_type = NULL, $bundle = NULL) {
 }
 
 /**
- * Enable support for a specific entity type.
+ * Enable support for a specific entity type if setting does not exist.
  *
  * @param string $entity_type
+ *    The entity type.
  * @param string $bundle
+ *    The bundle of the entity.
+ * @param bool $force_enable
+ *   If TRUE, then the type is enabled regardless of any stored variables.
+ *
+ * @return bool
+ *   TRUE if either the bundle or entity type was enabled by this function.
  */
-function metatag_entity_type_enable($entity_type, $bundle = NULL) {
+function metatag_entity_type_enable($entity_type, $bundle = NULL, $force_enable = FALSE) {
   // The bundle was defined.
+  $bundle_set = FALSE;
   if (isset($bundle)) {
-    variable_set('metatag_enable_' . $entity_type . '__' . $bundle, TRUE);
+    $stored_bundle = variable_get('metatag_enable_' . $entity_type . '__' . $bundle, NULL);
+    if ($force_enable || !isset($stored_bundle)) {
+      variable_set('metatag_enable_' . $entity_type . '__' . $bundle, TRUE);
+      $bundle_set = TRUE;
+    }
   }
 
   // Always enable the entity type, because otherwise there's no point in
   // enabling the bundle.
-  variable_set('metatag_enable_' . $entity_type, TRUE);
+  $entity_type_set = FALSE;
+  $stored_entity_type = variable_get('metatag_enable_' . $entity_type, NULL);
+  if ($force_enable || !isset($stored_entity_type)) {
+    variable_set('metatag_enable_' . $entity_type, TRUE);
+    $entity_type_set = TRUE;
+  }
 
   // Clear the static cache so that the entity type / bundle will work.
   drupal_static_reset('metatag_entity_supports_metatags');
+
+  return $bundle_set || $entity_type_set;
 }
 
 /**
  * Disable support for a specific entity type.
  *
  * @param string $entity_type
+ *    The entity type.
  * @param string $bundle
+ *    The bundle of the entity.
  */
 function metatag_entity_type_disable($entity_type, $bundle = NULL) {
   // The bundle was defined.
@@ -1744,6 +1787,12 @@ function metatag_page_build(&$page) {
     return;
   }
 
+  // Special consideration for the Me module, which uses the "user/me" path and
+  // will cause problems.
+  if (arg(0) == 'user' && arg(1) == 'me' && function_exists('me_menu_alter')) {
+    return;
+  }
+
   // The page region can be changed.
   $region = variable_get('metatag_page_region', 'content');
 
@@ -1772,7 +1821,10 @@ function metatag_page_build(&$page) {
     }
     else {
       $metatags = metatag_metatags_view($instance, array());
-      metatag_cache_set($cid, $metatags);
+      // If output caching is enabled, save this for later.
+      if (variable_get('metatag_cache_output', FALSE)) {
+        metatag_cache_set($cid, $metatags);
+      }
     }
 
     $page[$region]['metatags'][$instance] = $metatags;
@@ -1801,7 +1853,10 @@ function metatag_page_build(&$page) {
     }
     else {
       $metatags = metatag_metatags_view($instance, array());
-      metatag_cache_set($cid, $metatags);
+      // If output caching is enabled, save this for later.
+      if (variable_get('metatag_cache_output', FALSE)) {
+        metatag_cache_set($cid, $metatags);
+      }
     }
     $page[$region]['metatags'][$instance] = $metatags;
   }
@@ -1810,14 +1865,14 @@ function metatag_page_build(&$page) {
 /**
  * Returns whether the current page is the page of the passed in entity.
  *
- * @param $entity_type
+ * @param string $entity_type
  *    The entity type; e.g. 'node' or 'user'.
- * @param $entity
+ * @param object $entity
  *    The entity object.
  *
- * @return
+ * @return mixed
  *   TRUE if the current page is the page of the specified entity, or FALSE
- *   otherwise.
+ *   otherwise. If $entity_type == 'comment' return empty (FALSE).
  */
 function _metatag_entity_is_page($entity_type, $entity) {
   // Exclude comment entities as this conflicts with comment_fragment.module.
@@ -1828,8 +1883,8 @@ function _metatag_entity_is_page($entity_type, $entity) {
   $uri = entity_uri($entity_type, $entity);
   $current_path = current_path();
 
-  // Support for Workbench Moderation v1 - if this is a node, check if the
-  // content type supports moderation.
+  // Support for Workbench Moderation - if this is a node, check if the content
+  // type supports moderation.
   if ($entity_type == 'node' && function_exists('workbench_moderation_node_type_moderated') && workbench_moderation_node_type_moderated($entity->type) === TRUE) {
     return !empty($uri['path']) && ($current_path == $uri['path'] || $current_path == $uri['path'] . '/draft');
   }
@@ -1929,8 +1984,7 @@ function metatag_field_attach_form($entity_type, $entity, &$form, &$form_state,
   $options['context'] = $entity_type;
 
   // @todo Remove metatag_form_alter() when https://www.drupal.org/node/1284642 is fixed in core.
-  //metatag_metatags_form($form, $instance, $metatags, $options);
-
+  // metatag_metatags_form($form, $instance, $metatags, $options);
   $form['#metatags'] = array(
     'instance' => $instance,
     'metatags' => $metatags,
@@ -1954,7 +2008,7 @@ function metatag_form_alter(&$form, $form_state, $form_id) {
 /**
  * Get the meta tag information array of a meta tag.
  *
- * @param $metatag
+ * @param string $name
  *   The meta tag name, e.g. description, for which the info shall be returned,
  *   or NULL to return an array with info about all meta tags.
  */
@@ -1968,8 +2022,8 @@ function metatag_get_info($type = NULL, $name = NULL) {
 
   global $language;
   if (!isset($info)) {
-    // hook_metatag_info() includes translated strings, so each language is cached
-    // separately.
+    // hook_metatag_info() includes translated strings, so each language is
+    // cached separately.
     $cid = 'info:' . $language->language;
 
     if ($cache = metatag_cache_get($cid)) {
@@ -2009,6 +2063,9 @@ function metatag_get_info($type = NULL, $name = NULL) {
   }
 }
 
+/**
+ * Return instance of metatag.
+ */
 function metatag_get_instance($metatag, array $data = array()) {
   $info = metatag_get_info('tags', $metatag);
   if (!empty($info['class']) && class_exists($info['class'])) {
@@ -2020,16 +2077,16 @@ function metatag_get_instance($metatag, array $data = array()) {
 /**
  * Return the string value of a meta tag.
  *
- * @param $metatag
+ * @param string $metatag
  *   The meta tag string.
- * @param $data
+ * @param array $data
  *   The array of data for the meta tag class instance.
- * @param $options
+ * @param array $options
  *   An optional array of additional options to pass to the getValue() method
  *   of the meta tag class instance.
  *   - raw: A boolean if TRUE will not perform token replacement.
  *
- * @return
+ * @return string
  *   A string value.
  */
 function metatag_get_value($metatag, array $data, array $options = array()) {
@@ -2144,7 +2201,7 @@ function metatag_html_head_alter(&$elements) {
       'canonical',
       'shortlink',
       // Leave the shortcut icon, that's more of a theming thing.
-      // 'shortcut icon',
+      // 'shortcut icon',.
     );
     foreach ($elements as $name => &$element) {
       // Ignore meta tags provided by Metatag.
@@ -2166,11 +2223,17 @@ function metatag_html_head_alter(&$elements) {
   }
 }
 
+/**
+ * Implements hook_get_form().
+ */
 function metatag_metatag_get_form($metatag, array $data = array(), array $options = array()) {
   $instance = metatag_get_instance($metatag, $data);
   return $instance->getForm($options);
 }
 
+/**
+ * Returns Instance info if exists otherwise return FALSE.
+ */
 function metatag_config_instance_info($instance = NULL) {
   global $language;
 
@@ -2231,10 +2294,10 @@ function metatag_filter_values_from_defaults(array &$values, array $defaults = a
 /**
  * Return all the parents of a given configuration instance.
  *
- * @param $instance
+ * @param string $instance
  *   A meta tag configuration instance.
  *
- * @return
+ * @return array
  *   An array of instances starting with the $instance parameter, with the end
  *   of the array being the global instance.
  */
@@ -2255,7 +2318,7 @@ function metatag_config_get_parent_instances($instance, $include_global = TRUE)
 /**
  * Get the proper label of a configuration instance.
  *
- * @param $instance
+ * @param string $instance
  *   A meta tag configuration instance.
  */
 function metatag_config_instance_label($instance) {
@@ -2344,19 +2407,19 @@ function metatag_config_is_enabled($instance, $include_defaults = FALSE, $includ
 }
 
 /**
- * Wrapper around entity_language() to use LANGUAGE_NONE if the entity does not
- * have a language assigned.
+ * Wrapper around entity_language().
  *
- * @param $entity_type
+ * @param mixed $entity_type
  *   An entity type's machine name.
- * @param $entity
+ * @param object $entity
  *   The entity to review; will be typecast to an object if an array is passed.
  *
- * @return
- *   A string indicating the language code to be used.
+ * @return string
+ *   A string indicating the language code to be used; returns LANGUAGE_NONE if
+ *   the entity does not have a language assigned.
  */
 function metatag_entity_get_language($entity_type, $entity) {
-  // Determine the entity's language, af
+  // Determine the entity's language, af.
   $langcode = entity_language($entity_type, (object) $entity);
 
   // If no matching language was found, which will happen for e.g. terms and
@@ -2509,6 +2572,8 @@ function metatag_ctools_render_alter(&$info, $page, $context) {
 /**
  * Checks if this entity is the default revision (published).
  *
+ * Only needed when running Workbench Moderation v1; v3 is skipped.
+ *
  * @param object $entity
  *   The entity object, e.g., $node.
  *
@@ -2535,11 +2600,16 @@ function _metatag_isdefaultrevision($entity) {
   // Every moderation module saving a forward revision needs to return FALSE.
   // @todo: Refactor this workaround under D8.
 
-  // Support for Workbench Moderation v1 - if this is a node, check if the
-  // content type supports moderation.
-  if (function_exists('workbench_moderation_node_type_moderated') && workbench_moderation_node_type_moderated($entity->type) === TRUE) {
-    return !empty($entity->workbench_moderation['updating_live_revision']);
+  // Workbench Moderation v1 uses the hook_node_presave() for some custom logic.
+  // This was replaced with hook_entity_presave() in v3, so only proceed if the
+  // old hook implementation is present.
+  if (function_exists('workbench_moderation_node_presave')) {
+    // If this is a node, check if the content type supports moderation.
+    if (function_exists('workbench_moderation_node_type_moderated') && workbench_moderation_node_type_moderated($entity->type) === TRUE) {
+      return !empty($entity->workbench_moderation['updating_live_revision']);
+    }
   }
+
   return FALSE;
 }
 
@@ -2595,17 +2665,21 @@ function metatag_cache_default_cid_parts(array $cid_parts = array()) {
 /**
  * Wrapper for cache_set.
  *
- * @see cache_set().
+ * @see cache_set()
  */
 function metatag_cache_set($cid, $data) {
-  // Cache the data for later.
-  return cache_set($cid, $data, 'cache_metatag');
+  // By default the cached data will not expire.
+  $expire = CACHE_PERMANENT;
+
+  // Triggers hook_metatag_cache_set_expire_alter().
+  drupal_alter("metatag_cache_set_expire", $expire, $cid, $data);
+  return cache_set($cid, $data, 'cache_metatag', $expire);
 }
 
 /**
  * Wrapper for cache_get.
  *
- * @see cache_get().
+ * @see cache_get()
  */
 function metatag_cache_get($cid) {
   // Try to load the object.
@@ -2616,6 +2690,7 @@ function metatag_cache_get($cid) {
  * Determines if we are in an error page and return the appropriate instance.
  *
  * @return string
+ *   String of error.
  */
 function metatag_is_error_page() {
   $known_errors = array(
@@ -2651,9 +2726,12 @@ function metatag_admin_menu_cache_info() {
  * modes, must be fieldable, and may not be a configuration entity.
  *
  * @param string $entity_type
+ *    The entity type.
  * @param array $entity_info
+ *    Entity information.
  *
  * @return bool
+ *    Return TRUE if suitable.
  */
 function metatag_entity_type_is_suitable($entity_type, $entity_info = array()) {
   $suitable = TRUE;
@@ -2719,8 +2797,9 @@ function metatag_entity_type_is_suitable($entity_type, $entity_info = array()) {
  */
 function metatag_node_type_insert($info) {
   if (metatag_entity_supports_metatags('node')) {
-    metatag_entity_type_enable('node', $info->type);
-    drupal_set_message(t('Metatag support has been enabled for the @label content type.', array('@label' => $info->name)));
+    if (metatag_entity_type_enable('node', $info->type)) {
+      drupal_set_message(t('Metatag support has been enabled for the @label content type.', array('@label' => $info->name)));
+    }
   }
 }
 
@@ -2740,8 +2819,9 @@ function metatag_node_type_delete($info) {
  */
 function metatag_taxonomy_vocabulary_insert($vocabulary) {
   if (metatag_entity_supports_metatags('taxonomy_term')) {
-    metatag_entity_type_enable('taxonomy_term', $vocabulary->machine_name);
-    drupal_set_message(t('Metatag support has been enabled for the @label vocabulary.', array('@label' => $vocabulary->name)));
+    if (metatag_entity_type_enable('taxonomy_term', $vocabulary->machine_name)) {
+      drupal_set_message(t('Metatag support has been enabled for the @label vocabulary.', array('@label' => $vocabulary->name)));
+    }
   }
 }
 
@@ -2764,7 +2844,7 @@ function metatag_workbench_moderation_transition($node, $previous_state, $new_st
 }
 
 /**
- * sort callback for sorting by metatag instance string values.
+ * Sort callback for sorting by metatag instance string values.
  */
 function _metatag_config_instance_sort($a, $b) {
   $a_contexts = explode(':', $a);
@@ -3030,6 +3110,8 @@ function metatag_translations_delete($metatags, $context) {
 }
 
 /**
+ * Implements hook_config_insert().
+ *
  * Implements hook_metatag_config_insert() on behalf of i18n_string.
  */
 function i18n_string_metatag_config_insert($config) {
@@ -3038,6 +3120,8 @@ function i18n_string_metatag_config_insert($config) {
 }
 
 /**
+ * Implements hook_config_update().
+ *
  * Implements hook_metatag_config_update() on behalf of i18n_string.
  */
 function i18n_string_metatag_config_update($config) {
@@ -3046,6 +3130,8 @@ function i18n_string_metatag_config_update($config) {
 }
 
 /**
+ * Implements hook_config_delete().
+ *
  * Implements hook_metatag_config_delete() on behalf of i18n_string.
  */
 function i18n_string_metatag_config_delete($config) {

+ 45 - 43
sites/all/modules/contrib/seo/metatag/metatag.search_api.inc

@@ -21,58 +21,60 @@ function metatag_search_api_alter_callback_info() {
 /**
  * Adds meta tag values to the indexed items.
  */
-class MetatagSearchAlterCallback extends SearchApiAbstractAlterCallback {
+if (class_exists('SearchApiAbstractAlterCallback')) {
+  class MetatagSearchAlterCallback extends SearchApiAbstractAlterCallback {
 
-  /**
-   * {@inheritdoc}
-   */
-  public function supportsIndex(SearchApiIndex $index) {
-    // Only works on entities.
-    return (bool) $index->getEntityType();
-  }
+    /**
+     * {@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 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();
+    /**
+     * {@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;
+      // 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,
+        );
       }
-      $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;
     }
 
-    return $properties;
   }
-
 }

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

@@ -19,14 +19,14 @@ function metatag_token_info() {
 
   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']);
+      $label = $metatag_info['groups'][$value['group']]['label'] . ': ' . $value['label'];
     }
     else {
-      $label = t('Basic tags') . ': ' . t($value['label']);
+      $label = t('Basic tags') . ': ' . $value['label'];
     }
     $info['tokens']['metatag'][$value['name']] = array(
       'name' => $label,
-      'description' => t($value['description']),
+      'description' => $value['description'],
     );
   }
 
@@ -77,7 +77,7 @@ function metatag_tokens($type, $tokens, array $data = array(), array $options =
   if ($type == 'metatag' && !empty($data['metatag'])) {
     $metatag = $data['metatag'];
     foreach ($tokens as $name => $original) {
-      if(isset($metatag[$name])){
+      if (isset($metatag[$name])) {
         $replacements[$original] = $sanitize ? filter_xss($metatag[$name]) : $metatag[$name];
       }
     }

+ 5 - 4
sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.info

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

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

@@ -10,7 +10,7 @@
 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')),
+    '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,
     ),
@@ -29,122 +29,122 @@ function metatag_app_links_metatag_info() {
   $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>'),
+    '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'),
+    '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'),
+    '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'),
+    '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>'),
+    '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.'),
+    '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'),
+    '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>'),
+    '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.'),
+    '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'),
+    '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>'),
+    '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.'),
+    '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'),
+    '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>'),
+    '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'),
+    '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'),
+    '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>'),
+    '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'),
+    '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'),
+    '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>'),
+    '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'),
+    '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'),
+    'label' => t('Windows Universal app name'),
     'description' => t('The name of the app (suitable for display)'),
   ) + $defaults;
 

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

@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * Tests that each of the Metatag App Links tags work correctly.
+ */
+class MetatagAppLinksTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: App Links',
+      'description' => 'Test the App Links meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'al:android:app_name',
+    'al:android:class',
+    'al:android:package',
+    'al:android:url',
+    'al:ios:app_name',
+    'al:ios:app_store_id',
+    'al:ios:url',
+    'al:ipad:app_name',
+    'al:ipad:app_store_id',
+    'al:ipad:url',
+    'al:iphone:app_name',
+    'al:iphone:app_store_id',
+    'al:iphone:url',
+    'al:web:should_fallback',
+    'al:web:url',
+    'al:windows:app_id',
+    'al:windows:app_name',
+    'al:windows:url',
+    'al:windows_phone:app_id',
+    'al:windows_phone:app_name',
+    'al:windows_phone:url',
+    'al:windows_universal:app_id',
+    'al:windows_universal:app_name',
+    'al:windows_universal:url',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_name_attribute = 'property';
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_app_links';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    // All of the tags have their underlines replaced with colons.
+
+    // Start with the beginning of the string.
+    $tag_name = str_replace('al_', 'al:', $tag_name);
+
+    // The three-ish standard tag names.
+    $tag_name = str_replace('_app_id', ':app_id', $tag_name);
+    $tag_name = str_replace('_app_name', ':app_name', $tag_name);
+    $tag_name = str_replace('_app_store_id', ':app_store_id', $tag_name);
+    $tag_name = str_replace('_url', ':url', $tag_name);
+
+    // One-offs.
+    $tag_name = str_replace('_class', ':class', $tag_name);
+    $tag_name = str_replace('_package', ':package', $tag_name);
+    $tag_name = str_replace('_should_fallback', ':should_fallback', $tag_name);
+
+    return $tag_name;
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_app_links/tests/metatag_app_links.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag AppLinks module.
  */
-
 class MetatagAppLinksTest extends MetatagTestHelper {
 
   /**

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

@@ -5,16 +5,16 @@ core = 7.x
 
 configure = admin/config/search/metatags/context
 
-dependencies[] = metatag
-dependencies[] = context
+dependencies[] = metatag:metatag
+dependencies[] = context: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"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 9 - 3
sites/all/modules/contrib/seo/metatag/metatag_context/tests/metatag_context.i18n.test

@@ -1,10 +1,10 @@
 <?php
+
 /**
- * @file
  * Tests the Metatag:Context module for i18n integration.
  */
-
 class MetatagContextI18nTest extends MetatagTestHelper {
+
   /**
    * {@inheritdoc}
    */
@@ -13,6 +13,7 @@ class MetatagContextI18nTest extends MetatagTestHelper {
       'name' => 'Metatag:Context tests, with i18n',
       'description' => 'Test Metatag integration with the Context and i18n modules.',
       'group' => 'Metatag',
+      'dependencies' => array('ctools', 'token', 'context', 'i18n'),
     );
   }
 
@@ -34,6 +35,9 @@ class MetatagContextI18nTest extends MetatagTestHelper {
     // Enable all of the modules that are needed.
     parent::setUp($modules);
 
+    // Add more locales.
+    $this->setupLocales();
+
     // Set up the locales.
     $perms = array(
       'translate admin strings',
@@ -45,7 +49,9 @@ class MetatagContextI18nTest extends MetatagTestHelper {
     );
     // This replaces the one from MetatagContextTest().
     $this->adminUser = $this->createAdminUser($perms);
-    $this->setupLocales();
+
+    // Log in the admin user.
+    $this->drupalLogin($this->adminUser);
     
     // Reload the translations.
     drupal_flush_all_caches();

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

@@ -1,13 +1,10 @@
 <?php
-/**
- * @file
- * Functional tests for the Metatag:Context module.
- */
 
 /**
- * This extends the basic Metatag test class to reduce code duplication.
+ * Functional tests for the Metatag:Context module.
  */
 class MetatagContextTest extends MetatagTestHelper {
+
   /**
    * {@inheritdoc}
    */
@@ -16,6 +13,7 @@ class MetatagContextTest extends MetatagTestHelper {
       'name' => 'Metatag:Context tests',
       'description' => 'Test basic Metatag:Context functionality.',
       'group' => 'Metatag',
+      'dependencies' => array('ctools', 'token', 'context'),
     );
   }
 
@@ -36,6 +34,8 @@ class MetatagContextTest extends MetatagTestHelper {
       'bypass node access',
     );
     $this->adminUser = $this->createAdminUser($perms);
+
+    // Log in the admin user.
     $this->drupalLogin($this->adminUser);
 
     // Create a content type, with underscores.

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

@@ -5,13 +5,13 @@ core = 7.x
 ; Don't show this on the modules admin page.
 hidden = TRUE
 
-dependencies[] = context
-dependencies[] = metatag
-dependencies[] = metatag_context
+dependencies[] = context:context
+dependencies[] = metatag:metatag
+dependencies[] = metatag:metatag_context
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 5 - 4
sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.info

@@ -2,14 +2,15 @@ name = Metatag: Dublin Core
 description = Provides the fifteen <a href="http://dublincore.org/documents/dces/">Dublin Core Metadata Element Set 1.1</a> meta tags from the <a href="http://dublincore.org/">Dublin Core Metadata Institute</a>.
 package = SEO
 core = 7.x
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; Tests.
 files[] = tests/metatag_dc.test
+files[] = tests/metatag_dc.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

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

@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * Tests that each of the Metatag Dublin Core tags work correctly.
+ */
+class MetatagDcTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: Dublin Core',
+      'description' => 'Test the Dublin Core meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'dcterms.contributor',
+    'dcterms.coverage',
+    'dcterms.creator',
+    'dcterms.date',
+    'dcterms.description',
+    'dcterms.format',
+    'dcterms.identifier',
+    'dcterms.language',
+    'dcterms.publisher',
+    'dcterms.relation',
+    'dcterms.rights',
+    'dcterms.source',
+    'dcterms.subject',
+    'dcterms.title',
+    'dcterms.type',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_dc';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    return str_replace('dcterms_', 'dcterms.', $tag_name);
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_key() for 'dcterms.type'.
+   */
+  public function dcterms_type_test_key() {
+    return 'metatags[und][dcterms.type][value]';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'dcterms.type'.
+   */
+  public function dcterms_type_test_value() {
+    return 'Text';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_field_xpath() for 'dcterms.type'.
+   */
+  public function dcterms_type_test_field_xpath() {
+    return "//select[@name='metatags[und][dcterms.type][value]']";
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_dc/tests/metatag_dc.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag DC module.
  */
-
 class MetatagDcTest extends MetatagTestHelper {
 
   /**

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

@@ -2,15 +2,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
+dependencies[] = metatag:metatag
+dependencies[] = metatag:metatag_dc
 
 ; Tests.
 files[] = tests/metatag_dc_advanced.test
+files[] = tests/metatag_dc_advanced.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

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

@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * Tests that each of the Metatag Advanced Dublin Core tags work correctly.
+ */
+class MetatagDcAdvancedTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: Dublin Core Advanced',
+      'description' => 'Test the Advanced Dublin Core meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    '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',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_dc_advanced';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    return str_replace('dcterms_', 'dcterms.', $tag_name);
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_dc_advanced/tests/metatag_dc_advanced.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag DC Advanced module.
  */
-
 class MetatagDcAdvancedTest extends MetatagTestHelper {
 
   /**

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

@@ -3,14 +3,14 @@ description = Provides development / debugging functionality for the Metatag mod
 package = Development
 core = 7.x
 tags[] = developer
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; Tests.
 files[] = tests/metatag_devel.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_devel/tests/metatag_devel.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag Devel module.
  */
-
 class MetatagDevelest extends MetatagTestHelper {
 
   /**

+ 5 - 4
sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.info

@@ -2,14 +2,15 @@ name = Metatag: Facebook
 description = "Provides support for Facebook's custom meta tags."
 package = SEO
 core = 7.x
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; Tests.
 files[] = tests/metatag_facebook.test
+files[] = tests/metatag_facebook.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

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

@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Tests that each of the Metatag Facebook tags work correctly.
+ */
+class MetatagFacebookTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: Facebook',
+      'description' => 'Test the Facebook meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_facebook';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'fb:admins',
+    'fb:app_id',
+    'fb:pages',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_name_attribute = 'property';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    return str_replace('fb_', 'fb:', $tag_name);
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_facebook/tests/metatag_facebook.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag Facebook module.
  */
-
 class MetatagFacebookTest extends MetatagTestHelper {
 
   /**

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

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

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

@@ -39,7 +39,7 @@ class DrupalMaskIconMetaTag extends DrupalTextMetaTag {
   public function getValue(array $options = array()) {
     $value = array(
       'value' => '',
-      'color' => '',
+      'color' => NULL,
     );
 
     // The 'color' attribute will only be output if the 'value' is present.

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

@@ -31,6 +31,27 @@ function metatag_favicons_theme() {
   return $info;
 }
 
+/**
+ * Implements hoko_html_head_alter().
+ *
+ * Remove the default shortcut icon if one was set by Metatag.
+ */
+function metatag_favicons_html_head_alter(&$elements) {
+  if (isset($elements['metatag_shortcut icon'])) {
+    foreach ($elements as $key => $element) {
+      if (isset($element['#tag']) && $element['#tag'] == 'link') {
+        if (isset($element['#attributes']) && is_array($element['#attributes'])) {
+          if (isset($element['#attributes']['rel'])) {
+            if ($element['#attributes']['rel'] == 'shortcut icon') {
+              unset($elements[$key]);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
 /**
  * Theme callback for a favicon meta tag.
  *

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

@@ -0,0 +1,246 @@
+<?php
+
+/**
+ * Tests that each of the Metatag Favicons tags work correctly.
+ */
+class MetatagFaviconsTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: Favicons',
+      'description' => 'Test the Favicons meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'apple-touch-icon',
+    'apple-touch-icon-precomposed',
+    'apple-touch-icon-precomposed_114x114',
+    'apple-touch-icon-precomposed_120x120',
+    'apple-touch-icon-precomposed_144x144',
+    'apple-touch-icon-precomposed_152x152',
+    'apple-touch-icon-precomposed_180x180',
+    'apple-touch-icon-precomposed_72x72',
+    'apple-touch-icon-precomposed_76x76',
+    'apple-touch-icon_114x114',
+    'apple-touch-icon_120x120',
+    'apple-touch-icon_144x144',
+    'apple-touch-icon_152x152',
+    'apple-touch-icon_180x180',
+    'apple-touch-icon_72x72',
+    'apple-touch-icon_76x76',
+    'icon_16x16',
+    'icon_192x192',
+    'icon_32x32',
+    'icon_96x96',
+    'mask-icon',
+    'shortcut icon',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_favicons';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_tag = 'link';
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_name_attribute = 'rel';
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_value_attribute = 'href';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagValue() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'icon_16x16',
+   */
+  public function icon_16x16_test_output_xpath() {
+    return "//link[@rel='icon' and @sizes='16x16']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'icon_192x192',
+   */
+  public function icon_192x192_test_output_xpath() {
+    return "//link[@rel='icon' and @sizes='192x192']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'icon_32x32',
+   */
+  public function icon_32x32_test_output_xpath() {
+    return "//link[@rel='icon' and @sizes='32x32']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'icon_96x96',
+   */
+  public function icon_96x96_test_output_xpath() {
+    return "//link[@rel='icon' and @sizes='96x96']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_precomposed',
+   */
+  public function apple_touch_icon_precomposed_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon-precomposed' and not(@sizes)]";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_precomposed_114x114',
+   */
+  public function apple_touch_icon_precomposed_114x114_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon-precomposed' and @sizes='114x114']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_precomposed_120x120',
+   */
+  public function apple_touch_icon_precomposed_120x120_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon-precomposed' and @sizes='120x120']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_precomposed_144x144',
+   */
+  public function apple_touch_icon_precomposed_144x144_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon-precomposed' and @sizes='144x144']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_precomposed_152x152',
+   */
+  public function apple_touch_icon_precomposed_152x152_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon-precomposed' and @sizes='152x152']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_precomposed_180x180',
+   */
+  public function apple_touch_icon_precomposed_180x180_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon-precomposed' and @sizes='180x180']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_precomposed_72x72',
+   */
+  public function apple_touch_icon_precomposed_72x72_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon-precomposed' and @sizes='72x72']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_precomposed_76x76',
+   */
+  public function apple_touch_icon_precomposed_76x76_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon-precomposed' and @sizes='76x76']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'apple_touch_icon',
+   */
+  public function apple_touch_icon_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon' and not(@sizes)]";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_114x114',
+   */
+  public function apple_touch_icon_114x114_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon' and @sizes='114x114']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_120x120',
+   */
+  public function apple_touch_icon_120x120_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon' and @sizes='120x120']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_144x144',
+   */
+  public function apple_touch_icon_144x144_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon' and @sizes='144x144']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_152x152',
+   */
+  public function apple_touch_icon_152x152_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon' and @sizes='152x152']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_180x180',
+   */
+  public function apple_touch_icon_180x180_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon' and @sizes='180x180']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_72x72',
+   */
+  public function apple_touch_icon_72x72_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon' and @sizes='72x72']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'apple_touch_icon_76x76',
+   */
+  public function apple_touch_icon_76x76_test_output_xpath() {
+    return "//link[@rel='apple-touch-icon' and @sizes='76x76']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath for 'mask-icon'.
+   */
+  public function mask_icon_test_tag_name() {
+    return 'mask-icon';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_tag_name for 'shortcut icon'.
+   */
+  public function shortcut_icon_test_tag_name() {
+    return 'shortcut icon';
+  }
+
+}

+ 20 - 2
sites/all/modules/contrib/seo/metatag/metatag_favicons/tests/metatag_favicons.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag Favicons module.
  */
-
 class MetatagFaviconsTest extends MetatagTestHelper {
 
   /**
@@ -205,6 +204,25 @@ class MetatagFaviconsTest extends MetatagTestHelper {
     $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 empty color.
+    $color = '';
+
+    // 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->assertFalse(isset($xpath[0]['color']));
   }
 
 }

+ 22 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_cse/README.txt

@@ -0,0 +1,22 @@
+Metatag: Google Custom Search Engine (CSE)
+------------------------------------------
+This module adds certain meta tags that are primarily used by Google's Custom
+Search Engine aka "CSE".
+
+The following meta tags are provided:
+* thumbnail
+* department
+* audience
+* doc_status
+* rating
+
+
+Credits
+------------------------------------------------------------------------------
+The initial development was by Carlos E Basqueira [3].
+
+
+References
+------------------------------------------------------------------------------
+1: http://www.dublincore.org/documents/dces/
+2: https://www.drupal.org/u/cebasqueira

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

@@ -0,0 +1,16 @@
+name = "Metatag: Google Custom Search Engine (CSE)"
+description = "Provides support for meta tags used for Google Custom Search Engine."
+package = SEO
+core = 7.x
+dependencies[] = metatag:metatag
+
+; Tests.
+files[] = tests/metatag_google_cse.test
+files[] = tests/metatag_google_cse.tags.test
+
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
+core = "7.x"
+project = "metatag"
+datestamp = "1487171290"
+

+ 5 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_cse/metatag_google_cse.install

@@ -0,0 +1,5 @@
+<?php
+/**
+ * @file
+ * Installation and update scripts for Metatag:Google CSE.
+ */

+ 62 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_cse/metatag_google_cse.metatag.inc

@@ -0,0 +1,62 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the Metatag:Google CSE.
+ */
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_google_cse_metatag_info() {
+  $info['groups']['google_cse'] = array(
+    'label' => t('Google Custom Search Engine (CSE)'),
+    'description' => t("Meta tags used to control the mobile browser experience. Some of these meta tags have been replaced by newer mobile browsers. These meta tags usually only need to be set globally, rather than per-page."),
+    'form' => array(
+      '#weight' => 80,
+    ),
+  );
+
+  $weight = 80;
+
+  // Default values for each meta tag.
+  $tag_info_defaults = array(
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'google_cse',
+    'element' => array(
+      '#theme' => 'metatag_google_cse',
+    ),
+  );
+
+  $info['tags']['thumbnail'] = array(
+    'label' => t('Thumbnail'),
+    'description' => t('Use a url of a valid image.'),
+    'image' => TRUE,
+    'weight' => ++$weight,
+  ) + $tag_info_defaults;
+
+  $info['tags']['department'] = array(
+    'label' => t('Department'),
+    'description' => t('Department tag.'),
+    'weight' => ++$weight,
+  ) + $tag_info_defaults;
+
+  $info['tags']['audience'] = array(
+    'label' => t('Content audience'),
+    'description' => t('The content audience, e.g. "all".'),
+    'weight' => ++$weight,
+  ) + $tag_info_defaults;
+
+  $info['tags']['doc_status'] = array(
+    'label' => t('Document status'),
+    'description' => t('The document status, e.g. "draft".'),
+    'weight' => ++$weight,
+  ) + $tag_info_defaults;
+
+  $info['tags']['google_rating'] = array(
+    'label' => t('Results sorting'),
+    'description' => t('Works only with numeric values.'),
+    'weight' => ++$weight,
+  ) + $tag_info_defaults;
+
+  return $info;
+}

+ 47 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_cse/metatag_google_cse.module

@@ -0,0 +1,47 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag:Google CSE.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_google_cse_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function metatag_google_cse_theme() {
+  $info['metatag_google_cse'] = array(
+    'render element' => 'element',
+  );
+
+  return $info;
+}
+
+/**
+ * Theme callback for a normal meta tag.
+ *
+ * The format is:
+ * <meta name="[name]" content="[value]" />
+ */
+function theme_metatag_google_cse($variables) {
+  $element = &$variables['element'];
+
+  if ($element['#name'] === 'google_rating') {
+    $element['#name'] = 'rating';
+  }
+
+  $args = array(
+    '#name'  => 'name',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}

+ 52 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_cse/tests/metatag_google_cse.tags.test

@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Tests that each of the Metatag Google CSE tags work correctly.
+ */
+class MetatagGoogleCSETagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: Google CSE',
+      'description' => 'Test the Metatag:Google CSE meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'audience',
+    'department',
+    'doc_status',
+    'google_rating',
+    'thumbnail',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp(array $modules = array()) {
+    $modules[] = 'metatag_google_cse';
+    parent::setUp($modules);
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_tag_name() for 'google_rating'.
+   */
+  public function google_rating_test_tag_name() {
+    return 'rating';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'thumbnail'.
+   */
+  public function thumbnail_test_value() {
+    return $this->randomImageUrl();
+  }
+
+}

+ 43 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_cse/tests/metatag_google_cse.test

@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * Tests for the Metatag Google CSE module.
+ */
+class MetatagGoogleCSETest extends MetatagTestHelper {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag Google CSE tests',
+      'description' => 'Test the Metatag:Google CSE module.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp(array $modules = array()) {
+    $modules[] = 'metatag_google_cse';
+
+    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.'));
+  }
+
+}

+ 2 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_plus/README.txt

@@ -7,6 +7,8 @@ The following Google+ tags are provided:
 * itemprop:name
 * itemprop:description
 * itemprop:image
+* author
+* publisher
 
 Also itemtype is provided to add schema in the HTML markup as follows:
 

+ 4 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.inc

@@ -16,10 +16,14 @@
  */
 class DrupalSchemaMetaTag extends DrupalTextMetaTag {
 
+  /**
+   * {@inheritdoc}
+   */
   public function getElement(array $options = array()) {
     $element = array();
     $value = $this->getValue($options);
     $element['#attached']['metatag_set_preprocess_variable'][] = array('html', 'itemtype', $value);
     return $element;
   }
+
 }

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

@@ -1,18 +1,19 @@
 name = Metatag: Google+
-description = "Provides support for Google+ 'itemscope' meta tags."
+description = "Provides support for Google+ 'itemscope', 'author' and 'publisher' meta tags."
 package = SEO
 core = 7.x
 
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 files[] = metatag_google_plus.inc
 
 ; Tests.
 files[] = tests/metatag_google_plus.test
+files[] = tests/metatag_google_plus.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 23 - 1
sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.metatag.inc

@@ -120,7 +120,7 @@ function metatag_google_plus_metatag_info() {
   ) + $defaults;
   $info['tags']['itemprop:image'] = array(
     '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. '),
+    '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(
@@ -128,5 +128,27 @@ function metatag_google_plus_metatag_info() {
     ),
   ) + $defaults;
 
+  $info['tags']['author'] = array(
+    'label' => t('Author URL'),
+    'description' => t("Used by some search engines to confirm authorship of the content on a page. Should be either the full URL for the author's Google+ profile page or a local page with information about the author."),
+    'class' => 'DrupalLinkMetaTag',
+    'weight' => ++$weight,
+    'element' => array(),
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  ) + $defaults;
+  $info['tags']['publisher'] = array(
+    'label' => t('Publisher URL'),
+    'description' => t("Used by some search engines to confirm publication of the content on a page. Should be the full URL for the publication's Google+ profile page."),
+    'class' => 'DrupalLinkMetaTag',
+    'weight' => ++$weight,
+    'element' => array(),
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  ) + $defaults;
+
+
   return $info;
 }

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

@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * Tests that each of the Metatag GooglePlus tags work correctly.
+ */
+class MetatagGooglePlusTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: GooglePlus',
+      'description' => 'Test the Google Plus meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    // @todo Can't test the output of this tag, it depends upon changes to the
+    // html.tpl.php file.
+    // 'itemtype',
+    'itemprop:name',
+    'itemprop:description',
+    'itemprop:image',
+    'author',
+    'publisher',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_google_plus';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    // The itemprop meta tags don't have 'itemprop' in their attribute value,
+    // 'itemprop' is the name of the attribute itself.
+    return str_replace('itemprop_', '', $tag_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_name_attribute = 'itemprop';
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'author'.
+   */
+  public function author_test_output_xpath() {
+    return "//link[@rel='author']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value_attribute() for 'author'.
+   */
+  public function author_test_value_attribute() {
+    return 'href';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_key() for 'itemtype'.
+   */
+  public function itemtype_test_key() {
+    return 'metatags[und][itemtype][value]';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'itemtype'.
+   */
+  public function itemtype_test_value() {
+    return 'Article';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'itemprop_image'.
+   */
+  public function itemprop_image_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_field_xpath() for 'itemtype'.
+   */
+  public function itemtype_test_field_xpath() {
+    return "//select[@name='metatags[und][itemtype][value]']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'itemtype'.
+   */
+  public function itemtype_test_output_xpath() {
+    return "//html[@rel='itemtype']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'publisher'.
+   */
+  public function publisher_test_output_xpath() {
+    return "//link[@rel='publisher']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'publisher'.
+   */
+  public function publisher_test_value_attribute() {
+    return 'href';
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_google_plus/tests/metatag_google_plus.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag GooglePlus module.
  */
-
 class MetatagGooglePlusTest extends MetatagTestHelper {
 
   /**

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

@@ -3,18 +3,23 @@ description = Provides support for the hreflang meta tag with some extra logic t
 package = SEO
 core = 7.x
 
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; The locale module is required in order to have multiple languages/locales.
-dependencies[] = locale
+dependencies[] = drupal:locale
 
 ; Tests.
 files[] = tests/metatag_hreflang.test
-files[] = metatag_hreflang.with_entity_translation.test
+files[] = tests/metatag_hreflang.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Entity Translation integration.
+test_dependencies[] = devel:devel
+test_dependencies[] = entity_translation:entity_translation
+files[] = tests/metatag_hreflang.with_entity_translation.test
+
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 34 - 0
sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.install

@@ -34,3 +34,37 @@ function metatag_hreflang_uninstall() {
   // Delete any custom variables that are used.
   variable_del('metatag_hreflang_allow_dupe');
 }
+
+/**
+ * Implementations of hook_update_N().
+ */
+
+/**
+ * Clear the Metatag cache so the updated hreflang default is caught.
+ */
+function metatag_hreflang_update_7101() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  return t('All Metatag caches cleared.');
+}
+
+/**
+ * Fix hreflang=xdefault for config definitions.
+ */
+function metatag_hreflang_update_7102() {
+  module_load_include('install', 'metatag');
+  $meta_tag = 'hreflang_xdefault';
+  $old_value = '[node:source:url]';
+  $new_value = '[node:url-original]';
+  return metatag_update_replace_config_value($meta_tag, $old_value, $new_value);
+}
+
+/**
+ * Fix hreflang=xdefault for all entities.
+ */
+function metatag_hreflang_update_7103() {
+  module_load_include('install', 'metatag');
+  $meta_tag = 'hreflang_xdefault';
+  $old_value = '[node:source:url]';
+  $new_value = '[node:url-original]';
+  return metatag_update_replace_entity_value($sandbox, $meta_tag, $old_value, $new_value);
+}

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

@@ -21,7 +21,7 @@ function metatag_hreflang_metatag_bundled_config_alter(array &$configs) {
       case 'node':
         // The x-default should default to the source language.
         $config->config += array(
-          'hreflang_xdefault' => array('value' => '[node:source:url]'),
+          'hreflang_xdefault' => array('value' => '[node:url-original]'),
         );
 
         // Add all of the other hreflang values.

+ 17 - 13
sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.module

@@ -4,6 +4,9 @@
  * Primary hook implementations for Metatag:hreflang.
  */
 
+// @todo Clear caches for all versions of an entity when a translation is edited
+// so that the hreflang meta tags update appropriately.
+
 /**
  * Implements hook_ctools_plugin_api().
  */
@@ -34,8 +37,8 @@ function theme_metatag_link_hreflang($variables) {
   $element['#name'] = 'alternate';
   $args = array(
     '#name' => 'rel',
-    '#value' => 'href',
     '#hreflang' => 'hreflang',
+    '#value' => 'href',
   );
   element_set_attributes($element, $args);
   unset($element['#value']);
@@ -55,25 +58,26 @@ function metatag_hreflang_form_metatag_admin_settings_form_alter(&$form, &$form_
 }
 
 /**
- * Implements hook_html_head_alter().
+ * Implements hook_metatag_metatags_view_alter().
  *
- * Remove any hreflang="LANGCODE" values that match hreflang="x-default".
+ * Remove any hreflang="LANGCODE" values that match hreflang="x-default". Using
+ * this hook instead of hook_html_head_alter() as it gets closer to Metatag's
+ * data structures, and the results are cached so this won't be executed on
+ * every page request.
  */
-function metatag_hreflang_html_head_alter(&$elements) {
+function metatag_hreflang_metatag_metatags_view_alter(&$output, $instance, $options) {
   // This behaviour may be disabled from the Metatag settings page.
   if (!variable_get('metatag_hreflang_allow_dupe', FALSE)) {
-    if (!empty($elements['metatag_hreflang_xdefault']['#value'])) {
-      $default = $elements['metatag_hreflang_xdefault']['#value'];
-
-      // Look for other hreflang meta tags.
-      foreach ($elements as $tag_name => &$element) {
+    if (!empty($output['hreflang_xdefault'])) {
+      $default = $output['hreflang_xdefault']['#attached']['drupal_add_html_head'][0][0]['#value'];
+      foreach ($output as $tag_name => &$tag) {
         // Skip the x-default tag.
-        if ($tag_name == 'metatag_hreflang_xdefault') {
+        if ($tag_name == 'hreflang_xdefault') {
           continue;
         }
-        if (strpos($tag_name, 'metatag_hreflang_') === 0) {
-          if ($element['#value'] == $default) {
-            $element['#access'] = FALSE;
+        if (strpos($tag_name, 'hreflang_') === 0) {
+          if ($tag['#attached']['drupal_add_html_head'][0][0]['#value'] == $default) {
+            $tag['#attached']['drupal_add_html_head'][0][0]['#access'] = FALSE;
           }
         }
       }

+ 23 - 0
sites/all/modules/contrib/seo/metatag/metatag_hreflang/metatag_hreflang.tokens.inc

@@ -14,6 +14,13 @@ function metatag_hreflang_token_info() {
     return;
   }
 
+  // Don't do anything if the patch was applied to Entity Translation to add
+  // these.
+  // @see https://www.drupal.org/node/2603056
+  if (module_load_include('tokens.inc', 'entity_translation')) {
+    return;
+  }
+
   $info = array();
 
   $languages = language_list('enabled');
@@ -30,6 +37,11 @@ function metatag_hreflang_token_info() {
     }
   }
 
+  $info['tokens']['node']['url-original'] = array(
+    'name' => t('URL (original language)'),
+    'description' => t("The URL for the node that is the original source for the current node's translation."),
+  );
+
   return $info;
 }
 
@@ -51,6 +63,17 @@ function metatag_hreflang_tokens($type, $tokens, array $data = array(), array $o
       $languages = language_list('enabled');
       if (!empty($languages[1]) && is_array($languages[1]) && count($languages[1]) > 1) {
         foreach ($tokens as $name => $original) {
+          // The original entity's URL.
+          if ($name == 'url-original') {
+            if (isset($node->translations->original, $languages[1][$node->translations->original])) {
+              $url_options = $options;
+              $url_options['language'] = $languages[1][$node->translations->original];
+              $url_options['absolute'] = TRUE;
+              $replacements[$original] = url('node/' . $node->nid, $url_options);
+            }
+          }
+
+          // Separate URLs for each translation.
           foreach ($node->translations->data as $langcode => $translation) {
             if ($name == 'url-' . $langcode) {
               $url_options = $options;

+ 108 - 0
sites/all/modules/contrib/seo/metatag/metatag_hreflang/tests/metatag_hreflang.tags.test

@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * Tests that each of the Metatag Hreflang tags work correctly.
+ */
+class MetatagHreflangTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: Hreflang',
+      'description' => 'Test the Hreflang meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'hreflang_xdefault',
+    // Additional meta tags added at the end of setUp().
+    // @see setUp()
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_hreflang';
+    parent::setUp($modules);
+
+    // Create an admin user that can also manage locales.
+    $perms = array(
+      // For Locale, so languages can be added.
+      'administer languages',
+    );
+    $this->adminUser = $this->createAdminUser($perms);
+
+    // Log in the admin user.
+    $this->drupalLogin($this->adminUser);
+
+    // Add some new languages.
+    foreach ($this->supportedLocales() as $langcode) {
+      if ($langcode != 'en') {
+        $this->addSiteLanguage($langcode);
+      }
+    }
+
+    // Clear all the caches so that all of the various hooks are activated and
+    // the appropriate tokens, fields, meta tags, etc are available.
+    drupal_flush_all_caches();
+
+    // Additional meta tags that will be available.
+    foreach ($this->supportedLocales() as $langcode) {
+      $this->tags[] = 'hreflang_' . $langcode;
+    }
+  }
+
+  /**
+   * The list of locales that are being tested.
+   *
+   * @return array
+   *   A simple list of language codes.
+   */
+  private function supportedLocales() {
+    return array(
+      'de',
+      'fr',
+      'es',
+      // English is automatic, so remember to not try enabling it.
+      'en',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_tag = 'link';
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_name_attribute = 'hreflang';
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_value_attribute = 'href';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagValue() {
+    return base_path() . $this->randomMachineName() . '/' . $this->randomMachineName();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    $tag_name = str_replace('xdefault', 'x-default', $tag_name);
+    return str_replace('hreflang_', '', $tag_name);
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_hreflang/tests/metatag_hreflang.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag HrefLang module.
  */
-
 class MetatagHreflangTest extends MetatagTestHelper {
 
   /**

+ 249 - 4
sites/all/modules/contrib/seo/metatag/metatag_hreflang/tests/metatag_hreflang.with_entity_translation.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
- * Tests for the Metatag module to ensure the hreflang meta tags work correctly.
+ * Tests for hreflang handle when Entity Translation is used.
  */
-
 class MetatagHreflangWithEntityTranslationTest extends MetatagTestHelper {
 
   /**
@@ -14,6 +13,7 @@ class MetatagHreflangWithEntityTranslationTest extends MetatagTestHelper {
       'name' => 'Metatag tests for hreflang with Entity Translation',
       'description' => 'Test Metatag:hreflang with the Entity Translation module.',
       'group' => 'Metatag',
+      'dependencies' => array('devel', 'entity_translation'),
     );
   }
 
@@ -21,13 +21,258 @@ class MetatagHreflangWithEntityTranslationTest extends MetatagTestHelper {
    * {@inheritdoc}
    */
   function setUp(array $modules = array()) {
+    // Used for debugging some token values.
+    $modules[] = 'devel';
+
     // Need Locale for the multiple languages.
     $modules[] = 'locale';
 
+    // Need Entity Translation for the tokens to work.
+    $modules[] = 'entity_translation';
+
+    // This module.
     $modules[] = 'metatag_hreflang';
 
+    // Enable all of the modules & install the site.
     parent::setUp($modules);
+
+    // Add some new languages.
+    $this->setupLocales($this->supportedLocales());
+
+    // The content that will be used.
+    $content_type = 'page';
+
+    // Create an admin user that can also manage locales.
+    $perms = array(
+      // For Locale, so languages can be added.
+      'administer languages',
+
+      // For Entity Translation, so content can be translated.
+      'translate any entity',
+
+      // For Devel, for access to the node's "devel" tab.
+      'access devel information',
+
+      // For Field UI, so field settings can be changed.
+      'administer fields',
+
+      // For Node, so content type settings can be changed.
+      'administer content types',
+
+      // For Node, so content can be created and edited.
+      'create ' . $content_type . ' content',
+      'edit any ' . $content_type . ' content',
+    );
+    $this->adminUser = $this->createAdminUser($perms);
+
+    // Log in the admin user.
+    $this->drupalLogin($this->adminUser);
+
+    // Enable translation support for the content type.
+    variable_set('language_content_type_' . $content_type, ENTITY_TRANSLATION_ENABLED);
+
+    // Allow the body field to be translated.
+    $this->drupalGet('admin/structure/types/manage/' . $content_type . '/fields/body');
+    $this->assertResponse(200);
+    $this->assertFieldByName('field[translatable]');
+    $edit = array(
+      'field[translatable]' => TRUE,
+    );
+    $this->drupalPost(NULL, $edit, t('Save settings'));
+
+    // Clear all the caches so that all of the various hooks are activated and
+    // the appropriate tokens, fields, meta tags, etc are available.
+    drupal_flush_all_caches();
+  }
+
+  /**
+   * The list of locales that are being tested.
+   *
+   * @return array
+   *   A simple list of language codes.
+   */
+  private function supportedLocales() {
+    return array(
+      'de',
+      'fr',
+      'es',
+      'en',
+    );
+  }
+
+  /**
+   * Assert that the appropriate hreflang meta tag fields are present.
+   *
+   * @param string $form_langcode
+   *   The langcode of the current form. Defaults to LANGUAGE_NONE, which is
+   *   what is used on an empty node/add form.
+   */
+  private function assertHreflangFields($form_langcode = LANGUAGE_NONE) {
+    // The x-default field has a specific default.
+    $this->assertFieldByName("metatags[{$form_langcode}][hreflang_xdefault][value]", "[node:url-original]", 'Found the hreflang=x-default meta tag and it has the correct default value.');
+
+    // Confirm each of the support locales has its own field and the appropriate
+    // default value.
+    foreach ($this->supportedLocales() as $langcode) {
+      $this->assertFieldByName("metatags[{$form_langcode}][hreflang_{$langcode}][value]", "[node:url-{$langcode}]", format_string('Found the hreflang field for the "%lang" locale and it has the correct default value.', array('%lang' => $langcode)));
+    }
+  }
+
+  /**
+   * Confirm that each locale has a field added and shows the appropriate
+   * default value.
+   */
+  function testFormFields() {
+    $this->drupalGet('node/add/page');
+    $this->assertResponse(200);
+
+    // Confirm the fields exist.
+    $this->assertHreflangFields();
+  }
+
+  /**
+   * Confirm that the meta tags output are correct.
+   */
+  function testOutput() {
+    // All of the locales we're supporting in these tests. The languages have
+    // been enabled already, so this gets a list of language objects.
+    $languages = language_list('enabled');
+    $locales = $languages[1];
+
+    // Identify the site's default language.
+    $default_language = language_default('language');
+
+    // Create an English node so it can be translated.
+    $args = array(
+      'language' => $default_language,
+    );
+    $node = $this->drupalCreateNode($args);
+    $this->verbose($node);
+
+    // Load the translation page.
+    $this->drupalGet('node/' . $node->nid . '/translate');
+    $this->assertResponse(200);
+    $this->assertText(t('Not translated'));
+
+    // Confirm that there are links to translate the node.
+    $urls = array();
+    foreach ($locales as $langcode => $locale) {
+      // There won't be a link to translate to English, that's the default
+      // language for thos node.
+      if ($langcode == $default_language) {
+        continue;
+      }
+
+      // Confirm that a link to translate the node into each locale exists.
+      $url = 'node/' . $node->nid . '/edit/add/' . $node->language . '/' . $langcode;
+      $urls[$langcode] = $url;
+      // @todo This fails in testbot.
+      // $this->assertLinkbyHref(url($url));
+    }
+
+    // Check each of the 'translate' pages loads properly.
+    foreach ($urls as $langcode => $url) {
+      // Confirm the 'translate' page loads.
+      $this->drupalGet($url);
+      $this->assertResponse(200);
+
+      // Confirm all of the hreflang fields exist.
+      $this->assertHreflangFields($langcode);
+
+      // Save the translation.
+      $edit = array(
+        // Add a custom title.
+        "metatags[{$langcode}][title][value]" => "Tranlation for {$langcode}",
+      );
+      $this->drupalPost(NULL, $edit, t('Save'));
+    }
+
+    // Load the translation page again to confirm everything was translated.
+    $this->drupalGet('node/' . $node->nid . '/translate');
+    $this->assertResponse(200);
+    $this->assertNoText(t('Not translated'));
+
+    // Load the node's devel page to see the translations data.
+    $this->drupalGet('node/' . $node->nid . '/devel');
+    $this->assertResponse(200);
+
+    // Load the node's devel page and confirm each of the tokens is available.
+    $this->drupalGet('node/' . $node->nid . '/devel/token');
+    $this->assertResponse(200);
+    foreach ($locales as $langcode => $locale) {
+      $this->assertText("[node:url-{$langcode}]");
+    }
+
+    // Load the node page again, confirm each hreflang meta tag.
+    $this->drupalGet('node/' . $node->nid);
+    $this->assertResponse(200);
+    $xpath = $this->xpath("//link[@rel='alternate']");
+    $this->verbose($xpath);
+    $this->assertEqual(count($xpath), count($locales), 'The correct number of hreflang meta tags was found');
+
+    // Try to find the position of the xdefault value in the $xpath structure.
+    $xdefault_pos = NULL;
+    // This is slightly messy logic as the sort order of $locales may be
+    // different to the meta tags.
+    foreach ($locales as $langcode => $locale) {
+      $found = FALSE;
+      foreach ($xpath as $ctr => $item) {
+        if ($item['hreflang'] == 'x-default') {
+          $xdefault_pos = $ctr;
+        }
+        elseif ($item['hreflang'] == $langcode) {
+          $found = TRUE;
+          $this->assertEqual($xpath[$ctr]['hreflang'], $langcode);
+          // @todo Fix this. Not sure why, but the url() call returns the URL
+          // without the language prefix.
+          // $url_options = array(
+          //   'language' => $locale,
+          //   'absolute' => TRUE,
+          // );
+          // $this->assertEqual($xpath[$ctr]['href'], url('node/' . $node->nid, $url_options));
+        }
+      }
+
+      // The node's default language should not have been found, it should have
+      // been turned into an xdefault.
+      if ($langcode == $node->language) {
+        $this->assertFalse((bool)$found, format_string("A regular hreflang meta tag for the node's default language (%lang) was not found.", array('%lang' => $langcode)));
+      }
+
+      // Translations should have been found.
+      else {
+        $this->assertTrue((bool)$found, format_string('The hreflang meta tag for %lang was found.', array('%lang' => $langcode)));
+      }
+    }
+
+    // Confirm the hreflang=xdefault meta tag was found.
+    $this->assertNotNull($xdefault_pos, 'The hreflang=xdefault meta tag was found.');
+    if (!is_null($xdefault_pos)) {
+      $this->assertEqual($xpath[$xdefault_pos]['href'], url('node/' . $node->nid, array('absolute' => TRUE)), 'Found the x-default value.');
+    }
+
+    // Enable the xdefault-dupe option.
+    variable_set('metatag_hreflang_allow_dupe', TRUE);
+    metatag_config_cache_clear();
+
+    // Load the node page again.
+    $this->drupalGet('node/' . $node->nid);
+    $this->assertResponse(200);
+
+    // Confirm there are now more meta tags.
+    $xpath = $this->xpath("//link[@rel='alternate']");
+    $this->verbose($xpath);
+    $this->assertEqual(count($xpath), count($locales) + 1, 'The correct number of hreflang meta tags was found.');
+    $found = FALSE;
+    foreach ($xpath as $ctr => $item) {
+      if ($item['hreflang'] == $node->language) {
+        $found = $ctr;
+      }
+    }
+    $this->assertTrue((bool)$found, "Found an hreflang meta tag for the node's default locale.");
+    if ($found) {
+      $this->assertEqual($xpath[$found]['hreflang'], $node->language);
+    }
   }
 
-  // @todo Make sure the hreflang meta tag is added for each enabled language.
 }

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

@@ -4,14 +4,14 @@ core = 7.x
 package = SEO
 
 ; Need Metatag.
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; Tests.
 files[] = tests/metatag_importer.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 2 - 2
sites/all/modules/contrib/seo/metatag/metatag_importer/metatag_importer.page_title.inc

@@ -2,7 +2,7 @@
 /**
  * @file
  * Functionality for migrating data from the Page Title module.
- */  
+ */
 
 /**
  * FormAPI callback for the Page Title importer.
@@ -70,7 +70,7 @@ function metatag_importer_for_page_title() {
         if (!empty($metatag_config_node_type) && isset($metatag_config_node_type->config['title'])) {
           $title_setting = $metatag_config_node_type->config['title']['value'];
         }
-        else if (isset($metatag_config_node->config['title'])) {
+        elseif (isset($metatag_config_node->config['title'])) {
           $title_setting = $metatag_config_node->config['title']['value'];
         }
         else {

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_importer/tests/metatag_importer.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag Importer module.
  */
-
 class MetatagImporterTest extends MetatagTestHelper {
 
   /**

+ 29 - 28
sites/all/modules/contrib/seo/metatag/metatag_mobile/README.txt

@@ -4,11 +4,12 @@ This submodule of Metatag adds a number of new meta tags commonly used for
 tailoring the experience of people using mobile devices.
 
 Mobile:
-  <meta name="theme-color" value="[VALUE]" />
-  <meta name="MobileOptimized" value="[VALUE]" />
-  <meta name="HandheldFriendly" value="[VALUE]" />
-  <meta name="viewport" value="[VALUE]" />
+  <meta name="theme-color" content="[VALUE]" />
+  <meta name="MobileOptimized" content="[VALUE]" />
+  <meta name="HandheldFriendly" content="[VALUE]" />
+  <meta name="viewport" content="[VALUE]" />
   <meta http-equiv="cleartype" content="[VALUE]" />
+  <link rel="amphtml" href="[VALUE]" />
 
 iOS:
   <meta name="apple-itunes-app" content="[VALUE]" />
@@ -16,37 +17,37 @@ iOS:
   <meta name="apple-mobile-web-app-status-bar-style" content="[VALUE]" />
   <meta name="apple-mobile-web-app-title" content="[VALUE]" />
   <meta name="format-detection" content="[VALUE]" />
-  <link href="alternative" value="ios-app://[VALUE]" />
+  <link rel="alternate" href="ios-app://[VALUE]" />
 
 Android:
-  <link href="manifest" value="[VALUE]" />
-  <link href="alternative" value="android-app://[VALUE]" />
+  <link rel="manifest" href="[VALUE]" />
+  <link rel="alternate" href="android-app://[VALUE]" />
 
 Windows:
   <meta http-equiv="X-UA-Compatible" content="[VALUE]" />
-  <meta name="application-name" value="[VALUE]" />
-  <meta name="msapplication-allowDomainApiCalls" value="[VALUE]" />
-  <meta name="msapplication-allowDomainMetaTags" value="[VALUE]" />
-  <meta name="msapplication-badge" value="[VALUE]" />
-  <meta name="msapplication-config" value="[VALUE]" />
-  <meta name="msapplication-navbutton" value="[VALUE]" />
-  <meta name="msapplication-notification" value="[VALUE]" />
-  <meta name="msapplication-square150x150logo" value="[VALUE]" />
-  <meta name="msapplication-square310x310logo" value="[VALUE]" />
-  <meta name="msapplication-square70x70logo" value="[VALUE]" />
-  <meta name="msapplication-wide310x150logo" value="[VALUE]" />
-  <meta name="msapplication-starturl" value="[VALUE]" />
-  <meta name="msapplication-task" value="[VALUE]" />
-  <meta name="msapplication-task-separator" value="[VALUE]" />
-  <meta name="msapplication-tilecolor" value="[VALUE]" />
-  <meta name="msapplication-tileimage" value="[VALUE]" />
-  <meta name="msapplication-tooltip" value="[VALUE]" />
-  <meta name="msapplication-window" value="[VALUE]" />
+  <meta name="application-name" content="[VALUE]" />
+  <meta name="msapplication-allowDomainApiCalls" content="[VALUE]" />
+  <meta name="msapplication-allowDomainMetaTags" content="[VALUE]" />
+  <meta name="msapplication-badge" content="[VALUE]" />
+  <meta name="msapplication-config" content="[VALUE]" />
+  <meta name="msapplication-navbutton" content="[VALUE]" />
+  <meta name="msapplication-notification" content="[VALUE]" />
+  <meta name="msapplication-square150x150logo" content="[VALUE]" />
+  <meta name="msapplication-square310x310logo" content="[VALUE]" />
+  <meta name="msapplication-square70x70logo" content="[VALUE]" />
+  <meta name="msapplication-wide310x150logo" content="[VALUE]" />
+  <meta name="msapplication-starturl" content="[VALUE]" />
+  <meta name="msapplication-task" content="[VALUE]" />
+  <meta name="msapplication-task-separator" content="[VALUE]" />
+  <meta name="msapplication-tilecolor" content="[VALUE]" />
+  <meta name="msapplication-tileimage" content="[VALUE]" />
+  <meta name="msapplication-tooltip" content="[VALUE]" />
+  <meta name="msapplication-window" content="[VALUE]" />
 
 
 Configuration
 --------------------------------------------------------------------------------
-By default the two link alternative meta tags include a prefix - "android-app://" and "ios-app://". To remove this prefix just change the theme
+By default the two link alternate meta tags include a prefix - "android-app://" and "ios-app://". To remove this prefix just change the theme
 functions, e.g.:
 
 /**
@@ -56,7 +57,7 @@ functions, e.g.:
  */
 function MYTHEME_metatag_mobile_android_app($variables) {
   // Pass everything through to the normal 'link' tag theme.
-  $variables['element']['#name'] = 'alternative';
+  $variables['element']['#name'] = 'alternate';
 
   // Don't actually want this.
   // $variables['element']['#value'] = 'android-app://' . $variables['element']['#value'];
@@ -71,7 +72,7 @@ function MYTHEME_metatag_mobile_android_app($variables) {
  */
 function MYTHEME_metatag_mobile_ios_app($variables) {
   // Pass everything through to the normal 'link' tag theme.
-  $variables['element']['#name'] = 'alternative';
+  $variables['element']['#name'] = 'alternate';
 
   // Don't actually want this.
   // $variables['element']['#value'] = 'ios-app://' . $variables['element']['#value'];

+ 5 - 4
sites/all/modules/contrib/seo/metatag/metatag_mobile/metatag_mobile.info

@@ -2,14 +2,15 @@ name = "Metatag: Mobile & UI Adjustments"
 description = "Provides support for meta tags used to control the mobile browser experience."
 package = SEO
 core = 7.x
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; Tests.
 files[] = tests/metatag_mobile.test
+files[] = tests/metatag_mobile.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 58 - 39
sites/all/modules/contrib/seo/metatag/metatag_mobile/metatag_mobile.metatag.inc

@@ -10,7 +10,7 @@
 function metatag_mobile_metatag_info() {
   $info['groups']['mobile'] = array(
     'label' => t('Mobile & UI Adjustments'),
-    'description' => t("Meta tags used to control the mobile browser experience. Some of these meta tags have been replaced by newer mobile browsers. These meta tags usually only need to be set globally, rather than per-page."),
+    'description' => t('Meta tags used to control the mobile browser experience. Some of these meta tags have been replaced by newer mobile browsers. These meta tags usually only need to be set globally, rather than per-page.'),
     'form' => array(
       '#weight' => 80,
     ),
@@ -24,14 +24,14 @@ function metatag_mobile_metatag_info() {
   );
   $info['groups']['android_mobile'] = array(
     'label' => t('Android'),
-    'description' => t("Custom meta tags used by the Android OS, browser, etc."),
+    'description' => t('Custom meta tags used by the Android OS, browser, etc.'),
     'form' => array(
       '#weight' => 82,
     ),
   );
   $info['groups']['windows_mobile'] = array(
     'label' => t('Windows & Windows Mobile'),
-    'description' => t("Custom meta tags used by the Windows and Windows Mobile OSes, IE browser, etc."),
+    'description' => t('Custom meta tags used by the Windows and Windows Mobile OSes, IE browser, etc.'),
     'form' => array(
       '#weight' => 83,
     ),
@@ -40,7 +40,7 @@ function metatag_mobile_metatag_info() {
   $weight = 80;
 
   // Default values for each meta tag.
-  $tag_info_defaults = array(
+  $defaults = array(
     'description' => '',
     'class' => 'DrupalTextMetaTag',
     'group' => 'mobile',
@@ -50,27 +50,27 @@ function metatag_mobile_metatag_info() {
     'label' => t('Theme Color'),
     'description' => t('A color in hexidecimal format, e.g. "#0000ff" for blue; must include the "#" symbol. Used by some browsers to control the background color of the toolbar, the color used with an icon, etc.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['MobileOptimized'] = array(
     'label' => t('Mobile Optimized'),
     'description' => t('Using the value "width" tells certain mobile Internet Explorer browsers to display as-is, without being resized. Alternatively a numerical width may be used to indicate the desired page width the page should be rendered in: "240" is the suggested default, "176" for older browsers or "480" for newer devices with high DPI screens.'),
     'weight' => ++$weight,
     'multiple' => TRUE,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['HandheldFriendly'] = array(
     'label' => t('Handheld-Friendly'),
     'description' => t('Some older mobile browsers will expect this meta tag to be set to "true" to indicate that the site has been designed with mobile browsers in mind.'),
     'weight' => ++$weight,
     'multiple' => TRUE,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['viewport'] = array(
     'label' => t('Viewport'),
     'description' => t('Used by most contemporary browsers to control the display for mobile browsers. Please read a guide on responsive web design for details of what values to use.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['cleartype'] = array(
     'label' => t('Cleartype'),
@@ -79,10 +79,29 @@ function metatag_mobile_metatag_info() {
     'element' => array(
       '#theme' => 'metatag_http_equiv',
     ),
-  ) + $tag_info_defaults;
+  ) + $defaults;
+
+  $info['tags']['amphtml'] = array(
+    'label' => t('AMP URL'),
+    'description' => t('Provides an absolute URL to an AMP-formatted version of the current page. See the <a href="@url">official AMP specifications</a> for details on how the page should be formatted.', array('@url' => 'https://www.ampproject.org/')),
+    'class' => 'DrupalLinkMetaTag',
+    'weight' => ++$weight,
+  ) + $defaults;
+
+  $info['tags']['alternate_handheld'] = array(
+    'label' => t('Handheld URL'),
+    'description' => t('Provides an absolute URL to a specially formatted version of the current page designed for "feature phones", mobile phones that do not support modern browser standards. See the <a href="@url">official Google Mobile SEO Guide</a> for details on how the page should be formatted.', array('@url' => 'https://developers.google.com/webmasters/mobile-sites/mobile-seo/other-devices?hl=en#feature_phones')),
+    'class' => 'DrupalLinkMetaTag',
+    'weight' => ++$weight,
+    'element' => array(
+      '#theme' => 'metatag_mobile_alt_handheld',
+      '#name' => 'alternate',
+      '#media' => 'handheld',
+    ),
+  ) + $defaults;
 
   // Default values for each meta tag.
-  $tag_info_defaults = array(
+  $defaults = array(
     'description' => '',
     'class' => 'DrupalTextMetaTag',
     'group' => 'apple_mobile',
@@ -92,31 +111,31 @@ function metatag_mobile_metatag_info() {
     'label' => t('iTunes App details'),
     'description' => t('This informs iOS devices to display a banner to a specific app. If used, it must provide the "app-id" value, the "affiliate-data" and "app-argument" values are optional.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['apple-mobile-web-app-capable'] = array(
     'label' => t('Web app capable?'),
     'description' => t('If set to "yes", the application will run in full-screen mode; the default behavior is to use Safari to display web content.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['apple-mobile-web-app-status-bar-style'] = array(
     'label' => t('Status bar color'),
     'description' => t('Requires the "Web app capable" meta tag to be set to "yes". May be set to "default", "black", or "black-translucent".'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['apple-mobile-web-app-title'] = array(
     'label' => t('Apple Mobile Web App Title'),
     'description' => t('Overrides the long site title when using the Apple Add to Home Screen.'),
     'weight' => ++$weight
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['format-detection'] = array(
     'label' => t('Format detection'),
     'description' => t('If set to "telephone=no" the page will not be checked for phone numbers, which would be presented.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['ios-app-link-alternative'] = array(
     'label' => t('iOS app link alternative'),
@@ -127,10 +146,10 @@ function metatag_mobile_metatag_info() {
       '#theme' => 'metatag_mobile_ios_app',
     ),
     'header' => FALSE,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   // Default values for each meta tag.
-  $tag_info_defaults = array(
+  $defaults = array(
     'description' => '',
     'class' => 'DrupalTextMetaTag',
     'group' => 'android_mobile',
@@ -145,7 +164,7 @@ function metatag_mobile_metatag_info() {
       '#theme' => 'metatag_mobile_android_app',
     ),
     'header' => FALSE,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['android-manifest'] = array(
     'label' => t('Manifest'),
@@ -155,10 +174,10 @@ function metatag_mobile_metatag_info() {
     'element' => array(
       '#name' => 'manifest',
     ),
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   // Default values for each meta tag.
-  $tag_info_defaults = array(
+  $defaults = array(
     'description' => '',
     'class' => 'DrupalTextMetaTag',
     'group' => 'windows_mobile',
@@ -171,120 +190,120 @@ function metatag_mobile_metatag_info() {
     'element' => array(
       '#theme' => 'metatag_http_equiv',
     ),
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['application-name'] = array(
     'label' => t('Application name'),
     'description' => t('The default name displayed with the pinned sites tile (or icon). Set the content attribute to the desired name.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-allowDomainApiCalls'] = array(
     'label' => t('MSApplication - Allow domain API calls'),
     'description' => t('Allows tasks to be defined on child domains of the fully qualified domain name associated with the pinned site. Should be either "true" or "false".'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-allowDomainMetaTags'] = array(
     'label' => t('MSApplication - Allow domain meta tags'),
     'description' => t('Allows tasks to be defined on child domains of the fully qualified domain name associated with the pinned site. Should be either "true" or "false".'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-badge'] = array(
     'label' => t('MSApplication - Badge'),
     'description' => t('A semi-colon -separated string that must contain the "polling-uri=" value with the full URL to a <a href="http://go.microsoft.com/fwlink/p/?LinkID=314019">Badge Schema XML file</a>. May also contain "frequency=" value set to either 30, 60, 360, 720 or 1440 (default) which specifies (in minutes) how often the URL should be polled.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-config'] = array(
     'label' => t('MSApplication - Config'),
     'description' => t('Should contain the full URL to a <a href="https://msdn.microsoft.com/en-us/library/dn320426(v=vs.85).aspx">Browser configuration schema</a> file that further controls tile customizations.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-navbutton-color'] = array(
     'label' => t('MSApplication - Nav button color'),
     'description' => t('Controls the color of the Back and Forward buttons in the pinned site browser window.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-notification'] = array(
     'label' => t('MSApplication - Notification'),
     'description' => t('A semi-colon -separated string containing "polling-uri=" (required), "polling-uri2=", "polling-uri3=", "polling-uri4=" and "polling-uri5=" to indicate the URLs for notifications. May also contain a "frequency=" value to specify how often (in minutes) the URLs will be polled; limited to 30, 60, 360, 720 or 1440 (default). May also contain the value "cycle=" to control the notifications cycle.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-square150x150logo'] = array(
     'label' => t('MSApplication - Square logo, 150px x 150px'),
     'description' => t('The URL to a logo file that is 150px by 150px.'),
     'image' => TRUE,
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-square310x310logo'] = array(
     'label' => t('MSApplication - Square logo, 310px x 310px'),
     'description' => t('The URL to a logo file that is 310px by 310px.'),
     'image' => TRUE,
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-square70x70logo'] = array(
     'label' => t('MSApplication - Square logo, 70px x 70px'),
     'description' => t('The URL to a logo file that is 70px by 70px.'),
     'image' => TRUE,
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-wide310x150logo'] = array(
     'label' => t('MSApplication - Wide logo, 310px x 150px'),
     'description' => t('The URL to a logo file that is 310px by 150px.'),
     'image' => TRUE,
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-starturl'] = array(
     'label' => t('MSApplication - Start URL'),
     'description' => t('The URL to the root page of the site.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-task'] = array(
     'label' => t('MSApplication - Task'),
     'description' => t('A semi-colon -separated string defining the "jump" list task. Should contain the "name=" value to specify the task\'s name, the "action-uri=" value to set the URL to load when the jump list is clicked, the "icon-uri=" value to set the URL to an icon file to be displayed, and "window-type=" set to either "tab" (default), "self" or "window" to control how the link opens in the browser.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-task-separator'] = array(
     'label' => t('MSApplication - Task separator'),
     'description' => t(''),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-tilecolor'] = array(
     'label' => t('MSApplication - Tile color'),
     'description' => t('The HTML color to use as the background color for the live tile.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-tileimage'] = array(
     'label' => t('MSApplication - Tile image'),
     'description' => t('The URL to an image to use as the background for the live tile.'),
     'image' => TRUE,
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-tooltip'] = array(
     'label' => t('MSApplication - Tooltip'),
     'description' => t('Controls the text shown in the tooltip for the pinned site\'s shortcut.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   $info['tags']['msapplication-window'] = array(
     'label' => t('MSApplication - Window'),
     'description' => t('A semi-colon -separated value that controls the dimensions of the initial window. Should contain the values "width=" and "height=" to control the width and height respectively.'),
     'weight' => ++$weight,
-  ) + $tag_info_defaults;
+  ) + $defaults;
 
   return $info;
 }

+ 24 - 2
sites/all/modules/contrib/seo/metatag/metatag_mobile/metatag_mobile.module

@@ -23,6 +23,9 @@ function metatag_mobile_theme() {
   $info['metatag_mobile_ios_app'] = array(
     'render element' => 'element',
   );
+  $info['metatag_mobile_alt_handheld'] = array(
+    'render element' => 'element',
+  );
 
   return $info;
 }
@@ -35,7 +38,7 @@ function metatag_mobile_theme() {
  */
 function theme_metatag_mobile_android_app($variables) {
   // Pass everything through to the normal 'link' tag theme.
-  $variables['element']['#name'] = 'alternative';
+  $variables['element']['#name'] = 'alternate';
   $variables['element']['#value'] = 'android-app://' . $variables['element']['#value'];
 
   return theme('metatag_link_rel', $variables);
@@ -49,12 +52,31 @@ function theme_metatag_mobile_android_app($variables) {
  */
 function theme_metatag_mobile_ios_app($variables) {
   // Pass everything through to the normal 'link' tag theme.
-  $variables['element']['#name'] = 'alternative';
+  $variables['element']['#name'] = 'alternate';
   $variables['element']['#value'] = 'ios-app://' . $variables['element']['#value'];
 
   return theme('metatag_link_rel', $variables);
 }
 
+
+/**
+ * Theme callback for a handheld-formatted alternative URL.
+ *
+ * The format is:
+ * <link rel="alternate" media="handheld" href="https://phone.example.com/the/page" />
+ */
+function theme_metatag_mobile_alt_handheld($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#name' => 'rel',
+    '#media' => 'media',
+    '#value' => 'href',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}
+
 /*
 * theme-color
 * MobileOptimized

+ 220 - 0
sites/all/modules/contrib/seo/metatag/metatag_mobile/tests/metatag_mobile.tags.test

@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * Tests that each of the Metatag Mobile tags work correctly.
+ */
+class MetatagMobileTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: Mobile',
+      'description' => 'Test the mobile meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'alternate_handheld',
+    'amphtml',
+    'android-app-link-alternative',
+    'android-manifest',
+    'apple-itunes-app',
+    'apple-mobile-web-app-capable',
+    'apple-mobile-web-app-status-bar-style',
+    'apple-mobile-web-app-title',
+    'application-name',
+    'cleartype',
+    'format-detection',
+    'HandheldFriendly',
+    'ios-app-link-alternative',
+    'MobileOptimized',
+    'msapplication-allowDomainApiCalls',
+    'msapplication-allowDomainMetaTags',
+    'msapplication-badge',
+    'msapplication-config',
+    'msapplication-navbutton-color',
+    'msapplication-notification',
+    'msapplication-square150x150logo',
+    'msapplication-square310x310logo',
+    'msapplication-square70x70logo',
+    'msapplication-starturl',
+    'msapplication-task',
+    'msapplication-task-separator',
+    'msapplication-tilecolor',
+    'msapplication-tileimage',
+    'msapplication-tooltip',
+    'msapplication-wide310x150logo',
+    'msapplication-window',
+    'theme-color',
+    'viewport',
+    'x-ua-compatible',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_mobile';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    // These tags all use dashes instead of underlines.
+    $tag_name = str_replace('_', '-', $tag_name);
+
+    // Fix a few specific tags.
+    $tag_name = str_replace('mobileoptimized', 'MobileOptimized', $tag_name);
+    $tag_name = str_replace('handheldfriendly', 'HandheldFriendly', $tag_name);
+
+    return $tag_name;
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'alternate-handheld'.
+   */
+  public function alternate_handheld_test_output_xpath() {
+    return "//link[@rel='alternate' and @media='handheld']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value_attribute() for 'alternate-handheld'.
+   */
+  public function alternate_handheld_test_value_attribute() {
+    return 'href';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'amphtml'.
+   */
+  public function amphtml_test_output_xpath() {
+    return "//link[@rel='amphtml']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value_attribute() for 'amphtml'.
+   */
+  public function amphtml_test_value_attribute() {
+    return 'href';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'android-app-link-alternative'.
+   */
+  public function android_app_link_alternative_test_output_xpath() {
+    return "//link[@rel='alternate' and starts-with(@href, 'android-app:')]";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_preprocess_output() for
+   * 'android-app-link-alternative'.
+   */
+  public function android_app_link_alternative_test_preprocess_output($string) {
+    return 'android-app://' . $string;
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value_attribute() for
+   * 'android-app-link-alternative'.
+   */
+  public function android_app_link_alternative_test_value_attribute() {
+    return 'href';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for 'android_manifest'.
+   */
+  public function android_manifest_test_output_xpath() {
+    return "//link[@rel='manifest']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value_attribute() for 'android_manifest'.
+   */
+  public function android_manifest_test_value_attribute() {
+    return 'href';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_name_attribute() for 'cleartype'.
+   */
+  public function cleartype_test_name_attribute() {
+    return 'http-equiv';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_xpath() for
+   * 'ios_app_link_alternative'.
+   */
+  public function ios_app_link_alternative_test_output_xpath() {
+    return "//link[@rel='alternate' and starts-with(@href, 'ios-app:')]";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_output_prefix() for
+   * 'ios_app_link_alternative'.
+   */
+  public function ios_app_link_alternative_test_preprocess_output($string) {
+    return 'ios-app://' . $string;
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value_attribute() for
+   * 'ios_app_link_alternative'.
+   */
+  public function ios_app_link_alternative_test_value_attribute() {
+    return 'href';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'msapplication-square150x150logo'.
+   */
+  public function msapplication_square150x150logo_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'msapplication-square310x310logo'.
+   */
+  public function msapplication_square310x310logo_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'msapplication-square70x70logo'.
+   */
+  public function msapplication_square70x70logo_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'msapplication-tileimage'.
+   */
+  public function msapplication_tileimage_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'msapplication-wide310x150logo'.
+   */
+  public function msapplication_wide310x150logo_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_name_attribute() for 'x-ua-compatible'.
+   */
+  public function x_ua_compatible_test_name_attribute() {
+    return 'http-equiv';
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_mobile/tests/metatag_mobile.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag Mobile module.
  */
-
 class MetatagMobileTest extends MetatagTestHelper {
 
   /**

+ 5 - 4
sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.info

@@ -2,14 +2,15 @@ name = Metatag: OpenGraph
 description = Provides support for Open Graph Protocol meta tags.
 package = SEO
 core = 7.x
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; Tests.
 files[] = tests/metatag_opengraph.test
+files[] = tests/metatag_opengraph.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 2 - 2
sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.install

@@ -80,7 +80,7 @@ function metatag_opengraph_update_7103(&$sandbox) {
   module_load_include('install', 'metatag');
   $old_tag = 'og:video';
   $new_tag = 'og:video:url';
-  return metatag_update_replace_meta_tag($sandbox, $old_tag, $new_tag);
+  return metatag_update_replace_entity_tag($sandbox, $old_tag, $new_tag);
 }
 
 /**
@@ -90,7 +90,7 @@ function metatag_opengraph_update_7104() {
   module_load_include('install', 'metatag');
   $old_tag = 'og:video';
   $new_tag = 'og:video:url';
-  return metatag_update_replace_config($old_tag, $new_tag);
+  return metatag_update_replace_config_tag($old_tag, $new_tag);
 }
 
 /**

+ 2 - 0
sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.metatag.inc

@@ -603,6 +603,7 @@ function _metatag_opengraph_type_options() {
       'city' => t('City'),
       'country' => t('Country'),
       'landmark' => t('Landmark'),
+      'place' => t('Place'),
       'state_province' => t('State or province'),
     ),
     t('Products and Entertainment') => array(
@@ -612,6 +613,7 @@ function _metatag_opengraph_type_options() {
       'food' => t('Food'),
       'game' => t('Game'),
       'product' => t('Product'),
+      'product.group' => t('Product group'),
       'song' => t('Song'),
       'video.movie' => t('Movie'),
       'video.tv_show' => t('TV show'),

+ 178 - 0
sites/all/modules/contrib/seo/metatag/metatag_opengraph/tests/metatag_opengraph.tags.test

@@ -0,0 +1,178 @@
+<?php
+
+/**
+ * Tests that each of the Metatag OpenGraph tags work correctly.
+ */
+class MetatagOpenGraphTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: OpenGraph',
+      'description' => 'Test the OpenGraph meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'article:author',
+    'article:expiration_time',
+    'article:modified_time',
+    'article:published_time',
+    'article:publisher',
+    'article:section',
+    'article:tag',
+    'book:author',
+    'book:isbn',
+    'book:release_date',
+    'book:tag',
+    'og:audio',
+    'og:audio:secure_url',
+    'og:audio:type',
+    'og:country_name',
+    'og:description',
+    'og:determiner',
+    'og:email',
+    'og:fax_number',
+    'og:image',
+    'og:image:height',
+    'og:image:secure_url',
+    'og:image:type',
+    'og:image:url',
+    'og:image:width',
+    'og:latitude',
+    'og:locale',
+    'og:locale:alternate',
+    'og:locality',
+    'og:longitude',
+    'og:phone_number',
+    'og:postal_code',
+    'og:region',
+    'og:see_also',
+    'og:site_name',
+    'og:street_address',
+    'og:title',
+    'og:type',
+    'og:updated_time',
+    'og:url',
+    'og:video:height',
+    'og:video:secure_url',
+    'og:video:type',
+    'og:video:url',
+    'og:video:width',
+    'profile:first_name',
+    'profile:gender',
+    'profile:last_name',
+    'profile:username',
+    'video:actor',
+    'video:actor:role',
+    'video:director',
+    'video:duration',
+    'video:release_date',
+    'video:series',
+    'video:tag',
+    'video:writer',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_opengraph';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_name_attribute = 'property';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    // All OG tags use colons to separate levels.
+    $tag_name = str_replace('_', ':', $tag_name);
+
+    // Fix a few specific tags.
+    $tag_name = str_replace('secure:url', 'secure_url', $tag_name);
+    $tag_name = str_replace(':time', '_time', $tag_name);
+    $tag_name = str_replace(':date', '_date', $tag_name);
+    $tag_name = str_replace(':name', '_name', $tag_name);
+    $tag_name = str_replace(':address', '_address', $tag_name);
+    $tag_name = str_replace('see:also', 'see_also', $tag_name);
+    $tag_name = str_replace(':number', '_number', $tag_name);
+    $tag_name = str_replace(':code', '_code', $tag_name);
+
+    return $tag_name;
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_key() for 'og:type'.
+   */
+  public function og_type_test_key() {
+    return 'metatags[und][og:type][value]';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'og:type'.
+   */
+  public function og_type_test_value() {
+    return 'article';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_field_xpath() for 'og:type'.
+   */
+  public function og_type_test_field_xpath() {
+    return "//select[@name='metatags[und][og:type][value]']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_key() for 'og:determiner'.
+   */
+  public function og_determiner_test_key() {
+    return 'metatags[und][og:determiner][value]';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'og:determiner'.
+   */
+  public function og_determiner_test_value() {
+    return 'a';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_field_xpath() for 'og:determiner'.
+   */
+  public function og_determiner_test_field_xpath() {
+    return "//select[@name='metatags[und][og:determiner][value]']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'og:image'.
+   */
+  public function og_image_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'og:image:url'.
+   */
+  public function og_image_url_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'og:image:secure_url'.
+   */
+  public function og_image_secure_url_test_value() {
+    return str_replace('http://', 'https://', $this->randomImageUrl());
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_opengraph/tests/metatag_opengraph.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag OpenGraph module.
  */
-
 class MetatagOpenGraphTest extends MetatagTestHelper {
 
   /**

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

@@ -1,16 +1,17 @@
-name = Metatag:OpenGraph Products
+name = Metatag: OpenGraph Products
 description = Provides additional Open Graph Protocol meta tags for describing products.
 package = SEO
 core = 7.x
-dependencies[] = metatag
-dependencies[] = metatag_opengraph
+dependencies[] = metatag:metatag
+dependencies[] = metatag:metatag_opengraph
 
 ; Tests.
 files[] = tests/metatag_opengraph_products.test
+files[] = tests/metatag_opengraph_products.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

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

@@ -11,7 +11,7 @@ function metatag_opengraph_products_metatag_info() {
   // Open Graph products.
   $info['groups']['open-graph-products'] = array(
     'label' => t('Open Graph - Products'),
-    'description' => t("These Open Graph meta tags for describing products.", array('@ogp' => 'http://ogp.me/')),
+    'description' => t("These Open Graph meta tags for describing products."),
     'form' => array(
       '#weight' => 51,
     ),

+ 83 - 0
sites/all/modules/contrib/seo/metatag/metatag_opengraph_products/tests/metatag_opengraph_products.tags.test

@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * Tests that each of the Metatag OpenGraph Products tags work correctly.
+ */
+class MetatagOpenGraphProductsTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: OpenGraph Products',
+      'description' => 'Test the OpenGraph Products meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'product:availability',
+    'product:brand',
+    'product:category',
+    'product:color',
+    'product:condition',
+    'product:ean',
+    'product:expiration_time',
+    'product:isbn',
+    'product:material',
+    'product:mfr_part_no',
+    'product:pattern',
+    'product:plural_title',
+    'product:price:amount',
+    'product:price:currency',
+    'product:product_link',
+    'product:retailer',
+    'product:retailer_part_no',
+    'product:retailer_title',
+    'product:shipping_cost:amount',
+    'product:shipping_cost:currency',
+    'product:shipping_weight:units',
+    'product:shipping_weight:value',
+    'product:size',
+    'product:upc',
+    'product:weight:units',
+    'product:weight:value',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_opengraph_products';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $test_name_attribute = 'property';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    // All OG tags use colons to separate levels.
+    $tag_name = str_replace('_', ':', $tag_name);
+
+    // Fix a few specific tags.
+    $tag_name = str_replace(':weight', '_weight', $tag_name);
+    $tag_name = str_replace('product_weight', 'product:weight', $tag_name);
+    $tag_name = str_replace(':cost', '_cost', $tag_name);
+    $tag_name = str_replace(':part:no', '_part_no', $tag_name);
+    $tag_name = str_replace(':title', '_title', $tag_name);
+    $tag_name = str_replace(':link', '_link', $tag_name);
+    $tag_name = str_replace(':time', '_time', $tag_name);
+
+    return $tag_name;
+  }
+
+}

+ 1 - 3
sites/all/modules/contrib/seo/metatag/metatag_opengraph_products/tests/metatag_opengraph_products.test

@@ -1,11 +1,9 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag OpenGraph Products module.
  */
-
 class MetatagOpenGraphProductsTest extends MetatagTestHelper {
-
   /**
    * {@inheritdoc}
    */

+ 5 - 5
sites/all/modules/contrib/seo/metatag/metatag_panels/metatag_panels.info

@@ -3,16 +3,16 @@ description = Provides Metatag integration within the Panels interface.
 package = SEO
 core = 7.x
 
-dependencies[] = metatag
-dependencies[] = panels
+dependencies[] = metatag:metatag
+dependencies[] = panels:panels
 
 ; Tests.
 files[] = tests/metatag_panels.test
 files[] = tests/metatag_panels.i18n.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 9 - 3
sites/all/modules/contrib/seo/metatag/metatag_panels/tests/metatag_panels.i18n.test

@@ -1,10 +1,10 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag module for the direct Panels integration, using i18n.
  */
-
 class MetatagPanelsI18nTest extends MetatagTestHelper {
+
   /**
    * {@inheritdoc}
    */
@@ -13,6 +13,7 @@ class MetatagPanelsI18nTest extends MetatagTestHelper {
       'name' => 'Metatag:Panels i18n tests',
       'description' => 'Test Metatag integration via the Metatag:Panels module.',
       'group' => 'Metatag',
+      'dependencies' => array('ctools', 'token', 'panels', 'page_manager', 'i18n'),
     );
   }
 
@@ -36,6 +37,9 @@ class MetatagPanelsI18nTest extends MetatagTestHelper {
 
     parent::setUp($modules);
 
+    // Add more locales.
+    $this->setupLocales();
+
     // Set up the locales.
     $perms = array(
       'administer languages',
@@ -45,7 +49,9 @@ class MetatagPanelsI18nTest extends MetatagTestHelper {
       'translate user-defined strings',
     );
     $this->adminUser = $this->createAdminUser($perms);
-    $this->setupLocales();
+
+    // Log in the admin user.
+    $this->drupalLogin($this->adminUser);
 
     // Reload the translations.
     drupal_flush_all_caches();

+ 3 - 2
sites/all/modules/contrib/seo/metatag/metatag_panels/tests/metatag_panels.test

@@ -1,10 +1,10 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag module for the direct Panels integration.
  */
-
 class MetatagPanelsTest extends MetatagTestHelper {
+
   /**
    * {@inheritdoc}
    */
@@ -13,6 +13,7 @@ class MetatagPanelsTest extends MetatagTestHelper {
       'name' => 'Metatag:Panels tests',
       'description' => 'Test Metatag integration via the Metatag:Panels module.',
       'group' => 'Metatag',
+      'dependencies' => array('ctools', 'token', 'panels', 'page_manager'),
     );
   }
 

+ 7 - 7
sites/all/modules/contrib/seo/metatag/metatag_panels/tests/metatag_panels_tests.info

@@ -5,14 +5,14 @@ core = 7.x
 ; Don't show this on the modules admin page.
 hidden = TRUE
 
-dependencies[] = ctools
-dependencies[] = page_manager
-dependencies[] = metatag
-dependencies[] = metatag_panels
+dependencies[] = ctools:ctools
+dependencies[] = ctools:page_manager
+dependencies[] = metatag:metatag
+dependencies[] = metatag:metatag_panels
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 5 - 4
sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.info

@@ -2,14 +2,15 @@ name = Metatag: Twitter Cards
 description = "Provides support for Twitter's Card meta tags."
 package = SEO
 core = 7.x
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; Tests.
 files[] = tests/metatag_twitter_cards.test
+files[] = tests/metatag_twitter_cards.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 2 - 2
sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.install

@@ -27,7 +27,7 @@ function metatag_twitter_cards_update_7100(&$sandbox) {
   module_load_include('install', 'metatag');
   $old_tag = 'twitter:image:src';
   $new_tag = 'twitter:image';
-  return metatag_update_replace_meta_tag($sandbox, $old_tag, $new_tag);
+  return metatag_update_replace_entity_tag($sandbox, $old_tag, $new_tag);
 }
 
 /**
@@ -37,7 +37,7 @@ function metatag_twitter_cards_update_7101() {
   module_load_include('install', 'metatag');
   $old_tag = 'twitter:image:src';
   $new_tag = 'twitter:image';
-  return metatag_update_replace_config($old_tag, $new_tag);
+  return metatag_update_replace_config_tag($old_tag, $new_tag);
 }
 
 /**

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

@@ -367,7 +367,7 @@ function metatag_twitter_cards_metatag_info() {
   ) + $defaults;
   $info['tags']['twitter:app:id:googleplay'] = array(
     'label' => t('Google Play app ID'),
-    'description' => t("String value, and should be the domain hierarchy representation of your app's ID in Google Play."),
+    'description' => t("Your app ID in the Google Play Store (i.e. \"com.android.app\")."),
     'weight' => ++$weight,
     'element' => array(
       '#theme' => 'metatag_twitter_cards'

+ 137 - 0
sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/tests/metatag_twitter_cards.tags.test

@@ -0,0 +1,137 @@
+<?php
+
+/**
+ * Tests that each of the Metatag Twitter Cards tags work correctly.
+ */
+class MetatagTwitterCardsTagsTest extends MetatagTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tags: Twitter Cards',
+      'description' => 'Test the Twitter Cards meta tags.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public $tags = array(
+    'twitter:app:country',
+    'twitter:app:id:googleplay',
+    'twitter:app:id:ipad',
+    'twitter:app:id:iphone',
+    'twitter:app:name:googleplay',
+    'twitter:app:name:ipad',
+    'twitter:app:name:iphone',
+    'twitter:app:url:googleplay',
+    'twitter:app:url:ipad',
+    'twitter:app:url:iphone',
+    'twitter:card',
+    'twitter:creator',
+    'twitter:creator:id',
+    'twitter:data1',
+    'twitter:data2',
+    'twitter:description',
+    'twitter:image',
+    'twitter:image0',
+    'twitter:image1',
+    'twitter:image2',
+    'twitter:image3',
+    'twitter:image:alt',
+    'twitter:image:height',
+    'twitter:image:width',
+    'twitter:label1',
+    'twitter:label2',
+    'twitter:player',
+    'twitter:player:height',
+    'twitter:player:stream',
+    'twitter:player:stream:content_type',
+    'twitter:player:width',
+    'twitter:site',
+    'twitter:site:id',
+    'twitter:title',
+    'twitter:url',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'metatag_twitter_cards';
+    parent::setUp($modules);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTestTagName($tag_name) {
+    // All OG tags use colons to separate levels.
+    $tag_name = str_replace('_', ':', $tag_name);
+
+    // Fix a few specific tags.
+    $tag_name = str_replace('content:type', 'content_type', $tag_name);
+
+    return $tag_name;
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_key() for 'twitter:card'.
+   */
+  public function twitter_card_test_key() {
+    return 'metatags[und][twitter:card][value]';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'twitter:card'.
+   */
+  public function twitter_card_test_value() {
+    return 'summary';
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_field_xpath() for 'twitter:card'.
+   */
+  public function twitter_card_test_field_xpath() {
+    return "//select[@name='metatags[und][twitter:card][value]']";
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'twitter:image'.
+   */
+  public function twitter_image_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'twitter:image0'.
+   */
+  public function twitter_image0_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'twitter:image1'.
+   */
+  public function twitter_image1_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'twitter:image2'.
+   */
+  public function twitter_image2_test_value() {
+    return $this->randomImageUrl();
+  }
+
+  /**
+   * Implements {meta_tag_name}_test_value() for 'twitter:image3'.
+   */
+  public function twitter_image3_test_value() {
+    return $this->randomImageUrl();
+  }
+
+}

+ 1 - 2
sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/tests/metatag_twitter_cards.test

@@ -1,9 +1,8 @@
 <?php
+
 /**
- * @file
  * Tests for the Metatag Twitter Cards module.
  */
-
 class MetatagTwitterCardsTest extends MetatagTestHelper {
 
   /**

+ 5 - 4
sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.info

@@ -2,14 +2,15 @@ name = Metatag: Verification
 description = "Various meta tags for verifying ownership of a site."
 package = SEO
 core = 7.x
-dependencies[] = metatag
+dependencies[] = metatag:metatag
 
 ; Tests.
 files[] = tests/metatag_verification.test
+files[] = tests/metatag_verification.tags.test
 
-; Information added by Drupal.org packaging script on 2016-06-30
-version = "7.x-1.17"
+; Information added by Drupal.org packaging script on 2017-02-15
+version = "7.x-1.21"
 core = "7.x"
 project = "metatag"
-datestamp = "1467306248"
+datestamp = "1487171290"
 

+ 25 - 0
sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.install

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @file
+ * Update scripts for the Metatag:Verification submodule.
+ */
+
+/**
+ * Implementations of hook_update_N().
+ */
+
+/**
+ * Remove the Alexa verification tag.
+ */
+function metatag_verification_update_7100() {
+  module_load_include('install', 'metatag');
+  metatag_update_delete_config('alexaVerifyID');
+}
+
+/**
+ * Remove the Yahoo verification tag.
+ */
+function metatag_verification_update_7101() {
+  module_load_include('install', 'metatag');
+  metatag_update_delete_config('y_key');
+}

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