/** * drawRequirementsGraph Will draw a requirement graph if PEAR::Image_GraphViz is installed * * @param boolean $pInstallVersion Use the actual installed version instead of the version that will be in bitweaver after the upgrade * @param string $pFormat dot output format * @param string $pCommand dot or neato * @access public * @return boolean TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function drawRequirementsGraph($pInstallVersion = FALSE, $pFormat = 'png', $pCommand = 'dot') { global $gBitSmarty, $gBitThemes; // only do this if we can load PEAR GraphViz interface if (@(include_once 'Image/GraphViz.php')) { ksort($this->mPackages); $deps = $this->calculateRequirements($pInstallVersion); $delKeys = $matches = array(); // crazy manipulation of hash to remove duplicate version matches. // we do this that we can use double headed arrows in the graph below. foreach ($deps as $key => $req) { foreach ($deps as $k => $d) { if ($req['requires'] == $d['package'] && $req['package'] == $d['requires'] && $req['result'] == 'ok' && $d['result'] == 'ok') { $deps[$key]['dir'] = 'both'; $matches[$key] = $k; } } } // get duplicates foreach ($matches as $key => $match) { unset($delKeys[$match]); $delKeys[$key] = $match; } // remove dupes from hash foreach ($delKeys as $key) { unset($deps[$key]); } // start drawing stuff $graph = new Image_GraphViz(TRUE, $gBitThemes->getGraphvizGraphAttributes(), 'Requirements', TRUE); $fromattributes = $toattributes = $gBitThemes->getGraphvizNodeAttributes(); foreach ($deps as $node) { //$fromNode = ucfirst( $node['package'] )."\n".$node['package_version']; //$toNode = ucfirst( $node['requires'] )."\n".$node['required_version']['min']; $fromNode = ucfirst($node['package']); $toNode = ucfirst($node['requires']); switch ($node['result']) { case 'max_dep': $edgecolor = 'chocolate3'; $label = 'Maximum version\\nexceeded'; $toNode .= "\n" . $node['required_version']['min'] . " - " . $node['required_version']['max']; $toattributes['fillcolor'] = 'khaki'; break; case 'min_dep': $edgecolor = 'crimson'; $label = 'Minimum version\\nnot met'; $toNode .= "\n" . $node['required_version']['min']; if (!empty($node['required_version']['max'])) { $toNode .= " - " . $node['required_version']['max']; } $toattributes['fillcolor'] = 'pink'; break; case 'missing': $edgecolor = 'crimson'; $label = 'Not installed\\nor activated'; $toNode .= "\n" . $node['required_version']['min']; if (!empty($node['required_version']['max'])) { $toNode .= " - " . $node['required_version']['max']; } $toattributes['fillcolor'] = 'pink'; break; default: $edgecolor = ''; $label = ''; $toattributes['fillcolor'] = 'white'; break; } $fromattributes['URL'] = "http://www.bitweaver.org/wiki/" . ucfirst($node['package']) . "Package"; $graph->addNode($fromNode, $fromattributes); $toattributes['URL'] = "http://www.bitweaver.org/wiki/" . ucfirst($node['requires']) . "Package"; $graph->addNode($toNode, $toattributes); $graph->addEdge(array($fromNode => $toNode), $gBitThemes->getGraphvizEdgeAttributes(array('dir' => !empty($node['dir']) ? $node['dir'] : '', 'color' => $edgecolor, 'fontcolor' => $edgecolor, 'label' => $label))); } if (preg_match("#(png|gif|jpe?g|bmp|svg|tif)#i", $pFormat)) { $graph->image($pFormat, $pCommand); } else { return $graph->fetch($pFormat, $pCommand); } } else { return FALSE; } }
/** * This method returns a list of absolute paths containing the location of * images that inlustrate a specific workflow. Such images are generated * each workflow specification. * If any requested image is not present or if it's older than the * workflow's configuration file, all images for such workflow are * generated. * * @warning This method requires GraphViz. * * @param string $workflowName Workflow name to look for. * @return string[string] Returns a list of paths containing these keys: * WKFL_AFIELD_FILE, WKFL_AFIELD_THUMB (these are defined constants). */ public function graphPath($workflowName) { // // Defautl values. $out = false; // // Checking and including library. if (@(require_once 'Image/GraphViz.php')) { // // Checking required directories permissions. $this->checkGraphsDirectories(); // // Global dependencies. global $WKFLDefaults; // // Default values. $generateIt = false; // // Retrieving the workflow. $workflow = $this->getWorkflow($workflowName); // // Guessing paths. $graphPath = Sanitizer::DirPath("{$WKFLDefaults[WKFL_DEFAULTS_GRAPHS_PATH]}/{$workflowName}.png"); $graphThumbPath = Sanitizer::DirPath("{$WKFLDefaults[WKFL_DEFAULTS_GRAPHS_PATH]}/{$workflowName}-256px.png"); // // Checking paths existence. if (!$generateIt && (!is_file($graphPath) || !is_file($graphThumbPath))) { $generateIt = true; } // // Checking paths' last modification dates. if (!$generateIt) { $workflowTime = filemtime($workflow->path()); $generateIt = filemtime($graphPath) < $workflowTime || filemtime($graphThumbPath) < $workflowTime; } // // Do these images have to be generated? if ($generateIt) { // // Logging pre-generation information. $this->_log->log(LGGR_LOG_LEVEL_INFO, "Gereating graph for '{$workflowName}'."); $this->_log->log(LGGR_LOG_LEVEL_DEBUG, "Workflow '{$workflowName}' graph path: '{$graphPath}'"); $this->_log->log(LGGR_LOG_LEVEL_DEBUG, " thumb path: '{$graphThumbPath}'"); // // Creating an image. $graph = new \Image_GraphViz(); // // Addind begining node. $graph->addNode('BEGIN', ['shape' => 'circle', 'label' => '', 'color' => 'black']); // // Configuration shortcut. $workflowConfig = $workflow->config(); // // Creating a squared node for each step. foreach ($workflowConfig->steps as $stepName => $step) { $graph->addNode($stepName, array('label' => $stepName, 'shape' => 'box')); } // // Addind ending node. $graph->addNode('END', ['shape' => 'circle', 'label' => '', 'color' => 'black', 'style' => 'filled']); // // Linking the beginning node to the first step. $graph->addEdge(['BEGIN' => $workflowConfig->startsAt]); // // Linking all steps based on connections // confiugrations. foreach ($workflowConfig->steps as $stepName => $step) { // // Checking each connection for current // step. foreach ($step->connections as $connName => $conn) { // // Checking if this connection // either changes the flow status // or sets the next step. if (isset($conn->status)) { // // Checking what change is taking. switch ($conn->status) { case WKFL_ITEM_FLOW_STATUS_FAILED: case WKFL_ITEM_FLOW_STATUS_DONE: // // These statuses link to the end. $graph->addEdge([$stepName => 'END'], ['label' => $connName, 'fontcolor' => 'brown', 'color' => 'brown']); break; case WKFL_ITEM_FLOW_STATUS_WAIT: // // Basic attributes for each link. $attrs = ['label' => "{$connName}\n[wait]", 'color' => 'orange', 'fontcolor' => 'orange', 'style' => 'dashed']; // // Checking if this waiting has some extra // configuration. if (isset($conn->wait)) { // // This status creates a vitual node // representing the waiting step. $nodeName = "wait_{$stepName}"; $graph->addNode($nodeName, ['shape' => 'box', 'label' => "wait:{$stepName}", 'fontcolor' => 'orange', 'color' => 'orange']); // // Linking the current step to this // virtual node. $graph->addEdge([$stepName => $nodeName], $attrs); // // Linking the virtual node to the current // one for less attempts than a limit. $attrs['label'] = "{$connName}\n[wait<={$conn->wait->attempts}]"; $graph->addEdge([$nodeName => $stepName], $attrs); // // Linking the virtual node something else // for more attempts than a limit. $attrs['label'] = "{$connName}\n[wait>{$conn->wait->attempts}]"; if (isset($conn->wait->status)) { // // Linking to the end. $graph->addEdge([$nodeName => 'END'], $attrs); } elseif (isset($conn->wait->step)) { // // Linking to another step. $graph->addEdge([$nodeName => $conn->wait->step], $attrs); } } else { $nextStep = isset($conn->step) ? $conn->step : $stepName; $graph->addEdge([$stepName => $nextStep], $attrs); } break; } } elseif (isset($conn->step)) { // // Linking this step to // the next one. $graph->addEdge([$stepName => $conn->step], ['label' => $connName, 'fontcolor' => 'darkgreen', 'color' => 'darkgreen']); } } } // // Saving the generated graphic in two locations, // one for the actual image and other for 256 // pixels thumbnail. file_put_contents($graphPath, $graph->fetch('png')); file_put_contents($graphThumbPath, $graph->fetch('png')); // // Croping thumbnail. self::CropImage($graphThumbPath, 256); } // // Building the returning information. $out = array(WKFL_AFIELD_FILE => $graphPath, WKFL_AFIELD_THUMB => $graphThumbPath); } else { // // Logging the error of not having the proper library. $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Pear GraphViz plugin hasn't been installed."); } return $out; }