Fix for A custom node type for vehicles using taxonomy to manage make / model details

  1. <?php
  2. // $Id: vehicle.module,v 1.26 2008/10/9 13:40:00 michaelphipps Exp $
  3.  
  4. /**
  5.  * @node_vehicle
  6.  * This module defines a custom node type with a custom make / model form element
  7.  * that is managed via taxonomy.
  8.  *
  9.  * No Database required
  10.  */
  11.  
  12. /**
  13.  * Implementation of hook_node_info().
  14.  */
  15. function node_vehicle_node_info() {
  16.   return array(
  17.     'vehicle' => array(
  18.       'name' => t('Vehicle'),
  19.       'module' => 'node_vehicle',
  20.       'description' => t("This is an Vehicle type with a few fields."),
  21.       'has_title' => TRUE,
  22.       'title_label' => t('Example Title'),
  23.       'has_body' => TRUE,
  24.       'body_label' => t('Example Body'),
  25.     )
  26.   );
  27. }
  28.  
  29. /**
  30. * Implementation of hook_menu().
  31. *
  32. *  Provide a simple menu hook to access the javascript function.
  33. */
  34. function node_vehicle_menu() {
  35.   $items['vehicle/js'] = array(
  36.     'type' => 'MENU_CALLBACK',
  37.     'page callback' => 'node_vehicle_makemodel_javascript',
  38.     'access arguments' => array('access content'),
  39.   );
  40.   return $items;
  41. }
  42.  
  43. /**
  44.  * Implementation of hook_access().
  45.  */
  46. function node_vehicle_access($op, $node, $account) {
  47.   if ($op == 'create') {
  48.     return user_access('create vehicle content', $account);
  49.   }
  50.  
  51.   if ($op == 'update') {
  52.     if (user_access('edit any vehicle content', $account) || (user_access('edit own vehicle content', $account) && ($account->uid == $node->uid))) {
  53.       return TRUE;
  54.     }
  55.   }
  56.  
  57.   if ($op == 'delete') {
  58.     if (user_access('delete any vehicle content', $account) || (user_access('delete own vehicle content', $account) && ($account->uid == $node->uid))) {
  59.       return TRUE;
  60.     }
  61.   }
  62. }
  63.  
  64. /**
  65.  * Implementation of hook_perm().
  66.  */
  67. function node_vehicle_perm() {
  68.   return array(
  69.     'create vehicle content',
  70.     'delete own vehicle content',
  71.     'delete any vehicle content',
  72.     'edit own vehicle content',
  73.     'edit any vehicle content',
  74.   );
  75. }
  76.  
  77.  
  78. /**
  79.  * Implementation of hook_elements().
  80.  */
  81. function node_vehicle_elements() {
  82.   $type['makemodel'] = array(
  83.     '#input' => TRUE,
  84.     '#process' => array('node_vehicle_makemodel_expand'),
  85.     '#element_validate' => array('node_vehicle_makemodel_validate'),
  86.     '#default_value' => array('make' => '', 'model' => ''),
  87.   );
  88.   return $type;
  89. }
  90.  
  91. /**
  92.  * The process callback to expand the makemodel control.
  93.  */
  94. function node_vehicle_makemodel_expand($element) {
  95.   $element['#tree'] = TRUE;
  96.  
  97.   if ( !isset($element['#value']) ) {
  98.     $element['#value'] = array('make' => '', 'model' => '');
  99.   }
  100.  
  101.   // Get the list of makes from the make/models vocabulary
  102.   $vid = 1;// Should the user choose their Taxonomy category?
  103.   $parent = 0;
  104.   $max_depth = 1;
  105.   $makes = taxonomy_get_tree($vid, $parent, -1, $max_depth);
  106.   foreach ($makes as $make) {
  107.     $make_options[$make->tid] = $make->name;
  108.   }
  109.  
  110.   if ( isset($element['#value']['make']) ) {
  111.     $models = taxonomy_get_tree($vid, $element['#value']['make'], -1, $max_depth);
  112.     foreach ($models as $model) {
  113.       $model_options[$model->tid] = $model->name;
  114.     }
  115.   }
  116.   else {
  117.     $model_options = array();
  118.   }
  119.  
  120.   $element['make'] = array(
  121.     '#type' => 'select',
  122.     '#value' => $element['#value']['make'],
  123.     '#options' => $make_options,
  124.     '#attributes' =>  array('onchange' => 'changeModel();'),
  125.   );
  126.  
  127.   $element['model'] = array(
  128.     '#type' => 'select',
  129.     '#value' => $element['#value']['model'],
  130.     '#options' => $model_options,
  131.   );
  132.  
  133.   return $element;
  134.  
  135. }
  136.  
  137. /**
  138.  * Our element's validation function.
  139.  *
  140.  * Not currently being used, because I'm unsure what validation I need to do on taxonomy
  141.  * data and if it is even needed at all.
  142.  */
  143. function node_vehicle_makemodel_validate($form, &$form_state) {
  144.   if ( isset($form['#value']['make']) ) {
  145.     if (0 == 1) {
  146.       form_error($form['make'], t('Please select a make.'));
  147.     }
  148.   }
  149.   if ( isset($form['#value']['model']) ) {
  150.     if (0 == 1) {
  151.       form_error($form['model'], t('Please select a model.'));
  152.     }
  153.   }
  154.   return $form;
  155.  
  156. }
  157.  
  158.  
  159.  
  160. /**
  161.  * Theme function to format the output.
  162.  *
  163.  * We use the container-inline class so that all three of the HTML elements
  164.  * are placed next to each other, rather than on separate lines.
  165.  */
  166. function theme_makemodel($element) {
  167.   return theme('form_element', $element, '<div class="container-inline">'. $element['#children'] .'</div>');
  168. }
  169.  
  170. function node_vehicle_makemodel_javascript() {
  171.  
  172.   // Getting the list of makes from the make/models vocabulary
  173.   $vid = 1 ;  // Should the user choose their Taxonomy category?
  174.   $parent = 0;
  175.   $max_depth = 1;
  176.   $makes = taxonomy_get_tree($vid, $parent, -1, $max_depth);
  177.   foreach ($makes as $make) {
  178.     $make_options[$make->tid ] = $make->name;
  179.   }
  180.  
  181.   unset($temp);
  182.   $content .=  "model = new Array();\n";
  183.    
  184.   $result = db_query("SELECT term_data.tid AS id, term_hierarchy.parent AS parentid, term_data.name AS model FROM {term_data} join {term_hierarchy} ON (term_data.tid = term_hierarchy.tid) WHERE term_data.vid=1 AND term_hierarchy.parent >0 ORDER BY parentid, model");
  185.   while ($models = db_fetch_object($result)) {
  186.     if ( $temp == $models->parentid ) {
  187.       $content .=  ",";
  188.     }
  189.     else {
  190.       if ( isset($temp) ) $content .=  ");\n";
  191.       $temp=$models->parentid;
  192.       $content .=  "model[$temp] = new Array(\n";
  193.     }
  194.     $content .=  "'". $models->model ."',". $models->id ."\n";
  195.   }
  196.  
  197.   $content .=  ");\n";
  198.  
  199.   $content .= '//change model
  200. function changeModel(){
  201. var objMake = document.getElementById("edit-makemodel-make");
  202. var objModel = document.getElementById("edit-makemodel-model");
  203.  
  204. var make_id = objMake[objMake.selectedIndex].value;
  205.  
  206. //clear model select
  207. oldLength = objModel.length;
  208. for (i = oldLength; i >= 0; i--) objModel.options[i] = null;
  209.  
  210. //build model select
  211. if (make_id=="")
  212.  objModel.options[0] = new Option("Any model","");
  213. else
  214. for (i = 0; i <= model[make_id].length-1; i=i+2)
  215. objModel.options[i/2] = new Option(model[make_id][i],model[make_id][i+1]);
  216.  
  217. }
  218. ';
  219.  
  220.   header("Content-type: text/javascript");
  221.   print $content;
  222.   exit();
  223.  
  224. }
  225.  
  226.  
  227. /**
  228.  * Implementation of hook_form().
  229.  *
  230.  * Describe the form for collecting vehicle information.
  231.  */
  232. function node_vehicle_form(&$node) {
  233.   // The site admin can decide if this node type has a title and body, and how
  234.   // the fields should be labeled. We need to load these settings so we can
  235.   // build the node form correctly.
  236.   $type = node_get_types('type', $node);
  237.  
  238.   if ($type->has_title) {
  239.     $form['title'] = array(
  240.       '#type' => 'textfield',
  241.       '#title' => check_plain($type->title_label),
  242.       '#required' => TRUE,
  243.       '#default_value' => $node->title,
  244.       '#weight' => -5
  245.     );
  246.   }
  247.  
  248.   if ($type->has_body) {
  249.     $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
  250.   }
  251.  
  252.   // Add the javascript to make the make/model selection work properly
  253.   drupal_add_js('vehicle/js');
  254.  
  255.   // Now we define the form elements specific to our node type.  
  256.   $form['makemodel']= array(
  257.     '#type' => 'makemodel',
  258.     '#title' => t("Make/Model"),
  259.     '#default_value' => array('make' => $node->make, 'model' => $node->model),
  260.     '#description' => t('Select a vehicle make and model.'),
  261.   );
  262.  
  263.   return $form;
  264. }
  265.  
  266. /**
  267.  * Implementation of hook_validate().
  268.  *
  269.  * Again, unsure if I need this since I am collecting taxonomy tids from select boxes.
  270.  */
  271. function node_vehicle_validate(&$node) {
  272.   // include validation logic (if required)
  273. }
  274.  
  275. /**
  276.  * Implementation of hook_insert().
  277.  *
  278.  * Saves the taxonomy details from the makemodel field.
  279.  */
  280. function node_vehicle_insert($node) {
  281.   taxonomy_node_save($node, $node->makemodel);
  282. }
  283.  
  284. /**
  285.  * Implementation of hook_update().
  286.  *
  287.  * Saves the taxonomy details from the makemodel field
  288.  */
  289. function node_vehicle_update($node) {
  290.   // if this is a new node or we're adding a new revision,
  291.   if ($node->revision) {
  292.     node_vehicle_insert($node);
  293.   }
  294.   taxonomy_node_save($node, $node->makemodel);
  295. }
  296.  
  297. /**
  298.  * Implementation of hook_nodeapi().
  299.  *
  300.  * When a node revision is deleted, we need to remove the corresponding record
  301.  * from our table. The only way to handle revision deletion is by implementing
  302.  * hook_nodeapi().
  303.  */
  304. function node_vehicle_nodeapi(&$node, $op, $teaser, $page) {
  305.   switch ($op) {
  306.     case 'delete revision':
  307.       // Do taxonomy terms get removed automatically?  If so, nothing is required here
  308.      
  309.       break;
  310.   }
  311. }
  312.  
  313. /**
  314.  * Implementation of hook_delete().
  315.  *
  316.  * When a node is deleted, we need to remove all related records from out table.
  317.  */
  318. function node_vehicle_delete($node) {
  319.   // No logic is required here at this moment
  320. }
  321.  
  322. /**
  323.  * Implementation of hook_load().
  324.  *
  325.  * Need to separate the vehicle terms into separate make / model elements
  326.  * so they can be used to populate existing nodes
  327.  */
  328. function node_vehicle_load($node) {
  329.   $vid=1;
  330.   $make_model_array = taxonomy_node_get_terms_by_vocabulary($node, $vid);
  331.  
  332.   foreach ($make_model_array as $key => $value) {
  333.     if ( taxonomy_get_parents($value->tid) ) {
  334.       $model=$key;
  335.     }
  336.     else {
  337.       $make=$key;
  338.     }
  339.   }
  340.  
  341.   $additions = new stdClass();
  342.   $additions->make = $make;
  343.   $additions->model = $model;
  344.  
  345.   return $additions;
  346. }
  347.  
  348. /**
  349.  * Implementation of hook_view().
  350.  *
  351.  * This is a typical implementation that simply runs the node text through
  352.  * the output filters.
  353.  *
  354.  * Do I need this?
  355.  */
  356. function node_vehicle_view($node, $teaser = FALSE, $page = FALSE) {
  357.   $node = node_prepare($node, $teaser);
  358.   return $node;
  359. }
  360.  
  361.  
  362. /**
  363.  * Implementation of hook_theme().
  364.  *
  365.  * Tell Drupal about theme functions and their arguments.
  366.  */
  367. function node_vehicle_theme() {
  368.   return array(
  369.      'makemodel' => array(
  370.       'arguments' => array('element'),
  371.     ),
  372.   );
  373. }

Submit Fix

Any tags you'd like to associate with your code, delimitered by commas (example: Views, CCK, Module, etc).
Select the syntax highlighting mode to use.