Bachir Soussi Chiadmi преди 9 години
родител
ревизия
faf4573c53
променени са 74 файла, в които са добавени 11835 реда и са изтрити 0 реда
  1. 583 0
      sites/all/modules/contrib/seo/metatag/CHANGELOG.txt
  2. 339 0
      sites/all/modules/contrib/seo/metatag/LICENSE.txt
  3. 319 0
      sites/all/modules/contrib/seo/metatag/README.txt
  4. BIN
      sites/all/modules/contrib/seo/metatag/arrow-down.png
  5. BIN
      sites/all/modules/contrib/seo/metatag/arrow-right.png
  6. 88 0
      sites/all/modules/contrib/seo/metatag/metatag.admin.css
  7. 665 0
      sites/all/modules/contrib/seo/metatag/metatag.admin.inc
  8. 48 0
      sites/all/modules/contrib/seo/metatag/metatag.admin.js
  9. 372 0
      sites/all/modules/contrib/seo/metatag/metatag.api.php
  10. 21 0
      sites/all/modules/contrib/seo/metatag/metatag.drush.inc
  11. 90 0
      sites/all/modules/contrib/seo/metatag/metatag.features.inc
  12. 65 0
      sites/all/modules/contrib/seo/metatag/metatag.feeds.inc
  13. 18 0
      sites/all/modules/contrib/seo/metatag/metatag.i18n.inc
  14. 345 0
      sites/all/modules/contrib/seo/metatag/metatag.inc
  15. 28 0
      sites/all/modules/contrib/seo/metatag/metatag.info
  16. 1557 0
      sites/all/modules/contrib/seo/metatag/metatag.install
  17. 353 0
      sites/all/modules/contrib/seo/metatag/metatag.metatag.inc
  18. 125 0
      sites/all/modules/contrib/seo/metatag/metatag.migrate.inc
  19. 2337 0
      sites/all/modules/contrib/seo/metatag/metatag.module
  20. 390 0
      sites/all/modules/contrib/seo/metatag/metatag.test
  21. 91 0
      sites/all/modules/contrib/seo/metatag/metatag.theme.inc
  22. 193 0
      sites/all/modules/contrib/seo/metatag/metatag.tokens.inc
  23. 57 0
      sites/all/modules/contrib/seo/metatag/metatag.vertical-tabs.js
  24. 40 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/README.txt
  25. 12 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.info
  26. 162 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.metatag.inc
  27. 15 0
      sites/all/modules/contrib/seo/metatag/metatag_app_links/metatag_app_links.module
  28. 31 0
      sites/all/modules/contrib/seo/metatag/metatag_context/README.txt
  29. 247 0
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.admin.inc
  30. 285 0
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.context.inc
  31. 15 0
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.info
  32. 13 0
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.install
  33. 117 0
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.module
  34. 145 0
      sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.test
  35. 79 0
      sites/all/modules/contrib/seo/metatag/metatag_dc/README.txt
  36. 12 0
      sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.info
  37. 185 0
      sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.metatag.inc
  38. 40 0
      sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.module
  39. 13 0
      sites/all/modules/contrib/seo/metatag/metatag_devel/metatag_devel.info
  40. 181 0
      sites/all/modules/contrib/seo/metatag/metatag_devel/metatag_devel.module
  41. 12 0
      sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.info
  42. 5 0
      sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.install
  43. 54 0
      sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.metatag.inc
  44. 26 0
      sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.module
  45. 60 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/README.txt
  46. 25 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.inc
  47. 15 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.info
  48. 139 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.metatag.inc
  49. 64 0
      sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.module
  50. 12 0
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.info
  51. 65 0
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.install
  52. 631 0
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.metatag.inc
  53. 70 0
      sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.module
  54. 27 0
      sites/all/modules/contrib/seo/metatag/metatag_panels/README.txt
  55. 16 0
      sites/all/modules/contrib/seo/metatag/metatag_panels/metatag_panels.info
  56. 179 0
      sites/all/modules/contrib/seo/metatag/metatag_panels/metatag_panels.module
  57. 44 0
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/README.txt
  58. 11 0
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.info
  59. 91 0
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc
  60. 39 0
      sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.module
  61. 16 0
      sites/all/modules/contrib/seo/metatag/metatag_verification/README.txt
  62. 12 0
      sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.info
  63. 54 0
      sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.metatag.inc
  64. 14 0
      sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.module
  65. 16 0
      sites/all/modules/contrib/seo/metatag/metatag_views/README.txt
  66. 16 0
      sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views.info
  67. 33 0
      sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views.metatag.inc
  68. 106 0
      sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views.module
  69. 101 0
      sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views.tokens.inc
  70. 21 0
      sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views.views.inc
  71. 99 0
      sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views_plugin_display_extender_metatags.inc
  72. 12 0
      sites/all/modules/contrib/seo/metatag/tests/metatag_test.info
  73. 54 0
      sites/all/modules/contrib/seo/metatag/tests/metatag_test.metatag.inc
  74. 20 0
      sites/all/modules/contrib/seo/metatag/tests/metatag_test.module

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

@@ -0,0 +1,583 @@
+Metatag 7.x-1.x-dev, xxxx-xx-xx
+-------------------------------
+#2442183 by DamienMcKenna, jwilson: Mention the Image URL Formatter module in
+  the README.txt file.
+#2451231 by DamienMcKenna: Fixed the Devel:Generate integration.
+By DamienMcKenna: Rearranged og:type select_or_other integration code to be
+  after the og:type tag definition.
+By DamienMcKenna: Removed duplicate description for video:writer meta tag.
+By DamienMcKenna: Standardized structure of all theme functions.
+#2452985 by DamienMcKenna: Added 39 additional Dublin Core meta tags.
+By DamienMcKenna: Removed duplicate dcterms.rights tag.
+By DamienMcKenna: Updated new 'date' dcterms tags to use 'date' generator.
+#2460791 by DamienMcKenna: Allow the page region used to trigger output to be
+  changed; see the advanced settings page for details.
+#2462117 by DamienMcKenna: Allow the included default configurations to be
+  disabled.
+#2454499 by Dmitriy.trt: metatag_config_is_enabled() returned FALSE for empty
+  config, when checked with defaults.
+#2407477 by greggles: Provide support for Twitter app tags without having to use
+  an "app" type.
+By DamienMcKenna: Fixed a small mistake in output of metatag_update_7011().
+#2429091 by deviantintegral, DamienMcKenna: Add support for applinks.org tags.
+#2417155 by dobe: Feeds import fails because of placement of entity_type.
+By DamienMcKenna: Minor text improvements per D8 branch.
+By DamienMcKenna: Clarified compatibility with Workbench Moderation.
+#2473459 by DamienMcKenna: Updated all links to d.o.
+#2479325 by DamienMcKenna: Require Token 1.6.
+#1491562 by jonathan_hunt, knalstaaf: Add instructions to README.txt explaining
+  how to configure meta tags for entity bundles.
+
+
+Metatag 7.x-1.5-beta1, 2015-02-02
+---------------------------------
+#2362639 by DamienMcKenna: Improved defaults for Google+ meta tags.
+#2318985 by DamienMcKenna: Indicate that Open Graph tags are used on Pinterest.
+#2362639 by DamienMcKenna: Added itemtype default values for Google+ meta tags.
+#2358137 by DamienMcKenna: Added a submodule for managing site verification
+  meta tags, the first of which is for Google.
+#2358131 by DamienMcKenna: Support for the Pinterest verification meta tag.
+#1848338 by larowlan, DamienMcKenna: Added more tests, especially one for
+  checking the editorial process on a node.
+#2362893 by ipo4ka704: Don't assume the first Panels context is an object.
+#2363591 by DamienMcKenna: Added a default for the 'image' meta tag on user
+  entity pages.
+#1967856 by duozersk: Fixed a minor mistake in the previous commit.
+#2370943 by Simon George: Removed redundant comment.
+#2373189 by nmillin: Added support for the Bing verification code.
+#2358139 by nmillin: Added support for the Yandex verification code.
+#2378127 by DamienMcKenna: Support for rel="alternate" hreflang="x" link tag.
+#2376915 by jenlampton, DamienMcKenna: Added the og:image:url meta tag.
+#1978708 by DamienMcKenna, scor: Updated warning about compatibility with the
+  RDF module in Drupal core before 7.33.
+#2385265 by mikemiles86: Correctly flatten Metatag form fieldsets in Context
+  integration.
+#2370439 by potop, DamienMcKenna: Work around hook_entity_load() problems by
+  loading entity info in metatag_entity_supports_metatags() on demand.
+#1868460 by preshetin: Added the rel=prev and rel=next meta tags.
+#2388339 by das-peter: Fix select_or_other integration for Metatag:GooglePlus,
+  add the missing element_validator.
+#2391975 by Spleshka: Support scenarios where the entity is possibly blank or
+  has no entity_id assigned yet, e.g. Profile2 pages.
+#2400241 by greggles: Typo in description of robots-notranslate option.
+#2400529 by greggles: Add support for OG product price:amount, price:currency.
+#2411607 by liberatr, DamienMcKenna: README.txt note about using the
+  field_multiple_types module to control how many items are output.
+#2415025 by DamienMcKenna: l() in metatag_metatag_info() creates recursion bug.
+#2411477 by betz, DamienMcKenna: $form[#entity] doesn't work for all entities.
+#2411549 by maijs: Language is lost during migration.
+#2198669 by D2ev: Using metatag tokens can easily cause an infinite loop.
+#2411477 by DamienMcKenna, betz: Follow-up to last change.
+
+
+Metatag 7.x-1.4, 2014-10-09
+---------------------------
+#2353079 by DamienMcKenna: Fixed Views integration, for real this time.
+#2344877 by DamienMcKenna: Fixed Panels integration, for real this time.
+
+
+Metatag 7.x-1.3, 2014-10-07
+---------------------------
+#2350967 by das-peter, DamienMcKenna: Fatal error occurred loading any View that
+  did not have meta tags assigned.
+#2344877 by DamienMcKenna, Mau Palantír, libelle2000: Fixed Panels integration.
+By DamienMcKenna: metatag_metatags_load()'s documentation was incorrect.
+#2347193 by DamienMcKenna: Updated Feeds integration to be compatible with the
+  new data structures in 1.0, and revision_id problems.
+
+
+Metatag 7.x-1.2, 2014-10-04
+---------------------------
+#2343909 by DamienMcKenna: Unable to update meta tags on nodes that didn't
+  contain translations.
+#2185791 by DamienMcKenna: Improved logic for deciding which meta tag values
+  to use for the current language; new advanced option allows loading of the
+  entity's default language's values if nothing else matches.
+#2346159 by DamienMcKenna: Fixed tag dependencies, which were broken in 1.0.
+#2346153 by DamienMcKenna: Added Twitter app 'name' tags, misc improvements to
+  Twitter Cards code.
+#2185791 by DamienMcKenna: Changed the no-values-to-load entity language default
+  logic so that the default language values will be loaded unless disabled.
+#1304038 by DamienMcKenna: Indicate in the README.txt how to disable output for
+  the three meta tags output by Drupal core by default.
+#2350129 by DamienMcKenna: Added a Drush command for clearing Metatag's caches.
+#2341795 by DamienMcKenna: Updated Metatag:Views to be compatible with the new
+  form data structure in 1.0.
+#2292043 by eric.chenchao, DamienMcKenna: Added Google+ 'itemprop' meta tags.
+#2341795 by DamienMcKenna: Fixed Views previews.
+#2289139 by maijs, DamienMcKenna: Allow each Views display to have different
+  meta tag values.
+
+
+Metatag 7.x-1.1, 2014-09-18
+---------------------------
+#2340639 by agoradesign: Additional check needed in hook_requirements to avoid
+  breaking installation profiles.
+#2340337 by DamienMcKenna: Config system updated for the new language-based
+  data handling.
+#2330823 by DamienMcKenna: REVERT: Remove the deprecated G+ Author meta tag.
+
+
+Metatag 7.x-1.0, 2014-09-17
+---------------------------
+#2319389 by DamienMcKenna: Additional Open Graph meta tags, for videos.
+#2169575 by gvorbeck: Workbench Moderation v2 doesn't need any hackery, so
+  removed the message in hook_requirements().
+#2140189 by ttkaminski, DamienMcKenna: Added an index to {metatag} table for the
+  'type' and 'revision_id' fields.
+#1391554 by DamienMcKenna: Handle scenarios where the legacy "metatags" module
+  had been installed.
+#2325459 by DamienMcKenna: Used JSHint to correct some minor JS bugs.
+#2326197 by Dave Reid: metatag_generate_entity_metatags() cache can be bypassed.
+By DamienMcKenna: Updated the og:image size guidelines.
+By DamienMcKenna: Remove the redundant metatag_taxonomy_term_view_alter().
+By DamienMcKenna: Support Twitter Cards fieldset in Metatag:Context.
+#1778286 by alberto56: Removed the deprecated metatag_ui module.
+#2331677 by othermachines: Updates 7025 and 7027 attempted to update the wrong
+  tables.
+#2330823 by othermachines: Remove the deprecated G+ Author meta tag.
+#2186155 by DamienMcKenna, grahamC, JeroenT: Resolved problems when saving an
+  entity directly rather than via entity form.
+By DamienMcKenna: Corrected the namespace prefix for OG video meta tags.
+#2186155 by DamienMcKenna: Follow-up to fix a number of scenarios.
+
+
+Metatag 7.x-1.0-rc2, 2014-08-05
+-------------------------------
+#1904266 by mvwensen, DamienMcKenna: Added the dcterms.modified meta tag.
+#2202031 by DamienMcKenna: Don't double-encode output, handle   specially.
+#2026343 by DamienMcKenna, skruf, valkum, wxman: Added many more Open Graph meta
+  tags.
+#2164919 by DamienMcKenna: Added an Advanced Settings page.
+#2241083 by DamienMcKenna: API structure for definiting field dependencies;
+  currently limited to hiding/showing fields, can be expanded later with
+  validation logic. Initial implementation for some Open Graph and Twitter Cards
+  meta tags.
+#2307523 by leewillis77, DamienMcKenna: Additional arguments for two
+  drupal_alter hooks.
+#2241083 by rooby: Refactored meta tag output generation using a new function,
+  metatag_generate_entity_metatags(), allowing for the tags to be independently
+  obtained for a given entity.
+#2262159 by DamienMcKenna: Bumped core requirement to 7.28, removed the
+  [node:summary] fix that's no longer needed.
+#2306449 by DamienMcKenna: Not having the Transliteration or Imagecache Token
+  modules installed no longer reports an error in hook_requirements().
+#1328562 by andremolnar, Greg Boggs, DamienMcKenna: Improved form descriptions.
+#1918706 by theunraveler, DamienMcKenna, Zekvyrin, JeroenT: [current-page:title]
+  didn't work correctly on Panels pages.
+#2153977 by paolomainardi, DamienMcKenna: Fix for translations of base entity
+  type configuration when there is no bundle configuration.
+
+
+Metatag 7.x-1.0-rc1, 2014-07-12
+-------------------------------
+By DamienMcKenna: Small improvement to the comment on update 7007.
+#2196393 by generalconsensus, aprohl5: Typo in hook_install().
+#2237507 by SebCorbin: Only delete all records when editing one entity revision.
+#2056739 by B-Prod: Incorrect language handling when displaying entity pages
+  using Panels.
+#2205675 by Romlam, greggles: Typo in variable name caused data to not load.
+#2265447 by opdavies: Ignore comment entities, conflict with comment_fragment.
+#2271685 by adee147: Typos in metatag_metatags_cache_clear().
+#2271811 by DamienMcKenna: Replaced theme_metatag_opengraph() with
+  theme_metatag_property().
+#1282636 by DamienMcKenna: Support meta tags that allow multiple values; first
+  supported tags are og:image and og:image:secure_url.
+#2273459 by DamienMcKenna: Improved Twitter Cards default values.
+#2273241 by DamienMcKenna: Use the new hook_metatag_bundled_config_alter() to
+  load settings from submodules.
+#2273493 by DamienMcKenna: Improved Dublin Core default values.
+#2274921 by DamienMcKenna: Token browser link missing on settings pages.
+#2277787 by eugene.ilyin: Missing translations in metatag_context.
+By DamienMcKenna: Removed duplicate 'devel_generate' setting for 'image_src'.
+#2282903 by DamienMcKenna: Special handling for meta tags that need to output a
+  secure URL, replace 'http://' with 'https://'.
+#2281833 by DamienMcKenna: Ensure multi-item values are output in a consistent
+  order.
+#2275323 by drastik: Provide link to settings page in Metatag:Context module.
+#1284810 by DamienMcKenna: Really recommend installing Imagecache Token.
+#1809356 by DamienMcKenna: Sort all meta tags.
+#2276361 by DamienMcKenna: Move Facebook meta tags into a separate submodule.
+#2185943 by fizk: Remove warnings about Exclude Node Title.
+#2266595 by hefox: Change watchdog() message to a warning not critical, to avoid
+  problems with Jenkins.
+#2193195 by 75th Trombone: Corrected a variable usage in README.txt.
+#1338612 by Lasac, DamienMcKenna: Added the content-language meta tag.
+#2291993 by DamienMcKenna: Duplicate fb meta tags causes lots of errors.
+#2285787 by SebCorbin: Entity Translation problems with revisions.
+#2025425 by moonray, David_Rothstein, hefox, DamienMcKenna: Cache improvement
+  to separate entity vs page language.
+#2186241 by nnevill.io1, DamienMcKenna: Revisions support for Panels.
+#2051407 by cha0s, DamienMcKenna: Language support for token integration.
+#2183203 by mikeytown2, juampy, DamienMcKenna: Improved queries in
+  metatag_metatags_load_multiple().
+#2227377 by DamienMcKenna: taxonomy_vocabulary_load() caused problems when
+  executed during hook_entity_info_alter().
+#1995564 by DamienMcKenna, willieseabrook: Added a warning about a possible
+  conflict with the Admin Language module.
+#2298337 by DamienMcKenna: Added an API option to indicate one meta tag replaces
+  another; updated API docs accordingly.
+#2267501 by DamienMcKenna: Renamed the 'twitter:image' meta tag to the correct
+  'twitter:image:src'.
+#2121437 by DamienMcKenna: Renamed the 'copyright' meta tag to the correct
+  'rights' tag.
+#2177455 by DamienMcKenna: Avoid errors when updating from older releases due
+  to missing revision_id field.
+#2178411 by DamienMcKenna, kporras07: Language not assigned correctly on CTools
+  -based pages.
+
+
+Metatag 7.x-1.0-beta9, 2014-01-18
+---------------------------------
+#2174363 by DamienMcKenna: Changed update 7018 to avoid attempting to create
+  duplicate records when updating; instead should there be a collision the
+  record with revision_id 0 will be deleted.
+#2176351 by DamienMcKenna: 403 and 404 error pages will use the global default
+  for the page title instead of copying the homepage's.
+#2175843 by DamienMcKenna: It was possible to get to update 7016 without the
+  revision_id field existing, so make sure it exists.
+#2081787 by attila.fekete: Don't let Metatag:Views overwrite the frontpage meta
+  tag config, matching how Metatag:Panels works.
+#2176375 by DamienMcKenna: Added note to README.txt about the Textimage module's
+  compatibility with Metatag.
+#2170771 by DamienMcKenna: Added support for the og:image:secure_url meta tag.
+
+
+Metatag 7.x-1.0-beta8, 2014-01-15
+---------------------------------
+#1995284 by DamienMcKenna: Replace $_SERVER['REQUEST_URI'] with request_uri().
+By DamienMcKenna: Updated the README.txt's Credits section to match the project
+  page.
+#1978708 by DamienMcKenna: Added a note to the README.txt, hook_install and
+  hook_requirements to mention that RDF can cause validation errors for the
+  Open Graph meta tag output.
+#1977640 by dsdeiz: Fixed a comment typo.
+#1978730 by DamienMcKenna: Added an installation note to read the README.txt
+  file.
+#1978568 by DamienMcKenna: Strip line breaks in all tag output.
+#1961354 by DamienMcKenna, thesame: Optionally provide additional permissions
+  so that access to modify each meta tag can be controlled individually, see
+  README.txt for more details.
+#1933678 by DamienMcKenna: Default Context configurations for the user login and
+  registration pages.
+#1816856 by DamienMcKenna: Default Context configuration for the main forum
+  page.
+#1292612 by DamienMcKenna: Default Context configuration for the main blog
+  page.
+#1988346 by DamienMcKenna: Form permissions were being overridden thus making
+  the Metatag fieldset visible when it shouldn't have been.
+#1994352 by AmbikaFR: Two strings were not translatable.
+#1970064 by Jorrit: Metatag:Panels did not load the data correctly.
+#1994634 by DamienMcKenna: DrupalTextMetaTag::getValue fails if
+  $options['instance'] element doesn't exist.
+#1994630 by DamienMcKenna: Cleanup/filter all meta tag output.
+By DamienMcKenna: Moved hook_requirements to the top of metatag.install.
+#1982164 by DamienMcKenna: Added hook_requirements note to ensure that Entity
+  Translation is up-to-date.
+#2020565 by DamienMcKenna: Save the correct language value on initial entity
+  creation.
+#1876034 by DamienMcKenna: Updated a comment to indicate that there was a
+  problem with Metatag itself when saving records via node_save(), not
+  Workbench Moderation after all.
+#2024277 by greggles, DamienMcKenna: Don't output a meta tag if the string is
+  blank, but still allow "0" to be output when needed.
+#1999936 by DamienMcKenna: Fixed poor logic for checking if a valid language
+  was available in metatag_metatags_values().
+#2024277 by DamienMcKenna: Follow-up to fix all meta tag output.
+#1498764 by nick_schuch, DamienMcKenna: Added the Revisit-After meta tag.
+#1671846 by benys, DamienMcKenna: Expose meta tags as tokens.
+#1830952 by DYdave, DamienMcKenna: Allow token types and patterns to be altered.
+#1859136 by plopesc, DamienMcKenna: Properly update meta tag records.
+#2045855 by czigor: Fix translation of meta tag info labels.
+#1572474 by PieIsGood, Dan Reinders, DamienMcKenna: Entity revision support.
+#2051401 by cha0s: Remove errant dpm() left in from earlier testing.
+#2037677 by adnasa, DamienMcKenna, tsvenson: UX improvement for the token popup.
+#1985932 by kolier: Correct the taxonomy term token on Panels pages.
+#2033723 by som30ind, DamienMcKenna: Fixed occasional error saving array values,
+  e.g. the ROBOTS tag.
+#1959830 by DamienMcKenna: Added a note to README.txt about Node Form Panes.
+#2061511 by amanire: Verify view display 'path' option exists before using it.
+#1776836 by kobee, DamienMcKenna: Added the Standout meta tag.
+#2095397 by DamienMcKenna: Allow method to skip skipping metatag_entity_view().
+#2095501 by DamienMcKenna: Logic mistake in metatag_metatags_delete_multiple()
+  meant records were never deleted.
+#2072087 by brunascle: Twitter Cards changed to use correct 'name' attribute.
+#2086037 by greggles: Only show schema warning messages to appropriate people.
+#1311050 by pasive, DamienMcKenna: Added the og:locale meta tag.
+#2082539 by DamienMcKenna, hswong3i: {metatag}.revision_id cannot be null.
+#2082539 by DamienMcKenna: Follow-up to make all revision_id values numeric.
+#1848338 by DamienMcKenna: Added a list of test scenarios that need to be added.
+#2152043 by DamienMcKenna: Devel Generate integration via Metatag:Devel
+  submodule.
+#2152043 by DamienMcKenna: Expanded Devel Generate integration to cover almost
+  all included meta tags.
+#1572474 by DamienMcKenna, HyperGlide, jyee, Kristen Pol, sylus: Fixes for
+  revisions support.
+#1876042 by DamienMcKenna: Rename variables to use $entity_id instead of $id
+  in metatag.admin.inc, $entity_type instead of $type in metatag.migrate.inc.
+#2157689/#2088299 by travelertt, iMiksu, DamienMcKenna: JS error broke
+  CKEditor, etc.
+#2168343 by DamienMcKenna: Clear EntityCache bins.
+#2062379 by DamienMcKenna: Restructured caching.
+#2168939 by DamienMcKenna: Don't skip batch processing on updates ran via Drush.
+#2169547 by DamienMcKenna: Clarification on Workbench Moderation support.
+#2090557 by Kristen Pol, DamienMcKenna: Don't cache tags on 403/404 error pages.
+#1848622 by DamienMcKenna: Translation helper for 'bar'.
+#1967856 by duozersk: Support for the noimageindex and notranslate robots tag
+  options.
+#2140463 by zhuber: Small misspelling in a comment.
+#1963678 by DC_Marc, gnuget, Albert Volkman: Additional Twitter Card meta tags.
+#2170363 by juampy: Incorrect data handling in DrupalDefaultMetaTag.
+#1286270 by DamienMcKenna: Provide options for disabling meta tags on specific
+  entity types or entity bundles, see README.txt for details.
+#2071649 by eelkeblok, DamienMcKenna: Verify the entity still exists when
+  loading meta tag data in metatag_ctools_render_alter() and
+  metatag_views_post_render().
+#2126157 by hefox: metatag_entity_has_metatags() returns TRUE for disabled
+  entities, not FALSE.
+#2001178 by jantoine, DamienMcKenna: Verify the language exists before saving.
+#1864306 by hefox: Export the 'disabled' state via Features, thus allowing
+  disabled configurations to be exported too.
+#2172883 by Kristen Pol, DamienMcKenna: Only use Workbench Moderation functions
+  on nodes.
+#1975552 by pivica, DamienMcKenna: Fixed errors when changing {metatag} table's
+  primary keys.
+#1864306 by DamienMcKenna: Follow-on to only export the $config->disabled
+  setting if it exists.
+#2173271 by deetergp: Spelling and grammer fixes for README.txt.
+#2172433 by fabsor, DamienMcKenna: Ensure that update 7015 runs early enough to
+  avoid data corruption or errors during other updates.
+#2156261 by plopesc, DamienMcKenna: Allow meta tags for 403/404 error pages to
+  be configured, along with some reasonable defaults; removed previous option to
+  control caching on these pages, the meta tags are now always cached.
+#2173863 by DamienMcKenna: Don't load meta tags on admin pages, provide setting
+  to override this.
+#2174363 by DamienMcKenna: Don't attempt to create revision records in update
+  7018 if one already exists.
+
+
+Metatag 7.x-1.0-beta7, 2013-04-22
+---------------------------------
+#1970946 by laura s: Twitter Cards no longer requires SSL.
+#1971406 by alextataurov, DamienMcKenna: Correct check to see if i18n is
+  installed.
+#1955898 by DamienMcKenna: Clicking 'cancel' when editing a per-path
+  configuration would cause the config to be deleted.
+#1955894 by plopesc: It wasn't possible to remove values from the
+  Metatag:Context editor.
+#1972038 by DamienMcKenna: Context admin page didn't display the '<front>' path
+  correctly.
+#1970064 by DamienMcKenna: Metatag:Context did not load the data correctly.
+#1970518 by John Morahan: Incorrect syntax in metatag.info.
+#1972932 by chrisjlee: Typo in hook_requirements.
+By DamienMcKenna: Removed trailing space in some files.
+#1951118 by DamienMcKenna: Display a runtime hook_requirements error message if
+  the old metatag.entity_translation.inc file is still present.
+By DamienMcKenna: Removed some tabs that snook in.
+#1973254 by plopesc: Added functional tests for Metatag:Context.
+#1284756 by dsdeiz: Add instructions to metatag.migrate.inc explaining how to
+  use the Migrate integration.
+#1954106 by DamienMcKenna: Simplified the project's name to just 'Metatag'.
+#1974870 by DamienMcKenna: Moved all modules to the 'SEO' package.
+
+
+Metatag 7.x-1.0-beta6, 2013-04-14
+---------------------------------
+#1961448 by DamienMcKenna: Disable the fb:app_id field if fb_social is present.
+#1282620 by idflood, evanbarter, mgifford, Lukas von Blarer, Peacog, zterry95,
+  DamienMcKenna: Configuration translation through integration with the i18n
+  module.
+#1498740 by devuo: Merged Diogo's metatag_panels module.
+#1804356 by Dave Reid: Merged Dave's metatag_views module.
+#1909224 by DamienMcKenna: Fixed sloppy code in metatag_metatags_form_submit().
+#1969428 by DamienMcKenna: Changed the DC 'property' attribute to 'name'.
+#1284756 by dsdeiz: Update Migrate integration for compatibility with v2.5,
+  support additional entity types.
+#1953724 by DamienMcKenna, joshf, wiifm, twistor: PostgreSQL compatibility for
+  recent updates.
+#1295524 by DamienMcKenna: Temporary fix for the [node:summary] token not
+  working.
+#1952190 by DamienMcKenna: Only run queries involving taxonomy data if the
+  Taxonomy module is enabled.
+
+
+Metatag 7.x-1.0-beta5, 2013-03-23
+---------------------------------
+#1844638 by DamienMcKenna: Updated help messages around update 7004, when ran
+  via Drush it will no longer used Batch API.
+#1844764 by Devin Carlson, DamienMcKenna: Fix arg placeholders in t() calls.
+#1846516 by Staratel: Incorrect arguments for watchdog().
+#1846516 by DamienMcKenna: Further incorrect arguments for watchdog().
+#1844638 by DamienMcKenna: Correctly used drupal_is_cli() instead of just
+  php_sapi_name().
+#1846978 by edulterado: Corrected the theme function name used with the
+  Twitter Cards submodule.
+#1307804 by juampy: Support for Select_or_Other for use with the OpenGraph
+  'type' field.
+#1854522 by DamienMcKenna: Redundant return statements in the MetaTag classes.
+#1852600 by DamienMcKenna: Only use the first page argument in the Views and
+  Panels preprocessors if it is numerical.
+#1850014 by plopesc: Not all contexts that may be shown on the admin page will
+  have a path condition defined.
+#1846080 by DamienMcKenna: Only support entities that have the 'metatags'
+  option specifically enabled.
+#1857116 by DamienMcKenna: Purge {metatag} records for a few known unsupported
+  entities that old versions would have saved.
+#1857116 by DamienMcKenna: Don't purge 'file' {metatag} records until #1857334
+  is decided.
+#1857360 by DamienMcKenna: Purge {metatag} records for nodes, taxonomy terms
+  and users that were purged but where the APIs of older versions failed to
+  remove them.
+#1857116 by DamienMcKenna: Purge {metatag} records for Profile2.
+#1852600 by helmo: Typo in Views integration function.
+#1852022 by DamienMcKenna: Don't export the {metatag_config}.cid field.
+#1862570 by DamienMcKenna: Purge any empty values that may have been added by
+  very early releases.
+#1862570 by DamienMcKenna: Follow-up to correctly handle the serialized empty
+  array.
+#1864340 by cdoyle, DamienMcKenna: Incorrect output for certain Twitter Card
+  tags.
+#1865170 by DamienMcKenna: Fix metatag_requirements() return array when the
+  Page Title module is also installed.
+#1722564 by DamienMcKenna: Provide a hook_requirements() message and README.txt
+  note about a possible conflict with the Exclude Node Title module.
+#1284756 by damiankloip, sylus, alanburke, lancee: Migrate module integration.
+#1865228 by greggles, DamienMcKenna: Added the rel=author link meta tag.
+#1866122 by DamienMcKenna: Added the twitter:site:id and twitter:creator:id
+  meta tags.
+#1866980 by makangus: Corrected metatag_features_revert().
+#1862818 by DYdave, DamienMcKenna: Added documentation for
+  hook_metatag_config_default().
+#1778534 by DamienMcKenna: Added the original-source meta tag.
+#1886170 by DamienMcKenna: Typo in the API docs regarding enabling metatag
+  support in custom entities.
+#1871020 by DamienMcKenna: Compatibility problem with Workbench_Moderation.
+#1773926 by Dave Reid: Fixed token validation fails on config edit if the
+  instance context is not an entity type.
+#1814736 by plach, Dave Reid: metatag_page_build() did not check if the
+  global:frontpage metatag configuration is disabled.
+#1871852: Fixed metatag_update_7005() did not check if the watchdog table
+  exists.
+#1891082 by bago, Dave Reid: Fixed metatag_config_instance_label() failed to
+  recurse properly.
+#1915284: Fixed metatag_html_head_alter() stopped removing duplicate tags too
+  soon. Fixed duplicate canonical links when global redirect is enabled.
+#1845326 by DamienMcKenna, Peacog: Resolved language handling problems to
+  correctly identify the langcode to properly work with or without
+  Entity_Translation.
+#1876042 by DamienMcKenna: Rename variables to use $entity_id instead of $id
+  and $entity_type instead of $type.
+#1859136 by splatio, DamienMcKenna, multpix: Feeds integration - allow meta tag
+  fields to be the target for data imported using the Feeds module.
+#1880302 by olli, DamienMcKenna: Resolve problems with Features integration.
+#1923030 by krlucas, DamienMcKenna: Only run metatag_entity_update() on
+  supported entities.
+#1844638 by DamienMcKenna, mikeytown2: Remove unnecessary duplicate {metatag}
+  records, fix language values for all entities.
+#1935084 by DamienMcKenna: Remove unnecessary items from metatag_hook_info()
+  that was causing problems with PHP 5.4.
+#1791720 by kbasarab: Added the news_keywords meta tag.
+#1934492 by juampy, DamienMcKenna: Added a page for reverting meta tags for
+  specific entity or bundle.
+#1386320: Note a known issue of using custom template files that do not output
+  the $page['content'] variable.
+#1917902 by DamienMcKenna: Ensure strings returned from token replacement of
+  text fields ([node:summary]) is passed through the appropriate text filters.
+#1919070 by DamienMcKenna: Fix any records that may have been corrupted by e.g.
+  #1871020.
+#1861656 by DamienMcKenna, torrance123: Optionally load the global meta tags on
+  all pages, enabled by default.
+#1871798 by mstrelan: Clear the Context plugin cache when metatag_context is
+  enabled so that the new plugin becomes available.
+#1932192 by DamienMcKenna: Only run metatag_entity_view() once per page view.
+#1900434 by Dustin Currie, j0rd, DamienMcKenna: Added several new OpenGraph meta
+  tags, including ones for videos, location and contact information.
+#1883118 by DamienMcKenna: Improve the help message on Metatag:Context's Path
+  field as neither relative nor absolute URLs will work.
+#1945114 by SergO, DamienMcKenna: A query from #1919070 was missing the
+  preproccess wrapper around the table name.
+#1908586 by DamienMcKenna: Added a line to README.txt explaining how to
+  customize the tokens used to generate the meta tags.
+#1350610 by DamienMcKenna: metatag_update_7001 needed to drop the primary key
+  before customizing it.
+#1859136 by DamienMcKenna: Fixed scenarios when updating an entity there are two
+  copies of the data submitted, e.g. Feeds integration.
+#1308790 by DamienMcKenna: Documented that [current-user] tokens should not be
+  used.
+#1318294 by DamienMcKenna: Documented how to use Imagecache Token to resize
+  images that are being used as tokens for meta tags.
+#1871534 by DamienMcKenna: Documented how some browser plugins can make the page
+  title appear to be wrapped with doublequotes though the output doesn't
+  actually show them.
+
+
+Metatag 7.x-1.0-beta4, 2012-11-17
+---------------------------------
+#1842764 by DamienMcKenna: Work around problems in metatag_entity_load()
+  stemming from an outdated database schema, leave a message suggesting the
+  site admin run the database updates.
+#1842868 by DamienMcKenna: Changed metatag_update_7003 to automatically assign
+  the correct language per entity, added update_7004 to fix records updated in
+  beta3, fixed the language selection for loading meta tags so sites without
+  translation functionality continue to work correctly.
+#1842868 by DamienMcKenna: Changed update 7003 again so it *only* adds the new
+  field, changed update 7004 so it will update all records using Batch API.
+#1843676 by DamienMcKenna: Changed the hook_requirements message to an INFO
+  message if Page_Title is also installed, will freak people out less.
+
+
+Metatag 7.x-1.0-beta3, 2012-11-16
+---------------------------------
+#1688286 by colan, DamienMcKenna: Support for Entity Translation.
+#1835030 by DamienMcKenna: Documentation and hook_requirements note re Drupal
+  core v7.17.
+#1840402 by DamienMcKenna, paperdhc: Corrected use of array_pop().
+#1841404 by mh86: Don't attempt to load meta tags for unsupported entities, and
+  don't support configuration-only entities.
+#1841564 by peximo: Correctly identify the content language being used on the
+  homepage.
+#1841774 by DamienMcKenna: Provide a warning via hook_requirements if the Page
+  Title module is also enabled, due to the possibilities of complications and
+  unexpected results.
+#1363476 by DamienMcKenna: Workaround to trigger metatag_entity_view() if the
+  current CTools (Panels, Panelizer, etc) page is an entity display page.
+#1842052 by DamienMcKenna: Don't process unsupported entities being displayed
+  via Views.
+#1664322 by nico059, kerasai, miechiel, idflood, DamienMcKenna, alexweber:
+  Twitter Cards meta tags.
+#1842198 by DamienMcKenna: Move the 'advanced' fieldset under the others.
+#1840236 by weri, Marty2081: Only revert the requested feature, not all
+  features.
+
+
+Metatag 7.x-1.0-beta2, 2012-10-30
+---------------------------------
+#1817580 by DamienMcKenna: Removed code that was enabling debug mode on all
+  Contexts.
+#1818240 by DamienMcKenna: Added $instance value to the drupal_alter() call in
+  metatag_metatags_view().
+#1817984 by DamienMcKenna, alexweber: Documented
+  hook_metatag_metatags_view_alter().
+#1818252 by DamienMcKenna: There was no caching on the front page's meta tags.
+#1818516 by DamienMcKenna: Incorrect variable check in metatag_page_build().
+#1818762 by DamienMcKenna: Updated hook_hook_info().
+#1466292 by DamienMcKenna: Listed hooks in metatag.api.php and everywhere the
+  hooks are triggered there's a comment to say what the hook is.
+#1818984 by DamienMcKenna: Add the $instance value to metatag_context's
+  triggering of hook_metatag_metatags_view.
+#1819000 by DamienMcKenna: Don't load default meta tags if no active contexts
+  define meta tags.
+#1819448 by DamienMcKenna: Error on admin page if any meta tags were disabled.
+#1818958 by DamienMcKenna: The $cid_parts array should contain all relevant
+  entity variables.
+#1820362 by DamienMcKenna: $cid_parts should use base_path() instead of '/'.
+#1820374 by DamienMcKenna: Front page $cid_parts did not include the full URL.
+#1822726 by DamienMcKenna: Ensure the CTools exportables system is loaded.
+#1818300 by eugene.ilyin, DamienMcKenna: Improved Features integration.
+#1151936 by DamienMcKenna, maximpodorov: Workaround to trigger
+  metatag_entity_view() if the current Views page is an entity display page.
+
+
+Metatag 7.x-1.0-beta1, 2012-10-19
+---------------------------------
+First mostly-stable release.

+ 339 - 0
sites/all/modules/contrib/seo/metatag/LICENSE.txt

@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

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

@@ -0,0 +1,319 @@
+Metatag
+-------
+This module allows you to automatically provide structured metadata, aka "meta
+tags", about your website and web pages.
+
+In the context of search engine optimization, providing an extensive set of
+meta tags may help improve your site's & pages' ranking, thus may aid with
+achieving a more prominent display of your content within search engine
+results. Additionally, using meta tags can help control the summary content
+that is used within social networks when visitors link to your site,
+particularly the Open Graph submodule for use with Facebook, Pinterest,
+LinkedIn, etc (see below).
+
+This version of the module only works with Drupal 7.28 and newer.
+
+
+Features
+------------------------------------------------------------------------------
+The primary features include:
+
+* The current supported basic meta tags are ABSTRACT, DESCRIPTION, CANONICAL,
+  GENERATOR, IMAGE_SRC, KEYWORDS, PUBLISHER, REVISIT-AFTER, RIGHTS, ROBOTS,
+  SHORTLINK and the page's TITLE tag.
+
+* Multi-lingual support using the Entity Translation module.
+
+* Translation support using the Internationalization (i18n) module.
+
+* Full support for entity revisions and workflows based upon revision editing,
+  including compatibility with the Revisioning and Workbench Moderation modules.
+
+* Per-path control over meta tags using the "Metatag: Context" submodule
+  (requires the Context module).
+
+* Integration with the Views module allowing meta tags to be controlled for
+  individual Views pages, with each display in the view able to have different
+  meta tags, by using the "Metatag: Views" submodule.
+
+* Integration with the Panels module allowing meta tags to be controlled for
+  individual Panels pages, by using the "Metatag: Panels" submodule.
+
+* The fifteen Dublin Core Basic Element Set 1.1 meta tags may be added by
+  enabling the "Metatag: Dublin Core" submodule.
+
+* The Open Graph Protocol meta tags, as used by Facebook, Pinterest, LinkedIn
+  and other sites, may be added by enabling the "Metatag: Open Graph" submodule.
+
+* The Twitter Cards meta tags may be added by enabling the "Metatag: Twitter
+  Cards" submodule.
+
+* Certain meta tags used by Google+ may be added by enabling the "Metatag:
+  Google+" submodule.
+
+* Facebook's fb:app_id and fb:admins meta tags may be added by enabling the
+  "Metatag: Facebook" submodule. These are useful for sites which are using
+  Facebook widgets or are building custom integration with Facebook's APIs,
+  but they are not needed by most sites and have no bearing on the Open Graph
+  meta tags.
+
+* The App Links meta tags may be added by enabling the Metatag: App Links
+  submodule.
+
+* Site verfication meta tags can be added, e.g. as used by the Google search
+  engine to confirm ownership of the site; see the "Metatag: Verification"
+  submodule.
+
+* An API allowing for additional meta tags to be added, beyond what is provided
+  by this module - see metatag.api.php for full details.
+
+* Support for the Migrate module for migrating data from another system - see
+  metatag.migrate.inc for full details.
+
+* Support for the Feeds module for importing data from external data sources or
+  file uploads.
+
+* Integrates with Devel_Generate, part of the Devel module, to automatically
+  generate meta tags for generated nodes, via the Metatag:Devel submodule.
+
+* Integrates with Workbench Moderation (both v1 and v2) allowing meta tags on
+  nodes to be managed through the workflow process.
+
+* The Transliteration and Imagecache Token modules (see below) are highly
+  recommended when using image meta tags, e.g. og:image.
+
+* Several advanced options may be controlled via the Advanced Settings page.
+
+
+Configuration
+------------------------------------------------------------------------------
+ 1. On the People Permissions administration page ("Administer >> People
+    >> Permissions") you need to assign:
+
+    - The "Administer meta tags" permission to the roles that are allowed to
+      access the meta tags admin pages to control the site defaults.
+
+    - The "Edit meta tags" permission to the roles that are allowed to change
+      meta tags on each individual page (node, term, etc).
+
+ 2. The main administrative page controls the site-wide defaults, both global
+    settings and defaults per entity (node, term, etc), in addition to those
+    assigned specifically for the front page:
+      admin/config/search/metatags
+
+ 3. In order to provide a specific configuration per entity bundle (content
+    type, vocabulary, etc), click "Add a Metatag default".
+
+ 4. Each supported entity object (nodes, terms, users) will have a set of meta
+    tag fields available for customization on their respective edit page, these
+    will inherit their values from the defaults assigned in #2 above. Any
+    values that are not overridden per object will automatically update should
+    the defaults be updated.
+
+ 5. As the meta tags are output using Tokens, it may be necessary to customize
+    the token display for the site's entities (content types, vocabularies,
+    etc). To do this go to e.g., admin/structure/types/manage/article/display,
+    in the "Custom Display Settings" section ensure that "Tokens" is checked
+    (save the form if necessary), then to customize the tokens go to:
+    admin/structure/types/manage/article/display/token
+
+
+Internationalization: i18n.module
+------------------------------------------------------------------------------
+All default configurations may be translated using the Internationalization
+(i18n) module. The custom strings that are assigned to e.g., the "Global: Front
+page" configuration will show up in the Translate Interface admin page
+(admin/config/regional/translate/translate) and may be customized per language.
+
+
+Fine Tuning
+------------------------------------------------------------------------------
+All of these may be controlled from the advanced settings page:
+admin/config/search/metatags/settings
+
+* It is possible to "disable" the meta tags provided by Drupal core, i.e.
+  "generator", "canonical URL" and "shortlink", though it may not be completely
+  obvious. Metatag takes over the display of these tags, thus any changes made
+  to them in Metatag will supercede Drupal's normal output. To hide a tag, all
+  that is necessary is to clear the default value for that tag, e.g. on the
+  global settings for nodes, which will result in the tag not being output for
+  those pages.
+* By default Metatag will load the global default values for all pages that do
+  not have meta tags assigned via the normal entity display or via Metatag
+  Context. This may be disabled by setting the variable 'metatag_load_all_pages'
+  to FALSE through one of the following methods:
+  * Use Drush to set the value:
+    drush vset metatag_load_all_pages FALSE
+  * Hardcode the value in the site's settings.php file:
+    $conf['metatag_load_all_pages'] = FALSE;
+  To re-enable this option simply set the value to TRUE.
+* By default users will be able to edit meta tags on forms based on the 'Edit
+  meta tags' permission. The 'metatag_extended_permissions' variable may be set
+  to TRUE to give each individual meta tag a separate permission. This allows
+  fine-tuning of the site's editorial control, and for rarely-used fields to be
+  hidden from most users. Note: The 'Edit meta tags' permission is still
+  required otherwise none of the meta tag fields will display at all. The
+  functionality may be disabled again by either removing the variable or
+  setting it to FALSE.
+* It's possible to disable Metatag integration for certain entity types or
+  bundles using variables. To disable an entity just assigning a variable
+  'metatag_enable_{$entity_type}' or 'metatag_enable_{$entity_type}__{$bundle}'
+  the value FALSE, e.g.:
+    // Disable metatags for file_entity.
+    $conf['metatag_enable_file'] = FALSE;
+    // Disable metatags for carousel nodes.
+    $conf['metatag_enable_node__carousel'] = FALSE;
+  To enable the entity and/or bundle simply set the value to TRUE or remove the
+  settings.php line. Note that the Metatag cache will need to be cleared after
+  changing these settings, specifically the 'info' records, e.g., 'info:en'; a
+  quick version of doing this is to clear the site caches using either Drush,
+  Admin Menu (flush all caches), or the "Clear all caches" button on
+  admin/config/development/performance.
+* By default Metatag will not display meta tags on admin pages. To enable meta
+  tags on admin pages simply set the 'metatag_tag_admin_pages' variable to TRUE
+  through one of the following methods:
+  * Use Drush to set the value:
+    drush vset metatag_tag_admin_pages TRUE
+  * Hardcode the value in the site's settings.php file:
+    $conf['metatag_tag_admin_pages'] = TRUE;
+  To re-enable this option simply set the value to FALSE or delete the
+  settings.php line.
+* When loading an entity with multiple languages for a specific language the
+  meta tag values saved for that language will be used if they exist, otherwise
+  values assigned to the entity's default language will be used. This
+  may be disabled using the enabling the "Don't load entity's default language
+  values if no languages match" option on the Advanced Settings page, which will
+  cause default values to be used should there not be any values assigned for
+  the current requested language.
+* When using Features to export Metatag configurations, it is suggested to
+  override all of the default configurations and then disable the default
+  configurations via the advanced settings page; doing so will avoid potential
+  conflicts of the same configurations being loaded by both the Metatag module
+  and the new Features-based modules.
+
+
+Developers
+------------------------------------------------------------------------------
+Full API documentation is available in metatag.api.php.
+
+To enable Metatag support in custom entities, add 'metatag' => TRUE to either
+the entity or bundle definition in hook_entity_info(); see metatag.api.php for
+further details and example code.
+
+The meta tags for a given entity object (node, etc) can be obtained as follows:
+  $metatags = metatags_get_entity_metatags($entity_id, $entity_type, $langcode);
+The result will be a nested array of meta tag structures ready for either output
+via drupal_render(), or examining to identify the actual text values.
+
+
+Troubleshooting / Known Issues
+------------------------------------------------------------------------------
+* Image fields do not output very easily in meta tags, e.g. for og:image,
+  without use of the Imagecache Token module (see below). This also provides a
+  way of using an image style to resize the original images first, rather than
+  requiring visitors download multi-megabyte original images.
+* When using custom page template files, e.g., page--front.tpl.php, it is
+  important to ensure that the following code is present in the template file:
+    <?php render($page['content']); ?>
+  or
+    <?php render($page['content']['metatags']); ?>
+  Without one of these being present the meta tags will not be displayed.
+* An alternative method to fixing the missing-tags problem is to change the page
+  region used to output the meta tags. The region used may be controlled from
+  the settings page, it is recommended to test different options to identify the
+  one that works best for a specific site.
+* Versions of Drupal older than v7.17 were missing necessary functionality for
+  taxonomy term pages to work correctly.
+* Using Metatag with values assigned for the page title and the Page Title
+  module simultaneously can cause conflicts and unexpected results.
+* 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.
+* Certain browser plugins, e.g., on Chrome, can cause the page title to be
+  displayed with additional double quotes, e.g., instead of:
+    <title>The page title | My cool site</title>
+  it will show:
+    <title>"The page title | My cool site"</title>
+  The solution is to remove the browser plugin - the page's actual output is not
+  affected, it is just a problem in the browser.
+* Drupal core versions before v7.33 had a bug which caused validation problems
+  in the Open Graph output if the RDF module was also enabled. The solution is
+  to update to core v7.33 or newer.
+* If the Administration Language (admin_language) module is installed, it is
+  recommended to disable the "Force language neutral aliases" setting on the
+  Admin Language settings page, i.e. set the "admin_language_force_neutral"
+  variable to FALSE. Failing to do so can lead to data loss in Metatag.
+
+
+Related modules
+------------------------------------------------------------------------------
+Some modules are available that extend Metatag with additional functionality:
+
+* Image URL Formatter
+  https://www.drupal.org/project/image_url_formatter
+  Provides a formatter for file and image fields to output the raw URL, and
+  optionally pass it through an image style. Useful for getting an image
+  field's token to output correctly for use in a meta tag.
+
+* Imagecache Token
+  https://www.drupal.org/project/imagecache_token
+  Provides additional tokens for image fields that can be used in e.g. the
+  og:image meta tag; ultimately makes it possible to actually use image meta
+  tags without writing custom code.
+
+* Transliteration
+  https://drupal.org/project/transliteration
+  Tidies up filenames for uploaded files, e.g. it can remove commas from
+  filenames that could otherwise break certain meta tags.
+
+* Alternative hreflang
+  https://www.drupal.org/project/hreflang
+  Output <link rel="alternate" hreflang="x" href="http://" /> meta tags for
+  each language available on the site.
+
+* Domain Meta Tags
+  https://drupal.org/project/domain_meta
+  Integrates with the Domain Access module, so each site of a multi-domain
+  install can separately control their meta tags.
+
+* Select or Other
+  https://drupal.org/project/select_or_other
+  Enhances the user experience of the metatag_opengraph submodule by allowing
+  the creation of custom Open Graph types.
+
+* Node Form Panes
+  https://drupal.org/project/node_form_panes
+  Create custom node-edit forms and control the location of the Metatag fields.
+
+* Textimage
+  https://drupal.org/project/textimage
+  Supports using Textimage's custom tokens in meta tag fields.
+
+* Field Multiple Limit
+  https://drupal.org/project/field_multiple_limit
+  Allows control over how many items are output in a multi-item field, useful
+  with meta tags that only allow for one item but which are assigned from fields
+  which accept multiple items, e.g. og:audio and og:video.
+
+
+Credits / Contact
+------------------------------------------------------------------------------
+Currently maintained by Damien McKenna [1] and Dave Reid [2]; all initial
+development was by Dave Reid.
+
+Ongoing development is sponsored by Mediacurrent [3] and Lullabot [4]. All
+initial development was sponsored by Acquia [5] and Palantir.net [6].
+
+The best way to contact the authors is to submit an issue, be it a support
+request, a feature request or a bug report, in the project issue queue:
+  https://www.drupal.org/project/issues/metatag
+
+
+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/

BIN
sites/all/modules/contrib/seo/metatag/arrow-down.png


BIN
sites/all/modules/contrib/seo/metatag/arrow-right.png


+ 88 - 0
sites/all/modules/contrib/seo/metatag/metatag.admin.css

@@ -0,0 +1,88 @@
+div.metatag-config-label {
+  padding-left: 20px;
+}
+
+div.metatag-config-label > a {
+  font-weight: bold;
+}
+
+.metatag-config-label.collapsed {
+  background-image: url('arrow-right.png');
+  background-repeat: no-repeat;
+  background-position: center left;
+}
+
+.metatag-config-label.expanded {
+  background-image: url('arrow-down.png');
+  background-repeat: no-repeat;
+  background-position: center left;
+}
+
+table.metatag-config-overview > tbody > tr > td:first-child {
+  width: 60%;
+}
+
+table.metatag-config-overview > tbody > tr > td {
+  vertical-align: top;
+}
+
+table.metatag-config-overview tbody div.indent {
+  margin-left: 20px;
+}
+
+table.metatag-config-overview tr.disabled > td:first-child a {
+  color: #999;
+}
+
+div.metatag-config-details {
+  color: #666;
+  margin-left: 20px;
+}
+
+div.metatag-config-details p {
+  margin: 0.75em 0;
+}
+
+div.metatag-config-details div.inheritance {
+  font-style: italic;
+}
+
+div.metatag-config-details table.metatag-value-summary {
+  border: none;
+  width: auto;
+  margin: 0.75em 0;
+}
+
+div.metatag-config-details table.metatag-value-summary td {
+  padding: 0 0.5em 0 0;
+  color: #666;
+}
+
+div.metatag-config-details table.metatag-value-summary tr,
+div.metatag-config-details table.metatag-value-summary tr td:last-child {
+  background-color: transparent;
+  border: none;
+}
+
+div.metatag-config-details table.metatag-value-summary tr td:first-child {
+  font-weight: bold;
+  padding-right: 2em;
+}
+
+/*div.metatag-config-details ul {
+  margin-left: 2em;
+}
+
+div.metatag-config-details ul li {
+  line-height: 105%;
+}
+
+div.metatag-config-details > p {
+  margin: 0.25em 0;
+}*/
+
+
+/* For the advanced settings page */
+.metatag-bundle-checkbox {
+  margin-left: 2em;
+}

+ 665 - 0
sites/all/modules/contrib/seo/metatag/metatag.admin.inc

@@ -0,0 +1,665 @@
+<?php
+
+/**
+ * @file
+ * Administration page callbacks for the metatag module.
+ */
+
+function _metatag_config_sort($a, $b) {
+  $return = NULL;
+  $a_contexts = explode(':', $a->instance);
+  $b_contexts = explode(':', $b->instance);
+  for ($i = 0; $i < max(count($a_contexts), count($b_contexts)); $i++) {
+    $a_context = isset($a_contexts[$i]) ? $a_contexts[$i] : '';
+    $b_context = isset($b_contexts[$i]) ? $b_contexts[$i] : '';
+    if ($a_context == $b_context) {
+      continue;
+    }
+    elseif ($a_context == 'global') {
+      $return = -1;
+    }
+    elseif ($a_context == '') {
+      $return = -1;
+    }
+    else {
+      $return = strcmp($a_context, $b_context);
+    }
+  }
+  return $return;
+}
+
+function _metatag_config_overview_indent($text, $instance) {
+  $parents = metatag_config_get_parent_instances($instance);
+  array_shift($parents);
+
+  // Add indentation to the leading cell.
+  if (!empty($parents)) {
+    $prefix = array_fill(0, count($parents), '<div class="indent">');
+    $suffix = array_fill(0, count($parents), '</div>');
+    $text = implode('', $prefix) . $text . implode('', $suffix);
+  }
+
+  return $text;
+}
+
+function metatag_config_overview() {
+  ctools_include('export');
+
+  $metatags = metatag_get_info('tags');
+
+  $configs = ctools_export_crud_load_all('metatag_config');
+  ksort($configs);
+  //uasort($configs, '_metatag_config_sort');
+
+  $rows = array();
+  foreach ($configs as $config) {
+    $row = array();
+
+    // Style disabled configurations differently.
+    if (!empty($config->disabled)) {
+      $row['class'][] = 'disabled';
+    }
+
+    $details = '<div class="metatag-config-label collapsed"><a href="#" class="toggle-details">' . check_plain(metatag_config_instance_label($config->instance)) . '</a></div>';
+    $details .= '<div class="metatag-config-details js-hide">';
+
+    $inherits = array();
+    $parents = metatag_config_get_parent_instances($config->instance);
+    array_shift($parents);
+    foreach (array_reverse($parents) as $parent) {
+      if (!isset($configs[$parent])) {
+        $rows[$parent] = array(
+          _metatag_config_overview_indent('<div class="metatag-config-label">' . check_plain(metatag_config_instance_label($parent)) . '</div>', $parent),
+          '',
+        );
+      }
+      else {
+        $inherits[$parent] = metatag_config_instance_label($parent);
+        if (!empty($configs[$parent]->disabled)) {
+          $inherits[$parent] .= ' ' . t('(disabled)');
+        }
+      }
+    }
+
+    // Show how this config inherits from its parents.
+    if (!empty($inherits)) {
+      $details .= '<div class="inheritance"><p>' . t('Inherits meta tags from: @parents', array('@parents' => implode(', ', $inherits))) . '</p></div>';
+    }
+
+    // Add a summary of the configuration's defaults.
+    $summary = array();
+    foreach ($config->config as $metatag => $data) {
+      // Skip meta tags that were disabled.
+      if (empty($metatags[$metatag])) {
+        continue;
+      }
+      $summary[] = array(
+        check_plain($metatags[$metatag]['label']) . ':',
+        check_plain(metatag_get_value($metatag, $data, array('raw' => TRUE))),
+      );
+    }
+    if (!empty($summary)) {
+      $details .= theme('table', array(
+        'rows' => $summary,
+        'attributes' => array('class' => array('metatag-value-summary')),
+      ));
+    }
+    else {
+      $details .= '<p class="warning">No overridden default meta tags</p>';
+      $row['class'][] = 'warning';
+    }
+
+    // Close the details div
+    $details .= '</div>';
+
+    // Add indentation to the leading cell based on how many parents the config has.
+    $details = _metatag_config_overview_indent($details, $config->instance);
+
+    $row['data']['details'] = $details;
+
+    $operations = array();
+    if (metatag_config_access('disable', $config)) {
+      $operations['edit'] = array(
+        'title' => ($config->export_type & EXPORT_IN_DATABASE) ? t('Edit') : t('Override'),
+        'href' => 'admin/config/search/metatags/config/' . $config->instance,
+      );
+    }
+    if (metatag_config_access('enable', $config)) {
+      $operations['enable'] = array(
+        'title' => t('Enable'),
+        'href' => 'admin/config/search/metatags/config/' . $config->instance . '/enable',
+        'query' => array(
+          'token' => drupal_get_token('enable-' . $config->instance),
+        ) + drupal_get_destination(),
+      );
+    }
+    if (metatag_config_access('disable', $config)) {
+      $operations['disable'] = array(
+        'title' => t('Disable'),
+        'href' => 'admin/config/search/metatags/config/' . $config->instance . '/disable',
+        'query' => array(
+          'token' => drupal_get_token('disable-' . $config->instance),
+        ) + drupal_get_destination(),
+      );
+    }
+    if (metatag_config_access('revert', $config)) {
+      $operations['revert'] = array(
+        'title' => t('Revert'),
+        'href' => 'admin/config/search/metatags/config/' . $config->instance . '/revert',
+      );
+    }
+    if (metatag_config_access('delete', $config)) {
+      $operations['delete'] = array(
+        'title' => t('Delete'),
+        'href' => 'admin/config/search/metatags/config/' . $config->instance . '/delete',
+      );
+    }
+    $operations['export'] = array(
+      'title' => t('Export'),
+      'href' => 'admin/config/search/metatags/config/' . $config->instance . '/export',
+    );
+    $row['data']['operations'] = array(
+      'data' => array(
+        '#theme' => 'links',
+        '#links' => $operations,
+        '#attributes' => array('class' => array('links', 'inline')),
+      ),
+    );
+
+    $rows[$config->instance] = $row;
+  }
+
+  $build['config_table'] = array(
+    '#theme' => 'table',
+    '#header' => array(
+      'type' => t('Type'),
+      'operations' => t('Operations'),
+    ),
+    '#rows' => $rows,
+    '#empty' => t('No meta tag defaults available yet.'),
+    '#attributes' => array(
+      'class' => array('metatag-config-overview'),
+    ),
+    '#attached' => array(
+      'js' => array(
+        drupal_get_path('module', 'metatag') . '/metatag.admin.js',
+      ),
+      'css' => array(
+        drupal_get_path('module', 'metatag') . '/metatag.admin.css',
+      ),
+    ),
+  );
+
+  return $build;
+}
+
+/**
+ * Build an FAPI #options array for the instance select field.
+ */
+function _metatag_config_instance_get_available_options() {
+  $options = array();
+  $instances = metatag_config_instance_info();
+
+  foreach ($instances as $instance => $instance_info) {
+    if (metatag_config_load($instance)) {
+      continue;
+    }
+    $parents = metatag_config_get_parent_instances($instance, FALSE);
+    array_shift($parents);
+    if (!empty($parents)) {
+      $parent = reset($parents);
+      $parent_label = isset($instances[$parent]['label']) ? $instances[$parent]['label'] : t('Unknown');
+      if (!isset($options[$parent_label])) {
+        $options[$parent_label] = array();
+        if (!metatag_config_load($parent)) {
+          $options[$parent_label][$parent] = t('All');
+        }
+      }
+      $options[$parent_label][$instance] = $instance_info['label'];
+      unset($options[$parent]);
+    }
+    else {
+      $options[$instance] = $instance_info['label'];
+    }
+  }
+
+  return $options;
+}
+
+function metatag_config_add_form($form, &$form_state) {
+  $form['instance'] = array(
+    '#type' => 'select',
+    '#title' => t('Type'),
+    '#description' => t('Select the type of default meta tags you would like to add.'),
+    '#options' => _metatag_config_instance_get_available_options(),
+    '#required' => TRUE,
+  );
+  $form['config'] = array(
+    '#type' => 'value',
+    '#value' => array(),
+  );
+
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['save'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add and configure'),
+  );
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => t('Cancel'),
+    '#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/metatags',
+  );
+
+  return $form;
+}
+
+function metatag_config_add_form_submit($form, &$form_state) {
+  form_state_values_clean($form_state);
+  $config = (object) $form_state['values'];
+  metatag_config_save($config);
+  $form_state['redirect'] = 'admin/config/search/metatags/config/' . $config->instance;
+}
+
+function metatag_config_edit_form($form, &$form_state, $config) {
+  $form['cid'] = array(
+    '#type' => 'value',
+    '#value' => !empty($config->cid) ? $config->cid : NULL,
+  );
+  $form['instance'] = array(
+    '#type' => 'value',
+    '#value' => $config->instance,
+  );
+
+  $contexts = explode(':', $config->instance);
+  $options['context'] = $contexts[0];
+  if ($contexts[0] != 'global') {
+    // The context part of the instance may not map to an entity type, so allow
+    // the token_get_entity_mapping() function to fallback to the provided type.
+    if ($token_type = token_get_entity_mapping('entity', $contexts[0], TRUE)) {
+      $options['token types'] = array($token_type);
+    }
+    else {
+      $options['token types'] = array($contexts[0]);
+    }
+    // Allow hook_metatag_token_types_alter() to modify the defined tokens.
+    drupal_alter('metatag_token_types', $options);
+  }
+
+  // Ensure that this configuration is properly compared to its parent 'default'
+  // configuration values.
+  if (count($contexts) > 1) {
+    // If the config is something like 'node:article' or 'taxonomy_term:tags'
+    // then the parent default config is 'node' or 'taxonomy_term'.
+    $default_instance = $contexts;
+    array_pop($default_instance);
+    $default_instance = implode(':', $default_instance);
+    $options['defaults'] = metatag_config_load_with_defaults($default_instance);
+  }
+  elseif ($contexts[0] != 'global') {
+    // If the config is something like 'node' or 'taxonomy_term' then the
+    // parent default config is 'global'.
+    $options['defaults'] = metatag_config_load_with_defaults('global');
+  }
+  else {
+    // If the config is 'global' than there are no parent defaults.
+    $options['defaults'] = array();
+  }
+
+  metatag_metatags_form($form, $config->instance, $config->config, $options);
+  $form['metatags']['#type'] = 'container';
+
+  // Make the token browser available.
+  $form['metatags']['#prefix'] = $form['metatags'][LANGUAGE_NONE]['#description'];
+  unset($form['metatags'][LANGUAGE_NONE]['#description']);
+
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['save'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => t('Cancel'),
+    '#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/metatags',
+  );
+
+  $form['#submit'][] = 'metatag_config_edit_form_submit';
+  return $form;
+}
+
+function metatag_config_edit_form_submit($form, &$form_state) {
+  // Build the configuration object and save it.
+  form_state_values_clean($form_state);
+  $config = (object) $form_state['values'];
+  // @todo Consider renaming the config field from 'config' to 'metatags'
+  $config->config = $config->metatags[LANGUAGE_NONE];
+  unset($config->metatags[LANGUAGE_NONE]);
+  metatag_config_save($config);
+
+  $label = metatag_config_instance_label($config->instance);
+  drupal_set_message(t('The meta tag defaults for @label have been saved.', array('@label' => $label)));
+
+  $form_state['redirect'] = 'admin/config/search/metatags';
+}
+
+function metatag_config_enable($config) {
+  if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'enable-' . $config->instance)) {
+    return MENU_ACCESS_DENIED;
+  }
+
+  ctools_export_crud_enable('metatag_config', $config);
+
+  $label = metatag_config_instance_label($config->instance);
+  drupal_set_message(t('The meta tag defaults for @label have been enabled.', array('@label' => $label)));
+  drupal_goto();
+}
+
+function metatag_config_disable($config) {
+  if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'disable-' . $config->instance)) {
+    return MENU_ACCESS_DENIED;
+  }
+
+  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_goto();
+}
+
+function metatag_config_delete_form($form, &$form_state, $config) {
+  $form['cid'] = array('#type' => 'value', '#value' => $config->cid);
+  $form['instance'] = array('#type' => 'value', '#value' => $config->instance);
+
+  $label = metatag_config_instance_label($config->instance);
+  $delete = metatag_config_access('delete', $config);
+  $title = $delete ? t('Are you sure you want to delete the meta tag defaults for @label?', array('@label' => $label)) : t('Are you sure you want to revert the meta tag defaults for @label?', array('@label' => $label));
+
+  return confirm_form(
+    $form,
+    $title,
+    'admin/config/search/metatags',
+    t('This action cannot be undone.')
+  );
+}
+
+function metatag_config_delete_form_submit($form, &$form_state) {
+  $config = metatag_config_load($form_state['values']['instance']);
+  metatag_config_delete($config->instance);
+
+  $label = metatag_config_instance_label($config->instance);
+  $delete = metatag_config_access('delete', $config);
+  $title = $delete ? t('The meta tag defaults for @label have been deleted.', array('@label' => $label)) : t('The meta tag defaults for @label have been reverted.', array('@label' => $label));
+  drupal_set_message($title);
+
+  $form_state['redirect'] = 'admin/config/search/metatags';
+}
+
+function metatag_config_export_form($config) {
+  ctools_include('export');
+  return drupal_get_form('ctools_export_form', ctools_export_crud_export('metatag_config', $config), t('Export'));
+}
+
+/**
+ * Form constructor to revert nodes to their default metatags.
+ *
+ * @see metatag_bulk_revert_form_submit()
+ * @ingroup forms
+ */
+function metatag_bulk_revert_form() {
+  // Get the list of entity:bundle options
+  $options = array();
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    foreach (array_keys($entity_info['bundles']) as $bundle) {
+      if (metatag_entity_supports_metatags($entity_type, $bundle)) {
+        $options[$entity_type . ':' . $bundle] =
+          $entity_info['label'] . ': ' . $entity_info['bundles'][$bundle]['label'];
+      }
+    }
+  }
+
+  $form['update'] = array(
+    '#type' => 'checkboxes',
+    '#required' => TRUE,
+    '#title' => t('Select the entities to revert'),
+    '#options' => $options,
+    '#default_value' => array(),
+    '#description' => t('All meta tags will be removed for all content of the selected entities.'),
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Revert'),
+  );
+
+  return $form;
+}
+
+/**
+ * Form submit handler for metatag reset bulk revert form.
+ *
+ * @see metatag_batch_revert_form()
+ * @see metatag_bulk_revert_batch_finished()
+ */
+function metatag_bulk_revert_form_submit($form, &$form_state) {
+  $batch = array(
+    'title' => t('Bulk updating metatags'),
+    'operations' => array(),
+    'finished' => 'metatag_bulk_revert_batch_finished',
+    'file' => drupal_get_path('module', 'metatag') . '/metatag.admin.inc',
+  );
+
+  // 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_set($batch);
+}
+
+/**
+ * Batch callback: delete custom metatags for selected bundles.
+ */
+function metatag_bulk_revert_batch_operation($entity_type, $bundle, &$context) {
+  if (!isset($context['sandbox']['current'])) {
+    $context['sandbox']['count'] = 0;
+    $context['sandbox']['current'] = 0;
+  }
+
+  // Query the selected entity table.
+  $entity_info = entity_get_info($entity_type);
+  $query = new EntityFieldQuery();
+  $query->entityCondition('entity_type', $entity_type)
+    ->propertyCondition($entity_info['entity keys']['id'], $context['sandbox']['current'], '>')
+    ->propertyOrderBy($entity_info['entity keys']['id']);
+  if ($entity_type != 'user') {
+    /**
+     * Entities which do not define a bundle such as User fail returning no results.
+     * @see https://www.drupal.org/node/1054168#comment-5266208
+     */
+    $query->entityCondition('bundle', $bundle);
+  }
+  // Get the total amount of entities to process.
+  if (!isset($context['sandbox']['total'])) {
+    $context['sandbox']['total'] = $query->count()->execute();
+    $query->count = FALSE;
+
+    // If there are no bundles to revert, stop immediately.
+    if (!$context['sandbox']['total']) {
+      $context['finished'] = 1;
+      return;
+    }
+  }
+
+  // Process 25 entities per iteration.
+  $query->range(0, 25);
+  $result = $query->execute();
+  $entity_ids = !empty($result[$entity_type]) ? array_keys($result[$entity_type]) : array();
+  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,
+      ));
+    }
+  }
+
+  $context['sandbox']['count'] += count($entity_ids);
+  $context['sandbox']['current'] = max($entity_ids);
+
+  if ($context['sandbox']['count'] != $context['sandbox']['total']) {
+    $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
+  }
+}
+
+/**
+ * Batch finished callback.
+ */
+function metatag_bulk_revert_batch_finished($success, $results, $operations) {
+  if ($success) {
+    if (!count($results)) {
+      drupal_set_message(t('No metatags were reverted.'));
+    }
+    else {
+      $message = theme('item_list', array('items' => $results));
+      drupal_set_message($message);
+    }
+  }
+  else {
+    $error_operation = reset($operations);
+    drupal_set_message(t('An error occurred while processing @operation with arguments : @args',
+      array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
+  }
+}
+
+/**
+ * Misc settings page.
+ */
+function metatag_admin_settings_form() {
+  $form = array();
+
+  $form['#attached'] = array(
+    'js' => array(
+      drupal_get_path('module', 'metatag') . '/metatag.admin.js',
+    ),
+    'css' => array(
+      drupal_get_path('module', 'metatag') . '/metatag.admin.css',
+    ),
+  );
+
+  $form['advanced'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+
+  $form['advanced']['metatag_load_all_pages'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Output meta tags even if only global settings apply'),
+    '#description' => t('By default Metatag will load the global default values for all pages that do not have meta tags assigned via the normal entity display or via Metatag Context. This may be disabled so that meta tags will only be output on pages that specifically have meta tags configured for them.'),
+    '#default_value' => variable_get('metatag_load_all_pages', TRUE),
+  );
+
+  $form['advanced']['metatag_tag_admin_pages'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Output meta tags on admin pages'),
+    '#description' => t('By default meta tags will not be output on admin pages, but it may be beneficial for some sites to do so.'),
+    '#default_value' => variable_get('metatag_tag_admin_pages', FALSE),
+  );
+
+  $form['advanced']['metatag_extended_permissions'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Advanced permissions'),
+    '#description' => t('Optionally add a permission for each individual meta tag. This provides tremendous flexibility for the editorial process, at the expense of making the permissions configuration more tedious.'),
+    '#default_value' => variable_get('metatag_extended_permissions', FALSE),
+  );
+
+  $form['advanced']['metatag_entity_no_lang_default'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("Don't load entity's default language values if no languages match"),
+    '#description' => t("On a site with multiple languages it is possible for an entity to not have meta tag values assigned for the language of the current page. By default the meta tags for an entity's default language value will be used in this scenario, with the canonical URL pointing to the URL. This option may be disabled, i.e. to only load meta tags for languages that specifically have them assigned, otherwise using defaults."),
+    '#default_value' => variable_get('metatag_entity_no_lang_default', FALSE),
+  );
+
+  $form['advanced']['metatag_load_defaults'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("Load the module's default configurations"),
+    '#description' => t("Control whether the module's default configuration is used. This will not affect configurations exported via Features."),
+    '#default_value' => variable_get('metatag_load_defaults', TRUE),
+  );
+
+  $form['advanced']['metatag_page_region'] = array(
+    '#type' => 'select',
+    '#title' => t("Page region to use"),
+    '#description' => t("By default Metatag uses the 'Content' region to trigger output of the meta tags. Some themes do not have this region, so it can be necessary to pick another."),
+    '#options' => system_region_list(variable_get('theme_default', 'bartik')),
+    '#default_value' => variable_get('metatag_page_region', 'content'),
+  );
+
+  $form['entities'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Master controls for all entities'),
+    '#description' => t('By enabling and disabling items here, it is possible to control which entities (e.g. nodes, taxonomy terms) and bundles (e.g. content types, vocabularies) will have meta tags available to them.<br />Note: the entities first have to have meta tags enabled via hook_entity_info; see the API documentation for full details.'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  foreach (entity_get_info() as $entity_name => $entity_info) {
+    // Only show entities that have been enabled via the hooks.
+    if (!empty($entity_info['metatags'])) {
+      $form['entities']['metatag_enable_' . $entity_name] = array(
+        '#type' => 'checkbox',
+        '#title' => t($entity_info['label']),
+        '#default_value' => variable_get('metatag_enable_' . $entity_name, TRUE),
+        '#description' => t('Enable meta tags for all pages this entity type.'),
+      );
+
+      if (!empty($entity_info['bundles'])) {
+        $desc_added = FALSE;
+        foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+          // Some entities, e.g. User, (core) File, have a bundle with the same
+          // name as the entity, so
+          if ($bundle_name != $entity_name) {
+            // Add an extra line to the description introducing the entity
+            // bundles.
+            if (!$desc_added) {
+              $form['entities']['metatag_enable_' . $entity_name]['#disabled'] = TRUE;
+              $form['entities']['metatag_enable_' . $entity_name]['#default_value'] = TRUE;
+              $form['entities']['metatag_enable_' . $entity_name]['#description'] =  t('Each bundle for this entity must be controlled individually.');
+              $desc_added = TRUE;
+            }
+            $form['entities']['metatag_enable_' . $entity_name . '__' . $bundle_name] = array(
+              '#type' => 'checkbox',
+              '#title' => t($bundle_info['label']),
+              '#default_value' => variable_get('metatag_enable_' . $entity_name . '__' . $bundle_name, isset($bundle_info['metatags']) ? $bundle_info['metatags'] : TRUE),
+              '#attributes' => array(
+                // Add some theming that'll indent this bundle.
+                'class' => array('metatag-bundle-checkbox'),
+              ),
+            );
+          }
+        }
+      }
+    }
+  }
+
+  // Extra submission logic.
+  $form['#submit'][] = 'metatag_admin_settings_form_submit';
+
+  return system_settings_form($form);
+}
+
+/**
+ * Form API submission callback for metatag_admin_settings_form().
+ */
+function metatag_admin_settings_form_submit() {
+  cache_clear_all('entity_info:', 'cache', TRUE);
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  drupal_set_message(t('The Metatag cache has been cleared, so all meta tags can be reloaded.'));
+}

+ 48 - 0
sites/all/modules/contrib/seo/metatag/metatag.admin.js

@@ -0,0 +1,48 @@
+/**
+ * @file
+ * Custom admin-side JS for the Metatag module.
+ */
+
+(function ($) {
+  'use strict';
+
+Drupal.behaviors.metatagUIConfigListing = {
+  attach: function (context) {
+    // Hidden elements to be visible if JavaScript is enabled.
+    $('.js-show').show();
+
+    // Make the leaf arrow clickable.
+    $('.metatag-config-label').hover(function(){
+      $(this).css({'cursor': 'pointer'});
+    })
+    .click(function(){
+      $(this).find('a.toggle-details', context).trigger('click');
+    });
+
+    // Show or hide the summary
+    $('table.metatag-config-overview a.toggle-details', context).click(function(event) {
+      $(this).parent('div').siblings('div.metatag-config-details').each(function() {
+        if ($(this).hasClass('js-hide')) {
+          $(this).slideDown('slow').removeClass('js-hide');
+        }
+        else {
+          $(this).slideUp('slow').addClass('js-hide');
+        }
+      });
+
+      // Change the expanded or collapsed state of the instance label.
+      if ($(this).parent('div').hasClass('collapsed')) {
+        $(this).parent('div').removeClass('collapsed').addClass('expanded');
+      }
+      else {
+        $(this).parent('div').removeClass('expanded').addClass('collapsed');
+      }
+
+      // This event may be triggered by a parent element click - so we don't
+      // want the click to bubble up otherwise we get recursive click events.
+      event.stopPropagation();
+    });
+  }
+};
+
+})(jQuery);

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

@@ -0,0 +1,372 @@
+<?php
+/**
+ * @file
+ * API documentation for the Metatag module.
+ */
+
+/**
+ * To enable Metatag support in custom entities, add 'metatags' => TRUE to the
+ * entity definition in hook_entity_info(), e.g.:
+ * 
+ * /**
+ *  * Implements hook_entity_info().
+ *  *
+ *  * Taken from the Examples module.
+ *  * /
+ * function entity_example_entity_info() {
+ *   $info['entity_example_basic'] = array(
+ *     'label' => t('Example Basic Entity'),
+ *     'controller class' => 'EntityExampleBasicController',
+ *     'base table' => 'entity_example_basic',
+ *     'uri callback' => 'entity_example_basic_uri',
+ *     'fieldable' => TRUE,
+ *     'metatags' => TRUE,
+ *     'entity keys' => array(
+ *       'id' => 'basic_id' , // The 'id' (basic_id here) is the unique id.
+ *       'bundle' => 'bundle_type' // Bundle will be determined by the 'bundle_type' field
+ *     ),
+ *     'bundle keys' => array(
+ *       'bundle' => 'bundle_type',
+ *     ),
+ *     'static cache' => TRUE,
+ *     'bundles' => array(
+ *       'first_example_bundle' => array(
+ *         'label' => 'First example bundle',
+ *         'admin' => array(
+ *           'path' => 'admin/structure/entity_example_basic/manage',
+ *           'access arguments' => array('administer entity_example_basic entities'),
+ *         ),
+ *       ),
+ *     ),
+ *     'view modes' => array(
+ *       'tweaky' => array(
+ *         'label' => t('Tweaky'),
+ *         'custom settings' =>  FALSE,
+ *       ),
+ *     )
+ *   );
+ * 
+ *   return $info;
+ * }
+ *
+ * The definition of each bundle may be handled separately, thus support may be
+ * disabled for the entity as a whole but enabled for individual bundles. This
+ * is handled via the 'metatags' value on the bundle definition, e.g.:
+ *
+ *     'bundles' => array(
+ *       'first_example_bundle' => array(
+ *         'label' => 'First example bundle',
+ *         'metatags' => TRUE,
+ *         'admin' => array(
+ *           'path' => 'admin/structure/entity_example_basic/manage',
+ *           'access arguments' => array('administer entity_example_basic entities'),
+ *         ),
+ *       ),
+ *     ),
+ */
+
+/**
+ * Provides a default configuration for Metatag intances.
+ *
+ * This hook allows modules to provide their own Metatag instances which can
+ * either be used as-is or as a "starter" for users to build from.
+ *
+ * This hook should be placed in MODULENAME.metatag.inc and it will be auto-
+ * loaded. MODULENAME.metatag.inc *must* be in the same directory as the
+ * .module file which *must* also contain an implementation of
+ * hook_ctools_plugin_api, preferably with the same code as found in
+ * metatag_ctools_plugin_api().
+ *
+ * The $config->disabled boolean attribute indicates whether the Metatag
+ * instance should be enabled (FALSE) or disabled (TRUE) by default.
+ *
+ * @return
+ *   An associative array containing the structures of Metatag instances, as
+ *   generated from the Export tab, keyed by the Metatag config name.
+ *
+ * @see metatag_metatag_config_default()
+ * @see metatag_ctools_plugin_api()
+ */
+function hook_metatag_config_default() {
+  $configs = array();
+
+  $config = new stdClass();
+  $config->instance = 'config1';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'title' => array('value' => '[current-page:title] | [site:name]'),
+    'generator' => array('value' => 'Drupal 7 (http://drupal.org)'),
+    'canonical' => array('value' => '[current-page:url:absolute]'),
+    'shortlink' => array('value' => '[current-page:url:unaliased]'),
+  );
+  $configs[$config->instance] = $config;
+
+  $config = new stdClass();
+  $config->instance = 'config2';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'title' => array('value' => '[user:name] | [site:name]'),
+  );
+  $configs[$config->instance] = $config;
+
+  return $configs;
+}
+
+/**
+ * Internal hook for adding further configuration values in bundled submodules.
+ *
+ * The defaults provided by the main Metatag module need to be extended by the
+ * bundled submodules before they are presented to other modules for altering
+ * via hook_metatag_config_default_alter(), in case differences in module
+ * weights and loading priorities cause the submodules' settings to run after
+ * those of any custom modules.
+ *
+ * @see hook_metatag_config_default()
+ * @see hook_metatag_config_default_alter()
+ */
+function hook_metatag_bundled_config_alter(&$config) {
+}
+  
+/**
+ * 
+ */
+function hook_metatag_config_default_alter(&$config) {
+}
+
+/**
+ * 
+ */
+function hook_metatag_config_delete($entity_type, $entity_ids, $revision_ids, $langcode) {
+}
+
+/**
+ * 
+ */
+function hook_metatag_config_insert($config) {
+}
+
+/**
+ * 
+ */
+function hook_metatag_config_instance_info() {
+  return array();
+}
+
+/**
+ * 
+ */
+function hook_metatag_config_instance_info_alter(&$info) {
+}
+
+/**
+ * 
+ */
+function hook_metatag_config_load() {
+}
+
+/**
+ * 
+ */
+function hook_metatag_config_load_presave() {
+}
+
+/**
+ * 
+ */
+function hook_metatag_config_presave($config) {
+}
+
+/**
+ * 
+ */
+function hook_metatag_config_update($config) {
+}
+
+/**
+ * Definition of the meta tags and groups.
+ *
+ * @return array
+ *   A nested array of 'tags' and 'groups', each keyed off the machine name for
+ *   their respective structure type, with the following values:
+ *   Tags:
+ *     'label' - The name for this meta tag.
+ *     'description' - An explanation of what this meta tag is used for and what
+ *       values are permissible.
+ *     'class' - The class name that controls this meta tag.
+ *     'weight' - Used to sort the meta tags during output.
+ *     'group' - The machine name of a group this meta tag will be contained
+ *       within.
+ *     'context' - Optionally control the type of configuration the meta tag
+ *       will be available from. Possible values are:
+ *       [empty] - All meta tags apply to all possible objects, by default.
+ *       'global' - This will make it only show in the global meta tag
+ *         configuration form.
+ *       [entity type] - Makes the meta tag only show for specific entity types.
+ *     'header' - Optionally output the meta tag as an HTTP header value.
+ *     'element' - Optional attributes for rendering the meta tag. Should
+ *       contain the following:
+ *       '#theme' - The theming function used to render the meta tag.
+ *     'replaces' - An optional array of meta tags that this meta tag replaces.
+ *       Used to indicate that these deprecated meta tags will be replaced by
+ *       this newer one, their values will be used, upon the next object save
+ *       the deprecated tag will be entirely replaced by the new meta tag. While
+ *       one meta tag can replace several others, only one of the possible
+ *       values will be used, the others will be silently purged upon the next
+ *       configuration/object save.
+ *     'form' - Optional items to be passed directly to the form; uses standard
+ *       Form API values.
+ *     'devel_generate' - Optional values to be passed to the Devel Generate
+ *       submodule. Should be an array containing one of the following values:
+ *       'type' - One of the following:
+ *         'canonical' - The token for the absolute URL for the current page.
+ *         'email' - An email address randomly generated at the site's hostname.
+ *         'float' - A random floating point number between 0.0 and 999.999.
+ *         'image' - A randomly generated image.
+ *         'integer' - A random integer between 0 and 999.
+ *         'phone' - A phone number in the format 999-999-9999.
+ *         'select' - A value randomly selected from the available form options.
+ *         'text' - Random text string.
+ *         'twitter' - A Twitter username.
+ *         'url' - A randomly generated URL on this site.
+ *       'maxlength' - The maximum length / number of iterations of this value,
+ *         defaults to 10.
+ *     'dependencies' - Optional nested array of values to indicate other meta
+ *       tags that must be present in order for this meta tag to be visible. See
+ *       The Open Graph and Twitter Cards submodules for example usage. Each
+ *       dependency must contain the following elements:
+ *       'dependency' - The name of the meta tag that is required.
+ *       'attribute' - The name of the other meta tag that is to be checked,
+ *         most meta tags use "value" as the attribute name.
+ *       'condition' - The state condition to be checked against, e.g. "filled"
+ *         to check text values, "checked" for a checkbox, "value" to compare
+ *         the raw selection; see https://api.drupal.org/drupal_process_states
+ *         for more details.
+ *       'value' - The field value to check the 'condition' against. see
+ *         https://api.drupal.org/drupal_process_states for further details.
+ *   Groups:
+ *     'label' - The name for this group.
+ *     'description' - A detailed explanation of these meta tags.
+ *     'form' - Additional items to be passed directly to the form.
+ *   Note: 'label', 'description', and any text strings passed in 'form', should
+ *   be translated.
+ *
+ * @see metatag_metatag_info().
+ */
+function hook_metatag_info() {
+  return array();
+}
+
+/**
+ * 
+ */
+function hook_metatag_info_alter(&$info) {
+}
+
+/**
+ * 
+ */
+function hook_metatag_load_entity_from_path_alter(&$path, $result) {
+}
+
+/**
+ * Alter metatags before being cached.
+ *
+ * This hook is invoked prior to the meta tags for a given page are cached.
+ *
+ * @param array $output
+ *   All of the meta tags to be output for this page in their raw format. This
+ *   is a heavily nested array.
+ * @param string $instance
+ *   An identifier for the current page's page type, typically a combination
+ *   of the entity name and bundle name, e.g. "node:story".
+ * @param array $options
+ *   All of the options used to generate the meta tags.
+ */
+function hook_metatag_metatags_view_alter(&$output, $instance, $options) {
+  if (isset($output['description']['#attached']['drupal_add_html_head'][0][0]['#value'])) {
+    $output['description']['#attached']['drupal_add_html_head'][0][0]['#value'] = 'O rly?';
+  }
+}
+
+/**
+ * 
+ */
+function hook_metatag_page_cache_cid_parts_alter(&$cid_parts) {
+}
+
+/**
+ * 
+ */
+function hook_metatag_presave(&$metatags, $entity_type, $entity_id, $revision_id, $langcode) {
+}
+
+/**
+ * Allows modules to alter the defined list of tokens available
+ * for metatag patterns replacements.
+ *
+ * By default only context (for example: global, node, etc...)
+ * related tokens are made available to metatag patterns replacements.
+ * This hook allows other modules to extend the default declared tokens.
+ *
+ * @param array $options
+ *   (optional) An array of options including the following keys and values:
+ *   - token types: An array of token types to be passed to theme_token_tree().
+ *   - context: An identifier for the configuration instance type, typically
+ *     an entity name or object name, e.g. node, views, taxonomy_term.
+ *
+ * @see metatag_config_edit_form()
+ * @see metatag_field_attach_form()
+ */
+function hook_metatag_token_types_alter(&$options) {
+  // Watchout: $options['token types'] might be empty
+  if (!isset($options['token types'])) {
+    $options['token types'] = array();
+  }
+
+  if ($options['context'] == 'config1'){
+    $options['token types'] += array('token_type1','token_type2');
+  }
+  elseif ($options['context'] == 'config2'){
+    $options['token types'] += array('token_type3','token_type4');
+  }
+}
+
+/**
+ * Allows modules to alter defined token patterns and values before replacement.
+ *
+ * The metatag module defines default token patterns replacements depending on
+ * the different configuration instances (contexts, such as global, node, ...).
+ * This hook provides an opportunity for other modules to alter the patterns or
+ * the values for replacements, before tokens are replaced (token_replace).
+ *
+ * See facetapi and facetapi_bonus modules for an example of implementation.
+ *
+ * @param $pattern
+ *   A string potentially containing replaceable tokens. The pattern could also
+ *   be altered by reference, allowing modules to implement further logic, such
+ *   as tokens lists or masks/filters.
+ * @param $types
+ *   Corresponds to the 'token data' property of the $options object.
+ *   (optional) An array of keyed objects. For simple replacement scenarios
+ *   'node', 'user', and others are common keys, with an accompanying node or
+ *   user object being the value. Some token types, like 'site', do not require
+ *   any explicit information from $data and can be replaced even if it is
+ *   empty.
+ * @param string $tag_name
+ *   The name of the meta tag being altered.
+ *
+ * @see DrupalTextMetaTag::getValue()
+ */
+function hook_metatag_pattern_alter(&$pattern, &$types, $tag_name) {
+  if (strpos($pattern, 'token_type1') !== FALSE) {
+    $types['token_type1'] = "data to be used in hook_tokens for replacement";
+  }
+  if (strpos($pattern, 'token_type2') !== FALSE) {
+    // Load something or do some operations.
+    $types['token_type2'] = array("Then fill in the array with the right data");
+    // $pattern could also be altered, for example, strip off [token_type3].
+    $pattern = str_replace('[token_type3]', '', $pattern);
+  }
+}

+ 21 - 0
sites/all/modules/contrib/seo/metatag/metatag.drush.inc

@@ -0,0 +1,21 @@
+<?php
+/**
+ * @file
+ * Drush integration for Metatag.
+ */
+
+/**
+ * Implements hook_drush_cache_clear().
+ */
+function metatag_drush_cache_clear(&$types) {
+  if (function_exists('module_exists') && module_exists('metatag')) {
+    $types['metatag'] = 'drush_metatag_cache_clear_token_info';
+  }
+}
+
+/**
+ * Clear caches internal to Metatag module.
+ */
+function drush_metatag_cache_clear_token_info() {
+  metatag_config_cache_clear();
+}

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

@@ -0,0 +1,90 @@
+<?php
+/**
+ * @file
+ * Features integration for the Metatag module.
+ */
+
+/**
+ * Implements hook_features_export().
+ */
+function metatag_features_export($data, &$export, $module_name = '', $type = 'metatag') {
+  $pipe = array();
+
+  foreach ($data as $name) {
+    if (metatag_config_load($name)) {
+      $export['features'][$type][$name] = $name;
+    }
+  }
+
+  $export['dependencies']['metatag'] = 'metatag';
+
+  return $pipe;
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function metatag_features_export_render($module_name, $data, $export = NULL) {
+  $code = array();
+  $code[] = '  $config = array();';
+  $code[] = '';
+
+  foreach ($data as $key => $name) {
+    if (is_object($name)) {
+      $name = $name->instance;
+    }
+    if ($config = metatag_config_load($name)) {
+      $export = new stdClass();
+      $export->instance = $config->instance;
+      if (isset($config->disabled)) {
+        $export->disabled = $config->disabled;
+      }
+      $export->config = $config->config;
+      $export = features_var_export($export, '  ');
+      $key = features_var_export($name);
+      $code[] = "  // Exported Metatag config instance: {$name}.";
+      $code[] = "  \$config[{$key}] = {$export};";
+      $code[] = "";
+    }
+  }
+  $code[] = '  return $config;';
+  $code = implode("\n", $code);
+  return array('metatag_export_default' => $code);
+}
+
+/**
+ * Implements hook_features_revert().
+ */
+function metatag_features_revert($module) {
+  if ($feature_conf = features_get_default('metatag', $module)) {
+    foreach (array_keys($feature_conf) as $config) {
+      if ($conf = metatag_config_load($config)) {
+        db_delete('metatag_config')->condition('instance', $config)->execute();
+      }
+      unset($feature_conf[$config]['cid']);
+      $object = new stdClass();
+      $object->cid = NULL;
+      $object->instance = $config;
+      $object->config = $feature_conf[$config]['config'];
+      metatag_config_save($object);
+    }
+  }
+}
+
+/**
+ * Implements hook_features_export_options().
+ */
+function metatag_features_export_options() {
+  $instances = metatag_config_instance_info();
+  foreach ($instances as $key => $instance) {
+    $options[$key] = $key;
+  };
+  return $options;
+}
+
+/**
+ * Implements hook_features_rebuild().
+ */
+function metatag_features_rebuild($module) {
+  metatag_features_revert($module);
+}

+ 65 - 0
sites/all/modules/contrib/seo/metatag/metatag.feeds.inc

@@ -0,0 +1,65 @@
+<?php
+/**
+ * @file
+ * Feeds mapping implementation for the Metatag module.
+ */
+
+/**
+ * Implements hook_feeds_processor_targets_alter().
+ */
+function metatag_feeds_processor_targets_alter(&$targets, $entity_type, $bundle) {
+  if (metatag_entity_supports_metatags($entity_type)) {
+    $info = metatag_get_info();
+    foreach ($info['tags'] as $name => $tag) {
+      $targets['meta_' . $name] = array(
+        'name' => 'Meta tag: ' . check_plain($tag['label']),
+        'callback' => 'metatag_feeds_set_target',
+        'description' => $tag['description'],
+      );
+    }
+  }
+}
+
+/**
+ * Callback function to set value of a metatag tag.
+ */
+function metatag_feeds_set_target($source, $entity, $target, $value) {
+  // Don't do anything if we weren't given any data.
+  if (empty($value)) {
+    return;
+  }
+
+  // Strip the prefix that was added above.
+  $name = str_replace('meta_', '', $target);
+
+  // Identify the type of entity being imported.
+  $entity_type = $entity->feeds_item->entity_type;
+
+  // Check for any existing data that may not have been loaded already.
+  if (!isset($entity->metatags) && !empty($entity->feeds_item->entity_id) && is_numeric($entity->feeds_item->entity_id)) {
+    $entity->metatags = array();
+    $entity_id = $entity->feeds_item->entity_id;
+
+    // Load the existing entity.
+    $stored_entities = entity_load($entity_type, array($entity_id));
+    if (!empty($stored_entities)) {
+      $stored_entity = reset($stored_entities);
+      if (!empty($stored_entity)) {
+        // This function returns all values for all revisions.
+        $metatags = metatag_metatags_load($entity_type, $entity_id);
+        if (!empty($metatags)) {
+          // Only load meta tags for the current revision.
+          list(, $revision_id) = entity_extract_ids($entity_type, $stored_entity);
+          if (!empty($metatags[$revision_id])) {
+            // Just copy all meta tags for all languages.
+            $entity->metatags = $metatags[$revision_id];
+          }
+        }
+      }
+    }
+  }
+
+  // Assign the value.
+  $langcode = metatag_entity_get_language($entity_type, $entity);
+  $entity->metatags[$langcode][$name]['value'] = $value;
+}

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

@@ -0,0 +1,18 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) hooks.
+ */
+
+/**
+ * Implements hook_i18n_string_info().
+ */
+function metatag_i18n_string_info() {
+  $groups['metatag'] = array(
+    'title' => t('Metatag'),
+    'description' => t('Configurable metatags.'),
+    'format' => FALSE,
+    'list' => FALSE,
+  );
+  return $groups;
+}

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

@@ -0,0 +1,345 @@
+<?php
+
+interface DrupalMetaTagInterface {
+
+  /**
+   * Constructor
+   *
+   * @param array $info
+   *   The information about the meta tag from metatag_get_info().
+   */
+  function __construct(array $info, array $data = array());
+
+  function getForm();
+
+  //function validateForm();
+
+  //function processForm();
+
+  function getValue();
+
+  function getWeight();
+
+  function getElement();
+
+  function tidyValue($value);
+}
+
+class DrupalDefaultMetaTag implements DrupalMetaTagInterface {
+
+  protected $info;
+  protected $data = array('value' => '');
+  protected $weight = 0;
+
+  function __construct(array $info, array $data = NULL) {
+    $this->info = $info;
+    if (isset($data)) {
+      $this->data = $data;
+    }
+  }
+
+  /**
+   * Calculate the weight or this meta tag.
+   *
+   * @return integer
+   */
+  public function getWeight() {
+    static $counter = 0;
+
+    // If no weight value is found, stack this meta tag at the end.
+    $weight = 100;
+    if (!empty($this->info['weight'])) {
+      $weight = $this->info['weight'];
+    }
+
+    return $weight + ($counter++ * 0.1);
+  }
+
+  public function getForm(array $options = array()) {
+    return array();
+  }
+
+  public function getValue(array $options = array()) {
+    return $this->tidyValue($this->data['value']);
+  }
+
+  public function getElement(array $options = array()) {
+    $value = $this->getValue($options);
+    if (strlen($value) === 0) {
+      return array();
+    }
+
+    // The stack of elements that will be output.
+    $elements = array();
+
+    // Dynamically add each option to this setting.
+    $base_element = isset($this->info['element']) ? $this->info['element'] : array();
+
+    // Single item.
+    if (empty($this->info['multiple'])) {
+      $values = array($value);
+    }
+
+    // Multiple items.
+    else {
+      $values = array_filter(explode(',', $value));
+    }
+
+    // Loop over each item.
+    if (!empty($values)) {
+      foreach ($values as $ctr => $value) {
+        $value = trim($value);
+
+        // Some meta tags must be output as secure URLs.
+        if (!empty($this->info['secure'])) {
+          $value = str_replace('http://', 'https://', $value);
+        }
+
+        // Combine the base configuration for this meta tag with the value.
+        $element = $base_element + array(
+          '#theme' => 'metatag',
+          '#tag' => 'meta',
+          '#id' => 'metatag_' . $this->info['name'] . '_' . $ctr,
+          '#name' => $this->info['name'],
+          '#value' => $value,
+          '#weight' => $this->getWeight(),
+        );
+
+        // Add header information if desired.
+        if (!empty($this->info['header'])) {
+          $element['#attached']['drupal_add_http_header'][] = array($this->info['header'], $value);
+        }
+
+        $elements[] = array($element, $element['#id']);
+      }
+    }
+
+    if (!empty($elements)) {
+      return array(
+        '#attached' => array('drupal_add_html_head' => $elements),
+      );
+    }
+  }
+
+  /**
+   * Remove unwanted formatting from a meta tag.
+   *
+   * @param $value string
+   *   The meta tag value to be tidied up.
+   *
+   * @return string
+   *   The meta tag value after it has been tidied up.
+   */
+  public function tidyValue($value) {
+    // Specifically replace encoded spaces, because some WYSIWYG editors are
+    // silly. Do this before decoding the other HTML entities so that the output
+    // doesn't end up with a bunch of a-circumflex characters.
+    $value = str_replace('&nbsp;', ' ', $value);
+
+    // Convert any HTML entities into regular characters.
+    $value = decode_entities($value);
+
+    // Remove any HTML code that might have been included.
+    $value = strip_tags($value);
+
+    // Strip errant whitespace.
+    $value = str_replace(array("\r\n", "\n", "\r", "\t"), ' ', $value);
+    $value = str_replace('  ', ' ', $value);
+    $value = str_replace('  ', ' ', $value);
+    $value = trim($value);
+
+    return $value;
+  }
+}
+
+/**
+ * Text-based meta tag controller.
+ */
+class DrupalTextMetaTag extends DrupalDefaultMetaTag {
+
+  public function getForm(array $options = array()) {
+    $options += array(
+      'token types' => array(),
+    );
+
+    $form['value'] = isset($this->info['form']) ? $this->info['form'] : array();
+
+    $form['value'] += array(
+      '#type' => 'textfield',
+      '#title' => $this->info['label'],
+      '#description' => !empty($this->info['description']) ? $this->info['description'] : '',
+      '#default_value' => isset($this->data['value']) ? $this->data['value'] : '',
+      '#element_validate' => array('token_element_validate'),
+      '#token_types' => $options['token types'],
+      '#maxlength' => 1024,
+    );
+
+    if (!empty($this->info['multiple'])) {
+      $form['value']['#description'] .= ' ' . t('Multiple values may be used, separated by a comma. Note: Tokens that return multiple values will be handled automatically.');
+    }
+
+    // Support for dependencies, using Form API's #states system.
+    // @see metatag.api.php.
+    // @see https://api.drupal.org/drupal_process_states
+    if (!empty($this->info['dependencies'])) {
+      foreach ($this->info['dependencies'] as $specs) {
+        $form['value']['#states']['visible'][':input[name*="[' . $specs['dependency'] . '][' . $specs['attribute'] . ']"]'] = array(
+          $specs['condition'] => $specs['value'],
+        );
+      }
+    }
+
+    return $form;
+  }
+
+  public function getValue(array $options = array()) {
+    $options += array(
+      'instance' => '',
+      'token data' => array(),
+      'clear' => TRUE,
+      'sanitize' => TRUE,
+      'raw' => FALSE,
+    );
+
+    $name = "metatag:" . $options["instance"] . ":" . $this->info["name"];
+
+    $value = metatag_translate($name, $this->data['value']);
+    if (empty($options['raw'])) {
+      // Give other modules the opportunity to use hook_metatag_pattern_alter()
+      // to modify defined token patterns and values before replacement.
+      drupal_alter('metatag_pattern', $value, $options['token data'], $this->info['name']);
+      $value = token_replace($value, $options['token data'], $options);
+    }
+    return $this->tidyValue($value);
+  }
+}
+
+/**
+ * Link type meta tag controller.
+ */
+class DrupalLinkMetaTag extends DrupalTextMetaTag {
+
+  public function getElement(array $options = array()) {
+    $element = isset($this->info['element']) ? $this->info['element'] : array();
+
+    $value = $this->getValue($options);
+    if (strlen($value) === 0) {
+      return array();
+    }
+
+    $element += array(
+      '#theme' => 'metatag_link_rel',
+      '#tag' => 'link',
+      '#id' => 'metatag_' . $this->info['name'],
+      '#name' => $this->info['name'],
+      '#value' => $value,
+      '#weight' => $this->getWeight(),
+    );
+
+    if (!isset($this->info['header']) || !empty($this->info['header'])) {
+      // Also send the generator in the HTTP header.
+      // @todo This does not support 'rev' or alternate link headers.
+      $element['#attached']['drupal_add_http_header'][] = array('Link', '<' . $value . '>;' . drupal_http_header_attributes(array('rel' => $element['#name'])), TRUE);
+    }
+
+    return array(
+      '#attached' => array('drupal_add_html_head' => array(array($element, $element['#id']))),
+    );
+  }
+}
+
+/**
+ * Title meta tag controller.
+ *
+ * This extends DrupalTextMetaTag as we need to alter variables in
+ * template_preprocess_html() rather output a normal meta tag.
+ */
+class DrupalTitleMetaTag extends DrupalTextMetaTag {
+
+  public function getElement(array $options = array()) {
+    $element = array();
+    $value = $this->getValue($options);
+    $element['#attached']['metatag_set_preprocess_variable'][] = array('html', 'head_title', $value);
+    $element['#attached']['metatag_set_preprocess_variable'][] = array('html', 'head_array', array('title' => $value));
+    return $element;
+  }
+}
+
+/**
+ * Multiple value meta tag controller.
+ */
+class DrupalListMetaTag extends DrupalDefaultMetaTag {
+
+  function __construct(array $info, array $data = NULL) {
+    // Ensure that the $data['value] argument is an array.
+    if (empty($data['value'])) {
+      $data['value'] = array();
+    }
+    $data['value'] = (array) $data['value'];
+
+    parent::__construct($info, $data);
+  }
+
+  public function getForm(array $options = array()) {
+    $form['value'] = isset($this->info['form']) ? $this->info['form'] : array();
+
+    $form['value'] += array(
+      '#type' => 'checkboxes',
+      '#title' => $this->info['label'],
+      '#description' => !empty($this->info['description']) ? $this->info['description'] : '',
+      '#default_value' => isset($this->data['value']) ? $this->data['value'] : array(),
+    );
+
+    return $form;
+  }
+
+  public function getValue(array $options = array()) {
+    $values = array_keys(array_filter($this->data['value']));
+    sort($values);
+    $value = implode(', ', $values);
+    return $this->tidyValue($value);
+  }
+}
+
+/**
+ * Date interval meta tag controller.
+ */
+class DrupalDateIntervalMetaTag extends DrupalDefaultMetaTag {
+
+  public function getForm(array $options = array()) {
+    $form['value'] = array(
+      '#type' => 'textfield',
+      '#title' => t('@title interval', array('@title' => $this->info['label'])),
+      '#default_value' => isset($this->data['value']) ? $this->data['value'] : '',
+      '#element_validate' => array('element_validate_integer_positive'),
+      '#maxlength' => 4,
+      '#description' => isset($this->info['description']) ? $this->info['description'] : '',
+    );
+    $form['period'] = array(
+      '#type' => 'select',
+      '#title' => t('@title interval type', array('@title' => $this->info['label'])),
+      '#default_value' => isset($this->data['period']) ? $this->data['period'] : '',
+      '#options' => array(
+        '' => t('- none -'),
+        'day' => t('Day(s)'),
+        'week' => t('Week(s)'),
+        'month' => t('Month(s)'),
+        'year' => t('Year(s)'),
+      ),
+    );
+
+    return $form;
+  }
+
+  public function getValue(array $options = array()) {
+    $value = '';
+    if (!empty($this->data['value'])) {
+      $interval = intval($this->data['value']);
+      if (!empty($interval) && !empty($this->data['period'])) {
+        $period = $this->data['period'];
+        $value = format_plural($interval, '@count ' . $period, '@count ' . $period . 's');
+      }
+    }
+    return $value;
+  }
+}

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

@@ -0,0 +1,28 @@
+name = Metatag
+description = "Adds support and an API to implement meta tags."
+package = SEO
+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)
+
+; CTools is required.
+dependencies[] = ctools
+
+; Must have this release of Token to avoid a bug with tokens that contain the
+; colon symbol, as used in OG meta tags.
+dependencies[] = token (>= 1.6)
+
+configure = admin/config/search/metatags
+
+files[] = metatag.inc
+files[] = metatag.migrate.inc
+files[] = metatag.test
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

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

@@ -0,0 +1,1557 @@
+<?php
+/**
+ * @file
+ * Install, update, and uninstall functions for the metatag module.
+ */
+
+/**
+ * Implements hook_requirements().
+ */
+function metatag_requirements($phase) {
+  $requirements = array();
+  // Ensure translations don't break during installation.
+  $t = get_t();
+
+  if ($phase == 'install') {
+    // Handle scenarios where the site had the legacy "metatags" module
+    // installed but then had Metatag installed on top of it.
+    if (function_exists('db_table_exists') && function_exists('db_field_exists') && function_exists('db_query') && Database::isActiveConnection()) {
+      // Check if the primary table already exists.
+      if (db_table_exists('metatag')) {
+        // Check to see if all of the fields exist in the table. If one of the
+        // fields does not exist, proceed with the fix.
+        $fields = array(
+          'entity_type',
+          'entity_id',
+          'revision_id',
+          'language',
+          'data',
+        );
+        foreach ($fields as $field) {
+          // This field doesn't exist, so determine what to do.
+          if (!db_field_exists('metatag', $field)) {
+            // The table contains data, so rename it.
+            if (db_query("SELECT COUNT(*) FROM {metatag}")->fetchField() > 0) {
+              db_query("RENAME TABLE {metatag} TO {metatag}_legacy");
+              $message = 'An out-of-date version of the {metatag} table was discovered. As the table contained data it was renamed with a suffix of "_legacy". This will not prevent installation from continuing, but will need to be dealt with later. See <a href="https://www.drupal.org/node/1391554">https://www.drupal.org/node/1391554</a> for further details.';
+            }
+            // The table is empty, so delete it.
+            else {
+              db_query("DROP TABLE {metatag}");
+              $message = 'An out-of-date version of the {metatag} table was discovered. As the table was empty it was simply removed so that it could be recreated in the correct format. Installation may now proceed. See <a href="https://www.drupal.org/node/1391554">https://www.drupal.org/node/1391554</a> for further details.';
+            }
+            $requirements['metatag'] = array(
+              'severity' => REQUIREMENT_WARNING,
+              'title' => 'Metatag',
+              'value' => $t('Legacy data discovered.'),
+              'description' => $t($message),
+            );
+            drupal_set_message($t($message), 'warning');
+            break;
+          }
+        }
+      }
+    }
+  }
+  elseif ($phase == 'runtime') {
+    // Work out the release of D7 that is currently running.
+    list($major, $minor) = explode('.', VERSION);
+    // Strip off any suffixes on the version string, e.g. "17-dev".
+    if (strpos('-', $minor)) {
+      list($minor, $suffix) = explode('-', $minor);
+    }
+
+    // Releases of Drupal older than 7.28 did not have entity_language(), which
+    // is now required, and had a broken [node:summary] token.
+    if ($minor < 28) {
+      $requirements['metatag'] = array(
+        'severity' => REQUIREMENT_WARNING,
+        'title' => 'Metatag',
+        'value' => $t('Upgrade Drupal core to v7.28 or newer'),
+        'description' => $t("This older version of Drupal core is missing functionality necessary for the module's multilingual support and contains a broken [node:summary] token, it must be upgraded to version 7.28 or newer."),
+      );
+    }
+    // Everything's OK.
+    else {
+      $requirements['metatag'] = array(
+        'severity' => REQUIREMENT_OK,
+        'title' => 'Metatag',
+        'value' => $t('Drupal core is compatible'),
+        'description' => $t('Older versions of Drupal core contained bugs that made them incompatible with Metatag, but this version will work correctly.'),
+      );
+    }
+
+    // Add a note if Page Title is also installed.
+    if (module_exists('page_title')) {
+      $requirements['metatag_page_title'] = array(
+        'severity' => REQUIREMENT_INFO,
+        '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.'),
+      );
+    }
+
+    // Add a note if the deprecated metatag.entity_translation.inc file still
+    // exists.
+    $filename = 'metatag.entity_translation.inc';
+    if (file_exists(dirname(__FILE__) . '/' . $filename)) {
+      $requirements['metatag_deprecated_et_file'] = array(
+        'severity' => REQUIREMENT_ERROR,
+        'title' => 'Metatag',
+        'value' => $t('Unwanted :filename file found', array(':filename' => $filename)),
+        'description' => $t("The :filename file was removed in v7.x-1.0-beta5 but it still exists in the site's Metatag module's directory and will cause problems. This file needs to be removed. The file's path in the Drupal directory structure is:<br /><code>!short_path</code><br />The file's full path is:<br /><code>!full_path</code>", array(':filename' => $filename, '!short_path' => drupal_get_path('module', 'metatag') . '/' . $filename, '!full_path' => dirname(__FILE__) . $filename)),
+      );
+    }
+
+    // Check that Entity_Translation is current.
+    if (module_exists('entity_translation')) {
+      $rev = db_query("SELECT schema_version FROM {system} WHERE name = :module", array(':module' => 'entity_translation'))->fetchColumn();
+      if ($rev < 7004) {
+        $requirements['metatag_et_old'] = array(
+          'severity' => REQUIREMENT_ERROR,
+          'title' => 'Metatag',
+          'value' => $t('<a href="@url">Entity_Translation</a> is out of date and requires updating', array('@url' => 'https://www.drupal.org/project/entity_translation')),
+          'description' => $t('The Entity_Translation module is out of date and needs to be updated in order to be compatible with Metatag.'),
+        );
+      }
+    }
+
+    // It's recommended to install the Transliteration module to clean up file
+    // paths for use with image meta tags.
+    if (!module_exists('transliteration')) {
+      $requirements['metatag_transliteration'] = array(
+        'severity' => REQUIREMENT_INFO,
+        'title' => 'Metatag',
+        'value' => $t('The Transliteration module is recommended.'),
+        'description' => $t("It is recommended to install the <a href=\"@url\">Transliteration module</a> to clean up filenames of uploaded files that may be used with image meta tags.", array('@url' => 'https://drupal.org/project/transliteration')),
+      );
+    }
+
+    // It's recommended to install the Imagecache Token module to make image
+    // tokens easier to do.
+    if (!module_exists('imagecache_token')) {
+      $requirements['metatag_imagecache_token'] = array(
+        'severity' => REQUIREMENT_INFO,
+        'title' => 'Metatag',
+        'value' => $t('The Imagecache Token module is recommended.'),
+        'description' => $t("It is recommended to install the <a href=\"@url\">Imagecache Token module</a> to make it easier to control image meta tags, e.g. og:image.", array('@url' => 'https://drupal.org/project/imagecache_token')),
+      );
+    }
+
+    // The Admin Language module can cause problems.
+    if (!module_exists('admin_language') && variable_get('admin_language_force_neutral', 0)) {
+      $requirements['metatag_admin_language'] = array(
+        'severity' => REQUIREMENT_WARNING,
+        'title' => 'Metatag',
+        'value' => $t('Conflict with Admin Language module.'),
+        'description' => $t("Using the \"@option\" with Metatag can lead to data loss, so it is recommended to <a href=\"@url\">disable that option</a>.", array('@option' => t('Force language neutral aliases'), '@url' => url('admin/config/regional/language/admin_language'))),
+      );
+    }
+  }
+
+  return $requirements;
+}
+
+/**
+ * Implements hook_schema().
+ */
+function metatag_schema() {
+  $schema['metatag_config'] = array(
+    'description' => 'Storage of meta tag configuration and defaults.',
+    'export' => array(
+      'key' => 'instance',
+      'key name' => 'Instance',
+      'primary key' => 'cid',
+      'identifier' => 'config',
+      'default hook' => 'metatag_config_default',
+      'api' => array(
+        'owner' => 'metatag',
+        'api' => 'metatag',
+        'minimum_version' => 1,
+        'current_version' => 1,
+      ),
+      'cache defaults' => TRUE,
+      'default cache bin' => 'cache_metatag',
+    ),
+    'fields' => array(
+      'cid' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier for a metatag configuration set.',
+        'no export' => TRUE,
+      ),
+      'instance' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The machine-name of the configuration, typically entity-type:bundle.',
+      ),
+      'config' => array(
+        'type' => 'blob',
+        'size' => 'big',
+        'not null' => TRUE,
+        'serialize' => TRUE,
+        'description' => 'Serialized data containing the meta tag configuration.',
+        'translatable' => TRUE,
+      ),
+    ),
+    'primary key' => array('cid'),
+    'unique keys' => array(
+      'instance' => array('instance'),
+    ),
+  );
+
+  $schema['metatag'] = array(
+    'fields' => array(
+      'entity_type' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The entity type this data is attached to.',
+      ),
+      'entity_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The entity id this data is attached to.',
+      ),
+      'revision_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The revision_id for the entity object this data is attached to.',
+      ),
+      'language' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The language of the tag.',
+      ),
+      'data' => array(
+        'type' => 'blob',
+        'size' => 'big',
+        'not null' => TRUE,
+        'serialize' => TRUE,
+      ),
+    ),
+    'indexes' => array(
+      'type_revision' => array('entity_type','revision_id'),
+    ),
+    'primary key' => array('entity_type', 'entity_id', 'revision_id', 'language'),
+  );
+
+  $schema['cache_metatag'] = drupal_get_schema_unprocessed('system', 'cache');
+  $schema['cache_metatag']['description'] = t('Cache table for the generated meta tag output.');
+
+  return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function metatag_install() {
+  drupal_set_message(t("Thank you for installing the Metatag module. It is recommended to read the module's <a href=\"!url\" title=\"Read the Metatag module's documentation\">README.txt</a> file as there are some known issues that may affect this site.", array('!url' => url(drupal_get_path('module', 'metatag') . '/README.txt'))));
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function metatag_uninstall() {
+  // This variable is created via hook_enable.
+  variable_del('metatag_schema_installed');
+  // Used to control whether 403/404 pages are cached.
+  variable_del('metatag_cache_error_pages');
+  // Used to make meta tags display on admin pages.
+  variable_del('metatag_tag_admin_pages');
+
+  // Temp variables, just in case they weren't removed already.
+  variable_del('metatag_skip_update_7017');
+
+  // Used to note that the schema for the main {metatag} table were sufficiently
+  // updated.
+  variable_del('metatag_has_revision_id');
+
+  // Used to force an entity's default language values to be used if nothing
+  // else matched.
+  variable_del('metatag_entity_no_lang_default');
+
+  // Controls which page region is used to trigger output of the meta tags.
+  variable_del('metatag_page_region');
+
+  // Optionally disable the default configurations.
+  variable_del('metatag_load_defaults');
+}
+
+/**
+ * Implements hook_enable().
+ */
+function metatag_enable() {
+  variable_set('metatag_schema_installed', TRUE);
+}
+
+/**
+ * Implements hook_disable().
+ */
+// function metatag_disable() {
+// }
+
+/**
+ * Disable the deprecated metatag_ui module which has been merged into metatag.
+ */
+function metatag_update_7000() {
+  if (module_exists('metatag_ui')) {
+    module_disable(array('metatag_ui'), FALSE);
+    drupal_uninstall_modules(array('metatag_ui'), FALSE);
+  }
+}
+
+/**
+ * Fix the "{metatag_config}.cid column cannot be NULL" error.
+ */
+function metatag_update_7001() {
+  $table_name = 'metatag_config';
+  $field_name = 'cid';
+  $field_spec = array(
+    'type' => 'serial',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'description' => 'The primary identifier for a metatag configuration set.',
+  );
+  $keys = array('primary key' => array($field_name));
+
+  // Before making any changes, drop the existing primary key.
+  // Let's add a temporary unique key for cid so MySQL will let it go.
+  // Hint taken from https://drupal.org/node/2064305#comment-7753197.
+  db_add_unique_key($table_name, 'temp_key', array($field_name, 'instance'));
+
+  // Unforunately there is no API way to check if a primary key exists, so if
+  // it doesn't exist the db_drop_primary_key() call will fail.
+  try {
+    db_drop_primary_key($table_name);
+  }
+  catch (Exception $e) {
+    drupal_set_message('Caught an exception: ', $e->getMessage());
+  }
+
+  // Rejig the field, and turn on the primary key again.
+  db_change_field($table_name, $field_name, $field_name, $field_spec, $keys);
+
+  // Finally, remove the temporary unique key because it's no longer useful.
+  db_drop_unique_key($table_name, 'temp_key');
+}
+
+/**
+ * Disable the deprecated metatag_ui module which has been merged into metatag,
+ * again.
+ */
+function metatag_update_7002() {
+  if (module_exists('metatag_ui')) {
+    module_disable(array('metatag_ui'), FALSE);
+    drupal_uninstall_modules(array('metatag_ui'), FALSE);
+    drupal_set_message(t('The deprecated Metatag UI module has been disabled.'));
+  }
+}
+
+/**
+ * Add the {metatag}.language field.
+ */
+function metatag_update_7003() {
+  // Set the target table and field name.
+  $table_name = 'metatag';
+  $field_name = 'language';
+
+  // Don't add the new field if it already exists.
+  if (!db_field_exists($table_name, $field_name)) {
+    // Describe the new field.
+    $field_spec = array(
+      'type' => 'varchar',
+      'length' => 32,
+      'not null' => TRUE,
+      'default' => '',
+      'description' => 'The language of the tag',
+    );
+
+    // Add it and update the primary key.
+    db_add_field($table_name, $field_name, $field_spec);
+    db_drop_primary_key($table_name);
+    db_add_primary_key($table_name, array('entity_type', 'entity_id', 'language'));
+  }
+}
+
+/**
+ * Replaced by updates 7009, 7010, 7011, 7012 and 7013.
+ */
+function metatag_update_7004() {
+  // Do nothing.
+}
+
+/**
+ * Removing wrong metatag watchdog entries that break the admin/reports/dblog
+ * page.
+ */
+function metatag_update_7005() {
+  if (db_table_exists('watchdog')) {
+    db_delete('watchdog')
+      ->condition('type', 'metatag')
+      ->condition('variables', serialize('info'))
+      ->execute();
+  }
+}
+
+/**
+ * Remove {metatag} records that were added by old versions of the module for
+ * entities that don't actually support Metatag. A more complete version of
+ * this will be added later on after it's (hopefully) guaranteed that all
+ * modules have updated to the correct API usage.
+ */
+function metatag_update_7006() {
+  $entity_types = array(
+    // Core.
+    'comment',
+    'menu_link',
+    'taxonomy_vocabulary',
+    // Some contrib entities.
+    'mailchimp_list',
+    'profile2',
+    'profile2_type',
+    'redirect',
+    'rules_config',
+    'wysiwyg_profile',
+  );
+  foreach ($entity_types as $entity_type) {
+    $num_deleted = db_delete('metatag')
+      ->condition('entity_type', $entity_type)
+      ->execute();
+    if ($num_deleted > 0) {
+      drupal_set_message(t('Removed @count meta tag record(s) for the @type entity type, it does not support meta tags.', array('@count' => $num_deleted, '@type' => $entity_type)));
+    }
+  }
+}
+
+/**
+ * Remove {metatag} records for nodes, users and taxonomy terms that have been
+ * deleted; older versions of Metatag may have failed to purge these.
+ */
+function metatag_update_7007() {
+  $nodes = db_query("SELECT m.entity_id
+    FROM {metatag} m
+    LEFT OUTER JOIN {node} n
+      ON m.entity_id=n.nid
+    WHERE m.entity_type='node'
+      AND n.nid IS NULL")
+    ->fetchCol();
+  if (count($nodes) > 0) {
+    $deleted = db_delete('metatag')
+      ->condition('entity_type', 'node')
+      ->condition('entity_id', $nodes)
+      ->execute();
+    if ($deleted > 0) {
+      drupal_set_message(t('Removed @count meta tag record(s) for nodes that had been purged.', array('@count' => $deleted)));
+    }
+    else {
+      drupal_set_message(t('There were no meta tag records to purge for removed nodes. This is a good thing :)'));
+    }
+  }
+
+  $users = db_query("SELECT m.entity_id
+    FROM {metatag} m
+    LEFT OUTER JOIN {users} u
+      ON m.entity_id=u.uid
+    WHERE m.entity_type='user'
+      AND u.uid IS NULL")
+    ->fetchCol();
+  if (count($users) > 0) {
+    $deleted = db_delete('metatag')
+      ->condition('entity_type', 'user')
+      ->condition('entity_id', $users)
+      ->execute();
+    if ($deleted > 0) {
+      drupal_set_message(t('Removed @count meta tag record(s) for users that had been purged.', array('@count' => $deleted)));
+    }
+    else {
+      drupal_set_message(t('There were no meta tag records to purge for removed users. This is a good thing :)'));
+    }
+  }
+
+  // Only run this if the Taxonomy module is enabled.
+  if (module_exists('taxonomy')) {
+    $terms = db_query("SELECT m.entity_id
+      FROM {metatag} m
+      LEFT OUTER JOIN {taxonomy_term_data} t
+        ON m.entity_id=t.tid
+      WHERE m.entity_type='taxonomy_term'
+        AND t.tid IS NULL")
+      ->fetchCol();
+    if (count($terms) > 0) {
+      $deleted = db_delete('metatag')
+        ->condition('entity_type', 'taxonomy_term')
+        ->condition('entity_id', $terms)
+        ->execute();
+      if ($deleted > 0) {
+        drupal_set_message(t('Removed @count meta tag record(s) for taxonomy terms that had been purged.', array('@count' => $deleted)));
+      }
+      else {
+        drupal_set_message(t('There were no meta tag records to purge for removed taxonomy terms. This is a good thing :)'));
+      }
+    }
+  }
+}
+
+/**
+ * Remove any empty records that may be hanging around from old releases.
+ */
+function metatag_update_7008() {
+  $conditions = db_or()
+    ->isNull('data')
+    ->condition('data', '')
+    ->condition('data', serialize(array()));
+  $deleted = db_delete("metatag")
+    ->condition($conditions)
+    ->execute();
+  if ($deleted > 0) {
+    drupal_set_message(t('Purged @count empty meta tag record(s).', array('@count' => $deleted)));
+  }
+}
+
+/**
+ * Fix {metatag} records for taxonomy terms.
+ */
+function metatag_update_7009() {
+  if (module_exists('taxonomy')) {
+    // Fix the {metatag} table first.
+    metatag_update_7015();
+
+    // Remove duplicates.
+    _metatag_remove_dupes('taxonomy_term');
+  }
+
+  // The taxonomy term entity doesn't support a 'language' option, so reset it
+  // to LANGUAGE_NONE.
+  $result = db_query("UPDATE {metatag} SET language = :language WHERE entity_type='taxonomy_term'", array(':language' => LANGUAGE_NONE));
+  if ($result->rowCount() > 0) {
+    drupal_set_message(t('Fixed language values for @count taxonomy terms.', array('@count' => $result->rowCount())));
+  }
+}
+
+/**
+ * Fix {metatag} records for users.
+ */
+function metatag_update_7010() {
+  // Fix the {metatag} table first.
+  metatag_update_7015();
+
+  // Remove duplicates.
+  _metatag_remove_dupes('user');
+
+  // Update User values.
+  $result = db_query("UPDATE {metatag} SET language = :language WHERE entity_type='user'", array(':language' => LANGUAGE_NONE));
+  if ($result->rowCount() > 0) {
+    drupal_set_message(t('Fixed language values for @count user records.', array('@count' => $result->rowCount())));
+  }
+}
+
+/**
+ * Fix {metatag} records for nodes.
+ */
+function metatag_update_7011(&$sandbox) {
+  // Fix the {metatag} table first.
+  metatag_update_7015();
+
+  // Only proceed if Entity_Translation is not enabled as it allows each node
+  // record to have multiple languages available.
+  if (module_exists('entity_translation')) {
+    drupal_set_message(t("Entity Translation is enabled, so node meta tags will not be updated, to avoid accidental dataloss."));
+    return;
+  }
+
+  // Process records by groups of 10 (arbitrary value).
+  // When a group is processed, the batch update engine determines whether it
+  // should continue processing in the same request or provide progress
+  // feedback to the user and wait for the next request.
+  $limit = 10;
+  // When ran through Drush it's Ok to process a larger number of objects at a
+  // time.
+  if (drupal_is_cli()) {
+    $limit = 100;
+  }
+
+  // Use the sandbox at your convenience to store the information needed
+  // to track progression between successive calls to the function.
+  if (!isset($sandbox['progress'])) {
+    // The count of records visited so far.
+    $sandbox['progress'] = 0;
+
+    // Remove duplicates.
+    _metatag_remove_dupes('node');
+
+    // Update Node values.
+    $nodes = db_query("SELECT n.nid, n.language FROM {node} n INNER JOIN {metatag} m ON n.nid = m.entity_id WHERE m.entity_type = 'node' AND n.language != m.language ORDER BY nid");
+    $sandbox['records'] = array();
+    foreach ($nodes as $record) {
+      $sandbox['records'][] = $record;
+    }
+
+    // If there's no data, don't bother with the extra work.
+    if (empty($sandbox['records'])) {
+      watchdog('metatag', 'Update 7011: No nodes need the Metatag language values fixed.', array(), WATCHDOG_INFO);
+      if (drupal_is_cli()) {
+        drupal_set_message(t('Update 7011: No nodes need the Metatag language values fixed.'));
+      }
+      return t('No nodes need the Metatag language values fixed.');
+    }
+
+    // Total records that must be visited.
+    $sandbox['max'] = count($sandbox['records']);
+
+    // A place to store messages during the run.
+    $sandbox['messages'] = array();
+
+    // An initial record of the number of records to be updated.
+    watchdog('metatag', 'Update 7011: !count records to update.', array('!count' => $sandbox['max']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7011: !count records to update.', array('!count' => $sandbox['max'])));
+    }
+
+    // Last record processed.
+    $sandbox['current_record'] = -1;
+
+    // Because a lot of other processing happens on the first iteration, just do
+    // one.
+    $limit = 1;
+  }
+
+  // Set default values.
+  for ($ctr = 0; $ctr < $limit; $ctr++) {
+    $sandbox['current_record']++;
+    if (empty($sandbox['records'][$sandbox['current_record']])) {
+      break;
+    }
+
+    // Shortcuts for later.
+    $langcode = $sandbox['records'][$sandbox['current_record']]->language;
+    $nid = $sandbox['records'][$sandbox['current_record']]->nid;
+
+    db_update('metatag')
+      ->fields(array('language' => $langcode))
+      ->condition('entity_type', 'node')
+      ->condition('entity_id', $nid)
+      ->execute();
+
+    // Update our progress information.
+    $sandbox['progress']++;
+  }
+
+  // Set the "finished" status, to tell batch engine whether this function
+  // needs to run again. If you set a float, this will indicate the progress of
+  // the batch so the progress bar will update.
+  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($sandbox['#finished']) {
+    // Clear all caches so the fixed data will be reloaded.
+    cache_clear_all('*', 'cache_metatag', TRUE);
+
+    // A final log of the number of records that were converted.
+    watchdog('metatag', 'Update 7011: !count records were updated in total.', array('!count' => $sandbox['progress']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7011: !count records were updated.', array('!count' => $sandbox['progress'])));
+    }
+
+    // hook_update_N() may optionally return a string which will be displayed
+    // to the user.
+    return t('Fixed the Metatag language values for @count nodes.', array('@count' => $sandbox['progress']));
+  }
+}
+
+/**
+ * Remove duplicate {metatag} records for non-core entities.
+ */
+function metatag_update_7012() {
+  // Fix the {metatag} table first.
+  metatag_update_7015();
+
+  if (module_exists('entity_translation')) {
+    drupal_set_message(t("Entity Translation is enabled, duplicate meta tags will not be removed for custom entities, to avoid accidental dataloss."));
+    return;
+  }
+
+  $records = db_select('metatag', 'm')
+    ->fields('m', array('entity_type'))
+    ->condition('m.entity_type', array('node', 'taxonomy_term', 'user'), 'NOT IN')
+    ->orderBy('m.entity_type', 'ASC')
+    ->orderBy('m.entity_id', 'ASC')
+    ->distinct()
+    ->execute();
+
+  $entity_types = array();
+  foreach ($records as $record) {
+    $entity_types[] = $record->entity_type;
+    // Remove duplicates.
+    _metatag_remove_dupes($record->entity_type);
+  }
+
+  if (empty($entity_types)) {
+    drupal_set_message(t('There were no other records to fix.'));
+  }
+}
+
+/**
+ * Fix the {metatag} language value for all non-core entity records. This might
+ * take a while, depending on how much data needs to be converted.
+ */
+function metatag_update_7013(&$sandbox) {
+  // Fix the {metatag} table first.
+  metatag_update_7015();
+
+  if (module_exists('entity_translation')) {
+    drupal_set_message(t("Entity Translation is enabled, meta tags will not be updated for custom entities, to avoid accidental dataloss."));
+    return;
+  }
+
+  // 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;
+
+    // Because the {metatag} table uses multiple primary keys, there's no easy
+    // way to do this, so we're going to cache all record keys and manually
+    // step through them.
+    $records = db_select('metatag', 'm')
+      ->fields('m', array('entity_type', 'entity_id'))
+      ->condition('m.entity_type', array('node', 'taxonomy_term', 'user'), 'NOT IN')
+      ->orderBy('m.entity_type', 'ASC')
+      ->orderBy('m.entity_id', 'ASC')
+      ->execute();
+    $sandbox['records'] = array();
+    foreach ($records as $record) {
+      $sandbox['records'][] = $record;
+    }
+
+    // If there's no data, don't bother with the extra work.
+    if (empty($sandbox['records'])) {
+      watchdog('metatag', 'Update 7013: No meta tag records need updating.', array(), WATCHDOG_INFO);
+      if (drupal_is_cli()) {
+        drupal_set_message(t('Update 7013: No meta tag records need updating.'));
+      }
+      return t('No meta tag records need updating.');
+    }
+
+    // Total records that must be visited.
+    $sandbox['max'] = count($sandbox['records']);
+
+    // A place to store messages during the run.
+    $sandbox['messages'] = array();
+
+    // An initial record of the number of records to be updated.
+    watchdog('metatag', 'Update 7013: !count records to update.', array('!count' => $sandbox['max']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7013: !count records to update.', array('!count' => $sandbox['max'])));
+    }
+
+    // Last record processed.
+    $sandbox['current_record'] = -1;
+  }
+
+  // Process records by groups of 10 (arbitrary value).
+  // When a group is processed, the batch update engine determines whether it
+  // should continue processing in the same request or provide progress
+  // feedback to the user and wait for the next request.
+  $limit = 10;
+  // When ran through Drush it's Ok to process a larger number of objects at a
+  // time.
+  if (drupal_is_cli()) {
+    $limit = 100;
+  }
+
+  // Set default values.
+  for ($ctr = 0; $ctr < $limit; $ctr++) {
+    $sandbox['current_record']++;
+    if (empty($sandbox['records'][$sandbox['current_record']])) {
+      break;
+    }
+
+    // Shortcuts for later.
+    $entity_type = $sandbox['records'][$sandbox['current_record']]->entity_type;
+    $entity_id = $sandbox['records'][$sandbox['current_record']]->entity_id;
+
+    // Load the entity.
+    $entities = entity_load($entity_type, array($entity_id));
+    if (!empty($entities)) {
+      $entity = array_pop($entities);
+
+      // Make sure that the entity has a language set.
+      if (!empty($entity)) {
+        // If there's a (non-empty) language value, use it.
+        $new_language = entity_language($entity_type, $entity);
+        if (empty($new_language)) {
+          $new_language = LANGUAGE_NONE;
+        }
+        // Update the 'language' value.
+        db_update('metatag')
+          ->fields(array('language' => $new_language))
+          ->condition('entity_type', $entity_type)
+          ->condition('entity_id', $entity_id)
+          ->execute();
+      }
+    }
+
+    // Update our progress information.
+    $sandbox['progress']++;
+  }
+
+  // Set the "finished" status, to tell batch engine whether this function
+  // needs to run again. If you set a float, this will indicate the progress of
+  // the batch so the progress bar will update.
+  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($sandbox['#finished']) {
+    // Clear all caches so the fixed data will be reloaded.
+    cache_clear_all('*', 'cache_metatag', TRUE);
+
+    // A final log of the number of records that were converted.
+    watchdog('metatag', 'Update 7013: !count records were updated in total.', array('!count' => $sandbox['progress']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7013: !count records were updated.', array('!count' => $sandbox['progress'])));
+    }
+
+    // hook_update_N() may optionally return a string which will be displayed
+    // to the user.
+    return t('!count records were updated in total.', array('!count' => $sandbox['progress']));
+  }
+}
+
+/**
+ * Remove duplicate records for a given entity.
+ *
+ * It should be OK to run this without doing a separate batch process as there
+ * shouldn't be many records that have this problem. Hopefully.
+ *
+ * @param $entity_type
+ *   The name of an entity type to check for.
+ */
+function _metatag_remove_dupes($entity_type) {
+  $purge_count = 0;
+
+  // First step: fix the records. There should not be multiple records for the
+  // same entity_id with different languages.
+  $dupe_records = db_query("SELECT m.entity_id, count(m.language) AS the_count
+    FROM {metatag} m
+    WHERE
+      m.entity_type = :type
+    GROUP BY m.entity_id
+    HAVING count(m.language) > 1", array(':type' => $entity_type));
+
+  if (!empty($dupe_records)) {
+    foreach ($dupe_records as $record) {
+      $entity_id = $record->entity_id;
+      $langs = db_query("SELECT m.entity_id, m.language, m.data FROM {metatag} m WHERE m.entity_type = :type AND m.entity_id = :id", array(':type' => $entity_type, ':id' => $entity_id))->fetchAll();
+
+      // Work out which language record to remove. Will need to store this as
+      // an array incase there are multiple records to purge.
+      $langs_to_remove = array();
+
+      // Check for duplicate records.
+      // Outer loop starts from the beginning.
+      for ($outer = 0; $outer < count($langs); $outer++) {
+        // This record may have been removed already.
+        if (isset($langs[$outer])) {
+          // Inner loop starts from the end.
+          for ($inner = count($langs) - 1; $inner > 0; $inner--) {
+            // Work out if the outer loop's data is the same as the inner
+            // loop's.
+            if (isset($langs[$inner]) && $langs[$outer]->data == $langs[$inner]->data) {
+              // Remove the second record.
+              $langs_to_remove[] = $langs[$inner]->language;
+              unset($langs[$inner]);
+            }
+          }
+        }
+      }
+
+      // Only one record left.
+      if (count($langs) == 1) {
+        // This is how it should be, this record is fine.
+      }
+      // More than one record, work out which one to keep.
+      elseif (count($langs) > 1) {
+        // Work out the entity's language.
+        $entity = entity_load($entity_type, $entity_id);
+        $entity_language = entity_language($entity_type, $entity);
+        if (empty($language)) {
+          $entity_language = LANGUAGE_NONE;
+        }
+
+        // Work out if the entity's language record exists.
+        $lang_pos = NULL;
+        foreach ($langs as $key => $record) {
+          if ($record->language == $entity_language) {
+            $lang_pos = $key;
+            break;
+          }
+        }
+        // If the language record exists, delete the others.
+        if (isset($lang_pos)) {
+          foreach ($langs as $key => $record) {
+            if ($record->language != $entity_language) {
+              $langs_to_remove[] = $record->language;
+            }
+          }
+        }
+        // Otherwise look for a record for the site's default language.
+        else {
+          foreach ($langs as $key => $record) {
+            if ($record->language == $GLOBALS['language']->language) {
+              $lang_pos = $key;
+              break;
+            }
+          }
+          if (isset($lang_pos)) {
+            foreach ($langs as $key => $record) {
+              if ($record->language != $GLOBALS['language']->language) {
+                $langs_to_remove[] = $record->language;
+              }
+            }
+          }
+          // Finally check for LANGUAGE_NONE.
+          else {
+            foreach ($langs as $key => $record) {
+              if ($record->language == LANGUAGE_NONE) {
+                $lang_pos = $key;
+                break;
+              }
+            }
+            if (isset($lang_pos)) {
+              foreach ($langs as $key => $record) {
+                if ($record->language != LANGUAGE_NONE) {
+                  $langs_to_remove[] = $record->language;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      // Purge the redundant records.
+      if (!empty($langs_to_remove)) {
+        $purge_count += db_delete('metatag')
+          ->condition('entity_type', $entity_type)
+          ->condition('entity_id', $entity_id)
+          ->condition('language', $langs_to_remove)
+          ->execute();
+      }
+    }
+  }
+
+  if (empty($purge_count)) {
+    drupal_set_message(t('No duplicate :entity_type records were found (this is a good thing).', array(':entity_type' => $entity_type)));
+    watchdog('metatag', 'No duplicate :entity_type records were found (this is a good thing).', array(':entity_type' => $entity_type));
+  }
+  else {
+    drupal_set_message(t('Purged :count duplicate :entity_type record(s).', array(':count' => $purge_count, ':entity_type' => $entity_type)));
+    watchdog('metatag', 'Purged :count duplicate :entity_type record(s).', array(':count' => $purge_count, ':entity_type' => $entity_type));
+    return;
+  }
+}
+
+/**
+ * Fix {metatag} records that may have been corrupted by #1871020.
+ */
+function metatag_update_7014() {
+  $records = db_query("SELECT *
+  FROM {metatag} m
+  WHERE
+       m.data LIKE :nolang
+    OR m.data LIKE :lang
+    OR m.data LIKE :und",
+    array(
+      ':nolang' => 'a:1:{s:0:"";a:%:{s:%;a:%:{%;}}}',
+      ':lang' => 'a:1:{s:2:"__";a:%:{s:%;a:%:{%;}}}',
+      ':und' => 'a:1:{s:3:"___";a:%:{s:%;a:%:{%;}}}',
+    ));
+
+  // Nothing to fix.
+  if ($records->rowCount() == 0) {
+    drupal_set_message(t('No corrupt records to fix, this is good news :-)'));
+  }
+
+  // Fix the faulty records.
+  else {
+    foreach ($records as $record) {
+      // Extract the data and get the first element of the array, this should be
+      // valid data.
+      $record->data = reset(unserialize($record->data));
+
+      // Update the record.
+      drupal_write_record('metatag', $record, array('entity_type', 'entity_id', 'language'));
+    }
+    drupal_set_message(t('Fixed @count corrupt meta tag record(s).', array('@count' => $records->rowCount())));
+  }
+}
+
+/**
+ * Add the revision_id from the entity into metatag schema, adjust the primary
+ * keys accordingly.
+ */
+function metatag_update_7015() {
+  if (!db_field_exists('metatag', 'revision_id')) {
+    // Leave a note for metatag_metatags_load_multiple() that the revision_id
+    // field has been added.
+    variable_set('metatag_has_revision_id', TRUE);
+
+    // Tell update 7017 that it isn't needed.
+    variable_set('metatag_skip_update_7017', TRUE);
+
+    // Add the new field.
+    db_add_field('metatag', 'revision_id', array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The revision_id for the entity object this data is attached to.',
+      ));
+
+    // Remove the existing primary key. This may take some work so it can be
+    // database agnostic, i.e. some databases will not like it.
+    db_drop_primary_key('metatag');
+
+    // Add the new primary key.
+    db_add_primary_key('metatag', array('entity_type', 'entity_id', 'revision_id', 'language'));
+
+    drupal_set_message(t('Added the {metatag}.revision_id field.'));
+  }
+  else {
+    drupal_set_message(t('The {metatag}.revision_id field has already been added, nothing to do.'));
+  }
+}
+
+/**
+ * Update the revision ID to fix the NULL values, help avoid problems with
+ * update 7017.
+ */
+function metatag_update_7016() {
+  // It's possible that 7015 was not executed if the site had been updated to
+  // an early dev release, so make sure the revision_id field exists.
+  metatag_update_7015();
+
+  // Run the update.
+  db_query("UPDATE {metatag} SET revision_id = 0 WHERE revision_id IS NULL");
+}
+
+/**
+ * The {metatag}.revision_id field is required.
+ */
+function metatag_update_7017() {
+  if (!variable_get('metatag_skip_update_7017', FALSE)) {
+    // Let's add a temporary unique key so MySQL will let it go.
+    db_add_unique_key('metatag', 'temp_key', array('entity_type', 'entity_id', 'revision_id', 'language'));
+
+    // Now remove the PK before changing a field from serial.
+    db_drop_primary_key('metatag');
+
+    // Change the field.
+    db_change_field('metatag', 'revision_id', 'revision_id', array(
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+      'default' => 0,
+      'description' => 'The revision_id for the entity object this data is attached to.',
+    ));
+
+    // Manually re-add the PK.
+    db_add_primary_key('metatag', array('entity_type', 'entity_id', 'revision_id', 'language'));
+
+    // Finally, remove the temporary unique key because it's no longer useful.
+    db_drop_unique_key('metatag', 'temp_key');
+
+    drupal_set_message(t('Fixed the {metatag}.revision_id field.'));
+  }
+  else {
+    drupal_set_message(t("Didn't need to fix the {metatag}.revision_id field; please disperse, nothing to see here."));
+  }
+
+  // Delete the temporary variable.
+  variable_del('metatag_skip_update_7017');
+}
+
+/**
+ * Update the revision ID for each record. This may take some time. Should any
+ * nodes be discovered with a meta tag record for both revision_id 0 and the
+ * correct revision_id, the "0" value will be deleted; if this is not the
+ * desired result the {metatag} table must be manually pruned to have the
+ * correct records prior to letting this update run.
+ */
+function metatag_update_7018(&$sandbox) {
+  // Process records in small groups.
+  // When a group is processed, the batch update engine determines whether it
+  // should continue processing in the same request or provide progress
+  // feedback to the user and wait for the next request.
+  $limit = 10;
+  // When ran through Drush it's Ok to process a larger number of objects at a
+  // time.
+  if (drupal_is_cli()) {
+    $limit = 100;
+  }
+
+  // Use the sandbox at your convenience to store the information needed
+  // to track progression between successive calls to the function.
+  if (!isset($sandbox['progress'])) {
+    // The count of records visited so far.
+    $sandbox['progress'] = 0;
+
+    // Get a list of all records affected.
+    $sandbox['records'] = db_query("SELECT entity_type, entity_id, language
+      FROM {metatag}
+      WHERE revision_id = 0")
+      ->fetchAll();
+
+    // If there's no data, don't bother with the extra work.
+    if (empty($sandbox['records'])) {
+      watchdog('metatag', 'Update 7018: No {metatag} records needed to have the revision_id value fixed.', array(), WATCHDOG_INFO);
+      if (drupal_is_cli()) {
+        drupal_set_message(t('Update 7018: No {metatag} records needed to have the revision_id value fixed.'));
+      }
+      return t('No {metatag} records needed to have the revision_id value fixed.');
+    }
+
+    // Total records that must be visited.
+    $sandbox['max'] = count($sandbox['records']);
+
+    // A place to store messages during the run.
+    $sandbox['messages'] = array();
+
+    // An initial record of the number of records to be updated.
+    watchdog('metatag', 'Update 7018: !count records to update.', array('!count' => $sandbox['max']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7018: !count records to update.', array('!count' => $sandbox['max'])));
+    }
+
+    // Last record processed.
+    $sandbox['current_record'] = -1;
+
+    // Because a lot of other processing happens on the first iteration, just do
+    // one.
+    $limit = 1;
+  }
+
+  // Work out which entities support languages and revisions.
+  $has_language = array();
+  $has_revisions = array();
+  foreach (entity_get_info() as $entity_type => $info) {
+    $has_language[$entity_type] = FALSE;
+    $has_revisions[$entity_type] = FALSE;
+    if (!empty($info['entity keys']['language'])) {
+      $has_language[$entity_type] = $info['entity keys']['language'];
+    }
+    if (!empty($info['entity keys']['revision'])) {
+      $has_revisions[$entity_type] = $info['entity keys']['revision'];
+    }
+  }
+
+  // Set default values.
+  for ($ctr = 0; $ctr < $limit; $ctr++) {
+    $sandbox['current_record']++;
+    if (empty($sandbox['records'][$sandbox['current_record']])) {
+      break;
+    }
+
+    // Shortcuts for later.
+    $entity_type = $sandbox['records'][$sandbox['current_record']]->entity_type;
+    $entity_id = $sandbox['records'][$sandbox['current_record']]->entity_id;
+    // Make sure to load the correct language record.
+    $language = $sandbox['records'][$sandbox['current_record']]->language;
+    $conditions = array();
+    // Some entities don't include a language value.
+    if (!empty($has_language[$entity_type])) {
+      $conditions['language'] = $language;
+    }
+    $records = entity_load($entity_type, array($entity_id), $conditions);
+
+    // Fix this record.
+    if (!empty($records)) {
+      $entity = reset($records);
+
+      // Speed up the handling of entities that don't support revisions.
+      $revision_id = 0;
+      if (!empty($has_revisions[$entity_type])) {
+        list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
+        $revision_id = intval($revision_id);
+      }
+
+      // Don't bother updating records if the revision_id is 0.
+      if (!empty($revision_id)) {
+        $exists = db_query("SELECT entity_id
+            FROM {metatag}
+            WHERE entity_type = :entity_type
+            AND entity_id = :entity_id
+            AND revision_id = :revision_id
+            AND language = :language",
+          array(
+            ':entity_type' => $entity_type,
+            ':entity_id' => $entity_id,
+            ':revision_id' => $revision_id,
+            ':language' => $language,
+          ))->fetchObject();
+        // There isn't already a record for the revision_id, so update the
+        // metatag record.
+        if (!$exists) {
+          db_update('metatag')
+            ->fields(array('revision_id' => $revision_id))
+            ->condition('entity_type', $entity_type)
+            ->condition('entity_id', $entity_id)
+            ->condition('revision_id', 0)
+            ->condition('language', $language)
+            ->execute();
+        }
+        // The record exists, so delete the old one under the grounds that the
+        // one with a revision_id is newer.
+        // Disclaimer: this is completely arbitrary, without providing a UI to
+        // let the site maintainer/builder choose which of the two records to
+        // keep, we're stuck with a bad scenario. Thankfully this should not
+        // happen very often and would only affect sites that were running a
+        // dev release. Also, sorry :(
+        else {
+          db_delete('metatag')
+            ->condition('entity_type', $entity_type)
+            ->condition('entity_id', $entity_id)
+            ->condition('revision_id', 0)
+            ->condition('language', $language)
+            ->execute();
+        }
+
+        // Nodes can have multiple revisions, so create new {metatag} records
+        // for each of the other revisions.
+        if ($entity_type == 'node') {
+          $revisions = node_revision_list($entity);
+          if (count($revisions) > 1) {
+            $metatags = db_query("SELECT data
+                FROM {metatag}
+                WHERE entity_type = :entity_type
+                AND entity_id = :entity_id
+                AND language = :language",
+              array(
+                ':entity_type' => $entity_type,
+                ':entity_id' => $entity_id,
+                ':language' => $language,
+              ));
+            if (!empty($metatags) && isset($metatags->data) && !empty($metatags->data)) {
+              foreach ($revisions as $vid => $revision) {
+                // Only one record per nid/vid/langcode, thank you.
+                if ($vid != $revision_id) {
+                  // Check that there isn't already a record for this revision.
+                  $exists = db_query("SELECT entity_id
+                      FROM {metatag}
+                      WHERE entity_type = :entity_type
+                      AND entity_id = :entity_id
+                      AND revision_id = :revision_id
+                      AND language = :language",
+                    array(
+                      ':entity_type' => $entity_type,
+                      ':entity_id' => $entity_id,
+                      ':revision_id' => $vid,
+                      ':language' => $language,
+                    ))->fetchObject();
+                  if (!$exists) {
+                    $node = node_load($entity_id, $vid);
+                    $record = new StdClass();
+                    $record->entity_type = $entity_type;
+                    $record->entity_id = $entity_id;
+                    $record->revision_id = $vid;
+                    $record->language = $language;
+                    $record->data = $metatags->data;
+                    drupal_write_record('metatag', $record);
+                  }
+                }
+              }
+            }
+          }
+        }
+
+        // Other entity types.
+        else {
+          drupal_set_message(t('Metatag records for @type objects have not been checked for revisions.', array('@type' => $entity_type)), 'status', FALSE);
+        }
+      }
+    }
+
+    // Update our progress information.
+    $sandbox['progress']++;
+  }
+
+  // Set the "finished" status, to tell batch engine whether this function
+  // needs to run again. If you set a float, this will indicate the progress of
+  // the batch so the progress bar will update.
+  $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($sandbox['#finished']) {
+    // Clear all caches so the fixed data will be reloaded.
+    cache_clear_all('*', 'cache_metatag', TRUE);
+
+    // A final log of the number of records that were converted.
+    watchdog('metatag', 'Update 7018: !count records were updated in total.', array('!count' => $sandbox['progress']), WATCHDOG_INFO);
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Update 7018: !count records were updated.', array('!count' => $sandbox['progress'])));
+    }
+
+    // hook_update_N() may optionally return a string which will be displayed
+    // to the user.
+    return t('Fixed the revision_id values for !count {metatag} records.', array('!count' => $sandbox['progress']));
+  }
+}
+
+/**
+ * Clear the entity_cache bins.
+ */
+function metatag_update_7019() {
+  if (module_exists('entitycache')) {
+    foreach (drupal_get_schema() as $table_name => $spec) {
+      if (strpos($table_name, 'cache_entity_') === 0) {
+        cache_clear_all('*', $table_name, TRUE);
+        drupal_set_message(t("Cleared the @table cache bin", array('@table' => $table_name)));
+      }
+    }
+  }
+  else {
+    drupal_set_message(t("The EntityCache module is not installed, nothing to do."));
+  }
+}
+
+/**
+ * Clear the Metatag cache.
+ */
+function metatag_update_7020() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  return t('All Metatag caches cleared.');
+}
+
+/**
+ * Clear the existing Metatag cache so all unwanted 403/404 paths can be
+ * purged.
+ */
+function metatag_update_7021() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  return t('All Metatag caches cleared.');
+}
+
+/**
+ * A minor bit of tidy-up after update 7015.
+ */
+function metatag_update_7022() {
+  variable_del('metatag_skip_update_7015');
+}
+
+/**
+ * Clear the Metatag cache because $cid_parts was changed.
+ */
+function metatag_update_7023() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  return t('All Metatag caches cleared.');
+}
+
+/**
+ * Rename the 'twitter:image' meta tag to 'twitter:image:src', part 1.
+ */
+function metatag_update_7024() {
+  // Find all {metatag} records that contained an entry for the old meta tag.
+  $records = db_query("SELECT entity_type, entity_id, revision_id, language, data
+    FROM {metatag}
+    WHERE data LIKE '%twitter:image%'");
+  // This message will be returned if nothing needed to be updated.
+  $none_message = t('No Metatag entity records needed to have the "twitter:image" meta tag fixed.');
+
+  if ($records->rowCount() == 0) {
+    drupal_set_message($none_message);
+  }
+  else {
+    $keys = array('entity_type', 'entity_id', 'revision_id', 'language');
+
+    // Loop over the values and correct them.
+    $counter = 0;
+    foreach ($records as $record) {
+      $record->data = unserialize($record->data);
+      if (isset($record->data['twitter:image'])) {
+        $record->data['twitter:image:src'] = $record->data['twitter:image'];
+        unset($record->data['twitter:image']);
+        drupal_write_record('metatag', $record, $keys);
+        $counter++;
+      }
+    }
+    if ($counter == 0) {
+      drupal_set_message($none_message);
+    }
+    else {
+      drupal_set_message(t('Converted the "twitter:image" meta tag for @count entity records to the correct "twitter:image:src" meta tag.', array('@count' => $counter)));
+    }
+  }
+}
+
+/**
+ * Replaced by update 7030.
+ */
+function metatag_update_7025() {
+  // Do nothing.
+}
+
+/**
+ * Rename the 'copyright' meta tag to 'rights', part 1.
+ */
+function metatag_update_7026() {
+  // Find all {metatag} records that contained an entry for the old meta tag.
+  $records = db_query("SELECT entity_type, entity_id, revision_id, language, data
+    FROM {metatag}
+    WHERE data LIKE '%copyright%'");
+  // This message will be returned if nothing needed to be updated.
+  $none_message = t('No Metatag entity records needed to have the "copyright" meta tag fixed.');
+
+  if ($records->rowCount() == 0) {
+    drupal_set_message($none_message);
+  }
+  else {
+    $keys = array('entity_type', 'entity_id', 'revision_id', 'language');
+
+    // Loop over the values and correct them.
+    $counter = 0;
+    foreach ($records as $record) {
+      $record->data = unserialize($record->data);
+      if (isset($record->data['copyright'])) {
+        $record->data['rights'] = $record->data['copyright'];
+        unset($record->data['copyright']);
+        drupal_write_record('metatag', $record, $keys);
+        $counter++;
+      }
+    }
+    if ($counter == 0) {
+      drupal_set_message($none_message);
+    }
+    else {
+      drupal_set_message(t('Converted the "copyright" meta tag for @count entity records to the correct "rights" meta tag.', array('@count' => $counter)));
+    }
+  }
+}
+
+/**
+ * Replaced by update 7031.
+ */
+function metatag_update_7027() {
+  // Do nothing.
+}
+
+/**
+ * Clear the menu cache so the new Advanced Settings page will be picked up.
+ */
+function metatag_update_7028() {
+  variable_set('menu_rebuild_needed', TRUE);
+}
+
+/**
+ * Add an index to the {metatag} table to speed up some queries.
+ */
+function metatag_update_7029() {
+  db_add_index('metatag', 'type_revision', array('entity_type', 'revision_id'));
+  drupal_set_message(t('Added an index to the main Metatag table that will hopefully improve performance a little.'));
+}
+
+/**
+ * Rename the 'twitter:image' meta tag to 'twitter:image:src', part 2.
+ */
+function metatag_update_7030() {
+  // Find all {metatag_config} records that contained an entry for the old meta
+  // tag.
+  $records = db_query("SELECT cid, config
+    FROM {metatag_config}
+    WHERE config LIKE '%twitter:image%'");
+  // This message will be returned if nothing needed to be updated.
+  $none_message = t('No Metatag configuration records needed to have the "twitter:image" meta tag fixed. That said, there may be other configurations elsewhere that do need updating.');
+
+  // Loop over the values and correct them.
+  if ($records->rowCount() == 0) {
+    drupal_set_message($none_message);
+  }
+  else {
+    $keys = array('cid');
+
+    // Loop over the values and correct them.
+    $counter = 0;
+    foreach ($records as $record) {
+      $record->config = unserialize($record->config);
+      if (isset($record->config['twitter:image'])) {
+        $record->config['twitter:image:src'] = $record->config['twitter:image'];
+        unset($record->config['twitter:image']);
+        drupal_write_record('metatag_config', $record, $keys);
+        $counter++;
+      }
+    }
+    if ($counter == 0) {
+      drupal_set_message($none_message);
+    }
+    else {
+      drupal_set_message(t('Converted the "twitter:image" meta tag for @count configurations to the correct "twitter:image:src" meta tag.', array('@count' => $counter)));
+    }
+  }
+}
+
+/**
+ * Rename the 'copyright' meta tag to 'rights', part 2.
+ */
+function metatag_update_7031() {
+  // Find all {metatag_config} records that contained an entry for the old meta
+  // tag.
+  $records = db_query("SELECT cid, config
+    FROM {metatag_config}
+    WHERE config LIKE '%copyright%'");
+  // This message will be returned if nothing needed to be updated.
+  $none_message = t('No Metatag configuration records needed to have the "copyright" meta tag fixed. That said, there may be other configurations elsewhere that do need updating.');
+
+  // Loop over the values and correct them.
+  if ($records->rowCount() == 0) {
+    drupal_set_message($none_message);
+  }
+  else {
+    $keys = array('cid');
+
+    // Loop over the values and correct them.
+    $counter = 0;
+    foreach ($records as $record) {
+      $record->config = unserialize($record->config);
+      if (isset($record->config['copyright'])) {
+        $record->config['rights'] = $record->config['copyright'];
+        unset($record->config['copyright']);
+        drupal_write_record('metatag_config', $record, $keys);
+        $counter++;
+      }
+    }
+    if ($counter == 0) {
+      drupal_set_message($none_message);
+    }
+    else {
+      drupal_set_message(t('Converted the "copyright" meta tag for @count configurations to the correct "rights" meta tag.', array('@count' => $counter)));
+    }
+  }
+}
+
+/**
+ * Clear the Metatag cache.
+ */
+function metatag_update_7032() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  return t('All Metatag caches cleared.');
+}
+
+/**
+ * These originally removed the 'author' meta tag, but it was subsequently
+ * decided that this was not the correct approach, that the meta tag should not
+ * be removed after all.
+ *
+ * @see https://www.drupal.org/node/2330823
+ */
+function metatag_update_7033() {
+}
+function metatag_update_7034() {
+}
+function metatag_update_7035() {
+}

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

@@ -0,0 +1,353 @@
+<?php
+
+/**
+ * Implements hook_metatag_config_default().
+ */
+function metatag_metatag_config_default() {
+  // Optionally skip loading the defaults.
+  if (!variable_get('metatag_load_defaults', TRUE)) {
+    return;
+  }
+
+  $configs = array();
+
+  $config = new stdClass();
+  $config->instance = 'global';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'title' => array('value' => '[current-page:title] | [site:name]'),
+    'generator' => array('value' => 'Drupal 7 (http://drupal.org)'),
+    'canonical' => array('value' => '[current-page:url:absolute]'),
+    'shortlink' => array('value' => '[current-page:url:unaliased]'),
+  );
+  $configs[$config->instance] = $config;
+
+  $config = new stdClass();
+  $config->instance = 'global:frontpage';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'title' => array('value' => variable_get('site_slogan') ? '[site:name] | [site:slogan]' : '[site:name]'),
+    'canonical' => array('value' => '[site:url]'),
+    'shortlink' => array('value' => '[site:url]'),
+  );
+  $configs[$config->instance] = $config;
+
+  $config = new stdClass();
+  $config->instance = 'global:403';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'canonical' => array('value' => '[site:url]'),
+    'shortlink' => array('value' => '[site:url]'),
+  );
+  $configs[$config->instance] = $config;
+
+  $config = new stdClass();
+  $config->instance = 'global:404';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'canonical' => array('value' => '[site:url]'),
+    'shortlink' => array('value' => '[site:url]'),
+  );
+  $configs[$config->instance] = $config;
+
+  $config = new stdClass();
+  $config->instance = 'node';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'title' => array('value' => '[node:title] | [site:name]'),
+    'description' => array('value' => '[node:summary]'),
+  );
+  $configs[$config->instance] = $config;
+
+  if (module_exists('taxonomy')) {
+    $config = new stdClass();
+    $config->instance = 'taxonomy_term';
+    $config->api_version = 1;
+    $config->disabled = FALSE;
+    $config->config = array(
+      'title' => array('value' => '[term:name] | [site:name]'),
+      'description' => array('value' => '[term:description]'),
+    );
+    $configs[$config->instance] = $config;
+  }
+
+  $config = new stdClass();
+  $config->instance = 'user';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'title' => array('value' => '[user:name] | [site:name]'),
+  );
+  if (variable_get('user_pictures')) {
+    $config->config['image'] = array('value' => '[user:picture:url]');
+  }
+  $configs[$config->instance] = $config;
+
+  // Before returning these, allow the bundled submodules to override them, thus
+  // extending the "real" defaults before they can then be altered by other
+  // modules.
+  // See hook_metatag_bundled_config_alter() in the API documentation.
+  drupal_alter('metatag_bundled_config', $configs);
+
+  return $configs;
+}
+
+/**
+ * Implements hook_metatag_config_instance_info().
+ */
+function metatag_metatag_config_instance_info() {
+  $info['global']           = array('label' => t('Global'));
+  $info['global:frontpage'] = array('label' => t('Front page'));
+  $info['global:403']       = array('label' => t('403 access denied'));
+  $info['global:404']       = array('label' => t('404 page not found'));
+
+  // Add instance information for entities.
+  $entity_types = entity_get_info();
+  foreach ($entity_types as $entity_type => $entity_info) {
+    if (metatag_entity_supports_metatags($entity_type)) {
+      $info[$entity_type] = array('label' => $entity_info['label']);
+      foreach ($entity_info['bundles'] as $bundle => $bundle_info) {
+        if (count($entity_info['bundles'] == 1) && $bundle == $entity_type) {
+          // Skip default bundles (entities that do not really have bundles).
+          continue;
+        }
+        if (metatag_entity_supports_metatags($entity_type, $bundle)) {
+          $info[$entity_type . ':' . $bundle] = array('label' => $bundle_info['label']);
+        }
+      }
+    }
+  }
+
+  return $info;
+}
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_metatag_info() {
+  $info['groups']['advanced'] = array(
+    'label' => t('Advanced'),
+    'form' => array(
+      '#weight' => 100,
+    ),
+  );
+
+  // "Simple" meta tags go first.
+  $weight = 0;
+
+  $info['tags']['title'] = array(
+    'label' => t('Page title'),
+    'description' => t("The text to display in the title bar of a visitor's web browser when they view this page. This meta tag may also be used as the title of the page when a visitor bookmarks or favorites this page."),
+    'class' => 'DrupalTitleMetaTag',
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['description'] = array(
+    'label' => t('Description'),
+    'description' => t("A brief and concise summary of the page's content, preferably 150 characters or less. The description meta tag may be used by search engines to display a snippet about the page in search results."),
+    'class' => 'DrupalTextMetaTag',
+    'weight' => ++$weight,
+    'form' => array(
+      '#type' => 'textarea',
+      '#rows' => 2,
+      '#wysiwyg' => FALSE,
+    ),
+  );
+
+  $info['tags']['abstract'] = array(
+    'label' => t('Abstract'),
+    'description' => t("A brief and concise summary of the page's content, preferably 150 characters or less. The abstract meta tag may be used by search engines for archiving purposes."),
+    'class' => 'DrupalTextMetaTag',
+    'weight' => ++$weight,
+    'form' => array(
+      '#type' => 'textarea',
+      '#rows' => 2,
+      '#wysiwyg' => FALSE,
+    ),
+  );
+
+  $info['tags']['keywords'] = array(
+    'label' => t('Keywords'),
+    'description' => t("A comma-separated list of keywords about the page. This meta tag is <em>not</em> supported by most search engines anymore."),
+    'class' => 'DrupalTextMetaTag',
+    'weight' => ++$weight,
+  );
+
+  // More advanced meta tags.
+  $info['tags']['robots'] = array(
+    'label' => t('Robots'),
+    'description' => t("Provides search engines with specific directions for what to do when this page is indexed."),
+    'class' => 'DrupalListMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'form' => array(
+      '#options' => array(
+        'index' => t('Allow search engines to index this page (assumed).'),
+        'follow' => t('Allow search engines to follow links on this page (assumed).'),
+        'noindex' => t('Prevents search engines from indexing this page.'),
+        'nofollow' => t('Prevents search engines from following links on this page.'),
+        'noarchive' => t('Prevents cached copies of this page from appearing in search results.'),
+        'nosnippet' => t('Prevents descriptions from appearing in search results, and prevents page caching.'),
+        'noodp' => t('Blocks the <a href="!opendirectory">Open Directory Project</a> description from appearing in search results.', array('!opendirectory' => 'http://www.dmoz.org/')),
+        'noydir' => t('Prevents Yahoo! from listing this page in the <a href="@ydir">Yahoo! Directory</a>.', array('@ydir' => 'http://dir.yahoo.com/')),
+        'noimageindex' => t('Prevent search engines from indexing images on this page.'),
+        'notranslate' => t('Prevent search engines from offering to translate this page in search results.'),
+      ),
+    ),
+  );
+
+  $info['tags']['news_keywords'] = array(
+    'label' => t('Google News Keywords'),
+    'description' => t('A comma-separated list of keywords about the page. This meta tag is used as an indicator in <a href="@google_news">Google News</a>.', array('@google_news' => 'http://support.google.com/news/publisher/bin/answer.py?hl=en&answer=68297')),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['standout'] = array(
+    'label' => t('Google Standout'),
+    'description' => t("Highlight standout journalism on the web, especially for breaking news; used as an indicator in <a href=\"@google_news\">Google News</a>. Warning: Don't abuse it, to be used a maximum of 7 times per calendar week!", array('@google_news' => 'https://support.google.com/news/publisher/answer/191283?hl=en&ref_topic=2484650')),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['generator'] = array(
+    'label' => t('Generator'),
+    'description' => t("Describes the name and version number of the software or publishing tool used to create the page."),
+    'class' => 'DrupalTextMetaTag',
+    'header' => 'X-Generator',
+    'context' => array('global'),
+    'group' => 'advanced',
+    'weight' => ++$weight,
+  );
+
+  $info['tags']['rights'] = array(
+    'label' => t('Rights'),
+    'description' => t("Details about intellectual property, such as copyright or trademarks; does not automatically protect the site's content or intellectual property."),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'replaces' => array(
+      'copyright',
+    ),
+  );
+
+  $info['tags']['image_src'] = array(
+    'label' => t('Image'),
+    'description' => t("An image associated with this page, for use as a thumbnail in social networks and other services."),
+    'class' => 'DrupalLinkMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'image',
+    ),
+  );
+
+  $info['tags']['canonical'] = array(
+    'label' => t('Canonical URL'),
+    'description' => t("Preferred page location or URL to help eliminate duplicate content for search engines."),
+    'class' => 'DrupalLinkMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'canonical',
+    ),
+  );
+
+  $info['tags']['shortlink'] = array(
+    'label' => t('Shortlink URL'),
+    'description' => t('A brief URL, often created by a URL shortening service.'),
+    'class' => 'DrupalLinkMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'shortlink',
+    ),
+  );
+
+  $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' => '',
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'description' => t("Used to indicate the URL that broke the story, and can link to either an internal URL or an external source. If the full URL is not known it is acceptable to use a partial URL or just the domain name."),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  );
+
+  $info['tags']['prev'] = array(
+    'label' => t('Previous page URL'),
+    'description' => t('Used for paginated content. Meet Google recommendations to <a href="@google_pagination">indicate paginated content</a> by providing URL with rel="prev" link.', array('@google_pagination' => 'https://support.google.com/webmasters/answer/1663744')),
+    'class' => 'DrupalLinkMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  );
+
+  $info['tags']['next'] = array(
+    'label' => t('Next page URL'),
+    'description' => t('Used for paginated content. Meet Google recommendations to <a href="@google_pagination">indicate paginated content</a> by providing URL with rel="next" link.', array('@google_pagination' => 'https://support.google.com/webmasters/answer/1663744')),
+    'class' => 'DrupalLinkMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  );
+
+  $info['tags']['revisit-after'] = array(
+    'label' => t('Revisit After'),
+    'description' => t('Tell search engines when to index the page again. Very few search engines support this tag, it is more useful to use an <a href="@xmlsitemap">XML Sitemap</a> file.', array('@xmlsitemap' => 'https://www.drupal.org/project/xmlsitemap')),
+    'class' => 'DrupalDateIntervalMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'date',
+    ),
+  );
+
+  $info['tags']['content-language'] = array(
+    'label' => t('Content language'),
+    'description' => t("A deprecated meta tag for defining this page's two-letter language code(s)."),
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'advanced',
+    'weight' => ++$weight,
+    'element' => array(
+      '#theme' => 'metatag_http_equiv',
+    ),
+  );
+
+  return $info;
+}

+ 125 - 0
sites/all/modules/contrib/seo/metatag/metatag.migrate.inc

@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Metatag support for Migrate.
+ */
+
+/**
+ * Basic usage of the Migrate integration.
+ *
+ * This example assumes the custom module's name is "example_migrate".
+ * 
+ * example_migrate.inc:
+ * 
+ * class MetatagTestMigration extends DynamicMigration {
+ * 
+ *   public function __construct() {
+ *     parent::__construct();
+ * 
+ *     $this->description = t('Migrate test.');
+ * 
+ *     $this->map = new MigrateSQLMap(
+ *       $this->machineName,
+ *       array(
+ *         'id' => array(
+ *           'type' => 'varchar',
+ *           'not null' => TRUE,
+ *           'length' => 254,
+ *           'description' => 'ID of record.',
+ *         ),
+ *       ),
+ *       MigrateDestinationNode::getKeySchema()
+ *     );
+ * 
+ *     $this->source = new MigrateSourceCSV(
+ *       drupal_get_path('module', 'example_migrate') . '/sample.csv',
+ *       array(),
+ *       array('header_rows' => TRUE)
+ *     );
+ * 
+ *     $this->destination = new MigrateDestinationNode('article');
+ * 
+ *     $this->addFieldMapping('metatag_description', 'description');
+ *     $this->addFieldMapping('metatag_keywords', 'keywords');
+ *   }
+ * }
+ * 
+ * example_migrate.migrate.inc:
+ * 
+ * /**
+ *  * Implements hook_migrate_api().
+ *  * /
+ * function example_migrate_migrate_api() {
+ *   $api = array(
+ *     'api' => 2,
+ *     'migrations' => array(
+ *       'MetatagTest' => array('class_name' => 'MetatagTestMigration'),
+ *     ),
+ *   );
+ * 
+ *   return $api;
+ * }
+ */
+
+/**
+ * Implements hook_migrate_api().
+ */
+function metatag_migrate_api() {
+  $api = array(
+    'api' => 2,
+    'destination handlers' => array(
+      'MigrateMetatagHandler',
+    ),
+  );
+
+  return $api;
+}
+
+/**
+ * Metatag destination handler.
+ */
+class MigrateMetatagHandler extends MigrateDestinationHandler {
+
+  public function __construct() {
+    $entity_types = array();
+    foreach (entity_get_info() as $entity_type => $entity_info) {
+      if (isset($entity_info['metatags']) && !empty($entity_info['metatags'])) {
+        $entity_types[] = $entity_type;
+      }
+    }
+
+    $this->registerTypes($entity_types);
+  }
+
+  /**
+   * Implements MigrateDestinationHandler::fields().
+   */
+  public function fields() {
+    $fields = array();
+    $elements = metatag_get_info();
+
+    foreach ($elements['tags'] as $value) {
+      $metatag_field = 'metatag_' . $value['name'];
+      $fields[$metatag_field] = $value['description'];
+    }
+
+    return $fields;
+  }
+
+  /**
+   * Implements MigrateDestinationHandler::prepare().
+   */
+  public function prepare($entity, stdClass $row) {
+    $elements = metatag_get_info();
+
+    foreach ($elements['tags'] as $value) {
+      $metatag_field = 'metatag_' . $value['name'];
+      if (isset($entity->$metatag_field)) {
+        $language = isset($entity->language) ? $entity->language : LANGUAGE_NONE;
+        $entity->metatags[$language][$value['name']]['value'] = $entity->$metatag_field;
+        unset($entity->$metatag_field);
+      }
+    }
+  }
+}

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

@@ -0,0 +1,2337 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function metatag_help($path, $arg) {
+  if ($path == 'admin/config/search/metatags') {
+    return '<p>' . t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.') . '</p>';
+  }
+  elseif ($path == 'admin/help#metatag') {
+    return '<p>' . t('The Metatag module provides a options to let each page have customized meta data added to the "meta" tags in the HEAD section of the document.') . '</p>';
+  }
+  elseif ($path == 'admin/config/search/metatags/bulk-revert') {
+    return '<p>' . t('This form <strong>will wipe out</strong> all custom meta tags for the selected entities, reverting them to the default configuration assigned at the <a href="@url">Defaults tab</a>. For example, if the meta tags are changed for an article they will be removed if the "Node: Article" checkbox is selected.', array('@url' => url('admin/config/search/metatags'))) . '</p>';
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function metatag_theme() {
+  $info['metatag'] = array(
+    'render element' => 'element',
+    'file' => 'metatag.theme.inc',
+  );
+  $info['metatag_http_equiv'] = array(
+    'render element' => 'element',
+    'file' => 'metatag.theme.inc',
+  );
+  $info['metatag_link_rel'] = array(
+    'render element' => 'element',
+    'file' => 'metatag.theme.inc',
+  );
+  $info['metatag_link_rev'] = array(
+    'render element' => 'element',
+    'file' => 'metatag.theme.inc',
+  );
+  $info['metatag_property'] = array(
+    'render element' => 'element',
+    'file' => 'metatag.theme.inc',
+  );
+
+  return $info;
+}
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function metatag_hook_info() {
+  $hooks = array(
+    'metatag_config_default',
+    'metatag_config_default_alter',
+    'metatag_config_delete',
+    'metatag_config_insert',
+    'metatag_config_instance_info',
+    'metatag_config_instance_info_alter',
+    'metatag_config_load',
+    'metatag_config_load_presave',
+    'metatag_config_update',
+    'metatag_info',
+    'metatag_info_alter',
+  );
+
+  return array_fill_keys($hooks, array('group' => 'metatag'));
+}
+
+/**
+ * Implements hook_permission().
+ */
+function metatag_permission() {
+  $permissions['administer meta tags'] = array(
+    'title' => t('Administer meta tags'),
+    'restrict access' => TRUE,
+    'description' => t('Control the main settings pages and modify per-object meta tags.'),
+  );
+  $permissions['edit meta tags'] = array(
+    'title' => t('Edit meta tags'),
+    'description' => t('Modify meta tags on individual entity records (nodes, terms, users, etc).'),
+  );
+
+  // Optional extended edit permissions.
+  if (variable_get('metatag_extended_permissions', FALSE)) {
+    $permissions['edit meta tags']['description'] .= '<br />' . t('<em>Extended Permissions</em> has been enabled. Roles have the :admin permission will see all meta tags on edit forms, otherwise the permissions below will control which meta tags are available and are needed in addition to <em>Edit meta tags</em>.', array(':admin' => t('Administer meta tags')));
+    $metatags = metatag_get_info();
+    foreach ($metatags['tags'] as $metatag_name => $metatag) {
+      $permissions['edit meta tag: ' . $metatag_name] = array(
+        'title' => t('Extended permission: Edit :tag meta tag', array(':tag' => $metatag['label'])),
+        'description' => t('Customize the :tag meta tag on individual forms.', array(':tag' => $metatag['label'])),
+      );
+    }
+  }
+
+  return $permissions;
+}
+
+/**
+ * Implements hook_menu().
+ */
+function metatag_menu() {
+  $items['admin/config/search/metatags'] = array(
+    'title' => 'Metatag',
+    'description' => 'Configure Metatag defaults.',
+    'page callback' => 'metatag_config_overview',
+    'access arguments' => array('administer meta tags'),
+    'file' => 'metatag.admin.inc',
+  );
+  $items['admin/config/search/metatags/config'] = array(
+    'title' => 'Defaults',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/config/search/metatags/config/add'] = array(
+    'title' => 'Add a Metatag default',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_config_add_form'),
+    'access arguments' => array('administer meta tags'),
+    'file' => 'metatag.admin.inc',
+    'type' => MENU_LOCAL_ACTION,
+  );
+  $items['admin/config/search/metatags/config/%metatag_config'] = array(
+    'title callback' => 'metatag_config_title',
+    'title arguments' => array(5),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_config_edit_form', 5),
+    'access arguments' => array('administer meta tags'),
+    'file' => 'metatag.admin.inc',
+  );
+  $items['admin/config/search/metatags/config/%metatag_config/edit'] = array(
+    'title' => 'Edit',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/config/search/metatags/config/%metatag_config/enable'] = array(
+    'title' => 'Enable',
+    'page callback' => 'metatag_config_enable',
+    'page arguments' => array(5),
+    'access callback' => 'metatag_config_access',
+    'access arguments' => array('enable', 5),
+    'file' => 'metatag.admin.inc',
+  );
+  $items['admin/config/search/metatags/config/%metatag_config/disable'] = array(
+    'title' => 'Disable',
+    'page callback' => 'metatag_config_disable',
+    'page arguments' => array(5),
+    'access callback' => 'metatag_config_access',
+    'access arguments' => array('disable', 5),
+    'file' => 'metatag.admin.inc',
+  );
+  $items['admin/config/search/metatags/config/%metatag_config/revert'] = array(
+    'title' => 'Revert',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_config_delete_form', 5),
+    'access callback' => 'metatag_config_access',
+    'access arguments' => array('revert', 5),
+    'file' => 'metatag.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['admin/config/search/metatags/config/%metatag_config/delete'] = array(
+    'title' => 'Delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_config_delete_form', 5),
+    'access callback' => 'metatag_config_access',
+    'access arguments' => array('delete', 5),
+    'file' => 'metatag.admin.inc',
+  );
+  $items['admin/config/search/metatags/config/%metatag_config/export'] = array(
+    'title' => 'Export',
+    'page callback' => 'metatag_config_export_form',
+    'page arguments' => array(5),
+    'access arguments' => array('administer meta tags'),
+    'file' => 'metatag.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10,
+  );
+  $items['admin/config/search/metatags/settings'] = array(
+    'title' => 'Advanced settings',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_admin_settings_form'),
+    'access arguments' => array('administer meta tags'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 30,
+    'file' => 'metatag.admin.inc',
+  );
+  $items['admin/config/search/metatags/bulk-revert'] = array(
+    'title' => 'Bulk revert',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_bulk_revert_form'),
+    'access arguments' => array('administer meta tags'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 40,
+    'file' => 'metatag.admin.inc',
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_flush_caches().
+ */
+function metatag_flush_caches() {
+  return array('cache_metatag');
+}
+
+/**
+ * Load a metatag configuration record with all the defaults merged in.
+ *
+ * For example, given the configuration instance 'node:article', this function
+ * will load the configuration records for 'node:article', then 'node', and
+ * then finally 'global', with each attempt using an array merge.
+ *
+ * The levels of defaults is arranged by splitting the $instance variable by
+ * the colon character, and always using a 'global' instance at the end.
+ */
+function metatag_config_load_with_defaults($instance, $include_global = TRUE) {
+  $defaults = &drupal_static(__FUNCTION__, array());
+
+  // Statically cache defaults since they can include multiple levels.
+  $cid = "config:{$instance}" . ($include_global ? ':withglobal' : ':withoutglobal');
+  if (!isset($defaults[$cid])) {
+    if ($cache = metatag_cache_get($cid)) {
+      $defaults[$cid] = $cache->data;
+    }
+    else {
+      $defaults[$cid] = array();
+      $instances = metatag_config_get_parent_instances($instance, $include_global);
+      $configs = metatag_config_load_multiple($instances);
+      foreach ($instances as $key) {
+        // Ignore disabled configurations.
+        if (!isset($configs[$key]) || !empty($configs[$key]->disabled)) {
+          continue;
+        }
+
+        // Add config to the defaults array.
+        if (!empty($configs[$key]->config)) {
+          $defaults[$cid] += $configs[$key]->config;
+        }
+      }
+
+      metatag_cache_set($cid, $defaults[$cid]);
+    }
+  }
+
+  return $defaults[$cid];
+}
+
+/**
+ * Load a metatag configuration record.
+ */
+function metatag_config_load($instance) {
+  $results = metatag_config_load_multiple(array($instance));
+  return !empty($results[$instance]) ? $results[$instance] : FALSE;
+}
+
+/**
+ * Load multiple metatag configuration records.
+ */
+function metatag_config_load_multiple(array $instances) {
+  // Load the data.
+  ctools_include('export');
+  $configs = ctools_export_load_object('metatag_config', 'names', $instances);
+
+  // "Fix" any records that might be using old values. Ideally these will be
+  // permanently fixed by being re-saved or re-exported.
+  foreach (metatag_config_get_replacements() as $old_tag => $new_tag) {
+    foreach ($configs as $config_name => $config) {
+      if (isset($config->config[$old_tag])) {
+        $config->config[$new_tag] = $config->config[$old_tag];
+        unset($config->config[$old_tag]);
+      }
+    }
+  }
+
+  return $configs;
+}
+
+/**
+ * Identify the meta tags that have been deprecated and replaced by others.
+ */
+function metatag_config_get_replacements() {
+  $replacements = &drupal_static(__FUNCTION__);
+
+  if (!isset($replacements)) {
+    $replacements = array();
+
+    foreach (metatag_get_info('tags') as $tag_name => $tag_info) {
+      if (!empty($tag_info['replaces'])) {
+        if (!is_array($tag_info['replaces'])) {
+          $tag_info['replaces'] = array($tag_info['replaces']);
+        }
+        foreach ($tag_info['replaces'] as $replaces) {
+          $replacements[$replaces] = $tag_name;
+        }
+      }
+    }
+  }
+
+  return $replacements;
+}
+
+/**
+ * Save a metatag configuration record to the database.
+ */
+function metatag_config_save($config) {
+  $config->is_new = empty($config->cid);
+
+  // Allow modules to alter the configuration before it is saved using
+  // hook_metatag_config_presave().
+  module_invoke_all('metatag_config_presave', $config);
+
+  // Update the i18n string
+  if (function_exists('i18n_string_update')) {
+    $instance = $config->instance;
+
+    foreach ($config->config as $field => $item) {
+      $name = "metatag:" . $instance . ":" . $field;
+      i18n_string_update($name, $item['value']);
+    }
+  }
+
+  if ($config->is_new) {
+    drupal_write_record('metatag_config', $config);
+
+    // Allow modules to act upon the record insertion using
+    // hook_metatag_config_insert().
+    module_invoke_all('metatag_config_insert', $config);
+  }
+  else {
+    drupal_write_record('metatag_config', $config, array('cid'));
+
+    // Allow modules to act upon the record update using
+    // hook_metatag_config_insert().
+    module_invoke_all('metatag_config_update', $config);
+  }
+
+  unset($config->is_new);
+
+  // Clear any caches.
+  metatag_config_cache_clear();
+}
+
+/**
+ * Delete a metatag configuration record.
+ */
+function metatag_config_delete($instance) {
+  db_delete('metatag_config')
+    ->condition('instance', $instance)
+    ->execute();
+
+  // Clear any caches.
+  metatag_config_cache_clear();
+}
+
+/**
+ * Clear the metatag configuration cache.
+ */
+function metatag_config_cache_clear() {
+  cache_clear_all('*', 'cache_metatag', TRUE);
+  drupal_static_reset('metatag_config_load_with_defaults');
+  drupal_static_reset('metatag_entity_has_metatags');
+  drupal_static_reset('metatag_entity_supports_metatags');
+  ctools_include('export');
+  ctools_export_load_object_reset('metatag_config');
+}
+
+/**
+ * Load an entity's tags.
+ *
+ * @param $entity_type
+ *   The entity type to load.
+ * @param $entity_id
+ *   The ID of the entity to load.
+ *
+ * @return
+ *   An array of tag data keyed by revision ID and language.
+ */
+function metatag_metatags_load($entity_type, $entity_id) {
+  $metatags = metatag_metatags_load_multiple($entity_type, array($entity_id));
+  return !empty($metatags) ? reset($metatags) : array();
+}
+
+/**
+ * Load tags for multiple entities.
+ *
+ * @param $entity_type
+ *   The entity type to load.
+ * @param $entity_ids
+ *   The list of entity IDs.
+ *
+ * @return
+ *   An array of tag data, keyed by entity ID, revision ID and language.
+ */
+function metatag_metatags_load_multiple($entity_type, array $entity_ids, array $revision_ids = array()) {
+  // Double check entity IDs are numeric thanks to Entity API module.
+  $entity_ids = array_filter($entity_ids, 'is_numeric');
+  if (empty($entity_ids)) {
+    return array();
+  }
+
+  // Also need to check if the metatag table exists since this condition could
+  // fire before the table has been installed yet.
+  if (!variable_get('metatag_schema_installed', FALSE)) {
+    if (db_table_exists('metatag')) {
+      variable_set('metatag_schema_installed', TRUE);
+    }
+    else {
+      watchdog('metatag', 'The system tried to load metatag data before the schema was fully loaded.', array(), WATCHDOG_WARNING);
+      return array();
+    }
+  }
+
+  // Verify that the metatag.revision_id field has been added to the {metatag}
+  // table schema.
+  if (!variable_get('metatag_has_revision_id', FALSE)) {
+    if (db_field_exists('metatag', 'revision_id')) {
+      variable_set('metatag_has_revision_id', TRUE);
+    }
+    else {
+      watchdog('metatag', 'The database updates need to be ran.', array(), WATCHDOG_WARNING);
+      return 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');
+  // 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');
+  }
+  else {
+    $query->condition('m.entity_id', $entity_ids, 'IN');
+  }
+  $result = $query->execute();
+
+  // Marshal it into an array keyed by entity ID. Each value is an array of
+  // translations keyed by language code.
+  $metatags = array();
+  while ($record = $result->fetchObject()) {
+    $data = unserialize($record->data);
+
+    // "Fix" any records that might be using old values. Ideally these will be
+    // permanently fixed by being re-saved or re-exported.
+    foreach (metatag_config_get_replacements() as $old_tag => $new_tag) {
+      if (isset($data[$old_tag])) {
+        $data[$new_tag] = $data[$old_tag];
+        unset($data[$old_tag]);
+      }
+    }
+
+    $metatags[$record->entity_id][$record->revision_id][$record->language] = $data;
+  }
+
+  return $metatags;
+}
+
+/**
+ * Save an entity's tags.
+ *
+ * @param $entity_type
+ *   The entity type to load
+ * @param $entity_id
+ *   The entity's ID
+ * @param $revision_id
+ *   The entity's VID.
+ * @param $metatags
+ *   All of the tag information
+ * @param $language
+ *   The language of the translation set
+ */
+function metatag_metatags_save($entity_type, $entity_id, $revision_id, $metatags, $langcode, $old_vid = NULL) {
+  // If no language assigned, or the language doesn't exist, use the
+  // has-no-language language.
+  $languages = language_list();
+  if (empty($langcode) || !isset($languages[$langcode])) {
+    $langcode = LANGUAGE_NONE;
+  }
+
+  // Check that $entity_id is numeric because of Entity API and string IDs.
+  if (!is_numeric($entity_id)) {
+    return;
+  }
+
+  // The revision_id must be a numeric value; some entities use NULL for the
+  // revision so change that to a zero.
+  if (is_null($revision_id)) {
+    $revision_id = 0;
+  }
+
+  // Handle scenarios where the metatags are completely empty.
+  if (empty($metatags)) {
+    $metatags = array();
+    // Add an empty array record for each language.
+    $languages = db_query("SELECT language, ''
+        FROM {metatag}
+        WHERE (entity_type = :type)
+        AND (entity_id = :id)
+        AND (revision_id = :revision)",
+      array(
+        ':type'     => $entity_type,
+        ':id'       => $entity_id,
+        ':revision' => $revision_id,
+      ))->fetchAllKeyed();
+    foreach ($languages as $oldlang => $empty) {
+      $metatags[$oldlang] = array();
+    }
+  }
+
+  // Update each of the per-language metatag configurations in turn.
+  foreach ($metatags as $langcode => $new_metatags) {
+    // Allow other modules to alter the meta tags prior to saving using
+    // hook_metatag_presave().
+    foreach (module_implements('metatag_presave') as $module) {
+      $function = "{$module}_metatag_presave";
+      $function($new_metatags, $entity_type, $entity_id, $revision_id, $langcode);
+    }
+
+    // If the data array is empty, there is no data to actually save, so just
+    // delete the record from the database.
+    if (empty($new_metatags)) {
+      db_delete('metatag')
+        ->condition('entity_type', $entity_type)
+        ->condition('entity_id', $entity_id)
+        ->condition('revision_id', $revision_id)
+        ->condition('language', $langcode)
+        ->execute();
+    }
+    // Otherwise save the data for this entity.
+    else {
+      db_merge('metatag')
+        ->key(array(
+          'entity_type' => $entity_type,
+          'entity_id' => $entity_id,
+          'language' => $langcode,
+          'revision_id' => $revision_id,
+        ))
+        ->fields(array(
+          'data' => serialize($new_metatags),
+        ))
+        ->execute();
+    }
+  }
+
+  // Clear cached data.
+  metatag_metatags_cache_clear($entity_type, $entity_id);
+}
+
+/**
+ * Delete an entity's tags.
+ *
+ * @param $entity_type
+ *   The entity type
+ * @param $entity_id
+ *   The entity's ID
+ * @param $revision_id
+ *   The entity's VID.
+ * @param $langcode
+ *   The language ID of the entry to delete. If left blank, all language
+ *   entries for this entity will be deleted.
+ */
+function metatag_metatags_delete($entity_type, $entity_id, $revision_id = NULL, $langcode = NULL) {
+  $revision_ids = array();
+  if (!empty($revision_id)) {
+    $revision_ids[] = $revision_id;
+  }
+  return metatag_metatags_delete_multiple($entity_type, array($entity_id), $revision_ids, $langcode);
+}
+
+/**
+ * Delete multiple entities' tags.
+ *
+ * @param $entity_type
+ *   The entity type
+ * @param $entity_ids
+ *   The list of IDs
+ * @param $revision_id
+ *   An optional list of VIDs, if omitted all revisions will be deleted.
+ * @param $langcode
+ *   The language ID of the entities to delete. If left blank, all language
+ *   entries for the enities will be deleted.
+ */
+function metatag_metatags_delete_multiple($entity_type, array $entity_ids, array $revision_ids = array(), $langcode = NULL) {
+  // Double check entity IDs are numeric thanks to Entity API module.
+  $entity_ids = array_filter($entity_ids, 'is_numeric');
+
+  if ($metatags = metatag_metatags_load_multiple($entity_type, $entity_ids, $revision_ids)) {
+    $transaction = db_transaction();
+    try {
+      // Let other modules know about the records being deleted using
+      // hook_metatag_metatags_delete().
+      module_invoke_all('metatag_metatags_delete', $entity_type, $entity_ids, $revision_ids, $langcode);
+
+      // Set the entity to delete.
+      $query = db_delete('metatag')
+        ->condition('entity_type', $entity_type)
+        ->condition('entity_id', $entity_ids, 'IN');
+
+      // Optionally delete a specific revision.
+      if (!empty($revision_ids)) {
+        $query->condition('revision_id', $revision_ids, 'IN');
+      }
+
+      // Specify a language if there is one.
+      if (!empty($langcode)) {
+        $query->condition('language', $langcode);
+      }
+
+      // Perform the deletion(s).
+      $query->execute();
+
+      // Clear cached data.
+      metatag_metatags_cache_clear($entity_type, $entity_ids);
+    }
+    catch (Exception $e) {
+      $transaction->rollback();
+      watchdog_exception('metatag', $e);
+      throw $e;
+    }
+  }
+}
+
+/**
+ * Clear the cached records for a given entity type or entity ID.
+ *
+ * @param string $entity_type
+ *   The entity type to clear.
+ */
+function metatag_metatags_cache_clear($entity_type, $entity_ids = NULL) {
+  if (empty($entity_ids)) {
+    cache_clear_all("output:$entity_type", 'cache_metatag', TRUE);
+  }
+  else {
+    if (!is_array($entity_ids)) {
+      $entity_ids = array($entity_ids);
+    }
+    foreach ($entity_ids as $entity_id) {
+      cache_clear_all("output:$entity_type:$entity_id", 'cache_metatag', TRUE);
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_load().
+ */
+function metatag_entity_load($entities, $entity_type) {
+  // Wrap this in a try-catch block to work around occasions when the schema
+  // hasn't been updated yet.
+  try {
+    if (metatag_entity_supports_metatags($entity_type)) {
+      // Get the revision_ids.
+      $revision_ids = array();
+      foreach ($entities as $key => $entity) {
+        list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity);
+        $revision_id = intval($revision_id);
+        if (!empty($revision_id)) {
+          $revision_ids[] = $revision_id;
+        }
+      }
+
+      // Load all meta tags for these entities.
+      $metatags = metatag_metatags_load_multiple($entity_type, array_keys($entities), $revision_ids);
+
+      // Assign the metatag records for the correct revision ID.
+      foreach ($entities as $entity_id => $entity) {
+        list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity);
+        $revision_id = intval($revision_id);
+        $entities[$entity_id]->metatags = isset($metatags[$entity_id][$revision_id]) ? $metatags[$entity_id][$revision_id] : array();
+      }
+    }
+  }
+  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);
+    // Don't display the same message twice for Drush.
+    if (drupal_is_cli()) {
+      drupal_set_message(t('Run your updates, like drush updb.'));
+    }
+    // Only message people who can see it in watchdog and can likely fix it.
+    elseif (user_access('access site reports')) {
+      drupal_set_message(t('Error loading meta tag data, do the <a href="@update">database updates</a> need to be run?', array('@update' => base_path() . 'update.php')), 'error');
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function metatag_entity_insert($entity, $entity_type) {
+  if (isset($entity->metatags)) {
+    list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity);
+    $revision_id = intval($revision_id);
+
+    // Determine the entity's language.
+    $langcode = entity_language($entity_type, $entity);
+
+    // Unfortunately due to how core works, the normal entity_language()
+    // function returns 'und' instead of the node's language during node
+    // creation.
+    if ((empty($langcode) || $langcode == LANGUAGE_NONE) && !empty($entity->language)) {
+      $langcode = $entity->language;
+    }
+
+    // If no language was still found, use the 'no language' value.
+    if (empty($langcode)) {
+      $langcode = LANGUAGE_NONE;
+    }
+
+    // Work-around for initial entity creation where a language was selection
+    // but where it's different to the form's value.
+    if (!isset($entity->metatags[$langcode]) && isset($entity->metatags[LANGUAGE_NONE])) {
+      $entity->metatags[$langcode] = $entity->metatags[LANGUAGE_NONE];
+      unset($entity->metatags[LANGUAGE_NONE]);
+    }
+
+    // Support for Workbench Moderation v1.
+    if ($entity_type == 'node' && _metatag_isdefaultrevision($entity)) {
+      return;
+    }
+
+    metatag_metatags_save($entity_type, $entity_id, $revision_id, $entity->metatags, $langcode);
+  }
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function metatag_entity_update($entity, $entity_type) {
+  if (!metatag_entity_supports_metatags($entity_type)) {
+    return;
+  }
+
+  list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity);
+  $revision_id = intval($revision_id);
+
+  if (isset($entity->metatags)) {
+    // Determine the entity's new language. This will always be accurate as the
+    // language value will already have been updated by the time this function
+    // executes, and it will be loaded for the correct edit process.
+    $new_language = metatag_entity_get_language($entity_type, $entity);
+
+    // If applicable, determine the entity's original language. This cannot be
+    // obtained via the normal API as that data will already have been updated,
+    // instead check to see if the entity has an old-fasioned 'language' value.
+    if (isset($entity->original) && isset($entity->language) && isset($entity->original->language)) {
+      $old_language = $entity->original->language;
+
+      // If the language has changed then additional checking needs to be done.
+      // Need to compare against the entity's raw language value as they will
+      // be different when updating a translated entity, versus an untranslated
+      // entity or a source entity for translation, and give a false positive.
+      if ($new_language == $entity->language && $new_language != $old_language) {
+        // If this entity is not translated, or if it is translated but the
+        // translation was previously created, then some language cleanup needs
+        // to be done.
+        if (!isset($entity->translation) || (isset($entity->translation) && !empty($entity->translation['created']))) {
+          // Delete the old language record. This will not affect old revisions.
+          db_delete('metatag')
+            ->condition('entity_type', $entity_type)
+            ->condition('entity_id', $entity_id)
+            ->condition('revision_id', $revision_id)
+            ->condition('language', $old_language)
+            ->execute();
+
+          // Swap out the metatag values for the two languages.
+          if (isset($entity->metatags[$old_language])) {
+            $entity->metatags[$new_language] = $entity->metatags[$old_language];
+            unset($entity->metatags[$old_language]);
+          }
+        }
+      }
+    }
+
+    // Support for Workbench Moderation v1.
+    if ($entity_type == 'node' && _metatag_isdefaultrevision($entity)) {
+      return;
+    }
+
+    // Save the record.
+    $old_vid = isset($entity->old_vid) ? $entity->old_vid : NULL;
+    metatag_metatags_save($entity_type, $entity_id, $revision_id, $entity->metatags, $new_language, $old_vid);
+  }
+  else {
+    // Still ensure the meta tag output is cached.
+    metatag_metatags_cache_clear($entity_type, $entity_id);
+  }
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function metatag_entity_delete($entity, $entity_type) {
+  list($entity_id) = entity_extract_ids($entity_type, $entity);
+  metatag_metatags_delete($entity_type, $entity_id);
+}
+
+/**
+ * Implements hook_field_attach_delete_revision().
+ */
+function metatag_field_attach_delete_revision($entity_type, $entity) {
+  list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity);
+  $revision_id = intval($revision_id);
+  metatag_metatags_delete($entity_type, $entity_id, $revision_id);
+}
+
+/**
+ * Implements hook_entity_view().
+ *
+ * Provides additional argument to allow the display to be forced, to work
+ * around problems elsewhere in the APIs.
+ */
+function metatag_entity_view($entity, $entity_type, $view_mode, $langcode, $force = FALSE) {
+  // Only run this function once per page load.
+  static $i_will_say_this_only_once = FALSE;
+
+  // Only proceed if this entity object is the page being viewed.
+  if (_metatag_entity_is_page($entity_type, $entity)) {
+    // Some API calls need to force the data loading.
+    if (!$force) {
+      // Only run this function once per page load.
+      if ($i_will_say_this_only_once) {
+        return;
+      }
+      $i_will_say_this_only_once = TRUE;
+    }
+
+    // CTools uses 'page_manager' view mode to indicate the full entity display
+    // page rather than 'full', so streamline the internal processes.
+    if ($view_mode == 'page_manager') {
+      $view_mode = 'full';
+    }
+
+    // Generate metatags output.
+    if ($output = metatag_generate_entity_metatags($entity, $entity_type, $langcode, $view_mode)) {
+      list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
+      $instance = "{$entity_type}:{$bundle}";
+      // We need to register the term's metatags, so we can later fetch them.
+      // @see metatag_page_build().
+      metatag_page_set_metatags($instance, $output);
+    }
+  }
+}
+
+/**
+ * Generate the metatags for a given entity.
+ *
+ * @param object $entity
+ *   The entity object to generate the metatags for.
+ * @param string $entity_type
+ *   The entity type of the entity.
+ * @param string $langcode
+ *   The language code used for rendering the entity.
+ * @param string $view_mode
+ *   The view mode the entity is rendered in.
+ * @param bool $cached
+ *   TRUE if metatags can be loaded from and saved to the cache. FALSE if the
+ *   cache should be bypassed.
+ *
+ * @return array
+ *   A renderable array of metatags for the given entity.
+ */
+function metatag_generate_entity_metatags($entity, $entity_type, $langcode = NULL, $view_mode = 'full', $cached = TRUE) {
+  // If this entity object isn't allowed meta tags, don't continue.
+  if (!metatag_entity_has_metatags($entity_type, $entity)) {
+    return array();
+  }
+
+  // Obtain some details of the entity that are needed elsewhere.
+  list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
+  $revision_id = intval($revision_id);
+
+  // Check if a specific metatag config exists, otherwise just use the global
+  // one, stripping out the bundle.
+  $instance = "{$entity_type}:{$bundle}";
+  if (!metatag_config_load_with_defaults($instance, FALSE)) {
+    $instance = "{$entity_type}";
+  }
+
+  // Determine the language this entity actually uses.
+  $entity_language = metatag_entity_get_language($entity_type, $entity);
+
+  // If no language was requested, try the language defined for this page
+  // request.
+  if (empty($langcode)) {
+    $langcode = $GLOBALS['language_content']->language;
+  }
+
+  // This entity doesn't have any languages defined, i.e. it uses 'und'. This
+  // can't conflict with loading the wrong language as entities either have no
+  // language or they have specific one(s), they can't have both.
+  if ($entity_language == LANGUAGE_NONE) {
+    $langcode = LANGUAGE_NONE;
+  }
+
+  // If there are no meta tags for the currently identified language, and there
+  // *are* meta tags defined for the entity's default language, use the entity's
+  // default language's values, unless the "Don't load entity's default
+  // language values if no languages match" option is enabled on the advanced
+  // settings page.
+  elseif (empty($entity->metatags[$langcode]) && !empty($entity->metatags[$entity_language]) && !variable_get('metatag_entity_no_lang_default', FALSE)) {
+    $langcode = $entity_language;
+  }
+
+  // Other scenarios.
+  else {
+    // There's no need to do anything else - either there are meta tag values
+    // created for the requested language or there aren't.
+  }
+
+  $cid = FALSE;
+  if ($cached) {
+    // All applicable pieces for this current page.
+    $cid_parts = array(
+      'entity_type' => $entity_type,
+      'bundle' => $bundle,
+      'entity_id' => $entity_id,
+      'revision_id' => $revision_id,
+      // Cache separately based on the language of the passed-in entity and the
+      // overall active language of the page.
+      'langcode' => $langcode,
+      'language_content' => $GLOBALS['language_content']->language,
+      'view_mode' => $view_mode,
+    );
+    $cid = metatag_cache_default_cid_parts($cid_parts);
+  }
+
+  if ($cid && $cache = metatag_cache_get($cid)) {
+    $output = $cache->data;
+  }
+  else {
+    // Separate the meta tags.
+    $metatags = isset($entity->metatags) ? $entity->metatags : array();
+
+    // Build options for meta tag rendering.
+    $options = array(
+      'entity' => $entity,
+      'entity_type' => $entity_type,
+      'view_mode' => $view_mode,
+    );
+    // Ensure we actually pass a language object rather than language code.
+    $languages = language_list();
+    if (isset($languages[$langcode])) {
+      $options['language'] = $languages[$langcode];
+    }
+
+    // Reload the entity object from cache as it may have been altered.
+    if (!empty($entity_id)) {
+      $token_type = token_get_entity_mapping('entity', $entity_type);
+      $entities = entity_load($entity_type, array($entity_id));
+      $options['token data'][$token_type] = $entities[$entity_id];
+      $options['entity'] = $entities[$entity_id];
+    }
+
+    // Render the metatags and save to the cache.
+    $output = metatag_metatags_view($instance, $metatags, $options);
+    if ($cid) {
+      metatag_cache_set($cid, $output);
+    }
+  }
+
+  return $output;
+}
+
+/**
+ * Generate the metatags for a given entity.
+ *
+ * @param object $entity_id
+ *   The entity id of the entity to generate the metatags for.
+ * @param string $entity_type
+ *   The entity type of the entity to generate the metatags for.
+ * @param string $langcode
+ *   The language code used for rendering the entity.
+ *
+ * @return array
+ *   A renderable array of metatags for the given entity.
+ */
+function metatags_get_entity_metatags($entity_id, $entity_type, $langcode = NULL) {
+  $entities = entity_load($entity_type, array($entity_id));
+  $entity = reset($entities);
+  return !empty($entity) ? metatag_generate_entity_metatags($entity, $entity_type, $langcode) : array();
+}
+
+/**
+ * Build a renderable array of meta tag output.
+ *
+ * @param string $instance
+ *   The configuration instance key of the meta tags to use, e.g.
+ *   "node:article".
+ * @param array $metatags
+ *   An array of meta tag data.
+ * @param array $options
+ *   (optional) An array of options including the following keys and values:
+ *   - language: A language object.
+ *   - token data: An array of data to pass into token_replace() during
+ *                 meta tag value generation.
+ */
+function metatag_metatags_view($instance, array $metatags = array(), array $options = array()) {
+  $output = array();
+
+  // Convert language codes to a language object.
+  if (isset($options['language']) && is_string($options['language'])) {
+    $languages = language_list();
+    $options['language'] = isset($languages[$options['language']]) ? $languages[$options['language']] : NULL;
+  }
+
+  // If there are any tags, determine the translation to display.
+  if (!empty($metatags)) {
+    // Get the display language; default to the entity's language.
+    if (isset($options['language']) && isset($options['language']->language) && isset($metatags[$options['language']->language])) {
+      $metatags = $metatags[$options['language']->language];
+    }
+    // If no language requested, use the no-language value.
+    elseif (!empty($metatags[LANGUAGE_NONE])) {
+      $metatags = $metatags[LANGUAGE_NONE];
+    }
+    else {
+      $metatags = array();
+    }
+  }
+
+  // Add any default tags to the mix.
+  $metatags += metatag_config_load_with_defaults($instance);
+
+  $options['instance'] = $instance;
+
+  foreach ($metatags as $metatag => $data) {
+    if ((!empty($data['value']) || (isset($data['value']) && is_numeric($data['value'])))
+      && $metatag_instance = metatag_get_instance($metatag, $data)) {
+      $output[$metatag] = $metatag_instance->getElement($options);
+    }
+  }
+
+  // Allow the output meta tags to be modified using
+  // hook_metatag_metatags_view_alter().
+  drupal_alter('metatag_metatags_view', $output, $instance, $options);
+
+  return $output;
+}
+
+function metatag_metatags_values($instance, array $metatags = array(), array $options = array()) {
+  $values = array();
+
+  // Apply defaults to the data for each language.
+  foreach ($metatags as $language => $metatag) {
+    $metatags[$language] += metatag_config_load_with_defaults($instance);
+  }
+
+  // Generate output only if we have a valid language.
+  if (isset($options['language']) && is_string($options['language']) && isset($metatags[$options['language']])) {
+    $language = $options['language'];
+
+    // Convert language codes to a language object.
+    $languages = language_list();
+    $options['language'] = isset($languages[$language]) ? $languages[$language] : NULL;
+    $options['instance'] = $instance;
+    // Get output elements.
+    foreach ($metatags[$language] as $metatag => $data) {
+      if ($metatag_instance = metatag_get_instance($metatag, $data)) {
+        $values[$metatag] = $metatag_instance->getValue($options);
+      }
+    }
+  }
+
+  return array_filter($values, 'drupal_strlen');
+}
+
+/**
+ * Build a FAPI array for editing meta tags.
+ *
+ * @param array $form
+ *   The current FAPI array.
+ * @param string $instance
+ *   The configuration instance key of the metatags to use, e.g. "node:article".
+ * @param array $metatags
+ *   An array of metatag data.
+ * @param array $options
+ *   (optional) An array of options including the following keys and values:
+ *   - token types: An array of token types to be passed to theme_token_tree().
+ */
+function metatag_metatags_form(array &$form, $instance, array $metatags = array(), array $options = array()) {
+  $info = metatag_get_info();
+  if (empty($info['tags'])) {
+    return;
+  }
+
+  // Work out the language code to use, default to NONE.
+  $langcode = LANGUAGE_NONE;
+  if (!empty($form['#entity_type'])) {
+    if (!empty($form['#entity'])) {
+      $langcode = metatag_entity_get_language($form['#entity_type'], $form['#entity']);
+    }
+    else {
+      $entity_info = entity_get_info($form['#entity_type']);
+      if (!empty($entity_info['token type'])) {
+        $entity_key = '#' . $entity_info['token type'];
+        if (!empty($form[$entity_key])) {
+          $langcode = metatag_entity_get_language($form['#entity_type'], $form[$entity_key]);
+        }
+      }
+    }
+  }
+
+  // Merge in the default options.
+  $options += array(
+    'token types' => array(),
+    'defaults' => metatag_config_load_with_defaults($instance),
+    'instance' => $instance,
+  );
+
+  $form['metatags'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Meta tags'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#multilingual' => TRUE,
+    '#tree' => TRUE,
+    '#access' => user_access('edit meta tags') || user_access('administer meta tags'),
+    '#weight' => 40,
+    '#language' => $langcode,
+    '#attributes' => array(
+      'class' => array('metatags-form'),
+    ),
+  );
+  $form['metatags'][$langcode] = array(
+    '#metatag_defaults' => $options['defaults'],
+    '#type' => 'container',
+    '#multilingual' => TRUE,
+    '#tree' => TRUE,
+  );
+  // Show a different intro message for entity pages vs config pages.
+  if (isset($form['#entity'])) {
+    $form['metatags'][$langcode]['intro_text'] = array(
+      '#markup' => '<p>' . t('Configure the meta tags below. Tokens, e.g. "[node:summary]", automatically insert the corresponding information from that field or value, which helps to avoid redundant meta data and possible search engine penalization; see the "Browse available tokens" popup for more details.') . '</p>',
+      '#weight' => -10,
+    );
+  }
+  else {
+    $form['metatags'][$langcode]['intro_text'] = array(
+      '#markup' => '<p>' . t('Configure the meta tags below. Use tokens (see the "Browse available tokens" popup) to avoid redundant meta data and search engine penalization. For example, a \'keyword\' value of "example" will be shown on all content using this configuration, whereas using the [node:field_keywords] automatically inserts the "keywords" values from the current entity (node, term, etc).') . '</p>',
+      '#weight' => -10,
+    );
+  }
+
+  // Only support vertical tabs if there is a vertical tab element.
+  foreach (element_children($form) as $key) {
+    if (isset($form[$key]['#type']) && $form[$key]['#type'] == 'vertical_tabs') {
+      $form['metatags']['#group'] = $key;
+      $form['metatags']['#attached']['js']['vertical-tabs'] = drupal_get_path('module', 'metatag') . '/metatag.vertical-tabs.js';
+      break;
+    }
+  }
+
+  // Merge in the default meta tag configurations.
+  $metatags += $options['defaults'];
+
+  // This will be used later.
+  $group_metatag_access = array();
+
+  // Build the form for each metatag.
+  foreach ($info['tags'] as $metatag => $metatag_info) {
+    // @todo Replace context matching with hook_metatag_access().
+    if (isset($options['context']) && isset($metatag_info['context'])) {
+      if (!in_array($options['context'], $metatag_info['context'])) {
+        continue;
+      }
+    }
+
+    $metatag_instance = metatag_get_instance($metatag, isset($metatags[$metatag]) ? $metatags[$metatag] : array());
+    if (empty($metatag_instance)) {
+      continue;
+    }
+
+    // Get the form element from the meta tag class.
+    $metatag_form = $metatag_instance->getForm($options);
+
+    // Add a default value form element.
+    if (isset($options['defaults'][$metatag]['value'])) {
+      $metatag_form['default'] = array(
+        '#type' => 'hidden',
+        '#value' => $options['defaults'][$metatag]['value'],
+      );
+    }
+
+    // Optional extended edit permissions.
+    if (variable_get('metatag_extended_permissions', FALSE)) {
+      $metatag_form['#access'] = user_access('edit meta tag: ' . $metatag) || user_access('administer meta tags');
+    }
+    else {
+      $metatag_form['#access'] = $form['metatags']['#access'];
+    }
+
+    if (!empty($metatag_info['group'])) {
+      $group_key = $metatag_info['group'];
+      if (isset($info['groups'][$group_key]['label']) && !isset($form['metatags'][$langcode][$group_key])) {
+        $group = $info['groups'][$group_key] + array('form' => array(), 'description' => NULL);
+        $form['metatags'][$langcode][$group_key] = $group['form'] + array(
+          '#type' => 'fieldset',
+          '#title' => check_plain($group['label']),
+          '#description' => filter_xss($group['description']),
+          '#collapsible' => TRUE,
+          '#collapsed' => TRUE,
+        );
+      }
+      $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.
+      if (variable_get('metatag_extended_permissions', FALSE)) {
+        $form['metatags'][$langcode][$group_key]['#access'] = count(element_get_visible_children($form['metatags'][$langcode][$group_key])) > 0;
+      }
+      else {
+        $form['metatags'][$langcode][$group_key]['#access'] = $form['metatags']['#access'];
+      }
+      // Structure the access parameter into this array, and make use of it
+      // later when we move on. Besides, this foreach is getting heavy.
+      $group_metatag_access[$group_key] = $form['metatags'][$langcode][$group_key]['#access'];
+    }
+    else {
+      $form['metatags'][$langcode][$metatag] = $metatag_form;
+    }
+  }
+
+  // Hide the fieldset itself if there is not at least one of the meta tag
+  // fields visible; only bother checking this if the user had edit access in
+  // the first place.
+  if ($form['metatags']['#access'] && variable_get('metatag_extended_permissions', FALSE)) {
+    $form['metatags']['#access'] = count(element_get_visible_children($form['metatags'][$langcode])) > 0;
+  }
+
+  // Check the #access of each group. If it passed, we display options for
+  // tokens. By this we update the #description of each group.
+  if ($form['metatags']['#access']) {
+    // Built the token list.
+    $token_listing_link = theme('token_tree', array('token_types' => $options['token types'], 'dialog' => TRUE));
+
+    // Add the token list to the top of the fieldset.
+    $form['metatags'][$langcode]['#description'] = $token_listing_link;
+
+    // Check if each meta tag group is being displayed.
+    if (!empty($group_metatag_access)) {
+      foreach ($group_metatag_access as $group_key => $token_access) {
+        if ($token_access) {
+          // Update the description.
+          if (isset($form['metatags'][$langcode][$group_key]['#description'])) {
+            $form['metatags'][$langcode][$group_key]['#description'] .= '<br />';
+          }
+          else {
+            $form['metatags'][$langcode][$group_key]['#description'] = '';
+          }
+          $form['metatags'][$langcode][$group_key]['#description'] .= $token_listing_link;
+        }
+      }
+    }
+  }
+
+  // Add a submit handler to compare the submitted values against the deafult
+  // values.
+  $form += array('#submit' => array());
+  array_unshift($form['#submit'], 'metatag_metatags_form_submit');
+}
+
+/**
+ * Form API submission callback.
+ *
+ * Unset meta tag values that equal their default values, and load any
+ * additional meta tag values for other languages so that they can be properly
+ * saved later on.
+ *
+ * @see metatag_metatags_save()
+ */
+function metatag_metatags_form_submit($form, &$form_state) {
+  if (!empty($form_state['values']['metatags'])) {
+    // Unset meta tag values that equal their default values.
+    foreach ($form_state['values']['metatags'] as $langcode => $values) {
+      if (!empty($form['metatags'][$langcode]['#metatag_defaults'])) {
+        metatag_filter_values_from_defaults($form_state['values']['metatags'][$langcode], $form['metatags'][$langcode]['#metatag_defaults']);
+      }
+    }
+
+    // Need to load the entity's values for other languages, otherwise they will
+    // be incorrectly deleted later on.
+    if (isset($form['#entity']) && !empty($form['#entity']->metatags)) {
+      foreach ($form['#entity']->metatags as $langcode => $values) {
+        if (!isset($form_state['values']['metatags'][$langcode])) {
+          $form_state['values']['metatags'][$langcode] = $values;
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_extra_fields().
+ */
+function metatag_field_extra_fields() {
+  $extra = array();
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    foreach (array_keys($entity_info['bundles']) as $bundle) {
+      if (metatag_entity_supports_metatags($entity_type, $bundle)) {
+        $extra[$entity_type][$bundle]['form']['metatags'] = array(
+          'label' => t('Meta tags'),
+          'description' => t('Meta tag module form elements.'),
+          'weight' => 40,
+        );
+      }
+    }
+  }
+  return $extra;
+}
+
+/**
+ * Check if an individual entity has meta tags defined, or has defaults.
+ *
+ * @param string $entity_type
+ *   An entity type.
+ * @param object $entity
+ *   An entity object.
+ *
+ * @return boolean
+ *   TRUE or FALSE if the entity should have a form for or process meta tags.
+ */
+function metatag_entity_has_metatags($entity_type, $entity) {
+  // If an entity has custom meta tags assigned, then we should return TRUE.
+  if (!empty($entity->metatags)) {
+    return TRUE;
+  }
+
+  // Otherwise, check to see if there exists any enabed configuration for
+  // either the entity type, or bundle (even if the configuration is empty).
+  // If no configuration exists, then we should not be displaying the meta tag
+  // forms or processing meta tags on entity view.
+  $config_exists = &drupal_static(__FUNCTION__, array());
+  list( , , $bundle) = entity_extract_ids($entity_type, $entity);
+  // Do not pretend to have metatags when the bundle does not support them.
+  if (!metatag_entity_supports_metatags($entity_type, $bundle)) {
+    return FALSE;
+  }
+  $instance = "{$entity_type}:{$bundle}";
+  if (!isset($config_exists[$instance])) {
+    // Check if the intstance or its parents (excluding global) are enabled.
+    $config_exists[$instance] = metatag_config_is_enabled($instance, TRUE, FALSE);
+  }
+
+  return !empty($config_exists[$instance]);
+}
+
+/**
+ * Check whether the requested entity type (and bundle) support metatag.
+ *
+ * By default this will be FALSE, support has to be specifically enabled by
+ * assigning 'metatag' => TRUE within the hook_entity_info() definition for the
+ * entity.
+ */
+function metatag_entity_supports_metatags($entity_type = NULL, $bundle = NULL) {
+  $entity_types = &drupal_static(__FUNCTION__);
+
+  if (!isset($entity_types)) {
+    $entity_types = array();
+  }
+
+  // Everything else depends upon a specific entity type being checked.
+  if (isset($entity_type)) {
+    if (!isset($entity_types[$entity_type])) {
+      $entity_info = entity_get_info($entity_type);
+      if (empty($entity_info['metatags'])) {
+        $entity_types[$entity_type] = FALSE;
+      }
+      else {
+        $entity_types[$entity_type] = array();
+        foreach ($entity_info['bundles'] as $bundle_key => $bundle_info) {
+          $entity_types[$entity_type][$bundle_key] = !isset($bundle_info['metatags']) || !empty($bundle_info['metatags']);
+        }
+      }
+    }
+
+    // If a specific entity bundle is being compared, check it first.
+    if (isset($bundle)) {
+      // Allow entities to be disabled by assigning a variable
+      // 'metatag_enable_{$entity_type}__{$bundle}' the value FALSE, e.g.:
+      //
+      // // Disable metatags for carousel nodes.
+      // $conf['metatag_enable_node__carousel'] = FALSE;
+      //
+      // @see Advanced settings page.
+      if (variable_get('metatag_enable_' . $entity_type . '__' . $bundle, 'monkey') == FALSE) {
+        return FALSE;
+      }
+
+      return isset($entity_types[$entity_type][$bundle]) ? $entity_types[$entity_type][$bundle] : FALSE;
+    }
+
+    // Otherwise check the entity type itself.
+    else {
+      // Allow entities to be disabled by assigning a variable
+      // 'metatag_enable_{$entity_type}' the value FALSE, e.g.:
+      //
+      // // Disable metatags for file_entity.
+      // $conf['metatag_enable_file'] = FALSE;
+      //
+      // @see Advanced settings page.
+      if (variable_get('metatag_enable_' . $entity_type, 'monkey') == FALSE) {
+        return FALSE;
+      }
+
+      return isset($entity_types[$entity_type]) ? ($entity_types[$entity_type] !== FALSE) : FALSE;
+    }
+  }
+
+  return $entity_types;
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ *
+ * Enables Metatag support for the core entities.
+ */
+function metatag_entity_info_alter(&$info) {
+  $defaults['node'] = array(
+    'path' => 'node/%node',
+    'metatags' => TRUE,
+  );
+  $defaults['user'] = array(
+    'path' => 'user/%user',
+    'metatags' => TRUE,
+  );
+  $defaults['taxonomy_term'] = array(
+    'path' => 'taxonomy/term/%taxonomy_term',
+    'metatags' => TRUE,
+  );
+
+  // Just running taxonomy_vocabulary_load() here would cause problems for
+  // EntityCache in certain circumstances, so instead the query is executed
+  // directly instead.
+  if (module_exists('forum') && ($vocab_id = variable_get('forum_nav_vocabulary', 0))) {
+    $vocab_name = db_query("SELECT machine_name FROM {taxonomy_vocabulary} WHERE vid = :vid", array(':vid' => $vocab_id))->fetchField();
+    if (!empty($vocab_name)) {
+      $defaults['taxonomy_term']['bundles'][$vocab_name]['path'] = 'forum/%taxonomy_term';
+    }
+  }
+
+  foreach ($defaults as $key => $entity_defaults) {
+    if (isset($info[$key])) {
+      $info[$key] = drupal_array_merge_deep($entity_defaults, $info[$key]);
+    }
+  }
+}
+
+/**
+ * Given a path determine if it is an entity default path.
+ *
+ * @param $path
+ *   The internal path. The id of the entity should be in the string as '[id]'.
+ * @return
+ *   An array with the entity type and the loaded entity object.
+ */
+function metatag_load_entity_from_path($path) {
+  $entity_paths = &drupal_static(__FUNCTION__);
+  $result = FALSE;
+
+  if (!isset($entity_paths)) {
+    $entity_paths = array();
+    foreach (entity_get_info() as $entity_type => $entity_info) {
+      if (isset($entity_info['default path'])) {
+        $default_path = $entity_info['default path'];
+        $default_path = preg_quote($default_path, '/');
+        $default_path = str_replace('\[id\]', '(\d+)', $default_path);
+        $entity_paths[$entity_type] = $default_path;
+      }
+    }
+  }
+
+  foreach ($entity_paths as $entity_type => $default_path) {
+    if (preg_match("/^{$default_path}$/", $path, $matches)) {
+      if ($entity = entity_load($entity_type, array($matches[1]))) {
+        $result = array('entity_type' => $entity_type, 'entity' => reset($entity));
+      }
+      break;
+    }
+  }
+
+  // Allow other modules to customize the data using
+  // hook_metatag_load_entity_from_path_alter().
+  drupal_alter('metatag_load_entity_from_path', $path, $result);
+
+  return $result;
+}
+
+/**
+ * Add meta tags to be added later with metatag_page_build().
+ *
+ * @param string $instance
+ *   The configuration instance key of the meta tags, e.g. "node:article".
+ * @param array $metatags
+ *   An array of meta tags from metatag_metatags_view().
+ */
+function metatag_page_set_metatags($instance, $metatags) {
+  $page_metatags = &drupal_static(__FUNCTION__, array());
+  $page_metatags[$instance] = $metatags;
+}
+
+/**
+ * Retrieve the array of meta tags to be added with metatag_page_build().
+ */
+function metatag_page_get_metatags() {
+  // @todo Add alter to this result?
+  return drupal_static('metatag_page_set_metatags', array());
+}
+
+/**
+ * Implements hook_page_build().
+ */
+function metatag_page_build(&$page) {
+  // By default do not add meta tags to admin pages. To enable meta tags on
+  // admin pages set the 'metatag_tag_admin_pages' variable to TRUE.
+  if (path_is_admin(current_path()) && !variable_get('metatag_tag_admin_pages', FALSE)) {
+    return;
+  }
+
+  // The page region can be changed.
+  $region = variable_get('metatag_page_region', 'content');
+
+  // Ensure these arrays exist, otherwise several use cases will fail.
+  if (!isset($page[$region]) || !is_array($page[$region])) {
+    $page[$region] = array();
+  }
+  if (!isset($page[$region]['metatags']) || !is_array($page[$region]['metatags'])) {
+    $page[$region]['metatags'] = array();
+  }
+
+  // The front page has special consideration. Also, check if this is an error
+  // (403/404) page, those also require separate handling.
+  $instance = 'global:frontpage';
+  if ((drupal_is_front_page() && metatag_config_is_enabled($instance))
+    || ($instance = metatag_is_error_page())) {
+
+    // Generate the cache ID.
+    $cid_parts = array(
+      'instance' => $instance,
+    );
+    $cid = metatag_cache_default_cid_parts($cid_parts);
+
+    if ($cache = metatag_cache_get($cid)) {
+      $metatags = $cache->data;
+    }
+    else {
+      $metatags = metatag_metatags_view($instance, array());
+      metatag_cache_set($cid, $metatags);
+    }
+
+    $page[$region]['metatags'][$instance] = $metatags;
+  }
+
+  // Load any meta tags assigned via metatag_page_set_metatags(). Note: this
+  // must include the necessary defaults.
+  else {
+    $page[$region]['metatags'] += metatag_page_get_metatags();
+  }
+
+  // If no meta tags were loaded at least load the global defaults. This may be
+  // disabled, see README.txt for details.
+  if (empty($page[$region]['metatags']) && variable_get('metatag_load_all_pages', TRUE)) {
+    $instance = 'global';
+
+    // Generate the cache ID.
+    $cid_parts = array(
+      'instance' => $instance,
+      'path' => request_path(),
+    );
+    $cid = metatag_cache_default_cid_parts($cid_parts);
+
+    if ($cache = metatag_cache_get($cid)) {
+      $metatags = $cache->data;
+    }
+    else {
+      $metatags = metatag_metatags_view($instance, array());
+      metatag_cache_set($cid, $metatags);
+    }
+    $page[$region]['metatags'][$instance] = $metatags;
+  }
+}
+
+/**
+ * Returns whether the current page is the page of the passed in entity.
+ *
+ * @param $entity_type
+ *    The entity type; e.g. 'node' or 'user'.
+ * @param $entity
+ *    The entity object.
+ *
+ * @return
+ *   TRUE if the current page is the page of the specified entity, or FALSE
+ *   otherwise.
+ */
+function _metatag_entity_is_page($entity_type, $entity) {
+  // Exclude comment entities as this conflicts with comment_fragment.module.
+  if ($entity_type == 'comment') {
+    return;
+  }
+
+  $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.
+  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');
+  }
+
+  // Support for core node revisions.
+  elseif (!empty($uri['path']) && strpos($current_path, $uri['path']) === 0 && strpos($current_path, '/revisions/') && strpos($current_path, '/view')) {
+    return TRUE;
+  }
+
+  // Any other page.
+  else {
+    return !empty($uri['path']) && $current_path == $uri['path'];
+  }
+}
+
+/**
+ * Implements hook_field_attach_rename_bundle().
+ */
+function metatag_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
+  $instance_old = $entity_type . ':' . $bundle_old;
+  $instance_new = $entity_type . ':' . $bundle_new;
+  if ($config = metatag_config_load($instance_old)) {
+    $config->instance = $instance_new;
+    metatag_config_save($config);
+    metatag_config_delete($instance_old);
+  }
+}
+
+/**
+ * Implements hook_field_attach_delete_bundle().
+ */
+function metatag_field_attach_delete_bundle($entity_type, $bundle) {
+  $instance = $entity_type . ':' . $bundle;
+  metatag_config_delete($instance);
+}
+
+/**
+ * Implements hook_field_attach_form().
+ */
+function metatag_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
+  if (!metatag_entity_has_metatags($entity_type, $entity)) {
+    return;
+  }
+  // Entity_Translation will trigger this hook again, skip it.
+  if (!empty($form_state['entity_translation']['is_translation'])) {
+    return;
+  }
+
+  list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
+  $instance = "{$entity_type}:{$bundle}";
+
+  // Grab the meta tags for display in the form if there are any.
+  if (!empty($entity->metatags)) {
+    // Identify the language to use with this entity.
+    $entity_language = metatag_entity_get_language($entity_type, $entity);
+
+    // If this is a new translation using Entity Translation, load the meta
+    // tags from the entity's original language.
+    if (module_exists('entity_translation') && empty($form['#entity_translation_source_form']) && ($handler = entity_translation_entity_form_get_handler($form, $form_state)) && isset($entity->metatags[$handler->getSourceLanguage()])) {
+      $metatags = $entity->metatags[$handler->getSourceLanguage()];
+    }
+    // Determine from where we should get the tags.
+    elseif (isset($entity->metatags[$langcode])) {
+      // Set the tags to the translation set matching that of the form.
+      $metatags = $entity->metatags[$langcode];
+    }
+    // There is no translation for this entity's tags in the current
+    // language. Instead, display tags in the language of the entity, the
+    // source language of translations. The will provide translators with the
+    // original text to translate.
+    elseif (isset($entity->metatags[$entity_language])) {
+      $metatags = $entity->metatags[$entity_language];
+    }
+    // This is a preview so set the tags to the raw submission data.  No
+    // language has been set.
+    else {
+      $metatags = $entity->metatags;
+    }
+  }
+  else {
+    $metatags = array();
+  }
+
+  $options['token types'] = array(token_get_entity_mapping('entity', $entity_type));
+  $options['context'] = $entity_type;
+  // Allow hook_metatag_token_types_alter() to modify the defined tokens.
+  drupal_alter('metatag_token_types', $options);
+
+  // @todo Remove metatag_form_alter() when https://www.drupal.org/node/1284642 is fixed in core.
+  //metatag_metatags_form($form, $instance, $metatags, $options);
+
+  $form['#metatags'] = array(
+    'instance' => $instance,
+    'metatags' => $metatags,
+    'options' => $options,
+  );
+}
+
+/**
+ * Implements hook_form_alter().
+ *
+ * @todo Remove this when https://www.drupal.org/node/1284642 is fixed in core.
+ */
+function metatag_form_alter(&$form, $form_state, $form_id) {
+  if (!empty($form['#metatags']) && !isset($form['metatags'])) {
+    extract($form['#metatags']);
+    metatag_metatags_form($form, $instance, $metatags, $options);
+    unset($form['#metatags']);
+  }
+}
+
+/**
+ * Get the meta tag information array of a meta tag.
+ *
+ * @param $metatag
+ *   The meta tag name, e.g. description, for which the info shall be returned,
+ *   or NULL to return an array with info about all meta tags.
+ */
+function metatag_get_info($type = NULL, $name = NULL) {
+  // Use the advanced drupal_static() pattern, since this is called very often.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['metatag_info'] = &drupal_static(__FUNCTION__);
+  }
+  $info = &$drupal_static_fast['metatag_info'];
+
+  global $language;
+  if (!isset($info)) {
+    // hook_metatag_info() includes translated strings, so each language is cached
+    // separately.
+    $cid = 'info:' . $language->language;
+
+    if ($cache = metatag_cache_get($cid)) {
+      $info = $cache->data;
+    }
+    else {
+      // Obtain all metatag specs defined in other modules using
+      // hook_metatag_info().
+      $info = module_invoke_all('metatag_info');
+      $info += array('tags' => array(), 'groups' => array());
+
+      // Merge in default values.
+      foreach ($info['tags'] as $key => $data) {
+        $info['tags'][$key] += array(
+          // Merge in default values.
+          'name' => $key,
+          'class' => 'DrupalTextMetaTag',
+        );
+      }
+
+      // Let other modules alter the entity info using
+      // hook_metatag_info_alter().
+      drupal_alter('metatag_info', $info);
+
+      metatag_cache_set($cid, $info);
+    }
+  }
+
+  if (isset($type) && isset($name)) {
+    return isset($info[$type][$name]) ? $info[$type][$name] : FALSE;
+  }
+  elseif (isset($type)) {
+    return isset($info[$type]) ? $info[$type] : array();
+  }
+  else {
+    return $info;
+  }
+}
+
+function metatag_get_instance($metatag, array $data = array()) {
+  $info = metatag_get_info('tags', $metatag);
+  if (!empty($info['class']) && class_exists($info['class'])) {
+    $class = $info['class'];
+    return new $class($info, $data);
+  }
+}
+
+/**
+ * Return the string value of a meta tag.
+ *
+ * @param $metatag
+ *   The meta tag string.
+ * @param $data
+ *   The array of data for the meta tag class instance.
+ * @param $options
+ *   An optional array of additional options to pass to the getValue() method
+ *   of the meta tag class instance.
+ *   - raw: A boolean if TRUE will not perform token replacement.
+ *
+ * @return
+ *   A string value.
+ */
+function metatag_get_value($metatag, array $data, array $options = array()) {
+  $value = '';
+  if ($metatag_instance = metatag_get_instance($metatag, $data)) {
+    $options["instance"] = $metatag;
+    $value = $metatag_instance->getValue($options);
+  }
+  return $value;
+}
+
+/**
+ * Set a variable to be altered in metatag_preprocess_html().
+ *
+ * @see metatag_get_preprocess_variables()
+ * @see metatag_preprocess_html()
+ * @see metatag_preprocess_maintenance_page()
+ */
+function metatag_set_preprocess_variable($hook, $variable, $value) {
+  $variables = &drupal_static(__FUNCTION__, array());
+  $variables[$hook][$variable] = $value;
+}
+
+/**
+ * Return an array of variables to be altered in preprocess functions.
+ *
+ * @see metatag_set_preprocess_variable()
+ * @see metatag_preprocess_html()
+ * @see metatag_preprocess_maintenance_page()
+ */
+function metatag_get_preprocess_variables($hook) {
+  $variables = drupal_static('metatag_set_preprocess_variable', array());
+  return isset($variables[$hook]) ? $variables[$hook] : array();
+}
+
+/**
+ * Implements hook_preprocess_html().
+ */
+function metatag_preprocess_html(&$variables) {
+  foreach (metatag_get_preprocess_variables('html') as $variable => $value) {
+    $variables[$variable] = $value;
+  }
+}
+
+/**
+ * Implements hook_preprocess_maintenance_page().
+ */
+function metatag_preprocess_maintenance_page(&$variables) {
+  foreach (metatag_get_preprocess_variables('html') as $variable => $value) {
+    $variables[$variable] = $value;
+  }
+}
+
+/**
+ * Implements hook_html_head_alter().
+ */
+function metatag_html_head_alter(&$elements) {
+  // Remove duplicate link tags if found.
+  $metatags = metatag_get_info('tags');
+  foreach (array_keys($metatags) as $name) {
+    if (!isset($elements['metatag_' . $name]) || $elements['metatag_' . $name]['#tag'] != 'link') {
+      // Only check for link tags added by the metatags module.
+      continue;
+    }
+    foreach (array_keys($elements) as $key) {
+      if (strpos($key, 'drupal_add_html_head_link:' . $name . ':') === 0) {
+        unset($elements[$key]);
+      }
+    }
+  }
+
+  // Remove the default generator meta tag.
+  unset($elements['system_meta_generator']);
+}
+
+function metatag_metatag_get_form($metatag, array $data = array(), array $options = array()) {
+  $instance = metatag_get_instance($metatag, $data);
+  return $instance->getForm($options);
+}
+
+function metatag_config_instance_info($instance = NULL) {
+  global $language;
+
+  $info = &drupal_static(__FUNCTION__);
+
+  // hook_metatag_info() includes translated strings, so each language is cached
+  // separately.
+  $cid = 'metatag:config:instance:info:' . $language->language;
+
+  if (!isset($info)) {
+    if ($cache = metatag_cache_get($cid)) {
+      $info = $cache->data;
+    }
+    else {
+      // Allow modules to act upon the record insertion using
+      // hook_metatag_config_instance_info().
+      $info = module_invoke_all('metatag_config_instance_info');
+
+      // Allow other modules to customize the data using
+      // hook_metatag_config_instance_info_alter().
+      drupal_alter('metatag_config_instance_info', $info);
+
+      metatag_cache_set($cid, $info);
+    }
+  }
+
+  if (isset($instance)) {
+    return isset($info[$instance]) ? $info[$instance] : FALSE;
+  }
+  else {
+    return $info;
+  }
+}
+
+/**
+ * Filter out meta tag values that equal the default values.
+ *
+ * @todo Use information in $values[$metatag]['default'] rather than a $defaults parameter.
+ */
+function metatag_filter_values_from_defaults(array &$values, array $defaults = array()) {
+  foreach ($values as $metatag => $data) {
+    $default = isset($data['default']) ? $data['default'] : (isset($defaults[$metatag]['value']) ? $defaults[$metatag]['value'] : NULL);
+    if (isset($default) && isset($data['value']) && $default === $data['value']) {
+      // Meta tag has a default, and it matches user-submitted value.
+      unset($values[$metatag]);
+    }
+    elseif (!isset($default) && (is_string($data['value']) && !drupal_strlen($data['value']) || (is_array($data['value']) && !array_filter($data['value'])))) {
+      // Metatag does not have a default, and user did not submit a value.
+      unset($values[$metatag]);
+    }
+    if (isset($values[$metatag]['default'])) {
+      // Unset the default hidden value.
+      unset($values[$metatag]['default']);
+    }
+  }
+}
+
+/**
+ * Return all the parents of a given configuration instance.
+ *
+ * @param $instance
+ *   A meta tag configuration instance.
+ *
+ * @return
+ *   An array of instances starting with the $instance parameter, with the end
+ *   of the array being the global instance.
+ */
+function metatag_config_get_parent_instances($instance, $include_global = TRUE) {
+  $parents = array();
+  $segments = explode(':', $instance);
+  while (count($segments) > 0) {
+    $parents[] = implode(':', $segments);
+    array_pop($segments);
+  }
+  if ($include_global && end($parents) !== 'global') {
+    $parents[] = 'global';
+  }
+  reset($parents);
+  return $parents;
+}
+
+/**
+ * Get the proper label of a configuration instance.
+ *
+ * @param $instance
+ *   A meta tag configuration instance.
+ */
+function metatag_config_instance_label($instance) {
+  $labels = &drupal_static(__FUNCTION__, array());
+
+  if (!isset($labels[$instance])) {
+    $instance_parts = explode(':', $instance);
+    $instance_part = array_pop($instance_parts);
+    if ($context = metatag_config_instance_info($instance)) {
+      $labels[$instance] = $context['label'];
+    }
+    else {
+      $labels[$instance] = t('Unknown (@instance)', array('@instance' => $instance_part));
+    }
+    // Normally the following would use metatag_config_get_parent_instances()
+    // but since we already sliced the instance by separator and removed the
+    // last segment, putting the array back together gives us this instance's
+    // parent.
+    if (!empty($instance_parts)) {
+      $labels[$instance] = metatag_config_instance_label(implode(':', $instance_parts)) . ': ' . $labels[$instance];
+    }
+  }
+
+  return $labels[$instance];
+}
+
+/**
+ * Title callback for meta tag configuration instances.
+ */
+function metatag_config_title($config) {
+  return metatag_config_instance_label($config->instance);
+}
+
+/**
+ * Access callback for meta tag configuration instances.
+ */
+function metatag_config_access($op, $config = NULL) {
+  if (!user_access('administer meta tags')) {
+    return FALSE;
+  }
+
+  if ($op == 'enable') {
+    return !empty($config->disabled);
+  }
+  elseif ($op == 'disable') {
+    return empty($config->disabled);
+  }
+  elseif ($op == 'delete') {
+    return ($config->export_type & EXPORT_IN_DATABASE) && !($config->export_type & EXPORT_IN_CODE);
+  }
+  elseif ($op == 'revert') {
+    return ($config->export_type & EXPORT_IN_DATABASE) && ($config->export_type & EXPORT_IN_CODE);
+  }
+
+  return FALSE;
+}
+
+/**
+ * Checks if a metatag configuration record is enabled.
+ *
+ * @param string $instance
+ *   The configuration instance machine name.
+ *
+ * @return bool
+ *   TRUE if the configuration is enabled, or FALSE otherwise.
+ */
+function metatag_config_is_enabled($instance, $include_defaults = FALSE, $include_global = TRUE) {
+  if ($include_defaults) {
+    $instances = metatag_config_get_parent_instances($instance, $include_global);
+    $configs = metatag_config_load_multiple($instances);
+
+    // Check if one of the configs is enabled.
+    foreach ($configs as $config) {
+      if (empty($config->disabled)) {
+        return TRUE;
+      }
+    }
+
+    // No enabled configs found.
+    return FALSE;
+  }
+  else {
+    $config = metatag_config_load($instance);
+    return !empty($config) && empty($config->disabled);
+  }
+}
+
+/**
+ * Wrapper around entity_language() to use LANGUAGE_NONE if the entity does not
+ * have a language assigned.
+ *
+ * @param $entity_type
+ *   An entity type's machine name.
+ * @param $entity
+ *   The entity to review;
+ *
+ * @return
+ *   A string indicating the language code to be used.
+ */
+function metatag_entity_get_language($entity_type, $entity) {
+  // Determine the entity's language.
+  $langcode = entity_language($entity_type, $entity);
+
+  // If no matching language was found, which will happen for e.g. terms and
+  // users, it is normally recommended to use the system default language.
+  // However, as the system default language can change, this could potentially
+  // cause data loss / confusion problems; as a result use the system "no
+  // language" value to avoid any potential problems.
+  if (empty($langcode)) {
+    $langcode = LANGUAGE_NONE;
+  }
+
+  return $langcode;
+}
+
+/**
+ * Implements hook_features_api().
+ */
+function metatag_features_api() {
+  $components = array(
+    'metatag' => array(
+      'name' => t('Metatag'),
+      'feature_source' => TRUE,
+      'default_hook' => 'metatag_export_default',
+      'default_file' => FEATURES_DEFAULTS_INCLUDED,
+      'file' => drupal_get_path('module', 'metatag') . '/metatag.features.inc',
+    ),
+  );
+  return $components;
+}
+
+/**
+ * Implements hook_views_post_render().
+ */
+function metatag_views_post_render(&$view, &$output, &$cache) {
+  // Build a shortcut to the current display object.
+  $display = $view->display[$view->current_display];
+
+  // Only proceed if this view is a full page, don't process block or other
+  // Views display objects.
+  if ($display->display_plugin == 'page' && isset($display->display_options['path'])) {
+    // Check if this is an entity display page, if so trigger
+    // hook_entity_view().
+    foreach (entity_get_info() as $entity_type => $entity_info) {
+      // Entity paths will include an auto-loader that matches the entity's
+      // name, thus the path will be 'some/path/%entity_name'.
+      if (isset($entity_info['path']) && ($display->display_options['path'] . $entity_type) == $entity_info['path']) {
+        // Only proceed if this entity type supports meta tags.
+        if (metatag_entity_supports_metatags($entity_type)) {
+          // There must be at least one argument and the first argument must be
+          // numerical.
+          if (!empty($view->args) && is_numeric($view->args[0])) {
+            // Only the first argument is used.
+            $entities = entity_load($entity_type, array($view->args[0]));
+            // Only if the entity actually exists.
+            if (!empty($entities)) {
+              $entity = array_pop($entities);
+              metatag_entity_view($entity, $entity_type, 'full', NULL, TRUE);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_ctools_render_alter().
+ *
+ * Temporary solution to load meta tags on entity pages that are driven by
+ * CTools display handlers.
+ */
+function metatag_ctools_render_alter(&$info, $page, $context) {
+  // Only proceed if this is a full page (don't process individual panes) and
+  // there's an 'admin path' for the current task.
+  if ($page && !empty($context['task']['admin path'])) {
+    // Check if this is an entity display page, if so trigger
+    // hook_entity_view().
+    foreach (entity_get_info() as $entity_type => $entity_info) {
+      // Entity paths will include an auto-loader that matches the entity's
+      // name, thus the path will be 'some/path/%entity_name'.
+      if (isset($entity_info['path']) && $context['task']['admin path'] == $entity_info['path']) {
+        // Only proceed if this entity type supports meta tags.
+        if (metatag_entity_supports_metatags($entity_type)) {
+          // There must be at least one argument and the first argument must be
+          // numerical.
+          if (!empty($context['args']) && is_numeric($context['args'][0])) {
+            // We need to pop entity from contexts array.
+            $first_context = array_pop($context['contexts']);
+            // Only if the context actually exists, which would be an entity.
+            if (!empty($first_context->data) && is_object($first_context->data)) {
+              $langcode = $GLOBALS['language_content']->language;
+              metatag_entity_view($first_context->data, $entity_type, 'full', $langcode, TRUE);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_translation_delete().
+ *
+ * Required for content translations being handled via Entity_Translation to
+ * remove the appropriate record when a translation is removed without the
+ * corresponding entity record also being removed.
+ */
+function metatag_entity_translation_delete($entity_type, $entity, $langcode) {
+  // Get the entity's ID.
+  list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity);
+  $revision_id = intval($revision_id);
+
+  // Delete the translation.
+  metatag_metatags_delete($entity_type, $entity_id, $revision_id, $langcode);
+}
+
+/**
+ * Translates the metatag if i18n_string is enabled.
+ * @param $name
+ *   Array or string concatenated with ':' that contains textgroup and string context
+ * @param string $string
+ *   String in default language or array of strings to be translated
+ * @param $options
+ *   An associative array of additional options. @see i18n_string_translate()
+ */
+function metatag_translate($name, $string, $langcode = NULL, $update = FALSE) {
+  if (function_exists('i18n_string_translate')) {
+    $options = array(
+      'langcode' => $langcode,
+      'update' => $update,
+    );
+    return i18n_string_translate($name, $string, $options);
+  }
+  else {
+    return $string;
+  }
+}
+
+/**
+ * Checks if this entity is the default revision (published).
+ *
+ * @param object $entity
+ *   The entity object, e.g., $node.
+ *
+ * @return bool
+ *   TRUE if the entity is the default revision, FALSE otherwise.
+ */
+function _metatag_isdefaultrevision($entity) {
+  // D7 "Forward revisioning" is complex and causes a node_save() with the
+  // future node in node table. This fires hook_node_update() twice and cause
+  // abnormal behaviour in metatag.
+  //
+  // The steps taken by Workbench Moderation is to save the forward revision
+  // first and overwrite this with the live version in a shutdown function in
+  // a second step. This will confuse metatag. D7 has no generic property
+  // in the node object, if the node that is updated is the 'published' version
+  // or only a draft of a future version.
+  //
+  // This behaviour will change in D8 where $node->isDefaultRevision has been
+  // introduced. See below links for more details.
+  // - https://www.drupal.org/node/1879482
+  // - https://www.drupal.org/node/218755
+  // - https://www.drupal.org/node/1522154
+  //
+  // 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']);
+  }
+  return FALSE;
+}
+
+/**
+ * Generate the cache ID to use with metatag_cache_get/metatag_cache_set calls.
+ *
+ * @param array $cid_parts
+ *   A list of values to be used.
+ *
+ * @return string
+ *   The cache ID string.
+ */
+function metatag_cache_default_cid_parts(array $cid_parts = array()) {
+  // The initial parts, control the order of the parts.
+  $cid_part_defaults = array(
+    'cache_type' => 'output',
+    'instance' => '',
+    'entity_type' => '',
+    'entity_id' => '',
+    'bundle' => '',
+    'langcode' => $GLOBALS['language_content']->language,
+    'revision_id' => '',
+    'view_mode' => '',
+    'status' => 200,
+    'protocol' => $GLOBALS['is_https'] ? 'https' : 'http',
+    'hostname' => $_SERVER['HTTP_HOST'],
+    'base_path' => base_path(),
+  );
+  $cid_parts = array_merge($cid_part_defaults, $cid_parts);
+
+  // Add the HTTP status code.
+  $headers = drupal_get_http_header();
+  if (isset($headers['status'])) {
+    $cid_parts['status'] = intval($headers['status']);
+  }
+
+  // Allow each page in a sequence to have different values.
+  if (isset($_GET['page'])) {
+    $cid_parts['page'] = $_GET['page'];
+  }
+
+  // Allow other modules to customize the data using
+  // hook_metatag_page_cache_cid_parts_alter().
+  drupal_alter('metatag_page_cache_cid_parts', $cid_parts);
+
+  // Remove empty parts.
+  $cid_parts = array_filter($cid_parts);
+
+  // Concatenate the cache ID parts, trim the results to 128 chars.
+  return substr(implode(':', $cid_parts), 0, 128);
+}
+
+/**
+ * Wrapper for cache_set.
+ *
+ * @see cache_set().
+ */
+function metatag_cache_set($cid, $data) {
+  // Cache the data for later.
+  return cache_set($cid, $data, 'cache_metatag');
+}
+
+/**
+ * Wrapper for cache_get.
+ *
+ * @see cache_get().
+ */
+function metatag_cache_get($cid) {
+  // Try to load the object.
+  return cache_get($cid, 'cache_metatag');
+}
+
+/**
+ * Determines if we are in an error page and return the appropriate instance.
+ *
+ * @return string
+ */
+function metatag_is_error_page() {
+  $known_errors = array(
+    'global:403' => '403 Forbidden',
+    'global:404' => '404 Not Found',
+  );
+  $headers = drupal_get_http_header();
+  if (isset($headers['status'])) {
+    foreach ($known_errors as $error => $status) {
+      if ($status == $headers['status']) {
+        return $error;
+      }
+    }
+  }
+  return '';
+}

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

@@ -0,0 +1,390 @@
+<?php
+
+class MetaTagsTestHelper extends DrupalWebTestCase {
+  function setUp(array $modules = array()) {
+    $modules[] = 'ctools';
+    $modules[] = 'token';
+    $modules[] = 'metatag';
+    $modules[] = 'metatag_test';
+    parent::setUp($modules);
+  }
+}
+
+class MetaTagsNodeUITest extends MetaTagsTestHelper {
+  /**
+   * Admin user.
+   *
+   * @var \StdClass
+   */
+  protected $adminUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag tests for nodes',
+      'description' => 'Test Metatag edit functionality for nodes.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules = array(
+      'metatag_dc',
+      'metatag_facebook',
+      'metatag_google_plus',
+      'metatag_opengraph',
+      'metatag_twitter_cards',
+    );
+    parent::setUp($modules);
+  }
+
+  /**
+   * Tests filtering of values using metatag_filter_values_from_defaults().
+   */
+  public function testMetatagFilterValuesFromDefaults() {
+    // Create a content type.
+    $this->drupalCreateContentType(array(
+      'type' => 'metatag_test',
+      'name' => 'Test',
+    ));
+
+    // Create an admin-level user.
+    $this->adminUser = $this->drupalCreateUser(array(
+      'administer meta tags',
+      'edit meta tags',
+      'create metatag_test content',
+      'delete any metatag_test content',
+      'edit any metatag_test content',
+    ));
+    $this->drupalLogin($this->adminUser);
+
+    // Assign default values for the new content type.
+    $this->drupalGet('admin/config/search/metatags/config/add');
+    $this->drupalPost(NULL, array(
+      'instance' => 'node:metatag_test',
+    ), t('Add and configure'));
+    $this->drupalPost(NULL, array(
+      'metatags[und][dcterms.title][value]' => '[node:title]',
+      'metatags[und][dcterms.creator][value]' => '[node:author]',
+      'metatags[und][dcterms.date][value]' => '[node:created:custom:Y-m-d\TH:iP]',
+      'metatags[und][dcterms.format][value]' => 'text/html',
+      'metatags[und][dcterms.identifier][value]' => '[current-page:url:absolute]',
+      'metatags[und][dcterms.language][value]' => '[node:language]',
+    ), t('Save'));
+
+    // Create a test node.
+    $this->drupalGet('node/add/metatag-test');
+    $this->drupalPost(NULL, array(
+      'metatags[und][dcterms.title][value]' => '[node:title] ponies',
+      'title' => 'Who likes magic',
+    ), t('Save'));
+
+    // Verify that the node saved correctly.
+    $xpath = $this->xpath("//h1");
+    $t_args = array('@type' => 'Test', '%title' => 'Who likes magic');
+    $this->assertText(strip_tags(t('@type %title has been created.', $t_args)));
+    // This doesn't work for some reason, it seems the HTML is stripped off
+    // during output so the %title's standard Drupal wrappers don't match.
+    // $this->assertText(t('@type %title has been created.', $t_args));
+
+    // Verify the node data saved correctly.
+    $matches = array();
+    if (preg_match('@node/(\d+)$@', $this->getUrl(), $matches)) {
+      $nid = end($matches);
+      $node = node_load($nid);
+      // Only the non-default values are stored.
+      $expected = array(
+        'und' => array(
+          'dcterms.title' => array(
+            'value' => '[node:title] ponies',
+          ),
+        ),
+      );
+      $this->assertEqual($expected, $node->metatags);
+    }
+    else {
+      $this->fail(t('Could not determine node ID for created node.'));
+    }
+
+    // Verify the title is using the custom default for this content type.
+    $xpath = $this->xpath("//meta[@name='dcterms.title']");
+    $this->assertEqual(count($xpath), 1);
+    $this->assertEqual($xpath[0]['content'], "Who likes magic ponies");
+  }
+}
+
+class MetaTagsUnitTest extends MetaTagsTestHelper {
+  public static function getInfo() {
+    return array(
+      'name' => 'Metatag unit tests',
+      'description' => 'Test basic meta tag functionality.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * Test the metatag_config_load_with_defaults() function.
+   */
+  public function testConfigLoadDefaults() {
+    $defaults = metatag_config_load_with_defaults('test:foo');
+    $this->assertEqual($defaults, array(
+      // Basic meta tags.
+      'title' => array('value' => 'Test altered title'),
+      'description' => array('value' => 'Test foo description'),
+      'abstract' => array('value' => 'Test foo abstract'),
+      // 'keywords' => array('value' => ''),
+
+      // Advanced meta tags.
+      // 'robots' => array('value' => ''),
+      // 'news_keywords' => array('value' => ''),
+      // 'standout' => array('value' => ''),
+      // 'robots' => array('value' => ''),
+      // 'standout' => array('value' => ''),
+      'generator' => array('value' => 'Drupal 7 (http://drupal.org)'),
+      // 'standout' => array('value' => ''),
+      // 'image_src' => array('value' => ''),
+      'canonical' => array('value' => '[current-page:url:absolute]'),
+      'shortlink' => array('value' => '[current-page:url:unaliased]'),
+      // 'publisher' => array('value' => ''),
+      // 'author' => array('value' => ''),
+      // 'original-source' => array('value' => ''),
+      // 'revisit-after' => array('value' => ''),
+      // 'content-language' => array('value' => ''),
+
+      // Fake meta tag.
+      'test:foo' => array('value' => 'foobar'),
+    ));
+  }
+
+  public function testEntitySupport() {
+    $test_cases[1] = array('type' => 'node', 'bundle' => 'article', 'expected' => TRUE);
+    $test_cases[2] = array('type' => 'node', 'bundle' => 'page', 'expected' => TRUE);
+    $test_cases[3] = array('type' => 'node', 'bundle' => 'invalid-bundle', 'expected' => FALSE);
+    $test_cases[4] = array('type' => 'user', 'expected' => TRUE);
+    $test_cases[5] = array('type' => 'taxonomy_term', 'bundle' => 'tags', 'expected' => TRUE);
+    $test_cases[6] = array('type' => 'taxonomy_term', 'bundle' => 'invalid-bundle', 'expected' => FALSE);
+    foreach ($test_cases as $test_case) {
+      $test_case += array('bundle' => NULL);
+      $this->assertMetatagEntityHasMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']);
+    }
+
+    variable_set('metatag_test_entity_info_disable', TRUE);
+    drupal_static_reset('metatag_entity_has_metatags');
+    drupal_static_reset('metatag_entity_supports_metatags');
+    entity_info_cache_clear();
+
+    $test_cases[2]['expected'] = FALSE;
+    $test_cases[4]['expected'] = FALSE;
+    $test_cases[6]['expected'] = FALSE;
+    foreach ($test_cases as $test_case) {
+      $test_case += array('bundle' => NULL);
+      $this->assertMetatagEntityHasMetatags($test_case['type'], $test_case['bundle'], $test_case['expected']);
+    }
+  }
+
+  function assertMetatagEntityHasMetatags($entity_type, $bundle, $expected) {
+    $entity = entity_create_stub_entity($entity_type, array(0, NULL, $bundle));
+    return $this->assertEqual(
+      metatag_entity_has_metatags($entity_type, $entity),
+      $expected,
+      t("metatag_entity_has_metatags(:type, :entity) is :expected", array(
+        ':type' => var_export($entity_type, TRUE),
+        ':entity' => var_export($entity, TRUE),
+        ':expected' => var_export($expected, TRUE),
+      ))
+    );
+  }
+
+  /**
+   * Test the metatag_config_instance_label() function.
+   */
+  public function testConfigLabels() {
+    $test_cases = array(
+      'node' => 'Node',
+      'node:article' => 'Node: Article',
+      'node:article:c' => 'Node: Article: Unknown (c)',
+      'node:b' => 'Node: Unknown (b)',
+      'node:b:c' => 'Node: Unknown (b): Unknown (c)',
+      'a' => 'Unknown (a)',
+      'a:b' => 'Unknown (a): Unknown (b)',
+      'a:b:c' => 'Unknown (a): Unknown (b): Unknown (c)',
+      'a:b:c:d' => 'Unknown (a): Unknown (b): Unknown (c): Unknown (d)',
+    );
+
+    foreach ($test_cases as $input => $expected_output) {
+      drupal_static_reset('metatag_config_instance_label');
+      $actual_output = metatag_config_instance_label($input);
+      $this->assertEqual($actual_output, $expected_output);
+    }
+  }
+}
+
+
+// TODO: Test each meta tag.
+// TODO: Scenarios.
+//
+// 1. Node
+// * No language assignment.
+// * First save.
+//
+// 2. Node
+// * No language assignment.
+// * Edit existing revision.
+//
+// 3. Node
+// * No language assignment.
+// * Create new revision.
+// * Publish new revision.
+//
+// 4. Node
+// * No language assignment.
+// * Create new revision.
+// * Delete new revision.
+//
+// 5. Node + Translation
+// * No language assignment
+// * Change language assignment.
+//   * Edit existing revision.
+//
+// 6. Node + Translation
+// * No language assignment
+// * Change language assignment.
+//   * Create new revision.
+// * Publish new revision.
+//
+// 7. Node + Translation
+// * No language assignment
+// * Change language assignment.
+//   * Create new revision.
+// * Delete new revision.
+//
+// 8. Node + Translation
+// * Initial language assignment
+//
+// 9. Node + Translation
+// * Initial language assignment
+// * Create new revision.
+// * Publish new revision.
+//
+// 10. Node + Translation
+// * Initial language assignment
+// * Create new revision.
+// * Delete new revision.
+//
+// 11. Node + Translation
+// * Initial language assignment
+// * Change language assignment.
+//   * Create new revision.
+// * Publish new revision.
+//
+// 12. Node + Translation
+// * Initial language assignment
+// * Change language assignment.
+//   * Create new revision.
+// * Delete new revision.
+//
+// 13. Node + Translation
+// * Initial language assignment
+// * Create translated node.
+//
+// 14. Node + Translation
+// * Initial language assignment
+// * Create new revision.
+// * Publish new revision.
+// * Create translated node.
+//
+// 15. Node + Translation
+// * Initial language assignment
+// * Create new revision.
+// * Publish new revision.
+// * Create translated node.
+// * Delete translated node.
+//
+// 16. Node + Translation
+// * Initial language assignment
+// * Create translated node.
+// * Delete original node.
+//
+// 17. Node + Translation
+// * Initial language assignment
+// * Create new revision.
+// * Publish new revision.
+// * Create translated node.
+// * Delete original node.
+//
+// 18. Node + entity_translation
+// * Initial language assignment
+// * Create translated node.
+//
+// 19. Node + entity_translation
+// * Initial language assignment
+// * Create translated node.
+// * Delete original.
+//
+// 20. Node + entity_translation
+// * Initial language assignment
+// * Create translated node.
+// * Create new revision.
+// * Publish new revision.
+//
+// 21. Node + entity_translation
+// * Initial language assignment
+// * Create translated node.
+// * Create new revision.
+// * Publish new revision.
+// * Delete new revision.
+//
+// 22. Node + entity_translation
+// * Initial language assignment
+// * Create translated node.
+// * Create new revision.
+// * Publish new revision.
+// * Delete original.
+//
+// 23. Node + entity_translation
+// * Initial language assignment
+// * Create translated node.
+// * Create new revision.
+// * Publish new revision.
+// * Delete original.
+//
+// 24. Node + entity_translation
+// * Initial language assignment
+// * Create new revision.
+// * Publish new revision.
+// * Create translated node.
+//
+// 25. Node + entity_translation
+// * Initial language assignment
+// * Create new revision.
+// * Publish new revision.
+// * Create translated node.
+// * Delete new revision.
+//
+//
+// 30. Node + i18n
+//
+//
+// 50. Term
+// * Create term.
+//
+// 51. Term
+// * Create term.
+// * Change values.
+//
+//
+// 60. User
+// * Create user.
+//
+// 61. User
+// * Create user.
+// * Change values.
+//
+//
+// 70. Custom path
+// * Defaults loaded.

+ 91 - 0
sites/all/modules/contrib/seo/metatag/metatag.theme.inc

@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * @file
+ * Theme callbacks for the metatag module.
+ */
+
+/**
+ * Theme callback for a normal meta tag.
+ *
+ * The format is:
+ * <meta name="[name]" content="[value]" />
+ */
+function theme_metatag($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#name'  => 'name',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}
+
+/**
+ * Theme callback for a normal meta tag.
+ *
+ * The format is:
+ * <meta http-equiv="[name]" content="[value]" />
+ */
+function theme_metatag_http_equiv($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#name' => 'http-equiv',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}
+
+/**
+ * Theme callback for a rel link tag.
+ *
+ * The format is:
+ * <link rel="[name]" href="[value]" />
+ */
+function theme_metatag_link_rel($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#name' => 'rel',
+    '#value' => 'href',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}
+
+/**
+ * Theme callback for a rev link tag.
+ *
+ * The format is:
+ * <link rev="[name]" href="[value]" />
+ */
+function theme_metatag_link_rev($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#name' => 'rev',
+    '#value' => 'href',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}
+
+/**
+ * Theme callback for a proprty meta tag.
+ *
+ * The format is:
+ * <meta property="[name]" content="[value]" />
+ */
+function theme_metatag_property($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#name' => 'property',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}

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

@@ -0,0 +1,193 @@
+<?php
+/**
+ * @file
+ * Custom tokens for Metatag.
+ */
+
+/**
+ * Implements hook_token_info().
+ */
+function metatag_token_info() {
+  $info = array();
+
+  $info['types']['metatag'] = array(
+    'name' => t('Meta tags'),
+    'description' => t('Generated by the Metatag module, may not be used to fill in other meta tags.'),
+  );
+
+  $metatag_info = metatag_get_info();
+
+  foreach($metatag_info['tags'] as $value) {
+    $info['tokens']['metatag'][$value['name']] = array(
+      'name' => $value['label'],
+      'description' => $value['description']
+    );
+  }
+
+  if (module_exists('taxonomy')) {
+    $info['tokens']['term']['metatag'] = array(
+      'name' => t('Metatag.'),
+      'description' => t('Metatag.'),
+      'type' => 'metatag'
+    );
+  }
+
+  if (module_exists('node')) {
+    $info['tokens']['node']['metatag'] = array(
+      'name' => t('Metatag.'),
+      'description' => t('Metatag.'),
+      'type' => 'metatag'
+    );
+  }
+
+  return $info;
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function metatag_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  $replacements = array();
+
+  $sanitize = !empty($options['sanitize']);
+
+  // Metatag tokens.
+  if ($type == 'metatag' && !empty($data['metatag'])) {
+    $metatag = $data['metatag'];
+    foreach ($tokens as $name => $original) {
+      if(isset($metatag[$name])){
+        $replacements[$original] = $sanitize ? filter_xss($metatag[$name]) : $metatag[$name];
+      }
+    }
+  }
+
+  // Token tokens.
+  if ($type == 'term' && !empty($data['term'])) {
+    $term = $data['term'];
+
+    if ($metatag_tokens = token_find_with_prefix($tokens, 'metatag')) {
+      $result = metatag_token_generate_array($term, 'taxonomy_term', $term->vocabulary_machine_name);
+      $replacements += token_generate('metatag', $metatag_tokens, array('metatag' => $result), $options);
+    }
+  }
+
+  // Node tokens.
+  if ($type == 'node' && !empty($data['node'])) {
+    $node = $data['node'];
+
+    if ($metatag_tokens = token_find_with_prefix($tokens, 'metatag')) {
+      $result = metatag_token_generate_array($node, 'node', $node->type);
+      $replacements += token_generate('metatag', $metatag_tokens, array('metatag' => $result), $options);
+    }
+  }
+
+  return $replacements;
+}
+
+/**
+ * Generate an array of meta tags for a given entity.
+ */
+function metatag_token_generate_array($entity, $entity_type, $bundle) {
+  if (metatag_entity_supports_metatags($entity_type, $bundle)) {
+    $token_type = token_get_entity_mapping('entity', $entity_type);
+
+    $instance = "{$entity_type}:{$bundle}";
+    $options = array();
+    $options['token data'][$token_type] = $entity;
+    $options['entity'] = $entity;
+
+    $metatags = array();
+    if (!empty($entity->metatags)) {
+      $language = metatag_entity_get_language($entity_type, $entity);
+      if (!empty($entity->metatags[$language])) {
+        $metatags = $entity->metatags[$language];
+      }
+    }
+    $metatags += metatag_config_load_with_defaults($instance);
+    // Process it for entity metatag replacement to avoid infinite recursion.
+    $metatags = _metatag_token_process_metatag($metatags, $token_type);
+
+    $result = array();
+    foreach ($metatags as $metatag => $data) {
+      if ($metatag_instance = metatag_get_instance($metatag, $data)) {
+        $result[$metatag] = $metatag_instance->getValue($options);
+      }
+    }
+    return $result;
+  }
+
+  return NULL;
+}
+
+/**
+ * Loop through metatags to avoid recursion on entity tokens. It will replace
+ * entity metatag token to its actual entity metatag field value.
+ *
+ * @param array $metatags
+ *   An array of entity metatag tokens.
+ * @param string $token_type
+ *   The entity token type, such as 'node' or 'term'.
+ *
+ * @return array
+ *   Return metatags array with entity metatag tokens replaced.
+ */
+function _metatag_token_process_metatag($metatags, $token_type) {
+  foreach ($metatags as $metatag => $data) {
+    // Explode all metatag token in field.
+    $data_tokens = token_scan($data['value']);
+    if (isset($data_tokens[$token_type])) {
+      foreach($data_tokens[$token_type] as $key => $value) {
+        $metatag_parts = explode(':', $key);
+        // Check entity metatag token. Like [<entity_token_type>:metatag:<xyz>].
+        if ($metatag_parts[0] == 'metatag') {
+          $entity_field = implode(':', array_slice($metatag_parts, 1));
+          // Entity metatag field may contain other entity metatag token
+          // that need to be replaced too.
+          $replaced_value = metatag_token_entitymetatagtoken_replace($metatags, $metatags[$entity_field]['value'], $token_type);
+          $metatags[$metatag]['value'] = str_replace($value, $replaced_value, $metatags[$metatag]['value']);
+        }
+      }
+    }
+  }
+
+  return $metatags;
+}
+
+/**
+ * Replace entity metatag token with actual entity metatag field value.
+ *
+ * @param array $metatags
+ *   An array entity metatag tokens.
+ * @param string $token
+ *   A token to be replaced.
+ * @param string $token_type
+ *   The entity token type, such as 'node' or 'term'.
+ * @param array $search_tokens
+ *   An array of tokens to search for.
+ * @param array $replace_tokens
+ *   An array of tokens to be replaced.
+ *
+ * @return string
+ *   The replaced value of $token.
+ */
+function metatag_token_entitymetatagtoken_replace($metatags, $token, $token_type, $search_tokens = array(), $replace_tokens = array()) {
+  $data_tokens = token_scan($token);
+  // Check field has tokens.
+  if (isset($data_tokens[$token_type])) {
+    // Go through each token in field and find entity metatag tokens.
+    foreach($data_tokens[$token_type] as $key => $value) {
+      $metatag_parts = explode(':', $key);
+      if ($metatag_parts[0] == 'metatag' && !in_array($value, $search_tokens)) {
+        // Entity metatag field value.
+        $entity_field = implode(':', array_slice($metatag_parts, 1));
+        $replaced_value = metatag_token_entitymetatagtoken_replace($metatags, $metatags[$entity_field]['value'], $token_type, $replace_tokens, $search_tokens);
+        $replace_tokens[] = $replaced_value;
+        $search_tokens[] = $value;
+      }
+    }
+    return str_replace($search_tokens, $replace_tokens, $token);
+  }
+  else {
+    return $token;
+  }
+}

+ 57 - 0
sites/all/modules/contrib/seo/metatag/metatag.vertical-tabs.js

@@ -0,0 +1,57 @@
+/**
+ * @file
+ * Custom JS for controlling the Metatag vertical tab.
+ */
+
+(function ($) {
+  'use strict';
+
+Drupal.behaviors.metatagFieldsetSummaries = {
+  attach: function (context) {
+    $('fieldset.metatags-form', context).drupalSetSummary(function (context) {
+      var vals = [];
+      $("input[type='text'], select, textarea", context).each(function() {
+        var input_field = $(this).attr('name');
+        // Verify the field exists before proceeding.
+        if (input_field === undefined) {
+          return false;
+        }
+        var default_name = input_field.replace(/\[value\]/, '[default]');
+        var default_value = $("input[type='hidden'][name='" + default_name + "']", context);
+        if (default_value.length && default_value.val() === $(this).val()) {
+          // Meta tag has a default value and form value matches default value.
+          return true;
+        }
+        else if (!default_value.length && !$(this).val().length) {
+          // Meta tag has no default value and form value is empty.
+          return true;
+        }
+        var label = $("label[for='" + $(this).attr('id') + "']").text();
+        vals.push(Drupal.t('@label: @value', {
+          '@label': $.trim(label),
+          '@value': Drupal.truncate($(this).val(), 25) || Drupal.t('None')
+        }));
+      });
+      if (vals.length === 0) {
+        return Drupal.t('Using defaults');
+      }
+      else {
+        return vals.join('<br />');
+      }
+    });
+  }
+};
+
+/**
+ * Encode special characters in a plain-text string for display as HTML.
+ */
+Drupal.truncate = function (str, limit) {
+  if (str.length > limit) {
+    return str.substr(0, limit) + '...';
+  }
+  else {
+    return str;
+  }
+};
+
+})(jQuery);

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

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

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

@@ -0,0 +1,12 @@
+name = Metatag: App Links
+description = "Provides support for applinks.org meta tags."
+package = SEO
+core = 7.x
+dependencies[] = metatag
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

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

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

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

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

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

@@ -0,0 +1,31 @@
+Metatag Context
+---------------
+This module is provides a Metatag reaction for Context [1], thus allowing meta
+tags to be assigned to specific paths and other conditions.
+
+Configuration can controlled via the normal Context UI module or the new admin
+page available at: admin/config/search/metatags/context
+
+Included with the module are default Context configurations that may be enabled
+from the Context UI admin page and then customized as necessary. The included
+configurations are:
+  * user_login - for anonymous users accessing the user and user/login pages.
+  * user_register - for anonymous users accessing the user registration page.
+  * forum - for the main forum page from the Forum module. Topic pages are
+    handled as regular nodes, sub-forum pages are handled as regular term pages.
+  * blog - for the main blog page. Note: it does not cover the per-user blog
+    pages too.
+
+
+Credits
+------------------------------------------------------------------------------
+This module is based on the Context Metadata [2] module. The initial
+development was by Marcin Pajdzik [3] (sponsored by Dennis Publishing [4]).
+
+
+References
+------------------------------------------------------------------------------
+1: https://www.drupal.org/project/context
+2: https://www.drupal.org/project/context_metadata
+3: https://www.drupal.org/u/marcin-pajdzik
+4: http://www.dennis.co.uk/

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

@@ -0,0 +1,247 @@
+<?php
+/**
+ * @file
+ * Admin settings page for Metatag Context.
+ */
+
+/**
+ * Provides administration overview page for metatags by path settings.
+ */
+function metatag_context_context_overview() {
+  $contexts = context_enabled_contexts(TRUE);
+  $header = array(t('Name'), t('Paths'), t('Operations'));
+  $rows = array();
+
+  $caption = t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a> and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global')));
+
+  foreach ($contexts as $name => $context) {
+    // Only show context items that are specifically selected to be "Shown on
+    // metatag admin page".
+    if (isset($context->reactions['metatag_context_reaction']['metatag_admin']) &&  $context->reactions['metatag_context_reaction']['metatag_admin']) {
+      $ops = array(
+        l(t('Edit'), 'admin/config/search/metatags/context/' . $context->name, array('query' => array('destination' => 'admin/config/search/metatags/context'))),
+        l(t('Delete'), 'admin/config/search/metatags/context/' . $context->name . '/delete', array('query' => array('destination' => 'admin/config/search/metatags/context'))),
+      );
+      $rows[] = array(
+        $context->name,
+        isset($context->conditions['path']) ? htmlspecialchars(implode(', ', $context->conditions['path']['values'])) : t('No path condition.'),
+        implode(' | ', $ops),
+      );
+    }
+  }
+  return theme('table', array('header' => $header, 'rows' => $rows, 'caption' => $caption));
+}
+
+/**
+ * FormAPI callback to build the 'config_add' form.
+ */
+function metatag_context_config_add_form($form, &$form_state) {
+  $form['name'] = array(
+    '#title' => 'Name',
+    '#type' => 'textfield',
+    '#default_value' => '',
+    '#description' => 'The unique ID for this metatag path context rule. This must contain only lower case letters, numbers and underscores.',
+    '#required' => 1,
+    '#maxlength' => 255,
+    '#element_validate' => array('metatag_context_edit_name_validate'),
+  );
+
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['save'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add and configure'),
+  );
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => t('Cancel'),
+    '#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/metatags/context',
+  );
+  return $form;
+}
+
+/**
+ * FormAPI callback to validate the 'edit_name' field.
+ */
+function metatag_context_edit_name_validate($element, &$form_state) {
+  // Check for string identifier sanity.
+  if (!preg_match('!^[a-z0-9_-]+$!', $element['#value'])) {
+    form_error($element, t('The name can only consist of lowercase letters, underscores, dashes, and numbers.'));
+    return;
+  }
+
+  // Ensure the CTools exportables system is loaded.
+  ctools_include('export');
+
+  // Check for name collision.
+  if ($exists = ctools_export_crud_load('context', $element['#value'])) {
+    form_error($element, t('A context with this name already exists. Please choose another name or delete the existing item before creating a new one.'));
+  }
+}
+
+/**
+ * FormAPI callback to save the 'edit_name' form.
+ */
+function metatag_context_config_add_form_submit($form, &$form_state) {
+  $context = metatag_context_load_default_context();
+  $context->name = $form_state['values']['name'];
+  context_save($context);
+  $form_state['redirect'] = 'admin/config/search/metatags/context/' . $context->name;
+}
+
+/**
+ * FormAPI callback to build the edit form.
+ */
+function metatag_context_config_edit_form($form, &$form_state, $context) {
+  $form_state['metatag_context']['context'] = $context;
+
+  // Empty form to start with.
+  $form = array();
+  // Don't care about the instance name, the data is being managed by Context
+  // and not Metatag.
+  $instance = "";
+  $options = array();
+
+  $metatags = $context->reactions['metatag_context_reaction']['metatags'];
+  if (!isset($metatags[LANGUAGE_NONE])) {
+    $metatags = array(
+      LANGUAGE_NONE => $metatags,
+    );
+  }
+
+  // Load the METATAG form.
+  metatag_metatags_form($form, $instance, $metatags[LANGUAGE_NONE], $options);
+
+  $form['paths'] = array(
+    '#title' => 'Path',
+    '#description' => t('Set this metatag context when any of the paths above match the page path. Put each path on a separate line. You can use the <code>*</code> character (asterisk) as a wildcard and the <code>~</code> character (tilde) to exclude one or more paths. Use <code>&lt;front&gt;</code> for the site front page. Only local paths (e.g. "example/page") will work, do not use relative URLs ("/example/page") or absolute URLs ("http://example.com/example/page").'),
+    '#type' => 'textarea',
+    '#default_value' => isset($context->conditions['path']['values']) ? html_entity_decode(implode('&#13;&#10;', $context->conditions['path']['values'])) : '',
+    '#required' => 1,
+    '#weight' => -100,
+  );
+
+  // If other conditions are assigned, mention it.
+  $conditions = array_keys($context->conditions);
+  foreach ($conditions as $key => $condition) {
+    if ($condition == 'path') {
+      unset($conditions[$key]);
+    }
+  }
+  if (!empty($conditions)) {
+    $form['other_conditions'] = array(
+      '#prefix' => '<p><em>',
+      '#markup' => t('Other conditions have been assigned that must be controlled through the main Context settings page.'),
+      '#suffix' => '</em></p>',
+      '#weight' => -99.9,
+    );
+  }
+
+  $form['help'] = array(
+    '#prefix' => '<hr /><p><em>',
+    '#markup' => t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a> and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))),
+    '#suffix' => '</em></p>',
+    '#weight' => -99,
+  );
+
+  // Show all tokens.
+  $form['metatags']['tokens']['#token_types'] = 'all';
+
+  $form['metatags']['#type'] = 'container';
+  unset($form['metatags']['#collapsed']);
+  unset($form['metatags']['#collapsible']);
+
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['save'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => t('Cancel'),
+    '#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/metatags/context',
+  );
+  $form['#submit'][] = 'metatag_context_config_edit_form_submit';
+
+  return $form;
+}
+
+/**
+ * FormAPI callback for the final submission.
+ */
+function metatag_context_config_edit_form_submit($form, &$form_state) {
+  $context = $form_state['metatag_context']['context'];
+  $context->reactions['metatag_context_reaction']['metatags'] = $form_state['values']['metatags'];
+  $paths = explode("\n", str_replace("\r", "", $form_state['values']['paths']));
+  $paths = array_combine($paths, $paths);
+  $context->conditions['path']['values'] = $paths;
+  context_save($context);
+  $form_state['redirect'] = 'admin/config/search/metatags/context';
+}
+
+/**
+ * FormAPI callback to build the 'delete' form.
+ */
+function metatag_context_delete_form($form, &$form_state, $context) {
+  $form_state['metatag_context']['context'] = $context;
+
+  $form['delete'] = array(
+    '#value' => 'This action will permanently remove this item from your database.'
+  );
+
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['save'] = array(
+    '#type' => 'submit',
+    '#value' => t('Delete'),
+  );
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => t('Cancel'),
+    '#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/metatags/context',
+  );
+  $form['#submit'][] = 'metatag_context_delete_form_submit';
+
+  return $form;
+}
+
+/**
+ * FormAPI submission callback for the 'delete' form.
+ */
+function metatag_context_delete_form_submit($form, &$form_state) {
+  context_delete($form_state['metatag_context']['context']);
+  $form_state['redirect'] = 'admin/config/search/metatags/context';
+}
+
+/**
+ * Create a default Metatag-focused context.
+ *
+ * @return object
+ *   A context structure in the form of a StdClass object.
+ */
+function metatag_context_load_default_context() {
+  $context = new stdClass();
+  $context->disabled = FALSE; /* Edit this to true to make a default context disabled initially */
+  $context->api_version = 3;
+  $context->name = 'default_metatag_context';
+  $context->description = '';
+  $context->tag = 'Metatag';
+  $context->metatag = TRUE;
+  $context->conditions = array(
+    'path' => array(
+      'values' => array(
+      ),
+    ),
+  );
+  $context->reactions = array(
+    'metatag_context_reaction' => array(
+      'metatags' => array(),
+      'metatag_admin' => 1,
+    ),
+  );
+  $context->condition_mode = 0;
+  $context->weight = 0;
+
+  // Translatables
+  // Included for use with string extractors like potx.
+  t('Metatag');
+  return $context;
+}

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

@@ -0,0 +1,285 @@
+<?php
+/**
+ * @file
+ * Context reaction for Metatag.
+ */
+
+class metatag_context_reaction extends context_reaction {
+  function options_form($context) {
+    $form = array();
+
+    // Don't care about the instance name, the data is being managed by
+    // Context and not Metatag.
+    $instance = "";
+
+    // Load the previously saved settings.
+    $data = $this->fetch_from_context($context);
+    if (!isset($data['metatags'])) {
+      $data['metatags'] = array();
+    }
+
+    // Support the pre-v1.0 data structures that were not nested with the
+    // language code.
+    if (!isset($data['metatags'][LANGUAGE_NONE])) {
+      $data['metatags'] = array(
+        LANGUAGE_NONE => $data['metatags'],
+      );
+    }
+
+    // No options currently available.
+    $options = array();
+
+    $form['help'] = array(
+      '#prefix' => '<p><em>',
+      '#markup' => t('Values assigned here inherit from the <a href="@url" title="Edit the global default meta tags.">global defaults</a> and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))),
+      '#suffix' => '</em></p>',
+    );
+
+    $form['basic_header'] = array(
+      '#prefix' => '<hr /><h3>',
+      '#markup' => t('Basic tags'),
+      '#suffix' => '</h3>',
+    );
+
+    // Load the basic Metatag form.
+    metatag_metatags_form($form, $instance, $data['metatags'][LANGUAGE_NONE], $options);
+
+    // Stop the meta tag fields appearing within a fieldset.
+    $form['metatags']['#type'] = 'container';
+    unset($form['metatags']['#collapsed']);
+    unset($form['metatags']['#collapsible']);
+    unset($form['#submit']);
+
+    // Flatten the fieldsets because otherwise the Context module will not save
+    // them properly.
+    foreach (element_children($form['metatags'][LANGUAGE_NONE]) as $fieldset) {
+      $child = $form['metatags'][LANGUAGE_NONE][$fieldset];
+      if (isset($child['#type']) && $child['#type'] == 'fieldset') {
+        $form['metatags'][LANGUAGE_NONE][$fieldset . '_heading'] = array(
+          '#prefix' => '<hr /><h3>',
+          '#markup' => $form['metatags'][LANGUAGE_NONE][$fieldset]['#title'],
+          '#suffix' => '</h3>',
+        );
+        if (isset($form['metatags'][LANGUAGE_NONE][$fieldset]['#description'])) {
+          $form['metatags'][LANGUAGE_NONE][$fieldset . '_description'] = array(
+            '#prefix' => '<p>',
+            '#markup' => $form['metatags'][LANGUAGE_NONE][$fieldset]['#description'],
+            '#suffix' => '</p>',
+          );
+        }
+        foreach ($form['metatags'][LANGUAGE_NONE][$fieldset] as $key => $value) {
+          if (substr($key, 0, 1) == '#') {
+            unset($form['metatags'][LANGUAGE_NONE][$fieldset][$key]);
+            continue;
+          }
+          $form['metatags'][LANGUAGE_NONE][$key] = $value;
+          unset($form['metatags'][LANGUAGE_NONE][$key]['#parents']);
+          unset($form['metatags'][LANGUAGE_NONE][$fieldset][$key]);
+        }
+        unset($form['metatags'][LANGUAGE_NONE][$fieldset]);
+      }
+    }
+
+    // Show all takens.
+    $form['metatags']['tokens']['#token_types'] = 'all';
+
+    $form['metatag_admin'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Show on metatag admin page.'),
+      '#weight' => -98,
+      '#default_value' => isset($data['metatag_admin']) ? $data['metatag_admin'] : '',
+    );
+
+    return $form;
+  }
+
+  /**
+   * Output a list of active contexts.
+   */
+  function execute() {
+    $output = &drupal_static('metatag_context');
+
+    if (!isset($output)) {
+      $metatags = array();
+      $output = array();
+      $contexts = context_active_contexts();
+      $options = array();
+      $instance_names = array();
+
+      foreach ($contexts as $context) {
+        if (!empty($context->reactions['metatag_context_reaction']['metatags'])) {
+          $metadata_array = $context->reactions['metatag_context_reaction']['metatags'];
+          if (isset($metadata_array[LANGUAGE_NONE])) {
+            $metadata_array = $metadata_array[LANGUAGE_NONE];
+          }
+          foreach ($metadata_array as $key => $data) {
+            if (!empty($data['value'])) {
+              $metatags[$key] = $data;//t(check_plain($data['value']));
+            }
+          }
+
+          // Add this context to the list of instances.
+          $instance_names[] = $context->name;
+        }
+      }
+
+      // Only proceed if metatags were assigned.
+      if (!empty($metatags)) {
+        // Load the global defaults.
+        $metatags += metatag_config_load_with_defaults('');
+
+        // Compile the identifier for this combination based on the context
+        // names.
+        asort($instance_names);
+        $instance = 'context:' . implode(',', $instance_names);
+        $options['instance'] = $instance;
+
+        foreach ($metatags as $metatag => $data) {
+          if ($metatag_instance = metatag_get_instance($metatag, $data)) {
+            $output[$metatag] = $metatag_instance->getElement($options);
+          }
+        }
+
+        // Allow the output meta tags to be modified using
+        // hook_metatag_metatags_view_alter().
+        drupal_alter('metatag_metatags_view', $output, $instance);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_context_default_contexts().
+ */
+function metatag_context_context_default_contexts() {
+  $defaults = array();
+
+  $context = new stdClass();
+  $context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
+  $context->api_version = 3;
+  $context->name = 'user_login';
+  $context->description = 'A default Metatag:Context definition for the user login page. This needs to be enabled and then it can be customized as needed.';
+  $context->tag = 'Metatag';
+  $context->conditions = array(
+    'path' => array(
+      'values' => array(
+        'user' => 'user',
+        'user/login' => 'user/login',
+      ),
+    ),
+    'user' => array(
+      'values' => array(
+        'anonymous user' => 'anonymous user',
+      ),
+    ),
+  );
+  $context->reactions = array(
+    'metatag_context_reaction' => array(
+      'metatags' => array(
+        'title' => array(
+          'value' => '[current-page:title] | [site:name]',
+          'default' => '[current-page:title] | [site:name]',
+        ),
+      ),
+      'metatag_admin' => 1,
+    ),
+  );
+  $context->condition_mode = 1;
+  $defaults[$context->name] = $context;
+
+  $context = new stdClass();
+  $context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
+  $context->api_version = 3;
+  $context->name = 'user_register';
+  $context->description = 'A default Metatag:Context definition for the user registration page. This needs to be enabled and then it can be customized as needed.';
+  $context->tag = 'Metatag';
+  $context->conditions = array(
+    'path' => array(
+      'values' => array(
+        'user/register' => 'user/register',
+      ),
+    ),
+    'user' => array(
+      'values' => array(
+        'anonymous user' => 'anonymous user',
+      ),
+    ),
+  );
+  $context->reactions = array(
+    'metatag_context_reaction' => array(
+      'metatags' => array(
+        'title' => array(
+          'value' => '[current-page:title] | [site:name]',
+          'default' => '[current-page:title] | [site:name]',
+        ),
+      ),
+      'metatag_admin' => 1,
+    ),
+  );
+  $context->condition_mode = 1;
+  $defaults[$context->name] = $context;
+
+  if (module_exists('forum')) {
+    $context = new stdClass();
+    $context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
+    $context->api_version = 3;
+    $context->name = 'forum';
+    $context->description = 'A default Metatag:Context definition for the main forum page. This needs to be enabled and then it can be customized as needed.';
+    $context->tag = 'Metatag';
+    $context->conditions = array(
+      'path' => array(
+        'values' => array(
+          'forum' => 'forum',
+        ),
+      ),
+    );
+    $context->reactions = array(
+      'metatag_context_reaction' => array(
+        'metatags' => array(
+          'title' => array(
+            'value' => '[current-page:title] | [site:name]',
+            'default' => '[current-page:title] | [site:name]',
+          ),
+        ),
+        'metatag_admin' => 1,
+      ),
+    );
+    $context->condition_mode = 1;
+    $defaults[$context->name] = $context;
+  }
+
+  if (module_exists('blog')) {
+    $context = new stdClass();
+    $context->disabled = TRUE; /* Edit this to true to make a default context disabled initially */
+    $context->api_version = 3;
+    $context->name = 'blog';
+    $context->description = 'A default Metatag:Context definition for the main blog page. This needs to be enabled and then it can be customized as needed. Note: this does not cover the individual user blog pages, only the main blog page.';
+    $context->tag = 'Metatag';
+    $context->conditions = array(
+      'path' => array(
+        'values' => array(
+          'blog' => 'blog',
+        ),
+      ),
+    );
+    $context->reactions = array(
+      'metatag_context_reaction' => array(
+        'metatags' => array(
+          'title' => array(
+            'value' => '[current-page:title] | [site:name]',
+            'default' => '[current-page:title] | [site:name]',
+          ),
+        ),
+        'metatag_admin' => 1,
+      ),
+    );
+    $context->condition_mode = 1;
+    $defaults[$context->name] = $context;
+  }
+
+  // Translatables
+  // Included for use with string extractors like potx.
+  t('Metatag');
+
+  return $defaults;
+}

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

@@ -0,0 +1,15 @@
+name = Metatag: Context
+description = "Assigned Metatag using Context definitions, allowing them to be assigned by path and other criteria."
+package = SEO
+core = 7.x
+dependencies[] = context
+dependencies[] = metatag
+files[] = metatag_context.test
+configure = admin/config/search/metatags/context
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

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

@@ -0,0 +1,13 @@
+<?php
+/**
+ * @file
+ * Installation and update hooks for Metatag:Context.
+ */
+
+/**
+ * Implements hook_enable().
+ */
+function metatag_context_enable() {
+  // Clear the cache so Context and CTools know about this plugin.
+  cache_clear_all('plugins:context:plugins', 'cache');
+}

+ 117 - 0
sites/all/modules/contrib/seo/metatag/metatag_context/metatag_context.module

@@ -0,0 +1,117 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag Context.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function metatag_context_menu() {
+  $items['admin/config/search/metatags/context'] = array(
+    'title' => 'By path',
+    'page callback' => 'metatag_context_context_overview',
+    'access arguments' => array('administer meta tags'),
+    'file' => 'metatag_context.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['admin/config/search/metatags/context/add'] = array(
+    'title' => 'Add a meta tag by path',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_context_config_add_form'),
+    'access arguments' => array('administer meta tags'),
+    'file' => 'metatag_context.admin.inc',
+    'type' => MENU_LOCAL_ACTION,
+  );
+  $items['admin/config/search/metatags/context/%context'] = array(
+    'title' => 'Configure metatags by path',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_context_config_edit_form', 5),
+    'access arguments' => array('administer meta tags'),
+    'file' => 'metatag_context.admin.inc',
+  );
+  $items['admin/config/search/metatags/context/%context/delete'] = array(
+    'title' => 'Delete metatags by path',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('metatag_context_delete_form', 5),
+    'access arguments' => array('administer meta tags'),
+    'file' => 'metatag_context.admin.inc',
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_context_plugins().
+ */
+function metatag_context_context_plugins() {
+  return array(
+    'metatag_context_reaction' => array(
+      'handler' => array(
+        'path' => drupal_get_path('module', 'metatag_context'),
+        'file' => 'metatag_context.context.inc',
+        'class' => 'metatag_context_reaction',
+        'parent' => 'context_reaction',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_context_registry().
+ */
+function metatag_context_context_registry() {
+  return array(
+    'reactions' => array(
+      'metatag_context_reaction' => array(
+        'title' => t('Meta Data'),
+        'description' => t('Control page meta tags using the Metatag module.'),
+        'plugin' => 'metatag_context_reaction',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_context_page_reaction().
+ */
+function metatag_context_context_page_reaction() {
+  if ($plugin = context_get_plugin('reaction', 'metatag_context_reaction')) {
+    $plugin->execute();
+  }
+}
+
+/**
+ * Implements hook_page_build().
+ */
+function metatag_context_page_build(&$page) {
+  // Load the meta tags that have been generated for this page.
+  $metatags = drupal_static('metatag_context', array());
+
+  if (!empty($metatags)) {
+    // The page region can be changed.
+    $region = variable_get('metatag_page_region', 'content');
+    $page[$region]['metatags']['global'] = $metatags;
+  }
+}
+
+/**
+ * Implements hook_preprocess_html().
+ */
+function metatag_context_preprocess_html(&$variables) {
+  $metadata = drupal_static('metatag_context');
+
+  if (isset($metadata['metadata_title'])) {
+    $variables['head_title'] = token_replace($metadata['metadata_title']);
+  }
+}
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_context_ctools_plugin_api() {
+  list($module, $api) = func_get_args();
+  if ($module == "context" && $api == "context") {
+    return array("version" => "3");
+  }
+}

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

@@ -0,0 +1,145 @@
+<?php
+/**
+ * @file
+ * Functional tests for the Metatag:Context module.
+ */
+
+class MetatagContextTestCase extends DrupalWebTestCase {
+  /**
+   * The getInfo() method provides information about the test.
+   * In order for the test to be run, the getInfo() method needs
+   * to be implemented.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Meta tag context tests',
+      'description' => 'Test basic meta tag context functionality.',
+      'group' => 'Metatag',
+    );
+  }
+
+  /**
+   * Prepares the testing environment
+   */
+  public function setUp(array $modules = array()) {
+    $modules[] = 'metatag';
+    $modules[] = 'metatag_context';
+    parent::setUp($modules);
+    // Create user.
+    $this->privileged_user = $this->drupalCreateUser(array(
+      'bypass node access',
+      'administer content types',
+      'administer meta tags',
+    ));
+    $this->drupalLogin($this->privileged_user);
+    // Create content type, with underscores.
+    $type_name = strtolower($this->randomName(8)) . '_test';
+    $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
+    $this->type = $type->type;
+    // Store a valid URL name, with hyphens instead of underscores.
+    $this->hyphen_type = str_replace('_', '-', $this->type);
+  }
+
+  /**
+   * Performs the basic tests.
+   */
+  public function testMetatagContextBasic() {
+    // Create content type node.
+    $this->drupalPost('node/add/' . $this->hyphen_type, array('title' => $this->randomName(8)), t('Save'));
+    $this->context_name = drupal_strtolower($this->randomName(8));
+
+    // Generate metatags and check content.
+    $this->metatag_pages['node'] = $this->createMetatagObject('node/1', 'node_metatags');
+    $this->metatag_pages['page'] = $this->createMetatagObject('<front>', 'frontpage_metatags');
+    foreach ($this->metatag_pages as $page) {
+      $this->generateMetatag($page);
+      $this->checkMetatags($page);
+    }
+
+    // Edit metatag and check content.
+    $this->metatag_pages['node']->title = 'New title';
+    $this->metatag_pages['node']->description = '';
+    $this->editMetatag($this->metatag_pages['node']);
+    $this->checkMetatags($this->metatag_pages['node']);
+
+  }
+
+  /**
+   * Creates a metatag object which can be used for generate and check
+   * the metatag_context module behavior.
+   *
+   * @param $path
+   *   Path where generate metatags.
+   * @param $identifier
+   *   Custom test to identify metatags in source code.
+   *
+   * @return $metatag_object
+   *   Metatag mapping object.
+   */
+  function createMetatagObject($path, $identifier) {
+    $metatag_object = new stdClass();
+    $metatag_object->name = drupal_strtolower($this->randomName(10));
+    $metatag_object->path = $path;
+    $metatag_object->title = "My $identifier title";
+    $metatag_object->description = "My $identifier description";
+    $metatag_object->abstract = "My $identifier abstract";
+    $metatag_object->keywords = "My $identifier keywords";
+
+    return $metatag_object;
+  }
+
+  /**
+   * Generates metatags by path from a metatag_object instance.
+   *
+   * @return $metatag_object
+   *   Metatag mapping object.
+   */
+  function generateMetatag($metatag_object) {
+    //Add new Metatag object by path.
+    $edit = array(
+      'name' => $metatag_object->name,
+    );
+    $this->drupalPost('admin/config/search/metatags/context/add', $edit, t('Add and configure'));
+    $this->editMetatag($metatag_object);
+  }
+
+  /**
+   * Edits metatags by path from a metatag_object instance.
+   *
+   * @return $metatag_object
+   *   Metatag mapping object.
+   */
+  function editMetatag($metatag_object) {
+    $edit_metatag = array(
+      'paths' => $metatag_object->path,
+      'metatags[und][title][value]' => $metatag_object->title,
+      'metatags[und][description][value]' => $metatag_object->description,
+      'metatags[und][abstract][value]' => $metatag_object->abstract,
+      'metatags[und][keywords][value]' => $metatag_object->keywords,
+    );
+    $this->drupalPost('admin/config/search/metatags/context/' . $metatag_object->name, $edit_metatag, t('Save'));
+  }
+
+  /**
+   * Checks if metatags has been added correctly from a metatag_object instance.
+   *
+   * @return $metatag_object
+   *   Metatag mapping object.
+   */
+  function checkMetatags($metatag_object) {
+    $options = array('description', 'abstract', 'keywords');
+    $this->drupalGet($metatag_object->path);
+
+    foreach ($options as $option) {
+      if (!empty($metatag_object->{$option})) {
+        $this->assertRaw($metatag_object->{$option}, $option . ' found in ' . $metatag_object->path);
+      }
+      else {
+        $this->assertNoRaw('<meta name="' . $option, $option . ' not found in ' . $metatag_object->path);
+      }
+    }
+    if (!empty($metatag_object->title)) {
+      $this->assertRaw($metatag_object->title, 'Title found in ' . $metatag_object->path);
+    }
+  }
+}

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

@@ -0,0 +1,79 @@
+Metatag: Dublin Core
+--------------------
+This module adds the fifteen Dublin Core Metadata Element Set [1] to the
+available meta tags, as defined by the Dublin Core Metadata Institute [2]; it
+also adds forty tags which may be useful for certain sites.
+
+The following tags are provided:
+* 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
+
+Additional DCMI Metadata Terms meta tags:
+* dcterms.abstract
+* dcterms.accessRights
+* dcterms.accrualMethod
+* dcterms.accrualPeriodicity
+* dcterms.accrualPolicy
+* dcterms.alternative
+* dcterms.audience
+* dcterms.available
+* dcterms.bibliographicCitation
+* dcterms.conformsTo
+* dcterms.created
+* dcterms.dateAccepted
+* dcterms.dateCopyrighted
+* dcterms.dateSubmitted
+* dcterms.educationLevel
+* dcterms.extent
+* dcterms.hasFormat
+* dcterms.hasPart
+* dcterms.hasVersion
+* dcterms.instructionalMethod
+* dcterms.isFormatOf
+* dcterms.isPartOf
+* dcterms.isReferencedBy
+* dcterms.isReplacedBy
+* dcterms.isRequiredBy
+* dcterms.isVersionOf
+* dcterms.issued
+* dcterms.license
+* dcterms.mediator
+* dcterms.medium
+* dcterms.modified
+* dcterms.provenance
+* dcterms.references
+* dcterms.replaces
+* dcterms.requires
+* dcterms.rightsHolder
+* dcterms.spatial
+* dcterms.tableOfContents
+* dcterms.temporal
+* dcterms.valid
+
+
+Credits
+------------------------------------------------------------------------------
+The initial development was by Marty2081 [3] (sponsored by Gemeentemuseum Den
+Haag. [4]), with contributions by many in the community [5].
+
+
+References
+------------------------------------------------------------------------------
+1: http://dublincore.org/documents/dces/
+2: http://www.dublincore.org/
+3: https://www.drupal.org/u/marty2081
+4: http://www.gemeentemuseum.nl/
+5: https://www.drupal.org/node/1491616

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

@@ -0,0 +1,12 @@
+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
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

Файловите разлики са ограничени, защото са твърде много
+ 185 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.metatag.inc


+ 40 - 0
sites/all/modules/contrib/seo/metatag/metatag_dc/metatag_dc.module

@@ -0,0 +1,40 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the metatag_dc module.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_dc_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function metatag_dc_theme() {
+  $info['metatag_dc'] = array(
+    'render element' => 'element',
+  );
+
+  return $info;
+}
+
+/**
+ * Theme callback for a Dublin Core meta tag.
+ */
+function theme_metatag_dc($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#name' => 'name',
+    '#schema' => 'schema',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}

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

@@ -0,0 +1,13 @@
+name = Metatag:Devel
+description = Provides development / debugging functionality for the Metatag module. Integrates with Devel Generate.
+package = Development
+core = 7.x
+tags[] = developer
+dependencies[] = metatag
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

+ 181 - 0
sites/all/modules/contrib/seo/metatag/metatag_devel/metatag_devel.module

@@ -0,0 +1,181 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag:Devel.
+ */
+
+/**
+ * Implements hook_form_FORM_ID_alter() for devel_generate_content_form.
+ *
+ * Add extra options.
+ */
+function metatag_devel_form_devel_generate_content_form_alter(&$form, &$form_state, $form_id) {
+  $form['metatag_skip'] = array(
+    '#type' => 'select',
+    '#title' => t('Skip some meta tag values'),
+    '#options' => array(
+      0 => t("All meta tags given a value"),
+      2 => t('Every 2nd value'),
+      3 => t('Every 3rd value'),
+      4 => t('Every 4th value'),
+      5 => t('Every 5th value'),
+    ),
+    '#default' => 0,
+    '#description' => t('Control whether all of the meta tags will be given values, or .'),
+  );
+
+  // Move the submit button to after the rest of the fields.
+  $form['submit']['#weight'] = 100;
+}
+
+/**
+ * Implements hook_node_insert().
+ *
+ * Integrate with Devel Generate.
+ */
+function metatag_devel_node_insert($node) {
+  // Check to see if the node is generated by Devel Generate.
+  if (isset($node->devel_generate)) {
+    $metatags = array();
+
+    // Max length of words.
+    $title_length = $node->devel_generate['title_length'];
+
+    // Pregenerate the URL.
+    $url = url('<front>', array('absolute' => TRUE));
+
+    // Types of images.
+    $image_types = array('jpg' => 'jpg', 'png' => 'png', 'gif' => 'gif');
+
+    // Optionally skip some meta tags.
+    $skip = $node->devel_generate['metatag_skip'];
+
+    // Generate some meta tags.
+    $count = 0;
+    foreach (metatag_get_info('tags') as $tag => $tag_info) {
+      // Optionally skip records.
+      if (empty($skip) || $count == 0 || (($count % $skip) == 0)) {
+        // Default to ten words per tag.
+        $maxlength = 10;
+
+        // By default, just assume a simple text value.
+        $type = 'text';
+        if (strpos($tag, 'title') !== FALSE) {
+          $maxlength = $title_length;
+        }
+
+        // Allow tags to customize the generation settings.
+        if (!empty($tag_info['devel_generate'])) {
+          if (!empty($tag_info['devel_generate']['type'])) {
+            $type = $tag_info['devel_generate']['type'];
+          }
+          if (!empty($tag_info['devel_generate']['maxlength'])) {
+            $maxlength = $tag_info['devel_generate']['maxlength'];
+          }
+        }
+
+        // Work out how to handle possibly more complicated meta tags.
+        elseif (isset($tag_info['form']) && is_array($tag_info['form'])) {
+          // Textarea fields can be longer than other tags.
+          if (isset($tag_info['form']['#type']) && $tag_info['form']['#type'] == 'textarea') {
+            $maxlength = 20;
+          }
+          // Anything with an '#options' value will have one item picked at
+          // random.
+          elseif (isset($tag_info['form']['#options'])) {
+            $type = 'select';
+          }
+        }
+
+        // Simple values.
+        if ($type == 'text') {
+          $metatags[$tag]['value'] = devel_create_greeking($maxlength, TRUE);
+        }
+
+        // Select lists, pick a value at random.
+        elseif ($type == 'select') {
+          // Nested arrays, aka opgroups - collapse it down to one level.
+          if (is_array($tag_info['form']['#options'])) {
+            $options = array();
+            foreach ($tag_info['form']['#options'] as $option => $subopts) {
+              if (is_array($subopts)) {
+                $options += array_keys($subopts);
+              }
+              else {
+                $options[] = $option;
+              }
+            }
+          }
+          else {
+            $options = $tag_info['form']['#options'];
+          }
+          $metatags[$tag]['value'] = array_rand(drupal_map_assoc($options));
+
+          // Support checkboxes, which require the default value be an array.
+          if ($tag_info['class'] == 'DrupalListMetaTag') {
+            $metatags[$tag]['value'] = array($metatags[$tag]['value']);
+          }
+        }
+
+        // URL values.
+        elseif ($type == 'url') {
+          $metatags[$tag]['value'] = $url . strtolower(str_replace(' ', '/', devel_create_greeking($maxlength, TRUE)));
+        }
+
+        // Image URL values.
+        elseif ($type == 'image') {
+          $filepath = strtolower(str_replace(' ', '/', devel_create_greeking($maxlength, TRUE)));
+          $ext = array_rand($image_types);
+          $metatags[$tag]['value'] = $url . $filepath . '.' . $ext;
+        }
+
+        // Integers, generate an integer between 0 and 999.
+        elseif ($type == 'integer') {
+          $metatags[$tag]['value'] = rand(0, 999);
+        }
+
+        // Floats, generate an integer between 0 and 999.
+        elseif ($type == 'float') {
+          $metatags[$tag]['value'] = rand(0, 999) . '.' . rand(0, 999);
+        }
+
+        // Phone numbers will be given the US format of XXX-XXX-XXXX.
+        elseif ($type == 'phone') {
+          $metatags[$tag]['value'] = rand(100, 999) . '-' . rand(100, 999) . '-' . rand(1000, 9999);
+        }
+
+        // Email addresses.
+        elseif ($type == 'email') {
+          $metatags[$tag]['value'] = strtolower(devel_create_greeking(1, TRUE) . '@' . devel_create_greeking(1, TRUE) . '.com');
+        }
+
+        // Canonical URL values - just fill in the absolute URL for the current
+        // page.
+        elseif ($type == 'canonical') {
+          $metatags[$tag]['value'] = '[current-page:url:absolute]';
+        }
+
+        // Twitter usernames.
+        elseif ($type == 'twitter') {
+          $metatags[$tag]['value'] = '@' . devel_create_greeking(1, TRUE);
+        }
+
+        // Replace spaces in keyword fields with commas.
+        if (strpos($tag, 'keyword') !== FALSE) {
+          $metatags[$tag]['value'] = str_replace(' ', ',', $metatags[$tag]['value']);
+        }
+      }
+
+      // Bump the counter.
+      $count++;
+    }
+
+    // Adjust the values for the nested language structure.
+    $metatags = array(
+      $node->language => $metatags,
+    );
+
+    // Save the meta tags.
+    metatag_metatags_save('node', $node->nid, $node->vid, $metatags, $node->language);
+  }
+}

+ 12 - 0
sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.info

@@ -0,0 +1,12 @@
+name = Metatag: Facebook
+description = "Provides support for Facebook's custom meta tags."
+package = SEO
+core = 7.x
+dependencies[] = metatag
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

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

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

+ 54 - 0
sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.metatag.inc

@@ -0,0 +1,54 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the Metatag:Facebook module.
+ */
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_facebook_metatag_info() {
+  $info['groups']['facebook'] = array(
+    'label' => t('Facebook'),
+    'description' => t("Meta tags used to integrate with Facebook's APIs. Most sites do not need to use these, they are primarily of benefit for sites using either the Facebook widgets, the Facebook Connect single-signon system, or are using Facebook's APIs in a custom way. Sites that do need these meta tags usually will only need to set them globally."),
+    'form' => array(
+      '#weight' => 55,
+    ),
+  );
+
+  // Facebook meta tags stack after the simple tags.
+  $weight = 20;
+
+  // Default values for each meta tag.
+  $tag_info_defaults = array(
+    'description' => '',
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'facebook',
+    'element' => array(
+      '#theme' => 'metatag_property',
+    ),
+  );
+
+  $info['tags']['fb:admins'] = array(
+    'label' => t('Admins'),
+    'description' => t('A comma-separated list of Facebook user IDs of people who are considered administrators or moderators of this page.'),
+    'weight' => ++$weight,
+  ) + $tag_info_defaults;
+
+  $info['tags']['fb:app_id'] = array(
+    'label' => t('Application ID'),
+    'description' => t('A comma-separated list of Facebook Platform Application IDs applicable for this site.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'integer',
+    ),
+  ) + $tag_info_defaults;
+
+  // If the FB_Social module is installed already, disable the app_id field.
+  if (module_exists('fb_social')) {
+    $info['tags']['fb:app_id']['form']['#disabled'] = TRUE;
+    $info['tags']['fb:app_id']['form']['#description'] = t('The FB_Social module will automatically output this meta tag, go to the <a href="!fb_social">FB_Social settings page</a> to customize it.', array('!fb_social' => url('admin/config/user-interface/fb_social')));
+  }
+
+  return $info;
+}

+ 26 - 0
sites/all/modules/contrib/seo/metatag/metatag_facebook/metatag_facebook.module

@@ -0,0 +1,26 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag:Facebook.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_facebook_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_preprocess_html().
+ */
+function metatag_facebook_preprocess_html(&$variables) {
+  $variables['rdf_namespaces'] .= "\n  xmlns:fb=\"http://ogp.me/ns/fb#\"";
+}
+
+/*
+fb:admins
+fb:app_id
+*/

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

@@ -0,0 +1,60 @@
+Metatag: Google+
+-----------------
+This module adds support for meta tag configuration for Google+ Snippet [1].
+
+The following Google+ tags are provided:
+
+* itemprop:name
+* itemprop:description
+* itemprop:image
+
+Also itemtype is provided to add schema in the HTML markup as follows:
+
+<html itemscope itemtype="http://schema.org/Article">
+
+
+Usage
+--------------------------------------------------------------------------------
+Page type (itemtype) provides default type options from the Google+ Snippet page
+[1]; to add other types either install select_or_other module [2] or use the
+Metatag hooks (see metatag.api.php).
+
+
+Known Issues
+--------------------------------------------------------------------------------
+- When using Zen or its derived theme, the RDF Namespaces will be serialized
+  into an RDFa 1.1 prefix attribute, which means itemtype will be included in
+  prefix="...". To avoid this problem, this module will not add a itemtype
+  directly to $variable['rdf_namespaces'], instead, it will be necessary to add
+  code manually in the template.php or the custom theme.
+
+  Example code to use and adapt as needed:
+
+/**
+ * Implements template_preprocess_html().
+ *
+ * Add itemtype code for Google+ in the 'html_attributes_array' which is only
+ * available in Zen theme. Note Zen will convert rdf_namespaces to RDFa 1.1 with
+ * prefix, so putting itemtype there will cause it to be added to the
+ * prefix="itemtype=..." attribute.
+ *
+ * @see zen_preprocess_html()
+ */
+function MYTHEME_preprocess_html(&$variables, $hook) {
+  if (module_exists('metatag_google_plus') && isset($variables['itemtype'])) {
+    $variables['html_attributes_array']['itemscope itemtype'] = "http://schema.org/{$variables['itemtype']}";
+  }
+}
+
+
+Credits / Contact
+--------------------------------------------------------------------------------
+Originally developed by Eric Chen [3] and sponsored by Monkii [4].
+
+
+References
+--------------------------------------------------------------------------------
+1: https://developers.google.com/+/web/snippet/
+2. https://drupal.org/project/select_or_other
+3: https://drupal.org/user/265729
+4: http://monkii.com

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

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @file
+ * Metatag controller for Google+.
+ */
+
+/**
+ * Schema meta tag controller.
+ *
+ * This extends DrupalTextMetaTag as we need to alter variables in
+ * template_preprocess_html() rather output a normal meta tag.
+ *
+ * This controller is little different from DrupalTitleMetaTag and same value in
+ * the itemtype instead of title. So in the MYTHEME_preprocess_html(),
+ * $variable['itemtype'] will be provided.
+ */
+class DrupalSchemaMetaTag extends DrupalTextMetaTag {
+
+  public function getElement(array $options = array()) {
+    $element = array();
+    $value = $this->getValue($options);
+    $element['#attached']['metatag_set_preprocess_variable'][] = array('html', 'itemtype', $value);
+    return $element;
+  }
+}

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

@@ -0,0 +1,15 @@
+name = Metatag: Google+
+description = "Provides support for Google+ 'itemscope' meta tags."
+package = SEO
+core = 7.x
+
+dependencies[] = metatag
+
+files[] = metatag_google_plus.inc
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

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

@@ -0,0 +1,139 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the Metatag Google+ module.
+ */
+
+/**
+ * Implements hook_metatag_bundled_config_alter().
+ */
+function metatag_google_plus_metatag_bundled_config_alter(array &$configs) {
+  foreach ($configs as &$config) {
+    switch ($config->instance) {
+      case 'global':
+        $config->config += array(
+          'itemprop:name' => array('value' => '[current-page:title]'),
+        );
+        break;
+
+      case 'global:frontpage':
+        $config->config += array(
+          'itemprop:name' => array('value' => '[site:name]'),
+          'itemprop:description' => array('value' => '[site:slogan]'),
+        );
+        break;
+
+      // On error pages point everything to the homepage.
+      case 'global:403':
+      case 'global:404':
+        $config->config += array(
+          'itemprop:name' => array('value' => '[site:name]'),
+        );
+        break;
+
+      case 'node':
+        $config->config += array(
+          'itemprop:description' => array('value' => '[node:summary]'),
+          'itemprop:name' => array('value' => '[node:title]'),
+          'itemtype' => array('value' => 'Article'),
+        );
+        break;
+
+      case 'taxonomy_term':
+        $config->config += array(
+          'itemprop:description' => array('value' => '[term:description]'),
+          'itemprop:name' => array('value' => '[term:name]'),
+        );
+        break;
+
+      case 'user':
+        $config->config += array(
+          'itemprop:name' => array('value' => '[user:name]'),
+          'itemtype' => array('value' => 'Person'),
+        );
+        if (variable_get('user_pictures')) {
+          $config->config += array(
+            'itemprop:image' => array('value' => '[user:picture:url]'),
+          );
+        }
+        break;
+    }
+  }
+}
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_google_plus_metatag_info() {
+  $info['groups']['google-plus'] = array(
+    'label' => t('Google+'),
+    'description' => t('A set of meta tags specially for controlling the summaries displayed when content is shared on <a href="!url">Google+</a>.', array('!url' => 'https://plus.google.com/')),
+    'form' => array(
+      '#weight' => 80,
+    ),
+  );
+
+  // Google+ meta tags stack after the Twitter Cards tags.
+  $weight = 60;
+
+  // Defaults used for all cards.
+  $defaults = array(
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'google-plus',
+    'element' => array(
+      '#theme' => 'metatag_google_plus',
+    ),
+  );
+
+  $info['tags']['itemtype'] = array(
+    'label' => t('Page type'),
+    'description' => t('Schema type. <a href="!url">More schema info</a>. If your page type does not exist in options above, please install <a href="!url2">select_or_other</a> module to enter page type manually.', array('!url' => 'http://schema.org/docs/schemas.html', '!url2' => 'https://drupal.org/project/select_or_other')),
+    'class' => 'DrupalSchemaMetaTag',
+    'weight' => ++$weight,
+    'form' => array(
+      '#type' => 'select',
+      '#options' => array(
+        'Article' => t('Article'),
+        'Blog' => t('Blog'),
+        'Book' => t('Book'),
+        'Event' => t('Event'),
+        'LocalBusiness' => t('Local Business'),
+        'Organization' => t('Organization'),
+        'Person' => t('Person'),
+        'Product' => t('Product'),
+        'Review' => t('Review'),
+      ),
+      '#empty_option' => t('- None -'),
+    ),
+  ) + $defaults;
+
+  if (module_exists('select_or_other')) {
+    $info['tags']['itemtype']['form']['#type'] = 'select_or_other';
+    $info['tags']['itemtype']['form']['#other'] = 'Other (please type a value)';
+    $info['tags']['itemtype']['form']['#multiple'] = FALSE;
+    $info['tags']['itemtype']['form']['#other_unknown_defaults'] = 'other';
+    $info['tags']['itemtype']['form']['#select_type'] = 'select';
+    $info['tags']['itemtype']['form']['#element_validate'] = array('select_or_other_element_validate');
+  }
+
+  $info['tags']['itemprop:name'] = array(
+    'label' => t('Title'),
+    'description' => t('A Google+ title for the page being shared. Keep keywords towards the front.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+  $info['tags']['itemprop:description'] = array(
+    'label' => t('Description'),
+    'description' => t('Longer form description, you’ve 200 words here that can specifically reference your presence on Google+'),
+    'weight' => ++$weight,
+  ) + $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. '),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'image',
+    ),
+  ) + $defaults;
+
+  return $info;
+}

+ 64 - 0
sites/all/modules/contrib/seo/metatag/metatag_google_plus/metatag_google_plus.module

@@ -0,0 +1,64 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag: Google+.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_google_plus_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function metatag_google_plus_theme() {
+  $info['metatag_google_plus'] = array(
+    'render element' => 'element',
+  );
+
+  return $info;
+}
+
+/**
+ * Theme callback for an Google+ meta tag.
+ *
+ * The format is:
+ * <meta itemprop="[itemprop]" content="[value]" />
+ */
+function theme_metatag_google_plus($variables) {
+  $element = &$variables['element'];
+
+  // The format is e.g. 'itemprop:name'. Remove 'itemprop:' and store the rest
+  // in '#itemprop'.
+  $element['#itemprop'] = substr($element['#name'], 9);
+  $args = array(
+    '#itemprop' => 'itemprop',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+
+  return theme('html_tag', $variables);
+}
+
+/**
+ * Implements hook_preprocess_html().
+ *
+ * Add itemtype when available.
+ *
+ * We will not add itemtype in the rdf_namespaces when using Zen and its derived
+ * themes as Zen will serialize RDF Namespaces into an RDFa 1.1 prefix
+ * attribute, which means itemtype will be included in prefix="...".
+ *
+ * @see zen_preprocess_html()
+ */
+function metatag_google_plus_preprocess_html(&$variables) {
+  if (isset($variables['itemtype']) && !function_exists('zen_preprocess_html')) {
+    $variables['rdf_namespaces'] .= "\n  itemscope itemtype= \"http://schema.org/{$variables['itemtype']}\"";
+  }
+}

+ 12 - 0
sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.info

@@ -0,0 +1,12 @@
+name = Metatag:OpenGraph
+description = Provides support for Open Graph Protocol meta tags.
+package = SEO
+core = 7.x
+dependencies[] = metatag
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

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

@@ -0,0 +1,65 @@
+<?php
+/**
+ * @file
+ * Installation and update scripts for Metatag:OpenGraph.
+ */
+
+/**
+ * Implements hook_requirements().
+ */
+function metatag_opengraph_requirements($phase) {
+  $requirements = array();
+  // Ensure translations don't break during installation.
+  $t = get_t();
+
+  if ($phase == 'runtime') {
+    if (module_exists('rdf')) {
+      // Work out the release of D7 that is currently running.
+      list($major, $minor) = explode('.', VERSION);
+      // Strip off any suffixes on the version string, e.g. "17-dev".
+      if (strpos('-', $minor)) {
+        list($minor, $suffix) = explode('-', $minor);
+      }
+
+      if ($minor < 33) {
+        $requirements['metatag_og_rdf'] = array(
+          'severity' => REQUIREMENT_WARNING,
+          'title' => 'Metatag:OpenGraph',
+          'value' => $t('RDF problems with Drupal core releases before v7.33'),
+          'description' => $t('The core RDF module in Drupal before v7.33 caused validation problems for Open Graph meta tags. The solution is to update to v7.33 or newer.'),
+        );
+      }
+    }
+  }
+
+  return $requirements;
+}
+
+/**
+ * Implements hook_install().
+ */
+function metatag_opengraph_install() {
+  // Display a warning about possible conflicts with the RDF module.
+  if (module_exists('rdf')) {
+    drupal_set_message(t('The core RDF module is known to cause validation problems for Open Graph meta tags. Unless it is actually needed for the site, it may be worthwhile to disable the RDF module to avoid any possible problems for the Open Graph integration.'));
+  }
+}
+
+/**
+ * Implementations of hook_update_N().
+ */
+
+/**
+ * Enable the new Metatag:Facebook submodule.
+ */
+function metatag_opengraph_update_7100() {
+  module_enable(array('metatag_facebook'));
+  drupal_set_message(t('Enabled the new Metatag:Facebook submodule. If the Facebook meta tags are not being used then it is safe to disable.'));
+}
+
+/**
+ * Leave a warning about the two og:type value changes.
+ */
+function metatag_opengraph_update_7101() {
+  drupal_set_message(t('The "Movie" and "TV Show" values for the "Content type" open graph meta tag changed, if this site used those values they will need to be manually updated.'));
+}

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

@@ -0,0 +1,631 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the metatag_opengraph module.
+ */
+
+/**
+ * Implements hook_metatag_bundled_config_alter().
+ */
+function metatag_opengraph_metatag_bundled_config_alter(array &$configs) {
+  foreach ($configs as &$config) {
+    switch ($config->instance) {
+      case 'global':
+        $config->config += array(
+          'og:site_name' => array('value' => '[site:name]'),
+          'og:title' => array('value' => '[current-page:title]'),
+          'og:type' => array('value' => 'article'),
+          'og:url' => array('value' => '[current-page:url:absolute]'),
+        );
+        break;
+
+      case 'global:frontpage':
+        $config->config += array(
+          'og:description' => array('value' => '[site:slogan]'),
+          'og:title' => array('value' => '[site:name]'),
+          'og:type' => array('value' => 'website'),
+          'og:url' => array('value' => '[site:url]'),
+        );
+        break;
+
+      // On error pages point everything to the homepage.
+      case 'global:403':
+      case 'global:404':
+        $config->config += array(
+          'og:title' => array('value' => '[site:name]'),
+          'og:type' => array('value' => 'website'),
+          'og:url' => array('value' => '[site:url]'),
+        );
+        break;
+
+      case 'node':
+        $config->config += array(
+          'article:modified_time' => array('value' => '[node:changed:custom:c]'),
+          'article:published_time' => array('value' => '[node:created:custom:c]'),
+          'og:description' => array('value' => '[node:summary]'),
+          'og:title' => array('value' => '[node:title]'),
+          'og:updated_time' => array('value' => '[node:changed:custom:c]'),
+        );
+        break;
+
+      case 'taxonomy_term':
+        $config->config += array(
+          'og:description' => array('value' => '[term:description]'),
+          'og:title' => array('value' => '[term:name]'),
+        );
+        break;
+
+      case 'user':
+        $config->config += array(
+          'og:title' => array('value' => '[user:name]'),
+          'og:type' => array('value' => 'profile'),
+          'profile:username' => array('value' => '[user:name]'),
+        );
+        if (variable_get('user_pictures')) {
+          $config->config += array(
+            'og:image' => array('value' => '[user:picture:url]'),
+            // For now keep the old default.
+            // 'og:image:url' => array('value' => '[user:picture:url]'),
+          );
+        }
+        break;
+    }
+  }
+}
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_opengraph_metatag_info() {
+  $info['groups']['open-graph'] = array(
+    'label' => t('Open Graph'),
+    'description' => t("The <a href=\"@ogp\">Open Graph meta tags</a> are used control how Facebook, Pinterest, LinkedIn and other social networking sites interpret the site's content.", array('@ogp' => 'http://ogp.me/')),
+    'form' => array(
+      '#weight' => 50,
+    ),
+  );
+
+  // Default values for each meta tag.
+  $og_defaults = array(
+    'description' => '',
+    'class' => 'DrupalTextMetaTag',
+    'group' => 'open-graph',
+    'element' => array(
+      '#theme' => 'metatag_property',
+    ),
+  );
+
+  // Open Graph meta tags stack after the Facebook tags.
+  $weight = 25;
+
+  $info['tags']['og:site_name'] = array(
+    'label' => t('Site name'),
+    'description' => t('A human-readable name for the site, e.g., <em>IMDb</em>.'),
+    'context' => array('global'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+
+  $info['tags']['og:type'] = array(
+    'label' => t('Content type'),
+    'description' => t('The type of the content, e.g., <em>movie</em>.'),
+    'weight' => ++$weight,
+    'form' => array(
+      '#type' => 'select',
+      '#options' => _metatag_opengraph_type_options(),
+      '#empty_option' => t('- None -'),
+    ),
+    'devel_generate' => array(
+      'type' => 'select',
+    ),
+  ) + $og_defaults;
+
+  if (module_exists('select_or_other')) {
+    // Enhance the og:type field to support custom types.
+    $info['tags']['og:type']['form']['#type'] = 'select_or_other';
+    $info['tags']['og:type']['form']['#other'] = t('Other (please type a value)');
+    $info['tags']['og:type']['form']['#other_unknown_defaults'] = 'other';
+    $info['tags']['og:type']['form']['#theme'] = 'select_or_other';
+    $info['tags']['og:type']['form']['#element_validate'] = array('select_or_other_element_validate');
+  }
+
+  $info['tags']['og:url'] = array(
+    'label' => t('Page URL'),
+    'description' => t('Preferred page location or URL to help eliminate duplicate content for search engines, e.g., <em>http://www.imdb.com/title/tt0117500/</em>.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'canonical',
+    ),
+  ) + $og_defaults;
+
+  $info['tags']['og:title'] = array(
+    'label' => t('Content title'),
+    'description' => t('The title of the content, e.g., <em>The Rock</em>.'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+
+  $info['tags']['og:determiner'] = array(
+    'label' => t('Content title determiner'),
+    'description' => t("The word that appears before the content's title in a sentence. The default ignores this value, the 'Automatic' value should be sufficient if this is actually needed."),
+    'weight' => ++$weight,
+    'form' => array(
+      '#type' => 'select',
+      '#options' => array(
+        'auto' => 'Automatic',
+        'a' => 'A',
+        'an' => 'An',
+        'the' => 'The',
+      ),
+      '#empty_option' => t('- Ignore -'),
+    ),
+    'devel_generate' => array(
+      'type' => 'select',
+    ),
+  ) + $og_defaults;
+
+  $info['tags']['og:description'] = array(
+    'label' => t('Content description'),
+    'description' => t('A one to two sentence description of the content.'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+
+  // Basic tags.
+  $info['tags']['og:updated_time'] = array(
+    'label' => t('Content modification date & time'),
+    'description' => t("The date this content was last modified, with an optional time value. Needs to be in <a href='http://en.wikipedia.org/wiki/ISO_8601'>ISO 8601</a> format. Can be the same as the 'Article modification date' tag."),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+
+  $info['tags']['og:see_also'] = array(
+    'label' => t('See also'),
+    'description' => t('URLs to related content.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults;
+
+  $info['tags']['og:image'] = array(
+    'label' => t('Image'),
+    'description' => t('The URL of an image which should represent the content. For best results use an image that is at least 1200 x 630 pixels in size, but at least 600 x 316 pixels is a recommended minimum. Supports PNG, JPEG and GIF formats. Should not be used if og:image:url is used.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'image',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:image:url'] = array(
+    'label' => t('Image URL'),
+    'description' => t('A alternative version of og:image and has exactly the same requirements; only one needs to be used.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'image',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:image:secure_url'] = array(
+    'label' => t('Secure image URL'),
+    'description' => t('The secure URL (HTTPS) of an image which should represent the content. The image must be at least 50px by 50px and have a maximum aspect ratio of 3:1. Supports PNG, JPEG and GIF formats. All "http://" URLs will automatically be converted to "https://".'),
+    'multiple' => TRUE,
+    'secure' => TRUE,
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'image',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:image:type'] = array(
+    'label' => t('Image type'),
+    'description' => t('The type of image referenced above. Should be either "image/gif" for a GIF image, "image/jpeg" for a JPG/JPEG image, or "image/png" for a PNG image.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults;
+  $info['tags']['og:image:width'] = array(
+    'label' => t('Image width'),
+    'description' => t('The width of the above image(s). Note: if both the unsecured and secured images are provided, they should both be the same size.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'image',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:image:height'] = array(
+    'label' => t('Image height'),
+    'description' => t('The height of the above image(s). Note: if both the unsecured and secured images are provided, they should both be the same size.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'image',
+    ),
+  ) + $og_defaults;
+
+  $info['tags']['og:latitude'] = array(
+    'label' => t('Latitude'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'float',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:longitude'] = array(
+    'label' => t('Longitude'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'float',
+    ),
+  ) + $og_defaults;
+
+  $info['tags']['og:street-address'] = array(
+    'label' => t('Street address'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+  $info['tags']['og:locality'] = array(
+    'label' => t('Locality'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+  $info['tags']['og:region'] = array(
+    'label' => t('Region'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+  $info['tags']['og:postal-code'] = array(
+    'label' => t('Postal/ZIP code'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+  $info['tags']['og:country-name'] = array(
+    'label' => t('Country name'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+
+  $info['tags']['og:email'] = array(
+    'label' => t('Email'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'email',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:phone_number'] = array(
+    'label' => t('Phone number'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'phone',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:fax_number'] = array(
+    'label' => t('Fax number'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'phone',
+    ),
+  ) + $og_defaults;
+
+  $info['tags']['og:locale'] = array(
+    'label' => t('Locale'),
+    'description' => 'The locale these tags are marked up in, must be in the format language_TERRITORY. Default is en_US.',
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'maxlength' => 1,
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:locale:alternate'] = array(
+    'label' => t('Alternative locales'),
+    'description' => 'Other locales this content is available in, must be in the format language_TERRITORY, e.g. "fr_FR".',
+    'weight' => ++$weight,
+    'multiple' => TRUE,
+    'devel_generate' => array(
+      'maxlength' => 1,
+    ),
+  ) + $og_defaults;
+
+  // For "product" og:type.
+  $info['tags']['product:price:amount'] = array(
+    'label' => t('Price'),
+    'description' => t("For products, the numeric price with decimal point, without currency indicator. Values below 0.01 may not be supported by clients."),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+  $info['tags']['product:price:currency'] = array(
+    'label' => t('Currency'),
+    'description' => t("For products, the currency for the price (if any)."),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+
+  // For the "article" og:type.
+  $article_defaults = array(
+    'dependencies' => array(
+      array(
+        'dependency' => 'og:type',
+        'attribute' => 'value',
+        'condition' => 'value',
+        'value' => 'article',
+      ),
+    ),
+  );
+  $info['tags']['article:author'] = array(
+    'label' => t('Article author'),
+    'description' => t("Links an article to an author's Facebook profile, should be either URLs to the author's profile page or their Facebook profile IDs."),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults + $article_defaults;
+  $info['tags']['article:publisher'] = array(
+    'label' => t('Article publisher'),
+    'description' => t("Links an article to a publisher's Facebook page."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $article_defaults;
+  $info['tags']['article:section'] = array(
+    'label' => t('Article section'),
+    'description' => t('The primary section of this website the content belongs to.'),
+    'weight' => ++$weight,
+  ) + $og_defaults + $article_defaults;
+  $info['tags']['article:tag'] = array(
+    'label' => t('Article tag(s)'),
+    'description' => t('Appropriate keywords for this content.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults + $article_defaults;
+  $info['tags']['article:published_time'] = array(
+    'label' => t('Article publication date & time'),
+    'description' => t("The date this content was published on, with an optional time value. Needs to be in <a href='http://en.wikipedia.org/wiki/ISO_8601'>ISO 8601</a> format."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $article_defaults;
+  $info['tags']['article:modified_time'] = array(
+    'label' => t('Article modification date & time'),
+    'description' => t("The date this content was last modified, with an optional time value. Needs to be in <a href='http://en.wikipedia.org/wiki/ISO_8601'>ISO 8601</a> format."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $article_defaults;
+  $info['tags']['article:expiration_time'] = array(
+    'label' => t('Article expiration date & time'),
+    'description' => t("The date this content will expire, with an optional time value. Needs to be in <a href='http://en.wikipedia.org/wiki/ISO_8601'>ISO 8601</a> format."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $article_defaults;
+
+  // For the "profile" og:type.
+  $profile_defaults = array(
+    'dependencies' => array(
+      array(
+        'dependency' => 'og:type',
+        'attribute' => 'value',
+        'condition' => 'value',
+        'value' => 'profile',
+      ),
+    ),
+  );
+  $info['tags']['profile:first_name'] = array(
+    'label' => t('First name'),
+    'description' => t("The first name of the person who's Profile page this is."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $profile_defaults;
+  $info['tags']['profile:last_name'] = array(
+    'label' => t('Last name'),
+    'description' => t("The person's last name."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $profile_defaults;
+  $info['tags']['profile:username'] = array(
+    'label' => t('Username'),
+    'description' => t("A pseudonym / alias of this person."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $profile_defaults;
+  $info['tags']['profile:gender'] = array(
+    'label' => t('Gender'),
+    'description' => t("Any of Facebook's gender values should be allowed, the initial two being 'male' and 'female'."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $profile_defaults;
+
+  // Tags related to audio.
+  $info['tags']['og:audio'] = array(
+    'label' => t('Audio URL'),
+    'description' => t('The URL to an audio file that complements this object.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:audio:secure_url'] = array(
+    'label' => t('Audio secure URL'),
+    'description' => t('The secure URL to an audio file that complements this object. All "http://" URLs will automatically be converted to "https://".'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:audio:type'] = array(
+    'label' => t('Audio type'),
+    'description' => t('The MIME type of the audio file. Examples include "application/mp3" for an MP3 file.'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+
+  // For the "book" og:type.
+  $book_defaults = array(
+    'dependencies' => array(
+      array(
+        'dependency' => 'og:type',
+        'attribute' => 'value',
+        'condition' => 'value',
+        'value' => 'book',
+      ),
+    ),
+  );
+  $info['tags']['book:author'] = array(
+    'label' => t("Book's author"),
+    'description' => t("Links to the book's author's Facebook profile, should be either URLs to the author's profile page or their Facebook profile IDs."),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults + $book_defaults;
+  $info['tags']['book:isbn'] = array(
+    'label' => t("Book's ISBN"),
+    'description' => t("The book's <a href=\"http://en.wikipedia.org/wiki/International_Standard_Book_Number\">International Standard Book Number</a>, which may be in one of several formats."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $book_defaults;
+  $info['tags']['book:release_date'] = array(
+    'label' => t('Book release date'),
+    'description' => t("The date this content will expire, with an optional time value. Needs to be in <a href='http://en.wikipedia.org/wiki/ISO_8601'>ISO 8601</a> format."),
+    'weight' => ++$weight,
+  ) + $og_defaults + $book_defaults;
+  $info['tags']['book:tag'] = array(
+    'label' => t('Book tags'),
+    'description' => t('Appropriate keywords for this book.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults + $book_defaults;
+
+  // For the "video" og:type.
+  $video_defaults = array();
+  //   'dependencies' => array(
+  //     array(
+  //       'dependency' => 'og:type',
+  //       'attribute' => 'value',
+  //       'condition' => 'value',
+  //       'value' => 'profile',
+  //     ),
+  //   ),
+  // );
+  $info['tags']['og:video'] = array(
+    'label' => t('Video URL'),
+    'description' => t('The URL to a video file that complements this object.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:video:secure_url'] = array(
+    'label' => t('Video secure URL'),
+    'description' => t('A URL to a video file that complements this object using the HTTPS protocol. All "http://" URLs will automatically be converted to "https://".'),
+    'secure' => TRUE,
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'url',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:video:width'] = array(
+    'label' => t('Video width'),
+    'description' => t('The width of the video.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'integer',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:video:height'] = array(
+    'label' => t('Video height'),
+    'description' => t('The height of the video.'),
+    'weight' => ++$weight,
+    'devel_generate' => array(
+      'type' => 'integer',
+    ),
+  ) + $og_defaults;
+  $info['tags']['og:video:type'] = array(
+    'label' => t('Video type'),
+    'description' => t('The MIME type of the video file. Examples include "application/x-shockwave-flash" for a Flash video, or "text/html" if this is a standalone web page containing a video.'),
+    'weight' => ++$weight,
+  ) + $og_defaults;
+  $info['tags']['video:actor'] = array(
+    'label' => t('Actor(s)'),
+    'description' => t('Links to the Facebook profiles for actor(s) that appear in the video.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults + $video_defaults;
+  $info['tags']['video:actor:role'] = array(
+    'label' => t("Actors' role"),
+    'description' => t("The roles of the actor(s)."),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults + $video_defaults;
+  $info['tags']['video:director'] = array(
+    'label' => t('Director(s)'),
+    'description' => t('Links to the Facebook profiles for director(s) that worked on the video.'),
+    'weight' => ++$weight,
+  ) + $og_defaults + $video_defaults;
+  $info['tags']['video:writer'] = array(
+    'label' => t('Scriptwriter(s)'),
+    'description' => t('Links to the Facebook profiles for scriptwriter(s) for the video.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults + $video_defaults;
+  $info['tags']['video:duration'] = array(
+    'label' => t('Video duration (seconds)'),
+    'description' => t('The length of the video in seconds'),
+    'weight' => ++$weight,
+  ) + $og_defaults + $video_defaults;
+  $info['tags']['video:release_date'] = array(
+    'label' => t('Release date'),
+    'description' => t('The date the video was released.'),
+    'weight' => ++$weight,
+  ) + $og_defaults + $video_defaults;
+  $info['tags']['video:tag'] = array(
+    'label' => t('Tag'),
+    'description' => t('Tag words associated with this video.'),
+    'multiple' => TRUE,
+    'weight' => ++$weight,
+  ) + $og_defaults + $video_defaults;
+  $info['tags']['video:series'] = array(
+    'label' => t('Series'),
+    'description' => t('The TV show this series belongs to.'),
+    'weight' => ++$weight,
+    'dependencies' => array(
+      array(
+        'dependency' => 'og:type',
+        'attribute' => 'value',
+        'condition' => 'value',
+        'value' => 'video.episode',
+      ),
+    ),
+  ) + $og_defaults + $video_defaults;
+
+  return $info;
+}
+
+function _metatag_opengraph_type_options() {
+  $options = array(
+    t('Activities') => array(
+      'activity' => t('Activity'),
+      'sport' => t('Sport'),
+    ),
+    t('Businesses') => array(
+      'bar' => t('Bar', array('context' => 'an establishment')),
+      'company' => t('Company'),
+      'cafe' => t('Cafe'),
+      'hotel' => t('Hotel'),
+      'restaurant' => t('Restaurant'),
+    ),
+    t('Groups') => array(
+      'cause' => t('Cause'),
+      'sports_league' => t('Sports league'),
+      'sports_team' => t('Sports team'),
+    ),
+    t('Organizations') => array(
+      'band' => t('Band'),
+      'government' => t('Government'),
+      'non_profit' => t('Non-profit'),
+      'school' => t('School'),
+      'university' => t('University'),
+    ),
+    t('People') => array(
+      'actor' => t('Actor'),
+      'athlete' => t('Athlete'),
+      'author' => t('Author'),
+      'director' => t('Director'),
+      'musician' => t('Musician'),
+      'politician' => t('Politician'),
+      'profile' => t('Profile'),
+      'public_figure' => t('Public figure'),
+    ),
+    t('Places') => array(
+      'city' => t('City'),
+      'country' => t('Country'),
+      'landmark' => t('Landmark'),
+      'state_province' => t('State or province'),
+    ),
+    t('Products and Entertainment') => array(
+      'album' => t('Album'),
+      'book' => t('Book'),
+      'drink' => t('Drink'),
+      'food' => t('Food'),
+      'game' => t('Game'),
+      'product' => t('Product'),
+      'song' => t('Song'),
+      'video.movie' => t('Movie'),
+      'video.tv_show' => t('TV show'),
+      'video.episode' => t('TV show episode'),
+      'video.other' => t('Miscellaneous video'),
+    ),
+    t('Websites') => array(
+      'blog' => t('Blog'),
+      'website' => t('Website'),
+      'article' => t('Article'),
+    ),
+  );
+
+  return $options;
+}

+ 70 - 0
sites/all/modules/contrib/seo/metatag/metatag_opengraph/metatag_opengraph.module

@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * Implements hook_preprocess_html().
+ */
+function metatag_opengraph_preprocess_html(&$variables) {
+  // The RDF module adds the Open Graph namespace itself.
+  // @see rdf_rdf_namespaces()
+  if (!module_exists('rdf')) {
+    $variables['rdf_namespaces'] .= "\n  xmlns:og=\"http://ogp.me/ns#\"";
+  }
+
+  // @TODO Would it be worth dynamically identifying whether these should be
+  // added, or just output them all?
+
+  // Need an extra namespace for the "article" tags.
+  $variables['rdf_namespaces'] .= "\n  xmlns:article=\"http://ogp.me/ns/article#\"";
+  // Need an extra namespace for the "book" tags.
+  $variables['rdf_namespaces'] .= "\n  xmlns:book=\"http://ogp.me/ns/book#\"";
+  // Need an extra namespace for the "profile" tags.
+  $variables['rdf_namespaces'] .= "\n  xmlns:profile=\"http://ogp.me/ns/profile#\"";
+  // Need an extra namespace for the "video" tags.
+  $variables['rdf_namespaces'] .= "\n  xmlns:video=\"http://ogp.me/ns/video#\"";
+  // And for product tags.
+  $variables['rdf_namespaces'] .= "\n  xmlns:product=\"http://ogp.me/ns/product#\"";
+}
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_opengraph_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/*
+og:title = [node:title] / [user:name]
+og:type = article / profile
+og:image = ? / [user:picture:url]
+og:url = [node:url] / [user:url]
+og:description
+og:site_name = [site:name]
+
+og:latitude
+og:longitude
+og:street-address
+og:locality
+og:region
+og:postal-code
+og:country-name
+
+og:email
+og:phone_number
+og:fax_number
+
+og:video
+og:video:height
+og:video:width
+og:video:type
+
+og:audio
+og:audio:title
+og:audio:artist
+og:audio:album
+og:audio:type
+
+og:upc
+og:isbn
+*/

+ 27 - 0
sites/all/modules/contrib/seo/metatag/metatag_panels/README.txt

@@ -0,0 +1,27 @@
+Metatag: Panels
+-----------------
+This module adds support for meta tag configuration for Panels pages.
+
+Configuration is done within the "Metatag" tab existant in the Page Manager
+variant configuration page.
+
+
+Known Issues
+--------------------------------------------------------------------------------
+- Only contexts of a type that is supported by the Token API work.
+- Only one context for each type is currently supported. If you have two 'node'
+contexts, only the first node is elligible for replacement.
+
+
+Credits / Contact
+--------------------------------------------------------------------------------
+Originally developed by Diogo Correia [1] and sponsored by DRI — Discovery / Reinvention / Integration [2].
+
+This module is based on Panels Breadcrumbs [3] and the Meta tag: Context module.
+
+
+References
+--------------------------------------------------------------------------------
+1: https://www.drupal.org/u/devuo
+2: http://dri-global.com
+3: https://www.drupal.org/project/panels_breadcrumbs

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

@@ -0,0 +1,16 @@
+name = Metatag: Panels
+description = Provides Metatag integration within the Panels interface.
+package = SEO
+core = 7.x
+
+dependencies[] = ctools
+dependencies[] = metatag
+dependencies[] = panels
+dependencies[] = token
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

+ 179 - 0
sites/all/modules/contrib/seo/metatag/metatag_panels/metatag_panels.module

@@ -0,0 +1,179 @@
+<?php
+/**
+ * @file
+ * Main file for metatag_panels module.
+ */
+
+/**
+ * Implements hook_page_manager_variant_operations_alter().
+ */
+function metatag_panels_page_manager_variant_operations_alter(&$operations, $handler) {
+  // Use this obnoxious construct to safely insert our item.
+  reset($operations['children']);
+  $children_operations = array();
+  while (list($key, $value) = each($operations['children'])) {
+    $children_operations[$key] = $value;
+    if ($key == 'context') {
+      $children_operations['meta'] = array(
+        'title' => t('Meta tags'),
+        'description' => t('Edit variant level meta tags.'),
+        'form' => 'metatag_panels_form',
+      );
+    }
+  }
+  $operations['children'] = $children_operations;
+}
+
+/**
+ * Metatag panels configuration form.
+ */
+function metatag_panels_form($form, $form_state) {
+  $handler = $form_state['handler'];
+
+  // Load available contexts
+  ctools_include('context-task-handler');
+  $contexts = ctools_context_handler_get_all_contexts($form_state['task'], $form_state['subtask'], $handler);
+
+  // Convert contexts into keywords readable by the token engine.
+  $token_types = array();
+
+  foreach ($contexts as $context) {
+    if ($context->keyword == 'taxonomy_term') {
+      $token_types[] = 'term';
+    }
+    else {
+      $token_types[] = $context->keyword;
+    }
+  }
+
+  // Allow the user to enable/disable meta tags for this panel.
+  $form['settings']['metatags_enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable Metatag configuration.'),
+    '#default_value' => isset($handler->conf['metatag_panels']['enabled']) ? $handler->conf['metatag_panels']['enabled'] : FALSE,
+  );
+
+  // Don't set any metatag instance name as the configuration data is managed locally within panels.
+  $instance = '';
+  $options = array('token types' => $token_types);
+  $metatags = empty($handler->conf['metatag_panels']) ? array() : $handler->conf['metatag_panels']['metatags'];
+
+  // This leaves some possibility for future versions to support translation.
+  if (!isset($metatags[LANGUAGE_NONE])) {
+    $metatags = array(LANGUAGE_NONE => $metatags);
+  }
+
+  // Load the metatag form (passed by reference).
+  metatag_metatags_form($form, $instance, $metatags[LANGUAGE_NONE], $options);
+
+  // Modify metatag form defaults.
+  $form['metatags']['#collapsible'] = FALSE;
+  $form['metatags']['#collapsed'] = FALSE;
+
+  // Don't show the Metatag options until it's enabled.
+  $form['metatags']['#states'] = array(
+    'visible' => array(
+      ':input[name="metatags_enabled"]' => array('checked' => TRUE),
+    ),
+  );
+
+  return $form;
+}
+
+/**
+ * Submission handler for Metatag panels configuration form.
+ */
+function metatag_panels_form_submit($form, $form_state) {
+  $conf = array(
+    'enabled' => $form_state['values']['metatags_enabled'],
+    'metatags' => $form_state['values']['metatags'][LANGUAGE_NONE],
+  );
+
+  $form_state['handler']->conf['metatag_panels'] = $conf;
+}
+
+/**
+ * Implements hook_ctools_render_alter().
+ */
+function metatag_panels_ctools_render_alter($info, $page, $context) {
+  $output = &drupal_static('metatag_panels');
+
+  $handler = $context['handler'];
+
+  if (empty($handler->conf['metatag_panels']) || !$handler->conf['metatag_panels']['enabled']) {
+    return;
+  }
+
+  $metatags = $handler->conf['metatag_panels']['metatags'];
+  if (!is_array($metatags) || empty($metatags)) {
+    $metatags = array();
+  }
+
+  // If meta tags were found but they're not nested for the language, fix it.
+  // This leaves some possibility for future versions to support translation.
+  if (!empty($metatags) && !isset($metatags[LANGUAGE_NONE])) {
+    $metatags = array(LANGUAGE_NONE => $metatags);
+  }
+
+  // Append global defaults.
+  $all_metatags = array();
+  foreach ($metatags as $langcode => $values) {
+    if (!empty($values)) {
+      $all_metatags = $values + metatag_config_load_with_defaults('');
+    }
+  }
+  $metatags = $all_metatags;
+
+  if (empty($metatags)) {
+    return;
+  }
+
+  // Get the contexts that exist within this panel.
+  ctools_include('context-task-handler');
+  $task_object = ctools_context_handler_get_task_object($context['task'], $context['subtask'], $context['handler']);
+  $task_contexts = ctools_context_load_contexts($task_object, TRUE, $context['contexts']);
+
+  // Build the tokens out of CTools contexts.
+  $tokens = array();
+  foreach ($task_contexts as $task_context) {
+    $tokens[$task_context->keyword] = $task_context->data;
+  }
+
+  // Because of page execution order, sometimes the page title does not get set
+  // by Panels in time for metatags to use it, so we'll explicitly set it here
+  // if we need to.
+  if (!empty($info['title'])) {
+    drupal_set_title($info['title'], PASS_THROUGH);
+  }
+
+  // Build the Metatag.
+  $options = array(
+    'instance' => 'panels:' . $handler->name,
+    'token data' => $tokens,
+  );
+  foreach ($metatags as $metatag => $data) {
+    $metatag_instance = metatag_get_instance($metatag, $data);
+
+    if ($metatag_instance) {
+      $output[$metatag] = $metatag_instance->getElement($options);
+    }
+  }
+
+  // Give third-parties the opportunity to alter the metatag for a given instance.
+  drupal_alter('metatag_metatags_view', $output, $options['instance']);
+}
+
+/**
+ * Implements hook_page_build().
+ *
+ * @see metatag_panels_ctools_render_alter()
+ */
+function metatag_panels_page_build(&$page) {
+  $metatags = drupal_static('metatag_panels');
+
+  if (!empty($metatags)) {
+    // The page region can be changed.
+    $region = variable_get('metatag_page_region', 'content');
+    $page[$region]['metatags']['global'] = $metatags;
+  }
+}

+ 44 - 0
sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/README.txt

@@ -0,0 +1,44 @@
+Metatag: Twitter Cards
+----------------------
+This module adds the fourteen basic Twitter Cards meta tags [1]. The following
+tags are provided:
+
+* twitter:card
+* twitter:site
+* twitter:creator
+* twitter:url
+* twitter:title
+* twitter:description
+* twitter:image:src
+* twitter:image:width
+* twitter:image:height
+* twitter:player
+* twitter:player:width
+* twitter:player:height
+* twitter:player:stream
+* twitter:player:stream:content_type
+
+
+Usage
+------------------------------------------------------------------------------
+The Twitter Cards meta tags are configured along with all other meta tags;
+on-form help is provided to aid with configuring the meta tags.
+
+After enabling and configuring the meta tags it is important to first test [2]
+the meta tags for compliance with Twitter's standards, and then apply [3] to
+have your site's usage approved.
+
+
+Credits
+------------------------------------------------------------------------------
+The initial development was by nico059 [4] with contributions by many in the
+community [5].
+
+
+References
+------------------------------------------------------------------------------
+1: https://dev.twitter.com/docs/cards
+2: https://dev.twitter.com/docs/cards/preview
+3: https://www.drupal.org/u/marty2081
+4: http://www.gemeentemuseum.nl/
+5: https://www.drupal.org/node/1664322

+ 11 - 0
sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.info

@@ -0,0 +1,11 @@
+name = Metatag: Twitter Cards
+description = "Provides support for Twitter's Card meta tags."
+package = SEO
+core = 7.x
+dependencies[] = metatag
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

Файловите разлики са ограничени, защото са твърде много
+ 91 - 0
sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc


+ 39 - 0
sites/all/modules/contrib/seo/metatag/metatag_twitter_cards/metatag_twitter_cards.module

@@ -0,0 +1,39 @@
+<?php
+/**
+ * @file
+ * Primary hook implementations for Metatag: Twitter Cards.
+ */
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_twitter_cards_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function metatag_twitter_cards_theme() {
+  $info['metatag_twitter_cards'] = array(
+    'render element' => 'element',
+  );
+
+  return $info;
+}
+
+/**
+ * Theme callback for an twittercard meta tag.
+ */
+function theme_metatag_twitter_cards($variables) {
+  $element = &$variables['element'];
+  $args = array(
+    '#name' => 'name',
+    '#value' => 'content',
+  );
+  element_set_attributes($element, $args);
+  unset($element['#value']);
+  return theme('html_tag', $variables);
+}

+ 16 - 0
sites/all/modules/contrib/seo/metatag/metatag_verification/README.txt

@@ -0,0 +1,16 @@
+Metatag: Verification
+---------------------
+This module adds meta tags used to confirm ownership of the site with various
+search engines and online services.
+
+
+Usage
+------------------------------------------------------------------------------
+These tags are only available on the Global configuration section of the main
+settings interface at admin/config/search/metatag as they affect the site as a
+whole rather than portions of it.
+
+
+Credits
+------------------------------------------------------------------------------
+Development and maintenance by Damien McKenna.

+ 12 - 0
sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.info

@@ -0,0 +1,12 @@
+name = Metatag: Verification
+description = "Various meta tags for verifying ownership of a site."
+package = SEO
+core = 7.x
+dependencies[] = metatag
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

+ 54 - 0
sites/all/modules/contrib/seo/metatag/metatag_verification/metatag_verification.metatag.inc

@@ -0,0 +1,54 @@
+<?php
+/**
+ * @file
+ * Metatag integration for the Metatag: Verification module.
+ */
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_verification_metatag_info() {
+  $info['groups']['verification'] = array(
+    'label' => t('Site verification'),
+    'description' => t('These meta tags are used to confirm site ownership with search engines and other services.'),
+    'form' => array(
+      '#weight' => 110,
+    ),
+  );
+
+  // Stack the verification codes after most others.
+  $weight = 100;
+
+  // Defaults used for all meta tags.
+  $defaults = array(
+    'class' => 'DrupalTextMetaTag',
+    'context' => array('global'),
+    'group' => 'verification',
+  );
+
+  $info['tags']['google-site-verification'] = array(
+    'label' => t('Google'),
+    'description' => t('A string provided by <a href="https://www.google.com/">Google</a>.'),
+    'weight' => ++$weight,
+  ) + $defaults;
+
+  $info['tags']['p:domain_verify'] = array(
+    'label' => t('Pinterest'),
+    'description' => t('A string provided by <a href="@pinterest">Pinterest</a>, full details are available from the <a href="@verify_url">Pinterest online help</a>.', array('@pinterest' => 'https://www.pinterest.com/', '@verify_url' => 'https://help.pinterest.com/en/articles/verify-your-website')),
+    'weight' => ++$weight,
+  ) + $defaults;
+
+  $info['tags']['msvalidate.01'] = array(
+    'label' => t('Bing'),
+    'description' => t('A string provided by <a href="@bing">Bing</a>, full details are available from the <a href="@verify_url">Bing online help</a>.', array('@bing' => 'http://www.bing.com/', '@verify_url' => 'http://www.bing.com/webmaster/help/how-to-verify-ownership-of-your-site-afcfefc6')),
+    'weight' => ++$weight,
+  ) + $defaults;
+
+  $info['tags']['yandex-verification'] = array(
+    'label' => t('Yandex'),
+    'description' => t('A string provided by <a href="@yandex">Yandex</a>, full details are available from the <a href="@verify_url">Yandex online help</a>.', array('@yandex' => 'http://www.yandex.com/', '@verify_url' => 'http://api.yandex.com/webmaster/doc/dg/reference/hosts-type.xml')),
+    'weight' => ++$weight,
+  ) + $defaults;
+
+  return $info;
+}

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

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

+ 16 - 0
sites/all/modules/contrib/seo/metatag/metatag_views/README.txt

@@ -0,0 +1,16 @@
+Metatag: Views
+----------------
+This module adds support for meta tag configuration for Views pages.
+
+Configuration is done within the "Metatag" section of the Page Settings in
+the Views UI configuration page.
+
+
+Credits / Contact
+--------------------------------------------------------------------------------
+Originally developed by Dave Reid [1].
+
+
+References
+--------------------------------------------------------------------------------
+1: https://www.drupal.org/u/dave-reid

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

@@ -0,0 +1,16 @@
+name = Metatag: Views
+description = Provides Metatag integration within the Views interface.
+package = SEO
+core = 7.x
+
+dependencies[] = metatag
+dependencies[] = views
+
+files[] = metatag_views_plugin_display_extender_metatags.inc
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

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

@@ -0,0 +1,33 @@
+<?php
+/**
+ * @file
+ * Meta tag integration for the metatag_views module.
+ */
+
+/**
+ * Implements hook_metatag_config_instance_info().
+ */
+function metatag_views_metatag_config_instance_info() {
+  $info['view'] = array('label' => t('Views'));
+  return $info;
+}
+
+/**
+ * Implements hook_metatag_config_default().
+ */
+function metatag_views_metatag_config_default() {
+  $configs = array();
+
+  $config = new stdClass();
+  $config->instance = 'view';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'title' => array('value' => '[view:title] | [site:name]'),
+    'description' => array('value' => '[view:description]'),
+    'canonical' => array('value' => '[view:url]'),
+  );
+  $configs[$config->instance] = $config;
+
+  return $configs;
+}

+ 106 - 0
sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views.module

@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * @file
+ * Provides native meta tag integration with Views.
+ */
+
+/**
+ * Implements hook_views_api().
+ */
+function metatag_views_views_api() {
+  return array('api' => 3.0);
+}
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_views_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_view_preview_info_alter().
+ */
+function metatag_views_views_preview_info_alter(&$rows, $view) {
+  $metatags = $view->display_handler->get_option('metatags');
+  if (!is_array($metatags) || empty($metatags)) {
+    return;
+  }
+
+  // If meta tags were found but they're not nested for the language, fix it.
+  // This leaves some possibility for future versions to support translation.
+  if (!empty($metatags) && !isset($metatags[LANGUAGE_NONE])) {
+    $metatags = array(LANGUAGE_NONE => $metatags);
+  }
+
+  // Set the page title to be the previewed views title before fetching meta
+  // tag values.
+  $title = drupal_set_title();
+  if ($view_title = $view->get_title()) {
+    drupal_set_title($view_title);
+  }
+
+  $instance = 'view:' . $view->name;
+  $options['token data']['view'] = $view;
+  $values = metatag_metatags_values($instance, $metatags, $options);
+  foreach ($values as $metatag => $value) {
+    $metatag_info = metatag_get_info('tags', $metatag);
+    $values[$metatag] = check_plain($metatag_info['label']) . ': ' . check_plain($value);
+  }
+  if (!empty($values)) {
+    $rows['query'][] = array(
+      '<strong>' . t('Meta tags') . '</strong>',
+      implode('<br />', $values),
+    );
+  }
+
+  // Restore the page title.
+  drupal_set_title($title);
+}
+
+/**
+ * Implements hook_page_alter().
+ */
+function metatag_views_page_alter(&$page) {
+  $view = views_get_page_view();
+
+  // Check if Views metatags are enabled.
+  if (!empty($view) && metatag_config_is_enabled('view')) {
+    // The following is taken from views_get_page_view().
+    // If a module is still putting in the display like we used to, catch that.
+    if (is_subclass_of($view, 'views_plugin_display')) {
+      $view = $view->view;
+    }
+
+    // Prevent Views settings from overwriting global:frontpage.
+    if (drupal_is_front_page() && metatag_config_is_enabled('global:frontpage')) {
+      return;
+    }
+
+    // Load the meta tags for this view.
+    $metatags = $view->display_handler->get_option('metatags');
+    if (!is_array($metatags) || empty($metatags)) {
+      $metatags = array();
+    }
+
+    // If meta tags were found but they're not nested for the language, fix it.
+    // This leaves some possibility for future versions to support translation.
+    if (!empty($metatags) && !isset($metatags[LANGUAGE_NONE])) {
+      $metatags = array(LANGUAGE_NONE => $metatags);
+    }
+
+    // Build options for meta tag rendering.
+    $instance = 'view:' . $view->name;
+    $options = array();
+    $options['token data']['view'] = $view;
+
+    // The page region can be changed.
+    $region = variable_get('metatag_page_region', 'content');
+
+    // Add the metatags.
+    $page[$region]['metatags'][$instance] = metatag_metatags_view($instance, $metatags, $options);
+  }
+}

+ 101 - 0
sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views.tokens.inc

@@ -0,0 +1,101 @@
+<?php
+/**
+ * @file
+ * Token integration for the metatag_views module.
+ */
+
+/**
+ * Implements hook_token_info().
+ */
+function metatag_views_token_info() {
+  if (module_hook('views', 'token_info')) {
+    return array();
+  }
+
+  $info['types']['view'] = array(
+    'name' => t('View'),
+    'description' => t('Tokens related to views.'),
+    'needs-data' => 'view',
+  );
+  $info['tokens']['view']['name'] = array(
+    'name' => t('Name'),
+    'description' => t('The human-readable name of the view.'),
+  );
+  $info['tokens']['view']['description'] = array(
+    'name' => t('Description'),
+    'description' => t('The description of the view.'),
+  );
+  $info['tokens']['view']['machine-name'] = array(
+    'name' => t('Machine name'),
+    'description' => t('The machine-readable name of the view.'),
+  );
+  $info['tokens']['view']['title'] = array(
+    'name' => t('Title'),
+    'description' => t('The title of current display of the view.'),
+  );
+  $info['tokens']['view']['url'] = array(
+    'name' => t('URL'),
+    'description' => t('The URL of the view.'),
+    'type' => 'url',
+  );
+
+  return $info;
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function metatag_views_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  if (module_hook('views', 'tokens')) {
+    return array();
+  }
+
+  $url_options = array('absolute' => TRUE);
+  if (isset($options['language'])) {
+    $url_options['language'] = $options['language'];
+  }
+  $sanitize = !empty($options['sanitize']);
+  $langcode = isset($options['language']) ? $options['language']->language : NULL;
+
+  $replacements = array();
+
+  if ($type == 'view' && !empty($data['view'])) {
+    $view = $data['view'];
+
+    foreach ($tokens as $name => $original) {
+      switch ($name) {
+        case 'name':
+          $replacements[$original] = $sanitize ? check_plain($view->human_name) : $view->human_name;
+          break;
+
+        case 'description':
+          $replacements[$original] = $sanitize ? check_plain($view->description) : $view->description;
+          break;
+
+        case 'machine-name':
+          $replacements[$original] = $view->name;
+          break;
+
+        case 'title':
+          $title = $view->get_title();
+          $replacements[$original] = $sanitize ? check_plain($title) : $title;
+          break;
+
+        case 'url':
+          if ($path = $view->get_url()) {
+            $replacements[$original] = url($path, $url_options);
+          }
+          break;
+      }
+    }
+
+    // [view:url:*] nested tokens. This only works if Token module is installed.
+    if ($url_tokens = token_find_with_prefix($tokens, 'url')) {
+      if ($path = $view->get_url()) {
+        $replacements += token_generate('url', $url_tokens, array('path' => $path), $options);
+      }
+    }
+  }
+
+  return $replacements;
+}

+ 21 - 0
sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views.views.inc

@@ -0,0 +1,21 @@
+<?php
+/**
+ * @file
+ * Views integration for the metatag module.
+ */
+
+/**
+ * Implements hook_views_plugins().
+ */
+function metatag_views_views_plugins() {
+  $plugins = array();
+  $plugins['display_extender']['metatags'] = array(
+    'title' => t('Meta tags'),
+    'help' => t('Provides meta tags for views.'),
+    'handler' => 'metatag_views_plugin_display_extender_metatags',
+    'enabled' => TRUE,
+    'path' => drupal_get_path('module', 'metatag_views'),
+  );
+
+  return $plugins;
+}

+ 99 - 0
sites/all/modules/contrib/seo/metatag/metatag_views/metatag_views_plugin_display_extender_metatags.inc

@@ -0,0 +1,99 @@
+<?php
+/**
+ * @file
+ * Custom display extender plugin for Views.
+ */
+
+class metatag_views_plugin_display_extender_metatags extends views_plugin_display_extender {
+
+  /**
+   * Default values.
+   */
+  function options_definition() {
+    $options = parent::option_definition();
+    $options['metatags'] = array('default' => '');
+    return $options;
+  }
+  function options_definition_alter(&$options) {
+    $options['metatags'] = array('default' => array());
+  }
+
+  /**
+   * Defines where within the Views admin UI the new settings will be visible.
+   */
+  function options_summary(&$categories, &$options) {
+    $categories['metatags'] = array(
+      'title' => t('Meta tags'),
+      'column' => 'second',
+    );
+    $options['metatags'] = array(
+      'category' => 'metatags',
+      'title' => t('Meta tags'),
+      'value' => $this->has_metatags() ? t('Overridden') : t('Using defaults'),
+    );
+  }
+
+  /**
+   * Defines the form.
+   */
+  function options_form(&$form, &$form_state) {
+    if ($form_state['section'] == 'metatags') {
+      $form['#title'] .= t('The meta tags for this display');
+      $metatags = $this->get_metatags();
+
+      // Build/inject the Metatag form.
+      $instance = 'view:' . $form_state['view']->name;
+      $options['token types'] = array('view');
+      $options['context'] = 'view';
+      metatag_metatags_form($form, $instance, $metatags[LANGUAGE_NONE], $options);
+
+      $form['metatags']['#type'] = 'container';
+    }
+  }
+
+  /**
+   * Saves the form values.
+   */
+  function options_submit(&$form, &$form_state) {
+    if ($form_state['section'] == 'metatags') {
+      $metatags = $form_state['values']['metatags'];
+
+      // Leave some possibility for future versions to support translation.
+      foreach ($metatags as $langcode => $values) {
+        if (!empty($form['metatags'][$langcode]['#metatag_defaults'])) {
+          metatag_filter_values_from_defaults($form_state['values']['metatags'][$langcode], $form['metatags'][$langcode]['#metatag_defaults']);
+        }
+      }
+
+      $this->display->set_option('metatags', $metatags);
+    }
+  }
+
+  /**
+   * Identify whether or not the current display has custom meta tags defined.
+   */
+  protected function has_metatags() {
+    $metatags = $this->get_metatags();
+    return !empty($metatags[LANGUAGE_NONE]);
+  }
+
+  /**
+   * Get the Metatag configuration for this display.
+   *
+   * @return array
+   *   The meta tag values, keys by language (default LANGUAGE_NONE).
+   */
+  private function get_metatags() {
+    $metatags = $this->display->get_option('metatags');
+
+    // Leave some possibility for future versions to support translation.
+    if (empty($metatags)) {
+      $metatags = array(LANGUAGE_NONE => array());
+    }
+    if (!isset($metatags[LANGUAGE_NONE])) {
+      $metatags = array(LANGUAGE_NONE => $metatags);
+    }
+
+    return $metatags;
+  }
+}

+ 12 - 0
sites/all/modules/contrib/seo/metatag/tests/metatag_test.info

@@ -0,0 +1,12 @@
+name = Meta Tag Test
+description = Testing module for metatag.module
+core = 7.x
+dependencies[] = metatag
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-05-01
+version = "7.x-1.5-beta1+22-dev"
+core = "7.x"
+project = "metatag"
+datestamp = "1430441289"
+

+ 54 - 0
sites/all/modules/contrib/seo/metatag/tests/metatag_test.metatag.inc

@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * Implements hook_metatag_config_default().
+ */
+function metatag_test_metatag_config_default() {
+  $configs = array();
+
+  $config = new stdClass();
+  $config->instance = 'test';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'description' => array('value' => 'Test description'),
+  );
+  $configs[$config->instance] = $config;
+
+  $config = new stdClass();
+  $config->instance = 'test:foo';
+  $config->api_version = 1;
+  $config->disabled = FALSE;
+  $config->config = array(
+    'description' => array('value' => 'Test foo description'),
+    'abstract' => array('value' => 'Test foo abstract'),
+    'title' => array('value' => 'Test title'),
+    'test:foo' => array('value' => 'foobar'),
+  );
+  $configs[$config->instance] = $config;
+
+  return $configs;
+}
+
+/**
+ * Implements hook_metatag_config_default_alter().
+ */
+function metatag_test_metatag_config_default_alter(array &$configs) {
+  if (isset($configs['test:foo'])) {
+    $configs['test:foo']->config['title']['value'] = 'Test altered title';
+  }
+}
+
+/**
+ * Implements hook_metatag_info().
+ */
+function metatag_test_metatag_info() {
+  $info['test:foo'] = array(
+    'label' => t('Foo meta tag'),
+    'description' => t('Testing metatag.'),
+    'class' => 'DrupalTextMetaTag',
+    'group' => t('Testing'),
+  );
+
+  return $info;
+}

+ 20 - 0
sites/all/modules/contrib/seo/metatag/tests/metatag_test.module

@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * Implements hook_ctools_plugin_api().
+ */
+function metatag_test_ctools_plugin_api($owner, $api) {
+  if ($owner == 'metatag' && $api == 'metatag') {
+    return array('version' => 1);
+  }
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function metatag_test_entity_info_alter(&$info) {
+  if (variable_get('metatag_test_entity_info_disable')) {
+    $info['node']['bundles']['page']['metatags'] = FALSE;
+    $info['user']['metatags'] = FALSE;
+  }
+}

Някои файлове не бяха показани, защото твърде много файлове са промени