Merlinofchaos, being the incredibly awesome chap that he is, hammered out this module that converts the difficult to use taxonomy term selection into radio buttons or checkboxes. This is an amazingly handy piece of code and really shakes up the submission procedure.

Awesome it is, there are ways it could be improved, things that require better coders than I - imagine if taxonomy parent terms had the same expansion and shrinking that sections of the page had.

Here's a screenshot of the module in action:
http://squidgystudios.com/drupal/current.jpg

And a proposed screenshot of the ideal:
http://squidgystudios.com/drupal/ideal.jpg

Specifically, if child nodes were automatically assigned a different div class based on their childhood, and perhaps level of childhood, and the expansion/contraction of parent terms. I'm throwing a 50$ bounty on this, if anyone can introduce those elements.

And finally, the module itself!

Place this in a file called test.module for best results.

function test_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
    if (is_array($form['taxonomy'])) {
      foreach ($form['taxonomy'] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
          if ($taxonomy['#multiple']) {
            $form['taxonomy'][$vid]['#type'] = 'checkboxes';
          }
          else {
            $form['taxonomy'][$vid]['#type'] = 'radios';
            $form['taxonomy'][$vid]['#default_value'] = $form['taxonomy'][$vid]['#default_value'][0];
          }

          unset($form['taxonomy'][$vid]['#theme']);
          if ($form['taxonomy'][$vid]['#options'][0]) {
            unset($form['taxonomy'][$vid]['#options'][0]);
          }
          else {
            $form['taxonomy'][$vid]['#required'] = TRUE;
          }
          $form['taxonomy'][$vid]['#prefix'] = '<div class="taxonomy-form">';
          $form['taxonomy'][$vid]['#suffix'] = '</div>';
        }
      }
    }
  }
}

Comments

hardieas’s picture

who can just about figure out install, enable (after thinking really hard), I am impressed...

eliza411’s picture

Wow, what great timing for me. Thank you for this module. Users just don't use the multi-selects like they will these checkboxes and radio buttons!

I'm sure to someone who understands, the next question is obvious, but does this change the way the data is stored in the database, and if so, how?

Squidgy’s picture

No - the code works by intercepting the taxonomy output from the database and rearranging it into a prettier form - there's no database changes. If there were, there would have needed to be a .install file included with the module. Hope that helps!

DayShallCome’s picture

An issue I have with this is that for Taxonomy Vocabs where users can choose to not pick a term, it is impossible to 'uncheck' the widget. Does this make sense? Once a term in a vocab has been selected, it's impossible to 'uncheck' it.

Also, how are you guys getting the levels of a term to show up on the right side? I only get one large list of terms as opposed to the more structured for you have in current.jpg. any ideas?

Squidgy’s picture

I'm not sure I get what you mean with the unchecking - sorry if I'm being dumb. Can you walk me through how the error would show up?

In terms of getting the different taxonomies to line up horizontally, it's just a case of stuffing

.taxonomy-form {
  float: left;
  margin-right: 3em;
}

into styles.css.

Squidgy’s picture

Oh, wait, it occurs to me what you meant about having the terms show up like that - I'm sorry, that was my fault for unclear naming practices. The second level on the right there is not a set of subterms - it's a second vocabulary entirely, that just happens to be placed to the right of the first vocabulary.
The module doesn't support drilldown selection or anything like that, although it would be pretty funky if someone could expand it to do so.

pwolanin’s picture

Hmm, if only I'd seen this a day early- I've just been trying to figure out the same thing and nearly had it working: http://drupal.org/node/84018

---
Work: BioRAFT

Squidgy’s picture

hey, don't give up yet. If you could implement that into this code that would be amazingly handy! Having it style child links automatically would be a huge boon indeed.

Anonymous’s picture

Haven't tried this yet, but I'm assuming it can be set up to allow anonymous users to select (with check boxes) what nodes to display based on taxonomy (like on the frontpage of a news site)?

Also is it possible to implement a cookie feature that will remember what check boxes a user selects, so next time when he or she comes back the site will display contents based on their selection? (or is there another module that can already do this?)

Squidgy’s picture

No, this has nothing to do with the *display* of taxonomy to the public - simply the selection of it during the node creation process. This is so that users, when creating a node, can much more easily choose which term it will be placed under, rather than having to page through a vast morass of links to select one.

I'm sure something like what you described exists somewhere - but this isn't it.

heckp’s picture

the equivalent for exposed flters would be:

 function test_form_alter($form_id, &$form) {
if (isset($form['view']['#value']->exposed_filter)) {
		$view = $form['view']['#value'];
		foreach ($view->exposed_filter as $count => $expose) {
		  if (is_numeric($count)) {
          if ($form["filter$count"]['#multiple']) {
            $form["filter$count"]['#type'] = 'checkboxes';
            $form["filter$count"]['#process'] = array('test_expand_checkboxes' => array());
          }
          else {
            $form["filter$count"]['#type'] = 'radios';
            $form["filter$count"]['#default_value'] = $form['taxonomy'][$vid]['#default_value'][0];
            $form["filter$count"]['#process'] = array('test_expand_radios' => array());
          }
          unset($form["filter$count"]['#theme']);
          if ($form["filter$count"]['#options'][0]) {
            unset($form["filter$count"]['#options'][0]);
          }
          else {
            $form["filter$count"]['#required'] = TRUE;
          }
          $form["filter$count"]['#prefix'] = '<div class="exposed-filters">';
          $form["filter$count"]['#suffix'] = '</div>';
        }
  	}
  }
}
drewsterumd’s picture

Hey there - this looks to be something I want to implement on my site. I've only been playing in Drupal for a week now, and I don't have PHP skill... Ultimately, I'm looking to modify an exposed filter on a View page so that instead of dropdown boxes (based on a Category), I have checkboxes or radio buttons.

Will this script accomplish that? If so, how do I incorporate this into the site? Do I save it as a file and upload it via FTP? Do I paste it in somewhere in the administration side?

I don't really know what to do with these PHP snippets people paste on these discussion boards.

Thanks for all your help,
Andrew

pwolanin’s picture

In this case, the code is meant to be part of a module (as explained at the top)- it's an implementation of hook_form_alter:

http://api.drupal.org/api/4.7/function/hook_form_alter

Other snippets are for pages, blocks, etc. Depends on the context. Essentially all could be incorportated into a module, but the Drupal hooks can only be accessed via a module.

---
Work: BioRAFT

subspaceeddy’s picture

I'd really like to get this to work, but I can't. I've tried it on a number of test installs, even a clean one and get no joy. Could someone give me an explanation of what i may be doing wrong? So far I...
made a module (called it alter_form_select_types); enabled the module.
then go to create->story and i get the old taxonomy select layout
so, i inserted a couple of drupal_set_message calls to see how far it was getting and it fails on if (is_array($form['taxonomy'])) {
before this line i then put drupal_set_message("form.. " . print_r($form, true)); to dump out the values being passed to the function and examined them... there is no 'taxonomy' array at all, in fact none of the taxonomy vocabs or terms are in the dump at all, but still show up in the form displayed (i decided not to eat up screen space with the output but can supply..). Since this code intercepts all forms, i put the array dump code at the first line of the function to see if somehow the taxonomy bit of the form was being created elsewhere... it wasn't. So, it seems to me that the category/taxonomy select bit of create->story is being displayed without passing through this _form_alter, or what? totally confused..

Any ideas of where i might be going wrong? Is there some other setting i should alter somewhere else? I get the feeling i'm missing something glaringly obvious.

Squidgy’s picture

Sorry to hear your troubles, eddy - the module has to be called test.module, I believe. That should clear things up.

subspaceeddy’s picture

OK fixed it.. after trawling around a bit i found these discussions on hook calling order: http://drupal.org/node/49610 and http://drupal.org/node/54902. Because i'd named it 'alter...' it was being called before the form was being filled with taxonomy stuff. Since the original was called 'test_', luckily it was being called after (drupal calls hooks on a 'weight' order, (but all modules default to the same weight, 0), then an a-z order of modules with the same weight, so my 'alter' gets called early in the chain). Technically this is ok, but anyone implementing this module should really tell drupal when to call it. So i wrote a .install file containing this:

function alter_form_select_types_install() {  
  // New module weights in core: put this module after all other form_alters (last in the chain is good).
  $ret[] = db_query("UPDATE {system} SET weight = 10 WHERE name = 'alter_form_select_types'");
  return $ret;
}

so that the _form_alter hook in this module gets called last in the chain.. all works fine now!

Worth noting in case anyone else has similar probs.

hardieas’s picture

Fortunately, I renamed it to taxonomyboxes.module - both descriptive and fortuitously ok for the chaining weight :-)

Is there no way for the module itself to control its weight or have it as an admin interface setting (like for blocks)?
Even more useful would be some way of showing the order in which such things were called - might save much hair-pulling one day ...

subspaceeddy’s picture

The install file i wrote controls the module weight, and that's how all modules that need a specific weight do it. Since it's a simple write to the db table, someone could write a module that reads these values for all modules and allows module weights to be set through an admin form. this functionality was not added deliberately though, from what i could gather in reading the other threads... but it wouldn't be hard to do ;)

ngstigator’s picture

This should've been in Taxonomy Browser a long time ago.

Thanks for the code.

whatistocome’s picture

can't get it working.

i make a blank test.module, put your code in it, upload (straight into modules folder, not subfolder or anything)... go create a new posting.... and get the same old category that needs expanded and then has the dropdown box like normal.

Now, does this piece of code work with the categories module and does it matter that my content types are CCK? Or am I just doing something really dumb?

- Jason

Squidgy’s picture

It most likely has something to do with module weight - I don't think CCK should affect it, although it's quite possible categories is also messing with it. They do use quite different systems, I think. Try creating a test vocabulary using the regular taxonomy system and see if that gets replaced correctly.

Subspaceeddy came up with a solution for the module weight problem on his setup, which might fix yours - rename test.module to alter_form_select.module (since that is a more accurate name, anyways) and create a alter_form_select.install file containing the code from his earlier post (link).

If it still doesn't work, let me know!

whatistocome’s picture

It works with a test vocabulary via regular taxonomy system (it's slick btw). Now, this puts me back to square one since I use categories module exclusively. Any way to get this code working with categories?

- Jaso

subspaceeddy’s picture

I don't use the categories module so can't directly help you. However....

I've been generally messing with the module and have it working with the casetracker module, so i'm sure you could get it working with the category module. My code for the casetracker module is below so you can see the differences:

...
else if (is_array($form['case_detail'])) {
  foreach ($form['case_detail'] as $vid => $casetracker) {
    if ($vid == 'case_status_id' || $vid == 'case_type_id' || $vid == 'case_priority_id' || $vid == 'p_id') {  
      if ($casetracker['#multiple']) {
        $form['case_detail'][$vid]['#type'] = 'checkboxes';
      }
      else {
        $form['case_detail'][$vid]['#type'] = 'radios';
        $form['case_detail'][$vid]['#default_value'] = $form['case_detail'][$vid]['#default_value'];
      }
      $form['case_detail'][$vid]['#prefix'] = '<div id="casetracker-case-form">';
      $form['case_detail'][$vid]['#suffix'] = '</div>';
    } else if (strpos($vid, '#') === false) {
      $form['case_detail'][$vid]['#prefix'] = '<div id="casetracker-case-form">';
      $form['case_detail'][$vid]['#suffix'] = '</div>';
    }
  }
}

put this code directly after the closing } for the original 'if' block of code (if (is_array($form['taxonomy'])) {...}) and it will do both taxonomy and casetracker. In mine, i tagged all the form elements with divs so i could control the look in css. I can't remember why i put in the
else if (strpos($vid, '#') === false) {
(wish i'd commented it...) but i think it was so i could tag other form elements that were 'free tagged' or text input.

What i did was put

  drupal_set_message("form id ($form_id)");
  drupal_set_message("form.. " . print_r($form, true));

as the first lines of the function so that i could identify what form elements were present on the page and could find out how they identify themselves for the if (isarray(...)), then from this, identify which form elements were part of what i wanted to alter.

If you do mess with this code, remember to set the 'weight' to 10 so that this _form_alter is called last and all form elements for the page show up.

Hope that helps.

joshuajoshua’s picture

Is there any way this checkbox format could also be applied to the taxonomy browser as well? I'm using it already in my taxonomy menu (works GREAT)and it would be woderful to extend to the browser.

Thanks for any ideas related to this.

patchak’s picture

Could someone help me install this code?

I renamed to module to alter_form_select.module and created the .install file as suggested earlier in this thread, but still I'm unable to see the new selection checboxes on the site? Is this actually working for someone and if yes, could you give me a hint on how to install this?

Thanks

subspaceeddy’s picture

If it isn't working for you it may still be the 'weight' of the module that isn't being set properly. there is a module weights module here: http://drupal.org/project/moduleweight - i haven't tried it but it may be some use. set the weight of the module you have created to 10.

pwolanin’s picture

You can also set the weight of the module manually using whataever interface you usually have for MySQL (or postgreSQL)- it's just a column in the system table.

---
Work: BioRAFT

globalplayer’s picture

okay I can have category selection as a radio buttons and it's great for me , but how can I add a TAG/s to the category which I will select by radio button (to the content in this category) ???

Squidgy’s picture

I'm sorry, what? Can you run that by me again?

Squidgy’s picture

By the way, people should now ignore the code at the top and instead use Merlin's new extra-vengeanced edition at http://www.angrydonuts.com/taxonomy_checkboxes_v2_the_reven . Enjoy!

z.stolar’s picture

I made few changes and it SEEM to work under drupal 5. Can you have a look, test it and approve?
Changes are marked with a commented "ZS"

<?php

function taxonomy_checkboxes_expand_checkboxes($element) {
  $value = is_array($element['#value']) ? $element['#value'] : array();
  $element['#tree'] = TRUE;
  if (count($element['#options']) > 0) {
    if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
      $element['#default_value'] = array();
    }
    //foreach ($element['#options'] as $key => $choice) {
    foreach ($element['#options'] as $key => $opobject) { // ZS - drupal 5 has them as objects?
      if (!isset($element[$key])) {
				foreach($opobject->option as $opkey => $choice){ // ZS
					$tchoice = ltrim($choice, '-');
					$depth = strlen($choice) - strlen($tchoice);
					$element[$key] = array(
						'#type' => 'checkbox',
						'#prefix' => '<div style="margin-left: ' . (10 * $depth) . 'px">',
						'#suffix' => '</div>',
						'#processed' => TRUE,
						'#title' => $tchoice,
						'#return_value' => $opkey,
						'#default_value' => isset($value[$key]),
						'#attributes' => $element['#attributes']);
				} // ZS
      }
    }
  }
  return $element;
}
function taxonomy_checkboxes_expand_radios($element) {
  if (count($element['#options']) > 0) {
    //foreach ($element['#options'] as $key => $choice) {
     foreach ($element['#options'] as $key => $opobject) { // ZS - drupal 5 has them as objects?
			if (!isset($element[$key])) {
				foreach($opobject->option as $opkey => $choice){ // ZS
					$tchoice = ltrim($choice, '-');
					$depth = strlen($choice) - strlen($tchoice);
					$element[$key] = array(
						'#type' => 'radio',
						'#prefix' => '<div style="margin-left: ' . (10 * $depth) . 'px">',
						'#suffix' => '</div>',
						'#title' => $tchoice,
						'#return_value' => $opkey,
						'#default_value' => $element['#default_value'],
						'#attributes' => $element['#attributes'],
						'#parents' => $element['#parents'],
						'#spawned' => TRUE);
				} // ZS
      }
    }
  }
  return $element;
}
function taxonomy_checkboxes_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
    if (is_array($form['taxonomy'])) {
      foreach ($form['taxonomy'] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
          if ($taxonomy['#multiple']) {
            $form['taxonomy'][$vid]['#type'] = 'checkboxes';
            $form['taxonomy'][$vid]['#process'] = array('taxonomy_checkboxes_expand_checkboxes' => array());
          }
          else {
            $form['taxonomy'][$vid]['#type'] = 'radios';
            $form['taxonomy'][$vid]['#default_value'] = $form['taxonomy'][$vid]['#default_value'][0];
            $form['taxonomy'][$vid]['#process'] = array('taxonomy_checkboxes_expand_radios' => array());
          }
          unset($form['taxonomy'][$vid]['#theme']);
          if ($form['taxonomy'][$vid]['#options'][0]) {
            unset($form['taxonomy'][$vid]['#options'][0]);
          }
          else {
            $form['taxonomy'][$vid]['#required'] = TRUE;
          }
          $form['taxonomy'][$vid]['#prefix'] = '<div class="taxonomy-form">';
          $form['taxonomy'][$vid]['#suffix'] = '</div>';
        }
      }
    }
	}
}
?>
Alex Regenbogen’s picture

The edit-form looks very nice with this one enabled, but it looks like it does not pick up any of the selected terms.... (No checkboxes are being checked when the where before.

Also upon submitting any changes, there's a few "duplicate key entries".... Upon reopening the edit-form, there's nothing checked..
If I do this on a non-Multiple select vocab.. there are no problems.... only when the "Multiple select" option is checked...

Any ideas?

z.stolar’s picture

Once I handle the CVS system (been doing it with Tourtoise with no real success...), I'll create a module of it.
I need it for a site I'm building, so I guess I'll have results soon enough.

======================
Z.Stolar
linnovate.net - community content infrastructures

pwolanin’s picture

Did you make a cvs directory and project yet?

---
Work: BioRAFT

z.stolar’s picture

I'm using Toutoise CVS and I ran into few problems. I'm still trying to figure out the source for this problems.
If anyone has a better choice of a CVS software - I'd love to hear about it. I have 2 other modules, and 1 theme which are in my CVS queue...

pancho’s picture

Hi Zohar!

I don't like Tortoise as well, though I still use it together with Crimson Editor.
As soon as I find some time, I will install Eclipse, which is often recommended as PHP editor as well as CVS software. It is a huge development package though...

Before you create a project, I'd like to talk to you about the project scope:

  • Checkboxes (or radios) are not for all vocabularies the best solution. I'd like to decide this on a per-vocab basis. For regular taxonomy selects, I'd actually like to add some tweak options as well. Therefore "taxonomy_checkboxes" would not be a good name, something like "taxonomy_inputstyles" would be better.
  • The same tweak may also be useful for exposed view filters (see heckp's comment).
  • Just the other way around, it would be cool to tweak "Input formats" and "Comment settings" to be shown as a select box instead of radios. This would be no big deal.
  • Finally it would be great to give the user even more control over all the cluttered options in the node input form. For example:
    • display vocabs or terms side-by-side in multiple columns.
    • move taxonomy input to a separate tab, if not required. (Like Taxonomy switch does).
    • change '#collapsible' and '#collapsed' settings for each of the fieldsets in the node editing form

Of course we won't have all this right now, but we may add more and more features. However the project name should be broad enough to cover all this. I'd suggest something like "tweak_forms".

How do you think about all this?

Best regards, Pancho

P.S. Please see my updated 5.0-port...

emmajane’s picture

Here's a weird one... not all of my categories are showing up. Is there some kind of arbitrary limit that's been placed on the categories that appear? For one vocabulary I should have 40-ish terms (only 20 appear), for the second I have 10, but only 4 appear. Is this some kind of two-column weirdness gone wrong? I'm stumped....

CmdrGravy’s picture

Brilliant, I was just thinking I would have to do something like this to force people to choose at least one category and view the hierarchy as well and this looks ideal, nice one.

pancho’s picture

(updated)

I finally got this one ported to Drupal 5!

What does this code do?

  • it renders taxonomy selects either as checkboxes or as radios, depending on whether the vocabulary allows for multiple selection or not.
  • it indents child term elements according to their depth
  • it applies additional div-tags to allow for advanced theming

Improvements over the previous version for Drupal 4.7

  • Display of the '<none>'-choice is now correctly handled.
  • Works with or without category.module
  • Fixes a bug in category.module

Limitations not yet solved:

  • There is still this one limitation, that in a multiple hierarchy vocab terms with multiple parents are not quite correctly interpreted (they are shown only under their first parent term). I'm still not sure how this can be solved as only selects take object-style options.

The following three functions go into any module with a module name that alphapetically comes after 'taxonomy.module' (e.g. 'test.module'):

<?php

function test_form_alter($form_id, &$form) {
  
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
    if (is_array($form['taxonomy'])) {
      $taxonomy_section = 'taxonomy';
    } else if (is_array($form['category'])) {
      $taxonomy_section = 'category';
    } 

    if (isset($taxonomy_section)) {
      foreach ($form[$taxonomy_section] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
         
          // Fix category.module's bug
          if ($taxonomy_section == 'category') {
            $vocab = taxonomy_get_vocabulary($vid);
            $form[$taxonomy_section][$vid]['#required'] = $vocab['required'];
            unset($vocab);
          }

          // Modify Selectbox
          $form[$taxonomy_section][$vid] = _test_modify_select($form[$taxonomy_section][$vid]);
        }
      }
      $form[$taxonomy_section]['#prefix'] = '<div class="taxonomy-form">';
      $form[$taxonomy_section]['#suffix'] = '</div>';
    }
  unset($taxonomy_section);
  }
}

function _test_modify_select($element) {
  // Convert options from objects to arrays
  // as checkboxes and options (unlike select) don't take objects
  foreach ($element['#options'] as $arrkey => $arrchoice) {
    if (is_object($arrchoice)) {
      foreach ($arrchoice->option as $objkey => $objchoice) {
        $element['#options'][$objkey] = $objchoice;
        unset($element['#options'][$arrkey]);
      }
    }
  }
  
  if ($element['#multiple']) {
    $element['#type'] = 'checkboxes';
    // Non-required checkboxes need no '<none>' choice
    if (!$element['#required']) {
      unset($element['#options'][0]);
    }
    // Reprocess every single checkbox to allow for advanced theming
    $element['#process'] = array('_test_expand_checkboxes' => array());
  } else {
    $element['#type'] = 'radios';
    $element['#default_value'] = $element['#default_value'][0];
    // Non-required radios do need a '<none>' choice
    if (!$element['#required']) {
      $element['#options'][0] = '&lsaquo;' . t('none') . '&rsaquo;';
    }
    // Reprocess every single radio to allow for advanced theming
    $element['#process'] = array('_test_expand_radios' => array());
  }
  
  unset($element['#theme']);
  
  return $element;
}  
  
function _test_expand_checkboxes($thiselement) {
  $value = is_array($thiselement['#value']) ? $thiselement['#value'] : array();
  $thiselement['#tree'] = TRUE;
  if (count($thiselement['#options']) > 0) {
    foreach ($thiselement['#options'] as $key => $choice) {
      if (!isset($thiselement[$key])) {
        $tchoice = ltrim($choice, '-');
        $depth = strlen($choice) - strlen($tchoice);
        $thiselement[$key] = array(
          '#type' => 'checkbox',
          '#title' => $tchoice,
          '#prefix' => '<div style="margin-left: ' . (20 * $depth) . 'px">',
          '#suffix' => '</div>',
          '#return_value' => $key,
          '#default_value' => isset($value[$key]),
          '#attributes' => $thiselement['#attributes'],
          '#processed' => TRUE,
        );
      }
    }
  }
  return $thiselement;
}

function _test_expand_radios($thiselement) {
  if (count($thiselement['#options']) > 0) {
    foreach ($thiselement['#options'] as $key => $choice) {
      if (!isset($thiselement[$key])) {
        $tchoice = ltrim($choice, '-');
        $depth = strlen($choice) - strlen($tchoice);
        $thiselement[$key] = array(
          '#type' => 'radio',
          '#title' => $tchoice,
          '#prefix' => '<div style="margin-left: ' . (20 * $depth) . 'px">',
          '#suffix' => '</div>',
          '#return_value' => $key,
          '#default_value' => $thiselement['#default_value'],
          '#attributes' => $thiselement['#attributes'],
          '#parents' => $thiselement['#parents'],
          '#spawned' => TRUE,
        );
      }
    }
  }
  return $thiselement;
}

?>
z.stolar’s picture

There's a new module which has just been launched:
http://drupal.org/project/taxonomy_super_select

The code you've written seem to have few features that the other one doesn't, but basicly it does the same... so I think we shold look into merging with it :-)

======================
Z.Stolar
linnovate.net - community content infrastructures

pancho’s picture

Hey Zohar!

I've taken a look at that new module, but am pretty disappointed.
While it could be interesting to check how this is realized, it doesn't seem like the perfect starting-point. The results are (at least for my vocabs) messed up and not really usable.

The scopes are completely different, too (see this). So if there's going to be a healthy competition - it wouldn't be the worst. The best solution will survive to the next release.

I created a project as well (Form Tweaker), and would like to see you as a co-maintainer, if you want. The first dev-build will be packaged within the next few hours.

Best regards, Pancho

z.stolar’s picture

I sent you two or three mails through the contact form, also regarding the classified module. Did you get them?

nancydru’s picture

Checkboxes to select what content to display.

Nancy W.
now running 5 sites on Drupal so far
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in Your Database

joshua_cohen’s picture

Bookmarking

havoc’s picture

Pancho,

can you roll this into a stand-alone module?

z.stolar’s picture

avolve’s picture

Can someone list pro's and cons of Form Tweaker and Taxonomy Super Select?

Anyone have a screenshot of Form Tweaker?

Which one is likely to continue/be maintained?

thx
c.

avolve designs | ethical by design

wvd_vegt’s picture

To add (only slightly tested) support for the tac_lite module (to get rid of the listboxes there too) add the following code in 'test_form_alter()', just after the

  unset($taxonomy_section);

call.

  } else if ($form_id='user_edit') {
    if (is_array($form['tac_lite']['tac_lite'])) {
      $taxonomy_section = 'tac_lite';
    } 
   
    if (isset($taxonomy_section)) {
      foreach ($form[$taxonomy_section][$taxonomy_section] as $vid => $taxonomy) {

        if (is_numeric($vid)) {
          
          // Fix category.module's bug
          if ($taxonomy_section == 'category') {
            $vocab = taxonomy_get_vocabulary($vid);
            $form[$taxonomy_section][$taxonomy_section][$vid]['#required'] = $vocab['required'];
            unset($vocab);
          }

          $form[$taxonomy_section][$taxonomy_section][$vid] = _alterform_modify_select($form[$taxonomy_section][$taxonomy_section][$vid]);
        }
      }
      $form[$taxonomy_section][$taxonomy_section]['#prefix'] = '<div class="taxonomy-form">';
      $form[$taxonomy_section][$taxonomy_section]['#suffix'] = '</div>';
    }
  unset($taxonomy_section);   
  }

G.W. van der Vegt

wvd_vegt’s picture

Hi,

Small correction so the taxonomy lists are side-by-side.

          $form[$taxonomy_section][$taxonomy_section][$vid] = _alterform_modify_select($form[$taxonomy_section][$taxonomy_section][$vid]);
        }
      }
      $form[$taxonomy_section][$taxonomy_section]['#prefix'] = '<div class="taxonomy-form">';
      $form[$taxonomy_section][$taxonomy_section]['#suffix'] = '</div>';
    }
  unset($taxonomy_section);  
}

Should read

          $form[$taxonomy_section][$taxonomy_section][$vid] = _alterform_modify_select($form[$taxonomy_section][$taxonomy_section][$vid]);

          $form[$taxonomy_section][$taxonomy_section][$vid]['#prefix'] = '<div class="taxonomy-form">';
          $form[$taxonomy_section][$taxonomy_section][$vid]['#suffix'] = '</div>';
          //not sure what the next line does!
          unset($form[$taxonomy_section][$taxonomy_section][$vid]['#theme']);
        }
      }
      //Header & Border    
      $form[$taxonomy_section][$taxonomy_section]['#prefix'] = '<fieldset class="collapsible"><legend></legend>';
      $form[$taxonomy_section][$taxonomy_section]['#suffix'] = '</fieldset>';
    }
  unset($taxonomy_section);   
  }

G.W. van der Vegt

wvd_vegt’s picture

Hi,

After some debugging I found a small bug in above code

  foreach ($element['#options'] as $arrkey => $arrchoice) {
    if (is_object($arrchoice)) {
      foreach ($arrchoice->option as $objkey => $objchoice) {
        $element['#options'][$objkey] = $objchoice;
        unset($element['#options'][$arrkey]);
      }
    }
  }

should read:

  foreach ($element['#options'] as $arrkey => $arrchoice) {
    if (is_object($arrchoice)) {
      foreach ($arrchoice->option as $objkey => $objchoice) {
	//unset($element['#options'][$arrkey]);
        $element['#options'][$objkey] = $objchoice;
      }
    }
  }

  foreach ($element['#options'] as $arrkey => $arrchoice) {
    if (is_object($arrchoice)) {
      unset($element['#options'][$arrkey]);
    }
  }

As it's possible that a taxonomy term wipes out (unsets) a previously converted term. We had one vocabulary where consequently one term wasn't rendered even thought it was clearly in the input. Drupal_set_message() printing the array keys ($arrkey and $objkey) in the foreach loop revealed that one wiped the other before or after conversion (not clear yet).

G.W. van der Vegt

dannypfeiffer’s picture

Any possibility of getting this ported to drupal 6?

Danny Pfeiffer
http://RehabCreative.com

wvd_vegt’s picture

Hi,

For those interested: here is a port to Drupal 6.4 that seems to work.
I ported it, it passes code review with some minor issues but it still needs
some more testing
.

It shows single & multiselect taxonomies nicely.
It also handles & visualizes required taxonomies and hierachical ones.

Note: the part for altering taxonomies in views is commented out.

alterform.info:

; $Id: alterform.info,v 1.5 2008/09/16 03:44:07 wvd_vegt Exp $
name = Alterform
description = Alterform converts the Taxonomy Listboxes to Check/RadioBoxes.
dependencies[] = taxonomy
core = 6.x

; Information added by drupal.org packaging script on 2007-10-29
version = "6.x"
project = "alterform"
datestamp = "1193629802"

alterform.install:

<?php

// $Id: alterform.install,v 1.5 2008/09/16 03:44:07 wvd_vegt Exp $

/**
 * @file
 * The Alterform Module transforms the traditional listbox type of 
 * taxonomy selectors into checkboxes and radiobuttons for easier
 * editing.
 */
function alter_form_select_types_install() {  
  // New module weights in core: put this module after all other form_alters (last in the chain is good).
  $ret[] = db_query("UPDATE {system} SET weight = 10 WHERE name = 'alter_form_select_types'");
  return $ret;
}

alterform.module:

<?php

// $Id: alterform.info,v 1.5 2008/09/16 03:44:07 wvd_vegt Exp $

/**
 * @file
 * The Alterform Module transforms the traditional listbox type of 
 * taxonomy selectors into checkboxes and radiobuttons for easier
 * editing.
 *
 * veg:16-09-2008 - started port to Drupal 6.x.
 *                          completed port to Drupal 6.x.
 *                          passed code review with some minor issues.
 *                          corrected documentation.    
 * SHOULD THE RADIO BUTTONS NOT BE IN THE #OPTIONS???
 */

/**
 * Implementation of hook_form_alter(). 
 * 
 * This function examines the form and changes the taxonomy listboxes
 * to either radiobuttons or checkboxes, dependend on the #multiselect 
 * value. 
 * It also removes the unneccesary 'None' option for non required 
 * multiselect vocabularies.
 * Finally it indents hierarchical vocabularies.
 */

function alterform_form_alter(&$form, &$form_state, $form_id) {
  
  $path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/fb.php';
  if (file_exists($path)) {
    include_once $path;
  }

  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {

    ///////////////////////////////
    //Normal Content Creation Forms
    ///////////////////////////////
    
    //krumo($form);

    if (is_array($form['taxonomy'])) {
      $taxonomy_section = 'taxonomy';
    } 
    else if (is_array($form['category'])) {
      $taxonomy_section = 'category';
    } 

    $cnt=0;
    if (isset($taxonomy_section)) {
      foreach ($form[$taxonomy_section] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
         
          // Fix category.module's bug
          if ($taxonomy_section == 'category') {
            $vocab = taxonomy_vocabulary_load($vid);
            $form[$taxonomy_section][$vid]['#required'] = $vocab['required'];
            unset($vocab);
          }

          // Modify Selectbox
          $form[$taxonomy_section][$vid]['#prefix'] = '<div class="taxonomy-form">';
          _alterform_modify_select($form[$taxonomy_section][$vid], $vid);
          $form[$taxonomy_section][$vid]['#suffix'] = '</div>';

          $cnt++;
      
          unset($form[$taxonomy_section][$vid]['#theme']);
        }
      }
      if ($cnt==1) {
        $form[$taxonomy_section]['#prefix'] = '<fieldset class="collapsible"><legend>'.t('Category').'</legend>';
        $form[$taxonomy_section]['#suffix'] = '</fieldset>';
      }
    }
    unset($taxonomy_section);

    //krumo($form);
  } 
  else if ($form_id='user_edit') {

    ///////////////////////////////
    //Taxonomy Lite Forms
    ///////////////////////////////

    if (is_array($form['tac_lite']['tac_lite'])) {
      $taxonomy_section = 'tac_lite';
    } 
   
    if (isset($taxonomy_section)) {
      foreach ($form[$taxonomy_section][$taxonomy_section] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
          
          // Fix category.module's bug
          if ($taxonomy_section == 'category') {
            $vocab = taxonomy_vocabulary_load($vid);
            $form[$taxonomy_section][$taxonomy_section][$vid]['#required'] = $vocab['required'];
            unset($vocab);
          }

          $form[$taxonomy_section][$taxonomy_section][$vid]['#prefix'] = '<div class="taxonomy-lite-form">';
          _alterform_modify_select($form[$taxonomy_section][$taxonomy_section][$vid]);
          $form[$taxonomy_section][$taxonomy_section][$vid]['#suffix'] = '</div>';
      
          unset($form[$taxonomy_section][$taxonomy_section][$vid]['#theme']);
        }
      }
      $form[$taxonomy_section][$taxonomy_section]['#prefix'] = '<fieldset class="collapsible"><legend></legend>';
      $form[$taxonomy_section][$taxonomy_section]['#suffix'] = '</fieldset>';
    }
    unset($taxonomy_section);   
  } 
/*
    else if (isset($form['view_filters']['#value']->exposed_filter)) {

    ///////////////////////////////
    //Exposed Filters in Views Module
    ///////////////////////////////
  
    //Drupal_set_Message(print_r($form['view_filters']));
    //Drupal_set_Message('Views');  

    $view = $form['view_filters']['#value'];
    foreach ($view->exposed_filter as $count => $expose) {
      if (is_numeric($count)) {
        $taxonomy_section = 'filter$count';
        $form[$taxonomy_section] = _alterform_modify_select($form[$taxonomy_section]);

        //Original Code..     
        //if ($form["filter$count"]['#multiple']) {
        //      $form["filter$count"]['#type'] = 'checkboxes';
        //    $form["filter$count"]['#process'] = array('_alterform_modify_select' => array());
        //} else {
        //    $form["filter$count"]['#type'] = 'radios';
        //    $form["filter$count"]['#default_value'] = $form['taxonomy'][$vid]['#default_value'][0];
        //    $form["filter$count"]['#process'] = array('test_expand_radios' => array());
        //}
        //unset($form["filter$count"]['#theme']);
        //if ($form["filter$count"]['#options'][0]) {
        ///    unset($form["filter$count"]['#options'][0]);
        //} else {
        //    $form["filter$count"]['#required'] = TRUE;
        //}

          $form[$taxonomy_section]['#prefix'] = '<div class="exposed-filters">';
          $form[$taxonomy_section]['#suffix'] = '</div>';
        }
      }
    }
*/
}

/**
 * Internal function that does the major part of the transformation. 
 * For multiselect it's a bit more complex than for single select 
 * vocabularies.
 */
function _alterform_modify_select(&$element, $vid) {
  // Convert options from objects to arrays
  // as checkboxes and options (unlike select) don't take objects
  foreach ($element['#options'] as $arrkey => $arrchoice) {
    if (is_object($arrchoice)) {
      foreach ($arrchoice->option as $objkey => $objchoice) {
        $element['#options'][$objkey] = $objchoice;
      }
    }
  }

  foreach ($element['#options'] as $arrkey => $arrchoice) {
    if (is_object($arrchoice)) {
      unset($element['#options'][$arrkey]);
    }
  }

  if ($element['#multiple']) {                                  //Multiselect -> Checkboxes
    $element['#type'] = 'checkboxes';

    // Non-required checkboxes need no '<none>' choice
    if (!$element['#required']) {
      unset($element['#options']['']);
    } else {
      //$element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';
    }

    _alterform_expand_checkboxes($element, $vid);               //Checkboxes need somewhat more processing...
  } else {                                                      //Singleselect -> Radiobuttons
    $element['#type'] = 'radios';
    $element['#default_value'] = $element['#default_value'][0];

    if (!$element['#required']) {
      //unset($element['#options']['']);
      $element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';
    } else {
      $element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';
    }    

    _alterform_expand_radios($element, $vid);                   //Checkboxes need somewhat more processing...
    unset($element['#options']);
  }
  
  unset($element['#theme']);
}  
  
/**
 * Internal function that transforms multi-select vocabularies. 
 */
function _alterform_expand_checkboxes(&$thiselement, $vid) {
  $value = is_array($thiselement['#value']) ? $thiselement['#options'] : array();
  $thiselement['#tree'] = TRUE;

  if (count($thiselement['#options']) > 0) {
    foreach ($thiselement['#options'] as $key => $choice) {
      if (!isset($thiselement[$key])) {
        $tchoice = ltrim($choice, '-');
        $depth = strlen($choice) - strlen($tchoice);
        $thiselement[$key] = array(
          '#type' => 'checkbox',
          '#title' => $tchoice,
          '#prefix' => '<div style="margin-left: ' . (20 * $depth) . 'px">',
          '#suffix' => '</div>',
          '#return_value' => $key,
          '#default_value' => isset($value[$key]),
          //'#attributes' => $thiselement['#attributes'],
          //'#processed' => TRUE,
        );
      }
    }
  }
}

/**
 * Internal function that transforms single-select vocabularies. 
 */
function _alterform_expand_radios(&$thiselement, $vid) {
  if (count($thiselement['#options']) > 0) {
    foreach ($thiselement['#options'] as $key => $choice) {
      if (!isset($thiselement[$key])) {
        $tchoice = ltrim($choice, '-');
        $depth = strlen($choice) - strlen($tchoice);
        $thiselement[$key] = array(
          '#type' => 'radio',
          '#name' => 'vocabulary_'.$vid,
          '#title' => $tchoice,
          '#prefix' => '<div style="margin-left: ' . (20 * $depth) . 'px">',
          '#suffix' => '</div>',
          '#return_value' => $key,
          '#default_value' => $thiselement['#default_value'],
          //'#attributes' => $thiselement['#attributes'],
          //'#parents' => $thiselement['#parents'],
          //'#spawned' => TRUE,
        );
      }
    }
  }
}

Sample Style.css addition (taxonomies colored and side by side for saving precious vertical space):

.taxonomy-form {
  background-color: #fcf9e5;
  float: left;
  margin-top: 1em;
  margin-right: 1em;
}

.taxonomy-form .form-item {
  background-color: #fcf9e5;
  margin-left: 1em;
  margin-right: 1em;
}

.taxonomy-form .form-item .description {
  background-color: #fcf9e5;
  margin-left: 1em;
  margin-right: 1em;
}

.taxonomy-lite-form {
  background-color: #d3e7f4;
  float: left;
  margin-right: 1em;
}

.taxonomy-lite-form .form-item {
  background-color: #d3e7f4;
  margin-top: 1em;
  margin-left: 1em;
  margin-right: 1em;
}

.taxonomy-lite-form .form-item .description {
  background-color: #d3e7f4;
  margin-left: 1em;
  margin-right: 1em;
}
nancydru’s picture

Why don't you work with the maintainer of Taxonomy Super Select to integrate this?

NancyDru (formerly Nancy W. until I got married to Drupal)

eikes’s picture

First of all: Nice work, thanks a lot!

In the _alterform_expand_checkboxes function, the default value wasn't displayed properly, here is the correct version:

/**
* Internal function that transforms multi-select vocabularies.
*/
function _alterform_expand_checkboxes(&$thiselement, $vid) {
  $value = is_array($thiselement['#options']) ? $thiselement['#options'] : array();
  //krumo($thiselement);
  $thiselement['#tree'] = TRUE;

  if (count($thiselement['#options']) > 0) {
    foreach ($thiselement['#options'] as $key => $choice) {
      if (!isset($thiselement[$key])) {
        $tchoice = ltrim($choice, '-');
        $depth = strlen($choice) - strlen($tchoice);
        $thiselement[$key] = array(
          '#type' => 'checkbox',
          '#title' => $tchoice,
          '#prefix' => '<div style="margin-left: ' . (20 * $depth) . 'px">',
          '#suffix' => '</div>',
          '#return_value' => $key,
          '#default_value' => in_array($key, $thiselement['#default_value']),
        );
      }
    }
  }
}

arnoldc’s picture

Nicely done. I need one for D6.

Just curious if the following codes actually do anything in alterform.install file?

  $ret[] = db_query("UPDATE {system} SET weight = 10 WHERE name = 'alter_form_select_types'");

The system file will create an entry named "alterform" when the module is installed but where is 'alter_form_select_types' coming from?

zemote’s picture

I would love to see checkboxes + Freetagging together with this module.

globalplayer’s picture

I'm also looking for something like this for a long time...

rconstantine’s picture

http://drupal.org/node/148493

It isn't working with single select (i.e. radio buttons) for some reason, but should be close. Checkboxes, free tagging and ratings all work. Because everything is inside fieldsets, it does take up a lot of real estate on-screen when expanded. Theme/css patches are welcome to pretty that up.

The above linked module allows hierarchical free tagging (add a term and select the parent), and I don't think any other module does that yet.

rconstantine’s picture

I recently released CCK Taxonomy Super Select Ultra which is a combination of cck taxonomy and the above mentioned super select module. I'll be looking into the form tweaker module to see if there is anything that could make it better.

Be sure to read the description on the project page for current limitations.

Also, the interface may take some getting used to, but seems to work okay. Suggestions/patches are welcome.

Stephan_M’s picture

Do you have the problem that a Freetagging Vocabulary disrupts the floating of the taxonomies side by side?
I just solved that.

Look here: http://www.moebius.mynetcologne.de/librivox/resorting_categories.gif

Use this CSS instead:

.taxonomy-form {
  float: left;
  margin-right: 3em;
}
.form-item {
clear: both;
}

And shuffle all the vocabulaires to the top by giving them a higher weight:

<?php
function test_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
    if (is_array($form['taxonomy'])) {
      foreach ($form['taxonomy'] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
          if ($taxonomy['#multiple']) {
            $form['taxonomy'][$vid]['#type'] = 'checkboxes';
	    $form['taxonomy'][$vid]['#weight'] = -15;
          }
          else {
            $form['taxonomy'][$vid]['#type'] = 'radios';
            $form['taxonomy'][$vid]['#default_value'] = $form['taxonomy'][$vid]['#default_value'][0];
	    $form['taxonomy'][$vid]['#weight'] = -15;
          }

          unset($form['taxonomy'][$vid]['#theme']);
          if ($form['taxonomy'][$vid]['#options'][0]) {
            unset($form['taxonomy'][$vid]['#options'][0]);
          }
          else {
            $form['taxonomy'][$vid]['#required'] = TRUE;
          }
          $form['taxonomy'][$vid]['#prefix'] = '<div class="taxonomy-form">';
          $form['taxonomy'][$vid]['#suffix'] = '</div>';
        }
      }
    }
  }
}
?>

Thanks to the above snippet i don't need TaxonomySuperSelect anymore. Yipee.

nancydru’s picture

Let's see... one one hand I have an unsupported snippet that I will personally have to maintain. On the other hand I have an official module (with additional capabilities) that someone else has to maintain...

Nancy W.
now running 5 sites on Drupal so far
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in Your Database

Hunabku’s picture

5.7 kills the snippet. When it displays a taxonomy, each term is named "Option" instead of the term's actual name.

We can't lose the last elegant check box and radio button solution because all that is left are the bloated, soon to be dinosaurs like Taxonomy Super Select and Form Tweaker."

Here lies our beloved snippet's body


function test_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
    if (is_array($form['taxonomy'])) {
      foreach ($form['taxonomy'] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
          if ($taxonomy['#multiple']) {
            $form['taxonomy'][$vid]['#type'] = 'checkboxes';
        $form['taxonomy'][$vid]['#weight'] = -15;
          }
          else {
            $form['taxonomy'][$vid]['#type'] = 'radios';
            $form['taxonomy'][$vid]['#default_value'] = $form['taxonomy'][$vid]['#default_value'][0];
        $form['taxonomy'][$vid]['#weight'] = -15;
          }

          unset($form['taxonomy'][$vid]['#theme']);
          if ($form['taxonomy'][$vid]['#options'][0]) {
            unset($form['taxonomy'][$vid]['#options'][0]);
          }
          else {
            $form['taxonomy'][$vid]['#required'] = TRUE;
          }
          $form['taxonomy'][$vid]['#prefix'] = '<div class="taxonomy-form">';
          $form['taxonomy'][$vid]['#suffix'] = '</div>';
        }
      }
    }
  }
}

shawnc’s picture

...But where exactly am I dropping this module in my directory structure?
...the Taxonomy folder?
...Do I then need to activate it, as per usual?

Thanks in advance.

rconstantine’s picture

Which module are you referring to? The code in the original post is not a module, but a snippet of an idea. Several modules have resulted from this discussion. You need to choose one and follow its directions for installation. There is the formtweaker module, taxonomy_super_select module, and my cck_taxonomy_super_select_ultra module.

coupet’s picture

This module defines the "hierarchical_select" form element, which is a much enhanced way for letting the user select an option in a hierarchy.
http://drupal.org/project/hierarchical_select

----
Darly

rconstantine’s picture

Did you see the note on the project's page that says you can't have multiple selections? Kind of a deal breaker for most of the above-mentioned taxonomy-related modules. And a javascript requirement is a tough sell too. I love to have javascript on my sites, but I always need graceful degradation for the few cases where it won't be available.

Thanks for the post, though. Very interesting.

nancydru’s picture

rconstantine’s picture

Huh? I was referring to the post to which my comment was directed - i.e. the hierarchical_select module's project page. The three modules I listed above all have multi-select. Perhaps you misunderstood my comment about the deal-breaker? I meant that the above three modules can't use hierarchical_select since it can't do multi-select and they all need that ability. Also that many people want that ability and so hierarchical_select would be a deal-breaker for them in general as well.

cgjohnson’s picture

Community plumbers,

I discovered this module and it looks fantastic -- but I'm wondering if anyone can tell me whether I should use the Taxonomy Super Select module or the Super Select Ultra? Is there a functionality difference?

Taxonomy Super Select:
http://www.northstudio.com/drupal/modules/taxonomy_super_select.html

Super Select Ultra is here: http://drupal.org/project/cck_taxonomy_ssu

(or is there a taxonomy super-cali-fraggin-ultra-extra-mazing module somewhere I should use instead?)

thanks for these great contributed modules!

rconstantine’s picture

As the author of the latter, I can tell you that I wrote cck_taxonomy_ssu to satisfy a requirement I had that wasn't met by the former. I have not checked out the tss module since writing ccktssu, so I can't be sure of what features might be lacking. The first and major difference is that my module is a cck field module, so you can include it as a field in any type. Tss is a regular taxonomy module, so you can still include it the taxonomy way, but it will appear in the 'categories' fieldset. The cck field can be called whatever you want and you can have as many as you want in your content type.

Another difference that might be remedied by now was that (IIRC) tss was limited to a vocab nesting depth of 5. I recall submitting a patch which allows for infinite depth, but don't know if it was ever included. My module is a direct combination of tss and cck_taxonomy (hence the crazy long name; fun huh?), so it shares some code with tss and it allows for infinite nesting.

Another difference is that you have the option of including selected (checked) taxonomy terms in the normal (?) taxonomy way as a node tag, or in hiding the terms from the node view and restricting their storage to the cck tables. This is useful particularly for programmers of other modules (like myself) that have reasons for wanting the terms hidden from users, but be able to use them programmatically with other modules.

Also, my module allows for nested freetagging, which I believe no other taxonomy module does. One thing that needs to be added though is my oversight to allow comma or space-separated entry of multiple tags. Oops! However, a user can enter single tags into multiple freetagging text fields. If you enable the module and try it out, you'll see what I mean.

There is also some pretty cool CSS that come with it which replaces the standard fieldset look with plus boxes and squishes the display so that it isn't even half as long as it would be otherwise.

Additionally, you can have your users rate each term. This is particularly useful when you use this cck field in a nodeprofile content type as each user will each have one of these nodes. You could then, for example, have a dynamic list of food and users could tell you what they like. You could then write a module to sift through this rated data and see what appeals to your users and what doesn't. You could alert them when there are new terms that they haven't rated, or whatever (in your custom module); the data is there to use.

I'm sure I'm forgetting something.

My module is due for some attention, so I'll be getting to it soon. Check out the issue queue for features that will probably be added in the next release - things like comma-separated freetagging, disallowing freetagging at the root level, two column layout of terms (or other options?), fixing alphabetizing, fixing required-ness, and hopefully adding AJAX! That last one will be especially important for very large taxonomies. I know of one guy that had 5000 terms and his server kept crashing. I'm not sure if he ever got things working.

Anyway, I hope that answers some of your questions.

BTW, after the next update, I will include screen shots like I have with all recently-updated modules of mine.

One last thing - note the last time tss was updated and the issues in the queue. Mine is updated regularly and the queue kept short.

summit’s picture

Hi,

I am sorry if I am being stupid, but can you use a cck field, like cck_taxonomy_ssu, on standard node-types like page, story, weblink?
If so I would love to test your module and give feedback.

If not. Is there another way to still use your module so I not have to first create a taxonomy term and then create a page with the term tagged?

Thanks in advance for your reply!

greetings,
Martijn

rconstantine’s picture

but yes, you can use CCK field modules on standard content types. As for creating a taxonomy and tagging pages, I'm not sure what you mean. CCKTSSU uses regular vocabularies, so you need to set one up for use with it. I recommend the taxonomy manager module for filling your taxonomies with terms. You cannot use CCK fields outside of content types at present. I hope that for D7 someone will introduce the Super Nodes that have been talked about; then you'd be able to use CCK fields for things like profiles, comments, registration, or whatever.

cgjohnson’s picture

Disallowing free tagging at the root level has me VERY intrigued... it would be great for a city state taxonomy if the states were all fixed (thereby controlled) and users could select a state and freetag a city.... sounds like that's what this is getting at?

anyway, thanks for this info and this module!

cj

nancydru’s picture

I have not used the other, so I can't speak to it. I believe there is another called Form_Tweaker that does similar as well.

First TSS is available on DO: http://drupal.org/project/taxonomy_super_select I would rarely ever consider a module that was not in contributed status.

TSS works pretty well. As the other poster mentioned it is currently limited to 5 levels of hierarchy. That's way more than I can envision needing, but a few have complained that it's not enough. Another problem that I have seen is probably mostly a module weight issue: I had a module that pre-selected a taxo. term, but TSS didn't "see" that. I simply turned off TSS for that content type - a bit of a nuisance, but it worked.

Probably my biggest complaint about the module is easily remedied, although it doesn't seem to getting done: Maintain it. I haven't seen much work done on the issue queue.

Nancy W.
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in your database

cgjohnson’s picture

THanks, both of you who replied -- this is very helpful! I'll play with both and see if I have anything to add.

Nancy, one quick follow-up clarification: I'm fuzzy on what DO and contributed status really mean. Is TSS ultra (_SU) not in contributed status?

thanks!
cg

rconstantine’s picture

If I could guess what Nancy meant; I think she was commenting about the link you had for TSS as it pointed not to its project page here on Drupal.org (DO), but to the author's own site. Any module found on DO is a contributed module if it isn't in CORE (i.e. requires a separate download from Drupal itself). Is that clear?

cgjohnson’s picture

Yes, clear as day, thanks -- I should have used the DO link.

I'm wondering whether there's something I can use to create a fixed taxonomy vocab list with free tagging terms, so users would select Alabama and then either a city from the dropdown OR add their own city as a child term under Alabama, anyone know how this could be done?

I tried using tax. super select ultra but it gave me some errors.

thanks!

nancydru’s picture

The "refine by taxonomy" (or "taxonomy_refine", I can't remember which) module.

Nancy W.
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in your database

cgjohnson’s picture

Thanks, I'll check it now.

I also found this: http://blue.live4all.co.il/~mooffie/cms/node/add/bio
(a link off of this post: http://drupal.org/node/163577)
in case that helps someone else.

Anyone who's ever created a state drop down with a dependent city dropdown and ability for users to add new cities to a state, it would be great to hear from you! thanks!

nancydru’s picture

summit’s picture

Hi,

Last month I have tested all available radiobuttons taxonomy modules which I have found. But non of them where completely satisfiing.

Which one is the one to go for with the following requirements:
1) Only loading terms when clicking on the above level (otherwise a large vocabulary takes ages to load);
-> This is already done with taxonomy_strider, but this module doesn't work with IE6 and has not the freetagging posibility (yet) together with radiobutton selection of already available nodes.
2) Giving the possibility to select the parent level of terms, without having to click on child level
-> This is already done with cck_taxonomy_super_select_ultra, but this module loads all taxonomy terms in one, which gives a to big loading time for my purposes.
I do a development aid project in Nepal, and they are not having high speed internet, so loading needs to be quick and only loading what is necessary.

Formtweaker looks like the simplest solution, but I think I found a bug in this module, and it is unmaintained...

Please assist what to do with above functionality request, what module is the right one?
Am I forgetting a module which is satisfying my needs, please also reply!

Thanks in advance,
greetings,
Martijn

nancydru’s picture

Martijn, I'm not sure you're going to find any solution that doesn't have to load the whole vocabulary - at least at the server level. That's the way the taxonomy module organizes and delivers the data. It may be possible, but is beyond my skills, to have client-side code that shows what you want but still transfers the rest of the data in the background.

Nancy W.
Drupal Cookbook (for New Drupallers)
Adding Hidden Design or How To notes in your database

himerus’s picture

I could really use a similar solution for Druapl 6. The CCK taxonomy super select ultra along with all the similar modules have not been ported yet.

Jake Strawn
Himerus Inc. - http://himerusinc.com

summit’s picture

Hi, I am still on Drupal 5 and CCK taxonomy super select ultra unfortunately doesn't have AJAX support yet... With this it would really be a drupal top 5 module!
Greetings,
Martijn

dannypfeiffer’s picture

i believe you could do this with a module using hook_form_alter, but i'm not familiar enough w/ module development.

I made the "profile" module use radio instead of select for Gender selection.

i did so by opening profile.module, go to the profile form function:

function profile_form_profile($edit, $user, $category,

find the switch for:

switch ($field->type) {

Then find
case 'selection'

and change:

$fields[$category][$field->name] = array('#type' => 'select',

to

$fields[$category][$field->name] = array('#type' => 'radios',

... something similar could probably be done via a module or in the template.php file?

-Danny Pfeiffer
http://MaybeMike.com
http://DrupalForBeginners.com

Danny Pfeiffer
http://RehabCreative.com

nancydru’s picture

Select, radios, and checkboxes don't necessarily return the same data to the submit handler.

dannypfeiffer’s picture

While checkboxes won't work the same, I'm pretty sure radios will return the same value as a single-select. Works in my case anyway.

I went ahead and pulled my former code out of profile.module and made a simple custom module utilizing hook_form_alter

<?php
function myform_form_alter(&$form, $form_state, $form_id) {

 if ($form_id=='user_register') {
 $form['User Basics']['profile_gender']['#type']="radios";
 $form['User Basics']['profile_gender']['#default_value']="Female";
}
}
?>

This works for me. Again, just radios in place of a normal select list. not a multiple select list.

Danny Pfeiffer
http://RehabCreative.com

Pushkar Gaikwad’s picture

I second that.
Placement Papers | Proxy

wvd_vegt’s picture

Hi all

I have worked some more on my port to Drupal 6.4 Major (and unsolved problem) is that I cannto get the radiobuttons (ie the single choice taxonomies) to work and at the same time display hierarchy.

So i opted for a workaround by leaving the single select taxonomies as they are (drop down boxes) and only covert the multiselect ones to checkboxes. This approax has the advantage that it saves screen area as the combobox is smaller that a set of radio buttons.

Note: remove the trailing ?> from the sources (and <?php too from the info file).

alterform.info:

; $Id: alterform.info,v 1.5 2008/10/03 03:44:07 wvd_vegt Exp $
name = Alterform
description = Alterform converts the Taxonomy Listboxes to Check/RadioBoxes.
dependencies[] = taxonomy
core = 6.x

; Information added by drupal.org packaging script on 2007-09-29
version = "6.x"
project = "alterform"
datestamp = "1193629803"

alterform.install:


// $Id: alterform.install,v 1.5 2008/10/03 03:44:07 wvd_vegt Exp $

/**
 * @file
 * The Alterform Module transforms the traditional listbox type of 
 * taxonomy selectors into checkboxes and radiobuttons for easier
 * editing.
 */
function alter_form_select_types_install() {  
  // New module weights in core: put this module after all other form_alters (last in the chain is good).
  $ret[] = db_query("UPDATE {system} SET weight = 10 WHERE name = 'alter_form_select_types'");
  return $ret;
}

alterform.module:


// $Id: alterform.info,v 1.5 2008/10/03 03:44:07 wvd_vegt Exp $

/**
 * @file
 * The Alterform Module transforms the traditional listbox type of 
 * taxonomy selectors into checkboxes and radiobuttons for easier
 * editing.
 *
 * veg:16-09-2008 - started port to Drupal 6.x.
 *                  completed port to Drupal 6.x.
 *                  passed code review with some minor issues.
 *                  corrected documentation.
 * veg:19-09-2008 - Hierachical checkboxes are visualized properly.     
 *                  Hierachical radiobuttons are NOT visualized properly 
 *                  so code is disabled!
 * veg 29-09-2008 - Fixed a bug in #default_value (added test for empty array).
 * veg 03-10-2008 - Uploaded to Drupal.org
 */

/**
 * Implementation of hook_form_alter(). 
 * 
 * This function examines the form and changes the taxonomy listboxes
 * to either radiobuttons or checkboxes, dependend on the #multiselect 
 * value. 
 * It also removes the unneccesary 'None' option for non required 
 * multiselect vocabularies.
 * Finally it indents hierarchical vocabularies.
 */
function alterform_form_alter(&$form, &$form_state, $form_id) {
  
  $path = './'. drupal_get_path('module', 'devel') .'/krumo/class.krumo.php';
  if (file_exists($path)) {
    include_once $path;
  }

  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {

    ///////////////////////////////
    //Normal Content Creation Forms
    ///////////////////////////////
    
    if (is_array($form['taxonomy'])) {
      $taxonomy_section = 'taxonomy';
    } 
    else if (is_array($form['category'])) {
      $taxonomy_section = 'category';
    } 

    $cnt=0;
    if (isset($taxonomy_section)) {

      foreach ($form[$taxonomy_section] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
       
          // Fix category.module's bug
          if ($taxonomy_section == 'category') {
            $vocab = taxonomy_vocabulary_load($vid);
            $form[$taxonomy_section][$vid]['#required'] = $vocab['required'];
            unset($vocab);
          }

          // Modify Selectbox into Checboxes or Radios
          $form[$taxonomy_section][$vid]['#prefix'] = '<div class="taxonomy-form">';
          _alterform_modify_select($form[$taxonomy_section][$vid], $vid);
          $form[$taxonomy_section][$vid]['#suffix'] = '</div>';

          $cnt++;
      
          unset($form[$taxonomy_section][$vid]['#theme']);
        }
      }
      if ($cnt==1) {
        $form[$taxonomy_section]['#prefix'] = '<fieldset class="collapsible"><legend>'.t('Category').'</legend>';
        $form[$taxonomy_section]['#suffix'] = '</fieldset>';
      }
    }
    unset($taxonomy_section);
  } 
  else if ($form_id='user_edit') {

    ///////////////////////////////
    //Taxonomy Lite Forms
    ///////////////////////////////

    if (is_array($form['tac_lite']['tac_lite'])) {
      $taxonomy_section = 'tac_lite';
    } 
   
    if (isset($taxonomy_section)) {
      foreach ($form[$taxonomy_section][$taxonomy_section] as $vid => $taxonomy) {
        if (is_numeric($vid)) {
          
          // Fix category.module's bug
          if ($taxonomy_section == 'category') {
            $vocab = taxonomy_vocabulary_load($vid);
            $form[$taxonomy_section][$taxonomy_section][$vid]['#required'] = $vocab['required'];
            unset($vocab);
          }

          // Modify Selectbox into Checboxes or Radios
          $form[$taxonomy_section][$taxonomy_section][$vid]['#prefix'] = '<div class="taxonomy-lite-form">';
          _alterform_modify_select($form[$taxonomy_section][$taxonomy_section][$vid], $vid);
          $form[$taxonomy_section][$taxonomy_section][$vid]['#suffix'] = '</div>';
      
          unset($form[$taxonomy_section][$taxonomy_section][$vid]['#theme']);
        }
      }
      $form[$taxonomy_section][$taxonomy_section]['#prefix'] = '<fieldset class="collapsible"><legend></legend>';
      $form[$taxonomy_section][$taxonomy_section]['#suffix'] = '</fieldset>';
    }
    unset($taxonomy_section);   
  }
}

/**
 * Internal function that does the major part of the transformation. 
 * For multiselect it's a bit more complex than for single select 
 * vocabularies.
 */
function _alterform_modify_select(&$element, $vid) {

  if ($element['#multiple']) {                                  //Multiselect -> Checkboxes
    // Convert options from objects to arrays
    // as checkboxes and options (unlike select) don't take objects
    foreach ($element['#options'] as $arrkey => $arrchoice) {
      if (is_object($arrchoice)) {
        foreach ($arrchoice->option as $objkey => $objchoice) {
          $element['#options'][$objkey] = $objchoice;
        }
      }
    }

    foreach ($element['#options'] as $arrkey => $arrchoice) {
      if (is_object($arrchoice)) {
        unset($element['#options'][$arrkey]);
      }
    }
  }

  if ($element['#multiple']) {                                  //Multiselect -> Checkboxes
    $element['#type'] = 'checkboxes';

    // Non-required checkboxes need no '<none>' choice
    if (!$element['#required']) {
      unset($element['#options']['']);
    } else {
      //$element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';
    }

    if (is_array($element['#default_value'])) {
        $element['#default_value'] = array_values($element['#default_value']);
    } else {
        $element['#default_value'] = array();
    }
    
    _alterform_expand_checkboxes($element, $vid);               //Checkboxes need somewhat more processing...
  } 
  else 
  {                                                             //Singleselect -> Radiobuttons
/*
  $element['#type'] = 'radios';
   
  $element['#default_value'] = $element['#default_value'][0];

  if (!$element['#required']) {
     $element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';
  } else {
    $element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';   //Change '-Please choose-'      
  }    
*/  
  }
  
  unset($element['#theme']);
}  
  
/**
 *  
 */
/**
 * _alterform_expand_checkboxes()
 * 
 * Internal function that transforms multi-select vocabularies.
 * 
 * @param mixed $element - The vocabulary to transform. 
 * @param mixed $vid - The vocabulary Id (needed to create a unique name for the Radiobutton Group). 
 * @return void
 */
function _alterform_expand_checkboxes(&$element, $vid) {
  $value = is_array($element['#default_value']) ? $element['#default_value'] : array();
  $element['#tree'] = TRUE;

  $values = array_values($value);

  if (count($element['#options']) > 0) {
    foreach ($element['#options'] as $key => $choice) {
          if (!isset($element[$key])) {
        $tchoice = ltrim($choice, '-');
        $depth = strlen($choice) - strlen($tchoice);
        $element[$key] = array(
          '#type' => 'checkbox',
          '#title' => $tchoice,
          '#prefix' => '<div style="margin-left: ' . (20 * $depth) . 'px">',
          '#suffix' => '</div>',
          '#return_value' => $key,
          '#default_value' => in_array($key, $values),
          '#attributes' => $element['#attributes'],
          '#processed' => TRUE,
        );
      }
    }
    unset($element['#options']);
  }
}

/**
 * _alterform_expand_radios()
 * 
 * Internal function that transforms single-select vocabularies. 
 * 
 * @param mixed $element - The vocabulary to transform. 
 * @param mixed $vid - The vocabulary Id (needed to create a unique name for the Radiobutton Group). 
 * @return void
 */
function _alterform_expand_radios(&$element, $vid) {
  $value = is_array($element['#default_value']) ? $element['#default_value'] : array();

  krumo($value);
 
  $values = array_values($value);

  if (count($element['#options']) > 0) {
    foreach ($element['#options'] as $key => $choice) {
      if (!isset($element[$key])) {
        $tchoice = ltrim($choice, '-');
        $depth = strlen($choice) - strlen($tchoice);
        krumo($key);
        $element[$key]['vocabulary_'.$vid] = $key;
        $element[$key]['vocabulary_'.$vid] = array(
          '#type' => 'radio',
          '#name' => 'vocabulary_'.$vid,
          '#title' => $tchoice,
          '#prefix' => '<div style="margin-left: ' . (20 * $depth) . 'px">',
          '#suffix' => '</div>',
          '#return_value' => $key,
          '#default_value' => !in_array($key, $values),
//        '#default_value' => $values,
                  '#attributes' => $thiselement['#attributes'],
          '#parents' => $thiselement['#parents'],
          '#processed' => TRUE,
        );
      }
    }
//  unset($element['#default_value']);
    unset($element['#options']);
  }
}

Add the the theme's css:

Note it will color the taxonomy areas a bit so you can see where they are. Just remove the background-color attributes to disable this.

/**
 * 02-10-2008 VEG Added for Alterform Module
 */
div.taxonomy-form {
  background-color: #fff;
  float: left;
  margin-right: 3em;
}

.taxonomy-form .form-item {
  margin-bottom: 2px;
  margin-top: 0;
  padding-bottom: 0;
}

.taxonomy-lite-form {
  float: left;
  margin-right: 3em;
}

.taxonomy-lite-form .form-item {
  margin-bottom: 2px;
  margin-top: 0;
  padding-bottom: 0;
}

G.W. van der Vegt

nancydru’s picture

Why not use Taxonomy Super Select? It will be ported to 6.x momentarily, but feel free to submit a patch yourself. (Oh, BTW, saving space on the radios is a simple problem that TSS already has solved.)

NancyDru (formerly Nancy W. until I got married to Drupal)

eikes’s picture

I agree, TSS is much better and there's a working patch for D6 in the issue queue

johnhanley’s picture

wvd_vegt,

I needed to display my taxonomy multi-select as checkboxes and your module worked like a charm. Thanks very much for sharing.

-------------------------------------------------------

"If you don't read the newspaper you are uninformed;
if you do read the newspaper you are misinformed."
-- Mark Twain

summit’s picture

Him Bacteria_Man and Wvd_vegt,

Why not bring the module into drupal.org as say..taxonomy_checkbuttons or something like this?
Then more people will have advantage from this!

Thanks a lot in advance for considering this!
Greetings,
Martijn
www.trekking-world.com

johnhanley’s picture

Summit, I completely agree, this should be published as an official module. The name "taxonomy_checkboxes" seems fitting, but I'll leave it to Wvd_vegt to decide since it's his module.

In the meantime copying/pasting the files from his post works perfectly.

-------------------------------------------------------

"If you don't read the newspaper you are uninformed;
if you do read the newspaper you are misinformed."
-- Mark Twain

dharamgollapudi’s picture

subscribing...

cyberwolf’s picture

I think if the module is called alterform, the function in the .install file needs to call alterform_install() instead of alter_form_select_types_install(). Also the SQL query needs to be modified, the condition name = 'alter_form_select_types' needs to name = 'alterform'

cyberwolf’s picture

Found another bug in the code posted above: the order and hierarchy of the terms is not correct if any term IDs match the keys of the original #options array.

The code below fixes this. Just replace the _alterform_modify_select method with the one below.

function _alterform_modify_select(&$element, $vid) {

  if ($element['#multiple']) {                                  //Multiselect -> Checkboxes
    // Convert options from objects to arrays
    // as checkboxes and options (unlike select) don't take objects
    $new_options = array();
    foreach ($element['#options'] as $arrkey => $arrchoice) {
      if (is_object($arrchoice)) {
        foreach ($arrchoice->option as $objkey => $objchoice) {
          $new_options[$objkey] = $objchoice;
        }
      }
      $element['#options'] = $new_options;
    }
  }

  if ($element['#multiple']) {                                  //Multiselect -> Checkboxes
    $element['#type'] = 'checkboxes';

    // Non-required checkboxes need no '<none>' choice
    if (!$element['#required']) {
      unset($element['#options']['']);
    } else {
      //$element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';
    }

    if (is_array($element['#default_value'])) {
        $element['#default_value'] = array_values($element['#default_value']);
    } else {
        $element['#default_value'] = array();
    }
   
    _alterform_expand_checkboxes($element, $vid);               //Checkboxes need somewhat more processing...
  }
  else
  {                                                             //Singleselect -> Radiobuttons
/*
  $element['#type'] = 'radios';
  
  $element['#default_value'] = $element['#default_value'][0];

  if (!$element['#required']) {
     $element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';
  } else {
    $element['#options'][''] = '&lsaquo;' . t('none') . '&rsaquo;';   //Change '-Please choose-'     
  }   
*/ 
  }
 
  unset($element['#theme']);
} 
Mark Theunissen’s picture

There is a real module that does taxonomy checkboxes:

http://drupal.org/project/betterselect

cyberwolf’s picture

The betterselect module does not seem to handle hierarchical vocabularies well, it just displays them all under each other without any indenting. The code above does display the checkboxes with indenting though.

summit’s picture

+1 for taxonomy_checkboxes module. When it becomes a module, also maintenance on it would be much easier!

Melissamcewen’s picture

+2 for this!

nancydru’s picture

It would be far better to help out with the modules that already exist to do this. We simply do not need a duplicate module.

oknate’s picture

I tried the betterselect module and was excited by the bits and pieces I was able to extract and use, but not by the output of the module by itself. It also had some extra javascript that was unnecessary to me, so in the interest of figuring out how this works, I took pieces of the betterselect module for my own use.

The idea was to overcome a few annoying things with the betterselect module, that are also wrong with the default multiselect drop down.

1) the dash or hyphen to show the depth
2) the complete lack of showing the hierarchy visually.

So here's what I took and used:

in my hook form alter:


function ivoreportcard_form_alter(&$form, &$form_state, $form_id) {

	if($form_id == 'ivoreportcard_node_form') {
		
		
	       // add my custom process function to the terms I'd like to turn into checkboxes
		$form['taxonomy'][11]['#process'] = array('ivoreportcard_checkboxes');
		$form['taxonomy'][12]['#process'] = array('ivoreportcard_checkboxes');
		
                // the submit function I took from betterselect module
		$form['#submit'][] = 'ivoreportcard_taxonomy_from_checkboxes';
    }

}


Here's the submit function I borrowed from betterselect. Thank you, thank you for this!



/**
 * Restore submitted checkbox values back to format Taxonomy module expects.
 */
function ivoreportcard_taxonomy_from_checkboxes($form, &$form_state) {
  foreach ($form_state['values']['taxonomy'] as $index => $properties) {
    if (is_numeric($index) && $form['taxonomy'][$index]['#multiple']) {
      $options = array();
      foreach ($form_state['values']['taxonomy'][$index] as $tid => $selected) {
        if ($selected) {
          $options[$tid] = $tid;
          
        }
      }
      $form_state['values']['taxonomy'][$index] = $options;
    }
  }
}

The custom process function, borrowing heavily from betterselect:

function ivoreportcard_checkboxes($element) {
	
	$element['#type'] = 'checkboxes';
	unset($element['#theme']);
	
	 // If we're dealing with taxonomy, fix the option titles.
    if (is_object($element['#options'][0])) {
      // Build new array of options in format that theme_checkboxes expects.
      $options = array();
      $defaults = array();
      
      foreach ($element['#options'] as $option) {
        if (is_array($option->option)) {
          foreach ($option->option as $tid => $name) {
               /**
               * this is my custom alteration to the betterselect module, I'm querying the database
                * to get the name of the term, without the annoying hyphen in front
                */

          	$name = db_result(db_query("SELECT name FROM term_data WHERE tid = %d",$tid));
            $options[$tid] = check_plain($name);
            
           
          }
        }
      }
      $element['#options'] = $options;
    }

    // We now check that the element's options are correct. If they're not in
    // the right format, we abort.
    foreach($element['#options'] as $key => $val) {
      if (!is_numeric($key) || !is_string($val)) {
        return $element;
      }
    }
	
	
	    // The default value should be an array, if it isn't expand_checkboxes()
    // will make it one.
    if (isset($element['#default_value']) && is_array($element['#default_value']) && count($element['#default_value'])) {
      // Fix the value arrays; checkboxes are the exact inverse of multiselect,
      // apparently for vindictive fun.

      // First make sure there's at least 1 non-blank array element
      $temp = $element['#default_value'];
      $temp = array_shift($element['#default_value']);
      if (!empty($temp)) {
        $element['#default_value'] = array_flip($element['#default_value']);
        $element['#value'] = array_flip($element['#value']);
      }
    }


    // Switch display to checkboxes instead.
    $element['#type'] = 'checkboxes';
    unset($element['#theme']);
    $element = expand_checkboxes($element);
	
    $vid = $element['#parents'][1];
    
    
    /**
    *
    * get a datastructure that gives us the relationships of the terms, so that we can create a two level
    * hierarchy and know where to open and close our divs, 
    * and which are the parent terms, which we will turn into headers
    * (see 'ivoreportCard_get_parent_array' function below)
     */
	$parentsArray = ivoreportCard_get_parent_array($vid);



	
    
    	foreach($element as $key => $value) {
    	
    		
    	
    	if( is_numeric($key) ){
    		if($parentsArray[$key] == 0) {
    		
    			/** turn the parent elements into headers, and make them unselectable by 
                        * converting them into markup form element, instead of a checkbox.  We open the
                        * div right before
                         */
		    	$element[$key] = array('#type' => 'markup', '#value' => '<div class="block yellow" style="width: 98%; margin-right: 15px;"><p><b>'.$element[$key]['#title'].'</b></p>');
				
    		}
                /*
                 * Now we have to do some checking to see if the checkbox is the last child
                 * of the one that opened our div to close it.  It goes without saying this
                 * code sample only handles a two level taxonomy, with parents and children.
                 * that's all I usually need, and I like simple solutions often over the most portable
                 * because I like code I can read!
                 */
    		else if ($parentsArray[$key]) {
	    				$arr = $parentsArray['#topdown'][$parentsArray[$key]]['#children'];
	    				
	    				
	    				if($key == $arr[sizeof($arr)-1]) {
	    					$element[$key]['#suffix'] = "</div>";
	    				}
    				

    		}
    	}
    		
    		
    		
    	} // end foreach

	return $element;
}

This is a utility function that gets an array with the parent term ids for each term, as well as arrays with the parent term as the key element, to help figure out which is the last child term, after which we close our div tag. (see above)


function ivoreportCard_get_parent_array($vid) {
	$parentArray = array();
	
		$query = "SELECT td.*,th.parent, td2.name AS parentName 
FROM term_data td 
JOIN term_hierarchy th ON th.tid = td.tid 
LEFT JOIN term_data td2 ON th.parent = td2.tid 
WHERE td.vid = %d ORDER BY parent ASC";
		
	$result = db_query($query,$vid);
	
	while($r = db_fetch_array($result)) {
		$parentsArray['#topdown'][$r['parent']]['#children'][] = $r['tid'];
		$parentsArray[$r['tid']] = $r['parent'];
	}
	
	
	return $parentsArray;
}

I used this code in another project, but instead of having child terms, I just had a simple vocabulary of terms, so I altered the function above to remove the formatting:


function taxonomycheckboxes_checkboxes($element) {
   
    $element['#type'] = 'checkboxes';
    unset($element['#theme']);
   
     // If we're dealing with taxonomy, fix the option titles.
    if (is_object($element['#options'][0])) {
      // Build new array of options in format that theme_checkboxes expects.
      $options = array();
      $defaults = array();
     
      foreach ($element['#options'] as $option) {
        if (is_array($option->option)) {
          foreach ($option->option as $tid => $name) {
               /**
               * this is my custom alteration to the betterselect module, I'm querying the database
                * to get the name of the term, without the annoying hyphen in front
                */

              $name = db_result(db_query("SELECT name FROM term_data WHERE tid = %d",$tid));
            $options[$tid] = check_plain($name);
           
          
          }
        }
      }
      $element['#options'] = $options;
    }

    // We now check that the element's options are correct. If they're not in
    // the right format, we abort.
    foreach($element['#options'] as $key => $val) {
      if (!is_numeric($key) || !is_string($val)) {
        return $element;
      }
    }
   
   
        // The default value should be an array, if it isn't expand_checkboxes()
    // will make it one.
    if (isset($element['#default_value']) && is_array($element['#default_value']) && count($element['#default_value'])) {
      // Fix the value arrays; checkboxes are the exact inverse of multiselect,
      // apparently for vindictive fun.

      // First make sure there's at least 1 non-blank array element
      $temp = $element['#default_value'];
      $temp = array_shift($element['#default_value']);
      if (!empty($temp)) {
        $element['#default_value'] = array_flip($element['#default_value']);
        $element['#value'] = array_flip($element['#value']);
      }
    }


    // Switch display to checkboxes instead.
    $element['#type'] = 'checkboxes';
    unset($element['#theme']);
    $element = expand_checkboxes($element);
   
    $vid = $element['#parents'][1];
   
   

    return $element;
}



 

if you are new to programming, I suggest you print_r the data structure above and then die, so you can copy it into your module and inspect it. Here's some code you can plop into your custom module to look at data structures:

     print_r("<pre>");
print_r($parentsArray);
print_r("</pre>");
die();

Just substitute $parentsArray with whatever you're trying to look at!

Anyway, I spent a day trying to output a taxonomy in a form in this manner, so I hope this helps someone save some time, or at least learn how to get around one of Drupal 6's limitations!

tev’s picture

Hi Nate,

your code works like a charm!

I'm only having issues getting it to work with a zen sub theme.
This doesn't mean that the problem is in your code (btw, thanks for sharing, Nate!).
With Garland or Zen (not sub theme) everything works a expected.

Zen sub theme: I only get the output: "Array"

Has anybody got this to work with a zen sub theme?

I have the $closure output according to drupal standards.

Somehow I've reached the end of my debugging skills :-(

Thanks in advance.
Regards, Steven

tev’s picture

I had renamed the function of the above example
ivoreportcard_checkboxes($element)
according to my module to
mymodule_checkboxes($element)

Turns out that by some naming convention drupal invoked this method for the preprocessing of other checkbox elements outside of my module, for example during views configuration.

Working solution:
mymodule_taxonomy_to_checkboxes($element)

Problem solved :-)