You are here

metatag.install in Metatag 7

Same filename and directory in other branches
  1. 8 metatag.install

Install, update, and uninstall functions for the metatag module.

File

metatag.install
View source
<?php

/**
 * @file
 * Install, update, and uninstall functions for the metatag module.
 */

/**
 * Implements hook_requirements().
 */
function metatag_requirements($phase) {
  $requirements = array();

  // Ensure translations don't break during installation.
  $t = get_t();
  if ($phase == 'install') {

    // Handle scenarios where the site had the legacy "metatags" module
    // installed but then had Metatag installed on top of it.
    if (function_exists('db_table_exists') && function_exists('db_field_exists') && function_exists('db_query') && Database::isActiveConnection()) {

      // Check if the primary table already exists.
      if (db_table_exists('metatag')) {

        // Check to see if all of the fields exist in the table. If one of the
        // fields does not exist, proceed with the fix.
        $fields = array(
          'entity_type',
          'entity_id',
          'revision_id',
          'language',
          'data',
        );
        foreach ($fields as $field) {

          // This field doesn't exist, so determine what to do.
          if (!db_field_exists('metatag', $field)) {

            // The table contains data, so rename it.
            if (db_query("SELECT COUNT(*) FROM {metatag}")
              ->fetchField() > 0) {
              db_query("RENAME TABLE {metatag} TO {metatag}_legacy");
              $message = 'An out-of-date version of the {metatag} table was discovered. As the table contained data it was renamed with a suffix of "_legacy". This will not prevent installation from continuing, but will need to be dealt with later. See <a href="https://www.drupal.org/node/1391554">https://www.drupal.org/node/1391554</a> for further details.';
            }
            else {
              db_query("DROP TABLE {metatag}");
              $message = 'An out-of-date version of the {metatag} table was discovered. As the table was empty it was simply removed so that it could be recreated in the correct format. Installation may now proceed. See <a href="https://www.drupal.org/node/1391554">https://www.drupal.org/node/1391554</a> for further details.';
            }
            $requirements['metatag'] = array(
              'severity' => REQUIREMENT_WARNING,
              'title' => 'Metatag',
              'value' => $t('Legacy data discovered.'),
              'description' => $t($message),
            );
            drupal_set_message($t($message), 'warning');
            break;
          }
        }
      }
    }
  }
  elseif ($phase == 'runtime') {

    // Work out the release of D7 that is currently running.
    list($major, $minor) = explode('.', VERSION);

    // Strip off any suffixes on the version string, e.g. "17-dev".
    if (strpos('-', $minor)) {
      list($minor, $suffix) = explode('-', $minor);
    }

    // Releases of Drupal older than 7.40 support did not project:module syntax
    // for dependencies.
    if ($minor < 40) {
      $requirements['metatag'] = array(
        'severity' => REQUIREMENT_WARNING,
        'title' => 'Metatag',
        'value' => $t('Upgrade Drupal core to v7.40 or newer'),
        'description' => $t("This older version of Drupal core is missing functionality necessary for Metatag to work correctly, it must be upgraded to version 7.40 or newer."),
      );
    }

    // Add a note if Page Title is also installed.
    if (module_exists('page_title')) {
      $requirements['metatag_page_title'] = array(
        'severity' => REQUIREMENT_WARNING,
        'title' => 'Metatag',
        'value' => $t('Page Title module should be removed'),
        'description' => $t('The Metatag module is able to customize page titles, so running the Page Title module simultaneously can lead to complications. Please follow the instructions to <a href="@page">convert the Page Title settings</a> and uninstall the module.', array(
          '@page' => 'https://www.drupal.org/node/2774833',
        )),
      );
    }

    // Add a note if the deprecated metatag.entity_translation.inc file still
    // exists.
    $filename = 'metatag.entity_translation.inc';
    if (file_exists(dirname(__FILE__) . '/' . $filename)) {
      $requirements['metatag_deprecated_et_file'] = array(
        'severity' => REQUIREMENT_ERROR,
        'title' => 'Metatag',
        'value' => $t('Unwanted :filename file found', array(
          ':filename' => $filename,
        )),
        'description' => $t("The :filename file was removed in v7.x-1.0-beta5 but it still exists in the site's Metatag module's directory and will cause problems. This file needs to be removed. The file's path in the Drupal directory structure is:<br /><code>!short_path</code><br />The file's full path is:<br /><code>!full_path</code>", array(
          ':filename' => $filename,
          '!short_path' => drupal_get_path('module', 'metatag') . '/' . $filename,
          '!full_path' => dirname(__FILE__) . $filename,
        )),
      );
    }

    // Check that Entity_Translation is current.
    if (module_exists('entity_translation') && drupal_get_installed_schema_version('entity_translation') < 7004) {
      $requirements['metatag_et_version'] = array(
        'severity' => REQUIREMENT_ERROR,
        'title' => 'Metatag',
        'value' => $t('<a href="@url">Entity_Translation</a> is out of date and requires updating', array(
          '@url' => 'https://www.drupal.org/project/entity_translation',
        )),
        'description' => $t('The Entity_Translation module is out of date and needs to be updated in order to be compatible with Metatag.'),
      );
    }

    // It's recommended to install the Transliteration module to clean up file
    // paths for use with image meta tags.
    if (!module_exists('transliteration')) {
      $requirements['metatag_transliteration'] = array(
        'severity' => REQUIREMENT_INFO,
        'title' => 'Metatag',
        'value' => $t('The Transliteration module is recommended.'),
        'description' => $t("It is recommended to install the <a href=\"@url\">Transliteration module</a> to clean up filenames of uploaded files that may be used with image meta tags.", array(
          '@url' => 'https://drupal.org/project/transliteration',
        )),
      );
    }

    // It's recommended to install the Imagecache Token module to make image
    // tokens easier to do.
    if (!module_exists('imagecache_token')) {
      $requirements['metatag_imagecache_token'] = array(
        'severity' => REQUIREMENT_INFO,
        'title' => 'Metatag',
        'value' => $t('The Imagecache Token module is recommended.'),
        'description' => $t("It is recommended to install the <a href=\"@url\">Imagecache Token module</a> to make it easier to control image meta tags, e.g. og:image. See the Metatag module's README.txt for details.", array(
          '@url' => 'https://drupal.org/project/imagecache_token',
        )),
      );
    }

    // Recommend the Fast Token Browser module.
    if (!module_exists('fast_token_browser')) {
      $requirements['metatag_fasttokenbrowser'] = array(
        'severity' => REQUIREMENT_INFO,
        'title' => 'Metatag',
        'value' => $t('The Fast Token Browser module is recommended.'),
        'description' => $t("Using the !url can help avoid problems with the token browser.", array(
          '!url' => l(t('Fast Token Browser module'), 'https://www.drupal.org/project/fast_token_browser'),
        )),
      );
    }

    // The Admin Language module can cause problems.
    if (module_exists('admin_language') && variable_get('admin_language_force_neutral', 0)) {
      $requirements['metatag_admin_language'] = array(
        'severity' => REQUIREMENT_WARNING,
        'title' => 'Metatag',
        'value' => $t('Conflict with Admin Language module.'),
        'description' => $t("Using the \"@option\" with Metatag can lead to data loss, so it is recommended to <a href=\"@url\">disable that option</a>.", array(
          '@option' => t('Force language neutral aliases'),
          '@url' => url('admin/config/regional/language/admin_language'),
        )),
      );
    }

    // Token v7.x-1.6 is *highly* recommended.
    $token_module_info = system_get_info('module', 'token');

    // If the version string is not present then it means the module is running
    // from git, which means it can't be compared against. Alternatively, look
    // for the test file, which was the last commit of the 1.6 release.
    if (!empty($token_module_info['version']) || empty($token_module_info['files'])) {

      // If there's no test file then this is older than v1.6.
      if (empty($token_module_info['files'])) {
        $minor = 5;
      }
      else {

        // Versions are in the format 7.x-1.y, so split the string up to find
        // the 'y' portion.
        $version = explode('-', $token_module_info['version']);
        if (isset($version[1])) {
          list($major, $minor) = explode('.', $version[1]);

          // Strip off any suffixes on the version string, e.g. "17-dev".
          if (strpos('-', $minor)) {
            list($minor, $suffix) = explode('-', $minor);
          }
        }
        else {
          $minor = 0;
        }
      }

      // If v1.6 is not installed, give a warning.
      if ($minor < 6) {
        $requirements['metatag_token_version'] = array(
          'severity' => REQUIREMENT_WARNING,
          'title' => 'Metatag',
          'value' => $t('Token module is out of date.'),
          'description' => $t('It is highly recommended to install <a href="https://www.drupal.org/project/token">Token module</a> v7.x-1.6 or newer, otherwise there may be problems using certain meta tags.'),
        );
      }
    }

    // If Workbench Moderation is installed, show a message if it is out of
    // date.
    if (module_exists('workbench_moderation')) {
      $wm_module_info = system_get_info('module', 'workbench_moderation');

      // If the version string is not present then it means the module is
      // running from git, which means it can't be compared against.
      if (!empty($wm_module_info['version'])) {

        // Versions are in the format 7.x-1.y, so split the string up to find
        // the 'y' portion.
        $version = explode('-', $wm_module_info['version']);
        if (isset($version[1])) {
          list($major, $minor) = explode('.', $version[1]);
        }
        else {
          $major = 0;
        }
      }

      // If v3.x is not installed, give a message.
      if ($major < 3) {
        $requirements['metatag_wm_version'] = array(
          'severity' => REQUIREMENT_INFO,
          'title' => 'Metatag',
          'value' => $t('Workbench Moderation module is out of date.'),
          'description' => $t('It is recommended to use <a href="https://www.drupal.org/project/workbench_moderation">Workbench Moderation module</a> v7.x-3.0 or newer.'),
        );
      }
    }

    // Recommend the Schema.org Metatag module.
    if (!module_exists('schema_metatag')) {
      $requirements['metatag_schema'] = array(
        'severity' => REQUIREMENT_INFO,
        'title' => 'Metatag',
        'value' => $t('Schema.org Metatag is recommended'),
        'description' => $t('The <a href="@module">Schema.org Metatag</a> module is highly recommended to add <a href="@jsonld">JSON-LD</a> -formatted <a href="@schema">schema.org</a> compatible data structures to the site.', array(
          '@module' => 'https://www.drupal.org/project/schema_metatag',
          '@jsonld' => 'https://json-ld.org',
          '@schema' => 'http://schema.org',
        )),
      );
    }
    else {
      $requirements['metatag_schema'] = array(
        'severity' => REQUIREMENT_OK,
        'title' => 'Metatag',
        'value' => $t('Schema.org Metatag is installed'),
        'description' => $t('The <a href="@module">Schema.org Metatag</a> module is installed.', array(
          '@module' => 'https://www.drupal.org/project/schema_metatag',
        )),
      );
    }
  }
  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'] = 'Cache table for the generated meta tag output.';
  return $schema;
}

/**
 * Implements hook_install().
 */
function metatag_install() {
  drupal_set_message(t("Thank you for installing the Metatag module. It is recommended to read the module's <a href=\"!url\" title=\"Read the Metatag module's documentation\">README.txt</a> file as there are some known issues that may affect this site.", array(
    '!url' => url(drupal_get_path('module', 'metatag') . '/README.txt'),
  )));

  // 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;
    }
    elseif (empty($entity_info['fieldable'])) {
      continue;
    }

    // Ignore some view modes that are automatically added by certain modules.
    unset($entity_info['view modes']['ical']);
    unset($entity_info['view modes']['diff_standard']);
    unset($entity_info['view modes']['token']);

    // Entities without view modes are skipped.
    if (empty($entity_info['view modes'])) {
      continue;
    }

    // At this point, disable the entity by default.
    $entity_enabled = FALSE;

    // Anything that was specifically enabled via hook_entity_info() from older
    // versions will be enabled if not configured already.
    if (!empty($entity_info['metatag']) || !empty($entity_info['metatags'])) {
      $entity_enabled = variable_get($variable_name, 'monkey');
      if ($entity_enabled === 'monkey') {
        $entity_enabled = TRUE;
      }
    }
    variable_set($variable_name, $entity_enabled);

    // Loop through the bundles, but only if the entity is enabled.
    if ($entity_enabled) {
      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
        $variable_name = 'metatag_enable_' . $entity_type . '__' . $bundle_name;

        // If it wasn't specifically disabled before, enable it.
        $bundle_enabled = variable_get($variable_name, 'monkey');
        if ($bundle_name != FALSE) {
          variable_set($variable_name, TRUE);
        }
      }
    }
  }
  drupal_set_message(t('It may be worth verifying on the <a href="@url">Settings page</a> which types of content on the site should allow meta tags.', array(
    '@url' => url('admin/config/search/metatags/settings'),
  )));
}

/**
 * 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');

  // Customizable pager string.
  variable_del('metatag_pager_string');

  // Optionally enable translations of final output.
  variable_del('metatag_i18n_translate_output');

  // Optionally enable the automatic watchdog logging of i18n strings.
  variable_del('metatag_i18n_enable_watchdog');

  // Optionally disable the i18n integration.
  variable_del('metatag_i18n_disabled');

  // Optionally output core's meta tags.
  variable_del('metatag_leave_core_tags');

  // Optionally enable individual permissions for each meta tag.
  variable_del('metatag_extended_permissions');

  // Optionally load meta tags on admin pages.
  variable_del('metatag_load_all_pages');

  // Sanitize token replacement output.
  variable_del('metatag_token_sanitize');

  // Remove all possible 'enable' variables.
  foreach (entity_get_info() as $entity_type => $entity_info) {
    variable_del('metatag_enable_' . $entity_type);
    if (!empty($entity_info['bundles'])) {
      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
        variable_del('metatag_enable_' . $entity_type . '__' . $bundle_name);
      }
    }
  }

  // Remove all of the maxlength variables.
  variable_del('metatag_maxlength_title');
  variable_del('metatag_maxlength_description');
  variable_del('metatag_maxlength_abstract');
  variable_del('metatag_maxlength_keywords');
  variable_del('metatag_maxlength_news_keywords');

  // Whether regular text fields can have summary tokens too.
  variable_del('metatag_summary_text_field');

  // Length of summaries generated by the custom tokens.
  variable_del('metatag_summary_length');
}

/**
 * Implements hook_enable().
 */
function metatag_enable() {
  variable_set('metatag_schema_installed', TRUE);
}

/**
 * Replace one meta tag's with another in the entity records.
 *
 * @param array $sandbox
 *   A Batch API sandbox, passed by reference.
 * @param string $old_tag
 *   The meta tag that is to be replaced.
 * @param string $new_tag
 *   The meta tag that replaces the old one.
 */
function metatag_update_replace_entity_tag(array &$sandbox, $old_tag, $new_tag) {
  if (!isset($sandbox['progress'])) {

    // Count of all {metatag} records that contained an entry for the old meta
    // tag.
    $records_count = db_select('metatag', 'm')
      ->condition('m.data', '%' . db_like('"' . $old_tag . '"') . '%', 'LIKE')
      ->countQuery()
      ->execute()
      ->fetchField();
    if (empty($records_count)) {
      return t('No Metatag entity records needed to have the "@tag" meta tag renamed.', array(
        '@tag' => $old_tag,
      ));
    }
    $sandbox['max'] = $records_count;
    $sandbox['progress'] = 0;
  }

  // Count of rows that will be processed per iteration.
  $limit = 100;

  // Fetches a part of records.
  $records = db_select('metatag', 'm')
    ->fields('m', array())
    ->condition('m.data', '%' . db_like('"' . $old_tag . '"') . '%', 'LIKE')
    ->range(0, $limit)
    ->execute();
  $count = 0;
  $keys = array(
    'entity_type',
    'entity_id',
    'revision_id',
    'language',
  );

  // Loop over the values and correct them.
  foreach ($records as $record) {
    $record->data = unserialize($record->data);
    if (isset($record->data[$old_tag])) {
      $record->data[$new_tag] = $record->data[$old_tag];
      unset($record->data[$old_tag]);
      drupal_write_record('metatag', $record, $keys);

      // Clear the cache for the entity this belongs to.
      entity_get_controller($record->entity_type)
        ->resetCache(array(
        $record->entity_id,
      ));
    }
    $count++;
  }
  if (!empty($count)) {
    $sandbox['progress'] += $count;

    // In some cases the query yields results that cannot be fixed and we would
    // run into an infinite loop. Stop immediately if we processed all records.
    if ($sandbox['progress'] >= $sandbox['max']) {
      $sandbox['#finished'] = TRUE;
    }
    else {
      $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
    }
  }
  else {
    $sandbox['#finished'] = TRUE;
    return t('Converted the "@old_tag" meta tag for @count entity records to "@new_tag" meta tag.', array(
      '@old_tag' => $old_tag,
      '@new_tag' => $new_tag,
      '@count' => $sandbox['progress'],
    ));
  }
}

/**
 * Replace one meta tag's value in the entity records.
 *
 * @param array $sandbox
 *   A Batch API sandbox, passed by reference.
 * @param string $meta_tag
 *   The meta tag that needs its value replaced.
 * @param string $old_value
 *   The meta tag value that is to be replaced.
 * @param string $new_value
 *   The meta tag value that replaces the old one.
 */
function metatag_update_replace_entity_value(array &$sandbox, $meta_tag, $old_value, $new_value) {

  // The condition used for both queries.
  $db_and = db_and();
  $db_and
    ->condition('m.data', '%' . db_like('"' . $meta_tag . '"') . '%', 'LIKE');
  $db_and
    ->condition('m.data', '%' . db_like('"' . $old_value . '"') . '%', 'LIKE');
  if (!isset($sandbox['progress'])) {

    // Count of all {metatag} records that contained an entry for the old meta
    // tag value.
    $records_count = db_select('metatag', 'm')
      ->condition($db_and)
      ->countQuery()
      ->execute()
      ->fetchField();
    if (empty($records_count)) {
      return t('No Metatag entity records needed to have the "@tag" meta tag "@old_value" value replaced.', array(
        '@tag' => $meta_tag,
        '@old_value' => $old_value,
      ));
    }
    $sandbox['max'] = $records_count;
    $sandbox['progress'] = 0;

    // Keep track of the number of replaced values separately.
    $sandbox['count'] = 0;
  }

  // Count of rows that will be processed per iteration.
  $limit = 100;

  // Fetches a part of records.
  $records = db_select('metatag', 'm')
    ->fields('m', array())
    ->condition($db_and)
    ->range(0, $limit)
    ->execute();
  $count = 0;
  $keys = array(
    'entity_type',
    'entity_id',
    'revision_id',
    'language',
  );

  // Loop over the values and correct them.
  foreach ($records as $record) {
    $record->data = unserialize($record->data);
    if (isset($record->data[$meta_tag])) {
      $record->data[$meta_tag]['value'] = str_replace($old_value, $new_value, $record->data[$meta_tag]['value']);
      drupal_write_record('metatag', $record, $keys);

      // Clear the cache for the entity this belongs to.
      entity_get_controller($record->entity_type)
        ->resetCache(array(
        $record->entity_id,
      ));
      $sandbox['count']++;
    }
    $sandbox['progress']++;
  }
  if (!empty($count)) {

    // In some cases the query yields results that cannot be fixed and we would
    // run into an infinite loop. Stop immediately if we processed all records.
    if ($sandbox['progress'] >= $sandbox['max']) {
      $sandbox['#finished'] = TRUE;
    }
    else {
      $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
    }
  }
  else {
    $sandbox['#finished'] = TRUE;
    return t('Replaced the value of @count entity records for the "@meta_tag" meta tag.', array(
      '@meta_tag' => $meta_tag,
      '@count' => $sandbox['count'],
    ));
  }
}

/**
 * Replace one meta tag with another in the configs.
 *
 * @param string $old_tag
 *   The meta tag that is to be replaced.
 * @param string $new_tag
 *   The meta tag that replaces the old one.
 */
function metatag_update_replace_config_tag($old_tag, $new_tag) {

  // Find all {metatag_config} records that contained an entry for the old meta
  // tag.
  $records = db_select('metatag_config', 'm')
    ->fields('m', array(
    'cid',
    'config',
  ))
    ->condition('m.config', '%' . db_like('"' . $old_tag . '"') . '%', 'LIKE')
    ->execute();

  // This message will be returned if nothing needed to be updated.
  $none_message = t('No Metatag configuration records needed to have the "@tag" meta tag fixed. That said, there may be other configurations elsewhere that do need updating.', array(
    '@tag' => $old_tag,
  ));

  // Loop over the values and correct them.
  if ($records
    ->rowCount() == 0) {
    $message = $none_message;
  }
  else {
    $keys = array(
      'cid',
    );

    // Loop over the values and correct them.
    $counter = 0;
    foreach ($records as $record) {
      $record->config = unserialize($record->config);
      if (isset($record->config[$old_tag])) {
        $record->config[$new_tag] = $record->config[$old_tag];
        unset($record->config[$old_tag]);
        drupal_write_record('metatag_config', $record, $keys);
        $counter++;
      }
    }
    if ($counter == 0) {
      $message = $none_message;
    }
    else {
      $message = t('Converted the "@old_tag" meta tag for @count configurations to the new "@new_tag" meta tag.', array(
        '@old_tag' => $old_tag,
        '@new_tag' => $new_tag,
        '@count' => $counter,
      ));
    }
  }

  // Clear all Metatag caches.
  cache_clear_all('*', 'cache_metatag', TRUE);
  drupal_static_reset('metatag_config_load_with_defaults');
  drupal_static_reset('metatag_entity_supports_metatags');
  drupal_static_reset('metatag_config_instance_info');
  drupal_static_reset('metatag_get_info');
  ctools_include('export');
  ctools_export_load_object_reset('metatag_config');
  return $message;
}

/**
 * Replace one meta tag with another in the configs.
 *
 * @param string $meta_tag
 *   The meta tag that needs its value replaced.
 * @param string $old_value
 *   The meta tag value that is to be replaced.
 * @param string $new_value
 *   The meta tag value that replaces the old one.
 */
function metatag_update_replace_config_value($meta_tag, $old_value, $new_value) {

  // Find all {metatag_config} records that contained an entry for the old meta
  // tag.
  $db_and = db_and();
  $db_and
    ->condition('m.config', '%' . db_like('"' . $meta_tag . '"') . '%', 'LIKE');
  $db_and
    ->condition('m.config', '%' . db_like('"' . $old_value . '"') . '%', 'LIKE');
  $records = db_select('metatag_config', 'm')
    ->fields('m', array(
    'cid',
    'config',
  ))
    ->condition($db_and)
    ->execute();

  // This message will be returned if nothing needed to be updated.
  $none_message = t('No Metatag configuration records needed to have the "@tag" meta tag values updated. That said, there may be other configurations elsewhere that do need updating.', array(
    '@tag' => $meta_tag,
  ));

  // Loop over the values and correct them.
  if ($records
    ->rowCount() == 0) {
    $message = $none_message;
  }
  else {
    $keys = array(
      'cid',
    );

    // Loop over the values and correct them.
    $counter = 0;
    foreach ($records as $record) {
      $record->config = unserialize($record->config);
      if (isset($record->config[$meta_tag])) {
        $record->config[$meta_tag]['value'] = str_replace($old_value, $new_value, $record->config[$meta_tag]['value']);
        drupal_write_record('metatag_config', $record, $keys);
        $counter++;
      }
    }
    if ($counter == 0) {
      $message = $none_message;
    }
    else {
      $message = t('Replaced the value of @count entity records for the "@meta_tag" meta tag.', array(
        '@meta_tag' => $meta_tag,
        '@count' => $counter,
      ));
    }
  }

  // Clear all Metatag caches.
  cache_clear_all('*', 'cache_metatag', TRUE);
  drupal_static_reset('metatag_config_load_with_defaults');
  drupal_static_reset('metatag_entity_supports_metatags');
  drupal_static_reset('metatag_config_instance_info');
  drupal_static_reset('metatag_get_info');
  ctools_include('export');
  ctools_export_load_object_reset('metatag_config');
  return $message;
}

/**
 * Remove a specific meta tag from all configs.
 *
 * @param string $tag_name
 *   The meta tag that is to be removed.
 */
function metatag_update_delete_config($tag_name) {

  // Find all {metatag_config} records that contained an entry for the meta tag.
  $records = db_select('metatag_config', 'm')
    ->fields('m', array(
    'cid',
    'config',
  ))
    ->condition('m.config', '%' . db_like('"' . $tag_name . '"') . '%', 'LIKE')
    ->execute();

  // This message will be returned if nothing needed to be updated.
  $none_message = t('No Metatag configuration records needed to have the "@tag" meta tag removed.', array(
    '@tag' => $tag_name,
  ));

  // Loop over the values and correct them.
  if ($records
    ->rowCount() == 0) {
    drupal_set_message($none_message);
  }
  else {

    // Loop over the values and correct them.
    $counter = 0;
    foreach ($records as $record) {
      $record->config = unserialize($record->config);
      if (isset($record->config[$tag_name])) {
        unset($record->config[$tag_name]);
        drupal_write_record('metatag_config', $record, array(
          'cid',
        ));
        $counter++;
      }
    }
    if ($counter == 0) {
      drupal_set_message($none_message);
    }
    else {
      drupal_set_message(t('Removed the "@tag" meta tag for @count configurations.', array(
        '@tag' => $tag_name,
        '@count' => $counter,
      )));
    }
  }

  // Clear all Metatag caches.
  cache_clear_all('*', 'cache_metatag', TRUE);
  drupal_static_reset('metatag_config_load_with_defaults');
  drupal_static_reset('metatag_entity_supports_metatags');
  drupal_static_reset('metatag_config_instance_info');
  drupal_static_reset('metatag_get_info');
  ctools_include('export');
  ctools_export_load_object_reset('metatag_config');
}

/**
 * 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');
}

/**
 * Uninstall the deprecated metatag_ui module, the UI is now in the main module.
 */
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 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.
 *
 * This is 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 entities 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\n    FROM {metatag} m\n    LEFT OUTER JOIN {node} n\n      ON m.entity_id=n.nid\n    WHERE m.entity_type='node'\n      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\n    FROM {metatag} m\n    LEFT OUTER JOIN {users} u\n      ON m.entity_id=u.uid\n    WHERE m.entity_type='user'\n      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\n      FROM {metatag} m\n      LEFT OUTER JOIN {taxonomy_term_data} t\n        ON m.entity_id=t.tid\n      WHERE m.entity_type='taxonomy_term'\n        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(array &$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.
  if ($sandbox['progress'] >= $sandbox['max']) {
    $sandbox['#finished'] = TRUE;
  }
  else {
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
  }
  if ($sandbox['#finished']) {

    // Clear all caches so the fixed data will be reloaded.
    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(array &$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.
  if ($sandbox['progress'] >= $sandbox['max']) {
    $sandbox['#finished'] = TRUE;
  }
  else {
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
  }
  if ($sandbox['#finished']) {

    // Clear all caches so the fixed data will be reloaded.
    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 string $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\n    FROM {metatag} m\n    WHERE\n      m.entity_type = :type\n    GROUP BY m.entity_id\n    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.
      }
      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;
            }
          }
        }
        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;
              }
            }
          }
          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 *\n  FROM {metatag} m\n  WHERE\n       m.data LIKE :nolang\n    OR m.data LIKE :lang\n    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 :-)'));
  }
  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.
 *
 * Also 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(array &$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\n      FROM {metatag}\n      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);

    // Try to fallback to default language if no record was found. This may
    // happen when using entity_translation as the parent entity table only
    // contains one record.
    if (!empty($conditions) && empty($records)) {
      $records = entity_load($entity_type, array(
        $entity_id,
      ));
    }

    // Fix this record.
    if (!empty($records)) {
      $entity = reset($records);

      // 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\n            FROM {metatag}\n            WHERE entity_type = :entity_type\n            AND entity_id = :entity_id\n            AND revision_id = :revision_id\n            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();
        }
        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\n                FROM {metatag}\n                WHERE entity_type = :entity_type\n                AND entity_id = :entity_id\n                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\n                      FROM {metatag}\n                      WHERE entity_type = :entity_type\n                      AND entity_id = :entity_id\n                      AND revision_id = :revision_id\n                      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);
                  }
                }
              }
            }
          }
        }
        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.
  if ($sandbox['progress'] >= $sandbox['max']) {
    $sandbox['#finished'] = TRUE;
  }
  else {
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
  }
  if ($sandbox['#finished']) {

    // Clear all caches so the fixed data will be reloaded.
    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 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.');
}

/**
 * No-op update. Renaming twitter:image to twitter:image:src no longer needed.
 */
function metatag_update_7024() {

  // Do nothing.
}

/**
 * Replaced by update 7030.
 */
function metatag_update_7025() {

  // Do nothing.
}

/**
 * Rename the 'copyright' meta tag to 'rights', part 1.
 */
function metatag_update_7026(array &$sandbox) {
  $old_tag = 'copyright';
  $new_tag = 'rights';
  return metatag_update_replace_entity_tag($sandbox, $old_tag, $new_tag);
}

/**
 * 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() {
  if (!db_index_exists('metatag', 'type_revision')) {
    db_add_index('metatag', 'type_revision', array(
      'entity_type',
      'revision_id',
    ));
    return t('Added an index to the main Metatag table that will hopefully improve performance a little.');
  }
  else {
    return t('Did not add the index, it already existed.');
  }
}

/**
 * No-op update. Renaming twitter:image to twitter:image:src no longer needed.
 */
function metatag_update_7030() {

  // Do nothing.
}

/**
 * Rename the 'copyright' meta tag to 'rights', part 2.
 */
function metatag_update_7031() {
  $old_tag = 'copyright';
  $new_tag = 'rights';
  return metatag_update_replace_config_tag($old_tag, $new_tag);
}

/**
 * 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 are now skipped.
 *
 * 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() {
}

/**
 * Skipped.
 */
function metatag_update_7034() {
}

/**
 * Skipped.
 */
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;
    }
    elseif (empty($entity_info['view modes'])) {
      continue;
    }
    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);
              }
            }
            else {

              // Do nothing.
            }
          }
        }
      }
      else {

        // Do nothing.
      }
    }
    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 <a href="@url">Settings page</a> 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 core entity types to resolve issues from 1.5's changes.
 */
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);
      }
    }
  }
}

/**
 * Reload some meta tag caches.
 */
function metatag_update_7039() {
  foreach (language_list() as $langcode => $language) {
    cache_clear_all('info:' . $langcode, 'cache_metatag');
  }
}

/**
 * Fix any robots meta tags that were broken during importing from Nodewords.
 */
function metatag_update_7040(array &$sandbox) {

  // Process records by groups of 10 (arbitrary value).
  // When a group is processed, the batch update engine determines whether it
  // should continue processing in the same request or provide progress
  // feedback to the user and wait for the next request.
  $limit = 10;

  // When ran through Drush it's Ok to process a larger number of objects at a
  // time.
  if (drupal_is_cli()) {
    $limit = 100;
  }

  // Use the sandbox at your convenience to store the information needed
  // to track progression between successive calls to the function.
  if (!isset($sandbox['progress'])) {

    // The count of records visited so far.
    $sandbox['progress'] = 0;

    // Get a count of {metatag} records that have a robots meta tag definition
    // that is not in the correct format.
    $records = db_select('metatag', 'm')
      ->fields('m')
      ->condition('m.data', '%:6:"robots";%', 'LIKE')
      ->condition('m.data', '%:6:"robots";a:1:{s:5:"value";%', 'NOT LIKE')
      ->countQuery()
      ->execute()
      ->fetchField();

    // If there's no data, don't bother with the extra work.
    if (empty($records)) {
      watchdog('metatag', 'Update 7040: No robots meta tags need to be fixed.', array(), WATCHDOG_INFO);
      if (drupal_is_cli()) {
        drupal_set_message(t('Update 7040: No robots meta tags need to be fixed.'));
      }
      return t('No robots meta tags need to be fixed.');
    }

    // Total records that must be visited.
    $sandbox['max'] = $records;

    // A place to store messages during the run.
    $sandbox['messages'] = array();

    // An initial record of the number of records to be updated.
    watchdog('metatag', 'Update 7040: !count records to examine.', array(
      '!count' => $sandbox['max'],
    ), WATCHDOG_INFO);
    if (drupal_is_cli()) {
      drupal_set_message(t('Update 7040: !count records to examine.', array(
        '!count' => $sandbox['max'],
      )));
    }

    // Log how many records are fixed.
    $sandbox['fixed'] = 0;

    // Because a lot of other processing happens on the first iteration, just do
    // ten.
    $limit = 10;
  }

  // Get a list of records that need to be fixed.
  $records = db_select('metatag', 'm')
    ->fields('m')
    ->condition('m.data', '%:6:"robots";%', 'LIKE')
    ->condition('m.data', '%:6:"robots";a:1:{s:5:"value";%', 'NOT LIKE')
    ->range(0, $limit)
    ->execute();

  // Set default values.
  foreach ($records as $record) {

    // Extract the record.
    $record->data = unserialize($record->data);

    // See if the record needs to be fixed.
    if (!empty($record->data['robots']) && empty($record->data['robots']['value'])) {

      // Fix the record.
      $robots = $record->data['robots'];
      $record->data['robots'] = array(
        'value' => $robots,
      );

      // Update the database.
      db_update('metatag')
        ->fields(array(
        'data' => serialize($record->data),
      ))
        ->condition('entity_type', $record->entity_type)
        ->condition('entity_id', $record->entity_id)
        ->condition('revision_id', $record->revision_id)
        ->condition('language', $record->language)
        ->execute();

      // Clear the cache for this entity.
      entity_get_controller($record->entity_type)
        ->resetCache(array(
        $record->entity_id,
      ));

      // Update our progress information.
      $sandbox['fixed']++;
    }

    // Update our progress information.
    $sandbox['progress']++;
  }

  // Set the "finished" status, to tell batch engine whether this function
  // needs to run again. If you set a float, this will indicate the progress of
  // the batch so the progress bar will update.
  if ($sandbox['progress'] >= $sandbox['max']) {
    $sandbox['#finished'] = TRUE;
  }
  else {
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
  }

  // Only display a status message if process finished.
  if ($sandbox['#finished'] === TRUE) {

    // Clear all caches so the fixed data will be reloaded.
    cache_clear_all('*', 'cache_metatag', TRUE);

    // A final log of the number of records that were converted.
    watchdog('metatag', 'Update 7040: !count records were fixed.', array(
      '!count' => $sandbox['fixed'],
    ), WATCHDOG_INFO);
    if (drupal_is_cli()) {
      drupal_set_message(t('Update 7040: !count records were fixed.', array(
        '!count' => $sandbox['fixed'],
      )));
    }

    // hook_update_N() may optionally return a string which will be displayed
    // to the user.
    return t('Fixed the Metatag robots values for @count nodes.', array(
      '@count' => $sandbox['fixed'],
    ));
  }
}

/**
 * Rerun update 7018. This may take some time.
 *
 * The original did not run correctly when using Entity Translation. This was
 * fixed.
 */
function metatag_update_7041(array &$sandbox) {
  metatag_update_7018($sandbox);
}

/**
 * Delete a deprecated variable and clear all Metatag caches.
 */
function metatag_update_7100() {
  variable_del('metatag_translate_final_values');

  // Clear all caches so that i18n support can be activated, if necessary.
  cache_clear_all('*', 'cache_metatag', TRUE);
}

/**
 * Update i18n strings for all meta tags to use the new format.
 */
function metatag_update_7101() {
  if (!module_exists('locale') || !db_table_exists('locales_source')) {
    return t('No translations to fix as the locale system is not enabled.');
  }

  // Loop through each entity type. Not going to bother filtering this list,
  // it'll only be a difference of a few entities anyway and the queries
  // should be fairly quick.
  foreach (entity_get_info() as $entity_type => $entity_info) {

    // Entities must have bundles.
    if (empty($entity_info['bundles'])) {
      $entity_info['bundles'] = array(
        $entity_type => $entity_type,
      );
    }
    foreach ($entity_info['bundles'] as $bundle_type => $bundle_info) {

      // Update {locales_source}, replace 'metatag:ENTITYTYPE:BUNDLE:tag' with
      // 'metatag:metatag_config:ENTITYTYPE:BUNDLE:tag'.
      db_query("UPDATE {locales_source}\n        SET location = REPLACE(location, 'metatag:{$entity_type}:{$bundle_type}:', 'metatag:metatag_config:{$entity_type}:{$bundle_type}:'),\n          context = REPLACE(context, '{$entity_type}:{$bundle_type}:', 'metatag_config:{$entity_type}:{$bundle_type}:')\n        WHERE textgroup = 'metatag'\n          AND location LIKE 'metatag:{$entity_type}:{$bundle_type}:%'");

      // Update {locales_source}, replace 'metatag:ENTITYTYPE:tag' with
      // 'metatag:metatag_config:ENTITYTYPE:tag'.
      db_query("UPDATE {locales_source}\n        SET location = REPLACE(location, 'metatag:{$entity_type}:', 'metatag:metatag_config:{$entity_type}:'),\n          context = REPLACE(context, '{$entity_type}:', 'metatag_config:{$entity_type}:')\n        WHERE textgroup = 'metatag'\n          AND location LIKE 'metatag:{$entity_type}:%'");
    }
  }

  // Update {locales_source}, replace 'metatag:metatag:' with
  // 'metatag:output:'.
  db_query("UPDATE {locales_source}\n    SET location = REPLACE(location, 'metatag:metatag:', 'metatag:output:'),\n      context = REPLACE(context, 'metatag:', 'output:')\n    WHERE textgroup = 'metatag'\n      AND location LIKE 'metatag:metatag:%'");
}

/**
 * Re-run update 7101.
 */
function metatag_update_7102() {
  metatag_update_7101();
}

/**
 * Clear all metatag caches so the new entity caching structure takes over.
 */
function metatag_update_7103() {
  cache_clear_all('*', 'cache_metatag', TRUE);
}

/**
 * Remove the entity revision ID from the translation strings.
 */
function metatag_update_7104(array &$sandbox) {

  // Verify that locales are being used in the first place.
  if (!module_exists('locale') || !db_table_exists('locales_source')) {
    return t('Metatag: No translations to fix as the locale system is not enabled.');
  }

  // No need to do anything if output translation is disabled.
  if (!variable_get('metatag_i18n_translate_output', FALSE)) {
    return t('Metatag: Output translation is disabled, so no need to update anything.');
  }

  // Process records by groups of 10 (arbitrary value).
  // When a group is processed, the batch update engine determines whether it
  // should continue processing in the same request or provide progress
  // feedback to the user and wait for the next request.
  $limit = 10;

  // When ran through Drush it's Ok to process a larger number of objects at a
  // time.
  if (drupal_is_cli()) {
    $limit = 100;
  }

  // Use the sandbox at your convenience to store the information needed
  // to track progression between successive calls to the function.
  if (!isset($sandbox['progress'])) {

    // The count of records visited so far.
    $sandbox['progress'] = 0;

    // Total records that must be visited.
    $sandbox['max'] = db_query("SELECT COUNT(lid)\n      FROM {locales_source}\n      WHERE textgroup = 'metatag'\n      AND context LIKE 'output:%:%:%:%'")
      ->fetchField();

    // If there's no data, don't bother with the extra work.
    if (empty($sandbox['max'])) {
      watchdog('metatag', 'Update 7104: No nodes need the translation entity string fixed.', array(), WATCHDOG_INFO);
      if (drupal_is_cli()) {
        drupal_set_message(t('Update 7104: No nodes need the translation entity string fixed.'));
      }
      return t('No nodes need the Metatag language values fixed.');
    }

    // A place to store messages during the run.
    $sandbox['messages'] = array();

    // An initial record of the number of records to be updated.
    watchdog('metatag', 'Update 7104: !count records to update.', array(
      '!count' => $sandbox['max'],
    ), WATCHDOG_INFO);
    if (drupal_is_cli()) {
      drupal_set_message(t('Update 7104: !count records to update.', array(
        '!count' => $sandbox['max'],
      )));
    }
  }

  // Get a batch of records that need to be fixed.
  $records = db_query_range("SELECT lid, location, context\n    FROM {locales_source}\n    WHERE textgroup = 'metatag'\n    AND context LIKE 'output:%:%:%:%'", 0, $limit);

  // Update each of the records.
  foreach ($records as $record) {
    $old_location = '/metatag:output:([^:]*):([^:]*):([^:]*):([^:]*)/';
    $new_location = 'metatag:output:$1:$2:$4';
    $location = preg_replace($old_location, $new_location, $record->location);
    $old_context = '/output:([^:]*):([^:]*):([^:]*):([^:]*)/';
    $new_context = 'output:$1:$2:$4';
    $context = preg_replace($old_context, $new_context, $record->context);
    drupal_set_message($location);
    drupal_set_message($context);
    db_update('locales_source')
      ->fields(array(
      'location' => $location,
      'context' => $context,
    ))
      ->condition('lid', $record->lid)
      ->execute();

    // Update our progress information.
    $sandbox['progress']++;
  }

  // Set the "finished" status, to tell batch engine whether this function
  // needs to run again. If you set a float, this will indicate the progress of
  // the batch so the progress bar will update.
  if ($sandbox['progress'] >= $sandbox['max']) {
    $sandbox['#finished'] = TRUE;
  }
  else {
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
  }
  if ($sandbox['#finished']) {

    // Clear all caches so the fixed data will be reloaded.
    cache_clear_all('*', 'cache_metatag', TRUE);

    // hook_update_N() may optionally return a string which will be displayed
    // to the user.
    return t('Fixed the Metatag language values for @count nodes.', array(
      '@count' => $sandbox['progress'],
    ));
  }
}

/**
 * Fix the output translation strings.
 */
function metatag_update_7105() {
  if (!module_exists('locale') || !db_table_exists('locales_source')) {
    return t('No translations to fix as the locale system is not enabled.');
  }
  db_query("UPDATE {locales_source}\n    SET location = REPLACE(location, 'metatag:metatag:', 'metatag:output:'),\n      context = REPLACE(context, 'metatag:', 'output:')\n    WHERE textgroup = 'metatag'\n    AND location LIKE 'metatag:metatag:%'");
  return t('Fixed the Metatag output strings.');
}

/**
 * Rerun update 7105.
 *
 * The output translation strings were renamed to something shorter.
 */
function metatag_update_7106() {
  return metatag_update_7105();
}

/**
 * Fix the global config translation strings.
 */
function metatag_update_7107() {
  if (!module_exists('locale') || !db_table_exists('locales_source')) {
    return t('No translations to fix as the locale system is not enabled.');
  }
  db_query("UPDATE {locales_source}\n    SET location = REPLACE(location, 'metatag:global:', 'metatag:metatag_config:global:'),\n      context = REPLACE(context, 'global:', 'metatag_config:global:')\n    WHERE textgroup = 'metatag'\n    AND location LIKE 'metatag:global:%'");
  return t('Fixed the Metatag global config string translations.');
}

/**
 * Delete output translations if it's disabled.
 */
function metatag_update_7108(array &$sandbox) {
  if (!module_exists('locale') || !db_table_exists('locales_source')) {
    return t('No translations to fix as the locale system is not enabled.');
  }

  // If the output-translation option is enabled then don't delete anything.
  if (variable_get('metatag_i18n_translate_output', FALSE)) {
    return t("Metatag: Not deleting output translations because that option is enabled.");
  }
  $limit = 100;

  // When ran through Drush it's Ok to process a larger number of objects at a
  // time.
  if (drupal_is_cli()) {
    $limit = 500;
  }

  // Use the sandbox at your convenience to store the information needed to
  // track progression between successive calls to the function.
  if (!isset($sandbox['progress'])) {

    // The count of records visited so far.
    $sandbox['progress'] = 0;
    $sandbox['max'] = db_query("SELECT COUNT(lid)\n      FROM {locales_source}\n      WHERE textgroup = 'metatag'\n      AND context LIKE 'output:%'")
      ->fetchField();

    // If there's no data, don't bother with the extra work.
    if (empty($sandbox['max'])) {
      watchdog('metatag', 'Update 7108: No nodes need the translation entity string fixed.', array(), WATCHDOG_INFO);
      if (drupal_is_cli()) {
        drupal_set_message(t('Update 7108: No nodes need the translation entity string fixed.'));
      }
      return t('No nodes need the Metatag language values fixed.');
    }

    // A place to store messages during the run.
    $sandbox['messages'] = array();

    // An initial record of the number of records to be updated.
    watchdog('metatag', 'Update 7108: !count records to update.', array(
      '!count' => $sandbox['max'],
    ), WATCHDOG_INFO);
    if (drupal_is_cli()) {
      drupal_set_message(t('Update 7108: !count records to update.', array(
        '!count' => $sandbox['max'],
      )));
    }
  }

  // Get a batch of records that need to be fixed.
  $records = db_query_range("SELECT lid\n    FROM {locales_source}\n    WHERE textgroup = 'metatag'\n    AND context LIKE 'output:%'", 0, $limit);
  $lids = $records
    ->fetchCol();
  $count = count($lids);

  // Delete records in the tables in reverse order, so that if the query fails
  // and has to be reran it'll still find records. But it should be ok.
  if (db_table_exists('i18n_string')) {
    db_delete('i18n_string')
      ->condition('lid', $lids, 'IN')
      ->execute();
  }
  db_delete('locales_target')
    ->condition('lid', $lids, 'IN')
    ->execute();
  db_delete('locales_source')
    ->condition('lid', $lids, 'IN')
    ->execute();
  $sandbox['progress'] = $sandbox['progress'] + $count;

  // Set the "finished" status, to tell batch engine whether this function
  // needs to run again. If you set a float, this will indicate the progress of
  // the batch so the progress bar will update.
  if ($sandbox['progress'] >= $sandbox['max']) {
    $sandbox['#finished'] = TRUE;
  }
  else {
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
  }
  if ($sandbox['#finished']) {

    // Clear all caches so the fixed data will be reloaded.
    cache_clear_all('*', 'cache_metatag', TRUE);

    // hook_update_N() may optionally return a string which will be displayed
    // to the user.
    return t('Deleted output translation if disabled for  @count items.', array(
      '@count' => $sandbox['progress'],
    ));
  }
}

/**
 * Rename the 'icon_any' meta tag to 'mask-icon' in the entity records.
 */
function metatag_update_7109(array &$sandbox) {
  module_load_include('install', 'metatag');
  $old_tag = 'icon_any';
  $new_tag = 'mask-icon';
  return metatag_update_replace_entity_tag($sandbox, $old_tag, $new_tag);
}

/**
 * Rename the 'icon_any' meta tag to 'mask-icon' in the configs.
 */
function metatag_update_7110() {
  module_load_include('install', 'metatag');
  $old_tag = 'icon_any';
  $new_tag = 'mask-icon';
  return metatag_update_replace_config_tag($old_tag, $new_tag);
}

/**
 * Remove the "metatag_ui" record from the {system} table.
 */
function metatag_update_7111() {
  db_delete('system')
    ->condition('name', 'metatag_ui')
    ->execute();
}

/**
 * The Publisher meta tag is now part of the Google Plus submodule.
 */
function metatag_update_7112() {
  cache_clear_all('*', 'cache_metatag', TRUE);
  drupal_set_message(t('The Publisher meta tag is now part of the Google Plus submodule.'));
}

/**
 * Clear all metatag output caches if output caching is disabled.
 */
function metatag_update_7113() {
  if (variable_get('metatag_cache_output', TRUE) == FALSE) {
    cache_clear_all('output:', 'cache_metatag', TRUE);
  }
}

/**
 * Disable output caching.
 */
function metatag_update_7114() {
  variable_del('metatag_cache_output');
  drupal_set_message(t('Note: Output caching of meta tags has been disabled, it may be reenabled from the Metatag settings page if needed.'));
}

/**
 * Clear all metatag output caches, it will be rebuild if needed.
 */
function metatag_update_7115() {
  cache_clear_all('output:', 'cache_metatag', TRUE);
}

/**
 * Trigger a cache clear so the Views integration can be loaded.
 */
function metatag_update_7116() {

  // Nothing to see here.
}

Functions

Namesort descending Description
metatag_enable Implements hook_enable().
metatag_install Implements hook_install().
metatag_requirements Implements hook_requirements().
metatag_schema Implements hook_schema().
metatag_uninstall Implements hook_uninstall().
metatag_update_7000 Disable the deprecated metatag_ui module, which has been merged into metatag.
metatag_update_7001 Fix the "{metatag_config}.cid column cannot be NULL" error.
metatag_update_7002 Uninstall the deprecated metatag_ui module, the UI is now in the main module.
metatag_update_7003 Add the {metatag}.language field.
metatag_update_7004 Replaced by updates 7009, 7010, 7011, 7012 and 7013.
metatag_update_7005 Removing wrong metatag watchdog entries that break the dblog page.
metatag_update_7006 Remove {metatag} records that were added by old versions of the module.
metatag_update_7007 Remove {metatag} records for entities that have been deleted.
metatag_update_7008 Remove any empty records that may be hanging around from old releases.
metatag_update_7009 Fix {metatag} records for taxonomy terms.
metatag_update_7010 Fix {metatag} records for users.
metatag_update_7011 Fix {metatag} records for nodes.
metatag_update_7012 Remove duplicate {metatag} records for non-core entities.
metatag_update_7013 Fix the {metatag} language value for all non-core entity records.
metatag_update_7014 Fix {metatag} records that may have been corrupted by #1871020.
metatag_update_7015 Add the revision_id from the entity into metatag schema.
metatag_update_7016 Update the revision ID to fix the NULL values.
metatag_update_7017 The {metatag}.revision_id field is required.
metatag_update_7018 Update the revision ID for each record. This may take some time.
metatag_update_7019 Clear the entity_cache bins.
metatag_update_7020 Clear the Metatag cache.
metatag_update_7021 Clear the Metatag cache so all unwanted 403/404 paths can be purged.
metatag_update_7022 A minor bit of tidy-up after update 7015.
metatag_update_7023 Clear the Metatag cache because $cid_parts was changed.
metatag_update_7024 No-op update. Renaming twitter:image to twitter:image:src no longer needed.
metatag_update_7025 Replaced by update 7030.
metatag_update_7026 Rename the 'copyright' meta tag to 'rights', part 1.
metatag_update_7027 Replaced by update 7031.
metatag_update_7028 Clear the menu cache so the new Settings page will be picked up.
metatag_update_7029 Add an index to the {metatag} table to speed up some queries.
metatag_update_7030 No-op update. Renaming twitter:image to twitter:image:src no longer needed.
metatag_update_7031 Rename the 'copyright' meta tag to 'rights', part 2.
metatag_update_7032 Clear the Metatag cache.
metatag_update_7033 These originally removed the 'author' meta tag but are now skipped.
metatag_update_7034 Skipped.
metatag_update_7035 Skipped.
metatag_update_7036 Update variables to indicate which entities should be supported.
metatag_update_7037 Clear the menu cache so the renamed Settings page will be picked up.
metatag_update_7038 Manually enable all core entity types to resolve issues from 1.5's changes.
metatag_update_7039 Reload some meta tag caches.
metatag_update_7040 Fix any robots meta tags that were broken during importing from Nodewords.
metatag_update_7041 Rerun update 7018. This may take some time.
metatag_update_7100 Delete a deprecated variable and clear all Metatag caches.
metatag_update_7101 Update i18n strings for all meta tags to use the new format.
metatag_update_7102 Re-run update 7101.
metatag_update_7103 Clear all metatag caches so the new entity caching structure takes over.
metatag_update_7104 Remove the entity revision ID from the translation strings.
metatag_update_7105 Fix the output translation strings.
metatag_update_7106 Rerun update 7105.
metatag_update_7107 Fix the global config translation strings.
metatag_update_7108 Delete output translations if it's disabled.
metatag_update_7109 Rename the 'icon_any' meta tag to 'mask-icon' in the entity records.
metatag_update_7110 Rename the 'icon_any' meta tag to 'mask-icon' in the configs.
metatag_update_7111 Remove the "metatag_ui" record from the {system} table.
metatag_update_7112 The Publisher meta tag is now part of the Google Plus submodule.
metatag_update_7113 Clear all metatag output caches if output caching is disabled.
metatag_update_7114 Disable output caching.
metatag_update_7115 Clear all metatag output caches, it will be rebuild if needed.
metatag_update_7116 Trigger a cache clear so the Views integration can be loaded.
metatag_update_delete_config Remove a specific meta tag from all configs.
metatag_update_replace_config_tag Replace one meta tag with another in the configs.
metatag_update_replace_config_value Replace one meta tag with another in the configs.
metatag_update_replace_entity_tag Replace one meta tag's with another in the entity records.
metatag_update_replace_entity_value Replace one meta tag's value in the entity records.
_metatag_remove_dupes Remove duplicate records for a given entity.