field_collection.test 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271
  1. <?php
  2. /**
  3. * @file
  4. * field_collections tests.
  5. */
  6. /**
  7. * Test basics.
  8. */
  9. class FieldCollectionBasicTestCase extends DrupalWebTestCase {
  10. public static function getInfo() {
  11. return array(
  12. 'name' => 'Field collection',
  13. 'description' => 'Tests creating and using field collections.',
  14. 'group' => 'Field types',
  15. );
  16. }
  17. function setUp() {
  18. parent::setUp('field_collection', 'entity_crud_hook_test');
  19. // Create a field_collection field to use for the tests.
  20. $this->field_name = 'field_test_collection';
  21. $this->field = array('field_name' => $this->field_name, 'type' => 'field_collection', 'cardinality' => 4);
  22. $this->field = field_create_field($this->field);
  23. $this->field_id = $this->field['id'];
  24. $this->instance = array(
  25. 'field_name' => $this->field_name,
  26. 'entity_type' => 'node',
  27. 'bundle' => 'article',
  28. 'label' => $this->randomName() . '_label',
  29. 'description' => $this->randomName() . '_description',
  30. 'weight' => mt_rand(0, 127),
  31. 'settings' => array(),
  32. 'widget' => array(
  33. 'type' => 'hidden',
  34. 'label' => 'Test',
  35. 'settings' => array(),
  36. ),
  37. );
  38. $this->instance = field_create_instance($this->instance);
  39. }
  40. /**
  41. * Pass if the message $text was set by one of the CRUD hooks in
  42. * entity_crud_hook_test.module, i.e., if the $text is an element of
  43. * $_SESSION['entity_crud_hook_test'].
  44. *
  45. * @see EntityCrudHookTestCase::assertHookMessage()
  46. * @see FieldCollectionBasicTestCase::assertNoHookMessage()
  47. * @see FieldCollectionBasicTestCase::clearHookMessages()
  48. *
  49. * @param $text
  50. * Plain text to look for.
  51. * @param $message
  52. * Message to display.
  53. * @param $group
  54. * The group this message belongs to, defaults to 'Other'.
  55. * @return
  56. * TRUE on pass, FALSE on fail.
  57. */
  58. protected function assertHookMessage($text, $message = NULL, $group = 'Other') {
  59. if (!isset($message)) {
  60. $message = $text;
  61. }
  62. return $this->assertTrue(array_search($text, $_SESSION['entity_crud_hook_test']) !== FALSE, $message, $group);
  63. }
  64. /**
  65. * Fail if the message $text was set by one of the CRUD hooks in
  66. * entity_crud_hook_test.module, i.e., if the $text is an element of
  67. * $_SESSION['entity_crud_hook_test'].
  68. *
  69. * @see FieldCollectionBasicTestCase::assertHookMessage()
  70. * @see FieldCollectionBasicTestCase::clearHookMessages()
  71. *
  72. * @param $text
  73. * Plain text to look for.
  74. * @param $message
  75. * Message to display.
  76. * @param $group
  77. * The group this message belongs to, defaults to 'Other'.
  78. * @return
  79. * TRUE on pass, FALSE on fail.
  80. */
  81. protected function assertNoHookMessage($text, $message = NULL, $group = 'Other') {
  82. if (!isset($message)) {
  83. $message = $text;
  84. }
  85. return $this->assertFalse(array_search($text, $_SESSION['entity_crud_hook_test']) !== FALSE, $message, $group);
  86. }
  87. /**
  88. * Clear hook messages recorded by entity_crud_hook_test.
  89. *
  90. * @see FieldCollectionBasicTestCase::assertHookMessage()
  91. * @see FieldCollectionBasicTestCase::assertNoHookMessage()
  92. */
  93. protected function clearHookMessages() {
  94. $_SESSION['entity_crud_hook_test'] = array();
  95. }
  96. /**
  97. * Helper for creating a new node with a field collection item.
  98. */
  99. protected function createNodeWithFieldCollection() {
  100. $node = $this->drupalCreateNode(array('type' => 'article'));
  101. // Manually create a field_collection.
  102. $entity = entity_create('field_collection_item', array('field_name' => $this->field_name));
  103. $entity->setHostEntity('node', $node);
  104. $entity->save();
  105. return array($node, $entity);
  106. }
  107. /**
  108. * Tests CRUD.
  109. */
  110. function testCRUD() {
  111. list ($node, $entity) = $this->createNodeWithFieldCollection();
  112. $node = node_load($node->nid, NULL, TRUE);
  113. $this->assertEqual($entity->item_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['value'], 'A field_collection has been successfully created and referenced.');
  114. $this->assertEqual($entity->revision_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id'], 'A field_collection has been successfully created and referenced.');
  115. // Test adding an additional field_collection during node edit.
  116. $entity2 = entity_create('field_collection_item', array('field_name' => $this->field_name));
  117. $node->{$this->field_name}[LANGUAGE_NONE][] = array('entity' => $entity2);
  118. node_save($node);
  119. $node = node_load($node->nid, NULL, TRUE);
  120. $this->assertTrue(!empty($entity2->item_id) && !empty($entity2->revision_id), 'Field_collection has been saved.');
  121. $this->assertEqual($entity->item_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['value'], 'Existing reference has been kept during update.');
  122. $this->assertEqual($entity->revision_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id'], 'Existing reference has been kept during update (revision).');
  123. $this->assertEqual($entity2->item_id, $node->{$this->field_name}[LANGUAGE_NONE][1]['value'], 'New field_collection has been properly referenced');
  124. $this->assertEqual($entity2->revision_id, $node->{$this->field_name}[LANGUAGE_NONE][1]['revision_id'], 'New field_collection has been properly referenced (revision)');
  125. // Make sure deleting the field_collection removes the reference.
  126. $this->clearHookMessages();
  127. $entity2->delete();
  128. $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
  129. $node = node_load($node->nid, NULL, TRUE);
  130. $this->assertTrue(!isset($node->{$this->field_name}[LANGUAGE_NONE][1]), 'Reference correctly deleted.');
  131. // Make sure field_collections are removed during deletion of the host.
  132. $this->clearHookMessages();
  133. node_delete($node->nid);
  134. $this->assertNoHookMessage('entity_crud_hook_test_entity_presave called for type node');
  135. $this->assertTrue(entity_load('field_collection_item', FALSE) === array(), 'Field collections are deleted when the host is deleted.');
  136. // Try deleting nodes with collections without any values.
  137. $node = $this->drupalCreateNode(array('type' => 'article'));
  138. node_delete($node->nid);
  139. $this->assertTrue(node_load($node->nid, NULL, TRUE) == FALSE, 'Node without collection values deleted.');
  140. // Test creating a field collection entity with a not-yet saved host entity.
  141. $node = entity_create('node', array('type' => 'article'));
  142. $entity = entity_create('field_collection_item', array('field_name' => $this->field_name));
  143. $entity->setHostEntity('node', $node);
  144. $entity->save();
  145. // Now the node should have been saved with the collection and the link
  146. // should have been established.
  147. $this->assertTrue(!empty($node->nid), 'Node has been saved with the collection.');
  148. $this->assertTrue(count($node->{$this->field_name}[LANGUAGE_NONE]) == 1 && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['value']) && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id']), 'Link has been established.');
  149. // Again, test creating a field collection with a not-yet saved host entity,
  150. // but this time save both entities via the host.
  151. $node = entity_create('node', array('type' => 'article'));
  152. $entity = entity_create('field_collection_item', array('field_name' => $this->field_name));
  153. $entity->setHostEntity('node', $node);
  154. node_save($node);
  155. $this->assertTrue(!empty($entity->item_id) && !empty($entity->revision_id), 'Collection has been saved with the host.');
  156. $this->assertTrue(count($node->{$this->field_name}[LANGUAGE_NONE]) == 1 && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['value']) && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id']), 'Link has been established.');
  157. // Test Revisions.
  158. list ($node, $item) = $this->createNodeWithFieldCollection();
  159. $entity2 = entity_create('field_collection_item', array('field_name' => $this->field_name));
  160. $node->{$this->field_name}[LANGUAGE_NONE][] = array('entity' => $entity2);
  161. node_save($node);
  162. $this->assertEqual($entity2->archived, FALSE, 'New field collection item with new content revision is not archived.');
  163. // Test saving a new revision of a node.
  164. $node->revision = TRUE;
  165. node_save($node);
  166. $item_updated = field_collection_item_load($node->{$this->field_name}[LANGUAGE_NONE][0]['value']);
  167. $this->assertNotEqual($item->revision_id, $item_updated->revision_id, 'Creating a new host entity revision creates a new field collection revision.');
  168. // Test saving a new revision with a new field collection item.
  169. $node->revision = TRUE;
  170. // Test saving the node without creating a new revision.
  171. $item = $item_updated;
  172. $node->revision = FALSE;
  173. node_save($node);
  174. $item_updated = field_collection_item_load($node->{$this->field_name}[LANGUAGE_NONE][0]['value']);
  175. $this->assertEqual($item->revision_id, $item_updated->revision_id, 'Updating a new host entity without creating a new revision does not create a new field collection revision.');
  176. // Create a new revision of the node, such we have a non default node and
  177. // field collection revision. Then test using it.
  178. $vid = $node->vid;
  179. $item_revision_id = $item_updated->revision_id;
  180. $node->revision = TRUE;
  181. node_save($node);
  182. $item_updated = field_collection_item_load($node->{$this->field_name}[LANGUAGE_NONE][0]['value']);
  183. $this->assertNotEqual($item_revision_id, $item_updated->revision_id, 'Creating a new host entity revision creates a new field collection revision.');
  184. $this->assertTrue($item_updated->isDefaultRevision(), 'Field collection of default host entity revision is default too.');
  185. $this->assertEqual($item_updated->hostEntityId(), $node->nid, 'Can access host entity ID of default field collection revision.');
  186. $this->assertEqual($item_updated->hostEntity()->vid, $node->vid, 'Loaded default host entity revision.');
  187. $item = entity_revision_load('field_collection_item', $item_revision_id);
  188. $this->assertFalse($item->isDefaultRevision(), 'Field collection of non-default host entity is non-default too.');
  189. $this->assertEqual($item->hostEntityId(), $node->nid, 'Can access host entity ID of non-default field collection revision.');
  190. $this->assertEqual($item->hostEntity()->vid, $vid, 'Loaded non-default host entity revision.');
  191. // Delete the non-default revision and make sure the field collection item
  192. // revision has been deleted too.
  193. entity_revision_delete('node', $vid);
  194. $this->assertFalse(entity_revision_load('node', $vid), 'Host entity revision deleted.');
  195. $this->assertFalse(entity_revision_load('field_collection_item', $item_revision_id), 'Field collection item revision deleted.');
  196. // Test having archived field collections, i.e. collections referenced only
  197. // in non-default revisions.
  198. list ($node, $item) = $this->createNodeWithFieldCollection();
  199. // Create two revisions.
  200. $node_vid = $node->vid;
  201. $node->revision = TRUE;
  202. node_save($node);
  203. $node_vid2 = $node->vid;
  204. $node->revision = TRUE;
  205. node_save($node);
  206. // Now delete the field collection item for the default revision.
  207. $item = field_collection_item_load($node->{$this->field_name}[LANGUAGE_NONE][0]['value']);
  208. $item_revision_id = $item->revision_id;
  209. $item->deleteRevision();
  210. $node = node_load($node->nid);
  211. $this->assertTrue(!isset($node->{$this->field_name}[LANGUAGE_NONE][0]), 'Field collection item revision removed from host.');
  212. $this->assertFalse(field_collection_item_revision_load($item->revision_id), 'Field collection item default revision deleted.');
  213. $item = field_collection_item_load($item->item_id);
  214. $this->assertNotEqual($item->revision_id, $item_revision_id, 'Field collection default revision has been updated.');
  215. $this->assertTrue($item->archived, 'Field collection item has been archived.');
  216. $this->assertFalse($item->isInUse(), 'Field collection item specified as not in use.');
  217. $this->assertTrue($item->isDefaultRevision(), 'Field collection of non-default host entity is default (but archived).');
  218. $this->assertEqual($item->hostEntityId(), $node->nid, 'Can access host entity ID of non-default field collection revision.');
  219. $this->assertEqual($item->hostEntity()->nid, $node->nid, 'Loaded non-default host entity revision.');
  220. // Test deleting a revision of an archived field collection.
  221. $node_revision2 = node_load($node->nid, $node_vid2);
  222. $item = field_collection_item_revision_load($node_revision2->{$this->field_name}[LANGUAGE_NONE][0]['revision_id']);
  223. $item->deleteRevision();
  224. // There should be one revision left, so the item should still exist.
  225. $item = field_collection_item_load($item->item_id);
  226. $this->assertTrue($item->archived, 'Field collection item is still archived.');
  227. $this->assertFalse($item->isInUse(), 'Field collection item specified as not in use.');
  228. // Test that deleting the first node revision deletes the whole field
  229. // collection item as it contains its last revision.
  230. node_revision_delete($node_vid);
  231. $this->assertFalse(field_collection_item_load($item->item_id), 'Archived field collection deleted when last revision deleted.');
  232. // Test that removing a field-collection item also deletes it.
  233. list ($node, $item) = $this->createNodeWithFieldCollection();
  234. $node->{$this->field_name}[LANGUAGE_NONE] = array();
  235. $node->revision = FALSE;
  236. node_save($node);
  237. $this->assertFalse(field_collection_item_load($item->item_id), 'Removed field collection item has been deleted.');
  238. // Test removing a field-collection item while creating a new host revision.
  239. list ($node, $item) = $this->createNodeWithFieldCollection();
  240. $node->{$this->field_name}[LANGUAGE_NONE] = array();
  241. $node->revision = TRUE;
  242. node_save($node);
  243. // Item should not be deleted but archived now.
  244. $item = field_collection_item_load($item->item_id);
  245. $this->assertTrue($item, 'Removed field collection item still exists.');
  246. $this->assertTrue($item->archived, 'Removed field collection item is archived.');
  247. // Test removing an old node revision. Make sure that the field collection
  248. // is not removed
  249. list ($node, $item) = $this->createNodeWithFieldCollection();
  250. $node_vid = $node->vid;
  251. $node->revision = TRUE;
  252. node_save($node);
  253. $node_vid2 = $node->vid;
  254. $item_vid2 = $node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id'];
  255. node_revision_delete($node_vid);
  256. $item2 = field_collection_item_revision_load($item_vid2);
  257. $item_id2 = isset($item2->item_id) ? $item2->item_id : -1;
  258. $this->assertEqual($item_id2, $item->item_id, 'Removing an old node revision does not delete newer field collection revisions');
  259. }
  260. /**
  261. * Make sure the basic UI and access checks are working.
  262. */
  263. function testBasicUI() {
  264. // Add a field to the collection.
  265. $field = array(
  266. 'field_name' => 'field_text',
  267. 'type' => 'text',
  268. 'cardinality' => 1,
  269. 'translatable' => FALSE,
  270. );
  271. field_create_field($field);
  272. $instance = array(
  273. 'entity_type' => 'field_collection_item',
  274. 'field_name' => 'field_text',
  275. 'bundle' => $this->field_name,
  276. 'label' => 'Test text field',
  277. 'widget' => array(
  278. 'type' => 'text_textfield',
  279. ),
  280. );
  281. field_create_instance($instance);
  282. $user = $this->drupalCreateUser();
  283. $node = $this->drupalCreateNode(array('type' => 'article'));
  284. $this->drupalLogin($user);
  285. // Make sure access is denied.
  286. $path = 'field-collection/field-test-collection/add/node/' . $node->nid;
  287. $this->drupalGet($path);
  288. $this->assertText(t('Access denied'), 'Access has been denied.');
  289. $user_privileged = $this->drupalCreateUser(array('access content', 'edit any article content'));
  290. $this->drupalLogin($user_privileged);
  291. $this->drupalGet("node/$node->nid");
  292. $this->assertLinkByHref($path, 0, 'Add link is shown.');
  293. $this->drupalGet($path);
  294. $this->assertText(t('Test text field'), 'Add form is shown.');
  295. $edit['field_text[und][0][value]'] = $this->randomName();
  296. $this->drupalPost($path, $edit, t('Save'));
  297. $this->assertText(t('The changes have been saved.'), 'Field collection saved.');
  298. $this->assertText($edit['field_text[und][0][value]'], "Added field value is shown.");
  299. $edit['field_text[und][0][value]'] = $this->randomName();
  300. $this->drupalPost('field-collection/field-test-collection/1/edit', $edit, t('Save'));
  301. $this->assertText(t('The changes have been saved.'), 'Field collection saved.');
  302. $this->assertText($edit['field_text[und][0][value]'], "Field collection has been edited.");
  303. $this->drupalGet('field-collection/field-test-collection/1');
  304. $this->assertText($edit['field_text[und][0][value]'], "Field collection can be viewed.");
  305. // Add further 3 items, so we have reached 4 == maxium cardinality.
  306. $this->drupalPost($path, $edit, t('Save'));
  307. $this->drupalPost($path, $edit, t('Save'));
  308. $this->drupalPost($path, $edit, t('Save'));
  309. // Make sure adding doesn't work any more as we have restricted cardinality
  310. // to 1.
  311. $this->drupalGet($path);
  312. $this->assertText(t('Too many items.'), 'Maxium cardinality has been reached.');
  313. $this->drupalPost('field-collection/field-test-collection/1/delete', array(), t('Delete'));
  314. $this->drupalGet($path);
  315. // Add form is shown again.
  316. $this->assertText(t('Test text field'), 'Field collection item has been deleted.');
  317. // Test the viewing a revision. There should be no links to change it.
  318. $vid = $node->vid;
  319. $node = node_load($node->nid, NULL, TRUE);
  320. $node->revision = TRUE;
  321. node_save($node);
  322. $this->drupalGet("node/$node->nid/revisions/$vid/view");
  323. $this->assertResponse(403, 'Access to view revision denied');
  324. // Login in as admin and try again.
  325. $user = $this->drupalCreateUser(array('administer nodes', 'bypass node access'));
  326. $this->drupalLogin($user);
  327. $this->drupalGet("node/$node->nid/revisions/$vid/view");
  328. $this->assertNoResponse(403, 'Access to view revision granted');
  329. $this->assertNoLinkByHref($path, 'No links on revision view.');
  330. $this->assertNoLinkByHref('field-collection/field-test-collection/2/edit', 'No links on revision view.');
  331. $this->assertNoLinkByHref('field-collection/field-test-collection/2/delete', 'No links on revision view.');
  332. $this->drupalGet("node/$node->nid/revisions");
  333. }
  334. /**
  335. * Make sure that field_collection-entities are copied when host-entities do.
  336. */
  337. public function testCopyingEntities() {
  338. list($node, $entity) = $this->createNodeWithFieldCollection();
  339. // Create a copy of that node.
  340. $node->nid = NULL;
  341. $node->vid = NULL;
  342. $node->is_new = TRUE;
  343. node_save($node);
  344. $item = $node->{$this->field_name}[LANGUAGE_NONE][0];
  345. $this->assertNotEqual($entity->item_id, $item['value']);
  346. // Do a php clone to the $node object and save it.
  347. $node2 = clone $node;
  348. $node2->nid = NULL;
  349. $node2->is_new = TRUE;
  350. $node2->vid = NULL;
  351. node_save($node2);
  352. $item2 = $node2->{$this->field_name}[LANGUAGE_NONE][0];
  353. $this->assertNotEqual($item2['value'], $item['value']);
  354. // Create another copy this time (needlessly) forcing a new revision.
  355. $node->nid = NULL;
  356. $node->vid = NULL;
  357. $node->is_new = TRUE;
  358. $node->revision = TRUE;
  359. node_save($node);
  360. $item3 = $node->{$this->field_name}[LANGUAGE_NONE][0];
  361. $this->assertNotEqual($item['value'], $item3['value']);
  362. }
  363. }
  364. /**
  365. * Test using field collection with Rules.
  366. */
  367. class FieldCollectionRulesIntegrationTestCase extends DrupalWebTestCase {
  368. public static function getInfo() {
  369. return array(
  370. 'name' => 'Field collection Rules integration',
  371. 'description' => 'Tests using field collections with rules.',
  372. 'group' => 'Field types',
  373. 'dependencies' => array('rules'),
  374. );
  375. }
  376. function setUp() {
  377. parent::setUp(array('field_collection', 'rules'));
  378. variable_set('rules_debug_log', 1);
  379. }
  380. protected function createFields($cardinality = 4) {
  381. // Create a field_collection field to use for the tests.
  382. $this->field_name = 'field_test_collection';
  383. $this->field = array('field_name' => $this->field_name, 'type' => 'field_collection', 'cardinality' => $cardinality);
  384. $this->field = field_create_field($this->field);
  385. $this->field_id = $this->field['id'];
  386. $this->instance = array(
  387. 'field_name' => $this->field_name,
  388. 'entity_type' => 'node',
  389. 'bundle' => 'article',
  390. 'label' => $this->randomName() . '_label',
  391. 'description' => $this->randomName() . '_description',
  392. 'weight' => mt_rand(0, 127),
  393. 'settings' => array(),
  394. 'widget' => array(
  395. 'type' => 'hidden',
  396. 'label' => 'Test',
  397. 'settings' => array(),
  398. ),
  399. );
  400. $this->instance = field_create_instance($this->instance);
  401. // Add a field to the collection.
  402. $field = array(
  403. 'field_name' => 'field_text',
  404. 'type' => 'text',
  405. 'cardinality' => 1,
  406. 'translatable' => FALSE,
  407. );
  408. field_create_field($field);
  409. $instance = array(
  410. 'entity_type' => 'field_collection_item',
  411. 'field_name' => 'field_text',
  412. 'bundle' => $this->field_name,
  413. 'label' => 'Test text field',
  414. 'widget' => array(
  415. 'type' => 'text_textfield',
  416. ),
  417. );
  418. field_create_instance($instance);
  419. }
  420. /**
  421. * Test creation field collection items.
  422. */
  423. function testCreation() {
  424. $this->createFields();
  425. $node = $this->drupalCreateNode(array('type' => 'article'));
  426. // Create a field collection.
  427. $action_set = rules_action_set(array('node' => array('type' => 'node', 'bundle' => 'article')));
  428. $action_set->action('entity_create', array(
  429. 'type' => 'field_collection_item',
  430. 'param_field_name' => $this->field_name,
  431. 'param_host_entity:select' => 'node',
  432. ));
  433. $action_set->action('data_set', array('data:select' => 'entity-created:field-text', 'value' => 'foo'));
  434. $action_set->execute($node);
  435. $node = node_load($node->nid, NULL, TRUE);
  436. $this->assertTrue(!empty($node->{$this->field_name}[LANGUAGE_NONE][0]['value']), 'A field_collection has been successfully created.');
  437. $this->assertTrue(!empty($node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id']), 'A field_collection has been successfully created (revision).');
  438. // Now try making use of the field collection in rules.
  439. $action_set = rules_action_set(array('node' => array('type' => 'node', 'bundle' => 'article')));
  440. $action_set->action('drupal_message', array('message:select' => 'node:field-test-collection:0:field-text'));
  441. $action_set->execute($node);
  442. $msg = drupal_get_messages();
  443. $this->assertEqual(array_pop($msg['status']), 'foo', 'Field collection can be used.');
  444. RulesLog::logger()->checkLog();
  445. }
  446. /**
  447. * Test using field collection items via the host while they are being created.
  448. */
  449. function testUsageDuringCreation() {
  450. // Test using a single-cardinality field collection.
  451. $this->createFields(1);
  452. $node = $this->drupalCreateNode(array('type' => 'article'));
  453. $entity = entity_create('field_collection_item', array('field_name' => $this->field_name));
  454. $entity->setHostEntity('node', $node);
  455. // Now the field collection is linked to the host, but not yet saved.
  456. // Test using the wrapper on it.
  457. $wrapper = entity_metadata_wrapper('node', $node);
  458. $wrapper->get($this->field_name)->field_text->set('foo');
  459. $this->assertEqual($entity->field_text[LANGUAGE_NONE][0]['value'], 'foo', 'Field collection item used during creation via the wrapper.');
  460. // Now test it via Rules, which should save our changes.
  461. $set = rules_action_set(array('node' => array('type' => 'node', 'bundle' => 'article')));
  462. $set->action('data_set', array('data:select' => 'node:' . $this->field_name . ':field-text', 'value' => 'bar'));
  463. $set->execute($node);
  464. $this->assertEqual($entity->field_text[LANGUAGE_NONE][0]['value'], 'bar', 'Field collection item used during creation via Rules.');
  465. $this->assertTrue(!empty($entity->item_id) && !empty($entity->revision_id), 'Field collection item has been saved by Rules and the host entity.');
  466. RulesLog::logger()->checkLog();
  467. }
  468. }
  469. /**
  470. * Test using field collection with content that gets translated.
  471. */
  472. class FieldCollectionContentTranslationTestCase extends DrupalWebTestCase {
  473. public static function getInfo() {
  474. return array(
  475. 'name' => 'Field collection content translation',
  476. 'description' => 'Tests using content under translation.',
  477. 'group' => 'Field types',
  478. 'dependencies' => array('translation'),
  479. );
  480. }
  481. public function setUp() {
  482. parent::setUp(array('field_collection', 'translation'));
  483. // Create a field_collection field to use for the tests.
  484. $this->field_name = 'field_test_collection';
  485. $this->field = array('field_name' => $this->field_name, 'type' => 'field_collection', 'cardinality' => 4);
  486. $this->field = field_create_field($this->field);
  487. $this->field_id = $this->field['id'];
  488. $this->instance = array(
  489. 'field_name' => $this->field_name,
  490. 'entity_type' => 'node',
  491. 'bundle' => 'article',
  492. 'label' => $this->randomName() . '_label',
  493. 'description' => $this->randomName() . '_description',
  494. 'weight' => mt_rand(0, 127),
  495. 'settings' => array(),
  496. 'widget' => array(
  497. 'type' => 'field_collection_embed',
  498. 'label' => 'Test',
  499. 'settings' => array(),
  500. ),
  501. );
  502. $this->instance = field_create_instance($this->instance);
  503. // Add a field to the collection.
  504. $field = array(
  505. 'field_name' => 'field_text',
  506. 'type' => 'text',
  507. 'cardinality' => 1,
  508. 'translatable' => FALSE,
  509. );
  510. field_create_field($field);
  511. $instance = array(
  512. 'entity_type' => 'field_collection_item',
  513. 'field_name' => 'field_text',
  514. 'bundle' => $this->field_name,
  515. 'label' => 'Test text field',
  516. 'widget' => array(
  517. 'type' => 'text_textfield',
  518. ),
  519. );
  520. field_create_instance($instance);
  521. $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages', 'create article content', 'edit any article content', 'translate content'));
  522. $this->drupalLogin($admin_user);
  523. // Add German language.
  524. locale_add_language('de');
  525. // Set "Article" content type to use multilingual support.
  526. variable_set('language_content_type_article', TRANSLATION_ENABLED);
  527. }
  528. /**
  529. * Ensure field collections are cloned to new entities on content translation.
  530. */
  531. public function testContentTranslation() {
  532. // Create "Article" content.
  533. $edit['title'] = $this->randomName();
  534. $edit['body[' . LANGUAGE_NONE . '][0][value]'] = $this->randomName();
  535. $edit['language'] = 'en';
  536. $field_collection_name = 'field_test_collection[' . LANGUAGE_NONE . '][0][field_text][' . LANGUAGE_NONE . '][0][value]';
  537. $edit[$field_collection_name] = $this->randomName();
  538. $this->drupalPost('node/add/article', $edit, t('Save'));
  539. $this->assertRaw(t('Article %title has been created.', array('%title' => $edit['title'])), 'Article created.');
  540. $node1 = $this->drupalGetNodeByTitle($edit['title']);
  541. $this->drupalGet('node/' . $node1->nid . '/edit');
  542. $this->drupalGet('node/' . $node1->nid . '/translate');
  543. $this->drupalGet('node/add/article', array('query' => array('translation' => $node1->nid, 'target' => 'de')));
  544. // Suffix translations with the langcode.
  545. unset($edit['language']);
  546. $edit['title'] .= 'DE';
  547. $edit[$field_collection_name] .= 'DE';
  548. $this->drupalPost('node/add/article', $edit, t('Save'), array('query' => array('translation' => $node1->nid, 'target' => 'de')));
  549. $node2 = $this->drupalGetNodeByTitle($edit['title']);
  550. // Ensure that our new node is the translation of the first one.
  551. $this->assertEqual($node1->nid, $node2->tnid, 'Succesfully created translation.');
  552. // And check to see that their field collections are different.
  553. $this->assertNotEqual($node1->field_test_collection, $node2->field_test_collection, 'Field collections between translation source and translation differ.');
  554. }
  555. }
  556. /**
  557. * Test using field collection with content that gets translated with Entity Translation.
  558. */
  559. class FieldCollectionEntityTranslationTestCase extends DrupalWebTestCase {
  560. const TRANS_FIELD_EN = 'Translatable EN';
  561. const TRANS_FIELD_DE = 'Translatable DE';
  562. const TRANS_FIELD_DE_MOD = 'Translatable DE Mod';
  563. const UNTRANS_FIELD_EN = 'Untranslatable EN';
  564. const UNTRANS_FIELD_DE = 'Untranslatable DE';
  565. const UNTRANS_FIELD_DE_MOD = 'Untranslatable DE Mod';
  566. const NUM_VALUES = 4;
  567. public static function getInfo() {
  568. return array(
  569. 'name' => 'Field collection entity translation',
  570. 'description' => 'Tests using content under translation with Entity Translation.',
  571. 'group' => 'Field types',
  572. 'dependencies' => array('entity_translation'),
  573. );
  574. }
  575. /**
  576. * Login the given user only if she has not changed.
  577. */
  578. function login($user) {
  579. if (!isset($this->current_user) || $this->current_user->uid != $user->uid) {
  580. $this->current_user = $user;
  581. $this->drupalLogin($user);
  582. }
  583. }
  584. /**
  585. * Returns a user with administration rights.
  586. *
  587. * @param $permissions
  588. * Additional permissions for administrative user.
  589. */
  590. function getAdminUser(array $permissions = array()) {
  591. if (!isset($this->admin_user)) {
  592. $this->admin_user = $this->drupalCreateUser(array_merge(array(
  593. 'bypass node access',
  594. 'administer nodes',
  595. 'administer languages',
  596. 'administer content types',
  597. 'administer blocks',
  598. 'access administration pages',
  599. 'administer site configuration',
  600. 'administer entity translation',
  601. ), $permissions));
  602. }
  603. return $this->admin_user;
  604. }
  605. /**
  606. * Returns a user with minimal translation rights.
  607. *
  608. * @param $permissions
  609. * Additional permissions for administrative user.
  610. */
  611. function getTranslatorUser(array $permissions = array()) {
  612. if (!isset($this->translator_user)) {
  613. $this->translator_user = $this->drupalCreateUser(array_merge(array(
  614. 'create page content',
  615. 'edit own page content',
  616. 'delete own page content',
  617. 'translate any entity',
  618. ), $permissions));
  619. }
  620. return $this->translator_user;
  621. }
  622. /**
  623. * Install a specified language if it has not been already, otherwise make sure that the language is enabled.
  624. *
  625. * @param $langcode
  626. * The language code to check.
  627. */
  628. function addLanguage($langcode) {
  629. // Check to make sure that language has not already been installed.
  630. $this->drupalGet('admin/config/regional/language');
  631. if (strpos($this->drupalGetContent(), 'enabled[' . $langcode . ']') === FALSE) {
  632. // Doesn't have language installed so add it.
  633. $edit = array();
  634. $edit['langcode'] = $langcode;
  635. $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
  636. // Make sure we are not using a stale list.
  637. drupal_static_reset('language_list');
  638. $languages = language_list('language');
  639. $this->assertTrue(array_key_exists($langcode, $languages), t('Language was installed successfully.'));
  640. if (array_key_exists($langcode, $languages)) {
  641. $this->assertRaw(t('The language %language has been created and can now be used. More information is available on the <a href="@locale-help">help screen</a>.', array('%language' => $languages[$langcode]->name, '@locale-help' => url('admin/help/locale'))), t('Language has been created.'));
  642. }
  643. }
  644. elseif ($this->xpath('//input[@type="checkbox" and @name=:name and @checked="checked"]', array(':name' => 'enabled[' . $langcode . ']'))) {
  645. // It is installed and enabled. No need to do anything.
  646. $this->assertTrue(TRUE, 'Language [' . $langcode . '] already installed and enabled.');
  647. }
  648. else {
  649. // It is installed but not enabled. Enable it.
  650. $this->assertTrue(TRUE, 'Language [' . $langcode . '] already installed.');
  651. $this->drupalPost(NULL, array('enabled[' . $langcode . ']' => TRUE), t('Save configuration'));
  652. $this->assertRaw(t('Configuration saved.'), t('Language successfully enabled.'));
  653. }
  654. }
  655. public function setUp() {
  656. parent::setUp(array('field_collection', 'entity_translation'));
  657. $language_none = LANGUAGE_NONE;
  658. // Login with an admin user.
  659. $this->login($this->getAdminUser());
  660. // Add English and German languages.
  661. $this->addLanguage('en');
  662. $this->addLanguage('de');
  663. // Set "Article" content type to use multilingual support with translation.
  664. $edit = array();
  665. $edit['language_content_type'] = ENTITY_TRANSLATION_ENABLED;
  666. $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
  667. $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
  668. // Create a field collection field to use for the tests.
  669. $this->field_name = 'field_test_collection';
  670. $this->field_base = "{$this->field_name}[$language_none]";
  671. $this->field = array(
  672. 'field_name' => $this->field_name,
  673. 'type' => 'field_collection',
  674. 'cardinality' => -1,
  675. 'translatable' => TRUE,
  676. );
  677. $this->field = field_create_field($this->field);
  678. $this->field_id = $this->field['id'];
  679. $this->instance = array(
  680. 'field_name' => $this->field_name,
  681. 'entity_type' => 'node',
  682. 'bundle' => 'page',
  683. 'label' => $this->randomName() . '_label',
  684. 'description' => $this->randomName() . '_description',
  685. 'weight' => mt_rand(0, 127),
  686. 'settings' => array(),
  687. 'widget' => array(
  688. 'type' => 'field_collection_embed',
  689. 'label' => 'Test',
  690. 'settings' => array(),
  691. ),
  692. );
  693. $this->instance = field_create_instance($this->instance);
  694. // Enable entity translation of field collections.
  695. $this->drupalGet('admin/config/regional/entity_translation');
  696. $this->drupalPost('admin/config/regional/entity_translation', array('entity_translation_entity_types[field_collection_item]' => TRUE), t('Save configuration'));
  697. $this->assertRaw(t('The configuration options have been saved.'), t('Entity translation of field collections enabled.'));
  698. // Add an untraslatable field to the collection.
  699. $this->field_untrans_name = 'field_text_untrans';
  700. $this->field_untrans_base = "[{$this->field_untrans_name}][$language_none][0][value]";
  701. $field = array(
  702. 'field_name' => $this->field_untrans_name,
  703. 'type' => 'text',
  704. 'cardinality' => 1,
  705. 'translatable' => FALSE,
  706. );
  707. field_create_field($field);
  708. $instance = array(
  709. 'entity_type' => 'field_collection_item',
  710. 'field_name' => $this->field_untrans_name,
  711. 'bundle' => $this->field_name,
  712. 'label' => 'Test untranslatable text field',
  713. 'widget' => array(
  714. 'type' => 'text_textfield',
  715. ),
  716. );
  717. field_create_instance($instance);
  718. // Add a translatable field to the collection.
  719. $this->field_trans_name = 'field_text_trans';
  720. $this->field_trans_base = "[{$this->field_trans_name}][$language_none][0][value]";
  721. $this->field_trans_dest = "[{$this->field_trans_name}][de][0][value]";
  722. $field = array(
  723. 'field_name' => $this->field_trans_name,
  724. 'type' => 'text',
  725. 'cardinality' => 1,
  726. 'translatable' => TRUE,
  727. );
  728. field_create_field($field);
  729. $instance = array(
  730. 'entity_type' => 'field_collection_item',
  731. 'field_name' => $this->field_trans_name,
  732. 'bundle' => $this->field_name,
  733. 'label' => 'Test translatable text field',
  734. 'widget' => array(
  735. 'type' => 'text_textfield',
  736. ),
  737. );
  738. field_create_instance($instance);
  739. $this->login($this->getTranslatorUser());
  740. }
  741. /**
  742. * Creates a basic page with a value in the field collection.
  743. *
  744. * @param integer $num_values
  745. * The number of values to include in the field collection.
  746. * @param string $langcode
  747. * Language for the node.
  748. */
  749. protected function createPage($num_values, $langcode = 'en') {
  750. // Check if num_values is greater than the field cardinality.
  751. if ($num_values > self::NUM_VALUES) {
  752. $num_values = self::NUM_VALUES;
  753. }
  754. $title = $this->randomName();
  755. $this->drupalGet('node/add/page');
  756. $edit = array();
  757. $edit['title'] = $title;
  758. for ($i = 0; $i < $num_values; $i++) {
  759. if ($i != 0) {
  760. $this->drupalPost(NULL, array(), t('Add another item'));
  761. }
  762. $edit[$this->field_base . '[' . $i . ']' . $this->field_untrans_base] = self::UNTRANS_FIELD_EN . '_' . $i;
  763. $edit[$this->field_base . '[' . $i . ']' . $this->field_trans_base] = self::TRANS_FIELD_EN . '_' . $i;
  764. }
  765. $edit['language'] = $langcode;
  766. $this->drupalPost(NULL, $edit, t('Save'));
  767. $this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), t('Basic page created.'));
  768. // Check to make sure the node was created.
  769. $node = $this->drupalGetNodeByTitle($title);
  770. $this->assertTrue($node, t('Node found in database.'));
  771. return $node;
  772. }
  773. /**
  774. * Create a translation using the Entity Translation Form.
  775. *
  776. * @param $node
  777. * Node of the basic page to create translation for.
  778. * @param $langcode
  779. * The language code of the translation.
  780. * @param $source_langcode
  781. * The original language code.
  782. */
  783. protected function createTranslationForm($node, $langcode, $source_langcode = 'en') {
  784. $language_none = LANGUAGE_NONE;
  785. $edit = array();
  786. $this->drupalGet('node/' . $node->nid . '/edit/add/' . $source_langcode . '/' .$langcode);
  787. // Get the field collection in the original language.
  788. $fc_values = $node->{$this->field_name}[$source_langcode];
  789. // Check if all the fields were populated and fill it with the new value.
  790. foreach ($fc_values as $delta => $fc_value) {
  791. // Load the field collection item.
  792. $fc_item_array = entity_load('field_collection_item', array($fc_value['value']));
  793. $fc_item = reset($fc_item_array);
  794. $fc_untrans_key = "{$this->field_name}[$langcode][$delta]{$this->field_untrans_base}";
  795. $fc_trans_key = "{$this->field_name}[$langcode][$delta]{$this->field_trans_dest}";
  796. $this->assertFieldByXPath(
  797. "//input[@name='$fc_untrans_key']",
  798. $fc_item->{$this->field_untrans_name}[LANGUAGE_NONE][0]['value'],
  799. 'Original value of untranslatable field correctly populated'
  800. );
  801. $this->assertFieldByXPath(
  802. "//input[@name='$fc_trans_key']",
  803. $fc_item->{$this->field_trans_name}['en'][0]['value'],
  804. 'Original value of translatable field correctly populated'
  805. );
  806. $edit[$fc_untrans_key] = self::UNTRANS_FIELD_DE . '_' . $delta;
  807. $edit[$fc_trans_key] = self::TRANS_FIELD_DE . '_' . $delta;
  808. }
  809. // Save the translation.
  810. $this->drupalPost(NULL, $edit, t('Save'));
  811. $this->drupalGet('node/' . $node->nid . '/translate');
  812. $this->assertLinkByHref('node/' . $node->nid . '/edit/' . $langcode, 0, t('Translation edit link found. Translation created.'));
  813. // Reload the node.
  814. $node = node_load($node->nid, NULL, TRUE);
  815. // Check the values of the translated field.
  816. $this->checkFieldCollectionContent($node, $langcode);
  817. // Check the values of the field in the original language.
  818. $this->checkFieldCollectionContent($node, $source_langcode);
  819. return $node;
  820. }
  821. /**
  822. * Removes a translation using the entity translation form.
  823. *
  824. * @param mixed $node
  825. * The node to remove the translation from.
  826. * @param unknown $langcode
  827. * The language of the translation to remove.
  828. * @param unknown $source_langcode
  829. * The source language of the node.
  830. */
  831. protected function removeTranslationForm($node, $langcode, $source_langcode) {
  832. // Number of field collection items in the source language.
  833. $num_original_fc_items = count($node->{$this->field_name}[$source_langcode]);
  834. // Fetch the translation edit form.
  835. $this->drupalGet('node/' . $node->nid . '/edit/' . $langcode);
  836. // Remove the translation.
  837. $this->drupalPost(NULL, array(), t('Delete translation'));
  838. // Confirm deletion.
  839. $this->drupalPost(NULL, array(), t('Delete'));
  840. // Reload the node.
  841. $node = node_load($node->nid, NULL, TRUE);
  842. // Check that the translation is removed.
  843. $this->drupalGet('node/' . $node->nid . '/translate');
  844. $this->assertLinkByHref('node/' . $node->nid . '/edit/add/' . $source_langcode . '/' . $langcode, 0, 'The add translation link appears');
  845. $this->assert(empty($node->{$this->field_name}[$langcode]));
  846. // Check that the field collection in the original language has not changed.
  847. $num_fc_items = count($node->{$this->field_name}[$source_langcode]);
  848. $this->assertEqual($num_original_fc_items, $num_fc_items, 'The number of field collection items in the original language has not changed.');
  849. $this->checkFieldCollectionContent($node, $source_langcode);
  850. }
  851. /**
  852. * Creates a translation programmatically using Entity Translation.
  853. *
  854. * @param $node
  855. * Node of the basic page to create translation for.
  856. * @param $langcode
  857. * The language code of the translation.
  858. * @param $source_langcode
  859. * The source language code.
  860. */
  861. protected function createTranslation($node, $langcode) {
  862. $source_langcode = $node->language;
  863. // Get the Entity Translation Handler.
  864. $handler = entity_translation_get_handler('node', $node, TRUE);
  865. // Variable to hold the fields values.
  866. $values = array();
  867. // Translation settings.
  868. $translation = array(
  869. 'translate' => 0,
  870. 'status' => 1,
  871. 'language' => $langcode,
  872. 'source' => $source_langcode,
  873. 'uid' => $node->uid,
  874. );
  875. // Copy field values.
  876. foreach (field_info_instances('node', $node->type) as $instance) {
  877. $field_name = $instance['field_name'];
  878. $field = field_info_field($field_name);
  879. $field_value = array();
  880. // Copy the value of the translated field if it's translatable.
  881. if ($field['translatable']) {
  882. if (isset($node->{$field_name}[$node->language])) {
  883. $field_value = $node->{$field_name}[$source_langcode];
  884. $values[$field_name][$langcode] = $field_value;
  885. $node->{$field_name}[$langcode] = $field_value;
  886. }
  887. }
  888. }
  889. $handler->setTranslation($translation, $values);
  890. $handler->saveTranslations();
  891. field_attach_update('node', $node);
  892. // Reload an return the node.
  893. $node = node_load($node->nid, null, TRUE);
  894. return $node;
  895. }
  896. /**
  897. * Removes a translation programmatically using the entity translation api.
  898. *
  899. * @param mixed $node
  900. * The node to remove the translation from.
  901. * @param unknown $langcode
  902. * The language of the translation to remove.
  903. */
  904. protected function removeTranslation($node, $langcode) {
  905. // Get a translation entity handler.
  906. $handler = entity_translation_get_handler('node', $node, TRUE);
  907. // Remove the translation.
  908. $handler->removeTranslation($langcode);
  909. node_save($node);
  910. // Reload and return the node.
  911. $node = node_load($node->nid, null, TRUE);
  912. return $node;
  913. }
  914. /**
  915. * Creates a new revision of the node and checks the result.
  916. *
  917. * @param $node
  918. * @param $langcode
  919. * @param $source_langcode
  920. * @return
  921. * The new revision of the node.
  922. */
  923. protected function createRevision($node, $langcode, $source_langcode) {
  924. $node_original_revision = $node->vid;
  925. // The original entries of the translated field.
  926. $original_fc_item_ids = $node->{$this->field_name}[$langcode];
  927. // Create the revision.
  928. $node->revision = TRUE;
  929. node_save($node);
  930. // The new entries of the translated field.
  931. $new_fc_item_ids = $node->{$this->field_name}[$langcode];
  932. // Check that the field collection items are the same and a new revision of
  933. // each one has been created.
  934. foreach ($original_fc_item_ids as $delta => $value) {
  935. $this->assertEqual($value['value'], $new_fc_item_ids[$delta]['value'], t('We have the same field collection item'));
  936. $this->assertNotEqual($value['revision_id'], $new_fc_item_ids[$delta]['revision_id'], t('We have a new revision of the field collection item'));
  937. }
  938. return $node;
  939. }
  940. /**
  941. * Check the content of the field collection for a specified language.
  942. *
  943. * @param mixed $node
  944. * The node to check.
  945. * @param string $langcode
  946. * The language to check.
  947. */
  948. protected function checkFieldCollectionContent($node, $langcode) {
  949. switch($langcode) {
  950. case 'en':
  951. $untrans_field = self::UNTRANS_FIELD_EN;
  952. $trans_field = self::TRANS_FIELD_EN;
  953. break;
  954. case 'de':
  955. $untrans_field = self::UNTRANS_FIELD_DE;
  956. $trans_field = self::TRANS_FIELD_DE;
  957. break;
  958. }
  959. // Get the field collection in the specified language.
  960. $fc_values = $node->{$this->field_name}[$langcode];
  961. foreach ($fc_values as $delta => $fc_value) {
  962. // Load the field collection item.
  963. $fc_item_array = entity_load('field_collection_item', array($fc_value['value']));
  964. $fc_item = reset($fc_item_array);
  965. $fc_untrans_key = "{$this->field_name}[$langcode][$delta]{$this->field_untrans_base}";
  966. $fc_trans_key = "{$this->field_name}[$langcode][$delta]{$this->field_trans_base}";
  967. $this->assertEqual($untrans_field . '_' . $delta, $fc_item->{$this->field_untrans_name}[LANGUAGE_NONE][0]['value']);
  968. $this->assertEqual($trans_field . '_' . $delta, $fc_item->{$this->field_trans_name}[$langcode][0]['value']);
  969. }
  970. }
  971. /**
  972. * Returns the text field values of an specified node, language and delta.
  973. *
  974. * @param mixed $node
  975. * @param string $langcode
  976. * @param integer $delta
  977. * @return array
  978. */
  979. protected function getFieldValues($node, $langcode, $delta) {
  980. $return = array();
  981. if (isset($node->{$this->field_name}[$langcode][$delta]['value'])) {
  982. $fc_item_id = $node->{$this->field_name}[$langcode][$delta]['value'];
  983. // Load the field collection.
  984. $fc_item_array = entity_load('field_collection_item', array($fc_item_id));
  985. $fc_item = reset($fc_item_array);
  986. $return = array(
  987. 'field_untrans' => $fc_item->{$this->field_untrans_name}[LANGUAGE_NONE][0]['value'],
  988. 'field_trans' => $fc_item->{$this->field_trans_name}[$langcode][0]['value'],
  989. );
  990. }
  991. return $return;
  992. }
  993. /**
  994. * Ensures the right behaviour in all Entity Translation use cases.
  995. */
  996. public function testEntityTranslation() {
  997. $source_langcode = 'en';
  998. $translation_langcode = 'de';
  999. /*
  1000. * Test with a page with only one value in the field collection
  1001. */
  1002. // Create an article in the original language with only one field collection
  1003. // value.
  1004. $node = $this->createPage(1, $source_langcode);
  1005. // Create a traslation of the page through the entity translation form.
  1006. $node = $this->createTranslationForm($node, $translation_langcode, $source_langcode);
  1007. /*
  1008. * Test with a page with multiple values in the field collection.
  1009. */
  1010. $num_values = 4;
  1011. // Create a page in the original language with multiple field collection
  1012. // values.
  1013. $node = $this->createPage($num_values, $source_langcode);
  1014. // Create a traslation of the page through the entity translation form.
  1015. $node = $this->createTranslationForm($node, $translation_langcode, $source_langcode);
  1016. // Assign a new field collection item to an existing node.
  1017. $values = array();
  1018. $values['field_name'] = $this->field_name;
  1019. $fc_entity = entity_create('field_collection_item', $values);
  1020. $fc_entity->setHostEntity('node', $node, $translation_langcode);
  1021. $fc_entity->{$this->field_untrans_name}[LANGUAGE_NONE][0]['value'] = self::UNTRANS_FIELD_DE_MOD;
  1022. $fc_entity->{$this->field_trans_name}['de'][0]['value'] = self::TRANS_FIELD_DE_MOD;
  1023. $fc_entity->save(TRUE);
  1024. node_save($node);
  1025. // Reload the node to check it.
  1026. $node = node_load($node->nid, NULL, TRUE);
  1027. // Check that there is a new element in the translation.
  1028. $this->assertEqual($num_values + 1, count($node->{$this->field_name}[$translation_langcode]), t('We have one item more in translation.'));
  1029. // Check that the new element is correctly saved.
  1030. $fc_item_values = $this->getFieldValues($node, $translation_langcode, $num_values);
  1031. $this->assertEqual($fc_item_values['field_untrans'], self::UNTRANS_FIELD_DE_MOD);
  1032. $this->assertEqual($fc_item_values['field_trans'], self::TRANS_FIELD_DE_MOD);
  1033. // Check that we have the same items in the original language.
  1034. $this->assertEqual($num_values, count($node->{$this->field_name}[$source_langcode]), t('We have same items in the original language.'));
  1035. // Remove a field collection item from the translation.
  1036. $fc_item_id = $node->{$this->field_name}[$translation_langcode][0]['value'];
  1037. unset($node->{$this->field_name}[$translation_langcode][0]);
  1038. node_save($node);
  1039. // Reload the node.
  1040. $node = node_load($node->nid, NULL, TRUE);
  1041. // Check that we have one item less in the translation.
  1042. // We should take into account that we added a field one step before.
  1043. $this->assertEqual($num_values, count($node->{$this->field_name}[$translation_langcode]), t('We have one item less in translation.'));
  1044. // Check that we have the same items in the original language.
  1045. $this->assertEqual($num_values, count($node->{$this->field_name}[$source_langcode]), t('We have same items in the original language.'));
  1046. // Check that the field collection is removed from the database.
  1047. $fc_items = entity_load('field_collection_item', array($fc_item_id));
  1048. $this->assert(empty($fc_items), t('The field collection item has been removed from the database.'));
  1049. // Delete the translation.
  1050. $this->removeTranslationForm($node, $translation_langcode, $source_langcode);
  1051. /*
  1052. * Check the revisioning of an entity with translations.
  1053. */
  1054. $num_values = 4;
  1055. // Create a page in the original language with multiple field collection
  1056. // values.
  1057. $node_rev = $this->createPage($num_values, $source_langcode);
  1058. // Create a traslation of the page.
  1059. $node_rev = $this->createTranslationForm($node_rev, $translation_langcode, $source_langcode);
  1060. $original_revision = $node_rev->vid;
  1061. // Create a new revision of the node.
  1062. $node_rev = $this->createRevision($node_rev, $translation_langcode, $source_langcode);
  1063. /*
  1064. * Test creating programmatically.
  1065. */
  1066. $num_values = 4;
  1067. // Create a page in the original language.
  1068. $node_prog = $this->createPage($num_values, $source_langcode);
  1069. // Create programmatically a translation of the page.
  1070. $node_prog = $this->createTranslation($node_prog, $translation_langcode);
  1071. $orig_fc_items = $node_prog->{$this->field_name}[$source_langcode];
  1072. $trans_fc_items = $node_prog->{$this->field_name}[$translation_langcode];
  1073. $orig_fc_item_ids = array();
  1074. $trans_fc_item_ids = array();
  1075. // Check each item.
  1076. foreach ($orig_fc_items as $delta => $value) {
  1077. $orig_fc_item_ids[] = $value['value'];
  1078. $trans_fc_item_ids[] = $trans_fc_items[$delta]['value'];
  1079. // Check if we have new items for the translation.
  1080. $this->assertNotEqual($value['value'], $trans_fc_items[$delta]['value'], t('New item generated for translation.'));
  1081. }
  1082. // Check that the original item still exists in the database.
  1083. $fc_items = entity_load('field_collection_item', $orig_fc_item_ids);
  1084. $this->assert(!empty($fc_items), t('Field Collections in the source language still exist.'));
  1085. // Check that the translated item exists in the database.
  1086. $fc_items = entity_load('field_collection_item', $trans_fc_item_ids);
  1087. $this->assert(!empty($fc_items), t('Translations for the Field Collection exist.'));
  1088. // Remove the translation and check that the original field collection items
  1089. // are still there.
  1090. $node_prog = $this->removeTranslation($node, $translation_langcode);
  1091. // Check the content in the source language.
  1092. $this->checkFieldCollectionContent($node_prog, $source_langcode);
  1093. // Check that the field translated content has been removed.
  1094. $this->assert(empty($node->{$this->field_name}[$translation_langcode]), t('Translated content removed.'));
  1095. }
  1096. }