taxonomy.test 90 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332
  1. <?php
  2. /**
  3. * @file
  4. * Tests for taxonomy.module.
  5. */
  6. /**
  7. * Provides common helper methods for Taxonomy module tests.
  8. */
  9. class TaxonomyWebTestCase extends DrupalWebTestCase {
  10. /**
  11. * Returns a new vocabulary with random properties.
  12. */
  13. function createVocabulary() {
  14. // Create a vocabulary.
  15. $vocabulary = new stdClass();
  16. $vocabulary->name = $this->randomName();
  17. $vocabulary->description = $this->randomName();
  18. $vocabulary->machine_name = drupal_strtolower($this->randomName());
  19. $vocabulary->help = '';
  20. $vocabulary->nodes = array('article' => 'article');
  21. $vocabulary->weight = mt_rand(0, 10);
  22. taxonomy_vocabulary_save($vocabulary);
  23. return $vocabulary;
  24. }
  25. /**
  26. * Returns a new term with random properties in vocabulary $vid.
  27. */
  28. function createTerm($vocabulary) {
  29. $term = new stdClass();
  30. $term->name = $this->randomName();
  31. $term->description = $this->randomName();
  32. // Use the first available text format.
  33. $term->format = db_query_range('SELECT format FROM {filter_format}', 0, 1)->fetchField();
  34. $term->vid = $vocabulary->vid;
  35. taxonomy_term_save($term);
  36. return $term;
  37. }
  38. }
  39. /**
  40. * Tests the taxonomy vocabulary interface.
  41. */
  42. class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase {
  43. protected $vocabulary;
  44. protected $admin_user;
  45. public static function getInfo() {
  46. return array(
  47. 'name' => 'Taxonomy vocabulary interface',
  48. 'description' => 'Test the taxonomy vocabulary interface.',
  49. 'group' => 'Taxonomy',
  50. );
  51. }
  52. function setUp() {
  53. parent::setUp();
  54. $this->admin_user = $this->drupalCreateUser(array('administer taxonomy'));
  55. $this->drupalLogin($this->admin_user);
  56. $this->vocabulary = $this->createVocabulary();
  57. }
  58. /**
  59. * Create, edit and delete a vocabulary via the user interface.
  60. */
  61. function testVocabularyInterface() {
  62. // Visit the main taxonomy administration page.
  63. $this->drupalGet('admin/structure/taxonomy');
  64. // Create a new vocabulary.
  65. $this->clickLink(t('Add vocabulary'));
  66. $edit = array();
  67. $machine_name = drupal_strtolower($this->randomName());
  68. $edit['name'] = $this->randomName();
  69. $edit['description'] = $this->randomName();
  70. $edit['machine_name'] = $machine_name;
  71. $this->drupalPost(NULL, $edit, t('Save'));
  72. $this->assertRaw(t('Created new vocabulary %name.', array('%name' => $edit['name'])), 'Vocabulary created successfully.');
  73. // Edit the vocabulary.
  74. $this->drupalGet('admin/structure/taxonomy');
  75. $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.');
  76. $this->clickLink(t('edit vocabulary'));
  77. $edit = array();
  78. $edit['name'] = $this->randomName();
  79. $this->drupalPost(NULL, $edit, t('Save'));
  80. $this->drupalGet('admin/structure/taxonomy');
  81. $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.');
  82. // Try to submit a vocabulary with a duplicate machine name.
  83. $edit['machine_name'] = $machine_name;
  84. $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
  85. $this->assertText(t('The machine-readable name is already in use. It must be unique.'));
  86. // Try to submit an invalid machine name.
  87. $edit['machine_name'] = '!&^%';
  88. $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
  89. $this->assertText(t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
  90. // Ensure that vocabulary titles are escaped properly.
  91. $edit = array();
  92. $edit['name'] = 'Don\'t Panic';
  93. $edit['description'] = $this->randomName();
  94. $edit['machine_name'] = 'don_t_panic';
  95. $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
  96. $site_name = variable_get('site_name', 'Drupal');
  97. $this->drupalGet('admin/structure/taxonomy/don_t_panic');
  98. $this->assertTitle(t('Don\'t Panic | @site-name', array('@site-name' => $site_name)));
  99. $this->assertNoTitle(t('Don&#039;t Panic | @site-name', array('@site-name' => $site_name)));
  100. }
  101. /**
  102. * Changing weights on the vocabulary overview with two or more vocabularies.
  103. */
  104. function testTaxonomyAdminChangingWeights() {
  105. // Create some vocabularies.
  106. for ($i = 0; $i < 10; $i++) {
  107. $this->createVocabulary();
  108. }
  109. // Get all vocabularies and change their weights.
  110. $vocabularies = taxonomy_get_vocabularies();
  111. $edit = array();
  112. foreach ($vocabularies as $key => $vocabulary) {
  113. $vocabulary->weight = -$vocabulary->weight;
  114. $vocabularies[$key]->weight = $vocabulary->weight;
  115. $edit[$key . '[weight]'] = $vocabulary->weight;
  116. }
  117. // Saving the new weights via the interface.
  118. $this->drupalPost('admin/structure/taxonomy', $edit, t('Save'));
  119. // Load the vocabularies from the database.
  120. $new_vocabularies = taxonomy_get_vocabularies();
  121. // Check that the weights are saved in the database correctly.
  122. foreach ($vocabularies as $key => $vocabulary) {
  123. $this->assertEqual($new_vocabularies[$key]->weight, $vocabularies[$key]->weight, 'The vocabulary weight was changed.');
  124. }
  125. }
  126. /**
  127. * Test the vocabulary overview with no vocabularies.
  128. */
  129. function testTaxonomyAdminNoVocabularies() {
  130. // Delete all vocabularies.
  131. $vocabularies = taxonomy_get_vocabularies();
  132. foreach ($vocabularies as $key => $vocabulary) {
  133. taxonomy_vocabulary_delete($key);
  134. }
  135. // Confirm that no vocabularies are found in the database.
  136. $this->assertFalse(taxonomy_get_vocabularies(), 'No vocabularies found in the database.');
  137. $this->drupalGet('admin/structure/taxonomy');
  138. // Check the default message for no vocabularies.
  139. $this->assertText(t('No vocabularies available.'), 'No vocabularies were found.');
  140. }
  141. /**
  142. * Deleting a vocabulary.
  143. */
  144. function testTaxonomyAdminDeletingVocabulary() {
  145. // Create a vocabulary.
  146. $edit = array(
  147. 'name' => $this->randomName(),
  148. 'machine_name' => drupal_strtolower($this->randomName()),
  149. );
  150. $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
  151. $this->assertText(t('Created new vocabulary'), 'New vocabulary was created.');
  152. // Check the created vocabulary.
  153. $vocabularies = taxonomy_get_vocabularies();
  154. $vocabularies_keys = array_keys($vocabularies);
  155. $vid = $vocabularies[end($vocabularies_keys)]->vid;
  156. entity_get_controller('taxonomy_vocabulary')->resetCache();
  157. $vocabulary = taxonomy_vocabulary_load($vid);
  158. $this->assertTrue($vocabulary, 'Vocabulary found in database.');
  159. // Delete the vocabulary.
  160. $edit = array();
  161. $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/edit', $edit, t('Delete'));
  162. $this->assertRaw(t('Are you sure you want to delete the vocabulary %name?', array('%name' => $vocabulary->name)), '[confirm deletion] Asks for confirmation.');
  163. $this->assertText(t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'), '[confirm deletion] Inform that all terms will be deleted.');
  164. // Confirm deletion.
  165. $this->drupalPost(NULL, NULL, t('Delete'));
  166. $this->assertRaw(t('Deleted vocabulary %name.', array('%name' => $vocabulary->name)), 'Vocabulary deleted.');
  167. entity_get_controller('taxonomy_vocabulary')->resetCache();
  168. $this->assertFalse(taxonomy_vocabulary_load($vid), 'Vocabulary is not found in the database.');
  169. }
  170. }
  171. /**
  172. * Tests for taxonomy vocabulary functions.
  173. */
  174. class TaxonomyVocabularyTestCase extends TaxonomyWebTestCase {
  175. protected $vocabulary;
  176. protected $field;
  177. protected $field_name;
  178. protected $instance;
  179. public static function getInfo() {
  180. return array(
  181. 'name' => 'Taxonomy vocabularies',
  182. 'description' => 'Test loading, saving and deleting vocabularies.',
  183. 'group' => 'Taxonomy',
  184. );
  185. }
  186. function setUp() {
  187. parent::setUp('taxonomy', 'field_test');
  188. $admin_user = $this->drupalCreateUser(array('create article content', 'administer taxonomy'));
  189. $this->drupalLogin($admin_user);
  190. $this->vocabulary = $this->createVocabulary();
  191. }
  192. /**
  193. * Ensure that when an invalid vocabulary vid is loaded, it is possible
  194. * to load the same vid successfully if it subsequently becomes valid.
  195. */
  196. function testTaxonomyVocabularyLoadReturnFalse() {
  197. // Load a vocabulary that doesn't exist.
  198. $vocabularies = taxonomy_get_vocabularies();
  199. $vid = count($vocabularies) + 1;
  200. $vocabulary = taxonomy_vocabulary_load($vid);
  201. // This should not return an object because no such vocabulary exists.
  202. $this->assertTrue(empty($vocabulary), 'No object loaded.');
  203. // Create a new vocabulary.
  204. $this->createVocabulary();
  205. // Load the vocabulary with the same $vid from earlier.
  206. // This should return a vocabulary object since it now matches a real vid.
  207. $vocabulary = taxonomy_vocabulary_load($vid);
  208. $this->assertTrue(!empty($vocabulary) && is_object($vocabulary), 'Vocabulary is an object.');
  209. $this->assertEqual($vocabulary->vid, $vid, 'Valid vocabulary vid is the same as our previously invalid one.');
  210. }
  211. /**
  212. * Test deleting a taxonomy that contains terms.
  213. */
  214. function testTaxonomyVocabularyDeleteWithTerms() {
  215. // Delete any existing vocabularies.
  216. foreach (taxonomy_get_vocabularies() as $vocabulary) {
  217. taxonomy_vocabulary_delete($vocabulary->vid);
  218. }
  219. // Assert that there are no terms left.
  220. $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField());
  221. // Create a new vocabulary and add a few terms to it.
  222. $vocabulary = $this->createVocabulary();
  223. $terms = array();
  224. for ($i = 0; $i < 5; $i++) {
  225. $terms[$i] = $this->createTerm($vocabulary);
  226. }
  227. // Set up hierarchy. term 2 is a child of 1 and 4 a child of 1 and 2.
  228. $terms[2]->parent = array($terms[1]->tid);
  229. taxonomy_term_save($terms[2]);
  230. $terms[4]->parent = array($terms[1]->tid, $terms[2]->tid);
  231. taxonomy_term_save($terms[4]);
  232. // Assert that there are now 5 terms.
  233. $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField());
  234. taxonomy_vocabulary_delete($vocabulary->vid);
  235. // Assert that there are no terms left.
  236. $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField());
  237. }
  238. /**
  239. * Ensure that the vocabulary static reset works correctly.
  240. */
  241. function testTaxonomyVocabularyLoadStaticReset() {
  242. $original_vocabulary = taxonomy_vocabulary_load($this->vocabulary->vid);
  243. $this->assertTrue(is_object($original_vocabulary), 'Vocabulary loaded successfully.');
  244. $this->assertEqual($this->vocabulary->name, $original_vocabulary->name, 'Vocabulary loaded successfully.');
  245. // Change the name and description.
  246. $vocabulary = $original_vocabulary;
  247. $vocabulary->name = $this->randomName();
  248. $vocabulary->description = $this->randomName();
  249. taxonomy_vocabulary_save($vocabulary);
  250. // Load the vocabulary.
  251. $new_vocabulary = taxonomy_vocabulary_load($original_vocabulary->vid);
  252. $this->assertEqual($new_vocabulary->name, $vocabulary->name);
  253. $this->assertEqual($new_vocabulary->name, $vocabulary->name);
  254. // Delete the vocabulary.
  255. taxonomy_vocabulary_delete($this->vocabulary->vid);
  256. $vocabularies = taxonomy_get_vocabularies();
  257. $this->assertTrue(!isset($vocabularies[$this->vocabulary->vid]), 'The vocabulary was deleted.');
  258. }
  259. /**
  260. * Tests for loading multiple vocabularies.
  261. */
  262. function testTaxonomyVocabularyLoadMultiple() {
  263. // Delete any existing vocabularies.
  264. foreach (taxonomy_get_vocabularies() as $vocabulary) {
  265. taxonomy_vocabulary_delete($vocabulary->vid);
  266. }
  267. // Create some vocabularies and assign weights.
  268. $vocabulary1 = $this->createVocabulary();
  269. $vocabulary1->weight = 0;
  270. taxonomy_vocabulary_save($vocabulary1);
  271. $vocabulary2 = $this->createVocabulary();
  272. $vocabulary2->weight = 1;
  273. taxonomy_vocabulary_save($vocabulary2);
  274. $vocabulary3 = $this->createVocabulary();
  275. $vocabulary3->weight = 2;
  276. taxonomy_vocabulary_save($vocabulary3);
  277. // Fetch the names for all vocabularies, confirm that they are keyed by
  278. // machine name.
  279. $names = taxonomy_vocabulary_get_names();
  280. $this->assertEqual($names[$vocabulary1->machine_name]->name, $vocabulary1->name, 'Vocabulary 1 name found.');
  281. // Fetch all of the vocabularies using taxonomy_get_vocabularies().
  282. // Confirm that the vocabularies are ordered by weight.
  283. $vocabularies = taxonomy_get_vocabularies();
  284. $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary1->vid, 'Vocabulary was found in the vocabularies array.');
  285. $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary2->vid, 'Vocabulary was found in the vocabularies array.');
  286. $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary3->vid, 'Vocabulary was found in the vocabularies array.');
  287. // Fetch the vocabularies with taxonomy_vocabulary_load_multiple(), specifying IDs.
  288. // Ensure they are returned in the same order as the original array.
  289. $vocabularies = taxonomy_vocabulary_load_multiple(array($vocabulary3->vid, $vocabulary2->vid, $vocabulary1->vid));
  290. $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary3->vid, 'Vocabulary loaded successfully by ID.');
  291. $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary2->vid, 'Vocabulary loaded successfully by ID.');
  292. $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary1->vid, 'Vocabulary loaded successfully by ID.');
  293. // Fetch vocabulary 1 by name.
  294. $vocabulary = current(taxonomy_vocabulary_load_multiple(array(), array('name' => $vocabulary1->name)));
  295. $this->assertEqual($vocabulary->vid, $vocabulary1->vid, 'Vocabulary loaded successfully by name.');
  296. // Fetch vocabulary 1 by name and ID.
  297. $this->assertEqual(current(taxonomy_vocabulary_load_multiple(array($vocabulary1->vid), array('name' => $vocabulary1->name)))->vid, $vocabulary1->vid, 'Vocabulary loaded successfully by name and ID.');
  298. }
  299. /**
  300. * Tests that machine name changes are properly reflected.
  301. */
  302. function testTaxonomyVocabularyChangeMachineName() {
  303. // Add a field instance to the vocabulary.
  304. $field = array(
  305. 'field_name' => 'field_test',
  306. 'type' => 'test_field',
  307. );
  308. field_create_field($field);
  309. $instance = array(
  310. 'field_name' => 'field_test',
  311. 'entity_type' => 'taxonomy_term',
  312. 'bundle' => $this->vocabulary->machine_name,
  313. );
  314. field_create_instance($instance);
  315. // Change the machine name.
  316. $new_name = drupal_strtolower($this->randomName());
  317. $this->vocabulary->machine_name = $new_name;
  318. taxonomy_vocabulary_save($this->vocabulary);
  319. // Check that the field instance is still attached to the vocabulary.
  320. $this->assertTrue(field_info_instance('taxonomy_term', 'field_test', $new_name), 'The bundle name was updated correctly.');
  321. }
  322. /**
  323. * Test uninstall and reinstall of the taxonomy module.
  324. */
  325. function testUninstallReinstall() {
  326. // Fields and field instances attached to taxonomy term bundles should be
  327. // removed when the module is uninstalled.
  328. $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
  329. $this->field = array('field_name' => $this->field_name, 'type' => 'text', 'cardinality' => 4);
  330. $this->field = field_create_field($this->field);
  331. $this->instance = array(
  332. 'field_name' => $this->field_name,
  333. 'entity_type' => 'taxonomy_term',
  334. 'bundle' => $this->vocabulary->machine_name,
  335. 'label' => $this->randomName() . '_label',
  336. );
  337. field_create_instance($this->instance);
  338. module_disable(array('taxonomy'));
  339. require_once DRUPAL_ROOT . '/includes/install.inc';
  340. drupal_uninstall_modules(array('taxonomy'));
  341. module_enable(array('taxonomy'));
  342. // Now create a vocabulary with the same name. All field instances
  343. // connected to this vocabulary name should have been removed when the
  344. // module was uninstalled. Creating a new field with the same name and
  345. // an instance of this field on the same bundle name should be successful.
  346. unset($this->vocabulary->vid);
  347. taxonomy_vocabulary_save($this->vocabulary);
  348. unset($this->field['id']);
  349. field_create_field($this->field);
  350. field_create_instance($this->instance);
  351. }
  352. }
  353. /**
  354. * Unit tests for taxonomy term functions.
  355. */
  356. class TaxonomyTermFunctionTestCase extends TaxonomyWebTestCase {
  357. public static function getInfo() {
  358. return array(
  359. 'name' => 'Taxonomy term unit tests',
  360. 'description' => 'Unit tests for taxonomy term functions.',
  361. 'group' => 'Taxonomy',
  362. );
  363. }
  364. function testTermDelete() {
  365. $vocabulary = $this->createVocabulary();
  366. $valid_term = $this->createTerm($vocabulary);
  367. // Delete a valid term.
  368. taxonomy_term_delete($valid_term->tid);
  369. $terms = taxonomy_term_load_multiple(array(), array('vid' => $vocabulary->vid));
  370. $this->assertTrue(empty($terms), 'Vocabulary is empty after deletion.');
  371. // Delete an invalid term. Should not throw any notices.
  372. taxonomy_term_delete(42);
  373. }
  374. /**
  375. * Test a taxonomy with terms that have multiple parents of different depths.
  376. */
  377. function testTaxonomyVocabularyTree() {
  378. // Create a new vocabulary with 6 terms.
  379. $vocabulary = $this->createVocabulary();
  380. $term = array();
  381. for ($i = 0; $i < 6; $i++) {
  382. $term[$i] = $this->createTerm($vocabulary);
  383. }
  384. // $term[2] is a child of 1 and 5.
  385. $term[2]->parent = array($term[1]->tid, $term[5]->tid);
  386. taxonomy_term_save($term[2]);
  387. // $term[3] is a child of 2.
  388. $term[3]->parent = array($term[2]->tid);
  389. taxonomy_term_save($term[3]);
  390. // $term[5] is a child of 4.
  391. $term[5]->parent = array($term[4]->tid);
  392. taxonomy_term_save($term[5]);
  393. /**
  394. * Expected tree:
  395. * term[0] | depth: 0
  396. * term[1] | depth: 0
  397. * -- term[2] | depth: 1
  398. * ---- term[3] | depth: 2
  399. * term[4] | depth: 0
  400. * -- term[5] | depth: 1
  401. * ---- term[2] | depth: 2
  402. * ------ term[3] | depth: 3
  403. */
  404. // Count $term[1] parents with $max_depth = 1.
  405. $tree = taxonomy_get_tree($vocabulary->vid, $term[1]->tid, 1);
  406. $this->assertEqual(1, count($tree), 'We have one parent with depth 1.');
  407. // Count all vocabulary tree elements.
  408. $tree = taxonomy_get_tree($vocabulary->vid);
  409. $this->assertEqual(8, count($tree), 'We have all vocabulary tree elements.');
  410. // Count elements in every tree depth.
  411. foreach ($tree as $element) {
  412. if (!isset($depth_count[$element->depth])) {
  413. $depth_count[$element->depth] = 0;
  414. }
  415. $depth_count[$element->depth]++;
  416. }
  417. $this->assertEqual(3, $depth_count[0], 'Three elements in taxonomy tree depth 0.');
  418. $this->assertEqual(2, $depth_count[1], 'Two elements in taxonomy tree depth 1.');
  419. $this->assertEqual(2, $depth_count[2], 'Two elements in taxonomy tree depth 2.');
  420. $this->assertEqual(1, $depth_count[3], 'One element in taxonomy tree depth 3.');
  421. }
  422. }
  423. /**
  424. * Test for legacy node bug.
  425. */
  426. class TaxonomyLegacyTestCase extends TaxonomyWebTestCase {
  427. protected $admin_user;
  428. public static function getInfo() {
  429. return array(
  430. 'name' => 'Test for legacy node bug.',
  431. 'description' => 'Posts an article with a taxonomy term and a date prior to 1970.',
  432. 'group' => 'Taxonomy',
  433. );
  434. }
  435. function setUp() {
  436. parent::setUp('taxonomy');
  437. $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'administer nodes', 'bypass node access'));
  438. $this->drupalLogin($this->admin_user);
  439. }
  440. /**
  441. * Test taxonomy functionality with nodes prior to 1970.
  442. */
  443. function testTaxonomyLegacyNode() {
  444. // Posts an article with a taxonomy term and a date prior to 1970.
  445. $langcode = LANGUAGE_NONE;
  446. $edit = array();
  447. $edit['title'] = $this->randomName();
  448. $edit['date'] = '1969-01-01 00:00:00 -0500';
  449. $edit["body[$langcode][0][value]"] = $this->randomName();
  450. $edit["field_tags[$langcode]"] = $this->randomName();
  451. $this->drupalPost('node/add/article', $edit, t('Save'));
  452. // Checks that the node has been saved.
  453. $node = $this->drupalGetNodeByTitle($edit['title']);
  454. $this->assertEqual($node->created, strtotime($edit['date']), 'Legacy node was saved with the right date.');
  455. }
  456. }
  457. /**
  458. * Tests for taxonomy term functions.
  459. */
  460. class TaxonomyTermTestCase extends TaxonomyWebTestCase {
  461. protected $vocabulary;
  462. protected $instance;
  463. protected $admin_user;
  464. public static function getInfo() {
  465. return array(
  466. 'name' => 'Taxonomy term functions and forms.',
  467. 'description' => 'Test load, save and delete for taxonomy terms.',
  468. 'group' => 'Taxonomy',
  469. );
  470. }
  471. function setUp() {
  472. parent::setUp('taxonomy');
  473. $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access'));
  474. $this->drupalLogin($this->admin_user);
  475. $this->vocabulary = $this->createVocabulary();
  476. $field = array(
  477. 'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
  478. 'type' => 'taxonomy_term_reference',
  479. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  480. 'settings' => array(
  481. 'allowed_values' => array(
  482. array(
  483. 'vocabulary' => $this->vocabulary->machine_name,
  484. 'parent' => 0,
  485. ),
  486. ),
  487. ),
  488. );
  489. field_create_field($field);
  490. $this->instance = array(
  491. 'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
  492. 'bundle' => 'article',
  493. 'entity_type' => 'node',
  494. 'widget' => array(
  495. 'type' => 'options_select',
  496. ),
  497. 'display' => array(
  498. 'default' => array(
  499. 'type' => 'taxonomy_term_reference_link',
  500. ),
  501. ),
  502. );
  503. field_create_instance($this->instance);
  504. }
  505. /**
  506. * Test terms in a single and multiple hierarchy.
  507. */
  508. function testTaxonomyTermHierarchy() {
  509. // Create two taxonomy terms.
  510. $term1 = $this->createTerm($this->vocabulary);
  511. $term2 = $this->createTerm($this->vocabulary);
  512. // Check that hierarchy is flat.
  513. $vocabulary = taxonomy_vocabulary_load($this->vocabulary->vid);
  514. $this->assertEqual(0, $vocabulary->hierarchy, 'Vocabulary is flat.');
  515. // Edit $term2, setting $term1 as parent.
  516. $edit = array();
  517. $edit['parent[]'] = array($term1->tid);
  518. $this->drupalPost('taxonomy/term/' . $term2->tid . '/edit', $edit, t('Save'));
  519. // Check the hierarchy.
  520. $children = taxonomy_get_children($term1->tid);
  521. $parents = taxonomy_get_parents($term2->tid);
  522. $this->assertTrue(isset($children[$term2->tid]), 'Child found correctly.');
  523. $this->assertTrue(isset($parents[$term1->tid]), 'Parent found correctly.');
  524. // Load and save a term, confirming that parents are still set.
  525. $term = taxonomy_term_load($term2->tid);
  526. taxonomy_term_save($term);
  527. $parents = taxonomy_get_parents($term2->tid);
  528. $this->assertTrue(isset($parents[$term1->tid]), 'Parent found correctly.');
  529. // Create a third term and save this as a parent of term2.
  530. $term3 = $this->createTerm($this->vocabulary);
  531. $term2->parent = array($term1->tid, $term3->tid);
  532. taxonomy_term_save($term2);
  533. $parents = taxonomy_get_parents($term2->tid);
  534. $this->assertTrue(isset($parents[$term1->tid]) && isset($parents[$term3->tid]), 'Both parents found successfully.');
  535. }
  536. /**
  537. * Test that hook_node_$op implementations work correctly.
  538. *
  539. * Save & edit a node and assert that taxonomy terms are saved/loaded properly.
  540. */
  541. function testTaxonomyNode() {
  542. // Create two taxonomy terms.
  543. $term1 = $this->createTerm($this->vocabulary);
  544. $term2 = $this->createTerm($this->vocabulary);
  545. // Post an article.
  546. $edit = array();
  547. $langcode = LANGUAGE_NONE;
  548. $edit["title"] = $this->randomName();
  549. $edit["body[$langcode][0][value]"] = $this->randomName();
  550. $edit[$this->instance['field_name'] . '[' . $langcode . '][]'] = $term1->tid;
  551. $this->drupalPost('node/add/article', $edit, t('Save'));
  552. // Check that the term is displayed when the node is viewed.
  553. $node = $this->drupalGetNodeByTitle($edit["title"]);
  554. $this->drupalGet('node/' . $node->nid);
  555. $this->assertText($term1->name, 'Term is displayed when viewing the node.');
  556. // Edit the node with a different term.
  557. $edit[$this->instance['field_name'] . '[' . $langcode . '][]'] = $term2->tid;
  558. $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
  559. $this->drupalGet('node/' . $node->nid);
  560. $this->assertText($term2->name, 'Term is displayed when viewing the node.');
  561. // Preview the node.
  562. $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Preview'));
  563. $this->assertNoUniqueText($term2->name, 'Term is displayed when previewing the node.');
  564. $this->drupalPost(NULL, NULL, t('Preview'));
  565. $this->assertNoUniqueText($term2->name, 'Term is displayed when previewing the node again.');
  566. }
  567. /**
  568. * Test term creation with a free-tagging vocabulary from the node form.
  569. */
  570. function testNodeTermCreationAndDeletion() {
  571. // Enable tags in the vocabulary.
  572. $instance = $this->instance;
  573. $instance['widget'] = array('type' => 'taxonomy_autocomplete');
  574. $instance['bundle'] = 'page';
  575. field_create_instance($instance);
  576. // Prefix the terms with a letter to ensure there is no clash in the first
  577. // three letters.
  578. // @see https://www.drupal.org/node/2397691
  579. $terms = array(
  580. 'term1' => 'a' . $this->randomName(),
  581. 'term2' => 'b' . $this->randomName() . ', ' . $this->randomName(),
  582. 'term3' => 'c' . $this->randomName(),
  583. );
  584. $edit = array();
  585. $langcode = LANGUAGE_NONE;
  586. $edit["title"] = $this->randomName();
  587. $edit["body[$langcode][0][value]"] = $this->randomName();
  588. // Insert the terms in a comma separated list. Vocabulary 1 is a
  589. // free-tagging field created by the default profile.
  590. $edit[$instance['field_name'] . "[$langcode]"] = drupal_implode_tags($terms);
  591. // Preview and verify the terms appear but are not created.
  592. $this->drupalPost('node/add/page', $edit, t('Preview'));
  593. foreach ($terms as $term) {
  594. $this->assertText($term, 'The term appears on the node preview.');
  595. }
  596. $tree = taxonomy_get_tree($this->vocabulary->vid);
  597. $this->assertTrue(empty($tree), 'The terms are not created on preview.');
  598. // taxonomy.module does not maintain its static caches.
  599. drupal_static_reset();
  600. // Save, creating the terms.
  601. $this->drupalPost('node/add/page', $edit, t('Save'));
  602. $this->assertRaw(t('@type %title has been created.', array('@type' => t('Basic page'), '%title' => $edit["title"])), 'The node was created successfully.');
  603. foreach ($terms as $term) {
  604. $this->assertText($term, 'The term was saved and appears on the node page.');
  605. }
  606. // Get the created terms.
  607. $term_objects = array();
  608. foreach ($terms as $key => $term) {
  609. $term_objects[$key] = taxonomy_get_term_by_name($term);
  610. $term_objects[$key] = reset($term_objects[$key]);
  611. }
  612. // Test editing the node.
  613. $node = $this->drupalGetNodeByTitle($edit["title"]);
  614. $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
  615. foreach ($terms as $term) {
  616. $this->assertText($term, 'The term was retained after edit and still appears on the node page.');
  617. }
  618. // Delete term 1.
  619. $this->drupalPost('taxonomy/term/' . $term_objects['term1']->tid . '/edit', array(), t('Delete'));
  620. $this->drupalPost(NULL, NULL, t('Delete'));
  621. $term_names = array($term_objects['term2']->name, $term_objects['term3']->name);
  622. // Get the node and verify that the terms that should be there still are.
  623. $this->drupalGet('node/' . $node->nid);
  624. foreach ($term_names as $term_name) {
  625. $this->assertText($term_name, format_string('The term %name appears on the node page after one term %deleted was deleted', array('%name' => $term_name, '%deleted' => $term_objects['term1']->name)));
  626. }
  627. $this->assertNoText($term_objects['term1']->name, format_string('The deleted term %name does not appear on the node page.', array('%name' => $term_objects['term1']->name)));
  628. // Test autocomplete on term 2, which contains a comma.
  629. // The term will be quoted, and the " will be encoded in unicode (\u0022).
  630. $input = substr($term_objects['term2']->name, 0, 3);
  631. $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name . '/' . $input);
  632. $this->assertRaw('{"\u0022' . $term_objects['term2']->name . '\u0022":"' . $term_objects['term2']->name . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term2']->name)));
  633. // Test autocomplete on term 3 - it is alphanumeric only, so no extra
  634. // quoting.
  635. $input = substr($term_objects['term3']->name, 0, 3);
  636. $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name . '/' . $input);
  637. $this->assertRaw('{"' . $term_objects['term3']->name . '":"' . $term_objects['term3']->name . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->name)));
  638. // Test taxonomy autocomplete with a nonexistent field.
  639. $field_name = $this->randomName();
  640. $tag = $this->randomName();
  641. $message = t("Taxonomy field @field_name not found.", array('@field_name' => $field_name));
  642. $this->assertFalse(field_info_field($field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name)));
  643. $this->drupalGet('taxonomy/autocomplete/' . $field_name . '/' . $tag);
  644. $this->assertRaw($message, 'Autocomplete returns correct error message when the taxonomy field does not exist.');
  645. // Test the autocomplete path without passing a field_name and terms.
  646. // This should not trigger a PHP notice.
  647. $field_name = '';
  648. $message = t("Taxonomy field @field_name not found.", array('@field_name' => $field_name));
  649. $this->drupalGet('taxonomy/autocomplete');
  650. $this->assertRaw($message, 'Autocomplete returns correct error message when no taxonomy field is given.');
  651. }
  652. /**
  653. * Tests term autocompletion edge cases with slashes in the names.
  654. */
  655. function testTermAutocompletion() {
  656. // Add a term with a slash in the name.
  657. $first_term = $this->createTerm($this->vocabulary);
  658. $first_term->name = '10/16/2011';
  659. taxonomy_term_save($first_term);
  660. // Add another term that differs after the slash character.
  661. $second_term = $this->createTerm($this->vocabulary);
  662. $second_term->name = '10/17/2011';
  663. taxonomy_term_save($second_term);
  664. // Add another term that has both a comma and a slash character.
  665. $third_term = $this->createTerm($this->vocabulary);
  666. $third_term->name = 'term with, a comma and / a slash';
  667. taxonomy_term_save($third_term);
  668. // Try to autocomplete a term name that matches both terms.
  669. // We should get both term in a json encoded string.
  670. $input = '10/';
  671. $path = 'taxonomy/autocomplete/taxonomy_';
  672. $path .= $this->vocabulary->machine_name . '/' . $input;
  673. // The result order is not guaranteed, so check each term separately.
  674. $url = url($path, array('absolute' => TRUE));
  675. $result = drupal_http_request($url);
  676. $data = drupal_json_decode($result->data);
  677. $this->assertEqual($data[$first_term->name], check_plain($first_term->name), 'Autocomplete returned the first matching term.');
  678. $this->assertEqual($data[$second_term->name], check_plain($second_term->name), 'Autocomplete returned the second matching term.');
  679. // Try to autocomplete a term name that matches first term.
  680. // We should only get the first term in a json encoded string.
  681. $input = '10/16';
  682. $url = 'taxonomy/autocomplete/taxonomy_';
  683. $url .= $this->vocabulary->machine_name . '/' . $input;
  684. $this->drupalGet($url);
  685. $target = array($first_term->name => check_plain($first_term->name));
  686. $this->assertRaw(drupal_json_encode($target), 'Autocomplete returns only the expected matching term.');
  687. // Try to autocomplete a term name with both a comma and a slash.
  688. $input = '"term with, comma and / a';
  689. $url = 'taxonomy/autocomplete/taxonomy_';
  690. $url .= $this->vocabulary->machine_name . '/' . $input;
  691. $this->drupalGet($url);
  692. $n = $third_term->name;
  693. // Term names containing commas or quotes must be wrapped in quotes.
  694. if (strpos($third_term->name, ',') !== FALSE || strpos($third_term->name, '"') !== FALSE) {
  695. $n = '"' . str_replace('"', '""', $third_term->name) . '"';
  696. }
  697. $target = array($n => check_plain($third_term->name));
  698. $this->assertRaw(drupal_json_encode($target), 'Autocomplete returns a term containing a comma and a slash.');
  699. }
  700. /**
  701. * Save, edit and delete a term using the user interface.
  702. */
  703. function testTermInterface() {
  704. $edit = array(
  705. 'name' => $this->randomName(12),
  706. 'description[value]' => $this->randomName(100),
  707. );
  708. // Explicitly set the parents field to 'root', to ensure that
  709. // taxonomy_form_term_submit() handles the invalid term ID correctly.
  710. $edit['parent[]'] = array(0);
  711. // Create the term to edit.
  712. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', $edit, t('Save'));
  713. $terms = taxonomy_get_term_by_name($edit['name']);
  714. $term = reset($terms);
  715. $this->assertNotNull($term, 'Term found in database.');
  716. // Submitting a term takes us to the add page; we need the List page.
  717. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name);
  718. // Test edit link as accessed from Taxonomy administration pages.
  719. // Because Simpletest creates its own database when running tests, we know
  720. // the first edit link found on the listing page is to our term.
  721. $this->clickLink(t('edit'));
  722. $this->assertRaw($edit['name'], 'The randomly generated term name is present.');
  723. $this->assertText($edit['description[value]'], 'The randomly generated term description is present.');
  724. $edit = array(
  725. 'name' => $this->randomName(14),
  726. 'description[value]' => $this->randomName(102),
  727. );
  728. // Edit the term.
  729. $this->drupalPost('taxonomy/term/' . $term->tid . '/edit', $edit, t('Save'));
  730. // Check that the term is still present at admin UI after edit.
  731. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name);
  732. $this->assertText($edit['name'], 'The randomly generated term name is present.');
  733. $this->assertLink(t('edit'));
  734. // View the term and check that it is correct.
  735. $this->drupalGet('taxonomy/term/' . $term->tid);
  736. $this->assertText($edit['name'], 'The randomly generated term name is present.');
  737. $this->assertText($edit['description[value]'], 'The randomly generated term description is present.');
  738. // Did this page request display a 'term-listing-heading'?
  739. $this->assertPattern('|class="taxonomy-term-description"|', 'Term page displayed the term description element.');
  740. // Check that it does NOT show a description when description is blank.
  741. $term->description = '';
  742. taxonomy_term_save($term);
  743. $this->drupalGet('taxonomy/term/' . $term->tid);
  744. $this->assertNoPattern('|class="taxonomy-term-description"|', 'Term page did not display the term description when description was blank.');
  745. // Check that the term feed page is working.
  746. $this->drupalGet('taxonomy/term/' . $term->tid . '/feed');
  747. // Check that the term edit page does not try to interpret additional path
  748. // components as arguments for taxonomy_form_term().
  749. $this->drupalGet('taxonomy/term/' . $term->tid . '/edit/' . $this->randomName());
  750. // Delete the term.
  751. $this->drupalPost('taxonomy/term/' . $term->tid . '/edit', array(), t('Delete'));
  752. $this->drupalPost(NULL, NULL, t('Delete'));
  753. // Assert that the term no longer exists.
  754. $this->drupalGet('taxonomy/term/' . $term->tid);
  755. $this->assertResponse(404, 'The taxonomy term page was not found.');
  756. }
  757. /**
  758. * Save, edit and delete a term using the user interface.
  759. */
  760. function testTermReorder() {
  761. $this->createTerm($this->vocabulary);
  762. $this->createTerm($this->vocabulary);
  763. $this->createTerm($this->vocabulary);
  764. // Fetch the created terms in the default alphabetical order, i.e. term1
  765. // precedes term2 alphabetically, and term2 precedes term3.
  766. drupal_static_reset('taxonomy_get_tree');
  767. drupal_static_reset('taxonomy_get_treeparent');
  768. drupal_static_reset('taxonomy_get_treeterms');
  769. list($term1, $term2, $term3) = taxonomy_get_tree($this->vocabulary->vid);
  770. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name);
  771. // Each term has four hidden fields, "tid:1:0[tid]", "tid:1:0[parent]",
  772. // "tid:1:0[depth]", and "tid:1:0[weight]". Change the order to term2,
  773. // term3, term1 by setting weight property, make term3 a child of term2 by
  774. // setting the parent and depth properties, and update all hidden fields.
  775. $edit = array(
  776. 'tid:' . $term2->tid . ':0[tid]' => $term2->tid,
  777. 'tid:' . $term2->tid . ':0[parent]' => 0,
  778. 'tid:' . $term2->tid . ':0[depth]' => 0,
  779. 'tid:' . $term2->tid . ':0[weight]' => 0,
  780. 'tid:' . $term3->tid . ':0[tid]' => $term3->tid,
  781. 'tid:' . $term3->tid . ':0[parent]' => $term2->tid,
  782. 'tid:' . $term3->tid . ':0[depth]' => 1,
  783. 'tid:' . $term3->tid . ':0[weight]' => 1,
  784. 'tid:' . $term1->tid . ':0[tid]' => $term1->tid,
  785. 'tid:' . $term1->tid . ':0[parent]' => 0,
  786. 'tid:' . $term1->tid . ':0[depth]' => 0,
  787. 'tid:' . $term1->tid . ':0[weight]' => 2,
  788. );
  789. $this->drupalPost(NULL, $edit, t('Save'));
  790. drupal_static_reset('taxonomy_get_tree');
  791. drupal_static_reset('taxonomy_get_treeparent');
  792. drupal_static_reset('taxonomy_get_treeterms');
  793. $terms = taxonomy_get_tree($this->vocabulary->vid);
  794. $this->assertEqual($terms[0]->tid, $term2->tid, 'Term 2 was moved above term 1.');
  795. $this->assertEqual($terms[1]->parents, array($term2->tid), 'Term 3 was made a child of term 2.');
  796. $this->assertEqual($terms[2]->tid, $term1->tid, 'Term 1 was moved below term 2.');
  797. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name, array(), t('Reset to alphabetical'));
  798. // Submit confirmation form.
  799. $this->drupalPost(NULL, array(), t('Reset to alphabetical'));
  800. drupal_static_reset('taxonomy_get_tree');
  801. drupal_static_reset('taxonomy_get_treeparent');
  802. drupal_static_reset('taxonomy_get_treeterms');
  803. $terms = taxonomy_get_tree($this->vocabulary->vid);
  804. $this->assertEqual($terms[0]->tid, $term1->tid, 'Term 1 was moved to back above term 2.');
  805. $this->assertEqual($terms[1]->tid, $term2->tid, 'Term 2 was moved to back below term 1.');
  806. $this->assertEqual($terms[2]->tid, $term3->tid, 'Term 3 is still below term 2.');
  807. $this->assertEqual($terms[2]->parents, array($term2->tid), 'Term 3 is still a child of term 2.' . var_export($terms[1]->tid, 1));
  808. }
  809. /**
  810. * Test saving a term with multiple parents through the UI.
  811. */
  812. function testTermMultipleParentsInterface() {
  813. // Add a new term to the vocabulary so that we can have multiple parents.
  814. $parent = $this->createTerm($this->vocabulary);
  815. // Add a new term with multiple parents.
  816. $edit = array(
  817. 'name' => $this->randomName(12),
  818. 'description[value]' => $this->randomName(100),
  819. 'parent[]' => array(0, $parent->tid),
  820. );
  821. // Save the new term.
  822. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', $edit, t('Save'));
  823. // Check that the term was successfully created.
  824. $terms = taxonomy_get_term_by_name($edit['name']);
  825. $term = reset($terms);
  826. $this->assertNotNull($term, 'Term found in database.');
  827. $this->assertEqual($edit['name'], $term->name, 'Term name was successfully saved.');
  828. $this->assertEqual($edit['description[value]'], $term->description, 'Term description was successfully saved.');
  829. // Check that the parent tid is still there. The other parent (<root>) is
  830. // not added by taxonomy_get_parents().
  831. $parents = taxonomy_get_parents($term->tid);
  832. $parent = reset($parents);
  833. $this->assertEqual($edit['parent[]'][1], $parent->tid, 'Term parents were successfully saved.');
  834. }
  835. /**
  836. * Test taxonomy_get_term_by_name().
  837. */
  838. function testTaxonomyGetTermByName() {
  839. $term = $this->createTerm($this->vocabulary);
  840. // Load the term with the exact name.
  841. $terms = taxonomy_get_term_by_name($term->name);
  842. $this->assertTrue(isset($terms[$term->tid]), 'Term loaded using exact name.');
  843. // Load the term with space concatenated.
  844. $terms = taxonomy_get_term_by_name(' ' . $term->name . ' ');
  845. $this->assertTrue(isset($terms[$term->tid]), 'Term loaded with extra whitespace.');
  846. // Load the term with name uppercased.
  847. $terms = taxonomy_get_term_by_name(strtoupper($term->name));
  848. $this->assertTrue(isset($terms[$term->tid]), 'Term loaded with uppercased name.');
  849. // Load the term with name lowercased.
  850. $terms = taxonomy_get_term_by_name(strtolower($term->name));
  851. $this->assertTrue(isset($terms[$term->tid]), 'Term loaded with lowercased name.');
  852. // Try to load an invalid term name.
  853. $terms = taxonomy_get_term_by_name('Banana');
  854. $this->assertFalse($terms);
  855. // Try to load the term using a substring of the name.
  856. $terms = taxonomy_get_term_by_name(drupal_substr($term->name, 2));
  857. $this->assertFalse($terms);
  858. // Create a new term in a different vocabulary with the same name.
  859. $new_vocabulary = $this->createVocabulary();
  860. $new_term = new stdClass();
  861. $new_term->name = $term->name;
  862. $new_term->vid = $new_vocabulary->vid;
  863. taxonomy_term_save($new_term);
  864. // Load multiple terms with the same name.
  865. $terms = taxonomy_get_term_by_name($term->name);
  866. $this->assertEqual(count($terms), 2, 'Two terms loaded with the same name.');
  867. // Load single term when restricted to one vocabulary.
  868. $terms = taxonomy_get_term_by_name($term->name, $this->vocabulary->machine_name);
  869. $this->assertEqual(count($terms), 1, 'One term loaded when restricted by vocabulary.');
  870. $this->assertTrue(isset($terms[$term->tid]), 'Term loaded using exact name and vocabulary machine name.');
  871. // Create a new term with another name.
  872. $term2 = $this->createTerm($this->vocabulary);
  873. // Try to load a term by name that doesn't exist in this vocabulary but
  874. // exists in another vocabulary.
  875. $terms = taxonomy_get_term_by_name($term2->name, $new_vocabulary->machine_name);
  876. $this->assertFalse($terms, 'Invalid term name restricted by vocabulary machine name not loaded.');
  877. // Try to load terms filtering by a non-existing vocabulary.
  878. $terms = taxonomy_get_term_by_name($term2->name, 'non_existing_vocabulary');
  879. $this->assertEqual(count($terms), 0, 'No terms loaded when restricted by a non-existing vocabulary.');
  880. }
  881. /**
  882. * Tests that taxonomy term detail page is working even after the default
  883. * taxonomy_select_nodes() query is altered.
  884. */
  885. public function testTaxonomySelectNodesAlter() {
  886. // Create a new term.
  887. $term = $this->createTerm($this->vocabulary);
  888. // Create an article.
  889. $settings = array(
  890. 'type' => 'article',
  891. $this->instance['field_name'] => array(LANGUAGE_NONE => array(array('tid' => $term->tid))),
  892. );
  893. $this->drupalCreateNode($settings);
  894. // Check if the taxonomy term detail page is working.
  895. module_enable(array('taxonomy_nodes_test'));
  896. variable_set('taxonomy_nodes_test_query_node_access_alter', TRUE);
  897. $this->drupalGet('taxonomy/term/' . $term->tid);
  898. $this->assertResponse(200, 'The taxonomy term page is working.');
  899. variable_set('taxonomy_nodes_test_query_node_access_alter', FALSE);
  900. }
  901. /**
  902. * Test multiple forms on taxonomy terms overview page.
  903. */
  904. function testTaxonomyTermsOverviewPage() {
  905. // Enable block with custom form on taxonomy terms overview page.
  906. module_enable(array('taxonomy_test'));
  907. $this->drupalLogout();
  908. $admin_user1 = $this->drupalCreateUser(array('administer taxonomy', 'administer blocks', 'bypass node access'));
  909. $this->drupalLogin($admin_user1);
  910. $this->createTerm($this->vocabulary);
  911. $this->createTerm($this->vocabulary);
  912. $edit = array();
  913. $edit['blocks[taxonomy_test_test_block_form][region]'] = 'header';
  914. $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
  915. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name);
  916. $this->assertText('Simple form', 'Block successfully being displayed on the page.');
  917. // Try to submit the custom form to verify there are no errors.
  918. $this->drupalPost(NULL, array(), t('Submit'));
  919. }
  920. }
  921. /**
  922. * Tests the rendering of term reference fields in RSS feeds.
  923. */
  924. class TaxonomyRSSTestCase extends TaxonomyWebTestCase {
  925. protected $vocabulary;
  926. protected $instance;
  927. protected $admin_user;
  928. public static function getInfo() {
  929. return array(
  930. 'name' => 'Taxonomy RSS Content.',
  931. 'description' => 'Ensure that data added as terms appears in RSS feeds if "RSS Category" format is selected.',
  932. 'group' => 'Taxonomy',
  933. );
  934. }
  935. function setUp() {
  936. parent::setUp('taxonomy');
  937. $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access', 'administer content types', 'administer fields'));
  938. $this->drupalLogin($this->admin_user);
  939. $this->vocabulary = $this->createVocabulary();
  940. $field = array(
  941. 'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
  942. 'type' => 'taxonomy_term_reference',
  943. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  944. 'settings' => array(
  945. 'allowed_values' => array(
  946. array(
  947. 'vocabulary' => $this->vocabulary->machine_name,
  948. 'parent' => 0,
  949. ),
  950. ),
  951. ),
  952. );
  953. field_create_field($field);
  954. $this->instance = array(
  955. 'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
  956. 'bundle' => 'article',
  957. 'entity_type' => 'node',
  958. 'widget' => array(
  959. 'type' => 'options_select',
  960. ),
  961. 'display' => array(
  962. 'default' => array(
  963. 'type' => 'taxonomy_term_reference_link',
  964. ),
  965. ),
  966. );
  967. field_create_instance($this->instance);
  968. }
  969. /**
  970. * Tests that terms added to nodes are displayed in core RSS feed.
  971. *
  972. * Create a node and assert that taxonomy terms appear in rss.xml.
  973. */
  974. function testTaxonomyRSS() {
  975. // Create two taxonomy terms.
  976. $term1 = $this->createTerm($this->vocabulary);
  977. // RSS display must be added manually.
  978. $this->drupalGet("admin/structure/types/manage/article/display");
  979. $edit = array(
  980. "view_modes_custom[rss]" => '1',
  981. );
  982. $this->drupalPost(NULL, $edit, t('Save'));
  983. // Change the format to 'RSS category'.
  984. $this->drupalGet("admin/structure/types/manage/article/display/rss");
  985. $edit = array(
  986. "fields[taxonomy_" . $this->vocabulary->machine_name . "][type]" => 'taxonomy_term_reference_rss_category',
  987. );
  988. $this->drupalPost(NULL, $edit, t('Save'));
  989. // Post an article.
  990. $edit = array();
  991. $langcode = LANGUAGE_NONE;
  992. $edit["title"] = $this->randomName();
  993. $edit[$this->instance['field_name'] . '[' . $langcode . '][]'] = $term1->tid;
  994. $this->drupalPost('node/add/article', $edit, t('Save'));
  995. // Check that the term is displayed when the RSS feed is viewed.
  996. $this->drupalGet('rss.xml');
  997. $test_element = array(
  998. 'key' => 'category',
  999. 'value' => $term1->name,
  1000. 'attributes' => array(
  1001. 'domain' => url('taxonomy/term/' . $term1->tid, array('absolute' => TRUE)),
  1002. ),
  1003. );
  1004. $this->assertRaw(format_xml_elements(array($test_element)), 'Term is displayed when viewing the rss feed.');
  1005. }
  1006. }
  1007. /**
  1008. * Tests the hook implementations that maintain the taxonomy index.
  1009. */
  1010. class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase {
  1011. protected $vocabulary;
  1012. protected $instance_1;
  1013. protected $field_name_1;
  1014. protected $field_1;
  1015. protected $instance_2;
  1016. protected $field_name_2;
  1017. protected $field_2;
  1018. protected $admin_user;
  1019. public static function getInfo() {
  1020. return array(
  1021. 'name' => 'Taxonomy term index',
  1022. 'description' => 'Tests the hook implementations that maintain the taxonomy index.',
  1023. 'group' => 'Taxonomy',
  1024. );
  1025. }
  1026. function setUp() {
  1027. parent::setUp('taxonomy');
  1028. // Create an administrative user.
  1029. $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access'));
  1030. $this->drupalLogin($this->admin_user);
  1031. // Create a vocabulary and add two term reference fields to article nodes.
  1032. $this->vocabulary = $this->createVocabulary();
  1033. $this->field_name_1 = drupal_strtolower($this->randomName());
  1034. $this->field_1 = array(
  1035. 'field_name' => $this->field_name_1,
  1036. 'type' => 'taxonomy_term_reference',
  1037. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  1038. 'settings' => array(
  1039. 'allowed_values' => array(
  1040. array(
  1041. 'vocabulary' => $this->vocabulary->machine_name,
  1042. 'parent' => 0,
  1043. ),
  1044. ),
  1045. ),
  1046. );
  1047. field_create_field($this->field_1);
  1048. $this->instance_1 = array(
  1049. 'field_name' => $this->field_name_1,
  1050. 'bundle' => 'article',
  1051. 'entity_type' => 'node',
  1052. 'widget' => array(
  1053. 'type' => 'options_select',
  1054. ),
  1055. 'display' => array(
  1056. 'default' => array(
  1057. 'type' => 'taxonomy_term_reference_link',
  1058. ),
  1059. ),
  1060. );
  1061. field_create_instance($this->instance_1);
  1062. $this->field_name_2 = drupal_strtolower($this->randomName());
  1063. $this->field_2 = array(
  1064. 'field_name' => $this->field_name_2,
  1065. 'type' => 'taxonomy_term_reference',
  1066. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  1067. 'settings' => array(
  1068. 'allowed_values' => array(
  1069. array(
  1070. 'vocabulary' => $this->vocabulary->machine_name,
  1071. 'parent' => 0,
  1072. ),
  1073. ),
  1074. ),
  1075. );
  1076. field_create_field($this->field_2);
  1077. $this->instance_2 = array(
  1078. 'field_name' => $this->field_name_2,
  1079. 'bundle' => 'article',
  1080. 'entity_type' => 'node',
  1081. 'widget' => array(
  1082. 'type' => 'options_select',
  1083. ),
  1084. 'display' => array(
  1085. 'default' => array(
  1086. 'type' => 'taxonomy_term_reference_link',
  1087. ),
  1088. ),
  1089. );
  1090. field_create_instance($this->instance_2);
  1091. }
  1092. /**
  1093. * Tests that the taxonomy index is maintained properly.
  1094. */
  1095. function testTaxonomyIndex() {
  1096. // Create terms in the vocabulary.
  1097. $term_1 = $this->createTerm($this->vocabulary);
  1098. $term_2 = $this->createTerm($this->vocabulary);
  1099. // Post an article.
  1100. $edit = array();
  1101. $langcode = LANGUAGE_NONE;
  1102. $edit["title"] = $this->randomName();
  1103. $edit["body[$langcode][0][value]"] = $this->randomName();
  1104. $edit["{$this->field_name_1}[$langcode][]"] = $term_1->tid;
  1105. $edit["{$this->field_name_2}[$langcode][]"] = $term_1->tid;
  1106. $this->drupalPost('node/add/article', $edit, t('Save'));
  1107. // Check that the term is indexed, and only once.
  1108. $node = $this->drupalGetNodeByTitle($edit["title"]);
  1109. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1110. ':nid' => $node->nid,
  1111. ':tid' => $term_1->tid,
  1112. ))->fetchField();
  1113. $this->assertEqual(1, $index_count, 'Term 1 is indexed once.');
  1114. // Update the article to change one term.
  1115. $edit["{$this->field_name_1}[$langcode][]"] = $term_2->tid;
  1116. $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
  1117. // Check that both terms are indexed.
  1118. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1119. ':nid' => $node->nid,
  1120. ':tid' => $term_1->tid,
  1121. ))->fetchField();
  1122. $this->assertEqual(1, $index_count, 'Term 1 is indexed.');
  1123. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1124. ':nid' => $node->nid,
  1125. ':tid' => $term_2->tid,
  1126. ))->fetchField();
  1127. $this->assertEqual(1, $index_count, 'Term 2 is indexed.');
  1128. // Update the article to change another term.
  1129. $edit["{$this->field_name_2}[$langcode][]"] = $term_2->tid;
  1130. $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
  1131. // Check that only one term is indexed.
  1132. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1133. ':nid' => $node->nid,
  1134. ':tid' => $term_1->tid,
  1135. ))->fetchField();
  1136. $this->assertEqual(0, $index_count, 'Term 1 is not indexed.');
  1137. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1138. ':nid' => $node->nid,
  1139. ':tid' => $term_2->tid,
  1140. ))->fetchField();
  1141. $this->assertEqual(1, $index_count, 'Term 2 is indexed once.');
  1142. // Redo the above tests without interface.
  1143. $update_node = array(
  1144. 'nid' => $node->nid,
  1145. 'vid' => $node->vid,
  1146. 'uid' => $node->uid,
  1147. 'type' => $node->type,
  1148. 'title' => $this->randomName(),
  1149. );
  1150. // Update the article with no term changed.
  1151. $updated_node = (object) $update_node;
  1152. node_save($updated_node);
  1153. // Check that the index was not changed.
  1154. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1155. ':nid' => $node->nid,
  1156. ':tid' => $term_1->tid,
  1157. ))->fetchField();
  1158. $this->assertEqual(0, $index_count, 'Term 1 is not indexed.');
  1159. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1160. ':nid' => $node->nid,
  1161. ':tid' => $term_2->tid,
  1162. ))->fetchField();
  1163. $this->assertEqual(1, $index_count, 'Term 2 is indexed once.');
  1164. // Update the article to change one term.
  1165. $update_node[$this->field_name_1][$langcode] = array(array('tid' => $term_1->tid));
  1166. $updated_node = (object) $update_node;
  1167. node_save($updated_node);
  1168. // Check that both terms are indexed.
  1169. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1170. ':nid' => $node->nid,
  1171. ':tid' => $term_1->tid,
  1172. ))->fetchField();
  1173. $this->assertEqual(1, $index_count, 'Term 1 is indexed.');
  1174. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1175. ':nid' => $node->nid,
  1176. ':tid' => $term_2->tid,
  1177. ))->fetchField();
  1178. $this->assertEqual(1, $index_count, 'Term 2 is indexed.');
  1179. // Update the article to change another term.
  1180. $update_node[$this->field_name_2][$langcode] = array(array('tid' => $term_1->tid));
  1181. $updated_node = (object) $update_node;
  1182. node_save($updated_node);
  1183. // Check that only one term is indexed.
  1184. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1185. ':nid' => $node->nid,
  1186. ':tid' => $term_1->tid,
  1187. ))->fetchField();
  1188. $this->assertEqual(1, $index_count, 'Term 1 is indexed once.');
  1189. $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array(
  1190. ':nid' => $node->nid,
  1191. ':tid' => $term_2->tid,
  1192. ))->fetchField();
  1193. $this->assertEqual(0, $index_count, 'Term 2 is not indexed.');
  1194. }
  1195. /**
  1196. * Tests that there is a link to the parent term on the child term page.
  1197. */
  1198. function testTaxonomyTermHierarchyBreadcrumbs() {
  1199. // Create two taxonomy terms and set term2 as the parent of term1.
  1200. $term1 = $this->createTerm($this->vocabulary);
  1201. $term2 = $this->createTerm($this->vocabulary);
  1202. $term1->parent = array($term2->tid);
  1203. taxonomy_term_save($term1);
  1204. // Verify that the page breadcrumbs include a link to the parent term.
  1205. $this->drupalGet('taxonomy/term/' . $term1->tid);
  1206. $this->assertRaw(l($term2->name, 'taxonomy/term/' . $term2->tid), 'Parent term link is displayed when viewing the node.');
  1207. }
  1208. }
  1209. /**
  1210. * Test the taxonomy_term_load_multiple() function.
  1211. */
  1212. class TaxonomyLoadMultipleTestCase extends TaxonomyWebTestCase {
  1213. protected $taxonomy_admin;
  1214. public static function getInfo() {
  1215. return array(
  1216. 'name' => 'Taxonomy term multiple loading',
  1217. 'description' => 'Test the loading of multiple taxonomy terms at once',
  1218. 'group' => 'Taxonomy',
  1219. );
  1220. }
  1221. function setUp() {
  1222. parent::setUp();
  1223. $this->taxonomy_admin = $this->drupalCreateUser(array('administer taxonomy'));
  1224. $this->drupalLogin($this->taxonomy_admin);
  1225. }
  1226. /**
  1227. * Create a vocabulary and some taxonomy terms, ensuring they're loaded
  1228. * correctly using taxonomy_term_load_multiple().
  1229. */
  1230. function testTaxonomyTermMultipleLoad() {
  1231. // Create a vocabulary.
  1232. $vocabulary = $this->createVocabulary();
  1233. // Create five terms in the vocabulary.
  1234. $i = 0;
  1235. while ($i < 5) {
  1236. $i++;
  1237. $this->createTerm($vocabulary);
  1238. }
  1239. // Load the terms from the vocabulary.
  1240. $terms = taxonomy_term_load_multiple(NULL, array('vid' => $vocabulary->vid));
  1241. $count = count($terms);
  1242. $this->assertEqual($count, 5, format_string('Correct number of terms were loaded. !count terms.', array('!count' => $count)));
  1243. // Load the same terms again by tid.
  1244. $terms2 = taxonomy_term_load_multiple(array_keys($terms));
  1245. $this->assertEqual($count, count($terms2), 'Five terms were loaded by tid.');
  1246. $this->assertEqual($terms, $terms2, 'Both arrays contain the same terms.');
  1247. // Load the terms by tid, with a condition on vid.
  1248. $terms3 = taxonomy_term_load_multiple(array_keys($terms2), array('vid' => $vocabulary->vid));
  1249. $this->assertEqual($terms2, $terms3);
  1250. // Remove one term from the array, then delete it.
  1251. $deleted = array_shift($terms3);
  1252. taxonomy_term_delete($deleted->tid);
  1253. $deleted_term = taxonomy_term_load($deleted->tid);
  1254. $this->assertFalse($deleted_term);
  1255. // Load terms from the vocabulary by vid.
  1256. $terms4 = taxonomy_term_load_multiple(NULL, array('vid' => $vocabulary->vid));
  1257. $this->assertEqual(count($terms4), 4, 'Correct number of terms were loaded.');
  1258. $this->assertFalse(isset($terms4[$deleted->tid]));
  1259. // Create a single term and load it by name.
  1260. $term = $this->createTerm($vocabulary);
  1261. $loaded_terms = taxonomy_term_load_multiple(array(), array('name' => $term->name));
  1262. $this->assertEqual(count($loaded_terms), 1, 'One term was loaded.');
  1263. $loaded_term = reset($loaded_terms);
  1264. $this->assertEqual($term->tid, $loaded_term->tid, 'Term loaded by name successfully.');
  1265. }
  1266. }
  1267. /**
  1268. * Tests for taxonomy hook invocation.
  1269. */
  1270. class TaxonomyHooksTestCase extends TaxonomyWebTestCase {
  1271. public static function getInfo() {
  1272. return array(
  1273. 'name' => 'Taxonomy term hooks',
  1274. 'description' => 'Hooks for taxonomy term load/save/delete.',
  1275. 'group' => 'Taxonomy',
  1276. );
  1277. }
  1278. function setUp() {
  1279. parent::setUp('taxonomy', 'taxonomy_test');
  1280. module_load_include('inc', 'taxonomy', 'taxonomy.pages');
  1281. $taxonomy_admin = $this->drupalCreateUser(array('administer taxonomy'));
  1282. $this->drupalLogin($taxonomy_admin);
  1283. }
  1284. /**
  1285. * Test that hooks are run correctly on creating, editing, viewing,
  1286. * and deleting a term.
  1287. *
  1288. * @see taxonomy_test.module
  1289. */
  1290. function testTaxonomyTermHooks() {
  1291. $vocabulary = $this->createVocabulary();
  1292. // Create a term with one antonym.
  1293. $edit = array(
  1294. 'name' => $this->randomName(),
  1295. 'antonym' => 'Long',
  1296. );
  1297. $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add', $edit, t('Save'));
  1298. $terms = taxonomy_get_term_by_name($edit['name']);
  1299. $term = reset($terms);
  1300. $this->assertEqual($term->antonym, $edit['antonym'], 'Antonym was loaded into the term object.');
  1301. // Update the term with a different antonym.
  1302. $edit = array(
  1303. 'name' => $this->randomName(),
  1304. 'antonym' => 'Short',
  1305. );
  1306. $this->drupalPost('taxonomy/term/' . $term->tid . '/edit', $edit, t('Save'));
  1307. taxonomy_terms_static_reset();
  1308. $term = taxonomy_term_load($term->tid);
  1309. $this->assertEqual($edit['antonym'], $term->antonym, 'Antonym was successfully edited.');
  1310. // View the term and ensure that hook_taxonomy_term_view() and
  1311. // hook_entity_view() are invoked.
  1312. $term = taxonomy_term_load($term->tid);
  1313. $term_build = taxonomy_term_page($term);
  1314. $this->assertFalse(empty($term_build['term_heading']['term']['taxonomy_test_term_view_check']), 'hook_taxonomy_term_view() was invoked when viewing the term.');
  1315. $this->assertFalse(empty($term_build['term_heading']['term']['taxonomy_test_entity_view_check']), 'hook_entity_view() was invoked when viewing the term.');
  1316. // Delete the term.
  1317. taxonomy_term_delete($term->tid);
  1318. $antonym = db_query('SELECT tid FROM {taxonomy_term_antonym} WHERE tid = :tid', array(':tid' => $term->tid))->fetchField();
  1319. $this->assertFalse($antonym, 'The antonym were deleted from the database.');
  1320. }
  1321. }
  1322. /**
  1323. * Tests for taxonomy term field and formatter.
  1324. */
  1325. class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase {
  1326. protected $instance;
  1327. protected $vocabulary;
  1328. protected $field_name;
  1329. protected $field;
  1330. public static function getInfo() {
  1331. return array(
  1332. 'name' => 'Taxonomy term reference field',
  1333. 'description' => 'Test the creation of term fields.',
  1334. 'group' => 'Taxonomy',
  1335. );
  1336. }
  1337. function setUp() {
  1338. parent::setUp('field_test');
  1339. $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer taxonomy'));
  1340. $this->drupalLogin($web_user);
  1341. $this->vocabulary = $this->createVocabulary();
  1342. // Setup a field and instance.
  1343. $this->field_name = drupal_strtolower($this->randomName());
  1344. $this->field = array(
  1345. 'field_name' => $this->field_name,
  1346. 'type' => 'taxonomy_term_reference',
  1347. 'settings' => array(
  1348. 'allowed_values' => array(
  1349. array(
  1350. 'vocabulary' => $this->vocabulary->machine_name,
  1351. 'parent' => '0',
  1352. ),
  1353. ),
  1354. )
  1355. );
  1356. field_create_field($this->field);
  1357. $this->instance = array(
  1358. 'field_name' => $this->field_name,
  1359. 'entity_type' => 'test_entity',
  1360. 'bundle' => 'test_bundle',
  1361. 'widget' => array(
  1362. 'type' => 'options_select',
  1363. ),
  1364. 'display' => array(
  1365. 'full' => array(
  1366. 'type' => 'taxonomy_term_reference_link',
  1367. ),
  1368. ),
  1369. );
  1370. field_create_instance($this->instance);
  1371. }
  1372. /**
  1373. * Test term field validation.
  1374. */
  1375. function testTaxonomyTermFieldValidation() {
  1376. // Test valid and invalid values with field_attach_validate().
  1377. $langcode = LANGUAGE_NONE;
  1378. $entity = field_test_create_stub_entity();
  1379. $term = $this->createTerm($this->vocabulary);
  1380. $entity->{$this->field_name}[$langcode][0]['tid'] = $term->tid;
  1381. try {
  1382. field_attach_validate('test_entity', $entity);
  1383. $this->pass('Correct term does not cause validation error.');
  1384. }
  1385. catch (FieldValidationException $e) {
  1386. $this->fail('Correct term does not cause validation error.');
  1387. }
  1388. $entity = field_test_create_stub_entity();
  1389. $bad_term = $this->createTerm($this->createVocabulary());
  1390. $entity->{$this->field_name}[$langcode][0]['tid'] = $bad_term->tid;
  1391. try {
  1392. field_attach_validate('test_entity', $entity);
  1393. $this->fail('Wrong term causes validation error.');
  1394. }
  1395. catch (FieldValidationException $e) {
  1396. $this->pass('Wrong term causes validation error.');
  1397. }
  1398. }
  1399. /**
  1400. * Test widgets.
  1401. */
  1402. function testTaxonomyTermFieldWidgets() {
  1403. // Create a term in the vocabulary.
  1404. $term = $this->createTerm($this->vocabulary);
  1405. // Display creation form.
  1406. $langcode = LANGUAGE_NONE;
  1407. $this->drupalGet('test-entity/add/test-bundle');
  1408. $this->assertFieldByName("{$this->field_name}[$langcode]", '', 'Widget is displayed.');
  1409. // Submit with some value.
  1410. $edit = array(
  1411. "{$this->field_name}[$langcode]" => array($term->tid),
  1412. );
  1413. $this->drupalPost(NULL, $edit, t('Save'));
  1414. preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
  1415. $id = $match[1];
  1416. $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created.');
  1417. // Display the object.
  1418. $entity = field_test_entity_test_load($id);
  1419. $entities = array($id => $entity);
  1420. field_attach_prepare_view('test_entity', $entities, 'full');
  1421. $entity->content = field_attach_view('test_entity', $entity, 'full');
  1422. $this->content = drupal_render($entity->content);
  1423. $this->assertText($term->name, 'Term name is displayed.');
  1424. // Delete the vocabulary and verify that the widget is gone.
  1425. taxonomy_vocabulary_delete($this->vocabulary->vid);
  1426. $this->drupalGet('test-entity/add/test-bundle');
  1427. $this->assertNoFieldByName("{$this->field_name}[$langcode]", '', 'Widget is not displayed.');
  1428. }
  1429. /**
  1430. * Tests that vocabulary machine name changes are mirrored in field definitions.
  1431. */
  1432. function testTaxonomyTermFieldChangeMachineName() {
  1433. // Add several entries in the 'allowed_values' setting, to make sure that
  1434. // they all get updated.
  1435. $this->field['settings']['allowed_values'] = array(
  1436. array(
  1437. 'vocabulary' => $this->vocabulary->machine_name,
  1438. 'parent' => '0',
  1439. ),
  1440. array(
  1441. 'vocabulary' => $this->vocabulary->machine_name,
  1442. 'parent' => '0',
  1443. ),
  1444. array(
  1445. 'vocabulary' => 'foo',
  1446. 'parent' => '0',
  1447. ),
  1448. );
  1449. field_update_field($this->field);
  1450. // Change the machine name.
  1451. $old_name = $this->vocabulary->machine_name;
  1452. $new_name = drupal_strtolower($this->randomName());
  1453. $this->vocabulary->machine_name = $new_name;
  1454. taxonomy_vocabulary_save($this->vocabulary);
  1455. // Check that entity bundles are properly updated.
  1456. $info = entity_get_info('taxonomy_term');
  1457. $this->assertFalse(isset($info['bundles'][$old_name]), 'The old bundle name does not appear in entity_get_info().');
  1458. $this->assertTrue(isset($info['bundles'][$new_name]), 'The new bundle name appears in entity_get_info().');
  1459. // Check that the field instance is still attached to the vocabulary.
  1460. $field = field_info_field($this->field_name);
  1461. $allowed_values = $field['settings']['allowed_values'];
  1462. $this->assertEqual($allowed_values[0]['vocabulary'], $new_name, 'Index 0: Machine name was updated correctly.');
  1463. $this->assertEqual($allowed_values[1]['vocabulary'], $new_name, 'Index 1: Machine name was updated correctly.');
  1464. $this->assertEqual($allowed_values[2]['vocabulary'], 'foo', 'Index 2: Machine name was left untouched.');
  1465. }
  1466. /**
  1467. * Test empty taxonomy term reference field.
  1468. */
  1469. function testEmptyTaxonomyTermReferenceField() {
  1470. // Test if an empty value in the taxonomy reference field would trigger
  1471. // autocreate or if the value would be saved correctly.
  1472. $langcode = LANGUAGE_NONE;
  1473. $entity = field_test_create_stub_entity(NULL, NULL);
  1474. $entity->{$this->field_name}[$langcode][0]['tid'] = 0;
  1475. field_test_entity_save($entity);
  1476. $entity = field_test_entity_test_load($entity->ftid);
  1477. field_test_entity_save($entity);
  1478. $this->pass('Empty term ID does not trigger autocreate.');
  1479. // Try also NULL value.
  1480. $entity->{$this->field_name}[$langcode][0]['tid'] = NULL;
  1481. field_test_entity_save($entity);
  1482. $this->pass('NULL term ID does not trigger autocreate.');
  1483. field_attach_prepare_view('test_entity', array($entity->ftid => $entity), 'full');
  1484. $this->pass('NULL term ID prepared to display.');
  1485. }
  1486. }
  1487. /**
  1488. * Tests a taxonomy term reference field that allows multiple vocabularies.
  1489. */
  1490. class TaxonomyTermFieldMultipleVocabularyTestCase extends TaxonomyWebTestCase {
  1491. protected $instance;
  1492. protected $vocabulary1;
  1493. protected $vocabulary2;
  1494. protected $field_name;
  1495. protected $field;
  1496. public static function getInfo() {
  1497. return array(
  1498. 'name' => 'Multiple vocabulary term reference field',
  1499. 'description' => 'Tests term reference fields that allow multiple vocabularies.',
  1500. 'group' => 'Taxonomy',
  1501. );
  1502. }
  1503. function setUp() {
  1504. parent::setUp('field_test');
  1505. $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer taxonomy'));
  1506. $this->drupalLogin($web_user);
  1507. $this->vocabulary1 = $this->createVocabulary();
  1508. $this->vocabulary2 = $this->createVocabulary();
  1509. // Set up a field and instance.
  1510. $this->field_name = drupal_strtolower($this->randomName());
  1511. $this->field = array(
  1512. 'field_name' => $this->field_name,
  1513. 'type' => 'taxonomy_term_reference',
  1514. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  1515. 'settings' => array(
  1516. 'allowed_values' => array(
  1517. array(
  1518. 'vocabulary' => $this->vocabulary1->machine_name,
  1519. 'parent' => '0',
  1520. ),
  1521. array(
  1522. 'vocabulary' => $this->vocabulary2->machine_name,
  1523. 'parent' => '0',
  1524. ),
  1525. ),
  1526. )
  1527. );
  1528. field_create_field($this->field);
  1529. $this->instance = array(
  1530. 'field_name' => $this->field_name,
  1531. 'entity_type' => 'test_entity',
  1532. 'bundle' => 'test_bundle',
  1533. 'widget' => array(
  1534. 'type' => 'options_select',
  1535. ),
  1536. 'display' => array(
  1537. 'full' => array(
  1538. 'type' => 'taxonomy_term_reference_link',
  1539. ),
  1540. ),
  1541. );
  1542. field_create_instance($this->instance);
  1543. }
  1544. /**
  1545. * Tests term reference field and widget with multiple vocabularies.
  1546. */
  1547. function testTaxonomyTermFieldMultipleVocabularies() {
  1548. // Create a term in each vocabulary.
  1549. $term1 = $this->createTerm($this->vocabulary1);
  1550. $term2 = $this->createTerm($this->vocabulary2);
  1551. // Submit an entity with both terms.
  1552. $langcode = LANGUAGE_NONE;
  1553. $this->drupalGet('test-entity/add/test-bundle');
  1554. $this->assertFieldByName("{$this->field_name}[$langcode][]", '', 'Widget is displayed.');
  1555. $edit = array(
  1556. "{$this->field_name}[$langcode][]" => array($term1->tid, $term2->tid),
  1557. );
  1558. $this->drupalPost(NULL, $edit, t('Save'));
  1559. preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
  1560. $id = $match[1];
  1561. $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created.');
  1562. // Render the entity.
  1563. $entity = field_test_entity_test_load($id);
  1564. $entities = array($id => $entity);
  1565. field_attach_prepare_view('test_entity', $entities, 'full');
  1566. $entity->content = field_attach_view('test_entity', $entity, 'full');
  1567. $this->content = drupal_render($entity->content);
  1568. $this->assertText($term1->name, 'Term 1 name is displayed.');
  1569. $this->assertText($term2->name, 'Term 2 name is displayed.');
  1570. // Delete vocabulary 2.
  1571. taxonomy_vocabulary_delete($this->vocabulary2->vid);
  1572. // Re-render the content.
  1573. $entity = field_test_entity_test_load($id);
  1574. $entities = array($id => $entity);
  1575. field_attach_prepare_view('test_entity', $entities, 'full');
  1576. $entity->content = field_attach_view('test_entity', $entity, 'full');
  1577. $this->plainTextContent = FALSE;
  1578. $this->content = drupal_render($entity->content);
  1579. // Term 1 should still be displayed; term 2 should not be.
  1580. $this->assertText($term1->name, 'Term 1 name is displayed.');
  1581. $this->assertNoText($term2->name, 'Term 2 name is not displayed.');
  1582. // Verify that field and instance settings are correct.
  1583. $field_info = field_info_field($this->field_name);
  1584. $this->assertEqual(sizeof($field_info['settings']['allowed_values']), 1, 'Only one vocabulary is allowed for the field.');
  1585. // The widget should still be displayed.
  1586. $this->drupalGet('test-entity/add/test-bundle');
  1587. $this->assertFieldByName("{$this->field_name}[$langcode][]", '', 'Widget is still displayed.');
  1588. // Term 1 should still pass validation.
  1589. $edit = array(
  1590. "{$this->field_name}[$langcode][]" => array($term1->tid),
  1591. );
  1592. $this->drupalPost(NULL, $edit, t('Save'));
  1593. }
  1594. }
  1595. /**
  1596. * Test taxonomy token replacement in strings.
  1597. */
  1598. class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
  1599. protected $vocabulary;
  1600. protected $instance;
  1601. protected $langcode;
  1602. protected $admin_user;
  1603. public static function getInfo() {
  1604. return array(
  1605. 'name' => 'Taxonomy token replacement',
  1606. 'description' => 'Generates text using placeholders for dummy content to check taxonomy token replacement.',
  1607. 'group' => 'Taxonomy',
  1608. );
  1609. }
  1610. function setUp() {
  1611. parent::setUp();
  1612. $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access'));
  1613. $this->drupalLogin($this->admin_user);
  1614. $this->vocabulary = $this->createVocabulary();
  1615. $this->langcode = LANGUAGE_NONE;
  1616. $field = array(
  1617. 'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
  1618. 'type' => 'taxonomy_term_reference',
  1619. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  1620. 'settings' => array(
  1621. 'allowed_values' => array(
  1622. array(
  1623. 'vocabulary' => $this->vocabulary->machine_name,
  1624. 'parent' => 0,
  1625. ),
  1626. ),
  1627. ),
  1628. );
  1629. field_create_field($field);
  1630. $this->instance = array(
  1631. 'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
  1632. 'bundle' => 'article',
  1633. 'entity_type' => 'node',
  1634. 'widget' => array(
  1635. 'type' => 'options_select',
  1636. ),
  1637. 'display' => array(
  1638. 'default' => array(
  1639. 'type' => 'taxonomy_term_reference_link',
  1640. ),
  1641. ),
  1642. );
  1643. field_create_instance($this->instance);
  1644. }
  1645. /**
  1646. * Creates some terms and a node, then tests the tokens generated from them.
  1647. */
  1648. function testTaxonomyTokenReplacement() {
  1649. global $language;
  1650. // Create two taxonomy terms.
  1651. $term1 = $this->createTerm($this->vocabulary);
  1652. $term2 = $this->createTerm($this->vocabulary);
  1653. // Edit $term2, setting $term1 as parent.
  1654. $edit = array();
  1655. $edit['name'] = '<blink>Blinking Text</blink>';
  1656. $edit['parent[]'] = array($term1->tid);
  1657. $this->drupalPost('taxonomy/term/' . $term2->tid . '/edit', $edit, t('Save'));
  1658. // Create node with term2.
  1659. $edit = array();
  1660. $node = $this->drupalCreateNode(array('type' => 'article'));
  1661. $edit[$this->instance['field_name'] . '[' . $this->langcode . '][]'] = $term2->tid;
  1662. $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
  1663. // Generate and test sanitized tokens for term1.
  1664. $tests = array();
  1665. $tests['[term:tid]'] = $term1->tid;
  1666. $tests['[term:name]'] = check_plain($term1->name);
  1667. $tests['[term:description]'] = check_markup($term1->description, $term1->format);
  1668. $tests['[term:url]'] = url('taxonomy/term/' . $term1->tid, array('absolute' => TRUE));
  1669. $tests['[term:node-count]'] = 0;
  1670. $tests['[term:parent:name]'] = '[term:parent:name]';
  1671. $tests['[term:vocabulary:name]'] = check_plain($this->vocabulary->name);
  1672. foreach ($tests as $input => $expected) {
  1673. $output = token_replace($input, array('term' => $term1), array('language' => $language));
  1674. $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
  1675. }
  1676. // Generate and test sanitized tokens for term2.
  1677. $tests = array();
  1678. $tests['[term:tid]'] = $term2->tid;
  1679. $tests['[term:name]'] = check_plain($term2->name);
  1680. $tests['[term:description]'] = check_markup($term2->description, $term2->format);
  1681. $tests['[term:url]'] = url('taxonomy/term/' . $term2->tid, array('absolute' => TRUE));
  1682. $tests['[term:node-count]'] = 1;
  1683. $tests['[term:parent:name]'] = check_plain($term1->name);
  1684. $tests['[term:parent:url]'] = url('taxonomy/term/' . $term1->tid, array('absolute' => TRUE));
  1685. $tests['[term:parent:parent:name]'] = '[term:parent:parent:name]';
  1686. $tests['[term:vocabulary:name]'] = check_plain($this->vocabulary->name);
  1687. // Test to make sure that we generated something for each token.
  1688. $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
  1689. foreach ($tests as $input => $expected) {
  1690. $output = token_replace($input, array('term' => $term2), array('language' => $language));
  1691. $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
  1692. }
  1693. // Generate and test unsanitized tokens.
  1694. $tests['[term:name]'] = $term2->name;
  1695. $tests['[term:description]'] = $term2->description;
  1696. $tests['[term:parent:name]'] = $term1->name;
  1697. $tests['[term:vocabulary:name]'] = $this->vocabulary->name;
  1698. foreach ($tests as $input => $expected) {
  1699. $output = token_replace($input, array('term' => $term2), array('language' => $language, 'sanitize' => FALSE));
  1700. $this->assertEqual($output, $expected, format_string('Unsanitized taxonomy term token %token replaced.', array('%token' => $input)));
  1701. }
  1702. // Generate and test sanitized tokens.
  1703. $tests = array();
  1704. $tests['[vocabulary:vid]'] = $this->vocabulary->vid;
  1705. $tests['[vocabulary:name]'] = check_plain($this->vocabulary->name);
  1706. $tests['[vocabulary:description]'] = filter_xss($this->vocabulary->description);
  1707. $tests['[vocabulary:node-count]'] = 1;
  1708. $tests['[vocabulary:term-count]'] = 2;
  1709. // Test to make sure that we generated something for each token.
  1710. $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
  1711. foreach ($tests as $input => $expected) {
  1712. $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language));
  1713. $this->assertEqual($output, $expected, format_string('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
  1714. }
  1715. // Generate and test unsanitized tokens.
  1716. $tests['[vocabulary:name]'] = $this->vocabulary->name;
  1717. $tests['[vocabulary:description]'] = $this->vocabulary->description;
  1718. foreach ($tests as $input => $expected) {
  1719. $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language, 'sanitize' => FALSE));
  1720. $this->assertEqual($output, $expected, format_string('Unsanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
  1721. }
  1722. // Verify that tokens are generated for terms with empty description.
  1723. $term1->description = '';
  1724. taxonomy_term_save($term1);
  1725. $term2->description = NULL;
  1726. taxonomy_term_save($term2);
  1727. // Generate and test sanitized tokens.
  1728. $tests = array();
  1729. $tests['[term:description]'] = '';
  1730. foreach ($tests as $input => $expected) {
  1731. $output = token_replace($input, array('term' => $term1), array('language' => $language));
  1732. $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
  1733. $output = token_replace($input, array('term' => $term2), array('language' => $language));
  1734. $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
  1735. }
  1736. }
  1737. }
  1738. /**
  1739. * Tests for verifying that taxonomy pages use the correct theme.
  1740. */
  1741. class TaxonomyThemeTestCase extends TaxonomyWebTestCase {
  1742. public static function getInfo() {
  1743. return array(
  1744. 'name' => 'Taxonomy theme switching',
  1745. 'description' => 'Verifies that various taxonomy pages use the expected theme.',
  1746. 'group' => 'Taxonomy',
  1747. );
  1748. }
  1749. function setUp() {
  1750. parent::setUp();
  1751. // Make sure we are using distinct default and administrative themes for
  1752. // the duration of these tests.
  1753. variable_set('theme_default', 'bartik');
  1754. variable_set('admin_theme', 'seven');
  1755. // Create and log in as a user who has permission to add and edit taxonomy
  1756. // terms and view the administrative theme.
  1757. $admin_user = $this->drupalCreateUser(array('administer taxonomy', 'view the administration theme'));
  1758. $this->drupalLogin($admin_user);
  1759. }
  1760. /**
  1761. * Test the theme used when adding, viewing and editing taxonomy terms.
  1762. */
  1763. function testTaxonomyTermThemes() {
  1764. // Adding a term to a vocabulary is considered an administrative action and
  1765. // should use the administrative theme.
  1766. $vocabulary = $this->createVocabulary();
  1767. $this->drupalGet('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add');
  1768. $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page for adding a taxonomy term.");
  1769. // Viewing a taxonomy term should use the default theme.
  1770. $term = $this->createTerm($vocabulary);
  1771. $this->drupalGet('taxonomy/term/' . $term->tid);
  1772. $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page for viewing a taxonomy term.");
  1773. // Editing a taxonomy term should use the same theme as adding one.
  1774. $this->drupalGet('taxonomy/term/' . $term->tid . '/edit');
  1775. $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page for editing a taxonomy term.");
  1776. }
  1777. }
  1778. /**
  1779. * Tests the functionality of EntityFieldQuery for taxonomy entities.
  1780. */
  1781. class TaxonomyEFQTestCase extends TaxonomyWebTestCase {
  1782. protected $admin_user;
  1783. protected $vocabulary;
  1784. public static function getInfo() {
  1785. return array(
  1786. 'name' => 'Taxonomy EntityFieldQuery',
  1787. 'description' => 'Verifies operation of a taxonomy-based EntityFieldQuery.',
  1788. 'group' => 'Taxonomy',
  1789. );
  1790. }
  1791. function setUp() {
  1792. parent::setUp();
  1793. $this->admin_user = $this->drupalCreateUser(array('administer taxonomy'));
  1794. $this->drupalLogin($this->admin_user);
  1795. $this->vocabulary = $this->createVocabulary();
  1796. }
  1797. /**
  1798. * Tests that a basic taxonomy EntityFieldQuery works.
  1799. */
  1800. function testTaxonomyEFQ() {
  1801. $terms = array();
  1802. for ($i = 0; $i < 5; $i++) {
  1803. $term = $this->createTerm($this->vocabulary);
  1804. $terms[$term->tid] = $term;
  1805. }
  1806. $query = new EntityFieldQuery();
  1807. $query->entityCondition('entity_type', 'taxonomy_term');
  1808. $result = $query->execute();
  1809. $result = $result['taxonomy_term'];
  1810. asort($result);
  1811. $this->assertEqual(array_keys($terms), array_keys($result), 'Taxonomy terms were retrieved by EntityFieldQuery.');
  1812. // Create a second vocabulary and five more terms.
  1813. $vocabulary2 = $this->createVocabulary();
  1814. $terms2 = array();
  1815. for ($i = 0; $i < 5; $i++) {
  1816. $term = $this->createTerm($vocabulary2);
  1817. $terms2[$term->tid] = $term;
  1818. }
  1819. $query = new EntityFieldQuery();
  1820. $query->entityCondition('entity_type', 'taxonomy_term');
  1821. $query->entityCondition('bundle', $vocabulary2->machine_name);
  1822. $result = $query->execute();
  1823. $result = $result['taxonomy_term'];
  1824. asort($result);
  1825. $this->assertEqual(array_keys($terms2), array_keys($result), format_string('Taxonomy terms from the %name vocabulary were retrieved by EntityFieldQuery.', array('%name' => $vocabulary2->name)));
  1826. }
  1827. }
  1828. /**
  1829. * Tests that appropriate query tags are added.
  1830. */
  1831. class TaxonomyQueryAlterTestCase extends TaxonomyWebTestCase {
  1832. public static function getInfo() {
  1833. return array(
  1834. 'name' => 'Taxonomy query tags',
  1835. 'description' => 'Verifies that taxonomy_term_access tags are added to queries.',
  1836. 'group' => 'Taxonomy',
  1837. );
  1838. }
  1839. public function setUp() {
  1840. parent::setUp('taxonomy_test');
  1841. }
  1842. /**
  1843. * Tests that appropriate tags are added when querying the database.
  1844. */
  1845. public function testTaxonomyQueryAlter() {
  1846. // Create a new vocabulary and add a few terms to it.
  1847. $vocabulary = $this->createVocabulary();
  1848. $terms = array();
  1849. for ($i = 0; $i < 5; $i++) {
  1850. $terms[$i] = $this->createTerm($vocabulary);
  1851. }
  1852. // Set up hierarchy. Term 2 is a child of 1.
  1853. $terms[2]->parent = array($terms[1]->tid);
  1854. taxonomy_term_save($terms[2]);
  1855. $this->setupQueryTagTestHooks();
  1856. $loaded_term = taxonomy_term_load($terms[0]->tid);
  1857. $this->assertEqual($loaded_term->tid, $terms[0]->tid, 'First term was loaded');
  1858. $this->assertQueryTagTestResult(1, 'taxonomy_term_load()');
  1859. $this->setupQueryTagTestHooks();
  1860. $loaded_terms = taxonomy_get_tree($vocabulary->vid);
  1861. $this->assertEqual(count($loaded_terms), count($terms), 'All terms were loaded');
  1862. $this->assertQueryTagTestResult(1, 'taxonomy_get_tree()');
  1863. $this->setupQueryTagTestHooks();
  1864. $loaded_terms = taxonomy_get_parents($terms[2]->tid);
  1865. $this->assertEqual(count($loaded_terms), 1, 'All parent terms were loaded');
  1866. $this->assertQueryTagTestResult(2, 'taxonomy_get_parents()');
  1867. $this->setupQueryTagTestHooks();
  1868. $loaded_terms = taxonomy_get_children($terms[1]->tid);
  1869. $this->assertEqual(count($loaded_terms), 1, 'All child terms were loaded');
  1870. $this->assertQueryTagTestResult(2, 'taxonomy_get_children()');
  1871. $this->setupQueryTagTestHooks();
  1872. $query = db_select('taxonomy_term_data', 't');
  1873. $query->addField('t', 'tid');
  1874. $query->addTag('taxonomy_term_access');
  1875. $tids = $query->execute()->fetchCol();
  1876. $this->assertEqual(count($tids), count($terms), 'All term IDs were retrieved');
  1877. $this->assertQueryTagTestResult(1, 'custom db_select() with taxonomy_term_access tag (preferred)');
  1878. $this->setupQueryTagTestHooks();
  1879. $query = db_select('taxonomy_term_data', 't');
  1880. $query->addField('t', 'tid');
  1881. $query->addTag('term_access');
  1882. $tids = $query->execute()->fetchCol();
  1883. $this->assertEqual(count($tids), count($terms), 'All term IDs were retrieved');
  1884. $this->assertQueryTagTestResult(1, 'custom db_select() with term_access tag (deprecated)');
  1885. $this->setupQueryTagTestHooks();
  1886. $query = new EntityFieldQuery();
  1887. $query->entityCondition('entity_type', 'taxonomy_term');
  1888. $query->addTag('taxonomy_term_access');
  1889. $result = $query->execute();
  1890. $this->assertEqual(count($result['taxonomy_term']), count($terms), 'All term IDs were retrieved');
  1891. $this->assertQueryTagTestResult(1, 'custom EntityFieldQuery with taxonomy_term_access tag (preferred)');
  1892. $this->setupQueryTagTestHooks();
  1893. $query = new EntityFieldQuery();
  1894. $query->entityCondition('entity_type', 'taxonomy_term');
  1895. $query->addTag('term_access');
  1896. $result = $query->execute();
  1897. $this->assertEqual(count($result['taxonomy_term']), count($terms), 'All term IDs were retrieved');
  1898. $this->assertQueryTagTestResult(1, 'custom EntityFieldQuery with term_access tag (deprecated)');
  1899. }
  1900. /**
  1901. * Sets up the hooks in the test module.
  1902. */
  1903. protected function setupQueryTagTestHooks() {
  1904. taxonomy_terms_static_reset();
  1905. variable_set('taxonomy_test_query_alter', 0);
  1906. variable_set('taxonomy_test_query_term_access_alter', 0);
  1907. variable_set('taxonomy_test_query_taxonomy_term_access_alter', 0);
  1908. }
  1909. /**
  1910. * Verifies invocation of the hooks in the test module.
  1911. *
  1912. * @param int $expected_invocations
  1913. * The number of times the hooks are expected to have been invoked.
  1914. * @param string $method
  1915. * A string describing the invoked function which generated the query.
  1916. */
  1917. protected function assertQueryTagTestResult($expected_invocations, $method) {
  1918. $this->assertIdentical($expected_invocations, variable_get('taxonomy_test_query_alter'), 'hook_query_alter() invoked when executing ' . $method);
  1919. $this->assertIdentical($expected_invocations, variable_get('taxonomy_test_query_term_access_alter'), 'Deprecated hook_query_term_access_alter() invoked when executing ' . $method);
  1920. $this->assertIdentical($expected_invocations, variable_get('taxonomy_test_query_taxonomy_term_access_alter'), 'Preferred hook_query_taxonomy_term_access_alter() invoked when executing ' . $method);
  1921. }
  1922. }
  1923. /**
  1924. * Tests for taxonomy terms cache usage.
  1925. */
  1926. class TaxonomyTermCacheUsageTestCase extends TaxonomyWebTestCase {
  1927. public static function getInfo() {
  1928. return array(
  1929. 'name' => 'Taxonomy term cache usage',
  1930. 'description' => 'Tests for taxonomy term cache usage.',
  1931. 'group' => 'Taxonomy',
  1932. );
  1933. }
  1934. function setUp() {
  1935. parent::setUp('taxonomy', 'taxonomy_test');
  1936. }
  1937. /**
  1938. * Test taxonomy_get_term_by_name() cache usage.
  1939. */
  1940. function testTaxonomyGetTermByNameCacheUsage() {
  1941. // Create vocabulary and term.
  1942. $new_vocabulary = $this->createVocabulary();
  1943. $new_term = new stdClass();
  1944. $new_term->name = 'MixedCaseTerm';
  1945. $new_term->vid = $new_vocabulary->vid;
  1946. taxonomy_term_save($new_term);
  1947. // Try to load term with mixed case letters from the cache.
  1948. $taxonomy_controller = new TestTaxonomyTermController('taxonomy_term');
  1949. // First load to warm the cache.
  1950. $terms = $taxonomy_controller->load(array(), array('name' => $new_term->name));
  1951. $this->assertTrue(isset($terms[$new_term->tid]), 'Term loaded using exact name and vocabulary machine name.');
  1952. // Second load should load the $new_term from the cache.
  1953. $terms = $taxonomy_controller->loadFromCache(array(), array('name' => $new_term->name));
  1954. $this->assertTrue(isset($terms[$new_term->tid]), 'Term loaded using the cache.');
  1955. }
  1956. }
  1957. /**
  1958. * Tests appropriate access control to private file fields on a term.
  1959. */
  1960. class TaxonomyPrivateFileTestCase extends TaxonomyWebTestCase {
  1961. protected $vocabulary;
  1962. public static function getInfo() {
  1963. return array(
  1964. 'name' => 'Taxonomy term private file access',
  1965. 'description' => 'Verifies private files on terms have appropriate access control.',
  1966. 'group' => 'Taxonomy',
  1967. );
  1968. }
  1969. public function setUp() {
  1970. parent::setUp('taxonomy_test');
  1971. // Remove access content permission from registered users.
  1972. user_role_revoke_permissions(DRUPAL_AUTHENTICATED_RID, array('access content'));
  1973. $this->vocabulary = $this->createVocabulary();
  1974. // Add a field instance to the vocabulary.
  1975. $field = array(
  1976. 'field_name' => 'field_test',
  1977. 'type' => 'image',
  1978. 'settings' => array(
  1979. 'uri_scheme' => 'private'
  1980. ),
  1981. );
  1982. field_create_field($field);
  1983. $instance = array(
  1984. 'field_name' => 'field_test',
  1985. 'entity_type' => 'taxonomy_term',
  1986. 'label' => 'test',
  1987. 'bundle' => $this->vocabulary->machine_name,
  1988. 'widget' => array(
  1989. 'type' => 'image_image',
  1990. 'settings' => array(),
  1991. ),
  1992. );
  1993. field_create_instance($instance);
  1994. }
  1995. /**
  1996. * Tests access to a private file on a taxonomy term entity.
  1997. */
  1998. public function testTaxonomyImageAccess() {
  1999. $user = $this->drupalCreateUser(array('administer site configuration', 'administer taxonomy', 'access user profiles'));
  2000. $this->drupalLogin($user);
  2001. // Create a term and upload the image.
  2002. $term = $this->createTerm($this->vocabulary);
  2003. $files = $this->drupalGetTestFiles('image');
  2004. $image = array_pop($files);
  2005. $edit['files[field_test_' . LANGUAGE_NONE . '_0]'] = drupal_realpath($image->uri);
  2006. $this->drupalPost('taxonomy/term/' . $term->tid . '/edit', $edit, t('Save'));
  2007. $term = taxonomy_term_load($term->tid);
  2008. $this->assertText(t('Updated term @name.', array('@name' => $term->name)));
  2009. // Create a user that should have access to the file and one that doesn't.
  2010. $access_user = $this->drupalCreateUser(array('access content'));
  2011. $no_access_user = $this->drupalCreateUser();
  2012. $image = file_load($term->field_test[LANGUAGE_NONE][0]['fid']);
  2013. $image_url = file_create_url($image->uri);
  2014. $this->drupalLogin($access_user);
  2015. $this->drupalGet($image_url);
  2016. $this->assertResponse(200, 'Private image on term is accessible with right permission');
  2017. $this->drupalLogin($no_access_user);
  2018. $this->drupalGet($image_url);
  2019. $this->assertResponse(403, 'Private image on term not accessible without right permission');
  2020. }
  2021. }