diff --git a/sites/all/modules/metatag/CHANGELOG.txt b/sites/all/modules/metatag/CHANGELOG.txt
new file mode 100644
index 0000000..dfccd5c
--- /dev/null
+++ b/sites/all/modules/metatag/CHANGELOG.txt
@@ -0,0 +1,690 @@
+Metatag 7.x-1.7, 2015-07-24
+---------------------------
+#2537738 by deepak_zyxware: Incorrect path to fb_social settings page.
+#2535178 by DamienMcKenna: 'multiple' option on Viewport causes problems with
+ the meta tag's intended values.
+#2524460 by DamienMcKenna, adriancotter, gbirch, jrb: Remove custom wrangling
+ for Views-based custom entity displays, added new hook to allow other modules
+ to customize as needed (hook_metatag_views_post_render_get_entity).
+#2199533 by Adrian Richardson, DamienMcKenna, mairi: Don't reload entities when
+ processing tokens, it causes problems with content workflows.
+#2513892 by DamienMcKenna: Tests for user objects.
+#1658970 by DamienMcKenna, stefan.r, subhojit777, HyperGlide, jenlampton: Drush
+ script to convert data from the Page Title module.
+
+
+Metatag 7.x-1.6, 2015-06-30
+---------------------------
+#2503089 by DamienMcKenna: Added support for the "any" favicon, used for SVG
+ files in Safari 9.
+#2499865 by DamienMcKenna: Improvements to entity selection, all sites will now
+ automatically start off supporting news, terms and users.
+#2503097 by DamienMcKenna: Added the theme-color meta tag.
+#2503089 by jdanthinne, DamienMcKenna: Improved wording of the SVG favicon
+ description.
+#2499737 by DamienMcKenna: Moved the Dublin Core Additional Tags meta tags into
+ a new submodule, metatag_dc_advanced.
+#2499739 by DamienMcKenna: Moved the Open Graph Products meta tags into a new
+ submodule, metatag_opengraph_products.
+#2498173 by DamienMcKenna: Clarified the touch icon meta tags available by
+ adding separate primary vs precomposed tags.
+#2499739 by DamienMcKenna: Follow-up to fix a missing variable.
+#2507025 by DamienMcKenna: Fixed Panels/Panelizer support for entities after
+ changes in 1.5.
+#2505051 by DamienMcKenna: Automatically check for image URLs in image meta
+ tags; added a new 'image' attribute to meta tag specifications.
+#2504561 by hanoii: Remove unused metatag_load_entity_from_path() function and
+ corresponding hook.
+#2222711 by hanoii, DamienMcKenna, andyg5000: Fixed Views support for entities
+ after API changes in 1.5.
+#2467587 by DamienMcKenna: Clear the Metatag cache when a node's state is
+ changed via Workbench Moderation.
+#2449425 by DamienMcKenna: Only process string values for token replacement.
+#2265453 by zd123, DamienMcKenna: CTools keyword substitution for Panels
+ integration.
+#2512284 by DamienMcKenna: Missing token browser link on main Metatag fieldset.
+#2513890 by DamienMcKenna: Added tests for taxonomy term integration.
+#1404270 by JStanton, DamienMcKenna: Added the Refresh meta tag.
+#2384673 by etroid, DamienMcKenna: Added the shortcut icon meta tag.
+#2514852 by rrfegade: Spelling mistakes in README.txt files.
+#2514878 by DamienMcKenna: Ignore admin pages on Views/CTools-driven entity
+ pages.
+#2514812 by david_garcia: Fix Views integration for ECK entities.
+#2514572 by DamienMcKenna: Don't check if records exist when deleting them,
+ just run the deletion query.
+
+
+Metatag 7.x-1.5, 2015-05-29
+---------------------------
+#2442183 by DamienMcKenna, jwilson: Mention the Image URL Formatter module in
+ the README.txt file.
+#2451231 by DamienMcKenna: Fixed the Devel:Generate integration.
+By DamienMcKenna: Rearranged og:type select_or_other integration code to be
+ after the og:type tag definition.
+By DamienMcKenna: Removed duplicate description for video:writer meta tag.
+By DamienMcKenna: Standardized structure of all theme functions.
+#2452985 by DamienMcKenna: Added 39 additional Dublin Core meta tags.
+By DamienMcKenna: Removed duplicate dcterms.rights tag.
+By DamienMcKenna: Updated new 'date' dcterms tags to use 'date' generator.
+#2460791 by DamienMcKenna: Allow the page region used to trigger output to be
+ changed; see the advanced settings page for details.
+#2462117 by DamienMcKenna: Allow the included default configurations to be
+ disabled.
+#2454499 by Dmitriy.trt: metatag_config_is_enabled() returned FALSE for empty
+ config, when checked with defaults.
+#2407477 by greggles: Provide support for Twitter app tags without having to use
+ an "app" type.
+By DamienMcKenna: Fixed a small mistake in output of metatag_update_7011().
+#2429091 by deviantintegral, DamienMcKenna: Add support for applinks.org tags.
+#2417155 by dobe: Feeds import fails because of placement of entity_type.
+By DamienMcKenna: Minor text improvements per D8 branch.
+By DamienMcKenna: Clarified compatibility with Workbench Moderation.
+#2473459 by DamienMcKenna: Updated all links to d.o.
+#2479325 by DamienMcKenna: Require Token 1.6.
+#1491562 by jonathan_hunt, knalstaaf: Add instructions to README.txt explaining
+ how to configure meta tags for entity bundles.
+#2449425 by DamienMcKenna: Refactored select_or_other usage, API addition.
+#2487179 by DamienMcKenna: Allow longer cache IDs to reduce conflicts.
+#2473107 by DamienMcKenna: Added a warning about having more og:image:type
+ values than there are og:image values, it can lead to Facebook validation
+ errors.
+#2474427 by DamienMcKenna: Added an advanced option to disable the output cache.
+#2432517 by undertext, DamienMcKenna: Check all CTools contexts, not just the
+ first one.
+#2449425 by DamienMcKenna: Follow-up for select_or_other bug.
+#2415983 by ciss, DamienMcKenna: Core elements not removed when no metatag
+ substitute provided.
+#2490846 by DamienMcKenna: Using [metatag] tokens failed if the value was empty.
+#2466629 by DamienMcKenna, rupertj: Ensure entity is an object before checking
+ its language code.
+#2081717 by DamienMcKenna: Added Admin Menu item for flush the Metatag caches.
+By DamienMcKenna: Noted that the 'shortlink' meta tag replaces 'shorturl'.
+#2493711 by akoe, DamienMcKenna: Added geo.position, geo.placename, geo.region
+ and icbm meta tags.
+#2493395 by das-peter: Google+ itemtype meta tag malformatted.
+#1915926 by DamienMcKenna: Allow multiple fb:admins values.
+#2494271 by DamienMcKenna: og:street_address, og:postal_code, og:country_name
+ are incorrect.
+#1498762 by DamienMcKenna: Added the Rating meta tag.
+#2451271 by DamienMcKenna: Added the Referrer meta tag.
+#1285946 by DamienMcKenna: Added metatag_mobile submodule with a few mobile
+ -related meta tags.
+#2475147 by MatthewHager, DamienMcKenna: Fixed Feeds integration after its API
+ changed.
+#2070821 by DamienMcKenna, pounard: Major re-architecture to how supported
+ entities are handled.
+#2495877 by DamienMcKenna: Added a Context global config instance.
+#2495875 by DamienMcKenna: Added a Panels global config instance.
+#1281138 by jantoine, DamienMcKenna, drupalninja99, stuart.crouch, subhojit777,
+ KarlShea: Metatag:Importer submodule for importing data from Nodewords (D6).
+#2376921 by DamienMcKenna: Trigger an entity cache clear when meta tags are
+ saved or deleted.
+#2496487 by DamienMcKenna: The function is called entity_get_info(), not
+ entity_info().
+#2103321 by mistermoper, DamienMcKenna: Added 24 more Open Graph meta tags for
+ managing product information.
+#2085747 by DamienMcKenna: Added twelve favicon varieties to a new submodule,
+ Metatag: Favicons.
+By DamienMcKenna: Removed message from metatag_opengraph_install() warning about
+ compatibility with the RDF module, which is no longer applicable.
+#2408211 by infinet, MatthewHager: Context substitution added to Metatag: Panels
+ output.
+#2156653 by mitsuroseba, undertext, asgorobets, DamienMcKenna: Added a custom
+ pane for adding the meta tags fieldset to a node form customized via Panels.
+#2496487 by DamienMcKenna: Follow-up on previous commit, remove unneeded
+ function_exists() call.
+
+
+Metatag 7.x-1.5-beta1, 2015-02-02
+---------------------------------
+#2362639 by DamienMcKenna: Improved defaults for Google+ meta tags.
+#2318985 by DamienMcKenna: Indicate that Open Graph tags are used on Pinterest.
+#2362639 by DamienMcKenna: Added itemtype default values for Google+ meta tags.
+#2358137 by DamienMcKenna: Added a submodule for managing site verification
+ meta tags, the first of which is for Google.
+#2358131 by DamienMcKenna: Support for the Pinterest verification meta tag.
+#1848338 by larowlan, DamienMcKenna: Added more tests, especially one for
+ checking the editorial process on a node.
+#2362893 by ipo4ka704: Don't assume the first Panels context is an object.
+#2363591 by DamienMcKenna: Added a default for the 'image' meta tag on user
+ entity pages.
+#1967856 by duozersk: Fixed a minor mistake in the previous commit.
+#2370943 by Simon George: Removed redundant comment.
+#2373189 by nmillin: Added support for the Bing verification code.
+#2358139 by nmillin: Added support for the Yandex verification code.
+#2378127 by DamienMcKenna: Support for rel="alternate" hreflang="x" link tag.
+#2376915 by jenlampton, DamienMcKenna: Added the og:image:url meta tag.
+#1978708 by DamienMcKenna, scor: Updated warning about compatibility with the
+ RDF module in Drupal core before 7.33.
+#2385265 by mikemiles86: Correctly flatten Metatag form fieldsets in Context
+ integration.
+#2370439 by potop, DamienMcKenna: Work around hook_entity_load() problems by
+ loading entity info in metatag_entity_supports_metatags() on demand.
+#1868460 by preshetin: Added the rel=prev and rel=next meta tags.
+#2388339 by das-peter: Fix select_or_other integration for Metatag:GooglePlus,
+ add the missing element_validator.
+#2391975 by Spleshka: Support scenarios where the entity is possibly blank or
+ has no entity_id assigned yet, e.g. Profile2 pages.
+#2400241 by greggles: Typo in description of robots-notranslate option.
+#2400529 by greggles: Add support for OG product price:amount, price:currency.
+#2411607 by liberatr, DamienMcKenna: README.txt note about using the
+ field_multiple_types module to control how many items are output.
+#2415025 by DamienMcKenna: l() in metatag_metatag_info() creates recursion bug.
+#2411477 by betz, DamienMcKenna: $form[#entity] doesn't work for all entities.
+#2411549 by maijs: Language is lost during migration.
+#2198669 by D2ev: Using metatag tokens can easily cause an infinite loop.
+#2411477 by DamienMcKenna, betz: Follow-up to last change.
+
+
+Metatag 7.x-1.4, 2014-10-09
+---------------------------
+#2353079 by DamienMcKenna: Fixed Views integration, for real this time.
+#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 grammar fixes for README.txt.
+#2172433 by fabsor, DamienMcKenna: Ensure that update 7015 runs early enough to
+ avoid data corruption or errors during other updates.
+#2156261 by plopesc, DamienMcKenna: Allow meta tags for 403/404 error pages to
+ 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 ' ' . t('Any items marked "Unknown" are configurations in the system for entity types or bundles which have been disabled via the API or the Settings page; they will not be used.', array('@url' => url('admin/config/search/metatags/settings'))) . '
Technical note: Entity types must not be configuration entities and must have view modes in order to be compatible.'),
+ '#collapsible' => TRUE,
+ '#collapsed' => FALSE,
+ );
+
+ foreach (entity_get_info() as $entity_type => $entity_info) {
+ // Only show entities that are capable of using meta tags.
+ if (metatag_entity_type_is_suitable($entity_type, $entity_info)) {
+ $entity_enabled = metatag_entity_supports_metatags($entity_type);
+ $form['entities']['metatag_enable_' . $entity_type] = array(
+ '#type' => 'checkbox',
+ '#title' => t($entity_info['label']),
+ '#default_value' => $entity_enabled,
+ // '#description' => t('Enable meta tags for all pages of this entity type.'),
+ );
+
+ // Some entities, e.g. User, (core) File, have a single bundle with the
+ // same name as the entity, so only show the bundles list if there is
+ // more than one of them and the bundle's name isn't the same as the
+ // entity type's.
+ if (!empty($entity_info['bundles'])) {
+ foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+ if (count($entity_info['bundles']) > 1 || $entity_type != $bundle_name) {
+ // If the entity type was disabled, automatically enable the bundle.
+ // This will have the effect that if the entity type is enabled in
+ // the form then all of the bundles will then also be enabled. This
+ // is safe to do because in the rest of the module the bundle will
+ // be ignored if the entity is disabled.
+ $form['entities']['metatag_enable_' . $entity_type . '__' . $bundle_name] = array(
+ '#type' => 'checkbox',
+ '#title' => t($bundle_info['label']),
+ '#default_value' => !$entity_enabled || metatag_entity_supports_metatags($entity_type, $bundle_name),
+ '#attributes' => array(
+ // Add some theming that'll indent this bundle.
+ 'class' => array('metatag-bundle-checkbox'),
+ ),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="metatag_enable_' . $entity_type . '"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ }
+ }
+ }
+ }
+ }
+
+ $form['advanced'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Advanced settings'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+
+ $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_cache_output'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Cache meta tag output'),
+ '#description' => t('Disabling this will cause all meta tag output to be generated uniquely for each page load. Currently only affects entities. Note: the entity configuration and output for other types of pages will still be cached, but this can stop the {cache_metatag} table from growing out of control in some scenarios.'),
+ '#default_value' => variable_get('metatag_cache_output', TRUE),
+ );
+
+ $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'),
+ );
+
+ // 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.'));
+}
diff --git a/sites/all/modules/metatag/metatag.admin.js b/sites/all/modules/metatag/metatag.admin.js
new file mode 100644
index 0000000..64e6778
--- /dev/null
+++ b/sites/all/modules/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);
diff --git a/sites/all/modules/metatag/metatag.api.php b/sites/all/modules/metatag/metatag.api.php
new file mode 100644
index 0000000..dd18082
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.api.php
@@ -0,0 +1,360 @@
+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.
+ * 'multiple' - If set to TRUE the output will be comma-separated and output
+ * as multiple tags.
+ * 'image' - If set to TRUE some additional effort will be added to attempt
+ * extracting image URLs from the value. Currently limited to matching
+ * the default output of core image theming, i.e. the following string:
+ * src="[URL]" width=
+ * 'select_or_other' - If set to TRUE, form[#type] is set to 'select' and
+ * the "select_or_other" module is available, that module will be used to
+ * provide a text field to manually insert another option.
+ * 'form' - Optional items to be passed directly to the form; uses standard
+ * Form API values.
+ * 'devel_generate' - Optional values to be passed to the Devel Generate
+ * 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) {
+}
+
+/**
+ * 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);
+ }
+}
+
+/**
+ * Allow modules to override whether entity types are enabled for use.
+ *
+ * By default the system only support entities that are not configuration
+ * entities, have multiple view modes (excluding those created by the ical,
+ * diff and token modules), are fieldable, and are not one of the following:
+ * - field_collection_item (from the Field Collection module)
+ * - paragraphs_item (from the Paragraphs module)
+ *
+ * @param bool $suitable
+ * Whether or not the entity type is enabled for use with Metatag.
+ * @param string $entity_type
+ * The machine name of the entity type.
+ * @param array $entity_info
+ * The full specifications for this entity type, as returned by
+ * entity_get_info().
+ */
+function hook_metatag_entity_type_is_supported_alter(&$suitable, $entity_type, $entity_info) {
+ // Enable Metatag support for a custom entity that might otherwise be
+ // ignored, e.g. it doesn't allow fields.
+ if ($entity_type == 'my_entity') {
+ $suitable = TRUE;
+ }
+}
+
+/**
+ * Identify the entity type provided by a specific view.
+ *
+ * When a view is used to display a page it can be difficult to identify what
+ * entity type is being managed. Use this hook to inform Metatag what type of
+ * entity is being displayed.
+ *
+ * @param object $view
+ * The view object being displayed.
+ *
+ * @return string|NULL
+ * Should return a string indicating an entity type that will be paired with
+ * the views' first argument ($view->args[0]) to load that entity.
+ */
+function hook_metatag_views_post_render_get_entity($view) {
+ $display = $view->display[$view->current_display];
+ if ($display->display_options['path'] == 'coolpage/%') {
+ return 'my_entity';
+ }
+}
diff --git a/sites/all/modules/metatag/metatag.drush.inc b/sites/all/modules/metatag/metatag.drush.inc
new file mode 100644
index 0000000..950e560
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.drush.inc
@@ -0,0 +1,21 @@
+ $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);
+}
diff --git a/sites/all/modules/metatag/metatag.feeds.inc b/sites/all/modules/metatag/metatag.feeds.inc
new file mode 100644
index 0000000..4732d01
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.feeds.inc
@@ -0,0 +1,71 @@
+ $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, $values) {
+ // Don't do anything if we weren't given any data.
+ if (empty($values)) {
+ return;
+ }
+
+ // The Feed API was changed so that the $values are passed as an array rather
+ // than a single value. For backwards compatibility, support both.
+ if (!is_array($values)) {
+ $values = array($values);
+ }
+
+ // Strip the prefix that was added above.
+ $name = str_replace('meta_', '', $target);
+
+ // Identify the type of entity being imported.
+ $entity_type = $entity->feeds_item->entity_type;
+
+ // Check for any existing data that may not have been loaded already.
+ if (!isset($entity->metatags) && !empty($entity->feeds_item->entity_id) && is_numeric($entity->feeds_item->entity_id)) {
+ $entity->metatags = array();
+ $entity_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'] = $values[0];
+}
diff --git a/sites/all/modules/metatag/metatag.i18n.inc b/sites/all/modules/metatag/metatag.i18n.inc
new file mode 100644
index 0000000..194d14c
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.i18n.inc
@@ -0,0 +1,18 @@
+ t('Metatag'),
+ 'description' => t('Configurable metatags.'),
+ 'format' => FALSE,
+ 'list' => FALSE,
+ );
+ return $groups;
+}
diff --git a/sites/all/modules/metatag/metatag.inc b/sites/all/modules/metatag/metatag.inc
new file mode 100644
index 0000000..385fe6d
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.inc
@@ -0,0 +1,385 @@
+ '');
+ 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(' ', ' ', $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,
+ );
+
+ // Optional handling for items that allow multiple values.
+ if (!empty($this->info['multiple'])) {
+ $form['value']['#description'] .= ' ' . t('Multiple values may be used, separated by a comma. Note: Tokens that return multiple values will be handled automatically.');
+ }
+
+ // Optional handling for images.
+ if (!empty($this->info['image'])) {
+ $form['value']['#description'] .= ' ' . t('This will be able to extract the URL from an image field.');
+ }
+
+ // Optional support for select_or_other.
+ if ($form['value']['#type'] == 'select' && !empty($this->info['select_or_other']) && module_exists('select_or_other')) {
+ $form['value']['#type'] = 'select_or_other';
+ $form['value']['#other'] = t('Other (please type a value)');
+ $form['value']['#multiple'] = FALSE;
+ $form['value']['#other_unknown_defaults'] = 'other';
+ $form['value']['#other_delimiter'] = FALSE;
+ $form['value']['#theme'] = 'select_or_other';
+ $form['value']['#select_type'] = 'select';
+ $form['value']['#element_validate'] = array('select_or_other_element_validate');
+ }
+
+ // Support for dependencies, using Form API's #states system.
+ // @see metatag.api.php.
+ // @see https://api.drupal.org/drupal_process_states
+ 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);
+ }
+
+ // Special handling for images.
+ if (!empty($this->info['image'])) {
+ // If this contains embedded image tags, extract the image URLs.
+ if (strip_tags($value) != $value) {
+ if (!empty($this->info['multiple'])) {
+ $values = explode(',', $value);
+ }
+ else {
+ $values = array($value);
+ }
+ foreach ($values as $key => $val) {
+ $matches = array();
+ preg_match('/src="([^"]*)"/', $val, $matches);
+ if (!empty($matches[1])) {
+ $values[$key] = $matches[1];
+ }
+ }
+ $value = implode(',', $values);
+ }
+ }
+
+ 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;
+ }
+}
diff --git a/sites/all/modules/metatag/metatag.info b/sites/all/modules/metatag/metatag.info
new file mode 100644
index 0000000..9c1db1d
--- /dev/null
+++ b/sites/all/modules/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-07-24
+version = "7.x-1.7"
+core = "7.x"
+project = "metatag"
+datestamp = "1437763741"
+
diff --git a/sites/all/modules/metatag/metatag.install b/sites/all/modules/metatag/metatag.install
new file mode 100644
index 0000000..29f44a7
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.install
@@ -0,0 +1,1738 @@
+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 https://www.drupal.org/node/1391554 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 https://www.drupal.org/node/1391554 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:!short_path
The file's full path is:!full_path
", 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('Entity_Translation 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 Transliteration module to clean up filenames of uploaded files that may be used with image meta tags.", array('@url' => 'https://drupal.org/project/transliteration')),
+ );
+ }
+
+ // 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 disable that option.", 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 README.txt file as there are some known issues that may affect this site.", array('!url' => url(drupal_get_path('module', 'metatag') . '/README.txt'))));
+
+ // Always enable the node, taxonomy term and user entities.
+ foreach (array('node', 'taxonomy_term', 'user') as $entity_type) {
+ // Enable the main entity type.
+ $variable_name = 'metatag_enable_' . $entity_type;
+ variable_set($variable_name, TRUE);
+
+ // Update each entity bundle too.
+ $entity_info = entity_get_info($entity_type);
+ if (!empty($entity_info['bundles'])) {
+ foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+ $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;
+ variable_set($variable_name, TRUE);
+ }
+ }
+ }
+
+ // Possibly enable other entities, if they're specifically requested.
+ foreach (entity_get_info() as $entity_type => $entity_info) {
+ // Skip the three entity types that were already enabled.
+ if (in_array($entity_type, array('node', 'taxonomy_term', 'user'))) {
+ continue;
+ }
+
+ $variable_name = 'metatag_enable_' . $entity_type;
+
+ // Configuration entities are skipped.
+ if (isset($entity_info['configuration']) && $entity_info['configuration'] == TRUE) {
+ continue;
+ }
+
+ // Entities must have bundles.
+ if (empty($entity_info['bundles'])) {
+ continue;
+ }
+
+ // Entities must be fieldable.
+ elseif (empty($entity_info['fieldable'])) {
+ continue;
+ }
+
+ // Ignore some view modes that are automatically added by certain modules.
+ unset($entity_info['view modes']['ical']);
+ unset($entity_info['view modes']['diff_standard']);
+ unset($entity_info['view modes']['token']);
+
+ // Entities without view modes are skipped.
+ if (empty($entity_info['view modes'])) {
+ continue;
+ }
+
+ // At this point, disable the entity by default.
+ $entity_enabled = FALSE;
+
+ // Anything that was specifically enabled via hook_entity_info() from older
+ // versions will be enabled if not configured already.
+ if (!empty($entity_info['metatag']) || !empty($entity_info['metatags'])) {
+ $entity_enabled = variable_get($variable_name, 'monkey');
+ if ($entity_enabled === 'monkey') {
+ $entity_enabled = TRUE;
+ }
+ }
+
+ variable_set($variable_name, $entity_enabled);
+
+ // Loop through the bundles, but only if the entity is enabled.
+ if ($entity_enabled) {
+ foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+ $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;
+
+ // If it wasn't specifically disabled before, enable it.
+ $bundle_enabled = variable_get($variable_name, 'monkey');
+ if ($bundle_name != FALSE) {
+ variable_set($variable_name, TRUE);
+ }
+ }
+ }
+ }
+
+ drupal_set_message(t('It may be worth verifying on the Settings page which types of content on the site should allow meta tags.', array('@url' => url('admin/config/search/metatags/settings'))));
+}
+
+/**
+ * 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');
+
+ // Optionally disables the output cache.
+ variable_del('metatag_cache_output');
+
+ // Remove all possible 'enable' variables.
+ foreach (entity_get_info() as $entity_type => $entity_info) {
+ variable_del('metatag_enable_' . $entity_type);
+ if (!empty($entity_info['bundles'])) {
+ foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+ variable_del('metatag_enable_' . $entity_type . '__' . $bundle_name);
+ }
+ }
+ }
+}
+
+/**
+ * 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 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() {
+}
+
+/**
+ * Update variables to indicate which entities should be supported.
+ */
+function metatag_update_7036() {
+ foreach (entity_get_info() as $entity_type => $entity_info) {
+ $variable_name = 'metatag_enable_' . $entity_type;
+
+ // Configuration entities are skipped.
+ if (isset($entity_info['configuration']) && $entity_info['configuration'] == TRUE) {
+ continue;
+ }
+
+ // Entities without view modes are skipped.
+ elseif (empty($entity_info['view modes'])) {
+ continue;
+ }
+
+ // Basic core entities or "normal" entities that have been enabled via the
+ // API.
+ elseif (in_array($entity_type, array('node', 'taxonomy_term', 'user')) ||
+ !empty($entity_info['metatag']) || !empty($entity_info['metatags'])) {
+ // Check if the entity type has been enabled or disabled previously; if
+ // the variable equals a junk value then it was not previously set,
+ // therefore we'll set a default.
+ if (variable_get($variable_name, 'monkey') == 'monkey') {
+ // By default these entity types are enabled.
+ variable_set($variable_name, TRUE);
+ // Check each entity bundle.
+ if (!empty($entity_info['bundles'])) {
+ foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+ $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;
+ // Check if the bundle has been enabled or disabled previously; if
+ // the variable equals a junk value then it was not previously set,
+ // therefore we'll set a default.
+ if (variable_get($variable_name, 'monkey') == 'monkey') {
+ if (!empty($bundle_info['metatag']) || !empty($bundle_info['metatags'])) {
+ variable_set($variable_name, TRUE);
+ }
+ else {
+ variable_set($variable_name, FALSE);
+ }
+ }
+ // This variable was set before.
+ else {
+ // Do nothing.
+ }
+ }
+ }
+ }
+ // This variable was set before.
+ else {
+ // Do nothing.
+ }
+ }
+
+ // Disable this entity type.
+ else {
+ variable_set($variable_name, FALSE);
+ }
+ }
+
+ // Clear the caches.
+ cache_clear_all('*', 'cache_metatag', TRUE);
+ drupal_static_reset('metatag_config_load_with_defaults');
+ drupal_static_reset('metatag_entity_supports_metatags');
+ ctools_include('export');
+ ctools_export_load_object_reset('metatag_config');
+
+ drupal_set_message(t('The way that Metatag tracks which entity types are compatible has changed. Please review the Settings page to ensure that all of the entity types are enabled correctly.', array('@url' => 'admin/config/search/metatags/settings')));
+}
+
+/**
+ * Clear the menu cache so the renamed Settings page will be picked up.
+ */
+function metatag_update_7037() {
+ variable_set('menu_rebuild_needed', TRUE);
+}
+
+/**
+ * Manually enable all content types, vocabularies and the user entity to help
+ * resolve issues from 1.5's architecture change.
+ */
+function metatag_update_7038() {
+ foreach (array('node', 'taxonomy_term', 'user') as $entity_type) {
+ // Enable the main entity type.
+ $variable_name = 'metatag_enable_' . $entity_type;
+ variable_set($variable_name, TRUE);
+
+ // Update each entity bundle too.
+ $entity_info = entity_get_info($entity_type);
+ if (!empty($entity_info['bundles'])) {
+ foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+ $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;
+ variable_set($variable_name, TRUE);
+ }
+ }
+ }
+}
diff --git a/sites/all/modules/metatag/metatag.metatag.inc b/sites/all/modules/metatag/metatag.metatag.inc
new file mode 100644
index 0000000..c4bcc23
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.metatag.inc
@@ -0,0 +1,439 @@
+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 not 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 Open Directory Project description from appearing in search results.', array('!opendirectory' => 'http://www.dmoz.org/')),
+ 'noydir' => t('Prevents Yahoo! from listing this page in the Yahoo! Directory.', 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 Google News.', 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 Google News. 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']['rating'] = array(
+ 'label' => t('Content rating'),
+ 'description' => t('Used to indicate the intended audience for the content.'),
+ 'class' => 'DrupalTextMetaTag',
+ 'group' => 'advanced',
+ 'select_or_other' => TRUE,
+ 'form' => array(
+ '#type' => 'select',
+ '#options' => array(
+ 'general' => t('General'),
+ 'mature' => t("Mature"),
+ 'restricted' => t("Restricted"),
+ '14 years' => t("14 years or Older"),
+ 'safe for kids' => t("Safe for kids"),
+ ),
+ '#empty_option' => t('- None -'),
+ ),
+ 'weight' => ++$weight,
+ );
+
+ $info['tags']['referrer'] = array(
+ 'label' => t('Referrer policy'),
+ 'description' => t('Indicate to search engines and other page scrapers whether or not links should be followed. See the W3C specifications for further details.'),
+ 'class' => 'DrupalTextMetaTag',
+ 'group' => 'advanced',
+ 'select_or_other' => TRUE,
+ 'form' => array(
+ '#type' => 'select',
+ '#options' => array(
+ 'no-referrer' => t('No Referrer'),
+ 'origin' => t('Origin'),
+ 'no-referrer-when-downgrade' => t('No Referrer When Downgrade'),
+ 'origin-when-cross-origin' => t('Origin When Cross-Origin'),
+ 'unsafe-url' => t('Unsafe URL'),
+ ),
+ '#empty_option' => t('- None -'),
+ ),
+ 'weight' => ++$weight,
+ );
+
+ $info['tags']['generator'] = array(
+ 'label' => t('Generator'),
+ 'description' => t("Describes the name and version number of the software or publishing tool used to create the page."),
+ '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,
+ 'image' => TRUE,
+ '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,
+ 'replaces' => array(
+ 'shorturl',
+ ),
+ '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 indicate paginated content 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 indicate paginated content 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 XML Sitemap 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',
+ ),
+ );
+
+ $info['tags']['geo.position'] = array(
+ 'label' => t('Geo position'),
+ 'description' => t('Geo-spatial information in "latitude;longitude" format, e.g. "50.167958;-97.133185"; see Wikipedia for details.'),
+ 'class' => 'DrupalTextMetaTag',
+ 'group' => 'advanced',
+ 'weight' => ++$weight,
+ );
+
+ $info['tags']['geo.placename'] = array(
+ 'label' => t('Geo place name'),
+ 'description' => t("A location's formal name."),
+ 'class' => 'DrupalTextMetaTag',
+ 'group' => 'advanced',
+ 'weight' => ++$weight,
+ );
+
+ $info['tags']['geo.region'] = array(
+ 'label' => t('Geo region'),
+ 'description' => t("A location's two-letter international country code, with an optional two-letter region, e.g. \"US-NH\" for New Hampshire in the USA."),
+ 'class' => 'DrupalTextMetaTag',
+ 'group' => 'advanced',
+ 'weight' => ++$weight,
+ );
+
+ $info['tags']['icbm'] = array(
+ 'label' => t('ICBM'),
+ 'description' => t('Geo-spatial information in "latitude, longitude" format, e.g. "50.167958, -97.133185"; see Wikipedia for details.'),
+ 'class' => 'DrupalTextMetaTag',
+ 'group' => 'advanced',
+ 'weight' => ++$weight,
+ );
+
+ $info['tags']['refresh'] = array(
+ 'label' => t('Refresh'),
+ 'description' => t('The number of seconds to wait before refreshing the page. May also force redirect to another page using the format "5; url=http://example.com/", which would be triggered after five seconds.'),
+ 'class' => 'DrupalTextMetaTag',
+ 'group' => 'advanced',
+ 'element' => array(
+ '#theme' => 'metatag_http_equiv',
+ ),
+ );
+
+ return $info;
+}
diff --git a/sites/all/modules/metatag/metatag.migrate.inc b/sites/all/modules/metatag/metatag.migrate.inc
new file mode 100644
index 0000000..690479d
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.migrate.inc
@@ -0,0 +1,125 @@
+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 {
+
+ /**
+ * Identify a list of supported entity types.
+ */
+ public function __construct() {
+ $entity_types = metatag_entity_supports_metatags();
+ $entity_types = array_filter($entity_types);
+ $entity_types = array_keys($entity_types);
+
+ $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);
+ }
+ }
+ }
+}
diff --git a/sites/all/modules/metatag/metatag.module b/sites/all/modules/metatag/metatag.module
new file mode 100644
index 0000000..4c2dc0c
--- /dev/null
+++ b/sites/all/modules/metatag/metatag.module
@@ -0,0 +1,2504 @@
+' . t('To view a summary of the default meta tags and the inheritance, click on a meta tag type.') . '
' . 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.') . '
'; + } + elseif ($path == 'admin/config/search/metatags/bulk-revert') { + return '' . t('This form will wipe out all custom meta tags for the selected entities, reverting them to the default configuration assigned at the Defaults tab. 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'))) . '
'; + } +} + +/** + * 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_ctools_plugin_directory(). + */ +function metatag_ctools_plugin_directory($owner, $plugin_type) { + if ($owner == 'ctools' && $plugin_type == 'content_types') { + return "plugins/$plugin_type"; + } +} + +/** + * 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'] .= '' . 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.') . '
', + '#weight' => -10, + ); + } + else { + $form['metatags']['intro_text'] = array( + '#markup' => '' . 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).') . '
', + '#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']['#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'] .= '*
character (asterisk) as a wildcard and the ~
character (tilde) to exclude one or more paths. Use <front>
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('
', $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' => '', + '#markup' => t('Other conditions have been assigned that must be controlled through the main Context settings page.'), + '#suffix' => '
', + '#weight' => -99.9, + ); + } + + $form['help'] = array( + '#prefix' => '', + '#markup' => t('Values assigned here inherit from the global defaults and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))), + '#suffix' => '
', + '#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; +} diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.context.inc b/sites/all/modules/metatag/metatag_context/metatag_context.context.inc new file mode 100644 index 0000000..15984fb --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.context.inc @@ -0,0 +1,285 @@ +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' => '', + '#markup' => t('Values assigned here inherit from the global defaults and will override any other meta tags assigned elsewhere.', array('@url' => url('admin/config/search/metatags/config/global'))), + '#suffix' => '
', + ); + + $form['basic_header'] = array( + '#prefix' => '', + '#markup' => $form['metatags'][LANGUAGE_NONE][$fieldset]['#description'], + '#suffix' => '
', + ); + } + 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; +} diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.info b/sites/all/modules/metatag/metatag_context/metatag_context.info new file mode 100644 index 0000000..20bc9bb --- /dev/null +++ b/sites/all/modules/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-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.install b/sites/all/modules/metatag/metatag_context/metatag_context.install new file mode 100644 index 0000000..78415ce --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.install @@ -0,0 +1,13 @@ + t('Context')); + return $info; +} diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.module b/sites/all/modules/metatag/metatag_context/metatag_context.module new file mode 100644 index 0000000..dfa9b82 --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.module @@ -0,0 +1,117 @@ + '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"); + } +} diff --git a/sites/all/modules/metatag/metatag_context/metatag_context.test b/sites/all/modules/metatag/metatag_context/metatag_context.test new file mode 100644 index 0000000..234a87e --- /dev/null +++ b/sites/all/modules/metatag/metatag_context/metatag_context.test @@ -0,0 +1,149 @@ + '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_context'; + parent::setUp($modules); + + // Create user. + $perms = array( + 'bypass node access', + ); + $this->privileged_user = $this->createAdminUser($perms); + $this->drupalLogin($this->privileged_user); + + // Create a content type, with underscores. + $type_name = strtolower($this->randomName(8)) . '_test'; + $type = $this->createContentType($type_name, $type_name); + $this->type = $type->type; + + // Store a valid URL name, with hyphens instead of underscores. + $this->hyphen_type = str_replace('_', '-', $this->type); + } + + /** + * Performs the basic tests. + */ + public function testMetatagContextBasic() { + // Create content type node. + $this->drupalPost('node/add/' . $this->hyphen_type, array('title' => $this->randomName(8)), t('Save')); + $this->context_name = drupal_strtolower($this->randomName(8)); + + // Generate metatags and check content. + $this->metatag_pages['node'] = $this->createMetatagObject('node/1', 'node_metatags'); + $this->metatag_pages['page'] = $this->createMetatagObject('', + '#suffix' => '
', + ); + } + + return $form; +} + +/** + * Handles submission of the Nodewords migration form. + */ +function metatag_importer_form_submit($form, &$form_state) { + $types = array_filter($form_state['values']['types']); + _metatag_importer_import($types); +} + +function _metatag_importer_list_nodewords() { + $keys = array( + NODEWORDS_TYPE_DEFAULT => t('Default'), + NODEWORDS_TYPE_ERRORPAGE => t('Error page'), + NODEWORDS_TYPE_FRONTPAGE => t('Front page'), + NODEWORDS_TYPE_NONE => t('None'), + NODEWORDS_TYPE_NODE => t('Node'), + NODEWORDS_TYPE_PAGE => t('Page'), + NODEWORDS_TYPE_PAGER => t('Pager'), + NODEWORDS_TYPE_TERM => t('Taxonomy term'), + NODEWORDS_TYPE_TRACKER => t('Tracker'), + NODEWORDS_TYPE_USER => t('User'), + NODEWORDS_TYPE_VOCABULARY => t('Vocabulary'), + ); + + // Get a list of all records grouped by type. + $query = db_select('nodewords', 'nw') + ->fields('nw', array('type')) + ->orderBy('nw.type') + ->orderBy('nw.id') + // Exclude records that are empty. + ->condition('nw.content', 'a:1:{s:5:"value";s:0:"";}', '<>') + ->groupBy('nw.type'); + // Group-by. + $query->addExpression('COUNT(nw.id)', 'id_count'); + $filtered = $query->execute(); + + // Get a list of all records grouped by type. + $query = db_select('nodewords', 'nw') + ->fields('nw', array('type')) + ->orderBy('nw.type') + ->orderBy('nw.id') + ->groupBy('nw.type'); + // Group-by. + $query->addExpression('COUNT(nw.id)', 'id_count'); + $all = $query->execute()->fetchAllKeyed(); + + $types = array(); + foreach ($filtered as $record) { + $types['nodewords:' . $record->type] = t('Nodewords: @type - @non_empty records with values, @total total.', + array( + '@type' => $keys[$record->type], + '@non_empty' => $record->id_count, + '@total' => $all[$record->type], + )); + } + + return $types; +} + +/** + * Migrates Nodewords data to the Metatag module. + */ +function _metatag_importer_import(array $types = array()) { + $batch = array( + 'title' => t('Importing Nodewords data..'), + 'operations' => array( + array('_metatag_importer_migrate', array($types)), + ), + 'finished' => '_metatag_importer_finished', + 'file' => drupal_get_path('module', 'metatag_importer') . '/metatag_importer.admin.inc', + ); + batch_set($batch); + + // Kick off the batch. + batch_process(); +} + +/** + * Migrates Nodewords data to the Metatag module. + */ +function _metatag_importer_migrate(array $types = array(), &$context = array()) { + // Process this number of {nodewords} records at a time. + $limit = 50; + + if (empty($context['sandbox'])) { + // @todo Expand this so it can handle other types of things. + foreach ($types as $key => $val) { + $types[$key] = str_replace('nodewords:', '', $val); + } + + $context['sandbox']['progress'] = 0; + $context['sandbox']['current'] = 0; + $query = db_select('nodewords', 'nw') + ->fields('nw', array('mtid')) + ->orderBy('nw.mtid'); + if (!empty($types)) { + $query->condition('nw.type', $types, 'IN'); + } + $context['sandbox']['dataset'] = array_keys($query->execute()->fetchAllAssoc('mtid', PDO::FETCH_ASSOC)); + $context['sandbox']['max'] = count($context['sandbox']['dataset']); + + // Track all of the entities that could not be loaded. + $context['sandbox']['skipped'] = array(); + } + + // Retrieve Nodewords data. + $query = db_select('nodewords', 'nw') + ->fields('nw', array('mtid', 'type', 'id', 'name', 'content')) + // Continue on from the last record that was processed. + ->condition('nw.mtid', $context['sandbox']['current'], '>') + ->orderBy('nw.mtid'); + // @todo Finish off / test the $types handling. + // if (!empty($types)) { + // $query->condition('nw.type', $types, 'IN'); + // } + $query->range(0, $limit); + $results = $query->execute(); + + // Records that are being converted. + $records = array(); + + // Track records that are converted and will be ready to be deleted. + $to_delete = array(); + + // Convert Nodewords data into the Metatag format. + foreach ($results as $result) { + // Log the progress. + $context['sandbox']['current'] = $result->mtid; + $context['sandbox']['progress']++; + + // Convert the Nodewords record 'type' into something Metatag can use. + $type = _metatag_importer_convert_type($result->type); + + // Skip record types we're not handling just yet. + if (empty($type)) { + continue; + } + + // This could be an entity ID, but also possibly just a placeholder integer. + $record_id = $result->id; + + // Check if this record was skipped previously. + if (isset($context['sandbox']['skipped'][$type][$record_id])) { + // Delete this record anyway. + $to_delete[] = $result->mtid; + continue; + } + + // If this record is for an entity, verify that the entity exists. + if (in_array($type, array('node', 'taxonomy_term', 'user'))) { + $entity = entity_load($type, array($record_id)); + if (empty($entity)) { + $context['sandbox']['skipped'][$type][$record_id] = $record_id; + watchdog('metatag_importer', 'Unable to load @entity_type ID @id', array('@entity_type' => $type, '@id' => $record_id), WATCHDOG_WARNING); + + // Delete this record anyway. + $to_delete[] = $result->mtid; + continue; + } + } + + // Process the meta tag value, possibly also rename the meta tag name + // itself. + list($meta_tag, $value) = _metatag_importer_convert_data($result->name, unserialize($result->content)); + + // Don't import empty values. + if (!empty($value)) { + // Add the value to the stack. + $records[$type][$record_id][$meta_tag] = $value; + } + + // Note that this record is ready to be deleted. + $to_delete[] = $result->mtid; + } + + // Update or create Metatag records. + foreach ($records as $type => $data) { + foreach ($data as $record_id => $values) { + switch ($type) { + // Standard D7 entities are converted to {metatag} records using + // metatag_metatags_save(). + case 'node': + case 'taxonomy_term': + case 'user': + // watchdog('metatag_importer', 'Importing meta tags for @entity_type ID @id..', array('@entity_type' => $type, '@id' => $record_id), WATCHDOG_INFO); + $entity = entity_load($type, array($record_id)); + $entity = reset($entity); + $langcode = metatag_entity_get_language($type, $entity); + list($entity_id, $revision_id, $bundle) = entity_extract_ids($type, $entity); + + // Add these meta tags to the entity, overwriting anything that's + // already there. + foreach ($values as $name => $value) { + $entity->metatags[$langcode][$name] = $value; + } + metatag_metatags_save($type, $entity_id, $revision_id, $entity->metatags, $langcode); + // watchdog('metatag_importer', 'Imported meta tags for @entity_type ID @id.', array('@entity_type' => $type, '@id' => $record_id), WATCHDOG_INFO); + break; + + // Other Nodewords settings are converted to {metatag_config} records + // using metatag_config_save(). + case 'global': + case 'global:frontpage': + case 'global:404': + $config = metatag_config_load($type); + + // If a configuration was not found create a config object. + if (empty($config)) { + $config = (object) array( + 'instance' => $type, + ); + } + + // Add these meta tags to the configuration, overwriting anything + // that's already there. + foreach ($values as $name => $value) { + $config->config[$name] = $value; + } + + // Save the configuration. + metatag_config_save($config); + break; + + // // A 'vocabulary' setting becomes a default configuration. + // case 'vocabulary': + // $metatags = metatag_metatags_load($record->entity_type, $record->entity_id); + // $metatags = array_merge($metatags, $record->data); + // $vocabulary = taxonomy_vocabulary_load($record->entity_id); + // metatag_metatags_save($record->entity_type, $record->entity_id, $vocabulary->vid, $metatags, $vocabulary->language); + // break; + } + } + } + + // Delete some records. + if (!empty($to_delete)) { + db_delete('nodewords') + ->condition('mtid', $to_delete) + ->execute(); + } + + $context['finished'] = (empty($context['sandbox']['max']) || $context['sandbox']['progress'] >= $context['sandbox']['max']) ? TRUE : ($context['sandbox']['progress'] / $context['sandbox']['max']); + + if ($context['finished'] === TRUE) { + drupal_set_message(t('Imported @imported Nodewords records.', array('@imported' => $context['sandbox']['progress']))); + if (!empty($context['sandbox']['skipped'])) { + drupal_set_message(t('@skipped records were skipped because the corresponding entities were previously deleted.', array('@skipped' => count($context['sandbox']['skipped'])))); + } + } +} + +/** + * BatchAPI callback for when the import finishes. + */ +function _metatag_importer_finished($success, $results, $operations) { + if ($success) { + // Here we do something meaningful with the results. + $message = t("!count items were processed.", array( + '!count' => count($results), + )); + $message .= theme('item_list', array('items' => $results)); + drupal_set_message($message); + } + else { + // An error occurred. + // $operations contains the operations that remained unprocessed. + $error_operation = reset($operations); + $message = t('An error occurred while processing %error_operation with arguments: @arguments', array( + '%error_operation' => $error_operation[0], + '@arguments' => print_r($error_operation[1], TRUE), + )); + drupal_set_message($message, 'error'); + } +} + +/** + * Converts the Nodewords type to a Metatag entity or Metatag config instance. + * + * @param $type + * Nodewords type. + * + * @return + * Metatag entity type or configuration instance. + */ +function _metatag_importer_convert_type($type) { + // define('NODEWORDS_TYPE_DEFAULT', 1); + // define('NODEWORDS_TYPE_ERRORPAGE', 2); + // define('NODEWORDS_TYPE_FRONTPAGE', 3); + // define('NODEWORDS_TYPE_NONE', 0); + // define('NODEWORDS_TYPE_NODE', 5); + // define('NODEWORDS_TYPE_PAGE', 10); + // define('NODEWORDS_TYPE_PAGER', 4); + // define('NODEWORDS_TYPE_TERM', 6); + // define('NODEWORDS_TYPE_TRACKER', 7); + // define('NODEWORDS_TYPE_USER', 8); + // define('NODEWORDS_TYPE_VOCABULARY', 9); + switch ($type) { + case 1: + return 'global'; + + case 2: + return 'global:404'; + + case 3: + return 'global:frontpage'; + + // @todo Not yet sure how to handle pager items? + // case 4: + // return 'pager'; + + case 5: + return 'node'; + + case 6: + return 'taxonomy_term'; + + // @todo Not sure what to do with tracker pages. + // case 7: + // return 'tracker'; + + case 8: + return 'user'; + + // @todo Vocabulary records need to be converted to a config for that entity + // bundle. + // case 9: + // return 'vocabulary'; + + // @todo Page records need to be converted to Context definitions. + // case 10: + // return 'page'; + } + + return FALSE; +} + +/** + * Converts a meta tag's name and value from Nodewords to Metatag format. + * + * @param $name + * Meta tag name. + * @param $value + * Meta tag value in Nodewords format. + * + * @return + * The two arguments returned after being converted, in an array. + */ +function _metatag_importer_convert_data($name, $value) { + // Initial simplification of simple values. + if (is_array($value) && isset($value['value']) && count($value) === 1 && empty($value['value'])) { + $value = FALSE; + } + + // Reformat the meta tag data, and possibly name. + switch ($name) { + // The Dublin Core date value was stored as three separarate strings. + case 'dcterms.date': + // Skip this value if it doesn't contain an array of three values. + if (!is_array($value) || empty($value['month']) || empty($value['day']) || empty($value['year'])) { + $value = FALSE; + } + else { + $date = mktime(0, 0, 0, $value['month'], $value['day'], $value['year']); + $value = date('Y-m-d\TH:iP', $date); + } + break; + + // The location meta tag gets renamed and converted to a semi-colon + // -separated string. + case 'location': + // Catch empty values. + if (!is_array($value) || empty($value['latitutde']) || empty($value['longitude'])) { + $value = FALSE; + } + else { + $name = 'geo.position'; + $value = implode(';', $value); + } + break; + + // These values always seem to be wrong, just use the Metatag defaults. + case 'og:type': + $value = FALSE; + break; + + // Nodewords handle the title tag differently. + case 'page_title': + $name = 'title'; + // Remove two options that are no longer used. + unset($value['append']); + unset($value['divider']); + break; + + // A bug in Nodewords resulted in lots of junk data for this meta tag. + case 'revisit-after': + if (isset($value['value']) && intval($value['value']) === 1) { + $value = FALSE; + } + + // Robots needs some extra processing. + case 'robots': + if (is_array($value)) { + $robot_data = array(); + + // Convert each value to display the name if it is "on" and 0 if it is + // off. + foreach ($value as $robot_key => $robot_val) { + // Ignore junk values. + if ($robot_key == 'value') { + continue; + } + $robot_data[$robot_key] = ($robot_val == 0 ? 0 : $robot_key); + } + + // Filter out empty values. + $robot_data = array_filter($robot_data); + + // Catch empty values. + if (empty($robot_data)) { + $value = FALSE; + } + // Return any data that's remaining. + else { + $value = $robot_data; + } + } + else { + $value = FALSE; + } + break; + + // This meta tag was renamed. + case 'shorturl': + $name = 'shortlink'; + break; + + // Everything else should be ok. + default: + // Nothing to see here. + } + + // A final tidy-up. + if (is_array($value)) { + foreach ($value as $key => $val) { + $value[$key] = trim($val); + } + $value = array_filter($value); + } + + return array($name, $value); +} + + +/** + * The following will not be converted because they refer to site-wide defaults + * that should be customized appropriately based on the D7 site's content type + * architecture. + */ + +// 'nodewords_metatags_generation_method_' . $type: +// 0 - NODEWORDS_GENERATION_NEVER - never auto-generate the string. +// 1 - NODEWORDS_GENERATION_WHEN_EMPTY - when the field is empty. Default. +// 2 - NODEWORDS_GENERATION_ALWAYS - always use the generated string. + +// 'nodewords_metatags_generation_method_' . $type: +// 1 - NODEWORDS_GENERATION_BODY - use the body field. +// 2 - NODEWORDS_GENERATION_TEASER - use the node teaser. Default. +// 3 - NODEWORDS_GENERATION_TEASER_BODY - use teaser, failover to body if empty. diff --git a/sites/all/modules/metatag/metatag_importer/metatag_importer.drush.inc b/sites/all/modules/metatag/metatag_importer/metatag_importer.drush.inc new file mode 100644 index 0000000..394a46a --- /dev/null +++ b/sites/all/modules/metatag/metatag_importer/metatag_importer.drush.inc @@ -0,0 +1,74 @@ + dt('Convert data from Nodewords into Metatag.'), + // 'drupal dependencies' => array('metatag'), + // ); + + $items['metatag-convert-page-title'] = array( + 'description' => dt('Convert data from Page Title into Metatag.'), + 'drupal dependencies' => array('metatag'), + ); + + return $items; +} + +/** + * Callback to convert all Nodewords data. + */ +function drush_metatag_importer_metatag_convert_nodewords() { + if (!drush_confirm(dt('Ready to convert all data from Nodewords?'))) { + return; + } + + // Need to make sure the Nodewords table actually exists. + if (!db_table_exists('nodewords')) { + drush_set_error('metatag_importer', dt('Could not find the nodewords table.')); + return; + } + + // Offload all of the logic to the code contained in the admin file. + include('metatag_importer.admin.inc'); + + // Start the import. + // @todo This isn't working. + _metatag_importer_import(); + + drush_print(dt('Data converesion finished.')); +} + +/** + * Callback to convert Page Title data. + */ +function drush_metatag_importer_metatag_convert_page_title() { + if (!db_table_exists('page_title')) { + drush_set_error('metatag_importer', dt('Could not find the page_title table!')); + return; + } + + $records = db_query("SELECT COUNT(id) FROM {page_title} WHERE type IN ('node', 'taxonomy_term', 'user')")->fetchField(); + + if (empty($records)) { + return dt('There are no page_title records to convert!'); + } + + if (!drush_confirm(dt('Ready to convert !count record(s) from Page Title?', array('!count' => $records)))) { + return; + } + + include('metatag_importer.page_title.inc'); + + // Start the importer. + $count = metatag_importer_for_page_title(); + + drush_print(dt('Converted !count record(s) from the Page Title module.', array('!count' => $count))); +} diff --git a/sites/all/modules/metatag/metatag_importer/metatag_importer.info b/sites/all/modules/metatag/metatag_importer/metatag_importer.info new file mode 100644 index 0000000..720f3c3 --- /dev/null +++ b/sites/all/modules/metatag/metatag_importer/metatag_importer.info @@ -0,0 +1,14 @@ +name = Metatag Importer +description = Import data from other modules into Metatag. +core = 7.x +package = SEO + +; Need Metatag. +dependencies[] = metatag + +; Information added by Drupal.org packaging script on 2015-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_importer/metatag_importer.module b/sites/all/modules/metatag/metatag_importer/metatag_importer.module new file mode 100644 index 0000000..84dc886 --- /dev/null +++ b/sites/all/modules/metatag/metatag_importer/metatag_importer.module @@ -0,0 +1,22 @@ + 'Importer', + 'description' => 'Migrate settings and data from the Drupal 6 Nodewords module to the Drupal 7 Metatag module.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('metatag_importer_form'), + 'access arguments' => array('administer meta tags.'), + 'file' => 'metatag_importer.admin.inc', + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} diff --git a/sites/all/modules/metatag/metatag_importer/metatag_importer.page_title.inc b/sites/all/modules/metatag/metatag_importer/metatag_importer.page_title.inc new file mode 100644 index 0000000..98475b7 --- /dev/null +++ b/sites/all/modules/metatag/metatag_importer/metatag_importer.page_title.inc @@ -0,0 +1,119 @@ +fields('pt', array('type', 'id', 'page_title')) + ->execute(); + + // Get general metatag config settings. + $metatag_config_global = metatag_config_load('global'); + $metatag_config_node = metatag_config_load('node'); + $metatag_config_taxonomy_term = metatag_config_load('taxonomy_term'); + $metatag_config_user = metatag_config_load('user'); + + // Track any records that are skipped. + $skipped = array(); + + // Loop over each of the page_title records. + while ($pt_data = $page_titles->fetchObject()) { + $entity_type = $pt_data->type; + $entity_id = $pt_data->id; + + // Load the entity. + $entity = entity_load($entity_type, array($entity_id)); + if (empty($entity)) { + $skipped[] = $entity_type . ':' . $entity_id; + continue; + } + + $entity = reset($entity); + + // Extract additional values from the entity. + $langcode = metatag_entity_get_language($entity_type, $entity); + list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); + + // Load any possible existing meta tags for this object. + $data = metatag_metatags_load($entity_type, $entity_id); + + // Drop back one level because the results will be keyed by revision_id. + if (!empty($data)) { + $data = reset($data); + } + + switch ($entity_type) { + case 'node': + $metatag_config_node_type = metatag_config_load('node:' . $entity->type); + + if (!empty($metatag_config_node_type) && isset($metatag_config_node_type->config['title'])) { + $title_setting = $metatag_config_node_type->config['title']['value']; + } + else if (isset($metatag_config_node->config['title'])) { + $title_setting = $metatag_config_node->config['title']['value']; + } + else { + $title_setting = $metatag_config_global->config['title']['value']; + } + $metatag_title = str_replace('[current-page:title]', trim($pt_data->page_title), $title_setting); + $metatag_title = str_replace('[node:title]', trim($pt_data->page_title), $metatag_title); + break; + + case 'taxonomy_term': + $metatag_config_vocabulary_type = metatag_config_load('taxonomy_term:' . $entity->vocabulary_machine_name); + + if (!empty($metatag_config_vocabulary_type) && isset($metatag_config_vocabulary_type->config['title'])) { + $title_setting = $metatag_config_vocabulary_type->config['title']['value']; + } + elseif (isset($metatag_config_taxonomy_term->config['title'])) { + $title_setting = $metatag_config_taxonomy_term->config['title']['value']; + } + else { + $title_setting = $metatag_config_global->config['title']['value']; + } + $metatag_title = str_replace('[current-page:title]', trim($pt_data->page_title), $title_setting); + $metatag_title = str_replace('[term:name]', trim($pt_data->page_title), $metatag_title); + break; + + case 'user': + if (isset($metatag_config_user->config['title'])) { + $title_setting = $metatag_config_user->config['title']['value']; + } + else { + $title_setting = $metatag_config_global->config['title']['value']; + } + $metatag_title = str_replace('[current-page:title]', trim($pt_data->page_title), $title_setting); + $metatag_title = str_replace('[user:name]', trim($pt_data->page_title), $metatag_title); + break; + + // Something else? Leave such records for another time. + default: + $skipped[] = $entity_type . ':' . $entity_id; + // Jump back to the outer for() loop. + continue 2; + } + $data[$langcode]['title']['value'] = $metatag_title; + + metatag_metatags_save($entity_type, $entity_id, $revision_id, $data, $langcode); + $converted += db_delete('page_title') + ->condition('type', $entity_type) + ->condition('id', $entity_id) + ->execute(); + } + + // Log any records that were skipped. + if (!empty($skipped)) { + watchdog('metatag_importer', "Failed to convert the following page_title records: :records", array(':records' => implode(', ', $skipped))); + } + + return $converted; +} diff --git a/sites/all/modules/metatag/metatag_mobile/metatag_mobile.info b/sites/all/modules/metatag/metatag_mobile/metatag_mobile.info new file mode 100644 index 0000000..98a87e1 --- /dev/null +++ b/sites/all/modules/metatag/metatag_mobile/metatag_mobile.info @@ -0,0 +1,12 @@ +name = "Metatag: Mobile & UI Adjustments" +description = "Provides support for meta tags used to control the mobile browser experience." +package = SEO +core = 7.x +dependencies[] = metatag + +; Information added by Drupal.org packaging script on 2015-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_mobile/metatag_mobile.metatag.inc b/sites/all/modules/metatag/metatag_mobile/metatag_mobile.metatag.inc new file mode 100644 index 0000000..4a8e247 --- /dev/null +++ b/sites/all/modules/metatag/metatag_mobile/metatag_mobile.metatag.inc @@ -0,0 +1,64 @@ + t('Mobile & UI Adjustments'), + 'description' => t("Meta tags used to control the mobile browser experience. Some of these meta tags have been replaced by newer mobile browsers. These meta tags usually only need to be set globally, rather than per-page."), + 'form' => array( + '#weight' => 80, + ), + ); + + $weight = 80; + + // Default values for each meta tag. + $tag_info_defaults = array( + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'mobile', + ); + + $info['tags']['theme-color'] = array( + 'label' => t('Theme Color'), + 'description' => t('A color in hexidecimal format, e.g. "#0000ff" for blue; must include the "#" symbol. Used by some browsers to control the background color of the toolbar, the color used with an icon, etc.'), + 'weight' => ++$weight, + ) + $tag_info_defaults; + + $info['tags']['MobileOptimized'] = array( + 'label' => t('Mobile Optimized'), + 'description' => t('Using the value "width" tells certain mobile Internet Explorer browsers to display as-is, without being resized. Alternatively a numerical width may be used to indicate the desired page width the page should be rendered in: "240" is the suggested default, "176" for older browsers or "480" for newer devices with high DPI screens.'), + 'weight' => ++$weight, + 'multiple' => TRUE, + ) + $tag_info_defaults; + + $info['tags']['HandheldFriendly'] = array( + 'label' => t('Handheld-Friendly'), + 'description' => t('Some older mobile browsers will expect this meta tag to be set to "true" to indicate that the site has been designed with mobile browsers in mind.'), + 'weight' => ++$weight, + 'multiple' => TRUE, + ) + $tag_info_defaults; + + $info['tags']['viewport'] = array( + 'label' => t('Viewport'), + 'description' => t('Used by most contemporary browsers to control the display for mobile browsers. Please read a guide on responsive web design for details of what values to use.'), + 'weight' => ++$weight, + ) + $tag_info_defaults; + + $info['tags']['cleartype'] = array( + 'label' => t('Cleartype'), + 'description' => t('A legacy meta tag for older versions of Internet Explorer on Windows, use the value "on" to enable it; this tag is ignored by all other browsers.'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_http_equiv', + ), + ) + $tag_info_defaults; + + return $info; +} diff --git a/sites/all/modules/metatag/metatag_mobile/metatag_mobile.module b/sites/all/modules/metatag/metatag_mobile/metatag_mobile.module new file mode 100644 index 0000000..682d500 --- /dev/null +++ b/sites/all/modules/metatag/metatag_mobile/metatag_mobile.module @@ -0,0 +1,19 @@ + 1); + } +} + +/* +fb:admins +fb:app_id +*/ diff --git a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.info b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.info new file mode 100644 index 0000000..37ba77c --- /dev/null +++ b/sites/all/modules/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-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.install b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.install new file mode 100644 index 0000000..efba48f --- /dev/null +++ b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.install @@ -0,0 +1,62 @@ + 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; +} + +/** + * 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.')); +} + +/** + * The Open Graph Products meta tags are now in a new submodule. + */ +function metatag_opengraph_update_7102() { + drupal_set_message(t('The Open Graph Products meta tags have been moved into the new "Metatag: Open Graph Products" submodule.')); +} diff --git a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.metatag.inc b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.metatag.inc new file mode 100644 index 0000000..75605f1 --- /dev/null +++ b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.metatag.inc @@ -0,0 +1,623 @@ +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 Open Graph meta tags 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., IMDb.'), + '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., movie.'), + 'weight' => ++$weight, + 'select_or_other' => TRUE, + 'form' => array( + '#type' => 'select', + '#options' => _metatag_opengraph_type_options(), + '#empty_option' => t('- None -'), + ), + 'devel_generate' => array( + 'type' => 'select', + ), + ) + $og_defaults; + + $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., http://www.imdb.com/title/tt0117500/.'), + '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., The Rock.'), + '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 ISO 8601 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, + 'image' => 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, + 'image' => 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, + 'image' => 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. Note: there should be one value for each image, and having more than there are images may cause problems.'), + '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, + 'replaces' => array( + 'og:street-address', + ), + ) + $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, + 'replaces' => array( + 'og:postal-code', + ), + ) + $og_defaults; + $info['tags']['og:country_name'] = array( + 'label' => t('Country name'), + 'weight' => ++$weight, + 'replaces' => array( + 'og:country-name', + ), + ) + $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 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 ISO 8601 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 ISO 8601 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 ISO 8601 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 International Standard Book Number, 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 ISO 8601 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; +} diff --git a/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.module b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.module new file mode 100644 index 0000000..c2d194b --- /dev/null +++ b/sites/all/modules/metatag/metatag_opengraph/metatag_opengraph.module @@ -0,0 +1,70 @@ + 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 +*/ diff --git a/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.info b/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.info new file mode 100644 index 0000000..4897e57 --- /dev/null +++ b/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.info @@ -0,0 +1,13 @@ +name = Metatag:OpenGraph Products +description = Provides additional Open Graph Protocol meta tags for describing products. +package = SEO +core = 7.x +dependencies[] = metatag +dependencies[] = metatag_opengraph + +; Information added by Drupal.org packaging script on 2015-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.metatag.inc b/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.metatag.inc new file mode 100644 index 0000000..6c14735 --- /dev/null +++ b/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.metatag.inc @@ -0,0 +1,164 @@ + t('Open Graph - Products'), + 'description' => t("These Open Graph meta tags for describing products.", array('@ogp' => 'http://ogp.me/')), + 'form' => array( + '#weight' => 51, + ), + ); + + // Default values for each meta tag. + $defaults = array( + 'description' => '', + 'class' => 'DrupalTextMetaTag', + 'group' => 'open-graph-products', + 'element' => array( + '#theme' => 'metatag_property', + ), + ); + + $weight = 50; + + $info['tags']['product:price:amount'] = array( + 'label' => t('Price'), + 'description' => t("The numeric price with decimal point, without currency indicator. Values below 0.01 may not be supported by clients."), + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:price:currency'] = array( + 'label' => t('Currency'), + 'description' => t("The currency for the price (if any)."), + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:availability'] = array( + 'label' => t('Availability'), + 'description' => t('Case-insensitive string, possible values: "instock", "pending", "oos"; per Facebook\' documentation.', array('@url' => 'https://developers.facebook.com/docs/reference/opengraph/object-type/product')), + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:brand'] = array( + 'label' => t('Brand'), + 'description' => '', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:upc'] = array( + 'label' => t('UPC'), + 'description' => '', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:ean'] = array( + 'label' => t('EAN'), + 'description' => '', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:isbn'] = array( + 'label' => t('ISBN'), + 'description' => '', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:plural_title'] = array( + 'label' => t('Plural Title'), + 'description' => 'A title to be used to describe multiple items of this product', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:retailer'] = array( + 'label' => t('Retailer ID'), + 'description' => 'A Facebook ID (or reference to the profile) of the retailer.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:retailer_title'] = array( + 'label' => t('Retailer Name'), + 'description' => 'The name of the retailer.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:retailer_part_no'] = array( + 'label' => t('Retailer SKU/Product Number'), + 'description' => 'A retailer part number.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:mfr_part_no'] = array( + 'label' => t('Manufacturer SKU/Part Number'), + 'description' => 'A manufacturer part number.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:size'] = array( + 'label' => t('Size'), + 'description' => 'A size describing the product, such as S, M, L.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:product_link'] = array( + 'label' => t('Product Link'), + 'description' => 'A link to find out more about the product', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:category'] = array( + 'label' => t('Category'), + // 'description' => 'A category', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:color'] = array( + 'label' => t('Color'), + // 'description' => 'The product\'s color.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:material'] = array( + 'label' => t('Material'), + 'description' => 'A description of the material used to make the product.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:pattern'] = array( + 'label' => t('Pattern'), + 'description' => 'A description of the pattern used.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:shipping_cost:amount'] = array( + 'label' => t('Shipping Cost Amount'), + 'description' => 'The shipping costs.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:shipping_cost:currency'] = array( + 'label' => t('Shipping Cost Currency'), + 'description' => 'The shipping cost currency.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:weight:value'] = array( + 'label' => t('Product Weight'), + 'description' => 'The weight, without shipping materials.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:weight:units'] = array( + 'label' => t('Product Weight Units'), + 'description' => 'The unit of weight.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:shipping_weight:value'] = array( + 'label' => t('Shipping Weight'), + 'description' => 'The shipping weight.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:shipping_weight:units'] = array( + 'label' => t('Shipping Weight Units'), + 'description' => 'The unit of shipping weight.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:expiration_time'] = array( + 'label' => t('Expiration'), + 'description' => 'A time representing when the product expired, or will expire.', + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['product:condition'] = array( + 'label' => t('Condition'), + 'description' => 'The condition of the product.', + 'weight' => ++$weight, + ) + $defaults; + + return $info; +} diff --git a/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.module b/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.module new file mode 100644 index 0000000..d761369 --- /dev/null +++ b/sites/all/modules/metatag/metatag_opengraph_products/metatag_opengraph_products.module @@ -0,0 +1,14 @@ + 1); + } +} diff --git a/sites/all/modules/metatag/metatag_panels/README.txt b/sites/all/modules/metatag/metatag_panels/README.txt new file mode 100644 index 0000000..58f9b6c --- /dev/null +++ b/sites/all/modules/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 existent 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 diff --git a/sites/all/modules/metatag/metatag_panels/metatag_panels.info b/sites/all/modules/metatag/metatag_panels/metatag_panels.info new file mode 100644 index 0000000..ab2fe07 --- /dev/null +++ b/sites/all/modules/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-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_panels/metatag_panels.metatag.inc b/sites/all/modules/metatag/metatag_panels/metatag_panels.metatag.inc new file mode 100644 index 0000000..14a1981 --- /dev/null +++ b/sites/all/modules/metatag/metatag_panels/metatag_panels.metatag.inc @@ -0,0 +1,13 @@ + t('Panels')); + return $info; +} diff --git a/sites/all/modules/metatag/metatag_panels/metatag_panels.module b/sites/all/modules/metatag/metatag_panels/metatag_panels.module new file mode 100644 index 0000000..b65daf6 --- /dev/null +++ b/sites/all/modules/metatag/metatag_panels/metatag_panels.module @@ -0,0 +1,218 @@ + 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); + + // Add CTools substitutions list to the form. + $rows = array(); + foreach ($contexts as $context) { + foreach (ctools_context_get_converters('%' . check_plain($context->keyword) . ':', $context) as $keyword => $title) { + $rows[] = array( + check_plain($keyword), + t('@identifier: @title', array('@title' => $title, '@identifier' => $context->identifier)), + ); + } + } + if (!empty($rows)) { + $form['contexts'] = array( + '#title' => t('Substitutions'), + '#type' => 'fieldset', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + $header = array(t('Keyword'), t('Value')); + $form['contexts']['context'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $rows))); + $form['contexts']['#states'] = array( + 'visible' => array( + ':input[name="metatags_enabled"]' => array('checked' => TRUE), + ), + ); + } + + // 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; + } + + // Substitute Panels context variables. + foreach ($metatags as $metatag => &$data) { + if (is_string($data['value']) && strpos($data['value'], '%') !== FALSE) { + $data['value'] = ctools_context_keyword_substitute($data['value'], array(), $context['handler']->conf['display']->context); + } + } + + // 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) { + // Render CTools context substitution values prior to rendering the meta + // tag. + if (is_string($data['value'])) { + $data['value'] = ctools_context_keyword_substitute(trim($data['value']), array(), $task_contexts); + } + $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; + } +} diff --git a/sites/all/modules/metatag/metatag_twitter_cards/README.txt b/sites/all/modules/metatag/metatag_twitter_cards/README.txt new file mode 100644 index 0000000..8c00ded --- /dev/null +++ b/sites/all/modules/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 diff --git a/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.info b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.info new file mode 100644 index 0000000..d383434 --- /dev/null +++ b/sites/all/modules/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-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc new file mode 100644 index 0000000..74f6a86 --- /dev/null +++ b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.metatag.inc @@ -0,0 +1,417 @@ +instance) { + case 'global': + $config->config += array( + 'twitter:card' => array('value' => 'summary'), + 'twitter:title' => array('value' => '[current-page:title]'), + 'twitter:url' => array('value' => '[current-page:url:absolute]'), + ); + break; + + case 'global:frontpage': + $config->config += array( + 'twitter:description' => array('value' => '[site:slogan]'), + 'twitter:title' => array('value' => '[site:name]'), + 'twitter:url' => array('value' => '[site:url]'), + ); + break; + + // On error pages point everything to the homepage. + case 'global:403': + case 'global:404': + $config->config += array( + 'twitter:title' => array('value' => '[site:name]'), + 'twitter:url' => array('value' => '[site:url]'), + ); + break; + + case 'node': + $config->config += array( + 'twitter:description' => array('value' => '[node:summary]'), + 'twitter:title' => array('value' => '[node:title]'), + ); + break; + + case 'taxonomy_term': + $config->config += array( + 'twitter:description' => array('value' => '[term:description]'), + 'twitter:title' => array('value' => '[term:name]'), + ); + break; + + case 'user': + $config->config += array( + 'twitter:title' => array('value' => '[user:name]'), + ); + if (variable_get('user_pictures')) { + $config->config += array( + 'twitter:image:src' => array('value' => '[user:picture:url]'), + ); + } + break; + } + } +} + +/** + * Implements hook_metatag_info(). + */ +function metatag_twitter_cards_metatag_info() { + $info['groups']['twitter-cards'] = array( + 'label' => t('Twitter card'), + 'description' => t('A set of meta tags specially for controlling the summaries displayed when content is shared on Twitter.', array('!url' => 'https://twitter.com/')), + 'form' => array( + '#weight' => 60, + ), + ); + + // Twitter Cards meta tags stack after the Open Graph tags. + $weight = 40; + + // Defaults used for all cards. + $defaults = array( + 'class' => 'DrupalTextMetaTag', + 'group' => 'twitter-cards', + 'element' => array( + '#theme' => 'metatag_twitter_cards', + ), + ); + + $info['tags']['twitter:card'] = array( + 'label' => t('Twitter card type'), + 'description' => t('Notes: no other fields are required for a Summary card, a Photo card requires the \'image\' field, a Media player card requires the \'title\', \'description\', \'media player URL\', \'media player width\', \'media player height\' and \'image\' fields, a Summary Card with Large Image card requires the \'Summary\' field and the \'image\' field, a Gallery Card requires all the \'Gallery Image\' fields, an App Card requires the \'iPhone app ID\' field, the \'iPad app ID\' field and the \'Google Play app ID\' field, a Product Card requires the \'description\' field, the \'image\' field, the \'Label 1\' field, the \'Data 1\' field, the \'Label 2\' field and the \'Data 2\' field.'), + 'weight' => ++$weight, + 'form' => array( + '#type' => 'select', + '#options' => array( + 'summary' => t('Summary (default)'), + 'summary_large_image' => t('Summary with large image'), + 'photo' => t('Photo'), + 'player' => t('Media player'), + 'gallery' => t('Gallery'), + 'app' => t('App'), + 'product' => t('Product'), + ), + '#empty_option' => t('- None -'), + ), + ) + $defaults; + $info['tags']['twitter:site'] = array( + 'label' => t('Site\'s Twitter account'), + 'description' => t('The @username for the website, which will be displayed in the Card\'s footer; must include the @ symbol.'), + 'context' => array('global'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'twitter', + ), + ) + $defaults; + $info['tags']['twitter:site:id'] = array( + 'label' => t('Site\'s Twitter account ID'), + 'description' => t('The numerical Twitter account ID for the website, which will be displayed in the Card\'s footer.'), + 'context' => array('global'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'integer', + ), + ) + $defaults; + $info['tags']['twitter:creator'] = array( + 'label' => t('Creator\'s Twitter account'), + 'description' => t('The @username for the content creator / author for this page, including the @ symbol.'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'twitter', + ), + ) + $defaults; + $info['tags']['twitter:creator:id'] = array( + 'label' => t('Creator\'s Twitter account ID'), + 'description' => t('The numerical Twitter account ID for the content creator / author for this page.'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'integer', + ), + ) + $defaults; + $info['tags']['twitter:url'] = array( + 'label' => t('Page URL'), + 'description' => t('The permalink / canonical URL of the current page.'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'canonical', + ), + ) + $defaults; + $info['tags']['twitter:title'] = array( + 'label' => t('Title'), + 'description' => t('The page\'s title, which should be concise; it will be truncated at 70 characters by Twitter. This field is required unless this the \'type\' field is set to "photo".'), + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['twitter:description'] = array( + 'label' => t('Description'), + 'description' => t('A description that concisely summarizes the content of the page, as appropriate for presentation within a Tweet. Do not re-use the title text as the description, or use this field to describe the general services provided by the website. The string will be truncated, by Twitter, at the word to 200 characters.'), + 'weight' => ++$weight, + ) + $defaults; + $info['tags']['twitter:image:src'] = 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. Images larger than 120x120px will be resized and cropped square based on longest dimension. Images smaller than 60x60px will not be shown. If the \'type\' is set to Photo then the image must be at least 280x150px.'), + 'image' => TRUE, + 'weight' => ++$weight, + 'replaces' => array( + 'twitter:image', + ), + 'devel_generate' => array( + 'type' => 'image', + ), + ) + $defaults; + $info['tags']['twitter:image:width'] = array( + 'label' => t('Image width'), + 'description' => t('The width of the image being linked to, in pixels.'), + 'weight' => ++$weight, + 'dependencies' => array( + array( + 'dependency' => 'twitter:image:src', + 'attribute' => 'value', + 'condition' => 'filled', + 'value' => TRUE, + ), + ), + 'devel_generate' => array( + 'type' => 'integer', + ), + ) + $defaults; + $info['tags']['twitter:image:height'] = array( + 'label' => t('Image height'), + 'description' => t('The height of the image being linked to, in pixels.'), + 'weight' => ++$weight, + 'dependencies' => array( + array( + 'dependency' => 'twitter:image:src', + 'attribute' => 'value', + 'condition' => 'filled', + 'value' => TRUE, + ), + ), + 'devel_generate' => array( + 'type' => 'integer', + ), + ) + $defaults; + + // 'gallery' cards. + $gallery_defaults = array( + 'image' => TRUE, + 'dependencies' => array( + array( + 'dependency' => 'twitter:card', + 'attribute' => 'value', + 'condition' => 'value', + 'value' => 'gallery', + ), + ), + ) + $defaults; + $info['tags']['twitter:image0'] = array( + 'label' => t('1st Gallery Image'), + 'description' => t('A URL to the image representing the first photo in your gallery.'), + 'weight' => ++$weight, + ) + $gallery_defaults; + $info['tags']['twitter:image1'] = array( + 'label' => t('2nd Gallery Image'), + 'description' => t('A URL to the image representing the second photo in your gallery.'), + 'weight' => ++$weight, + ) + $gallery_defaults; + $info['tags']['twitter:image2'] = array( + 'label' => t('3rd Gallery Image'), + 'description' => t('A URL to the image representing the third photo in your gallery.'), + 'weight' => ++$weight, + ) + $gallery_defaults; + $info['tags']['twitter:image3'] = array( + 'label' => t('4th Gallery Image'), + 'description' => t('A URL to the image representing the fourth photo in your gallery.'), + 'weight' => ++$weight, + ) + $gallery_defaults; + + // 'player' cards. + $player_defaults = array( + 'dependencies' => array( + array( + 'dependency' => 'twitter:card', + 'attribute' => 'value', + 'condition' => 'value', + 'value' => 'player', + ), + ), + ) + $defaults; + $info['tags']['twitter:player'] = array( + 'label' => t('Media player URL'), + 'description' => t('The full URL for loading a media player. Required when using a Media player card.'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'url', + ), + ) + $player_defaults; + $info['tags']['twitter:player:width'] = array( + 'label' => t('Media player width'), + 'description' => t('The width of the media player iframe, in pixels. Required when using a Media player card.'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'integer', + ), + ) + $player_defaults; + $info['tags']['twitter:player:height'] = array( + 'label' => t('Media player height'), + 'description' => t('The height of the media player iframe, in pixels. Required when using a Media player card.'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'integer', + ), + ) + $player_defaults; + $info['tags']['twitter:player:stream'] = array( + 'label' => t('MP4 media stream URL'), + 'description' => t('The full URL for an MP4 video (h.264) or audio (AAC) stream, takes precidence over the other media player field.'), + 'weight' => ++$weight, + 'devel_generate' => array( + 'type' => 'url', + ), + ) + $player_defaults; + $info['tags']['twitter:player:stream:content_type'] = array( + 'label' => t('MP4 media stream MIME type'), + 'description' => t('The MIME type for the media contained in the stream URL, as defined by RFC 4337.', array('!url' => 'http://tools.ietf.org/rfc/rfc4337.txt')), + 'weight' => ++$weight, + 'devel_generate' => array( + 'maxlength' => 1, + ), + ) + $player_defaults; + + // 'app' cards. + $info['tags']['twitter:app:country'] = array( + 'label' => t('App Store Country'), + 'description' => t('If your application is not available in the US App Store, you must set this value to the two-letter country code for the App Store that contains your application.'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:name:iphone'] = array( + 'label' => t('iPhone app name'), + 'description' => t("The name of the iPhone app."), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:id:iphone'] = array( + 'label' => t('iPhone app ID'), + 'description' => t("String value, should be the numeric representation of your iPhone app's ID in the App Store."), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:url:iphone'] = array( + 'label' => t('iPhone app\'s custom URL scheme'), + 'description' => t('The iPhone app\'s custom URL scheme (must include "://" after the scheme name).'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:name:ipad'] = array( + 'label' => t('iPad app name'), + 'description' => t("The name of the iPad app."), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:id:ipad'] = array( + 'label' => t('iPad app ID'), + 'description' => t("String value, should be the numeric representation of your iPad app's ID in the App Store."), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:url:ipad'] = array( + 'label' => t('iPad app\'s custom URL scheme'), + 'description' => t('The iPad app\'s custom URL scheme (must include "://" after the scheme name).'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:name:googleplay'] = array( + 'label' => t('Google Play app name'), + 'description' => t("The name of the app in the Google Play app store."), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:id:googleplay'] = array( + 'label' => t('Google Play app ID'), + 'description' => t("String value, and should be the numeric representation of your app's ID in Google Play."), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + $info['tags']['twitter:app:url:googleplay'] = array( + 'label' => t('Google Play app\'s custom URL scheme'), + 'description' => t('The Google Play app\'s custom URL scheme (must include "://" after the scheme name).'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $defaults; + + // 'product' cards. + $product_defaults = array( + 'dependencies' => array( + array( + 'dependency' => 'twitter:card', + 'attribute' => 'value', + 'condition' => 'value', + 'value' => 'product', + ), + ), + ) + $defaults; + $info['tags']['twitter:label1'] = array( + 'label' => t('Label 1'), + 'description' => t('This field expects a string, and you can specify values for labels such as price, items in stock, sizes, etc.'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $product_defaults; + $info['tags']['twitter:data1'] = array( + 'label' => t('Data 1'), + 'description' => t('This field expects a string, and allows you to specify the types of data you want to offer (price, country, etc.).'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $product_defaults; + $info['tags']['twitter:label2'] = array( + 'label' => t('Label 2'), + 'description' => t('This field expects a string, and you can specify values for labels such as price, items in stock, sizes, etc.'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $product_defaults; + $info['tags']['twitter:data2'] = array( + 'label' => t('Data 2'), + 'description' => t('This field expects a string, and allows you to specify the types of data you want to offer (price, country, etc.).'), + 'weight' => ++$weight, + 'element' => array( + '#theme' => 'metatag_twitter_cards' + ), + ) + $product_defaults; + + return $info; +} diff --git a/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.module b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.module new file mode 100644 index 0000000..b268236 --- /dev/null +++ b/sites/all/modules/metatag/metatag_twitter_cards/metatag_twitter_cards.module @@ -0,0 +1,39 @@ + 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); +} diff --git a/sites/all/modules/metatag/metatag_verification/README.txt b/sites/all/modules/metatag/metatag_verification/README.txt new file mode 100644 index 0000000..f1cd798 --- /dev/null +++ b/sites/all/modules/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. diff --git a/sites/all/modules/metatag/metatag_verification/metatag_verification.info b/sites/all/modules/metatag/metatag_verification/metatag_verification.info new file mode 100644 index 0000000..eb042db --- /dev/null +++ b/sites/all/modules/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-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_verification/metatag_verification.metatag.inc b/sites/all/modules/metatag/metatag_verification/metatag_verification.metatag.inc new file mode 100644 index 0000000..7561b24 --- /dev/null +++ b/sites/all/modules/metatag/metatag_verification/metatag_verification.metatag.inc @@ -0,0 +1,54 @@ + 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 Google.'), + 'weight' => ++$weight, + ) + $defaults; + + $info['tags']['p:domain_verify'] = array( + 'label' => t('Pinterest'), + 'description' => t('A string provided by Pinterest, full details are available from the Pinterest online help.', 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 Bing, full details are available from the Bing online help.', 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 Yandex, full details are available from the Yandex online help.', array('@yandex' => 'http://www.yandex.com/', '@verify_url' => 'http://api.yandex.com/webmaster/doc/dg/reference/hosts-type.xml')), + 'weight' => ++$weight, + ) + $defaults; + + return $info; +} diff --git a/sites/all/modules/metatag/metatag_verification/metatag_verification.module b/sites/all/modules/metatag/metatag_verification/metatag_verification.module new file mode 100644 index 0000000..87f93d3 --- /dev/null +++ b/sites/all/modules/metatag/metatag_verification/metatag_verification.module @@ -0,0 +1,14 @@ + 1); + } +} diff --git a/sites/all/modules/metatag/metatag_views/README.txt b/sites/all/modules/metatag/metatag_views/README.txt new file mode 100644 index 0000000..4398edc --- /dev/null +++ b/sites/all/modules/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 diff --git a/sites/all/modules/metatag/metatag_views/metatag_views.info b/sites/all/modules/metatag/metatag_views/metatag_views.info new file mode 100644 index 0000000..1c9a427 --- /dev/null +++ b/sites/all/modules/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-07-24 +version = "7.x-1.7" +core = "7.x" +project = "metatag" +datestamp = "1437763741" + diff --git a/sites/all/modules/metatag/metatag_views/metatag_views.metatag.inc b/sites/all/modules/metatag/metatag_views/metatag_views.metatag.inc new file mode 100644 index 0000000..8eee9d4 --- /dev/null +++ b/sites/all/modules/metatag/metatag_views/metatag_views.metatag.inc @@ -0,0 +1,33 @@ + 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; +} diff --git a/sites/all/modules/metatag/metatag_views/metatag_views.module b/sites/all/modules/metatag/metatag_views/metatag_views.module new file mode 100644 index 0000000..64506ed --- /dev/null +++ b/sites/all/modules/metatag/metatag_views/metatag_views.module @@ -0,0 +1,106 @@ + 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( + '' . t('Meta tags') . '', + implode('