entity_translation.install 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. <?php
  2. /**
  3. * @file
  4. * Installation functions for Entity Translation module.
  5. */
  6. /**
  7. * Implements hook_schema().
  8. */
  9. function entity_translation_schema() {
  10. $schema = array();
  11. $schema['entity_translation'] = array(
  12. 'description' => 'Table to track entity translations',
  13. 'fields' => array(
  14. 'entity_type' => array(
  15. 'type' => 'varchar',
  16. 'length' => 128,
  17. 'not null' => TRUE,
  18. 'default' => '',
  19. 'description' => 'The entity type this translation relates to',
  20. ),
  21. 'entity_id' => array(
  22. 'type' => 'int',
  23. 'unsigned' => TRUE,
  24. 'not null' => TRUE,
  25. 'description' => 'The entity id this translation relates to',
  26. ),
  27. 'revision_id' => array(
  28. 'type' => 'int',
  29. 'unsigned' => TRUE,
  30. 'not null' => TRUE,
  31. 'description' => 'The entity revision id this translation relates to',
  32. ),
  33. 'language' => array(
  34. 'type' => 'varchar',
  35. 'length' => 32,
  36. 'not null' => TRUE,
  37. 'default' => '',
  38. 'description' => 'The target language for this translation.',
  39. ),
  40. 'source' => array(
  41. 'type' => 'varchar',
  42. 'length' => 32,
  43. 'not null' => TRUE,
  44. 'default' => '',
  45. 'description' => 'The source language from which this translation was created.',
  46. ),
  47. 'uid' => array(
  48. 'description' => 'The author of this translation.',
  49. 'type' => 'int',
  50. 'not null' => TRUE,
  51. 'default' => 0,
  52. ),
  53. 'status' => array(
  54. 'description' => 'Boolean indicating whether the translation is published (visible to non-administrators).',
  55. 'type' => 'int',
  56. 'not null' => TRUE,
  57. 'default' => 1,
  58. ),
  59. 'translate' => array(
  60. 'description' => 'A boolean indicating whether this translation needs to be updated.',
  61. 'type' => 'int',
  62. 'not null' => TRUE,
  63. 'default' => 0,
  64. ),
  65. 'created' => array(
  66. 'description' => 'The Unix timestamp when the translation was created.',
  67. 'type' => 'int',
  68. 'not null' => TRUE,
  69. 'default' => 0,
  70. ),
  71. 'changed' => array(
  72. 'description' => 'The Unix timestamp when the translation was most recently saved.',
  73. 'type' => 'int',
  74. 'not null' => TRUE,
  75. 'default' => 0,
  76. ),
  77. ),
  78. 'primary key' => array('entity_type', 'entity_id', 'language'),
  79. );
  80. $schema['entity_translation_revision'] = array(
  81. 'description' => 'Table to track entity translation revisions',
  82. 'fields' => array(
  83. 'entity_type' => array(
  84. 'type' => 'varchar',
  85. 'length' => 128,
  86. 'not null' => TRUE,
  87. 'default' => '',
  88. 'description' => 'The entity type this translation revision relates to',
  89. ),
  90. 'entity_id' => array(
  91. 'type' => 'int',
  92. 'unsigned' => TRUE,
  93. 'not null' => TRUE,
  94. 'description' => 'The entity id this translation revision relates to',
  95. ),
  96. 'revision_id' => array(
  97. 'type' => 'int',
  98. 'unsigned' => TRUE,
  99. 'not null' => TRUE,
  100. 'description' => 'The entity revision id this translation relates to',
  101. ),
  102. 'language' => array(
  103. 'type' => 'varchar',
  104. 'length' => 32,
  105. 'not null' => TRUE,
  106. 'default' => '',
  107. 'description' => 'The target language for this translation revision.',
  108. ),
  109. 'source' => array(
  110. 'type' => 'varchar',
  111. 'length' => 32,
  112. 'not null' => TRUE,
  113. 'default' => '',
  114. 'description' => 'The source language from which this translation revision was created.',
  115. ),
  116. 'uid' => array(
  117. 'description' => 'The author of this translation revision.',
  118. 'type' => 'int',
  119. 'not null' => TRUE,
  120. 'default' => 0,
  121. ),
  122. 'status' => array(
  123. 'description' => 'Boolean indicating whether the translation revision is published (visible to non-administrators).',
  124. 'type' => 'int',
  125. 'not null' => TRUE,
  126. 'default' => 1,
  127. ),
  128. 'translate' => array(
  129. 'description' => 'A boolean indicating whether this translation revision needs to be updated.',
  130. 'type' => 'int',
  131. 'not null' => TRUE,
  132. 'default' => 0,
  133. ),
  134. 'created' => array(
  135. 'description' => 'The Unix timestamp when the translation revision was created.',
  136. 'type' => 'int',
  137. 'not null' => TRUE,
  138. 'default' => 0,
  139. ),
  140. 'changed' => array(
  141. 'description' => 'The Unix timestamp when the translation revision was most recently saved.',
  142. 'type' => 'int',
  143. 'not null' => TRUE,
  144. 'default' => 0,
  145. ),
  146. ),
  147. 'primary key' => array('entity_type', 'revision_id', 'language'),
  148. 'indexes'=> array('revision_id' => array('revision_id')),
  149. );
  150. return $schema;
  151. }
  152. /**
  153. * Implements hook_install().
  154. */
  155. function entity_translation_install() {
  156. // entity_translation_form_alter() needs to run after locale_form_alter() and
  157. // translation_menu(); entity_translation_menu_alter() needs to run after
  158. // i18n_node_menu_alter().
  159. db_update('system')
  160. ->fields(array('weight' => 11))
  161. ->condition('name', 'entity_translation')
  162. ->execute();
  163. // Enable translation for nodes.
  164. variable_set('entity_translation_entity_types', array('node' => 'node'));
  165. // Make translation use the content language type.
  166. variable_set('translation_language_type', LANGUAGE_TYPE_CONTENT);
  167. // Enable revision support for entity translation.
  168. variable_set('entity_translation_revision_enabled', TRUE);
  169. // Enable taxonomy autocomplete support.
  170. variable_set('entity_translation_taxonomy_autocomplete', TRUE);
  171. }
  172. /**
  173. * Grant 'edit $type original values' permission to existing roles.
  174. */
  175. function _entity_translation_grant_edit_permissions() {
  176. variable_set('entity_translation_workflow_enabled', TRUE);
  177. $permissions = array();
  178. // Nodes.
  179. $permissions['node'][] = 'bypass node access';
  180. foreach (node_permissions_get_configured_types() as $type) {
  181. $permissions['node'][] = "edit own $type content";
  182. $permissions['node'][] = "edit any $type content";
  183. }
  184. // Comments.
  185. if (module_exists('comment')) {
  186. $permissions['comment'][] = 'administer comments';
  187. $permissions['comment'][] = 'edit own comments';
  188. }
  189. // Taxonomy terms.
  190. if (module_exists('taxonomy')) {
  191. $permissions['taxonomy_term'][] = 'administer taxonomy';
  192. foreach (taxonomy_get_vocabularies() as $vocabulary) {
  193. $permissions['taxonomy_term'][] = "edit terms in {$vocabulary->vid}";
  194. }
  195. }
  196. $assignments = array();
  197. foreach ($permissions as $entity_type => $permissions_filter) {
  198. if (entity_translation_enabled($entity_type)) {
  199. $permission = "edit $entity_type original values";
  200. $assignments[] = _entity_translation_grant_permission($permission, $permissions_filter);
  201. $permission = "edit $entity_type translation shared fields";
  202. $assignments[] = _entity_translation_grant_permission($permission, $permissions_filter);
  203. }
  204. }
  205. $assignments = '<ul><li>' . implode('</li><li>', $assignments) . '</li></ul>';
  206. $t = get_t();
  207. return $t('The following permissions have been assigned to existing roles: !assignments', array('!assignments' => $assignments));
  208. }
  209. /**
  210. * Grant the given permission to all roles which already have any of the
  211. * permissions specified in the $permissions_filter parameter.
  212. *
  213. * @param $permission
  214. * The new permission which to grant.
  215. * @param $permissions_filter
  216. * List of permissions used for loading roles.
  217. *
  218. * @return
  219. * A message describing permission changes.
  220. */
  221. function _entity_translation_grant_permission($permission, $permissions_filter = NULL) {
  222. $roles = user_roles(FALSE, $permissions_filter);
  223. foreach ($roles as $rid => $role) {
  224. user_role_grant_permissions($rid, array($permission));
  225. }
  226. $t = get_t();
  227. return $t('%permission was assigned to %roles', array(
  228. '%permission' => $permission,
  229. '%roles' => implode(', ', $roles)
  230. ));
  231. }
  232. /**
  233. * Implements hook_enable().
  234. */
  235. function entity_translation_enable() {
  236. // Re-activate entity translation for content types which had used it when
  237. // the module was last disabled (if any), unless these have since been altered
  238. // by the user to use a different translation option.
  239. $entity_translation_types = variable_get('entity_translation_disabled_content_types', array());
  240. foreach ($entity_translation_types as $index => $type) {
  241. if (variable_get("language_content_type_$type", 0) == 0) {
  242. variable_set("language_content_type_$type", ENTITY_TRANSLATION_ENABLED);
  243. }
  244. // We should show the warning only if we actually restored at least one
  245. // content type.
  246. else {
  247. unset($entity_translation_types[$index]);
  248. }
  249. }
  250. if ($entity_translation_types) {
  251. drupal_set_message(t('All content types previously configured to use field translation are now using it again.'), 'warning');
  252. }
  253. variable_del('entity_translation_disabled_content_types');
  254. }
  255. /**
  256. * Implements hook_disable().
  257. */
  258. function entity_translation_disable() {
  259. // Store record of which types are using entity translation, and set those
  260. // types to not be translated. These content types will be reset to use entity
  261. // translation again if the module is later re-enabled, unless they have been
  262. // changed by the user in the meantime.
  263. $entity_translation_types = array();
  264. foreach (node_type_get_types() as $type => $object) {
  265. if (variable_get("language_content_type_$type", 0) == ENTITY_TRANSLATION_ENABLED) {
  266. $entity_translation_types[] = $type;
  267. variable_set("language_content_type_$type", 0);
  268. }
  269. }
  270. if ($entity_translation_types) {
  271. variable_set('entity_translation_disabled_content_types', $entity_translation_types);
  272. drupal_set_message(t('All content types configured to use field translation now have multilingual support disabled. This change will be reverted if the entity translation module is enabled again.'), 'warning');
  273. }
  274. }
  275. /**
  276. * Implements hook_uninstall().
  277. */
  278. function entity_translation_uninstall() {
  279. db_delete('variable')
  280. ->condition('name', db_like('entity_translation_') . '%', 'LIKE')
  281. ->execute();
  282. variable_del('translation_language_type');
  283. variable_del('locale_field_language_fallback');
  284. }
  285. /**
  286. * Implements hook_update_N().
  287. */
  288. function entity_translation_update_7001() {
  289. db_update('system')
  290. ->fields(array('weight' => 11))
  291. ->condition('name', 'entity_translation')
  292. ->execute();
  293. }
  294. /**
  295. * Grant 'edit original values' and 'edit shared field' permissions to roles which have entity editing permissions.
  296. */
  297. function entity_translation_update_7002() {
  298. // Grant the 'edit original values' permission, so we don't break editing on
  299. // existing sites.
  300. return _entity_translation_grant_edit_permissions();
  301. }
  302. /**
  303. * Configure node and comment language settings to the prior default behavior.
  304. */
  305. function entity_translation_update_7003() {
  306. module_load_include('inc', 'entity_translation', 'entity_translation.admin');
  307. foreach (array_keys(entity_get_info()) as $entity_type) {
  308. entity_translation_settings_init($entity_type);
  309. }
  310. }
  311. /**
  312. * Rebuild entity information to update the path scheme settings.
  313. */
  314. function entity_translation_update_7004() {
  315. entity_info_cache_clear();
  316. }
  317. /**
  318. * Rebuild the class registry to pick up the translation handler factory class.
  319. */
  320. function entity_translation_update_7005() {
  321. registry_rebuild();
  322. }
  323. /**
  324. * Add revision schema for entity translation metadata.
  325. */
  326. function entity_translation_update_7006() {
  327. // Create revision id column.
  328. $spec = array(
  329. 'type' => 'int',
  330. 'unsigned' => TRUE,
  331. // If we have existing data we cannot enforce this to be NOT NULL.
  332. 'not null' => FALSE,
  333. 'description' => 'The entity revision id this translation relates to',
  334. );
  335. // Create revision id column if it doesn't exist already.
  336. if (!db_field_exists('entity_translation', 'revision_id')) {
  337. db_add_field('entity_translation', 'revision_id', $spec);
  338. }
  339. // Create the entity translation revision schema.
  340. $table = array(
  341. 'description' => 'Table to track entity translation revisions',
  342. 'fields' => array(
  343. 'entity_type' => array(
  344. 'type' => 'varchar',
  345. 'length' => 128,
  346. 'not null' => TRUE,
  347. 'default' => '',
  348. 'description' => 'The entity type this translation revision relates to',
  349. ),
  350. 'entity_id' => array(
  351. 'type' => 'int',
  352. 'unsigned' => TRUE,
  353. 'not null' => TRUE,
  354. 'description' => 'The entity id this translation revision relates to',
  355. ),
  356. 'revision_id' => array(
  357. 'type' => 'int',
  358. 'unsigned' => TRUE,
  359. 'not null' => TRUE,
  360. 'description' => 'The entity revision id this translation relates to',
  361. ),
  362. 'language' => array(
  363. 'type' => 'varchar',
  364. 'length' => 32,
  365. 'not null' => TRUE,
  366. 'default' => '',
  367. 'description' => 'The target language for this translation revision.',
  368. ),
  369. 'source' => array(
  370. 'type' => 'varchar',
  371. 'length' => 32,
  372. 'not null' => TRUE,
  373. 'default' => '',
  374. 'description' => 'The source language from which this translation revision was created.',
  375. ),
  376. 'uid' => array(
  377. 'description' => 'The author of this translation revision.',
  378. 'type' => 'int',
  379. 'not null' => TRUE,
  380. 'default' => 0,
  381. ),
  382. 'status' => array(
  383. 'description' => 'Boolean indicating whether the translation revision is published (visible to non-administrators).',
  384. 'type' => 'int',
  385. 'not null' => TRUE,
  386. 'default' => 1,
  387. ),
  388. 'translate' => array(
  389. 'description' => 'A boolean indicating whether this translation revision needs to be updated.',
  390. 'type' => 'int',
  391. 'not null' => TRUE,
  392. 'default' => 0,
  393. ),
  394. 'created' => array(
  395. 'description' => 'The Unix timestamp when the translation revision was created.',
  396. 'type' => 'int',
  397. 'not null' => TRUE,
  398. 'default' => 0,
  399. ),
  400. 'changed' => array(
  401. 'description' => 'The Unix timestamp when the translation revision was most recently saved.',
  402. 'type' => 'int',
  403. 'not null' => TRUE,
  404. 'default' => 0,
  405. ),
  406. ),
  407. 'primary key' => array('entity_type', 'revision_id', 'language'),
  408. 'indexes'=> array('revision_id' => array('revision_id')),
  409. );
  410. // Create entity translation revision table if it doesn't exist already.
  411. if (!db_table_exists('entity_translation_revision')) {
  412. db_create_table('entity_translation_revision', $table);
  413. }
  414. }
  415. /**
  416. * Disable revision support on existing installations.
  417. */
  418. function entity_translation_update_7007() {
  419. // Revision support is not enabled by default on existing installations as
  420. // making it work implies copying translation metadata to the
  421. // {entity_translation_revision} table for all the existing translations.
  422. // Since this process cannot be reliably implemented in an update function,
  423. // we leave the choice of manually performing the upgrade to people with the
  424. // required skills to do so. Be aware that enabling revision support on sites
  425. // where data has not been manually migrated may cause translation metadata to
  426. // be permanently lost or corrupted. See https://www.drupal.org/node/2396103.
  427. variable_set('entity_translation_revision_enabled', FALSE);
  428. }
  429. /**
  430. * Enable Entity Translation for taxonomy vocabularies having terms translated
  431. * through it.
  432. */
  433. function entity_translation_update_7008() {
  434. if (!module_exists('taxonomy')) {
  435. return;
  436. }
  437. // According to EXPLAIN joining {taxonomy_vocabulary} here makes the query
  438. // perform way worse, so we just split into two quick ones.
  439. $query = "SELECT t.vid
  440. FROM {entity_translation} et
  441. JOIN {taxonomy_term_data} t ON et.entity_type = 'taxonomy_term' AND et.entity_id = t.tid AND et.source != ''
  442. GROUP BY t.vid";
  443. $vids = db_query($query)->fetchCol();
  444. if ($vids) {
  445. $query = "SELECT v.machine_name FROM {taxonomy_vocabulary} v WHERE v.vid IN (:vids)";
  446. $names = db_query($query, array(':vids' => $vids))->fetchCol();
  447. $info = variable_get('entity_translation_taxonomy', array());
  448. foreach ($names as $name) {
  449. $info[$name] = TRUE;
  450. }
  451. variable_set('entity_translation_taxonomy', $info);
  452. }
  453. }
  454. /**
  455. * Make sure "i18n_mode" is correctly set for vocabularies having entity
  456. * translation enabled.
  457. */
  458. function entity_translation_update_7009() {
  459. $info = array_filter(variable_get('entity_translation_taxonomy', array()));
  460. if ($info && module_exists('i18n_taxonomy')) {
  461. $query = "UPDATE {taxonomy_vocabulary} SET i18n_mode = :et_mode WHERE machine_name IN (:names)";
  462. $names = array_keys(array_filter($info));
  463. db_query($query, array(':et_mode' => 32768, ':names' => $names));
  464. }
  465. }