metatag.install 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. <?php
  2. /**
  3. * @file
  4. * Install, update, and uninstall functions for the metatag module.
  5. */
  6. /**
  7. * Implements hook_requirements().
  8. */
  9. function metatag_requirements($phase) {
  10. $requirements = array();
  11. // Ensure translations don't break during installation.
  12. $t = get_t();
  13. if ($phase == 'runtime') {
  14. // Work out the release of D7 that is currently running.
  15. list($major, $minor) = explode('.', VERSION);
  16. // Strip off any suffixes on the version string, e.g. "17-dev".
  17. if (strpos('-', $minor)) {
  18. list($minor, $suffix) = explode('-', $minor);
  19. }
  20. // Releases of Drupal older than 7.15 did not have entity_language(), which
  21. // is now required.
  22. if ($minor < 15) {
  23. $requirements['metatag'] = array(
  24. 'severity' => REQUIREMENT_WARNING,
  25. 'title' => 'Metatag',
  26. 'value' => $t('Upgrade Drupal core to v7.15 or newer'),
  27. 'description' => $t("This older version of Drupal core is missing functionality necessary for the module's multilingual support, it must be upgraded to at least version 7.15."),
  28. );
  29. }
  30. // Releases of Drupal older than 7.17 did not trigger hook_entity_view on
  31. // term pages, so recommend updating.
  32. elseif ($minor < 17) {
  33. $requirements['metatag'] = array(
  34. 'severity' => REQUIREMENT_WARNING,
  35. 'title' => 'Metatag',
  36. 'value' => $t('Upgrade Drupal core to v7.17 or newer'),
  37. 'description' => $t('Your older version of Drupal core is missing functionality necessary for taxonomy term pages to work correctly, it is strongly recommended to upgrade to the latest release.'),
  38. );
  39. }
  40. // Everything's OK.
  41. else {
  42. $requirements['metatag'] = array(
  43. 'severity' => REQUIREMENT_OK,
  44. 'title' => 'Metatag',
  45. 'value' => $t('Drupal core is compatible'),
  46. 'description' => $t('Older versions of Drupal core were missing functionality necessary for taxonomy term pages to work correctly, but this version <em>will</em> work correctly.'),
  47. );
  48. }
  49. // Add a note if Page Title is also installed.
  50. if (module_exists('page_title')) {
  51. $requirements['metatag_page_title'] = array(
  52. 'severity' => REQUIREMENT_INFO,
  53. 'title' => 'Metatag',
  54. 'value' => $t('Possible conflicts with Page Title module'),
  55. 'description' => $t('The Metatag module is able to customize page titles so running the Page Title module simultaneously can lead to complications.'),
  56. );
  57. }
  58. // Add a note if Page Title is also installed.
  59. if (module_exists('exclude_node_title')) {
  60. $requirements['metatag_exclude_node_title'] = array(
  61. 'severity' => REQUIREMENT_INFO,
  62. 'title' => 'Metatag',
  63. 'value' => $t('Possible conflicts with Exclude Node Title module'),
  64. 'description' => $t('The Metatag module\'s default settings for content types (nodes) uses [node:title] for the page title. Unfortunately, Exclude Node Title hides this so the page title ends up blank. It is recommended to <a href="!config">change the "title" field\'s default value</a> to "[current-page:title]" instead of "[node:title]" for any content types affected by Exclude Node Title.', array('!config' => 'admin/config/search/metatags')),
  65. );
  66. }
  67. // Add a note if the deprecated metatag.entity_translation.inc file still
  68. // exists.
  69. $filename = 'metatag.entity_translation.inc';
  70. if (file_exists(dirname(__FILE__) . '/' . $filename)) {
  71. $requirements['metatag_deprecated_et_file'] = array(
  72. 'severity' => REQUIREMENT_ERROR,
  73. 'title' => 'Metatag',
  74. 'value' => $t('Unwanted :filename file found', array(':filename' => $filename)),
  75. '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)),
  76. );
  77. }
  78. // Check that Entity_Translation is current.
  79. if (module_exists('entity_translation')) {
  80. $rev = db_query("SELECT schema_version FROM {system} WHERE name = :module", array(':module' => 'entity_translation'))->fetchColumn();
  81. if ($rev < 7004) {
  82. $requirements['metatag_et_old'] = array(
  83. 'severity' => REQUIREMENT_ERROR,
  84. 'title' => 'Metatag',
  85. 'value' => $t('<a href="@url">Entity_Translation</a> is out of date and requires updating', array('@url' => 'http://drupal.org/project/entity_translation')),
  86. 'description' => $t('The Entity_Translation module is out of date and needs to be updated in order to be compatible with Metatag.'),
  87. );
  88. }
  89. }
  90. }
  91. return $requirements;
  92. }
  93. /**
  94. * Implements hook_schema().
  95. */
  96. function metatag_schema() {
  97. $schema['metatag_config'] = array(
  98. 'description' => 'Storage of meta tag configuration and defaults.',
  99. 'export' => array(
  100. 'key' => 'instance',
  101. 'key name' => 'Instance',
  102. 'primary key' => 'cid',
  103. 'identifier' => 'config',
  104. 'default hook' => 'metatag_config_default',
  105. 'api' => array(
  106. 'owner' => 'metatag',
  107. 'api' => 'metatag',
  108. 'minimum_version' => 1,
  109. 'current_version' => 1,
  110. ),
  111. 'cache defaults' => TRUE,
  112. 'default cache bin' => 'cache_metatag',
  113. ),
  114. 'fields' => array(
  115. 'cid' => array(
  116. 'type' => 'serial',
  117. 'unsigned' => TRUE,
  118. 'not null' => TRUE,
  119. 'description' => 'The primary identifier for a metatag configuration set.',
  120. 'no export' => TRUE,
  121. ),
  122. 'instance' => array(
  123. 'type' => 'varchar',
  124. 'length' => 255,
  125. 'not null' => TRUE,
  126. 'default' => '',
  127. 'description' => 'The machine-name of the configuration, typically entity-type:bundle.',
  128. ),
  129. 'config' => array(
  130. 'type' => 'blob',
  131. 'size' => 'big',
  132. 'not null' => TRUE,
  133. 'serialize' => TRUE,
  134. 'description' => 'Serialized data containing the meta tag configuration.',
  135. 'translatable' => TRUE,
  136. ),
  137. ),
  138. 'primary key' => array('cid'),
  139. 'unique keys' => array(
  140. 'instance' => array('instance'),
  141. ),
  142. );
  143. $schema['metatag'] = array(
  144. 'fields' => array(
  145. 'entity_type' => array(
  146. 'type' => 'varchar',
  147. 'length' => 32,
  148. 'not null' => TRUE,
  149. 'default' => '',
  150. 'description' => 'The entity type this data is attached to.',
  151. ),
  152. 'entity_id' => array(
  153. 'type' => 'int',
  154. 'unsigned' => TRUE,
  155. 'not null' => TRUE,
  156. 'default' => 0,
  157. 'description' => 'The entity id this data is attached to.',
  158. ),
  159. 'revision_id' => array(
  160. 'type' => 'int',
  161. 'unsigned' => TRUE,
  162. 'not null' => FALSE,
  163. 'default' => 0,
  164. 'description' => 'The revision_id for the entity object this data is attached to.',
  165. ),
  166. 'language' => array(
  167. 'type' => 'varchar',
  168. 'length' => 32,
  169. 'not null' => TRUE,
  170. 'default' => '',
  171. 'description' => 'The language of the tag.',
  172. ),
  173. 'data' => array(
  174. 'type' => 'blob',
  175. 'size' => 'big',
  176. 'not null' => TRUE,
  177. 'serialize' => TRUE,
  178. ),
  179. ),
  180. 'primary key' => array('entity_type', 'entity_id', 'revision_id', 'language'),
  181. );
  182. $schema['cache_metatag'] = drupal_get_schema_unprocessed('system', 'cache');
  183. $schema['cache_metatag']['description'] = t('Cache table for the generated meta tag output.');
  184. return $schema;
  185. }
  186. /**
  187. * Implements hook_install().
  188. */
  189. function metatag_install() {
  190. 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 afffect this site.", array('!url' => url(drupal_get_path('module', 'metatag') . '/README.txt'))));
  191. }
  192. /**
  193. * Implements hook_uninstall().
  194. */
  195. function metatag_uninstall() {
  196. // This variable is created via hook_enable.
  197. variable_del('metatag_schema_installed');
  198. }
  199. /**
  200. * Implements hook_enable().
  201. */
  202. function metatag_enable() {
  203. variable_set('metatag_schema_installed', TRUE);
  204. }
  205. /**
  206. * Implements hook_disable().
  207. */
  208. // function metatag_disable() {
  209. // }
  210. /**
  211. * Disable the deprecated metatag_ui module which has been merged into metatag.
  212. */
  213. function metatag_update_7000() {
  214. if (module_exists('metatag_ui')) {
  215. module_disable(array('metatag_ui'), FALSE);
  216. drupal_uninstall_modules(array('metatag_ui'), FALSE);
  217. }
  218. }
  219. /**
  220. * Fix the "{metatag_config}.cid column cannot be NULL" error.
  221. */
  222. function metatag_update_7001() {
  223. $table_name = 'metatag_config';
  224. $field_name = 'cid';
  225. $field_spec = array(
  226. 'type' => 'serial',
  227. 'unsigned' => TRUE,
  228. 'not null' => TRUE,
  229. 'description' => 'The primary identifier for a metatag configuration set.',
  230. );
  231. $keys = array('primary key' => array($field_name));
  232. // Before making any changes, drop the existing primary key. Unforunately
  233. // there is no API way to check if a primary key exists, so if it doesn't
  234. // exist the db_drop_primary_key() call will fail.
  235. try {
  236. db_drop_primary_key($table_name);
  237. }
  238. catch (Exception $e) {
  239. drupal_set_message('Caught an exception: ', $e->getMessage());
  240. }
  241. // Rejig the field, and turn on the primary key again.
  242. db_change_field($table_name, $field_name, $field_name, $field_spec, $keys);
  243. }
  244. /**
  245. * Disable the deprecated metatag_ui module which has been merged into metatag,
  246. * again.
  247. */
  248. function metatag_update_7002() {
  249. if (module_exists('metatag_ui')) {
  250. module_disable(array('metatag_ui'), FALSE);
  251. drupal_uninstall_modules(array('metatag_ui'), FALSE);
  252. drupal_set_message(t('The deprecated Metatag UI module has been disabled.'));
  253. }
  254. }
  255. /**
  256. * Add the {metatag}.language field.
  257. */
  258. function metatag_update_7003() {
  259. // Set the target table and field name.
  260. $table_name = 'metatag';
  261. $field_name = 'language';
  262. // Don't add the new field if it already exists.
  263. if (!db_field_exists($table_name, $field_name)) {
  264. // Describe the new field.
  265. $field_spec = array(
  266. 'type' => 'varchar',
  267. 'length' => 32,
  268. 'not null' => TRUE,
  269. 'default' => '',
  270. 'description' => 'The language of the tag',
  271. );
  272. // Add it and update the primary key.
  273. db_add_field($table_name, $field_name, $field_spec);
  274. db_drop_primary_key($table_name);
  275. db_add_primary_key($table_name, array('entity_type', 'entity_id', 'language'));
  276. }
  277. }
  278. /**
  279. * Replaced by updates 7009, 7010, 7011, 7012 and 7013.
  280. */
  281. function metatag_update_7004() {
  282. // Do nothing.
  283. }
  284. /**
  285. * Removing wrong metatag watchdog entries that break the admin/reports/dblog
  286. * page.
  287. */
  288. function metatag_update_7005() {
  289. if (db_table_exists('watchdog')) {
  290. db_delete('watchdog')
  291. ->condition('type', 'metatag')
  292. ->condition('variables', serialize('info'))
  293. ->execute();
  294. }
  295. }
  296. /**
  297. * Remove {metatag} records that were added by old versions of the module for
  298. * entities that don't actually support Metatag. A more complete version of
  299. * this will be added later on after it's (hopefully) guaranteed that all
  300. * modules have updated to the correct API usage.
  301. */
  302. function metatag_update_7006() {
  303. $entity_types = array(
  304. // Core.
  305. 'comment',
  306. 'menu_link',
  307. 'taxonomy_vocabulary',
  308. // Some contrib entities.
  309. 'mailchimp_list',
  310. 'profile2',
  311. 'profile2_type',
  312. 'redirect',
  313. 'rules_config',
  314. 'wysiwyg_profile',
  315. );
  316. foreach ($entity_types as $entity_type) {
  317. $num_deleted = db_delete('metatag')
  318. ->condition('entity_type', $entity_type)
  319. ->execute();
  320. if ($num_deleted > 0) {
  321. 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)));
  322. }
  323. }
  324. }
  325. /**
  326. * Remove {metatag} records for objects that have been deleted; older versions
  327. * of Metatag may have failed to purge these.
  328. */
  329. function metatag_update_7007() {
  330. $nodes = db_query("SELECT m.entity_id
  331. FROM {metatag} m
  332. LEFT OUTER JOIN {node} n
  333. ON m.entity_id=n.nid
  334. WHERE m.entity_type='node'
  335. AND n.nid IS NULL")
  336. ->fetchCol();
  337. if (count($nodes) > 0) {
  338. $deleted = db_delete('metatag')
  339. ->condition('entity_type', 'node')
  340. ->condition('entity_id', $nodes)
  341. ->execute();
  342. if ($deleted > 0) {
  343. drupal_set_message(t('Removed @count meta tag record(s) for nodes that had been purged.', array('@count' => $deleted)));
  344. }
  345. else {
  346. drupal_set_message(t('There were no meta tag records to purge for removed nodes. This is a good thing :)'));
  347. }
  348. }
  349. $users = db_query("SELECT m.entity_id
  350. FROM {metatag} m
  351. LEFT OUTER JOIN {users} u
  352. ON m.entity_id=u.uid
  353. WHERE m.entity_type='user'
  354. AND u.uid IS NULL")
  355. ->fetchCol();
  356. if (count($users) > 0) {
  357. $deleted = db_delete('metatag')
  358. ->condition('entity_type', 'user')
  359. ->condition('entity_id', $users)
  360. ->execute();
  361. if ($deleted > 0) {
  362. drupal_set_message(t('Removed @count meta tag record(s) for users that had been purged.', array('@count' => $deleted)));
  363. }
  364. else {
  365. drupal_set_message(t('There were no meta tag records to purge for removed users. This is a good thing :)'));
  366. }
  367. }
  368. // Only run this if the Taxonomy module is enabled.
  369. if (module_exists('taxonomy')) {
  370. $terms = db_query("SELECT m.entity_id
  371. FROM {metatag} m
  372. LEFT OUTER JOIN {taxonomy_term_data} t
  373. ON m.entity_id=t.tid
  374. WHERE m.entity_type='taxonomy_term'
  375. AND t.tid IS NULL")
  376. ->fetchCol();
  377. if (count($terms) > 0) {
  378. $deleted = db_delete('metatag')
  379. ->condition('entity_type', 'taxonomy_term')
  380. ->condition('entity_id', $terms)
  381. ->execute();
  382. if ($deleted > 0) {
  383. drupal_set_message(t('Removed @count meta tag record(s) for taxonomy terms that had been purged.', array('@count' => $deleted)));
  384. }
  385. else {
  386. drupal_set_message(t('There were no meta tag records to purge for removed taxonomy terms. This is a good thing :)'));
  387. }
  388. }
  389. }
  390. }
  391. /**
  392. * Remove any empty records that may be hanging around from old releases.
  393. */
  394. function metatag_update_7008() {
  395. $conditions = db_or()
  396. ->isNull('data')
  397. ->condition('data', '')
  398. ->condition('data', serialize(array()));
  399. $deleted = db_delete("metatag")
  400. ->condition($conditions)
  401. ->execute();
  402. if ($deleted > 0) {
  403. drupal_set_message(t('Purged @count empty meta tag record(s).', array('@count' => $deleted)));
  404. }
  405. }
  406. /**
  407. * Fix {metatag} records for taxonomy terms.
  408. */
  409. function metatag_update_7009() {
  410. if (module_exists('taxonomy')) {
  411. // Remove duplicates.
  412. _metatag_remove_dupes('taxonomy_term');
  413. }
  414. // The taxonomy term entity doesn't support a 'language' option, so reset it
  415. // to LANGUAGE_NONE.
  416. $result = db_query("UPDATE {metatag} SET language = :language WHERE entity_type='taxonomy_term'", array(':language' => LANGUAGE_NONE));
  417. if ($result->rowCount() > 0) {
  418. drupal_set_message(t('Fixed language values for @count taxonomy terms.', array('@count' => $result->rowCount())));
  419. }
  420. }
  421. /**
  422. * Fix {metatag} records for users.
  423. */
  424. function metatag_update_7010() {
  425. // Remove duplicates.
  426. _metatag_remove_dupes('user');
  427. // Update User values.
  428. $result = db_query("UPDATE {metatag} SET language = :language WHERE entity_type='user'", array(':language' => LANGUAGE_NONE));
  429. if ($result->rowCount() > 0) {
  430. drupal_set_message(t('Fixed language values for @count user records.', array('@count' => $result->rowCount())));
  431. }
  432. }
  433. /**
  434. * Fix {metatag} records for nodes.
  435. */
  436. function metatag_update_7011(&$sandbox) {
  437. // Only proceed if Entity_Translation is not enabled as it allows each node
  438. // record to have multiple languages available.
  439. if (module_exists('entity_translation')) {
  440. drupal_set_message(t("Entity Translation is enabled, so node meta tags will not be updated, to avoid accidental dataloss."));
  441. return;
  442. }
  443. // Process records by groups of 10 (arbitrary value).
  444. // When a group is processed, the batch update engine determines whether it
  445. // should continue processing in the same request or provide progress
  446. // feedback to the user and wait for the next request.
  447. $limit = 20;
  448. // Use the sandbox at your convenience to store the information needed
  449. // to track progression between successive calls to the function.
  450. if (!isset($sandbox['progress'])) {
  451. // The count of records visited so far.
  452. $sandbox['progress'] = 0;
  453. // Remove duplicates.
  454. _metatag_remove_dupes('node');
  455. // Update Node values.
  456. $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");
  457. $sandbox['records'] = array();
  458. foreach ($nodes as $record) {
  459. $sandbox['records'][] = $record;
  460. }
  461. // If there's no data, don't bother with the extra work.
  462. if (empty($sandbox['records'])) {
  463. watchdog('metatag', 'Update 7011: No nodes need the Metatag language values fixed.', array(), WATCHDOG_INFO);
  464. if (drupal_is_cli()) {
  465. drupal_set_message(t('Update 7011: No nodes need the Metatag language values fixed.'));
  466. }
  467. return t('No nodes need the Metatag language values fixed.');
  468. }
  469. // Total records that must be visited.
  470. $sandbox['max'] = count($sandbox['records']);
  471. // A place to store messages during the run.
  472. $sandbox['messages'] = array();
  473. // An initial record of the number of records to be updated.
  474. watchdog('metatag', 'Update 7011: !count records to update.', array('!count' => $sandbox['max']), WATCHDOG_INFO);
  475. if (drupal_is_cli()) {
  476. drupal_set_message(t('Update 7011: !count records to update.', array('!count' => $sandbox['max'])));
  477. }
  478. // Last record processed.
  479. $sandbox['current_record'] = -1;
  480. // Because a lot of other processing happens on the first iteration, just do
  481. // one.
  482. $limit = 1;
  483. }
  484. // The for loop will run as normal when ran via update.php, but when ran via
  485. // Drush it'll just run 'til it's finished.
  486. $increment = 1;
  487. if (drupal_is_cli()) {
  488. $increment = 0;
  489. }
  490. // Set default values.
  491. for ($ctr = 0; $ctr < $limit; $ctr += $increment) {
  492. $sandbox['current_record']++;
  493. if (empty($sandbox['records'][$sandbox['current_record']])) {
  494. break;
  495. }
  496. // Shortcuts for later.
  497. $langcode = $sandbox['records'][$sandbox['current_record']]->language;
  498. $nid = $sandbox['records'][$sandbox['current_record']]->nid;
  499. db_update('metatag')
  500. ->fields(array('language' => $langcode))
  501. ->condition('entity_type', 'node')
  502. ->condition('entity_id', $nid)
  503. ->execute();
  504. // Update our progress information.
  505. $sandbox['progress']++;
  506. }
  507. // Set the "finished" status, to tell batch engine whether this function
  508. // needs to run again. If you set a float, this will indicate the progress of
  509. // the batch so the progress bar will update.
  510. $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
  511. if ($sandbox['#finished']) {
  512. // Clear all caches so the fixed data will be reloaded.
  513. cache_clear_all('*', 'cache_metatag', TRUE);
  514. // A final log of the number of records that were converted.
  515. watchdog('metatag', 'Update 7011: !count records were updated in total.', array('!count' => $sandbox['progress']), WATCHDOG_INFO);
  516. if (drupal_is_cli()) {
  517. drupal_set_message(t('Update 7011: !count records were updated.', array('!count' => $sandbox['progress'])));
  518. }
  519. // hook_update_N() may optionally return a string which will be displayed
  520. // to the user.
  521. return t('Fixed the Metatag language values for @count nodes.', array('!count' => $sandbox['progress']));
  522. }
  523. }
  524. /**
  525. * Remove duplicate {metatag} records for non-core entities.
  526. */
  527. function metatag_update_7012() {
  528. if (module_exists('entity_translation')) {
  529. drupal_set_message(t("Entity Translation is enabled, duplicate meta tags will not be removed for custom entities, to avoid accidental dataloss."));
  530. return;
  531. }
  532. $records = db_select('metatag', 'm')
  533. ->fields('m', array('entity_type'))
  534. ->condition('m.entity_type', array('node', 'taxonomy_term', 'user'), 'NOT IN')
  535. ->orderBy('m.entity_type', 'ASC')
  536. ->orderBy('m.entity_id', 'ASC')
  537. ->distinct()
  538. ->execute();
  539. $entity_types = array();
  540. foreach ($records as $record) {
  541. $entity_types[] = $record->entity_type;
  542. // Remove duplicates.
  543. _metatag_remove_dupes($record->entity_type);
  544. }
  545. if (empty($entity_types)) {
  546. drupal_set_message(t('There were no other records to fix.'));
  547. }
  548. }
  549. /**
  550. * Fix the {metatag} language value for all non-core entity records. This might
  551. * take a while, depending on how much data needs to be converted.
  552. */
  553. function metatag_update_7013(&$sandbox) {
  554. if (module_exists('entity_translation')) {
  555. drupal_set_message(t("Entity Translation is enabled, meta tags will not be updated for custom entities, to avoid accidental dataloss."));
  556. return;
  557. }
  558. // Use the sandbox at your convenience to store the information needed
  559. // to track progression between successive calls to the function.
  560. if (!isset($sandbox['progress'])) {
  561. // The count of records visited so far.
  562. $sandbox['progress'] = 0;
  563. // Because the {metatag} table uses multiple primary keys, there's no easy
  564. // way to do this, so we're going to cache all record keys and manually
  565. // step through them.
  566. $records = db_select('metatag', 'm')
  567. ->fields('m', array('entity_type', 'entity_id'))
  568. ->condition('m.entity_type', array('node', 'taxonomy_term', 'user'), 'NOT IN')
  569. ->orderBy('m.entity_type', 'ASC')
  570. ->orderBy('m.entity_id', 'ASC')
  571. ->execute();
  572. $sandbox['records'] = array();
  573. foreach ($records as $record) {
  574. $sandbox['records'][] = $record;
  575. }
  576. // If there's no data, don't bother with the extra work.
  577. if (empty($sandbox['records'])) {
  578. watchdog('metatag', 'Update 7013: No meta tag records need updating.', array(), WATCHDOG_INFO);
  579. if (drupal_is_cli()) {
  580. drupal_set_message(t('Update 7013: No meta tag records need updating.'));
  581. }
  582. return t('No meta tag records need updating.');
  583. }
  584. // Total records that must be visited.
  585. $sandbox['max'] = count($sandbox['records']);
  586. // A place to store messages during the run.
  587. $sandbox['messages'] = array();
  588. // An initial record of the number of records to be updated.
  589. watchdog('metatag', 'Update 7013: !count records to update.', array('!count' => $sandbox['max']), WATCHDOG_INFO);
  590. if (drupal_is_cli()) {
  591. drupal_set_message(t('Update 7013: !count records to update.', array('!count' => $sandbox['max'])));
  592. }
  593. // Last record processed.
  594. $sandbox['current_record'] = -1;
  595. }
  596. // Process records by groups of 10 (arbitrary value).
  597. // When a group is processed, the batch update engine determines whether it
  598. // should continue processing in the same request or provide progress
  599. // feedback to the user and wait for the next request.
  600. $limit = 10;
  601. // The for loop will run as normal when ran via update.php, but when ran via
  602. // Drush it'll just run 'til it's finished.
  603. $increment = 1;
  604. if (drupal_is_cli()) {
  605. $increment = 0;
  606. }
  607. // Set default values.
  608. for ($ctr = 0; $ctr < $limit; $ctr += $increment) {
  609. $sandbox['current_record']++;
  610. if (empty($sandbox['records'][$sandbox['current_record']])) {
  611. break;
  612. }
  613. // Shortcuts for later.
  614. $entity_type = $sandbox['records'][$sandbox['current_record']]->entity_type;
  615. $entity_id = $sandbox['records'][$sandbox['current_record']]->entity_id;
  616. // Load the entity.
  617. $entities = entity_load($entity_type, array($entity_id));
  618. if (!empty($entities)) {
  619. $entity = array_pop($entities);
  620. // Make sure that the entity has a language set.
  621. if (!empty($entity)) {
  622. // If there's a (non-empty) language value, use it.
  623. $new_language = entity_language($entity_type, $entity);
  624. if (empty($new_language)) {
  625. $new_language = LANGUAGE_NONE;
  626. }
  627. // Update the 'language' value.
  628. db_update('metatag')
  629. ->fields(array('language' => $new_language))
  630. ->condition('entity_type', $entity_type)
  631. ->condition('entity_id', $entity_id)
  632. ->execute();
  633. }
  634. }
  635. // Update our progress information.
  636. $sandbox['progress']++;
  637. }
  638. // Set the "finished" status, to tell batch engine whether this function
  639. // needs to run again. If you set a float, this will indicate the progress of
  640. // the batch so the progress bar will update.
  641. $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
  642. if ($sandbox['#finished']) {
  643. // Clear all caches so the fixed data will be reloaded.
  644. cache_clear_all('*', 'cache_metatag', TRUE);
  645. // A final log of the number of records that were converted.
  646. watchdog('metatag', 'Update 7013: !count records were updated in total.', array('!count' => $sandbox['progress']), WATCHDOG_INFO);
  647. if (drupal_is_cli()) {
  648. drupal_set_message(t('Update 7013: !count records were updated.', array('!count' => $sandbox['progress'])));
  649. }
  650. // hook_update_N() may optionally return a string which will be displayed
  651. // to the user.
  652. return t('!count records were updated in total.', array('!count' => $sandbox['progress']));
  653. }
  654. }
  655. /**
  656. * Remove duplicate records for a given entity.
  657. *
  658. * It should be OK to run this without doing a separate batch process as there
  659. * shouldn't be many records that have this problem. Hopefully.
  660. *
  661. * @param $entity_type
  662. * The name of an entity type to check for.
  663. */
  664. function _metatag_remove_dupes($entity_type) {
  665. $purge_count = 0;
  666. // First step: fix the records. There should not be multiple records for the
  667. // same entity_id with different languages.
  668. $dupe_records = db_query("SELECT m.entity_id, count(m.language) AS the_count
  669. FROM {metatag} m
  670. WHERE
  671. m.entity_type = :type
  672. GROUP BY m.entity_id
  673. HAVING count(m.language) > 1", array(':type' => $entity_type));
  674. if (!empty($dupe_records)) {
  675. foreach ($dupe_records as $record) {
  676. $entity_id = $record->entity_id;
  677. $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();
  678. // Work out which language record to remove. Will need to store this as
  679. // an array incase there are multiple records to purge.
  680. $langs_to_remove = array();
  681. // Check for duplicate records.
  682. // Outer loop starts from the beginning.
  683. for ($outer = 0; $outer < count($langs); $outer++) {
  684. // This record may have been removed already.
  685. if (isset($langs[$outer])) {
  686. // Inner loop starts from the end.
  687. for ($inner = count($langs) - 1; $inner > 0; $inner--) {
  688. // Work out if the outer loop's data is the same as the inner
  689. // loop's.
  690. if (isset($langs[$inner]) && $langs[$outer]->data == $langs[$inner]->data) {
  691. // Remove the second record.
  692. $langs_to_remove[] = $langs[$inner]->language;
  693. unset($langs[$inner]);
  694. }
  695. }
  696. }
  697. }
  698. // Only one record left.
  699. if (count($langs) == 1) {
  700. // This is how it should be, this record is fine.
  701. }
  702. // More than one record, work out which one to keep.
  703. elseif (count($langs) > 1) {
  704. // Work out the entity's language.
  705. $entity = entity_load($entity_type, $entity_id);
  706. $entity_language = entity_language($entity_type, $entity);
  707. if (empty($language)) {
  708. $entity_language = LANGUAGE_NONE;
  709. }
  710. // Work out if the entity's language record exists.
  711. $lang_pos = NULL;
  712. foreach ($langs as $key => $record) {
  713. if ($record->language == $entity_language) {
  714. $lang_pos = $key;
  715. break;
  716. }
  717. }
  718. // If the language record exists, delete the others.
  719. if (isset($lang_pos)) {
  720. foreach ($langs as $key => $record) {
  721. if ($record->language != $entity_language) {
  722. $langs_to_remove[] = $record->language;
  723. }
  724. }
  725. }
  726. // Otherwise look for a record for the site's default language.
  727. else {
  728. foreach ($langs as $key => $record) {
  729. if ($record->language == $GLOBALS['language']->language) {
  730. $lang_pos = $key;
  731. break;
  732. }
  733. }
  734. if (isset($lang_pos)) {
  735. foreach ($langs as $key => $record) {
  736. if ($record->language != $GLOBALS['language']->language) {
  737. $langs_to_remove[] = $record->language;
  738. }
  739. }
  740. }
  741. // Finally check for LANGUAGE_NONE.
  742. else {
  743. foreach ($langs as $key => $record) {
  744. if ($record->language == LANGUAGE_NONE) {
  745. $lang_pos = $key;
  746. break;
  747. }
  748. }
  749. if (isset($lang_pos)) {
  750. foreach ($langs as $key => $record) {
  751. if ($record->language != LANGUAGE_NONE) {
  752. $langs_to_remove[] = $record->language;
  753. }
  754. }
  755. }
  756. }
  757. }
  758. }
  759. // Purge the redundant records.
  760. if (!empty($langs_to_remove)) {
  761. $purge_count += db_delete('metatag')
  762. ->condition('entity_type', $entity_type)
  763. ->condition('entity_id', $entity_id)
  764. ->condition('language', $langs_to_remove)
  765. ->execute();
  766. }
  767. }
  768. }
  769. if (empty($purge_count)) {
  770. drupal_set_message(t('No duplicate :entity_type records were found (this is a good thing).', array(':entity_type' => $entity_type)));
  771. watchdog('metatag', 'No duplicate :entity_type records were found (this is a good thing).', array(':entity_type' => $entity_type));
  772. }
  773. else {
  774. drupal_set_message(t('Purged :count duplicate :entity_type record(s).', array(':count' => $purge_count, ':entity_type' => $entity_type)));
  775. watchdog('metatag', 'Purged :count duplicate :entity_type record(s).', array(':count' => $purge_count, ':entity_type' => $entity_type));
  776. return;
  777. }
  778. }
  779. /**
  780. * Fix {metatag} records that may have been corrupted by #1871020.
  781. */
  782. function metatag_update_7014() {
  783. $records = db_query("SELECT *
  784. FROM {metatag} m
  785. WHERE
  786. m.data LIKE :nolang
  787. OR m.data LIKE :lang
  788. OR m.data LIKE :und",
  789. array(
  790. ':nolang' => 'a:1:{s:0:"";a:%:{s:%;a:%:{%;}}}',
  791. ':lang' => 'a:1:{s:2:"__";a:%:{s:%;a:%:{%;}}}',
  792. ':und' => 'a:1:{s:3:"___";a:%:{s:%;a:%:{%;}}}',
  793. ));
  794. // Nothing to fix.
  795. if ($records->rowCount() == 0) {
  796. drupal_set_message(t('No corrupt records to fix, this is good news :-)'));
  797. }
  798. // Fix the faulty records.
  799. else {
  800. foreach ($records as $record) {
  801. // Extract the data and get the first element of the array, this should be
  802. // valid data.
  803. $record->data = reset(unserialize($record->data));
  804. // Update the record.
  805. drupal_write_record('metatag', $record, array('entity_type', 'entity_id', 'language'));
  806. }
  807. drupal_set_message(t('Fixed @count corrupt meta tag record(s).', array('@count' => $records->rowCount())));
  808. }
  809. }
  810. /**
  811. * Add the revision_id from the entity into metatag schema, adjust the primary
  812. * keys accordingly.
  813. */
  814. function metatag_update_7015() {
  815. // Add the new field.
  816. db_add_field('metatag', 'revision_id', array(
  817. 'type' => 'int',
  818. 'unsigned' => TRUE,
  819. 'not null' => FALSE,
  820. 'default' => 0,
  821. 'description' => 'The revision_id for the entity object this data is attached to.',
  822. ));
  823. // Remove the existing primary key. This may take some work so it can be
  824. // database agnostic, i.e. some databases will not like it.
  825. db_drop_primary_key('metatag');
  826. // Add the new primary key.
  827. db_add_primary_key('metatag', array('entity_type', 'entity_id', 'revision_id', 'language'));
  828. drupal_set_message(t('Added the {metatag}.revision_id field.'));
  829. }