Automatic parent population with child nodes: In other words, when you click "add child category" this function will retrieve the data from the parent category and auto populate it, streamlining the category posting process immensly.

I wasn't sure how to post this, but after mentioning it within 5 minutes I got a request for a post. So I'm going ahead and posting the code I used to do this feature. This is as far as I am concerned a hack, but it hasn't failed me yet. Programming this was more fun than a clown car on fire, I hope you enjoy it as much as me. :)

NOTE: I take no responsibility in what this code will do to you're version of drupal and the category module. Use it at your own risk. That being said please backup you're modules before applying code from strange people. Onto the code!

Category.inc
First a new function for the category.inc file. This function checks for parent's if the variable it retrieves is empty. The idea behind it? Basically if you have a parent variable, and it's full, why recheck?

/**
 * Retrieves the parent node from the address bar  - stopchick
 *
 * @param $parentCheck
 *   Variable used and examined to see if there are parent variables already.
 *
 * @return
 *   parent ID from the address bar if there arent parents
 */
function category_check_for_parents($parentCheck) {

	if(empty($parentCheck)){
		$queryThing = $_GET['q'];
		$qPieces = explode("/", $queryThing);
		$parents = array(array_pop($qPieces));
		return $parents;
	}

	return $parentCheck;
}

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

Next were going to go to the "category_get_form" function, once there were gonna scroll wayyyy down to the area that looks like this:

  $blank = '<'. t('root') .'>';
  $hierarchy = 1;
  $parents = array();
  $exclude = array();
  $default_parent = 0;

  if ($node->nid || $node->cnid) {
    $parents = category_get_parents($node->nid);
    $hierarchy = $is_cat ? (isset($node->hierarchy) ? $node->hierarchy : 1) : variable_get('category_distant_containers', 1);

    // This makes the parent element have all possible options on submit,
    // to prevent validation errors with activeselect.
    if (!$activeselect && $is_cat) {
      $default_parent = $node->cnid;
    }

    if (empty($parents) && $node->cnid) {
      $parents[] = $node->cnid;
    }
  }

...and we want to add some code and make it look like this:


  $blank = '<'. t('root') .'>';  //qxz0001: populates parents in category outline adder//stopchick
  $hierarchy = 1;
  $parents = array();
  $exclude = array();
  $default_parent = 0;

  if ($node->nid || $node->cnid) {
    $parents = category_get_parents($node->nid);
	
	
//stopchick code below.  This snippet forces the parent of a new category to take the parent item in the address bar.

    for($i=0;$i<count($parents);$i++){
      $parents[$i] = $parents[$i]->cid;
    }

	if(!$parents){ 
		$parents = category_check_for_parents($parents); //Function that checks if there are parents in the URL and if not, sends back empty
	}

//end stopchick

    $hierarchy = $is_cat ? (isset($node->hierarchy) ? $node->hierarchy : 1) : variable_get('category_distant_containers', 1);

    // This makes the parent element have all possible options on submit,
    // to prevent validation errors with activeselect.
    if (!$activeselect && $is_cat) {
      $default_parent = $node->cnid;
    }

    if (empty($parents) && $node->cnid) {
      $parents[] = $node->cnid;
    }
  }

.o.o.O.o.o.
-------------------------------------------

category.module
Next it's time to edit the main module, no new functions in here, just a couple places to reference parents.
If you run a search you should find a bit of code that looks like this...

        else {
          $node_cats = array();
          foreach ($cats as $cat) {
            if ($cat->cnid == $container->cid) {
              $node_cats[] = $cat->cid;
            }
          }
          if (isset($default_cat)) {
            $node_cats[] = $default_cat;
          }
          if (empty($node_cats)) {
            $node_cats = NULL;
          }

          $form['category'][$container->cid] = category_form_node($container->cid, $node_cats, $container->help, 'category');  
          $form['category'][$container->cid]['#weight'] = $container->weight;
        }
      }

Were going to add one line in and make it look like this...

        else {
          $node_cats = array();
          foreach ($cats as $cat) {
            if ($cat->cnid == $container->cid) {
              $node_cats[] = $cat->cid;
            }
          }
          if (isset($default_cat)) {
            $node_cats[] = $default_cat;
          }
          if (empty($node_cats)) {
            $node_cats = NULL;
          }

          $form['category'][$container->cid] = category_form_node($container->cid, $node_cats, $container->help, 'category');
		  
	    $form['category'][$container->cid]['#default_value'] = category_check_for_parents($form['category'][$container->cid]['#default_value']); 
	    //Homebrew function by JonathanDStopchick
		  
          $form['category'][$container->cid]['#weight'] = $container->weight;
        }
      }

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

In the function _category_category_select change it from...

  $options = _category_category_select_options($cnid, $multiple, $blank, $value, $exclude);

  return array(
    '#type' => 'select',
    '#title' => $title,
    '#default_value' => $value,
    '#options' => $options,
    '#description' => $description,
    '#multiple' => $multiple,
    '#size' => $multiple ? min(12, count($options)) : 0,
    '#required' => TRUE,
  );

...to the following

  $options = _category_category_select_options($cnid, $multiple, $blank, $value, $exclude);

    if(!$value){//stopchick - parents check
		$value = category_check_for_parents($value); //Function that checks if there are parents in the URL and if not, sends back empty
	}
	

  return array(
    '#type' => 'select',
    '#title' => $title,
    '#default_value' => $value,
    '#options' => $options,
    '#description' => $description,
    '#multiple' => $multiple,
    '#size' => $multiple ? min(12, count($options)) : 0,
    '#required' => TRUE,
  );

...and that's it, that bit of code should allow you have an automatic parent check. Plus the function can be useful for other purposes!!! :-P OO is fun!

Comments

JonathanDStopchick’s picture

Oh dang, category_get_form posted incorrectly folks!! Ok, what the change should like is this.

Before

  $blank = '';
  $hierarchy = 1;
  $parents = array();
  $exclude = array();
  $default_parent = 0;

  if ($node->nid || $node->cnid) {
    $parents = category_get_parents($node->nid);
    $hierarchy = $is_cat ? (isset($node->hierarchy) ? $node->hierarchy : 1) : variable_get('category_distant_containers', 1);

    // This makes the parent element have all possible options on submit,
    // to prevent validation errors with activeselect.
    if (!$activeselect && $is_cat) {
      $default_parent = $node->cnid;
    }

    if (empty($parents) && $node->cnid) {
      $parents[] = $node->cnid;
    }
  }

After

$blank = '';  //qxz0001: populates parents in category outline adder//stopchick
  $hierarchy = 1;
  $parents = array();
  $exclude = array();
  $default_parent = 0;

  if ($node->nid || $node->cnid) {
    $parents = category_get_parents($node->nid);
	
	
//stopchick code below.  This snippet forces the parent of a new category to take the parent item in the address bar.

    for($i=0;$i<count($parents);$i++){
      $parents[$i] = $parents[$i]->cid;
    }

	if(!$parents){ 
		$parents = category_check_for_parents($parents); //Function that checks if there are parents in the URL and if not, sends back empty
	}

//end stopchick

    $hierarchy = $is_cat ? (isset($node->hierarchy) ? $node->hierarchy : 1) : variable_get('category_distant_containers', 1);

    // This makes the parent element have all possible options on submit,
    // to prevent validation errors with activeselect.
    if (!$activeselect && $is_cat) {
      $default_parent = $node->cnid;
    }

    if (empty($parents) && $node->cnid) {
      $parents[] = $node->cnid;
    }
  }

OK, hopefully with :code: tags it wont kill my brackets! G'lok folks!

TheWhippinpost’s picture

What version of Category are you running this against Johnathon?

I ask because the code in category_get_form you quote:

$blank = '';
  $hierarchy = 1;
  $parents = array();
  $exclude = array();
  $default_parent = 0;

Differs in my version of the latest CVS:

$blank = '<'. t('root') .'>';
  $hierarchy = 1;
  $parents = array();
  $exclude = array();
  $default_parent = 0;

So, like the crazy gadget man I am, I went ahead anyway and got the following error:

Parse error: parse error, unexpected $end in C:\Servers\Apache2\cgi-bin\drupal\modules\category\category.inc on line 1554

PS... I'm sure there was another code discrepancy too but I've rolled-back and my memory is cack McSmack!

HTH

JonathanDStopchick’s picture

// $Id: category.module,v 1.100.2.17 2006/07/10 09:36:36 jaza Exp $
//4.7.0 version

// $Id: category.inc,v 1.42.2.13 2006/07/10 09:02:10 jaza Exp $
//4.7.0 version

I was able to successfully implement this into the cvs version before fyi, so I'm not sure what the problem may be. But removing that "root" text(changing the $blank variable) I wouldn't think would do anything, of course, that is a wild guess. I look more into it later.

DayShallCome’s picture

This works very well for me. It makes adding child Container's MUCH easier. Congrats.

Now all I need to do is leverage it to make it so that other nodes (Blog posts, Stories, etc) can use this to import Parent data to their Category settings for easy content creation.

I'm not much of a programmer, so any ideas would be fantastic.

JonathanDStopchick’s picture

Oy vey, I acutally did this too o_O;... Lol, ok, I'll list the process.

I don't have the exact details, but I'll try my best to do it from memory. (The server was taken down by the admin for awhile)

Firstly you must allow your nodes to be treated as categories.
Step 1 - Go into admin/settings/categories, and expand content type settings. Check the box: "Allow other content types to be: Categories "
Step 2 - Dropdown the "Category transform settings" and select the node types you wish you use as categories.
Step 3 - Go to admin/categories and edit your target container. (the one you want to put other nodes in)
Step 4 - Under the "Container information" add the types of nodes you want to appear under that container.

Ok, thats all the steps for adding nodes that can have a category outline, now here is the fun part. You want children category we get to add a little bit of code.

category.module
Open category.module in your favorite text editor and find the line that looks like this:

  if ($type == 'node' && isset($node->parents)) {
    if (!$main && ($node->type != 'category-cat' || $node->hierarchy)) {
      if (_category_privileged('create categories')) {
        $links[] = l(t('add child category'), "node/add/category-cat/parent/$node->nid");
      }
      if (_category_privileged('create containers')) {
        $links[] = l(t('add child container'), "node/add/category-cont/parent/$node->nid");
      }
    }
  }

and add in a couple link lines: Each line is custom based on node types

  if ($type == 'node' && isset($node->parents)) {
    if (!$main && ($node->type != 'category-cat' || $node->hierarchy)) {
      if (_category_privileged('create categories')) {
        $links[] = l(t('add child category'), "node/add/category-cat/parent/$node->nid");
        $links[] = l(t('add an external link'), "node/add/redirection/parent/$node->nid"); //this adds the nodetype "redirection"
        $links[] = l(t('add a file as a subpage'), "node/add/disknode/parent/$node->nid");//this adds a "disknode"

	  }
      if (_category_privileged('create containers')) {
        $links[] = l(t('add a container'), "node/add/category-cont/parent/$node->nid");
      }
    }
  }

That should be the gist of it. You got yourself two more links that will add and populate automatically. Add as many or as few as you wish in the code.

DayShallCome’s picture

Great advice, as always.

As it is, though, I don't think you need to make the Node Types actual Categories, as it worked for me without doing so.

If only it worked for Category systems with active selects involved (as in, various menu levels that actively query the database). But I think that would require taking the parent of the parent node, etc. That might be pretty intensive to code.

DayShallCome’s picture

Actually, it would be really cool if someone could figure out how to make this work when you have a system of Active Select containers.

For instance, let's say you have (* denotes container):

Choose Animal*
Horse
Cat

Choose Breed (hidden)*
-Siamese
-Wildcat
-Shetland
-Donkey

With the members of Choose Breed able to have distant parents, so that the menu looks like this

Choose Animal*
Horse
-Shetland
-Donkey
Cat
-Siamese
-Wildcat

Having a system like this, it's hard to import parents into new Node Creation forms, because only the immediate parent is imported, and the higher containers (in this case Choose Animals) revert Choose Breed to NONE because of the active select situation. Does this make sense?

I'm assuming that you don't have this code, as your system does not seem to be using this set up. But if anyone has a similar set up, any input would be great. This would be a really cool feature for Category to have.

DayShallCome’s picture

Does anyone have a solution to this problem (or know how I might going about doing it)?

I have an active select system set up (with hidden containers and the like). I don't want to make existing nodes (like Blog, Story, etc) into categories for a number of reasons. Rather, I simply want to import parent data into the Category/Taxonomy of new nodes. In a system where hidden containers are not involved, Jonathan's method works perfectly. Unfortunately, my set up has activeselect and distant parents.

When I dig deeper than one level into my category hierarchy, and then attempt to create a Story (using Jonathan's cool method), the correct parent is imported into the appropriate Hidden Container's drop down menu. But it only stays there for a few seconds, because it's Parent Container is NONE, which then initializes my changed drop down menu back to NONE. Does this make sense?

How can I trace a Category's parents all the way back to root?

bdragon’s picture

How can I trace a Category's parents all the way back to root?

category_location() ?

DayShallCome’s picture

Hmm, interesting idea. I guess I should theoretically be able to use that function on any node, and then populate the ActivE select Boxes using the returned array. Cool! I'll work on this tomorrow.

DayShallCome’s picture

So what I'm trying to do is get the category data imported into my node creation form when I'm creating a new node under that category. As I'm using Active select, this is pretty tricky due to the Distant Parents

Alright, can someone check my logic here?

in category_form_alter, under case $type .'_node_form':

I will first test to see if this is a generic node creation form (node/add/nodetype) or one with a parent in the address bar (node/add/nodetype/parent/3).

If it does have a parent category (let's call it 'k'), I will find k's parent container. In that container's drop down menu, I will choose the correct category.

Then, I will find the distant parent of k, which will then become k. and the process will repeat itself until I find the root category.

Does this sound plausible? Also, how do I find a distant parent?

DayShallCome’s picture

So I'm looking at this code, trying to figure out what to do to fix my problem. How do I transfer data from an array into the select boxes in the Node Creation form?

What function actually propogates the data onto the screen?

DayShallCome’s picture

Okay, so I've figured that out. I now have only two issues:

1) In a hidden category, activeselect set up, I am interested in the both the parent and distant parent of a category. How would I go about tracing a category's distant parents all the way back to root? I can't see a function for this.

2) When this data is inputed to the taxonomy drop down menus, it works for about a second. But then activeselect causes the select boxes to revert to NONE. How would I go about disabling Active select for the initial propogation of the drop down menus?

Any help would be GREATLY appreciated. I'm very close here.

marcoBauli’s picture

Sorry, no coder, but maybe the patch for similar issue at http://drupal.org/node/68132 can help?