diff --git a/metatag.install b/metatag.install index 0a99f84..d830231 100644 --- a/metatag.install +++ b/metatag.install @@ -244,158 +244,10 @@ function metatag_update_7003() { } /** - * Update all records in the metatag table and assign each the correct language - * value, will also resolve problems created during the release of beta3. This - * might take a while, depending on how much data needs to be converted. + * Replaced by updates 7009, 7010, 7011, 7012 and 7013. */ -function metatag_update_7004(&$sandbox) { - // 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', 'language')) - ->orderBy('entity_type', 'ASC') - ->orderBy('entity_id', 'ASC') - ->execute(); - $sandbox['records'] = array(); - foreach ($records as $record) { - $sandbox['records'][] = $record; - } - // 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 upgraded. - watchdog('metatag', 'Update 7004: !count records to upgrade.', array('!count' => $sandbox['max']), WATCHDOG_INFO); - - // Last record processed. - $sandbox['current_record'] = -1; - } - - // If there's no data, don't bother with the extra work. - if (empty($sandbox['max'])) { - return t('No records needed to be updated.'); - } - - // Proceed as normal. - else { - // Process records by groups of 50 (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 = 50; - - // The for loop will run as normal when ran via update.php, but when ran - // via Drush it'll just run 'til it's finished. - $increment = 1; - if (drupal_is_cli()) { - $increment = 0; - } - - // Set default values. - for ($x = 0; $x < $limit; $x += $increment) { - $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; - $language = $sandbox['records'][$sandbox['current_record']]->language; - - // 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 and that it isn't the - // same as the meta tag record's language. - if (!empty($entity)) { - // If the record has multiple values already, i.e. someone saved a - // new record because they thought the records were missing. - try { - // If there's a (non-empty) language value, use it. - if (!empty($entity->language)) { - // The language values are different. - if ($entity->language != $language) { - // Update the record with the entity's language value. - db_update('metatag') - ->fields(array('language' => $entity->language)) - ->condition('entity_type', $entity_type) - ->condition('entity_id', $entity_id) - ->condition('language', $language) - ->execute(); - } - // The language values are the same. - else { - // Do nothing. - } - } - // There's no language value. - else { - // Assign the global 'no language' value. - db_update('metatag') - ->fields(array('language' => LANGUAGE_NONE)) - ->condition('entity_type', $entity_type) - ->condition('entity_id', $entity_id) - ->condition('language', $language) - ->execute(); - } - } - catch (Exception $e) { - // Delete the newer record. - db_delete('metatag') - ->condition('language', $entity->language) - ->condition('entity_type', $entity_type) - ->condition('entity_id', $entity_id) - ->execute(); - // Update the old one again. - db_update('metatag') - ->fields(array('language' => $entity->language)) - ->condition('entity_type', $entity_type) - ->condition('entity_id', $entity_id) - ->condition('language', $language) - ->execute(); - $sandbox['messages'][] = t('The duplicate record for :type record #:id has been removed, leaving the older record in place.', array(':type' => $entity_type, ':id' => $entity_id)); - } - } - } - - // Update our progress information. - $sandbox['progress']++; - } - - // A per-iterationl message, only record if not running via Drush. - if (!drupal_is_cli()) { - watchdog('metatag', 'Update 7004: !count records were updated.', array('!count' => $x), WATCHDOG_INFO); - } - - // Set the "finished" status, to tell batch engine whether this function - // needs to run again. If you set a float, this will indicate the progress - // of the batch so the progress bar will update. - $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']); - - if ($sandbox['#finished']) { - // Clear all caches so the fixed data will be reloaded. - cache_clear_all('*', 'cache_metatag', TRUE); - - // A final log of the number of records that were converted. - watchdog('metatag', 'Update 7004: !count records were updated in total.', array('!count' => $sandbox['progress']), WATCHDOG_INFO); - - // 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'])); - } - } +function metatag_update_7004() { + // Do nothing. } /** @@ -412,7 +264,7 @@ function metatag_update_7005() { } /** - * Remove metatag records that were added by old versions of the module for + * Remove {metatag} records that were added by old versions of the module for * entities that don't actually support meta tags. 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. @@ -442,8 +294,8 @@ function metatag_update_7006() { } /** - * Remove metatag records for objects that have been deleted; older versions of - * Metatag may have failed to purge these. + * Remove {metatag} records for objects that have been deleted; older versions + * of Metatag may have failed to purge these. */ function metatag_update_7007() { $result = db_query("DELETE m @@ -478,8 +330,7 @@ function metatag_update_7007() { } /** - * Remove metatag any empty records that may be hanging around from old - * releases. + * Remove any empty records that may be hanging around from old releases. */ function metatag_update_7008() { $result = db_query("DELETE m FROM {metatag} m WHERE m.data IS NULL or m.data = '' OR m.data = :empty", array(':empty' => serialize(array()))); @@ -487,3 +338,340 @@ function metatag_update_7008() { drupal_set_message(t('Purged @count empty meta tag record(s).', array('@count' => $result->rowCount()))); } } + +/** + * Fix {metatag} records for taxonomy terms. + */ +function metatag_update_7009() { + // Remove duplicates. + _metatag_remove_dupes('taxonomy_term'); + + // Taxonomy entities don't support a 'language' option, so reset them 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() { + // 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() { + // 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; + } + + // Remove duplicates. + _metatag_remove_dupes('node'); + + // Update Node values. + $result = db_query("UPDATE {metatag} AS m INNER JOIN {node} n ON m.entity_id=n.nid AND m.entity_type='node' SET m.language = n.language"); + if ($result->rowCount() > 0) { + drupal_set_message(t('Fixed language values for @count nodes.', array('@count' => $result->rowCount()))); + } +} + +/** + * Remove duplicate {metatag} records for non-core entities. + */ +function metatag_update_7012() { + if (module_exists('entity_translation')) { + drupal_set_message(t("Entity Translation is enabled, duplicate meta tags will not be removed for custom entities, to avoid accidental dataloss.")); + return; + } + + $records = db_select('metatag', 'm') + ->fields('m', array('entity_type')) + ->condition('m.entity_type', array('node', 'taxonomy_term', 'user'), 'NOT IN') + ->orderBy('m.entity_type', 'ASC') + ->orderBy('m.entity_id', 'ASC') + ->distinct() + ->execute(); + + $entity_types = array(); + foreach ($records as $record) { + $entity_types[] = $record->entity_type; + // Remove duplicates. + _metatag_remove_dupes($record->entity_type); + } + + if (empty($entity_types)) { + drupal_set_message(t('There were no other records to fix.')); + } +} + +/** + * Fix the {metatag} language value for all non-core entity records. This might + * take a while, depending on how much data needs to be converted. + */ +function metatag_update_7013(&$sandbox) { + 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; + + // The for loop will run as normal when ran via update.php, but when ran via + // Drush it'll just run 'til it's finished. + $increment = 1; + if (drupal_is_cli()) { + $increment = 0; + } + + // Set default values. + for ($ctr = 0; $ctr < $limit; $ctr += $increment) { + $sandbox['current_record']++; + if (empty($sandbox['records'][$sandbox['current_record']])) { + break; + } + + // Shortcuts for later. + $entity_type = $sandbox['records'][$sandbox['current_record']]->entity_type; + $entity_id = $sandbox['records'][$sandbox['current_record']]->entity_id; + + // Load the entity. + $entities = entity_load($entity_type, array($entity_id)); + if (!empty($entities)) { + $entity = array_pop($entities); + + // Make sure that the entity has a language set. + if (!empty($entity)) { + // If there's a (non-empty) language value, use it. + $new_language = entity_language($entity_type, $entity); + if (empty($new_language)) { + $new_language = LANGUAGE_NONE; + } + // Update the 'language' value. + db_update('metatag') + ->fields(array('language' => $new_language)) + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_id) + ->execute(); + } + } + + // Update our progress information. + $sandbox['progress']++; + } + + // Set the "finished" status, to tell batch engine whether this function + // needs to run again. If you set a float, this will indicate the progress of + // the batch so the progress bar will update. + $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']); + + if ($sandbox['#finished']) { + // Clear all caches so the fixed data will be reloaded. + cache_clear_all('*', 'cache_metatag', TRUE); + + // A final log of the number of records that were converted. + watchdog('metatag', 'Update 7013: !count records were updated in total.', array('!count' => $sandbox['progress']), WATCHDOG_INFO); + if (drupal_is_cli()) { + drupal_set_message(t('Update 7013: !count records were updated.', array('!count' => $sandbox['progress']))); + } + + // hook_update_N() may optionally return a string which will be displayed + // to the user. + return t('!count records were updated in total.', array('!count' => $sandbox['progress'])); + } +} + +/** + * Remove duplicate records for a given content type. + * + * It should be OK to run this without doing a separate batch process as there + * shouldn't be many records that have this problem. Hopefully. + * + * @param $entity_type + * The name of an entity type to check for. + */ +function _metatag_remove_dupes($entity_type) { + $purge_count = 0; + + // First step: fix the records. There should not be multiple records for the + // same entity_id with different languages. + $dupe_records = db_query("SELECT m.entity_id, count(m.language) AS the_count + FROM metatag m + WHERE + m.entity_type = :type + GROUP BY m.entity_id + HAVING the_count > 1", array(':type' => $entity_type)); + + if (!empty($dupe_records)) { + foreach ($dupe_records as $record) { + $entity_id = $record->entity_id; + $langs = db_query("SELECT m.entity_id, m.language, m.data FROM metatag m WHERE m.entity_type = :type AND m.entity_id = :id", array(':type' => $entity_type, ':id' => $entity_id))->fetchAll(); + + // Work out which language record to remove. Will need to store this as + // an array incase there are multiple records to purge. + $langs_to_remove = array(); + + // Check for duplicate records. + // Outer loop starts from the beginning. + for ($outer = 0; $outer < count($langs); $outer++) { + // This record may have been removed already. + if (isset($langs[$outer])) { + // Inner loop starts from the end. + for ($inner = count($langs) - 1; $inner > 0; $inner--) { + // Work out if the outer loop's data is the same as the inner + // loop's. + if (isset($langs[$inner]) && $langs[$outer]->data == $langs[$inner]->data) { + // Remove the second record. + $langs_to_remove[] = $langs[$inner]->language; + unset($langs[$inner]); + } + } + } + } + + // Only one record left. + if (count($langs) == 1) { + // This is how it should be, this record is fine. + } + // More than one record, work out which one to keep. + elseif (count($langs) > 1) { + // Work out the entity's language. + $entity = entity_load($entity_type, $entity_id); + $entity_language = entity_language($entity_type, $entity); + if (empty($language)) { + $entity_language = LANGUAGE_NONE; + } + + // Work out if the entity's language record exists. + $lang_pos = NULL; + foreach ($langs as $key => $record) { + if ($record->language == $entity_language) { + $lang_pos = $key; + break; + } + } + // If the language record exists, delete the others. + if (isset($lang_pos)) { + foreach ($langs as $key => $record) { + if ($record->language != $entity_language) { + $langs_to_remove[] = $record->language; + } + } + } + // Otherwise look for a record for the site's default language. + else { + foreach ($langs as $key => $record) { + if ($record->language == $GLOBALS['language']->language) { + $lang_pos = $key; + break; + } + } + if (isset($lang_pos)) { + foreach ($langs as $key => $record) { + if ($record->language != $GLOBALS['language']->language) { + $langs_to_remove[] = $record->language; + } + } + } + // Finally check for LANGUAGE_NONE. + else { + foreach ($langs as $key => $record) { + if ($record->language == LANGUAGE_NONE) { + $lang_pos = $key; + break; + } + } + if (isset($lang_pos)) { + foreach ($langs as $key => $record) { + if ($record->language != LANGUAGE_NONE) { + $langs_to_remove[] = $record->language; + } + } + } + } + } + } + + // Purge the redundant records. + if (!empty($langs_to_remove)) { + $purge_count += db_delete('metatag') + ->condition('entity_type', $entity_type) + ->condition('entity_id', $entity_id) + ->condition('language', $langs_to_remove) + ->execute(); + } + } + } + + if (empty($purge_count)) { + drupal_set_message(t('No duplicate :entity_type records were found (this is a good thing).', array(':entity_type' => $entity_type))); + watchdog('metatag', 'No duplicate :entity_type records were found (this is a good thing).', array(':entity_type' => $entity_type)); + } + else { + drupal_set_message(t('Purged :count duplicate :entity_type record(s).', array(':count' => $purge_count, ':entity_type' => $entity_type))); + watchdog('metatag', 'Purged :count duplicate :entity_type record(s).', array(':count' => $purge_count, ':entity_type' => $entity_type)); + return; + } +}