synonyms.module 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. <?php
  2. /**
  3. * @file
  4. * Provide synonyms feature for Drupal Taxonomy.
  5. */
  6. /**
  7. * The default field name to be used as a source of synonyms for a term.
  8. */
  9. define('SYNONYMS_DEFAULT_FIELD_NAME', 'synonyms_synonyms');
  10. /**
  11. * Implements hook_menu().
  12. */
  13. function synonyms_menu() {
  14. $items = array();
  15. $items['synonyms/autocomplete/%/%/%'] = array(
  16. 'title' => 'Autocomplete Synonyms',
  17. 'page callback' => 'synonyms_autocomplete',
  18. 'page arguments' => array(2, 3, 4),
  19. 'access arguments' => array('access content'),
  20. 'file' => 'synonyms.pages.inc',
  21. 'type' => MENU_CALLBACK,
  22. );
  23. return $items;
  24. }
  25. /**
  26. * Implements hook_taxonomy_term_update().
  27. */
  28. function synonyms_taxonomy_term_update($term) {
  29. // If we notice at least some change in synonyms of this term, we want to
  30. // trigger search re-indexing of nodes, where this term is referenced, since
  31. // change in term synonyms affects nodes ranking in the search.
  32. if (isset($term->original)) {
  33. $current_synonyms = synonyms_get_raw($term);
  34. $previous_synonyms = synonyms_get_raw($term->original);
  35. if (count($current_synonyms) != count($previous_synonyms)) {
  36. // Schedule re-indexing, because amount of synonyms has changed.
  37. module_load_include('inc', 'synonyms', 'synonyms.pages');
  38. synonyms_reindex_nodes_by_terms(array($term->tid));
  39. }
  40. else {
  41. foreach ($current_synonyms as $k => $current_synonym) {
  42. if ($current_synonyms[$k] != $previous_synonyms[$k]) {
  43. // Schedule re-indexing, because some synonym has changed.
  44. module_load_include('inc', 'synonyms', 'synonyms.pages');
  45. synonyms_reindex_nodes_by_terms(array($term->tid));
  46. break;
  47. }
  48. }
  49. }
  50. }
  51. }
  52. /**
  53. * Implements hook_entity_property_info().
  54. */
  55. function synonyms_entity_property_info() {
  56. $info = array();
  57. $properties = &$info['taxonomy_term']['properties'];
  58. $properties['synonyms'] = array(
  59. 'label' => t('Synonyms'),
  60. 'description' => t('Synonyms of entity.'),
  61. 'type' => 'list<text>',
  62. 'getter callback' => 'synonyms_get_sanitized',
  63. 'computed' => TRUE,
  64. 'sanitized' => TRUE,
  65. 'raw getter callback' => 'synonyms_get_raw',
  66. );
  67. return $info;
  68. }
  69. /**
  70. * Implements hook_node_update_index().
  71. */
  72. function synonyms_node_update_index($node) {
  73. $output = '';
  74. foreach (field_info_instances('node', $node->type) as $instance) {
  75. // We go a field by field looking for taxonomy term reference and
  76. // if that vocabulary has enabled synonyms, we add term's synonyms
  77. // to the search index.
  78. $field_info = field_info_field($instance['field_name']);
  79. if ($field_info['type'] == 'taxonomy_term_reference') {
  80. // For each term referenced in this node we have to add synonyms.
  81. $_terms = field_get_items('node', $node, $instance['field_name']);
  82. if (is_array($_terms) && !empty($_terms)) {
  83. $terms = array();
  84. foreach ($_terms as $v) {
  85. $terms[] = $v['tid'];
  86. }
  87. $terms = taxonomy_term_load_multiple($terms);
  88. foreach ($terms as $term) {
  89. $synonyms = synonyms_get_sanitized($term);
  90. if (!empty($synonyms)) {
  91. $output .= '<strong>' . implode(', ', $synonyms) . '</strong>';
  92. }
  93. }
  94. }
  95. }
  96. }
  97. return $output;
  98. }
  99. /**
  100. * Implements hook_synonyms_extractor_info().
  101. */
  102. function synonyms_synonyms_extractor_info() {
  103. // Here we provide synonyms extractors that come along with Synonyms module.
  104. return array(
  105. 'SynonymsSynonymsExtractor',
  106. 'TaxonomySynonymsExtractor',
  107. 'EntityReferenceSynonymsExtractor',
  108. );
  109. }
  110. /**
  111. * Public function.
  112. *
  113. * Provide info about what class reports ability to extract synonyms from
  114. * which field type. The output information of this function is collected via
  115. * hook_synonyms_extractor_info().
  116. *
  117. * @param string $field_type
  118. * Optionally you may specify to get a class responsible for a specific field
  119. * type only. If nothing is supplied, array of field_type => class_extractor
  120. * relation is returned
  121. * @param bool $reset
  122. * Whether collect all the info again from hooks, or cached info is fine
  123. *
  124. * @return array
  125. * Key of this array denotes the field type while value of the array denotes
  126. * which class reports ability to extract synonyms from such field type.
  127. */
  128. function synonyms_extractor_info($field_type = NULL, $reset = FALSE) {
  129. $cache = &drupal_static(__FUNCTION__);
  130. // Trying static cache.
  131. if (!is_array($cache) || $reset) {
  132. // Trying Drupal DB cache layer.
  133. $cid = 'synonyms_extractor_info';
  134. $cache = cache_get($cid);
  135. if (!isset($cache->data) || $reset) {
  136. // No cache has been found at all. So we call hooks and collect data.
  137. $info = array();
  138. $extractor_classes = module_invoke_all('synonyms_extractor_info');
  139. if (is_array($extractor_classes)) {
  140. foreach ($extractor_classes as $class) {
  141. foreach (call_user_func(array($class, 'fieldTypesSupported')) as $_field_type) {
  142. $info[$_field_type] = $class;
  143. }
  144. }
  145. }
  146. // Letting whoever wants to implement any changes after preprocessing the
  147. // data.
  148. drupal_alter('synonyms_extractor_info', $info);
  149. $cache = $info;
  150. cache_set($cid, $cache, 'cache', CACHE_TEMPORARY);
  151. }
  152. else {
  153. $cache = $cache->data;
  154. }
  155. }
  156. if (!is_null($field_type) && isset($cache[$field_type])) {
  157. return $cache[$field_type];
  158. }
  159. return $cache;
  160. }
  161. /**
  162. * Implements hook_form_FORM_ID_alter().
  163. */
  164. function synonyms_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) {
  165. if (isset($form_state['confirm_delete']) && $form_state['confirm_delete']) {
  166. return;
  167. }
  168. module_load_include('inc', 'synonyms', 'synonyms.pages');
  169. $form['synonyms'] = array(
  170. '#type' => 'fieldset',
  171. '#title' => t('Synonyms'),
  172. '#collapsible' => TRUE,
  173. '#tree' => TRUE,
  174. );
  175. $options = array(
  176. SYNONYMS_DEFAULT_FIELD_NAME => t('Default synonyms field'),
  177. );
  178. if (isset($form['#vocabulary']->vid)) {
  179. $instances = synonyms_instances_extract_applicable($form['#vocabulary']);
  180. foreach ($instances as $instance) {
  181. // Here we prefer some informative text for the default synonyms field
  182. // rather its label.
  183. if ($instance['field_name'] != SYNONYMS_DEFAULT_FIELD_NAME) {
  184. $options[$instance['field_name']] = $instance['label'];
  185. }
  186. }
  187. }
  188. $form['synonyms']['synonyms'] = array(
  189. '#type' => 'checkboxes',
  190. '#title' => t('Synonyms Fields'),
  191. '#options' => $options,
  192. '#description' => t('<p>This option allows you to assign synonym fields for each term of the vocabulary, allowing to reduce the amount of duplicates.</p><p><b>Important note:</b> unticking %default_field on a production website will result in loss of your synonyms.</p>', array('%default_field' => $options[SYNONYMS_DEFAULT_FIELD_NAME])),
  193. '#default_value' => synonyms_synonyms_fields($form['#vocabulary']),
  194. );
  195. // Adding our own submit handler.
  196. $form['#submit'][] = 'synonyms_taxonomy_form_vocabulary_submit';
  197. }
  198. /**
  199. * Implements hook_field_widget_info().
  200. */
  201. function synonyms_field_widget_info() {
  202. return array(
  203. 'synonyms_autocomplete' => array(
  204. 'label' => t('Synonyms friendly autocomplete term widget'),
  205. 'field types' => array('taxonomy_term_reference'),
  206. 'settings' => array(
  207. 'size' => 60,
  208. 'autocomplete_path' => 'synonyms/autocomplete',
  209. 'suggestion_size' => 10,
  210. 'suggest_only_unique' => FALSE,
  211. 'auto_creation' => 1,
  212. ),
  213. 'behaviors' => array(
  214. 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
  215. ),
  216. ),
  217. );
  218. }
  219. /**
  220. * Implements hook_field_widget_settings_form().
  221. */
  222. function synonyms_field_widget_settings_form($field, $instance) {
  223. $widget = $instance['widget'];
  224. $settings = $widget['settings'];
  225. $form = array();
  226. $form['auto_creation'] = array(
  227. '#type' => 'checkbox',
  228. '#title' => t('Allow auto-creation?'),
  229. '#description' => t('Whether users may create a new term by typing in a non-existing name into this field.'),
  230. '#default_value' => $settings['auto_creation'],
  231. );
  232. $form['suggestion_size'] = array(
  233. '#type' => 'textfield',
  234. '#title' => t('Suggestions Size'),
  235. '#description' => t('Please, enter how many suggested entities to show in the autocomplete textfield.'),
  236. '#required' => TRUE,
  237. '#element_validate' => array('element_validate_integer_positive'),
  238. '#default_value' => $settings['suggestion_size'],
  239. );
  240. $form['suggest_only_unique'] = array(
  241. '#type' => 'checkbox',
  242. '#title' => t('Suggest only one entry per term'),
  243. '#description' => t('If you want to include only term name or a single synonym, suggesting a particular term, while disregarding all ongoing ones, please, tick this checkbox on.'),
  244. '#default_value' => $settings['suggest_only_unique'],
  245. );
  246. return $form;
  247. }
  248. /**
  249. * Implements hook_field_widget_form().
  250. */
  251. function synonyms_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  252. $tags = array();
  253. foreach ($items as $item) {
  254. $tags[$item['tid']] = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['tid']);
  255. }
  256. $element += array(
  257. '#type' => 'textfield',
  258. '#default_value' => taxonomy_implode_tags($tags),
  259. '#autocomplete_path' => $instance['widget']['settings']['autocomplete_path'] . '/' . $field['field_name'] . '/' . $instance['entity_type'] . '/' . $instance['bundle'],
  260. '#size' => $instance['widget']['settings']['size'],
  261. '#maxlength' => 1024,
  262. '#element_validate' => array('taxonomy_autocomplete_validate', 'synonyms_autocomplete_validate'),
  263. '#auto_creation' => $instance['widget']['settings']['auto_creation'],
  264. );
  265. return $element;
  266. }
  267. /**
  268. * Implements hook_field_widget_error().
  269. */
  270. function synonyms_field_widget_error($element, $error, $form, &$form_state) {
  271. form_error($element, $error['message']);
  272. }
  273. /**
  274. * Form element validate handler.
  275. *
  276. * Handle validation for taxonomy term synonym-friendly autocomplete element.
  277. */
  278. function synonyms_autocomplete_validate($element, &$form_state) {
  279. // After taxonomy_autocomplete_validate() has finished its job any terms it
  280. // didn't find have been set for autocreation. We need to:
  281. // (a) Double-check that those terms are not synonyms.
  282. // (b) Check that synonyms' configurable auto-creation option is enabled.
  283. $value = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
  284. foreach ($value as $delta => $term) {
  285. if ($term['tid'] == 'autocreate') {
  286. $vocabulary = taxonomy_vocabulary_load($term['vid']);
  287. $synonym_tid = synonyms_get_term_by_synonym($term['name'], $vocabulary);
  288. if ($synonym_tid != 0) {
  289. $value[$delta]['tid'] = $synonym_tid;
  290. }
  291. elseif (empty($element['#auto_creation'])) {
  292. unset($value[$delta]);
  293. }
  294. }
  295. }
  296. $value = array_values($value);
  297. form_set_value($element, $value, $form_state);
  298. }
  299. /**
  300. * Try to find a term by its name or synonym.
  301. *
  302. * To maximize the match trimming and case-insensitive comparison is used.
  303. *
  304. * @param string $name
  305. * The string to be searched for its {taxonomy_term_data}.tid
  306. * @param object $vocabulary
  307. * Fully loaded vocabulary object in which you wish to search
  308. * @param int $parent
  309. * Optional. In case you want to narrow your search scope, this parameter
  310. * takes in the {taxonomy_term_data}.tid of the parent term, letting you
  311. * search only among its children
  312. *
  313. * @return int
  314. * If the look up was successful returns the {taxonomy_term_data}.tid of the
  315. * found term, otherwise returns 0
  316. */
  317. function synonyms_get_term_by_synonym($name, $vocabulary, $parent = 0) {
  318. $name = mb_strtoupper(trim($name), 'UTF-8');
  319. $tree = taxonomy_get_tree($vocabulary->vid, $parent, NULL, TRUE);
  320. foreach ($tree as $term) {
  321. if (mb_strtoupper($term->name, 'UTF-8') == $name) {
  322. return $term->tid;
  323. }
  324. // We additionally scan through the synonyms.
  325. $synonyms = synonyms_get_sanitized($term);
  326. foreach ($synonyms as $item) {
  327. if (mb_strtoupper($item, 'UTF-8') == $name) {
  328. return $term->tid;
  329. }
  330. }
  331. }
  332. // If we have reached down here, this means we haven't got any match
  333. // as fallback we return 0.
  334. return 0;
  335. }
  336. /**
  337. * Look up a term considering synonyms and if nothing found add one.
  338. *
  339. * This function is useful for automated creation of new terms as it won't
  340. * generate the same terms over and over again.
  341. *
  342. * @param string $name
  343. * The string to be searched for its {taxonomy_term_data}.tid
  344. * @param object $vocabulary
  345. * Fully loaded vocabulary object in which you wish to search
  346. * @param int $parent
  347. * Optional. In case you want to narrow your search scope, this parameter
  348. * takes in the {taxonomy_term_data}.tid of the parent term, letting you
  349. * search only among its children
  350. *
  351. * @return int
  352. * If a term already exists, its {taxonomy_term_data}.tid is returned,
  353. * otherwise it creates a new term and returns its {taxonomy_term_data}.tid
  354. */
  355. function synonyms_add_term_by_synonym($name, $vocabulary, $parent = 0) {
  356. $tid = synonyms_get_term_by_synonym($name, $vocabulary, $parent);
  357. if ($tid) {
  358. // We found some term, returning its tid.
  359. return $tid;
  360. }
  361. // We haven't found any term, so we create one.
  362. $term = (object) array(
  363. 'name' => $name,
  364. 'vid' => $vocabulary->vid,
  365. 'parent' => array($parent),
  366. );
  367. taxonomy_term_save($term);
  368. if (isset($term->tid)) {
  369. return $term->tid;
  370. }
  371. // Normally we shouldn't reach up to here, because a term would have got
  372. // created and the just created tid would have been returned. Nevertheless,
  373. // as a fallback in case of any error we return 0.
  374. return 0;
  375. }
  376. /**
  377. * Retrieve a list of sanitized synonyms of a taxonomy term.
  378. *
  379. * @param $item object
  380. * Fully loaded taxonomy term
  381. *
  382. * @return array
  383. * List of sanitized synonyms of a taxonomy term
  384. */
  385. function synonyms_get_sanitized($item) {
  386. $synonyms = array();
  387. foreach (synonyms_get_term_synonyms($item) as $synonym) {
  388. $synonyms[] = $synonym['safe_value'];
  389. }
  390. return $synonyms;
  391. }
  392. /**
  393. * Retrieve a list of raw synonyms of a taxonomy term.
  394. *
  395. * @param $item object
  396. * Fully loaded taxonomy term
  397. *
  398. * @return array
  399. * List of raw synonyms of a taxonomy term
  400. */
  401. function synonyms_get_raw($item) {
  402. $synonyms = array();
  403. foreach (synonyms_get_term_synonyms($item) as $synonym) {
  404. $synonyms[] = $synonym['value'];
  405. }
  406. return $synonyms;
  407. }
  408. /**
  409. * Public function for retrieving synonyms of a taxonomy term.
  410. *
  411. * @param object $term
  412. * Fully loaded taxonomy term for which the synonyms are desired
  413. *
  414. * @return array
  415. * Array of synonyms, if synonyms are disabled for the taxonomy term's
  416. * vocabulary, an empty array is returned. Each synonym subarray consists of
  417. * the following keys:
  418. * - value: (string) the value of a synonym as it was input by user
  419. * - safe_value: (string) a sanitized value of a synonym
  420. */
  421. function synonyms_get_term_synonyms($term) {
  422. $synonyms = array();
  423. $vocabulary = taxonomy_vocabulary_load($term->vid);
  424. foreach (synonyms_synonyms_fields($vocabulary) as $field) {
  425. $bundle = field_extract_bundle('taxonomy_term', $vocabulary);
  426. $instance = field_info_instance('taxonomy_term', $field, $bundle);
  427. $field = field_info_field($field);
  428. $items = field_get_items('taxonomy_term', $term, $field['field_name']);
  429. if (is_array($items) && !empty($items)) {
  430. $class = synonyms_extractor_info($field['type']);
  431. foreach (call_user_func(array($class, 'synonymsExtract'), $items, $field, $instance, $term, 'taxonomy_term') as $synonym) {
  432. $synonyms[] = array(
  433. 'value' => $synonym,
  434. 'safe_value' => check_plain($synonym),
  435. );
  436. }
  437. }
  438. }
  439. return $synonyms;
  440. }
  441. /**
  442. * Allow to merge $synonym_entity as a synonym into $trunk_entity.
  443. *
  444. * Helpful function during various merging operations. It allows you to add a
  445. * synonym (where possible) into one entity, which will represent another entity
  446. * in the format expected by the field in which the synonym is being added.
  447. * Important note: if the cardinality limit of the field into which you are
  448. * adding synonym has been reached, calling to this function will take no
  449. * effect.
  450. *
  451. * @param object $trunk_entity
  452. * Fully loaded entity object in which the synonym is being added
  453. * @param string $trunk_entity_type
  454. * Entity type of $trunk_entity
  455. * @param string $field
  456. * Field name that should exist in $trunk_entity and be enabled as a synonym
  457. * source. Into this field synonym will be added
  458. * @param object $synonym_entity
  459. * Fully loaded entity object which will be added as a synonym
  460. * @param string $synonym_entity_type
  461. * Entity type of $synonym_entity
  462. *
  463. * @return bool
  464. * Whether synonym has been successfully added
  465. */
  466. function synonyms_add_entity_as_synonym($trunk_entity, $trunk_entity_type, $field, $synonym_entity, $synonym_entity_type) {
  467. if ($trunk_entity_type != 'taxonomy_term') {
  468. // Currently synonyms module only operates on taxonomy terms.
  469. return FALSE;
  470. }
  471. if (!in_array($field, synonyms_synonyms_fields(taxonomy_vocabulary_load($trunk_entity->vid)))) {
  472. // $field either doesn't exist in the $trunk_entity or it's not enabled as
  473. // a source of synonyms.
  474. return FALSE;
  475. }
  476. // Preparing arguments for calling a method of Extractor class.
  477. $field = field_info_field($field);
  478. $extractor = synonyms_extractor_info($field['type']);
  479. $items = field_get_items($trunk_entity_type, $trunk_entity, $field['field_name']);
  480. $items = is_array($items) ? $items : array();
  481. $trunk_entity_ids = entity_extract_ids($trunk_entity_type, $trunk_entity);
  482. $instance = field_info_instance($trunk_entity_type, $field['field_name'], $trunk_entity_ids[2]);
  483. $extra_items = call_user_func(array($extractor, 'mergeEntityAsSynonym'), $items, $field, $instance, $synonym_entity, $synonym_entity_type);
  484. // Merging extracted synonym items into the values of the field that already
  485. // exist.
  486. // @todo: Currently we hardcode to LANGUAGE_NONE, but in future it would be
  487. // nice to have multilanguage support.
  488. $items = array_merge($items, $extra_items);
  489. $trunk_entity->{$field['field_name']}[LANGUAGE_NONE] = $items;
  490. // In future if this module eventually becomes a gateway for synonyms for any
  491. // entity types, we'll substitute it with entity_save().
  492. taxonomy_term_save($trunk_entity);
  493. return TRUE;
  494. }
  495. /**
  496. * Return array of field names that are sources of synonyms.
  497. *
  498. * Return array of field names that are currently enabled as source of
  499. * synonyms in the supplied vocabulary.
  500. *
  501. * @param object $vocabulary
  502. * Fully loaded taxonomy vocabulary object
  503. *
  504. * @return array
  505. * Array of field names
  506. */
  507. function synonyms_synonyms_fields($vocabulary) {
  508. $settings = synonyms_vocabulary_settings($vocabulary);
  509. if (!isset($settings['synonyms']) || !is_array($settings['synonyms'])) {
  510. // Weird as normally we expect to see here at least an empty array but
  511. // no problem. We simply initialize it.
  512. $settings['synonyms'] = array();
  513. }
  514. // It's not just as easy as pulling up already saved value. After this
  515. // we have to make sure that all the fields are still present and have not
  516. // been deleted from the vocabulary.
  517. $bundle = field_extract_bundle('taxonomy_term', $vocabulary);
  518. $instances = field_info_instances('taxonomy_term', $bundle);
  519. $settings['synonyms'] = array_intersect($settings['synonyms'], array_keys($instances));
  520. return $settings['synonyms'];
  521. }
  522. /**
  523. * Enforce the setting "synonyms".
  524. *
  525. * @param object $vocabulary
  526. * Fully loaded entity of a taxonomy vocabulary
  527. * @param array $fields
  528. * Array of fields that are enabled as a source of synonyms
  529. */
  530. function synonyms_synonyms_enforce($vocabulary, $fields) {
  531. $bundle = field_extract_bundle('taxonomy_term', $vocabulary);
  532. // Normally all the fields already exist, we just need to assure that
  533. // default synonyms field exists if it is enabled as a source of synonyms.
  534. // Otherwise we make sure we delete instance of the default field in the
  535. // vocabulary.
  536. $instance = field_info_instance('taxonomy_term', SYNONYMS_DEFAULT_FIELD_NAME, $bundle);
  537. if (in_array(SYNONYMS_DEFAULT_FIELD_NAME, $fields)) {
  538. if (is_null($instance)) {
  539. // Make sure the field exists.
  540. synonyms_default_field_ensure();
  541. field_create_instance(array(
  542. 'field_name' => SYNONYMS_DEFAULT_FIELD_NAME,
  543. 'entity_type' => 'taxonomy_term',
  544. 'bundle' => $bundle,
  545. 'label' => t('Synonyms'),
  546. 'description' => t('Please, enter the synonyms which should be related to this term.'),
  547. ));
  548. }
  549. }
  550. elseif (!is_null($instance)) {
  551. // Deleting the instance, we will delete the field separately when the
  552. // module gets uninstalled.
  553. field_delete_instance($instance, FALSE);
  554. }
  555. }
  556. /**
  557. * Return the current settings for the supplied $vocabulary.
  558. *
  559. * @param object $vocabulary
  560. * Fully loaded entity of a taxonomy vocabulary
  561. *
  562. * @return array
  563. * Array of current synonyms settings for the supplied vocabulary.
  564. * Should include the following keys:
  565. * - synonyms: (array) array of field names that are enabled as source of
  566. * synonyms
  567. */
  568. function synonyms_vocabulary_settings($vocabulary) {
  569. $settings = array();
  570. if (isset($vocabulary->vid)) {
  571. $settings = variable_get('synonyms_settings_' . $vocabulary->vid, array(
  572. 'synonyms' => array(),
  573. ));
  574. }
  575. return $settings;
  576. }
  577. /**
  578. * Save the current settings for the supplied $vocabulary.
  579. *
  580. * @param object $vocabulary
  581. * Fully loaded entity of a taxonomy vocabulary
  582. * @param array $settings
  583. * Settings the have to be stored. The structure of this array has to be
  584. * identical to the output array of the function
  585. * synonyms_vocabulary_settings()
  586. */
  587. function synonyms_vocabulary_settings_save($vocabulary, $settings) {
  588. // If source of synonyms has changed for this vocabulary, we have to trigger
  589. // search re-indexing on all the nodes that reference at least 1 term from
  590. // this vocabulary.
  591. $previous_settings = synonyms_vocabulary_settings($vocabulary);
  592. if (implode('', $settings['synonyms']) != implode('', $previous_settings['synonyms'])) {
  593. module_load_include('inc', 'synonyms', 'synonyms.pages');
  594. synonyms_reindex_nodes_by_vocabulary($vocabulary);
  595. }
  596. variable_set('synonyms_settings_' . $vocabulary->vid, $settings);
  597. // Now enforcing each setting.
  598. foreach ($settings as $k => $v) {
  599. switch ($k) {
  600. case 'synonyms':
  601. synonyms_synonyms_enforce($vocabulary, $v);
  602. break;
  603. }
  604. }
  605. }
  606. /**
  607. * Make sure the default synonyms field exists.
  608. *
  609. * If the field doesn't exist function creates one, if the field exists,
  610. * the function does nothing.
  611. */
  612. function synonyms_default_field_ensure() {
  613. $field = field_info_field(SYNONYMS_DEFAULT_FIELD_NAME);
  614. if (is_null($field)) {
  615. $field = array(
  616. 'field_name' => SYNONYMS_DEFAULT_FIELD_NAME,
  617. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  618. 'locked' => TRUE,
  619. 'type' => 'text',
  620. );
  621. field_create_field($field);
  622. }
  623. }
  624. /**
  625. * Extract instances that are applicable for being source of synonyms.
  626. *
  627. * Walk through all instances of a vocabulary and extract only valid candidates
  628. * for becoming a source of synonyms for the vocabulary terms.
  629. *
  630. * @param object $vocabulary
  631. * Fully loaded vocabulary object
  632. *
  633. * @return array
  634. * Array of instance arrays
  635. */
  636. function synonyms_instances_extract_applicable($vocabulary) {
  637. $applicable_field_types = array_keys(synonyms_extractor_info());
  638. $applicable = array();
  639. $bundle = field_extract_bundle('taxonomy_term', $vocabulary);
  640. foreach (field_info_instances('taxonomy_term', $bundle) as $instance) {
  641. $field = field_info_field($instance['field_name']);
  642. if (in_array($field['type'], $applicable_field_types)) {
  643. $applicable[] = $instance;
  644. }
  645. }
  646. return $applicable;
  647. }
  648. /**
  649. * Implements hook_views_api().
  650. */
  651. function synonyms_views_api() {
  652. return array(
  653. 'api' => 3,
  654. 'path' => drupal_get_path('module', 'synonyms') . '/views',
  655. );
  656. }