'Nodereference graph', 'page callback' => 'graphviz_noderef_page', 'page arguments' => array(1), 'access arguments' => array('access Graphviz Noderef display'), 'type' => MENU_LOCAL_TASK, ); return $items; } /** * options form for the node tab. */ function graphviz_noderef_page_form($form_state) { $options = drupal_map_assoc(range(0, 5)); // $form['title'] = array( // '#type' => 'textfield', // '#title' => t('Graph title'), // '#default_value' => isset($_POST['title']) ? $_POST['title'] : "" , // '#maxlength' => 64, // '#description' => t('title of the graph'), // ); $form['recursion_depth'] = array( '#type' => 'select', '#title' => t('Recursion depth'), '#default_value' => isset($_POST['recursion_depth']) ? $_POST['recursion_depth'] : 1 , '#options' => $options, '#description' => t('depth to continue searching. 0 only returns nodes 1 step away, 5 returns nodes up to 6 steps away'), ); $options = array('dot' => 'dot','neato' => 'neato','fdp' => 'fdp','twopi' => 'twopi','circo' => 'circo'); $form['command'] = array( '#type' => 'select', '#title' => t('Command'), '#default_value' => isset($_POST['command']) ? $_POST['command'] : "dot" , '#options' => $options, '#description' => t('which graphviz command to use to render the graph.'), ); $options = array('png'=> t("png"),'jpg' => t("jpg"),'gif' => t("gif"),'svg'=> t("svg")); $form['formats'] = array( '#type' => 'checkboxes', '#title' => t('Output Formats'), '#default_value' => isset($_POST['formats']) ? $_POST['formats'] : array('png') , '#options' => $options, '#description' => t('Select which graphics output formats to render to.') ); $form['imagemap'] = array( '#type' => 'checkbox', '#title' => t('Imagemap'), '#default_value' => isset($_POST['imagemap']) ? $_POST['imagemap'] : true , ); $form['graphoptions'] = array( '#type' => 'textfield', '#title' => t('Graph options'), '#default_value' => isset($_POST['graphoptions']) ? $_POST['graphoptions'] : "label=\"\", labelloc=\"t\", overlap=false, splines=true, sep=0.2" , '#description' => t('Display options for the entire graph.'), ); $form['nodeoptions'] = array( '#type' => 'textfield', '#title' => t('Graph title'), '#default_value' => isset($_POST['nodeoptions']) ? $_POST['nodeoptions'] : "fontsize=8.0, style=filled, fillcolor=\"#ffeedd\"" , '#description' => t('Display options for nodes.'), ); $form['edgeoptions'] = array( '#type' => 'textfield', '#title' => t('Graph title'), '#default_value' => isset($_POST['edgeoptions']) ? $_POST['edgeoptions'] : "color=\"lightblue\", penwidth=3, fontsize=8.0" , '#description' => t('Display options for edges.'), ); $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); $form['#redirect'] = FALSE; return $form; } /** * Page callback for node/%node/graph (See graphviz_noderef_menu()). */ function graphviz_noderef_page($node) { $output = drupal_get_form('graphviz_noderef_page_form'); $recursion_depth = isset($_POST['recursion_depth']) ? $_POST['recursion_depth'] : 1; $command = isset($_POST['command']) ? $_POST['command'] : "dot"; $formats = isset($_POST['formats']) ? $_POST['formats'] : array('png'); $imagemap = isset($_POST['imagemap']) ? "TRUE" : "FALSE" ; // $title= isset($_POST['title']) ? $_POST['title'] : ""; $graph =isset($_POST['graphoptions']) ? $_POST['graphoptions'] : "label=\"\", labelloc=\"t\", overlap=false, splines=true, sep=0.2"; $nodes =isset($_POST['nodeoptions']) ? $_POST['nodeoptions'] : "fontsize=8.0, style=filled, fillcolor=\"#ffeedd\""; $edges =isset($_POST['edgeoptions']) ? $_POST['edgeoptions'] : "color=\"lightblue\", penwidth=3, fontsize=8.0"; $headers = array('command'=>$command,'formats'=>$formats,'imagemap'=>$imagemap, 'graph'=>$graph,'nodes'=>$nodes,'edges'=>$edges); $output .= graphviz_noderef_render($node, $recursion_depth, $headers); return $output; } /** * Render the map!. */ function graphviz_noderef_render($node, $recursion_depth = 1, array $headers) { $tree = graphviz_noderef_get_ref_array((int)$node->nid,(int)$recursion_depth); $tree[2][(int)$node->nid]['target'] = true; $dot = graphviz_noderef_data_to_dot($tree[0], $tree[2], $headers); $output = graphviz_filter_process($dot); // $output = "
$dot
" . $output ; return $output; } /** * Custom functions for gathering and formatting data. * **/ function graphviz_noderef_data_to_dot(array $ref_array, array $parameters = array(), array $headers = array(), array $style = array()) { //initialize output $output = ""; //print headers $output .= graphviz_noderef_headers_to_dot($headers); //convert the parameters to dot format. $output .= graphviz_noderef_params_to_dot($parameters); //convert links array to dot format links $output .= graphviz_noderef_refs_to_dot($ref_array, $style); //print end $output .="};"; return $output; } /** * ouptut the actual node-network data in dot format. **/ function graphviz_noderef_refs_to_dot(array $ref_array, array $style = array()){ //convert links array to dot format links $link_array = array(); foreach ($ref_array as $link){ if ($link['type'] == "nodereference") { $from = $link['nid']; $to = $link['ref']; $field_name = $link['field']; } elseif ($link['type'] == "nodereferrer"){ $from = $link['ref']; $to = $link['nid']; $field_name = $link['referrer']; } $action = str_replace(array("field_","_"),array(""," "), $field_name); $link_array[] .= "\t$from -> $to [ label = \"$action\" ];\n"; } $output = join(array_unique($link_array)); return $output; } /** * print headers that tell graphviz_filter_process how to render the graph. **/ function graphviz_noderef_headers_to_dot(array $options = array() ) { $defaults = array('command' => "dot", 'formats' => "png", 'imagemap' => true); $headers = array_merge($defaults,$options); foreach ($headers as $key => $value) { switch ($key) { case 'command': $command = $value; break; case 'formats': $formats = join($value); break; case 'imagemap': $imagemap = ($value == true) ? "true" : "false" ; break; case 'graph': $graph = $value; break; case 'nodes': $nodes = $value; break; case 'edges': $edges = $value; break; } } $output = << $data) { $shape = ($data['type'] == "organisation") ? "box" : "ellipse"; $url = url("node/". $nid); $output .= "\t$nid [ shape = \"$shape\" label = \"". $data['title'] . "\" URL = \"". $url."\" " .(isset($data['target']) ? "fillcolor = pink" : "") . " ];\n"; } return $output; } function graphviz_noderef_get_ref_array($nid, $recursion_depth = 2, array $checked_nodes = array()) { // this function causes segfault when recursing from computed fields (perhaps other areas too?), probably stack overflow. Might be able to reduce stack by reducing arrays sent back? // ob_flush(); // static $runCount=1; print $runCount++; // ob_flush(); // don't be stupid - first two arguments must be int >= 1 if ( !is_int($nid) ) { print("Error: your nid argument is not an integer"); return;} if ($nid <= 0 ) { ("Error: your nid argument is $nid"); return;} if ( !is_int($recursion_depth) ) { ("Error: Your recursion depth is not an integer."); return;} if ( $recursion_depth < 0 ) { ("Error: Your recursion depth is less than 0"); return;} if ( in_array( $nid , $checked_nodes ) ) { ("Node $nid has already been checked"); return; } $node = node_load($nid); $node_data = array(); //get all custom fields of type referrer/reference from node. list($node_ref_links, $nids_to_check) = graphviz_noderef_get_node_refs($node); //add checked node to array to pass if ( !in_array($nid,($checked_nodes))){ $checked_nodes[] = $nid; } //collect ye node_data here if ( !in_array($nid, array_keys($node_data))){ $node_data["$nid"] = array ( 'title' => $node->title, 'type' => $node->type, 'depth' => $recursion_depth, 'count'=>1 ); } else { $node_data["$nid"]['count'] += 1; } //reduce recursion depth $recursion_depth--; //initialise $return_array $return_array = array(); //RECURSE - need to check checked_nodes! //only if recursion level still high enough if ($recursion_depth >= 0 ) { foreach ($node_ref_links as $node_ref) { //make sure that node hasn't already been checked. if ( in_array( $node_ref['ref'], $checked_nodes ) ) { //dprint_r( "Node ". $node_ref['ref'] . " has already been checked."); } else { //append results // $recursion_results = array(array(),array()); $recursion_results = graphviz_noderef_get_ref_array($node_ref['ref'], $recursion_depth, $checked_nodes); $return_array = array_merge($return_array,$node_ref_links, $recursion_results[0]); $checked_nodes = $checked_nodes + $recursion_results[1]; $node_data = $node_data + $recursion_results[2] ; } } } else { //if we've finished recursing, we still need to check the last nodes for title, url, etc. $return_array = array_merge($return_array,$node_ref_links); foreach ($nids_to_check as $nid) { if ( !in_array($nid, array_keys($node_data) ) ) { $node = node_load($nid); $node_data["$nid"] = array ( 'title' => $node->title, 'type' => $node->type, 'depth' => $recursion_depth ); $node_data["$nid"]['count'] = 1; } } } return array( $return_array, $checked_nodes, $node_data ); } /** * Get all the references to/from given node. * returns array of references and array of nids * * @param $node * Node object produced by node_load() */ function graphviz_noderef_get_node_refs($node){ //initialise arrays $references = array(); $referrers = array(); $fields = array(); $nids_to_check = array(); $fields = graphviz_noderef_get_ref_fields(); //loop over all possible reference/referrer fields, and add any that exist to apporopriate array foreach($fields as $field) { $field_name = $field['name']; if (isset($node->{$field_name}) && count($node->{$field_name}) > 0){ if ($field['type'] == "nodereference") { foreach ($node->$field_name as $reference){ if ( (int)($reference['nid']) != 0 ) { $references[] = array('nid' => $node->nid, 'type' => $field['type'], 'field' => $field_name, 'ref' => (int)($reference['nid']) ); $nids_to_check[] = $reference['nid']; } } } elseif ($field['type'] == "nodereferrer"){ $ref_field = $node->$field_name; foreach ($ref_field[0]['items'] as $referrer) { $nids_to_check[] = $referrer['nid']; foreach ($referrer['referrers'] as $referrername) { $referrers[] = array('nid' => $node->nid, 'type' => $field['type'], 'field' => $field_name, 'ref' => (int)$referrer['nid'], 'referrer' => $referrername); } } } else { return "Error: \$field['name'] is bad: " . $field['name']; } } } //stick $references and referrers in array together $node_ref_links = array_merge($references, $referrers); return array($node_ref_links, $nids_to_check); } /** * Get all nodereferer and nodereference fields */ function graphviz_noderef_get_ref_fields(){ $fields = array(); $result_fields = db_query( "SELECT field_name, type FROM {content_node_field} WHERE type = '%s' OR type = '%s' " , "nodereference", "nodereferrer"); while ($row = db_fetch_object($result_fields)) { $fields[] = array('name' => $row->{"field_name"}, 'type' =>$row->{"type"}); } return $fields; }