tode.module 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324
  1. <?php
  2. /**
  3. * developed by www.g-u-i.net
  4. * 2011
  5. * First time this field will be used, it will create a new term with the value inserted in the textfield,
  6. * after that, the term (tid) will always be the same, just update term name by updating the value of the field.
  7. * You can keep synchronized a fixe term with a node,
  8. * you can set an auto title from this field, The required,
  9. * number of value at 1 and term_node synch of global field are required
  10. *
  11. * <?php
  12. * // automatic node titles php snippet for translation
  13. * $items = field_get_items('node', $node, 'field_tode_program');
  14. * return t($items[0]['name']);
  15. * ?>
  16. *
  17. *
  18. */
  19. /**
  20. * Implements hook_views_api().
  21. */
  22. /*
  23. TODO hook_views_api().
  24. */
  25. // function tode_views_api() {
  26. // return array(
  27. // 'api' => 3,
  28. // 'path' => drupal_get_path('module', 'tode') . '/views',
  29. // );
  30. // }
  31. /**
  32. * Implementation of hook_theme().
  33. */
  34. function tode_theme() {
  35. return array(
  36. 'tode' => array(
  37. 'arguments' => array('element' => NULL),
  38. ),
  39. 'tode_node_formatter' => array(
  40. 'variables' => array('item' => NULL, 'viewmode' => 'full', 'nodes'=>NULL),
  41. ),
  42. );
  43. }
  44. /**
  45. * Implementation of hook_init().
  46. */
  47. function tode_init() {
  48. // File hooks and callbacks may be used by any module.
  49. drupal_add_css(drupal_get_path('module', 'tode') .'/tode.css');
  50. }
  51. /**
  52. * Implementation of hook_field_widget_info().
  53. */
  54. function tode_field_widget_info() {
  55. return array(
  56. 'tode' => array(
  57. 'label' => t('Tode (create then update one unique term)'),
  58. 'field types' => array('taxonomy_term_reference'),
  59. 'behaviors' => array(
  60. 'multiple values' => FIELD_BEHAVIOR_DEFAULT,
  61. 'default value' => FIELD_BEHAVIOR_NONE,
  62. ),
  63. 'settings' => array(
  64. 'size' => 60,
  65. 'show_term_form'=> -1,
  66. 'choose_term_parent'=> -1,
  67. 'maxlength'=> 255,
  68. 'redirect_term_to_node' => -1,
  69. 'show_create_tode' => -1,
  70. ),
  71. ),
  72. );
  73. }
  74. /**
  75. * Implementation of hook_widget_settings
  76. */
  77. function tode_field_widget_settings_form($field, $instance){
  78. // dsm($instance, 'tode_field_widget_settings_form : $instance');
  79. // dsm($field, 'field');
  80. $widget = $instance['widget'];
  81. $settings = $widget['settings'];
  82. // '#description' => t('First time this field will be used, it will create a new term with the value inserted in the textfield, after that, the term (tid) will always be the same, just update term name by updating the value of the field. You can keep synchronized a fixe term with a node, you can set an auto title from this field, The required, number of value at 1 and term_node synch of global field are required')
  83. $form['maxlength'] = array(
  84. '#type' => 'textfield',
  85. '#title' => t('Maximum length of term'),
  86. '#default_value' => $settings['maxlength'],
  87. '#element_validate' => array('_tode_widget_settings_maxlength_validate'),
  88. '#required' => TRUE,
  89. '#description' => t('Defines how many characters can be typed into the text field. For values higher than 255, remember that one term name can not be longer than 255 (would be cutted).'),
  90. );
  91. /*
  92. TODO complete the all flow of this
  93. */
  94. $form['show_term_form'] = array(
  95. '#type' => 'checkbox',
  96. '#title' => t('Show taxonomy term edit form ?'),
  97. '#default_value' => $settings['show_term_form'],
  98. );
  99. $form['choose_term_parent'] = array(
  100. '#type' => 'checkbox',
  101. '#title' => t('Enable taxonomy term parent selection ?'),
  102. '#default_value' => $settings['choose_term_parent'],
  103. );
  104. $form['redirect_term_to_node'] = array(
  105. '#type' => 'checkbox',
  106. '#title' => t('Rewrite all term link to theire tode node ?'),
  107. '#default_value' => $settings['redirect_term_to_node'],
  108. );
  109. $form['redirect_node_to_term'] = array(
  110. '#type' => 'checkbox',
  111. '#title' => t('Rewrite all node link to theire term node ?'),
  112. '#default_value' => $settings['redirect_node_to_term'],
  113. );
  114. $form['show_create_tode'] = array(
  115. '#type' => 'checkbox',
  116. '#title' => t('Show create "tode" on term fields about this tode\'s vocabulary ?'),
  117. '#default_value' => $settings['show_create_tode'],
  118. );
  119. return $form;
  120. }
  121. function _tode_widget_settings_maxlength_validate($element, &$form_state) {
  122. // dsm('_tode_widget_settings_maxlength_validate');
  123. // dsm($element, '$element');
  124. // dsm($form_state, '$form_state');
  125. $widget = $form_state['values']['instance']['widget'];
  126. $value = $widget['settings']['maxlength'];
  127. if (!is_numeric($value) || intval($value) != $value || $value <= 0) {
  128. form_error($element, t('"Maximum length" must be a positive integer.'));
  129. }
  130. # TODO checke that node to term and term to node can't be chexked together
  131. }
  132. /**
  133. * Implementation of FAPI hook_elements().
  134. *
  135. * Any FAPI callbacks needed for individual widgets can be declared here,
  136. * and the element will be passed to those callbacks for processing.
  137. *
  138. * Drupal will automatically theme the element using a theme with
  139. * the same name as the hook_elements key.
  140. *
  141. * Autocomplete_path is not used by text_widget but other widgets can use it
  142. * (see nodereference and userreference).
  143. *
  144. * http://drupal.org/node/224333#hook_element_info
  145. *
  146. */
  147. #hook_element_info() WHY ??
  148. /*
  149. function tode_element_info(){
  150. return array(
  151. 'tode' => array(
  152. '#input' => TRUE,
  153. '#columns' => array('value'),
  154. '#delta' => 0,
  155. '#process' => array('tode_element_process'),
  156. ),
  157. );
  158. }
  159. */
  160. /**
  161. * Implementation of hook_field_widget_form().
  162. */
  163. function tode_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  164. // dsm('- - - - tode_field_widget_form');
  165. // dsm($form, '&$form');
  166. // dsm($form_state, '&$form_state');
  167. // dsm($items, 'items');
  168. // dsm($element, '$element');
  169. // dsm($field, '$field');
  170. // dsm($instance, '$instance');
  171. if(isset($form_state['node'])){
  172. $language = $form_state['node']->language;
  173. }else{
  174. $language = null;
  175. }
  176. // dsm($node, '$node');
  177. if(isset($items[$delta])){
  178. $term = taxonomy_term_load($items[$delta]['tid']);
  179. // $term = i18n_taxonomy_term_get_translation($term, $node->language); // marche pas avec localized term
  180. // dsm($term, '$term');
  181. $term_parents = taxonomy_get_parents($term->tid);
  182. // dsm($term_parents, '$term_parents');
  183. $term_parent = array_pop($term_parents);
  184. }
  185. # no need of $node->translation_source because with node translation (not entity fields translation) tid remains
  186. # just have to translate term name on submit
  187. // if( !isset($node->id) && isset($node->translation_source) ){
  188. // }
  189. $form['tode_tid'] = array('#type' => 'hidden', '#value' => isset($term) ? $term->tid : 0,'#delta' => $element['#delta'],);
  190. # parent
  191. if( $instance['widget']['settings']['choose_term_parent'] ){
  192. // add parent form autocomplete if activated
  193. $vocabularies = array();
  194. foreach ($field['settings']['allowed_values'] as $tree)
  195. if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary']))
  196. $vocabularies[$vocabulary->vid] = $vocabulary;
  197. $vocabulary = reset($vocabularies);
  198. // dsm($vocabulary, '$vocabulary');
  199. $form['tode_vid'] = array('#type' => 'hidden', '#value' => $vocabulary->vid,'#delta' => $element['#delta'],);
  200. $form['tode_parent_term'] = array(
  201. '#title' => 'Parent ' . $vocabulary->name,
  202. '#type' => 'textfield',
  203. '#default_value' => isset($term_parent) ? $term_parent->name : '',
  204. '#autocomplete_path' => 'taxonomy/autocomplete' . '/' . $field['field_name'],
  205. '#size' => 60,
  206. '#maxlength' => 1024,
  207. '#element_validate' => array('tode_parent_autocomplete_validate'),
  208. );
  209. }else{
  210. // if parent selection not enabled set the current parent as hidden input
  211. $form['tode_parent_term'] = array(
  212. '#type' => 'hidden',
  213. '#value' => isset($term_parent) ? $term_parent->name : '',
  214. '#delta' => $element['#delta'],
  215. );
  216. }
  217. // set element form item
  218. $element += array(
  219. '#type' => 'textfield',
  220. '#default_value' => isset($term) ? ( module_exists('i18n_taxonomy') ? i18n_taxonomy_term_name($term, $language) : $term->name ) : '',
  221. '#size' => $instance['widget']['settings']['size'],
  222. '#maxlength' => $instance['widget']['settings']['maxlength'],
  223. '#element_validate' => array('tode_widget_validate'),
  224. // '#element_submit' => array('tode_widget_submit'),
  225. );
  226. # add the term edit form
  227. if(isset($term))
  228. _tode_add_term_form($form, $term, $instance, $element['#delta']);
  229. // dsm($form, 'end tode_field_widget_form :: $form');
  230. return $element;
  231. }
  232. /**
  233. * _tode_add_term_form($term)
  234. *
  235. * show the complete taxonomy term form if feature is enabled
  236. *
  237. */
  238. function _tode_add_term_form(&$form, $term, $instance, $delta){
  239. // dsm('- - - - _tode_add_term_form');
  240. module_load_include('inc', 'taxonomy', 'taxonomy.admin');
  241. $term_form = _tode_term_form($term);
  242. // dsm($term_form, 'term_form');
  243. if($term_form){
  244. unset($term_form['actions']);
  245. unset($term_form['#action']);
  246. unset($term_form['#method']);
  247. #unset vid info 'cause is in conflict with the node's vid field
  248. unset($term_form['vid']);
  249. $term_form = _tode_clean_form($term_form);
  250. $prefix = 'tode_termform_'.$instance['field_name'];
  251. $term_form = _tode_prefix_form($term_form, $prefix.'_');
  252. $visible = $instance['widget']['settings']['show_term_form'];
  253. $form[$prefix] = array(
  254. '#type' => 'fieldset',
  255. '#title' => t('Term edit'),
  256. '#tree' => TRUE,
  257. '#collapsible' => TRUE,
  258. '#collapsed' => FALSE,
  259. '#weight'=>1,
  260. // '#group'=>'additional_settings',
  261. '#prefix' => $visible ? null : '<div style="display:none;">',
  262. '#suffix' => $visible ? null : '</div>',
  263. );
  264. $form[$prefix] += $term_form;
  265. // $form['tode_termform'] = array('#type' => 'hidden', '#value' => 'true','#delta' => $delta,);
  266. }
  267. }
  268. /**
  269. * Form element validate handler for tode parent term autocomplete element.
  270. */
  271. function tode_parent_autocomplete_validate($element, &$form_state) {
  272. // dsm('- - - - tode_autocomplete_validate');
  273. // dsm($element, '$element');
  274. // dsm($form_state, '$form_state');
  275. $value = array();
  276. if ($typed_term = $element['#value']) {
  277. // Translate term names into actual terms.
  278. // See if the term exists in the chosen vocabulary and return the tid;
  279. // otherwise, set error.
  280. if ($possibilities = taxonomy_term_load_multiple(array(), array('name' => trim($typed_term), 'vid' => $form_state['values']['tode_vid']))) {
  281. $term = array_pop($possibilities);
  282. $value = (array)$term;
  283. }
  284. else {
  285. form_error($element, t('Parent term can only be an existing term.'));
  286. }
  287. }
  288. //
  289. // dsm('form_set_value');
  290. form_set_value($element, $value, $form_state);
  291. }
  292. /**
  293. * Validation function for the tode element
  294. *
  295. * parses input and sets the values as needed (tid) for storing the data
  296. */
  297. function tode_widget_validate($element, &$form_state){
  298. // dsm('- - - - tode_widget_validate');
  299. // dsm($form_state, 'form_state');
  300. // dsm($element, 'element');
  301. // $node = $form_state['node'];
  302. $value = array();
  303. if ($typed_term = $element['#value']) {
  304. $field = field_widget_field($element, $form_state);
  305. // dsm($field, 'field');
  306. $vocabularies = array();
  307. foreach ($field['settings']['allowed_values'] as $tree) {
  308. if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
  309. $vocabularies[$vocabulary->vid] = $vocabulary;
  310. }
  311. }
  312. // get the parent term tid or 0 (no parents)
  313. $parent_tid = isset($form_state['values']['tode_parent_term']['tid']) ? $form_state['values']['tode_parent_term']['tid'] : 0;
  314. // See if the term already exists and load the term
  315. // otherwise, create a new 'autocreate' term for insert.
  316. if($tid = $form_state['values']['tode_tid']){
  317. $term = taxonomy_term_load($tid);
  318. $term->name = $typed_term;
  319. $term->parent = $parent_tid;
  320. }else{
  321. $vocabulary = reset($vocabularies);
  322. $term = (object)array(
  323. 'tid' => 'autocreate', // autocreate not needed with direct taxonomy_term_save [EDIT] autocreate needed because of the hook_submit insted of hook_validate
  324. 'vid' => $vocabulary->vid,
  325. 'name' => $typed_term,
  326. 'vocabulary_machine_name' => $vocabulary->machine_name,
  327. 'parent' => $parent_tid,
  328. );
  329. // taxonomy_term_save($term); // save here the new term to directly get the tid on hook_node_validate
  330. // [EDIT] do not save the term here because it will be save even if node form is not validate
  331. // results term without node or mutiple orfan term
  332. // moving all process on node submit
  333. }
  334. // dsm($term, '$term');
  335. $value = (array)$term;
  336. }
  337. // dsm($value, '$value');
  338. form_set_value($element, $value, $form_state);
  339. }
  340. /**
  341. * Implements hook_field_widget_error().
  342. */
  343. function tode_field_widget_error($element, $error, $form, &$form_state) {
  344. # use this to set errors
  345. form_error($element['value'], $error['message']);
  346. }
  347. /**
  348. * Implements hook_node_submit().
  349. */
  350. function tode_node_submit($node, $form, &$form_state) {
  351. // dsm('- - - tode_node_submit');
  352. // dsm($node, '$node');
  353. // dsm($form, '$form');
  354. // dsm($form_state, '$form_state');
  355. module_load_include('inc', 'taxonomy', 'taxonomy.admin');
  356. // module_load_include('inc', 'i18n_string', 'i18n_string.pages');
  357. //
  358. $tode_fields = _tode_get_node_tode_fields_def($node);
  359. // dsm($tode_fields, '$tode_fields');
  360. if(!count($tode_fields))
  361. return;
  362. $language = $node->language;
  363. $default_language = language_default('language');
  364. // dsm($default_language, '$default_language');
  365. foreach ($tode_fields as $field_name => $field) {
  366. // retreive the prefixed termfom values (hidden or visible)
  367. $prefix = 'tode_termform_'.$field_name;
  368. // if term_form is not available
  369. // it meens that we are on the creation of a node
  370. // hook_node_presave will handle this case
  371. if( !isset($form_state['values'][$prefix]) )
  372. continue;
  373. // else
  374. // we are on the update or on the translation (from initial node)
  375. // so we can go forward
  376. // retreive the initial tode_field language
  377. $init_language = $form[$field_name]['#language'];
  378. // retreive the value of term field, to get the typed term name
  379. if(isset($form_state['values'][$field_name][$init_language][0])){
  380. $tode_field_term_value = $form_state['values'][$field_name][$init_language][0];
  381. }else if(isset($form_state['values'][$field_name][$language][0])){
  382. $tode_field_term_value = $form_state['values'][$field_name][$language][0];
  383. }else if(isset($form_state['values'][$field_name]['und'][0])){
  384. $tode_field_term_value = $form_state['values'][$field_name]['und'][0];
  385. }else{
  386. drupal_set_message('$form_state[values][$field_name][$language][0] is undefined', 'warning');
  387. $tode_field_term_value = false;
  388. }
  389. // dsm($tode_field_term_value, '$tode_field_term_value');
  390. if($tode_field_term_value){
  391. // dsm($form_state['values'][$prefix], '$form_state["values"][$prefix]');
  392. $values = _tode_prefix_form($form_state['values'][$prefix], $prefix.'_', FALSE);
  393. // dsm($values, 'values');
  394. if($field['field_info_field']['translatable'] && module_exists('i18n_taxonomy') && $language != 'und' && $language != $default_language){
  395. // $field['field_info_field']['translatable'] is about entity translation module
  396. $context= array('term',$values['tid'],'name');
  397. i18n_string_textgroup('taxonomy')->update_translation($context, $language, $tode_field_term_value['name']);
  398. $tode_field_term_value['name'] = $values['name'];
  399. }
  400. $parent_tid = $tode_field_term_value['parent'];
  401. $values['parent'] = array($parent_tid => $parent_tid);
  402. // define the form_state for term_form submit
  403. $new_term_form_state = array(
  404. 'build_info'=>array(
  405. 'args'=>array(0=>(object)$tode_field_term_value),
  406. ),
  407. "values"=>array(
  408. 'name'=> $tode_field_term_value['name'], // replace the original (hidden) term name value by the typed in the the entity field
  409. 'op'=> t('Save'),
  410. )
  411. );
  412. // add new values to form_state
  413. $new_term_form_state['values'] += $values;
  414. // dsm($new_term_form_state, 'form_state');
  415. drupal_form_submit('taxonomy_form_term', $new_term_form_state);
  416. }
  417. }
  418. }
  419. /**
  420. * Implements hook_node_presave().
  421. */
  422. function tode_node_presave($node) {
  423. // dsm('- - - tode_node_presave');
  424. // dsm($node, 'node');
  425. if(isset($node->nid))
  426. return;
  427. $tode_fields = _tode_get_node_tode_fields_def($node);
  428. if(!count($tode_fields))
  429. return;
  430. $default_language = language_default('language');
  431. // purpose of that is to directly atribute the right language to the term
  432. foreach ($tode_fields as $field_name => $field){
  433. $tode_field = $node->$field_name;
  434. if(!isset($tode_field[$node->language][0]))
  435. continue;
  436. $tode_field_term = $tode_field[$node->language][0]; // on node creation field language is always to und (sure ?)
  437. // // test the language, if not default language create the term name translation
  438. if( module_exists('i18n_taxonomy') && ( $node->language != 'und' || $node->language != $default_language ) ){
  439. $context= array('term',$tode_field_term['tid'],'name');
  440. i18n_string_textgroup('taxonomy')->update_translation($context, $node->language, $tode_field_term['name']);
  441. }
  442. }
  443. }
  444. /*
  445. TODO merge two terms when translation is made by selecting a node whiche already exists
  446. */
  447. /*
  448. TODO create a node when a term is created else where than the node which got the tode
  449. */
  450. /**
  451. * Implements hook_menu().
  452. */
  453. function tode_menu() {
  454. // dsm('tode_menu');
  455. $items = array();
  456. $items['tode/%ctools_js/add'] = array(
  457. 'title' => 'Tode modal add entity',
  458. 'page callback' => 'tode_entity_add',
  459. 'page arguments' => array(1,3,4,5),
  460. 'access callback' => TRUE,
  461. 'type' => MENU_CALLBACK,
  462. );
  463. return $items;
  464. }
  465. function tode_entity_add($js = FALSE, $bundle, $entity, $title = 'Title'){
  466. // Fall back if $js is not set.
  467. if (!$js) {
  468. return drupal_get_form('tode_entity_add_form', $bundle, $entity);
  469. }
  470. ctools_include('modal');
  471. ctools_include('ajax');
  472. $form_state = array(
  473. 'title' => t('Create '.$entity),
  474. 'ajax' => TRUE,
  475. 'build_info' => array('args' => array('0' => $bundle, '1' => $entity, '2' => $title)),
  476. );
  477. $output = ctools_modal_form_wrapper('tode_entity_add_form', $form_state);
  478. if (!empty($form_state['executed'])) {
  479. $commands = array();
  480. $commands[] = ctools_modal_command_dismiss();
  481. print ajax_render($commands);
  482. exit;
  483. }
  484. else {
  485. print ajax_render($output);
  486. exit;
  487. }
  488. }
  489. function tode_entity_add_form($form, $form_state, $bundle, $entity, $title) {
  490. $form = array();
  491. $form['title'] = array(
  492. '#type' => 'textfield',
  493. '#title' => t($title),
  494. '#size' => 40,
  495. '#maxlength' => 255,
  496. );
  497. $form['bundle'] = array(
  498. '#type' => 'hidden',
  499. '#value' => $bundle,
  500. );
  501. $form['entity'] = array(
  502. '#type' => 'hidden',
  503. '#value' => $entity,
  504. );
  505. $form['create'] = array(
  506. '#type' => 'submit',
  507. '#value' => t('Create'),
  508. );
  509. return $form;
  510. }
  511. function tode_entity_add_form_validate($form, &$form_state){
  512. if(empty($form_state['values']['title'])){
  513. form_set_error('title', 'Title field can\'t be empty!');
  514. }
  515. }
  516. function tode_entity_add_form_submit($form, &$form_state){
  517. global $user;
  518. // dsm($form_state, '$form_state');
  519. $values = $form_state['values'];
  520. $bundle_fields = field_info_instances($values['bundle']);
  521. // dsm($bundle_fields, '$bundle_fields');
  522. $fields = $bundle_fields[$values['entity']];
  523. // dsm($fields, '$fields');
  524. foreach ($fields as $field_name => $field) {
  525. if($field['widget']['type'] == 'tode'){
  526. $tode_field = $field;
  527. break;
  528. }
  529. }
  530. # get vocabulary
  531. $tode_field_infos = field_info_field($field['field_name']);
  532. // dsm($tode_field_infos, '$tode_field_infos');
  533. $voc_name = $tode_field_infos['settings']['allowed_values'][0]['vocabulary'];
  534. $vocabulary = taxonomy_vocabulary_machine_name_load($voc_name);
  535. // dsm($vocabulary, '$vocabulary');
  536. # create the term
  537. $term = new stdClass();
  538. $term->name = $values['title'];
  539. $term->vid = $vocabulary->vid; // ‘1’ is a vocabulary id you wish this term to assign to
  540. // $term->field_custom_field_name[LANGUAGE_NONE][0]['value'] = ‘Some value’; // OPTIONAL. If your term has a custom field attached it can added as simple as this
  541. taxonomy_term_save($term); // Finally, save our term
  542. // dsm($term, '$term');
  543. # create the node
  544. //http://www.group42.ca/creating_and_updating_nodes_programmatically_in_drupal_7
  545. //http://timonweb.com/how-programmatically-create-nodes-comments-and-taxonomies-drupal-7
  546. $node = new stdClass();
  547. $node->type = $values['entity'];
  548. node_object_prepare($node);
  549. $tode_field_name = $tode_field['field_name'];
  550. $node = (array)$node +array(
  551. "title" => $values['title'],
  552. "language" => LANGUAGE_NONE,
  553. "uid" => $user->uid,
  554. $tode_field_name => array( LANGUAGE_NONE => array(0 => array('tid' => $term->tid))),
  555. );
  556. // $node->$tode_field_name[$node->language]['tid'] = $term->tid;
  557. // $node->$tode_field_name = array( $node->language => array('tid' => $term->tid));
  558. // $tode_field_name = $tode_field['field_name'];
  559. // $node = (object) array(
  560. // "type" => $values['entity'],
  561. // // node_object_prepare($node);
  562. // "title" => $values['title'],
  563. // "language" => LANGUAGE_NONE,
  564. // "uid" => $user->uid,
  565. // $tode_field_name => array( LANGUAGE_NONE=>array('tid' => $term->tid)),
  566. // );
  567. $node = (object)$node;
  568. // dsm($node, '$node');
  569. node_save($node);
  570. // dsm($node, '$node');
  571. # tag the node with the term
  572. # set the tode field to the term tid
  573. }
  574. function BAD_ONE_tode_entity_add_form($js = FALSE, $bundle, $entity) {
  575. // dsm('- - - - tode_entity_add_form');
  576. // dsm($bundle, '$bundle');
  577. // dsm($entity, '$entity');
  578. // error_log($js);
  579. // error_log($bundle);
  580. // error_log($entity);
  581. //
  582. global $user;
  583. //module_load_include('inc', 'node', 'node.pages');
  584. ctools_include('node.pages', 'node', '');
  585. $node = (object) array(
  586. 'uid' => $user->uid,
  587. 'name' => (isset($user->name) ? $user->name : ''),
  588. 'type' => $entity,
  589. 'language' => LANGUAGE_NONE
  590. );
  591. $form_id = $entity.'_node_form';
  592. if (!$js) {
  593. return drupal_get_form($form_id, $node);
  594. }
  595. ctools_include('modal');
  596. ctools_include('ajax');
  597. $form_state = array(
  598. 'title' => t('Add '.$entity),
  599. 'ajax' => TRUE,
  600. );
  601. $form_state['build_info']['args'] = array($node);
  602. $output = ctools_modal_form_wrapper($entity.'_node_form', $form_state);
  603. if (!empty($form_state['executed'])) {
  604. $output = array();
  605. $output[] = ctools_modal_command_display( t('Node created'), '<div class="modal-message">'.$entity.' creation successful.</div>'); /** Add success message*/
  606. };
  607. print ajax_render($output);
  608. exit;
  609. }
  610. /**
  611. * Implements hook_field_widget_form_alter().
  612. *
  613. * add create entity bundle front of term reference field with tode voc
  614. */
  615. function DESACTIVATED_tode_field_widget_form_alter(&$element, &$form_state, $context) {
  616. $field = $context['field'];
  617. $instance = $context['instance'];
  618. if($field['type'] == 'taxonomy_term_reference' && ($instance['widget']['type'] == 'taxonomy_autocomplete' || $instance['widget']['type'] == 'autocomplete_deluxe_taxonomy')){
  619. // dsm('- - - - - tode_field_widget_form_alter');
  620. // dsm($element, '$element');
  621. // dsm($form_state, '$form_state');
  622. // dsm($context, '$context');
  623. // dsm($instance['widget']['type'], '$instance[widget][type]');
  624. foreach ($field['settings']['allowed_values'] as $key => $value) {
  625. if($tode = _tode_get_voc_tode_field_def($value['vocabulary'])){
  626. ctools_include('ajax');
  627. ctools_include('modal');
  628. ctools_modal_add_js();
  629. // ctools_add_css('tode', 'tode');
  630. // drupal_add_css(drupal_get_path('module', 'tode') . "tode.css");
  631. // dsm($tode, '$tode');
  632. $type_fields = field_info_instances('node');
  633. if(!isset($element['#attributes']["class"]))
  634. $element['#attributes']["class"] = array();
  635. $element['#attributes']["class"][] = "tode-add-modal";
  636. $element['#suffix'] = '';
  637. foreach ($tode['bundles'] as $bundle => $entities) {
  638. foreach ($entities as $entity) {
  639. $tode_instance = $type_fields[$entity][$tode['field_name']];
  640. $btn = ctools_modal_text_button(t('create a new %s', array('%s'=>$entity)), 'tode/nojs/add/'.$bundle.'/'.$entity.'/'.$tode_instance['label'], t('alt'), "button ctools-modal-tode-add-modal") . "\n"; // modal-popup-small
  641. // $btn .= ctools_modal_text_button(t('create new test'), 'tode/nojs/add/node/test', t('alt'));
  642. // dsm($btn);
  643. if($element['#suffix'] == '')
  644. $element['#suffix'] = '<div class="tode-add-modal-links">';
  645. $element['#suffix'] .= $btn;
  646. }
  647. }
  648. }
  649. }
  650. if( isset($element['#suffix']) && $element['#suffix'] != '' ){
  651. $throbber = theme('image', array('path' => ctools_image_path('loading_animation.gif', 'modal_forms'), 'alt' => t('Loading...'), 'title' => t('Loading')));
  652. $js_settings = array(
  653. 'tode-add-modal' => array(
  654. 'modalSize' => array(
  655. 'type' => 'fixed',
  656. 'width' => 500,
  657. 'height' => 200,
  658. ),
  659. 'modalOptions' => array(
  660. 'opacity' => 0.4,
  661. 'background' => '#000',
  662. ),
  663. 'animation' => 'fadeIn',
  664. // 'modalTheme' => 'ModalFormsPopup',
  665. 'throbber' => $throbber,
  666. 'closeText' => t('Close'),
  667. ),
  668. );
  669. drupal_add_js($js_settings, 'setting');
  670. $element['#prefix'] = '<div class="tode-add-modal-wrapper">';
  671. $element['#suffix'] .= '</div></div>';
  672. }
  673. // dsm($element, '$element');
  674. }
  675. }
  676. /**
  677. * Implements hook_taxonomy_term_insert().
  678. */
  679. // function tode_taxonomy_term_insert($term) {
  680. // dsm($term, '$term');
  681. // }
  682. /**
  683. * Implementation of hook_form_alter().
  684. *
  685. * node deletion to delete also the tode term
  686. *
  687. */
  688. function tode_form_alter(&$form, $form_state, $form_id){
  689. // dsm($form_id, 'tode form_alter form_id');
  690. // TODO block the deletion if tode term has children !! because this will delete them to …
  691. if($form_id == 'test_node_form'){
  692. // dsm($form_state);
  693. }
  694. if (stripos($form_id, 'node_delete_confirm') !== false){
  695. // dsm($form_id, 'tode form_alter form_id');
  696. _tode_node_delete_form_alter($form, $form_state);
  697. // dsm($form);
  698. }else if(stripos($form_id, 'node_admin_content') !== false){
  699. if(isset($form['operation']) && $form['operation']['#value'] == 'delete'){
  700. // dsm($form, 'node_admin_content form');
  701. // dsm($form_state, 'form_state');
  702. _tode_nodes_delete_form_alter($form, $form_state);
  703. }
  704. }
  705. }
  706. function _tode_node_delete_form_alter(&$form, $form_state){
  707. // dsm($form, '_tode_node_delete_form_alter : form');
  708. // get the node
  709. $node = $form['#node'];
  710. // dsm($node, '$node');
  711. #get the fields defenition of node type
  712. $tode_fields = _tode_get_node_tode_fields_def($node);
  713. // dsm($tode_fields);
  714. if(count($tode_fields) == 0)
  715. return;
  716. #get the terms value
  717. $terms = array('names'=>array(), 'tids'=>array());
  718. foreach ($tode_fields as $field_name => $field)
  719. _tode_populate_terms_node_delete($terms, $node->$field_name);
  720. _tode_node_delete_prepare_form($form, $terms);
  721. }
  722. function _tode_nodes_delete_form_alter(&$form, $form_state){
  723. // dsm($form, '_tode_nodes_delete_form_alter : form');
  724. $nodes = array();
  725. foreach ($form_state['values']['nodes'] as $nid => $actif)
  726. if($actif)
  727. $nodes[] = node_load($nid);
  728. #get the terms value
  729. $terms = array('names'=>array(), 'tids'=>array());
  730. foreach ($nodes as $node) {
  731. #get the fields definition of node type
  732. $tode_fields = _tode_get_node_tode_fields_def($node);
  733. //dsm($tode_fields);
  734. if(count($tode_fields) == 0)
  735. continue;
  736. foreach ($tode_fields as $field_name => $field)
  737. _tode_populate_terms_node_delete($terms, $node->$field_name);
  738. }
  739. _tode_node_delete_prepare_form($form, $terms);
  740. }
  741. function _tode_populate_terms_node_delete(&$terms, $tode_field){
  742. foreach ($tode_field as $language) {
  743. foreach ($language as $term) {
  744. $term = taxonomy_term_load($term['tid']);
  745. if(isset($term->tid) && !in_array($term->tid, $terms['tids'])){
  746. $terms['names'][] = $term->name;
  747. $terms['tids'][] = $term->tid;
  748. }
  749. }
  750. }
  751. }
  752. function _tode_node_delete_prepare_form(&$form, $terms){
  753. if(count($terms)){
  754. /*
  755. TODO add here a checkbox to select terms to delete
  756. */
  757. #add some warning in form description
  758. $form['description']['#markup'] .= '<br />'.t('this will also delete taxonomy terms : %terms', array('%terms'=>implode(', ', $terms['names'])));
  759. $form['tode_delete'] = array( '#type' => 'hidden', '#value' => serialize($terms['tids']),);
  760. $form['tode_terms'] = array('#type' => 'hidden', '#value' => serialize($terms['names']),);
  761. $form['#submit'][] = 'tode_delete_submit';
  762. }
  763. }
  764. function tode_delete_submit($form, &$form_state){
  765. $tids = unserialize($form['tode_delete']['#value']);
  766. foreach ($tids as $tid)
  767. taxonomy_term_delete($tid);
  768. $terms = unserialize($form['tode_terms']['#value']);
  769. drupal_set_message(t('Following Taxonomy terms have been deleted : %terms', array('%terms' => implode(', ', $terms) )), 'status');
  770. }
  771. /**
  772. * Implements hook_url_outbound_alter().
  773. */
  774. function tode_url_outbound_alter(&$path, &$options, $original_path) {
  775. // dsm('tode_url_outbound_alter');
  776. # terms url
  777. $term = false;
  778. $node = false;
  779. // if(isset($options['entity_type']) && $options['entity_type'] == 'taxonomy_term'){
  780. // // dsm('- - - - tode_url_outbound_alter');
  781. // // dsm($path, '$path');
  782. // // dsm($options, '$options');
  783. // // dsm($original_path, '$original_path');
  784. //
  785. // $term = $options['entity'];
  786. //
  787. // }else{
  788. // $args = explode('/', $original_path);
  789. // // dsm($args, 'args');
  790. // if($args[0] == 'taxonomy' && $args[1] == 'term' && is_numeric($args[2]) ){
  791. // $term = taxonomy_term_load($args[2]);
  792. // }
  793. //
  794. // }
  795. if (preg_match('/^node\/([0-9]*)$/', $path, $matches)) {
  796. $node = node_load($matches[1]);
  797. }
  798. # WARNING works only because nodeandtermrelink_url_outbound_alter() is trigered before this (KADIST)
  799. if (preg_match('/^taxonomy\/term\/([0-9]*)$/', $path, $matches)) {
  800. $term = taxonomy_term_load($matches[1]);
  801. }
  802. if($node){
  803. // dsm($node, 'node');
  804. if($fields = _tode_get_node_tode_fields_def($node)){
  805. foreach ($fields as $field_name => $field) {
  806. if (isset($field['widget']['settings']['redirect_node_to_term']) && $field['widget']['settings']['redirect_node_to_term']) {
  807. dsm($field, '$field');
  808. $items = field_get_items('node', $node, $field_name);
  809. dsm($items, 'items');
  810. $new_path = 'taxonomy/term/'.$items[0]['tid'];
  811. if( $new_alias = drupal_get_path_alias($new_path) ){//, $options['language']->language
  812. $path = $new_alias;
  813. $original_path = $new_path;
  814. $options['alias'] = TRUE;
  815. }else{
  816. $path = $new_path;
  817. }
  818. break;
  819. }
  820. }
  821. }
  822. }
  823. if($term){
  824. // dsm($term, '$term : '.$term->name);
  825. if($field = _tode_get_voc_tode_field_def($term->vocabulary_machine_name)){
  826. // dsm($field, '$field');
  827. // $field_settings = field_info_instance_settings($field->type);//field_info_widget_settings($field->field_name); //
  828. // dsm($field_settings, '$field_settings');
  829. # TODO add if widget setting; how to get the settings
  830. if (isset($field['widget']['settings']['redirect_term_to_node']) && $field['widget']['settings']['redirect_term_to_node']) {
  831. // dsm($field, '$field');
  832. // dsm($options, '$options');
  833. $sr = $field['field_info_field']['storage']['details']['sql']['FIELD_LOAD_CURRENT'];
  834. foreach ($sr as $table => $column) {
  835. $query = db_select($table, 'ft');
  836. $query->join('node', 'n', 'ft.entity_id = n.nid AND n.language = :language OR n.language = :language', array(':language' => $options['language']->language, ':language' => 'und'));
  837. $query
  838. ->fields('ft')
  839. ->condition('ft.'.$column['tid'], $term->tid);
  840. $result = $query->execute();
  841. break;
  842. }
  843. foreach ($result as $node) {
  844. // dsm($node, '$node');
  845. $new_path = 'node/'.$node->entity_id;
  846. if( $new_alias = drupal_get_path_alias($new_path, $options['language']->language) ){
  847. $path = $new_alias;
  848. $original_path = $new_path;
  849. $options['alias'] = TRUE;
  850. }else{
  851. $path = $new_path;
  852. }
  853. break;
  854. }
  855. }
  856. }
  857. }
  858. }
  859. /**
  860. * Implements hook_field_formatter_info().
  861. */
  862. function tode_field_formatter_info() {
  863. return array(
  864. 'tode' => array(
  865. 'label' => t('tode (node of the term)'),
  866. 'field types' => array('taxonomy_term_reference'),
  867. 'settings'=>array('viewmode' => "full"),
  868. ),
  869. );
  870. }
  871. /**
  872. * Implements hook_field_formatter_settings_form().
  873. */
  874. function tode_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  875. $element = array();
  876. $display = $instance['display'][$view_mode];
  877. $settings = $display['settings'];
  878. $formatter = $display['type'];
  879. if($formatter == 'tode'){
  880. if($todefield = _tode_get_voc_tode_field_def($field['settings']['allowed_values'][0]['vocabulary'])){
  881. $entity_infos = entity_get_info();
  882. foreach ($entity_infos['node']['view modes'] as $viewmode => $value) {
  883. $viewmode_options[$viewmode] = $value['label'];
  884. }
  885. $element['viewmode'] = array(
  886. '#type' => 'select',
  887. '#title' => t('View mode'),
  888. '#default_value' => $settings['viewmode'],
  889. '#description' => t('select the view mode for the node'),
  890. '#options' => $viewmode_options,
  891. );
  892. }else{
  893. // TODO: tell that vocabulary has any tode field
  894. }
  895. }
  896. return $element;
  897. }
  898. /**
  899. * Implements hook_field_formatter_settings_summary().
  900. */
  901. function tode_field_formatter_settings_summary($field, $instance, $view_mode) {
  902. $display = $instance['display'][$view_mode];
  903. if($display['type'] == 'tode'){
  904. $settings = $display['settings'];
  905. $summary = t('viewmode : %vm', array('%vm'=> isset($settings['viewmode']) ? $settings['viewmode'] : 'full') );
  906. return $summary;
  907. }
  908. }
  909. /**
  910. * Implements hook_field_formatter_view().
  911. */
  912. function tode_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  913. $element = array();
  914. $settings = $display['settings'];
  915. switch ($display['type']) {
  916. case 'tode':
  917. foreach ($items as $delta => $item) {
  918. $term = taxonomy_term_load($item['tid']);
  919. $entitys = tode_get_nids_from_term($term);
  920. $nodes = array();
  921. if(isset($entitys['node'])){
  922. foreach ($entitys['node'] as $nid => $n) {
  923. $node = node_load($nid);
  924. $nodes[$nid] = $node;
  925. }
  926. $element[$delta] = array(
  927. '#theme' => 'tode_node_formatter',
  928. '#item' => $item,
  929. '#viewmode' => $settings['viewmode'],
  930. '#nodes'=> $nodes,
  931. );
  932. }
  933. }
  934. break;
  935. }
  936. return $element;
  937. }
  938. function theme_tode_node_formatter($vars){
  939. // dsm($vars, 'theme_tode_node_formatter | vars');
  940. print render(entity_view('node', $vars['nodes'], $vars['viewmode']));
  941. }
  942. /**
  943. * HELPERS
  944. */
  945. function _tode_clean_form($form, $level = 0){
  946. foreach ($form as $key => $value) {
  947. if(strpos($key,'#') !== false || $key == 'form_build_id' || $key == 'form_id' || $key == 'form_token'){
  948. if( $level == 0 || $key == "#element_validate")
  949. unset($form[$key]);
  950. }elseif(is_array($value)){
  951. $form[$key] = _tode_clean_form($value, $level+1);
  952. }
  953. }
  954. return $form;
  955. }
  956. function _tode_prefix_form($form, $prefix = '', $add = TRUE){
  957. foreach ($form as $key => $value) {
  958. if(strpos($key,'#') === false){
  959. if((isset($value['#type']) && $value['#type'] == 'fieldset') || (!$add && is_array($value)))
  960. $value = _tode_prefix_form($value, $prefix, $add);
  961. if($add){
  962. $form[$prefix.$key] = $value;
  963. unset($form[$key]);
  964. }elseif(strpos($key, $prefix) !== false ){
  965. $form[str_replace($prefix, '', $key)] = $value;
  966. unset($form[$key]);
  967. }
  968. }
  969. }
  970. return $form;
  971. }
  972. /**
  973. * _tode_term_form($tid)
  974. *
  975. */
  976. function _tode_term_form($term){
  977. // dsm('_tode_term_form');
  978. if ($term) {
  979. $form_state = array(
  980. 'build_info'=>array(
  981. 'args'=>array(0=>$term)
  982. ),
  983. 'method'=>'post',
  984. );
  985. // function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary = NULL) {
  986. $term_form = drupal_retrieve_form('taxonomy_form_term', $form_state);
  987. drupal_prepare_form('taxonomy_form_term', $term_form, $form_state);
  988. return $term_form;
  989. }else{
  990. return false;
  991. }
  992. }
  993. /**
  994. * _tode_trim_options(&$form, $element)
  995. *
  996. */
  997. function _tode_trim_options(&$form, $item){
  998. foreach ($form[$item] as $field_name => $field) {
  999. if(((is_array($field)) && $field['#type'] == 'select') && $field['#multiple']){
  1000. $options = $field['#options'];
  1001. for ($i=0; $i < count($options); $i++) {
  1002. if(!isset($options[$i]->option))
  1003. continue;
  1004. $op = array();
  1005. foreach ($options[$i]->option as $key => $value)
  1006. $op[$key] = strlen($value) > 25 ? substr_replace ($value, ' [...] ', 15, -10) : $value; // affiche 'abc...xyz'
  1007. $options[$i]->option = $op;
  1008. }
  1009. $field['#options'] = $options;
  1010. $form[$item][$field_name] = $field;
  1011. }
  1012. }
  1013. }
  1014. /**
  1015. * _tode_get_node_tode_fields_def
  1016. */
  1017. function _tode_get_node_tode_fields_def($node){
  1018. // dsm($node, '_tode_get_fields_def');
  1019. #get the fields defenition of node type
  1020. $type_fields = field_info_instances('node');
  1021. // dsm($type_fields, 'type_fields definition');
  1022. #get the tode node fields
  1023. $tode_fields = array();
  1024. foreach ($type_fields[$node->type] as $field_name => $field) {
  1025. if($field['widget']['type'] == 'tode'){
  1026. $fieldinfos = field_info_field($field_name);
  1027. $field['field_info_field'] = $fieldinfos;
  1028. $tode_fields[$field_name] = $field;
  1029. }
  1030. }
  1031. return $tode_fields;
  1032. }
  1033. function _tode_get_voc_tode_field_def($voc_name){
  1034. $type_fields = field_info_instances('node');
  1035. // dsm($type_fields, '$type_fields');
  1036. foreach ($type_fields as $nodetype => $fields)
  1037. foreach ($fields as $field_name => $field)
  1038. if($field['widget']['type'] == 'tode'){
  1039. // dsm($field, '$field');
  1040. $fieldinfos = field_info_field($field_name);
  1041. // dsm($fieldinfos, '$fieldinfos');
  1042. $field['field_info_field'] = $fieldinfos;
  1043. if($voc_name == $fieldinfos['settings']['allowed_values'][0]['vocabulary'])
  1044. return $field;
  1045. }
  1046. return false;
  1047. }
  1048. function tode_get_nids_from_term($term, $language = false){
  1049. if(isset($term->vocabulary_machine_name) && $todefield = _tode_get_voc_tode_field_def($term->vocabulary_machine_name)){
  1050. // $fieldinfos = field_info_field($todefield->field_name);
  1051. $query = new EntityFieldQuery();
  1052. $query->entityCondition('entity_type', 'node')
  1053. ->fieldCondition($todefield['field_name'], 'tid', $term->tid);
  1054. if($language)
  1055. $query->propertyCondition('language', array('und', $language), 'IN');
  1056. $result = $query->execute();
  1057. if(count($result))
  1058. return $result;
  1059. }
  1060. return false;
  1061. }