title.test 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. <?php
  2. /**
  3. * @file
  4. * Tests for the Title module.
  5. */
  6. /**
  7. * Tests for legacy field replacement.
  8. */
  9. class TitleFieldReplacementTestCase extends DrupalWebTestCase {
  10. public static function getInfo() {
  11. return array(
  12. 'name' => 'Field replacement',
  13. 'description' => 'Test field replacement.',
  14. 'group' => 'Title',
  15. );
  16. }
  17. /**
  18. * {@inheritdoc}
  19. */
  20. protected function setUp() {
  21. parent::setUp('entity', 'field_test', 'title', 'title_test');
  22. }
  23. /**
  24. * Test field replacement API and workflow.
  25. */
  26. public function testFieldReplacementWorkflow() {
  27. $info = entity_get_info('test_entity');
  28. $label_key = $info['entity keys']['label'];
  29. $field_name = $label_key . '_field';
  30. // Enable field replacement for the test entity.
  31. title_field_replacement_toggle('test_entity', 'test_bundle', $label_key);
  32. $i = 0;
  33. $entity = field_test_create_stub_entity(FALSE, FALSE);
  34. while ($i++ <= 1) {
  35. // The first time the entity gets created the second time gets updated.
  36. title_test_entity_save($entity);
  37. // Check that the replacing field value has been synchronized on save.
  38. $query = db_select('test_entity', 'te');
  39. $query->addJoin('INNER', 'field_data_' . $field_name, 'f', 'te.ftid = f.entity_id');
  40. $record = $query
  41. ->fields('te')
  42. ->fields('f')
  43. ->condition('ftid', $entity->ftid)
  44. ->execute()
  45. ->fetch();
  46. $phase = $entity->is_new ? 'insert' : 'update';
  47. $this->assertIdentical($record->{$label_key}, $record->{$field_name . '_value'}, t('Field synchronization is correctly performed on %phase.', array('%phase' => $phase)));
  48. unset($entity->is_new);
  49. }
  50. // Store a dummy value in the legacy field.
  51. while (($label = $this->randomName()) == $entity->{$label_key});
  52. db_update('test_entity')
  53. ->fields(array($label_key => $label))
  54. ->execute();
  55. $record = db_select('test_entity', 'te')
  56. ->fields('te')
  57. ->condition('ftid', $entity->ftid)
  58. ->execute()
  59. ->fetch();
  60. $this->assertNotIdentical($record->{$label_key}, $entity->{$label_key}, t('Entity label has been changed.'));
  61. // Clear field cache so synchronization can be performed on field attach
  62. // load.
  63. cache_clear_all('*', 'cache_field');
  64. drupal_static_reset();
  65. // Check that the replacing field value is correctly synchronized on load
  66. // and view.
  67. $entity = title_test_entity_test_load($entity);
  68. title_test_phase_check('after_load', $entity);
  69. entity_view('test_entity', array($entity->ftid => $entity));
  70. foreach (title_test_phase_store() as $phase => $value) {
  71. $this->assertTrue($value, t('Field synchronization is correctly performed on %phase.', array('%phase' => $phase)));
  72. }
  73. // Change the value stored into the label field to check entity_label().
  74. if (isset($info['label callback'])) {
  75. $label = $this->randomName();
  76. $entity->{$field_name}[LANGUAGE_NONE][0]['value'] = $label;
  77. $this->assertIdentical(entity_label('test_entity', $entity), $label, t('entity_label() returns the expected value.'));
  78. }
  79. }
  80. /**
  81. * Test field replacement UI.
  82. */
  83. public function testFieldReplacementUI() {
  84. $permissions = array(
  85. 'access administration pages',
  86. 'view the administration theme',
  87. 'administer content types',
  88. 'administer taxonomy',
  89. 'administer comments',
  90. 'administer fields',
  91. );
  92. $admin_user = $this->drupalCreateUser($permissions);
  93. $this->drupalLogin($admin_user);
  94. foreach (entity_get_info() as $entity_type => $entity_info) {
  95. if (!empty($entity_info['field replacement'])) {
  96. foreach ($entity_info['bundles'] as $bundle => $bundle_info) {
  97. if (isset($bundle_info['admin']['path'])) {
  98. $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle) . '/fields';
  99. foreach ($entity_info['field replacement'] as $legacy_field => $info) {
  100. $path = $admin_path . '/replace/' . $legacy_field;
  101. $xpath = '//a[@href=:url and text()=:label]';
  102. $args = array(':url' => url($path), ':label' => t('replace'));
  103. $targs = array('%legacy_field' => $legacy_field, '%entity_type' => $entity_type, '%bundle' => $bundle);
  104. $field_name = $info['field']['field_name'];
  105. // Check that the current legacy field has a "replace" operation.
  106. $this->drupalGet($admin_path);
  107. $link = $this->xpath($xpath, $args);
  108. $this->assertEqual(count($link), 1, t('Replace link found for the field %legacy_field of the bundle %bundle of the entity %entity_type.', $targs));
  109. // Check that the legacy field has correctly been replaced through
  110. // field replacement UI.
  111. $this->drupalPost($path, array('enabled' => TRUE), t('Save settings'));
  112. _field_info_collate_fields(TRUE);
  113. $link = $this->xpath($xpath, $args);
  114. $this->assertTrue(empty($link) && title_field_replacement_enabled($entity_type, $bundle, $legacy_field), t('%legacy_field successfully replaced for the bundle %bundle of the entity %entity_type.', $targs));
  115. // Check that the enabled status cannot be changed unless the
  116. // field instance is removed.
  117. $this->drupalGet($path);
  118. $this->assertFieldByXPath('//form//input[@name="enabled" and @checked="checked" and @disabled="disabled"]', NULL, t('Field replacement for %legacy_field cannot be disabled unless the replacing field instance is deleted.', array('%legacy_field' => $legacy_field)));
  119. $this->drupalPost($path, array(), t('Save settings'));
  120. _field_info_collate_fields(TRUE);
  121. $this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, $legacy_field), t('Submitting the form does not alter field replacement settings.'));
  122. // Delete the field instance and check that the "replace"
  123. // operation is available again.
  124. $this->drupalPost($admin_path . '/' . $field_name . '/delete', array(), t('Delete'));
  125. $link = $this->xpath($xpath, $args);
  126. $this->assertEqual(count($link), 1, t('Replace link found for the field %legacy_field of the bundle %bundle of the entity %entity_type.', $targs));
  127. // Check that field replacement can be enabled again.
  128. $this->drupalGet($path);
  129. $this->assertFieldByXPath('//form//input[@name="enabled" and not(@checked) and not(@disabled)]', NULL, t('Field replacement for %legacy_field cannot be disabled unless the replacing field instance is deleted.', array('%legacy_field' => $legacy_field)));
  130. }
  131. }
  132. }
  133. }
  134. }
  135. }
  136. }
  137. /**
  138. * Tests for legacy field replacement.
  139. */
  140. class TitleAdminSettingsTestCase extends DrupalWebTestCase {
  141. public static function getInfo() {
  142. return array(
  143. 'name' => 'Admin settings',
  144. 'description' => 'Test the administration settings.',
  145. 'group' => 'Title',
  146. );
  147. }
  148. /**
  149. * {@inheritdoc}
  150. */
  151. protected function setUp() {
  152. parent::setUp('field_test', 'title', 'title_test');
  153. $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer taxonomy'));
  154. $this->drupalLogin($admin_user);
  155. }
  156. /**
  157. * Check for automated title_field attachment.
  158. */
  159. public function testAutomatedFieldAttachment() {
  160. $this->doTestAutomatedFieldAttachment(TRUE);
  161. $this->doTestAutomatedFieldAttachment(FALSE);
  162. }
  163. /**
  164. * Check that the fields are replaced or skipped depending on the given value.
  165. *
  166. * @param bool $enabled
  167. * Whether replacement is enabled or not.
  168. */
  169. public function doTestAutomatedFieldAttachment($enabled) {
  170. $edit = array(
  171. 'title_taxonomy_term[auto_attach][name]' => $enabled,
  172. 'title_taxonomy_term[auto_attach][description]' => $enabled,
  173. );
  174. $this->drupalPost('admin/config/content/title', $edit, t('Save configuration'));
  175. $edit = array(
  176. 'name' => $this->randomName(),
  177. 'machine_name' => drupal_strtolower($this->randomName()),
  178. 'description' => $this->randomString(16),
  179. );
  180. $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
  181. $entity_type = 'taxonomy_term';
  182. $bundle = $edit['machine_name'];
  183. field_info_cache_clear();
  184. $this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, 'name') == $enabled, 'Name field correctly processed.');
  185. $this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, 'description') == $enabled, 'Description field correctly processed.');
  186. }
  187. }
  188. /**
  189. * Tests for legacy field replacement.
  190. */
  191. class TitleTranslationTestCase extends DrupalWebTestCase {
  192. public static function getInfo() {
  193. return array(
  194. 'name' => 'Replaced fields translation',
  195. 'description' => 'Test replaced field translation.',
  196. 'group' => 'Title',
  197. );
  198. }
  199. /**
  200. * {@inheritdoc}
  201. */
  202. protected function setUp() {
  203. parent::setUp('locale', 'entity_translation', 'title', 'field_test', 'title_test');
  204. // Create a power user.
  205. $permissions = array(
  206. 'administer modules',
  207. 'view the administration theme',
  208. 'administer languages',
  209. 'administer taxonomy',
  210. 'administer entity translation',
  211. 'translate any entity',
  212. );
  213. $admin_user = $this->drupalCreateUser($permissions);
  214. $this->drupalLogin($admin_user);
  215. // Enable a translation language.
  216. $edit = array('langcode' => 'it');
  217. $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
  218. $this->assertTrue(drupal_multilingual(), t('Italian language installed.'));
  219. // Enable URL language negotiation.
  220. $name = 'language_content[enabled][locale-url]';
  221. $edit = array($name => 1);
  222. $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
  223. $this->assertFieldByName($name, 1, t('URL language negotiation enabled.'));
  224. // Enable taxonomy translation.
  225. $name = 'entity_translation_entity_types[taxonomy_term]';
  226. $edit = array($name => 1);
  227. $this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
  228. $this->assertFieldByName($name, 'taxonomy_term', t('Taxonomy translation enabled.'));
  229. // Create a new vocabulary.
  230. $name = drupal_strtolower($this->randomName());
  231. $edit = array(
  232. 'name' => $this->randomString(),
  233. 'machine_name' => $name,
  234. 'entity_translation_taxonomy' => 1,
  235. );
  236. $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
  237. $this->vocabulary = taxonomy_vocabulary_machine_name_load($name);
  238. $this->assertTrue($this->vocabulary, t('Vocabulary created.'));
  239. // Replace both taxonomy term legacy fields.
  240. $entity_type = 'taxonomy_term';
  241. foreach (title_field_replacement_info($entity_type) as $legacy_field => $info) {
  242. title_field_replacement_toggle($entity_type, $name, $legacy_field);
  243. $t_args = array('%legacy_field' => $legacy_field);
  244. $this->assertTrue(field_info_instance($entity_type, $info['field']['field_name'], $name), t('The %legacy_field field has been correctly replaced.', $t_args));
  245. }
  246. // Ensure static caches do not interfere with API calls.
  247. drupal_static_reset();
  248. }
  249. /**
  250. * Tests taxonomy programmatic translation workflow.
  251. */
  252. public function testProgrammaticTranslationWorkflow() {
  253. // Create a taxonomy term and assign it an original language different from
  254. // the default language.
  255. $langcode = 'it';
  256. $original_values = array(
  257. 'name' => $langcode . '_' . $this->randomName(),
  258. 'description' => $langcode . '_' . $this->randomName(),
  259. );
  260. $term = (object) ($original_values + array(
  261. 'format' => 'filtered_html',
  262. 'vocabulary_machine_name' => $this->vocabulary->machine_name,
  263. 'vid' => $this->vocabulary->vid,
  264. ));
  265. entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage($langcode);
  266. taxonomy_term_save($term);
  267. $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
  268. $term = $this->termLoad($term->tid);
  269. $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), 'Replacing field values correctly created from the legacy field values.');
  270. // Pollute synchronization cache to ensure the expected values are stored
  271. // anyway.
  272. title_entity_sync('taxonomy_term', $term, $langcode);
  273. // Create a translation using the default language.
  274. $translation_langcode = language_default()->language;
  275. $translated_values = array(
  276. 'name' => $translation_langcode . '_' . $this->randomName(),
  277. 'description' => $translation_langcode . '_' . $this->randomName(),
  278. );
  279. foreach ($translated_values as $name => $value) {
  280. $term->{$name} = $value;
  281. }
  282. $translation = array(
  283. 'language' => $translation_langcode,
  284. 'source' => $langcode,
  285. 'uid' => $this->loggedInUser->uid,
  286. 'status' => 1,
  287. 'translate' => 0,
  288. 'created' => REQUEST_TIME,
  289. 'changed' => REQUEST_TIME,
  290. );
  291. entity_translation_get_handler('taxonomy_term', $term)->setTranslation($translation);
  292. taxonomy_term_save($term);
  293. $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
  294. $term = $this->termLoad($term->tid, $translation_langcode);
  295. $this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode), 'Replacing field translations correctly created.');
  296. $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode, FALSE), 'Replacing field original values correctly preserved.');
  297. // Delete the translation.
  298. entity_translation_get_handler('taxonomy_term', $term)->removeTranslation($translation_langcode);
  299. taxonomy_term_save($term);
  300. $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
  301. $term = $this->termLoad($term->tid, $langcode);
  302. $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), 'Replacing field translations correctly deleted.');
  303. // Make the term language neutral.
  304. entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage(LANGUAGE_NONE);
  305. foreach ($original_values as $name => $value) {
  306. $field_name = $name . '_field';
  307. $term->{$field_name}[LANGUAGE_NONE] = $term->{$field_name}[$langcode];
  308. $term->{$field_name}[$langcode] = array();
  309. }
  310. taxonomy_term_save($term);
  311. $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
  312. $term = $this->termLoad($term->tid);
  313. $this->assertTrue($this->checkFieldValues($term, $original_values, LANGUAGE_NONE), 'Term original language correctly changed to the former translation language.');
  314. // Change the term language to the former translation language.
  315. entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage($translation_langcode);
  316. foreach ($original_values as $name => $value) {
  317. $field_name = $name . '_field';
  318. $term->{$field_name}[$translation_langcode] = $term->{$field_name}[LANGUAGE_NONE];
  319. $term->{$field_name}[LANGUAGE_NONE] = array();
  320. }
  321. taxonomy_term_save($term);
  322. $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
  323. $term = $this->termLoad($term->tid, $translation_langcode);
  324. $this->assertTrue($this->checkFieldValues($term, $original_values, $translation_langcode), 'Term original language correctly changed to language neutral.');
  325. // Make a replacing field untranslatable and change its value.
  326. $field_name = 'name_field';
  327. $field = field_info_field($field_name);
  328. $field['translatable'] = FALSE;
  329. field_update_field($field);
  330. $original_values['name'] = LANGUAGE_NONE . '_' . $this->randomName();
  331. $term->name = $original_values['name'];
  332. taxonomy_term_save($term);
  333. $this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
  334. $term = $this->termLoad($term->tid);
  335. $this->assertEqual($term->{$field_name}[LANGUAGE_NONE][0]['value'], $original_values['name'], 'Untranslatable replacing field on translatable entity correctly handled.');
  336. }
  337. /**
  338. * Tests taxonomy form translation workflow.
  339. */
  340. public function testFormTranslationWorkflow() {
  341. // Create a taxonomy term and check that legacy fields are properly
  342. // populated.
  343. $original_values = array(
  344. 'name' => $this->randomName(),
  345. 'description' => $this->randomName(),
  346. );
  347. $langcode = 'en';
  348. $edit = $this->editValues($original_values, $langcode);
  349. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', $edit, t('Save'));
  350. $term = current(entity_load('taxonomy_term', FALSE, array('name' => $original_values['name']), TRUE));
  351. $this->assertEqual($term->description, $original_values['description'], t('Taxonomy term created.'));
  352. // Translate the taxonomy term and check that both the original values and
  353. // the translations were correctly stored.
  354. $translated_values = array(
  355. 'name' => $this->randomName(),
  356. 'description' => $this->randomName(),
  357. );
  358. $translation_langcode = 'it';
  359. $edit = $this->editValues($translated_values, 'it');
  360. $this->drupalPost($translation_langcode . '/taxonomy/term/' . $term->tid . '/edit/add/' . $langcode . '/' . $translation_langcode, $edit, t('Save'));
  361. $term = $this->termLoad($term->tid);
  362. $this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode, FALSE), t('Taxonomy term translation created.'));
  363. $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), t('Taxonomy term original values preserved.'));
  364. // Check that legacy fields have the correct values.
  365. $this->assertEqual($term->name, $original_values['name'], t('Taxonomy term name correctly stored.'));
  366. $this->assertEqual($term->description, $original_values['description'], t('Taxonomy term description correctly stored.'));
  367. // Updated the taxonomy term translation and check that both the original
  368. // values and the translations were correctly stored.
  369. $translated_values = array(
  370. 'name' => $this->randomName(),
  371. 'description' => $this->randomName(),
  372. );
  373. $edit = $this->editValues($translated_values, $translation_langcode);
  374. $this->drupalPost($translation_langcode . '/taxonomy/term/' . $term->tid . '/edit/' . $translation_langcode, $edit, t('Save'));
  375. $term = $this->termLoad($term->tid);
  376. $this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode, FALSE), t('Taxonomy term translation updated.'));
  377. $this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), t('Taxonomy term original values preserved.'));
  378. // Check that legacy fields have the correct values.
  379. $this->assertEqual($term->name, $original_values['name'], t('Taxonomy term name correctly stored.'));
  380. $this->assertEqual($term->description, $original_values['description'], t('Taxonomy term description correctly stored.'));
  381. }
  382. /**
  383. * Loads a term using the given language as active language.
  384. *
  385. * @param int $tid
  386. * The term identifier.
  387. * @param string|null $langcode
  388. * (optional) The active language to be set. Defaults to none.
  389. *
  390. * @return object|bool
  391. * A term object.
  392. */
  393. protected function termLoad($tid, $langcode = NULL) {
  394. drupal_static_reset();
  395. title_active_language($langcode);
  396. return current(entity_load('taxonomy_term', array($tid), array(), TRUE));
  397. }
  398. /**
  399. * Returns the drupalPost() $edit array corresponding to the given values.
  400. */
  401. protected function editValues($values, $langcode) {
  402. $edit = array();
  403. foreach ($values as $name => $value) {
  404. $edit["{$name}_field[{$langcode}][0][value]"] = $value;
  405. }
  406. return $edit;
  407. }
  408. /**
  409. * Checks that the field values and optionally the legacy ones match the given values.
  410. */
  411. protected function checkFieldValues($term, $values, $langcode, $legacy_match = TRUE) {
  412. foreach ($values as $name => $value) {
  413. $field_value = $term->{$name . '_field'}[$langcode][0]['value'];
  414. if ($field_value != $value || ($legacy_match !== ($field_value == $term->{$name}))) {
  415. return FALSE;
  416. }
  417. }
  418. return TRUE;
  419. }
  420. /**
  421. * Checks that the legacy field values stored in the database match the given values.
  422. */
  423. protected function checkLegacyValues($term, $values) {
  424. $record = db_query('SELECT * FROM {taxonomy_term_data} t WHERE t.tid = :tid', array(':tid' => $term->tid))->fetchAssoc();
  425. foreach ($values as $name => $value) {
  426. if ($record[$name] != $value) {
  427. return FALSE;
  428. }
  429. }
  430. return TRUE;
  431. }
  432. }