5 * Copyright 2011, Alcatel-Lucent
7 * This file is part of FusionForge. FusionForge is free software;
8 * you can redistribute it and/or modify it under the terms of the
9 * GNU General Public License as published by the Free Software
10 * Foundation; either version 2 of the Licence, or (at your option)
13 * FusionForge is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * Standard Alcatel-Lucent disclaimer for contributing to open source
26 * "The Roadmap ("Contribution") has not been tested and/or
27 * validated for release as or in products, combinations with products or
28 * other commercial use. Any use of the Contribution is entirely made at
29 * the user's own responsibility and the user can not rely on any features,
30 * functionalities or performances Alcatel-Lucent has attributed to the
33 * THE CONTRIBUTION BY ALCATEL-LUCENT IS PROVIDED AS IS, WITHOUT WARRANTY
34 * OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
35 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, COMPLIANCE,
36 * NON-INTERFERENCE AND/OR INTERWORKING WITH THE SOFTWARE TO WHICH THE
37 * CONTRIBUTION HAS BEEN MADE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
38 * ALCATEL-LUCENT BE LIABLE FOR ANY DAMAGES OR OTHER LIABLITY, WHETHER IN
39 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
40 * CONTRIBUTION OR THE USE OR OTHER DEALINGS IN THE CONTRIBUTION, WHETHER
41 * TOGETHER WITH THE SOFTWARE TO WHICH THE CONTRIBUTION RELATES OR ON A STAND
45 require_once('../env.inc.php');
46 require_once $gfcommon.'include/pre.php';
47 require_once $gfcommon.'tracker/include/ArtifactTypeFactoryHtml.class.php';
48 require_once $gfcommon.'tracker/ArtifactFactory.class.php';
49 require_once $gfcommon.'tracker/Roadmap.class.php';
50 require_once $gfcommon.'tracker/RoadmapFactory.class.php';
52 // Templates ------------
53 $templates[0]['display_graph'] = 1;
54 $templates[0]['begin_tracker'] = '<table>'."\n";
55 $templates[0]['end_tracker'] = '</table>'."\n";
56 $templates[0]['separator'] = '<br/>'."\n";
57 $templates[0]['begin_release'] = '<fieldset><legend>%s<a href="%s">%s</a>%s</legend>'."\n";
58 $templates[0]['end_release'] = '</fieldset>'."\n";
59 $templates[0]['begin_ticket'] = '<tr>'."\n";
60 $templates[0]['ticket_icon'] = '<td class="align-center">%s</td>';
61 $templates[0]['ticket_id'] = '<td style="white-space: nowrap;"><a href="%s">[#%s]</a></td>';
62 $templates[0]['ticket_summary'] = '<td><span title="%s">%s</span></td>'."\n";
63 $templates[0]['end_ticket'] = '</tr>'."\n";
65 $templates[1]['display_graph'] = 0;
66 $templates[1]['begin_tracker'] = '<ul>'."\n";
67 $templates[1]['end_tracker'] = '</ul>'."\n";
68 $templates[1]['separator'] = '<br/>'."\n";
69 $templates[1]['begin_release'] = '<h2>%s<a href="%s">%s</a></h2>'."\n";
70 $templates[1]['end_release'] = "\n";
71 $templates[1]['begin_ticket'] = '<li>'."\n";
72 $templates[1]['ticket_icon'] = '';
73 $templates[1]['ticket_id'] = '<a href="%s">[#%s]</a> ';
74 $templates[1]['ticket_summary'] = '%2$s'."\n";
75 $templates[1]['end_ticket'] = '</li>'."\n";
77 //----------------------
79 function local_exit($msg='') {
89 $atfh->header(array('title' => _('Roadmap'), 'modal' => 1));
96 function nrange($col1, $col2, $nb) {
98 $range = array_fill(0, $nb, $col1);
112 for ($i = 2; $i <= $nb; $i++) {
113 $range[$i] = $range[1] + round(($range[0] - $range[1]) * sqrt(($i - 1) / $nb));
115 $range[] = array_shift($range);
118 $range = array_reverse($range);
125 function color_gradient($colors, $nb_colors) {
126 if (! is_array($colors)) {
129 elseif (count($colors) < 2 || $nb_colors <= 2) {
133 $colors[0] = preg_replace('/^#/', '', $colors[0]);
134 $colors[1] = preg_replace('/^#/', '', $colors[1]);
136 $colors_start = array_map('hexdec', str_split($colors[0], 2));
137 $colors_end = array_map('hexdec', str_split($colors[1], 2));
138 $format_func = create_function('$val', 'return sprintf("%02s", $val);');
140 $colors_comp = array();
141 for($i = 0; $i < 3; $i++) {
142 $colors_comp[] = array_map($format_func,
143 array_map('dechex', nrange($colors_start[$i], $colors_end[$i], $nb_colors)));
145 for($i = 0; $i < $nb_colors; $i++) {
146 $grad[] = $colors_comp[0][$i].$colors_comp[1][$i].$colors_comp[2][$i];
154 if (!forge_get_config('use_tracker')) {
155 $atfh->header(array('title' => _('Roadmap'), 'modal' => 1));
156 exit_disabled('home');
159 $group_id = getIntFromRequest('group_id');
161 $group = group_get_object($group_id);
162 if (!$group || !is_object($group)) {
165 if ($group->isError()) {
166 if($group->isPermissionDeniedError()) {
167 exit_permission_denied($group->getErrorMessage(),'tracker');
169 exit_error($group->getErrorMessage(), 'tracker');
173 $atfh = new ArtifactTypeFactoryHtml($group);
174 if (!$atfh || !is_object($atfh) || $atfh->isError()) {
175 exit_error(_('Error: could Not Get ArtifactTypeFactoryHtml'), 'tracker');
178 $template = getIntFromRequest('template', 0);
179 $ajax = getIntFromRequest('ajax', 0);
181 $roadmap_name = getStringFromRequest('roadmap_name');
182 $roadmap_id = getIntFromRequest('roadmap_id', 0);
183 $selected_release = getStringFromRequest('release');
184 $nb_release = getIntFromRequest('nb_release', 2);
185 $display_graph = getIntFromRequest('display_graph', 2);
187 $roadmap_factory = new RoadmapFactory($group);
188 $roadmaps = $roadmap_factory->getRoadmaps(true);
189 if (empty($roadmaps)) {
190 $information = _('No roadmap available');
191 local_exit('<p>'._('The roadmap provides a short view on the trackers by viewing all tickets related to a release.').'</p>'.
192 '<p>'.sprintf(_('If you have project administrator rights, you can easily <a href="%s">create roadmaps</a>.'),
193 '/tracker/admin/index.php?group_id='.$group_id.'&admin_roadmap=1&new_roadmap=submit').'</p>');
196 $selected_roadmap = null;
199 $selected_roadmap = $roadmap_factory->getRoadmapByName($roadmap_name, true);
200 if (! is_object($selected_roadmap)) {
201 $error_msg .= sprintf(_('Error: roadmap %s is not available'), $roadmap_name);
205 $roadmap_id = $selected_roadmap->getID();
208 elseif($roadmap_id) {
209 $selected_roadmap = $roadmap_factory->getRoadmapByID($roadmap_id, true);
210 if (! is_object($selected_roadmap)) {
211 $error_msg .= sprintf(_('Error: roadmap %s is not available'), 'ID='.$roadmap_id);
215 $roadmap_name = $selected_roadmap->getName();
219 $roadmap_id = $roadmap_factory->getDefault();
221 $selected_roadmap = new Roadmap($group, $roadmap_id);
222 if (! is_object($selected_roadmap)) {
223 $error_msg .= sprintf(_('Error: roadmap %s is not available'), 'ID='.$roadmap_id);
228 $selected_roadmap = $roadmaps[0];
229 if (! is_object($selected_roadmap)) {
230 local_exit(_('No roadmap available'));
232 $roadmap_id = $selected_roadmap->getID();
234 $roadmap_name = $selected_roadmap->getName();
236 $release_order = array_reverse($selected_roadmap->getReleases());
237 if ($selected_release && ! in_array($selected_release, $release_order)) {
238 $error_msg .= sprintf(_('Error: release %s is not available'), $selected_release);
245 $rel_states_colors = array();
246 $rel_states = array();
247 $total_rel_states = array();
248 $rel_art_states_colors = array();
249 $rel_art_states = array();
250 $total_rel_art_states = array();
253 $at_arr = $atfh->getArtifactTypes();
254 $artifact_type_list = $selected_roadmap->getList();
256 if (!$at_arr || count($at_arr) < 1) {
257 local_exit('<p class="information">'._('No trackers have been set up.').'</p>');
260 foreach ($at_arr as $artifact_type) {
261 if (!is_object($artifact_type)) {
263 } elseif ($artifact_type->isError()) {
264 echo $artifact_type->getErrorMessage();
267 $artifact_type_id = $artifact_type->getID();
269 if (! array_key_exists($artifact_type_id, $artifact_type_list) || ! $artifact_type_list[$artifact_type_id]) {
270 // This tracker is not used for the roadmap
273 $field_id = $artifact_type_list[$artifact_type_id];
275 $ath = new ArtifactTypeHtml($group, $artifact_type_id);
277 if (!forge_check_perm ('tracker', $artifact_type_id, 'read')) {
281 $artifact_type_name = $artifact_type->getName();
282 $uses_custom_status = $artifact_type->usesCustomStatuses();
284 // Get all states for this artifact_type; probably useless to do it
285 if ($display_graph && $templates[$template]['display_graph']) {
286 if ($uses_custom_status) {
287 $rel_art_states_colors[$artifact_type_name] = array();
288 $arr_status = $artifact_type->getExtraFieldElements($artifact_type->getCustomStatusField());
289 foreach ($arr_status as $status) {
290 @$red_green_count[$status['status_id']]++;
291 $rel_art_states_colors[$artifact_type_name][$status['element_name']] = 0;
294 $res = $artifact_type->getStatuses();
295 while ($row = db_fetch_array($res)) {
296 $rel_states_colors[$row['id']] = 0;
297 if (! $uses_custom_status) {
298 @$red_green_count[$status['id']]++;
299 $rel_art_states_colors[$artifact_type_name][$row['status_name']] = 0;
303 // Set color radiant for custom states graph
304 $nb_colors = count(array_keys($rel_art_states_colors[$artifact_type_name]));
305 if (count($red_green_count) && array_key_exists('1', $red_green_count) && array_key_exists('2', $red_green_count) &&
306 ($red_green_count['1'] + $red_green_count['2']) == $nb_colors) {
307 $colors_gradient = array_merge(color_gradient(array('ff0000', 'ffc1bf'), $red_green_count['1']),
308 color_gradient(array('c1ffbf', '00ff00'), $red_green_count['2']));
311 $colors = array('ff0000', '00ff00');
312 $colors_gradient = color_gradient($colors, $nb_colors);
315 foreach ($rel_art_states_colors[$artifact_type_name] as $state => $color) {
316 $rel_art_states_colors[$artifact_type_name][$state] = '#'.$colors_gradient[$i];
320 // Set color radiant for open/closed states graph
321 $nb_colors = count(array_keys($rel_states_colors));
322 $colors = array('dd0000', '00dd00');
323 $colors_gradient = color_gradient($colors, $nb_colors);
325 foreach ($rel_states_colors as $state => $color) {
326 $rel_states_colors[$state] = '#'.$colors_gradient[$i];
331 $af = new ArtifactFactory($ath);
332 if (!$af || !is_object($af)) {
333 exit_error(_('Could Not Get Factory'), 'tracker');
334 } elseif ($af->isError()) {
335 exit_error($af->getErrorMessage(), 'tracker');
338 if ($selected_release) {
339 $release_filter = array($selected_release);
341 elseif ($nb_release) {
342 $release_filter = array_slice($release_order, 0, $nb_release);
345 $release_filter = false;
347 $art_arr = $af->getArtifactsByReleases($field_id, $release_filter);
349 if (!$art_arr && $af->isError()) {
350 exit_error($af->getErrorMessage(), 'tracker');
353 if (! count($art_arr)) {
358 foreach($art_arr as $artifact) {
359 $extra_data = $artifact->getExtraFieldDataText();
360 $release_value = $extra_data[$field_id]['value'];
362 if ($selected_release && $release_value != $selected_release) {
366 $custom_status_name = $artifact->getCustomStatusName();
367 $artifact_id = $artifact->getID();
369 @$rmap[$release_value][$artifact_type_name] .= $templates[$template]['begin_ticket'];
372 if ($artifact->getStatusID() == 1) {
373 $icon = html_image('ic/ticket-open.png','','',array('alt' => $custom_status_name, 'title' => $custom_status_name));
376 $icon = html_image('ic/ticket-closed.png','','',array('alt' => $custom_status_name, 'title' => $custom_status_name));
378 $rmap[$release_value][$artifact_type_name] .= sprintf($templates[$template]['ticket_icon'], $icon);
381 $rmap[$release_value][$artifact_type_name] .= sprintf($templates[$template]['ticket_id'],
382 dirname(getStringFromServer('PHP_SELF')).'/?func=detail&aid='.$artifact_id .
383 '&group_id='. $group_id .'&atid='.$ath->getID(),
387 $rmap[$release_value][$artifact_type_name] .= sprintf($templates[$template]['ticket_summary'],
388 htmlentities($artifact->getDetails(), ENT_COMPAT, 'UTF-8'),
389 $artifact->getSummary());
391 $rmap[$release_value][$artifact_type_name] .= $templates[$template]['end_ticket'];
394 @$rel_states[$release_value][$artifact->getStatusID()]['count']++;
395 $rel_states[$release_value][$artifact->getStatusID()]['name'] = $artifact->getStatusName();
396 @$total_rel_states[$release_value]++;
397 @$rel_art_states[$release_value][$artifact_type_name][$custom_status_name]['count']++;
398 $rel_art_states[$release_value][$artifact_type_name][$custom_status_name]['name'] = $custom_status_name;
399 @$total_rel_art_states[$release_value][$artifact_type_name]++;
405 $atfh->header(array('title' => _('Roadmap'), 'modal' => 1));
407 // Start selection tools box
408 echo '<div id="div_options">';
409 echo '<fieldset><legend>'._('Display options').'</legend>';
410 echo '<form action="'.getStringFromServer('PHP_SELF').'?group_id='.$group_id.'" method="post">'."\n";
411 echo '<table class="fullwidth"><tr>'."\n";
413 if (! empty($roadmaps) && ! $selected_release) {
414 echo '<td class="align-center">' . _("Roadmap: ") . '<select name="roadmap" id="roadmap">'."\n";
415 foreach ($roadmaps as $roadmap) {
416 echo '<option value="'.$roadmap->getID().'"'.($roadmap->getID() == $roadmap_id ? ' selected="selected"' : '').' >'.$roadmap->getName().'</option>'."\n";
418 echo '</select></td>'."\n";
421 if (! $selected_release) {
422 echo '<td class="align-center">'._('Number of release(s) to display: ');
423 echo '<select name="nb_release" id="nb_release" >';
424 foreach (array(1, 2, 4, 8) as $nb) {
425 echo '<option value="'.$nb.'"'.($nb_release == $nb ? ' selected="selected"' : '').' >'.$nb.'</option>';
427 echo '<option value="0"'.(! $nb_release ? ' selected="selected"' : '').' >'._('All').'</option>';
428 echo '</select></td>';
431 echo sprintf('<td><a href="%s" >'._('Return to last release(s)').'</a></td>'."\n",
432 getStringFromServer('PHP_SELF').'?group_id='.$group_id.($roadmap_id ? '&roadmap_id='.$roadmap_id : ''));
435 echo '<td class="align-center">' . _("Display graphs: ") . '<select name="display_graph" id="display_graph">'."\n";
436 if (! $selected_release) {
437 echo '<option value="2"'.($display_graph == 2 ? ' selected="selected"' : '').' >'._('Only last').'</option>'."\n";
439 echo '<option value="1"'.($display_graph == 1 ? ' selected="selected"' : '').' >'._('All').'</option>'."\n";
440 echo '<option value="0"'.($display_graph == 0 ? ' selected="selected"' : '').' >'._('None').'</option>'."\n";
441 echo '</select></td>'."\n";
443 // End selection tools box
444 echo '<td id="noscript" class="align-center"><input type="submit" name="submit" value="'._('Submit').'" /></td>';
445 echo '</tr></table></form></fieldset></div>'."\n";
449 echo '<div id="div_roadmap">'."\n";
451 if (empty($release_order)) {
452 echo '<p><strong>'._('No release available').'</strong></p>';
455 for ($i = 0; $i < count($release_order); $i++) {
456 $release_value = $release_order[$i];
458 if ($selected_release && $release_value != $selected_release) {
462 if (! $selected_release && $nb_release && $i >= $nb_release) {
466 echo '<div id="release_'.$i.'" >'."\n";
467 echo sprintf($templates[$template]['begin_release'],
468 $roadmap_name._(': '),
469 getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&roadmap_id='.$roadmap_id.'&release='.urlencode($release_value),
471 ' '.html_image("ic/file-txt.png",'','',array("title"=>_('Display as text'), "onclick"=>"getReleaseTxt('".addslashes($release_value)."')")));
474 if ($display_graph && $templates[$template]['display_graph']) {
477 if (array_key_exists($release_value, $rel_states) && is_array($rel_states[$release_value])) {
478 foreach ($rel_states_colors as $state => $color) {
479 if (array_key_exists($state, $rel_states[$release_value])) {
480 $data = $rel_states[$release_value][$state];
481 $percent = round($data['count'] / $total_rel_states[$release_value] * 100);
482 $graph .= '<td style="background: '.$color.'; width: '.$percent.'%;"> </td>';
483 $legend .= '<td style="white-space: nowrap; width: '.$percent.'%;"><i>'.$data['name'].': '.$data['count'].' ('.$percent.'%)</i></td>';
489 <div class="graph<?php echo $graph_class ?>" <?php echo ($graph_class && $display_graph == 2 ? 'style="display: none"' : '') ?>>
490 <table class="fullwidth">
492 <td class="align-center">
493 <table class="progress halfwidth">
495 <tr><?php echo $graph; ?></tr>
498 <table class="halfwidth">
499 <tr class="align-center"><?php echo $legend ?></tr>
511 if (! array_key_exists($release_value, $rmap)) {
512 echo '<p>'._('No data for this release').'</p>'."\n";
515 foreach ($rmap[$release_value] as $artifact_type_name => $ticket_list) {
516 echo '<h3>'.$artifact_type_name.'</h3>'."\n";
519 if ($display_graph && $templates[$template]['display_graph']) {
522 if (array_key_exists($release_value, $rel_art_states) && is_array($rel_art_states[$release_value]) &&
523 array_key_exists($artifact_type_name, $rel_art_states[$release_value]) && is_array($rel_art_states[$release_value][$artifact_type_name])) {
525 foreach ($rel_art_states_colors[$artifact_type_name] as $state => $color) {
526 if (array_key_exists($state, $rel_art_states[$release_value][$artifact_type_name])) {
527 $data = $rel_art_states[$release_value][$artifact_type_name][$state];
528 $percent = round($data['count'] / $total_rel_art_states[$release_value][$artifact_type_name] * 100);
529 $total += $data['count'];
530 $graph .= '<td style="background: '.$color.'; width: '.$percent.'%;"> </td>';
531 $legend .= '<td style="white-space: nowrap; width: '.$percent.'%;"><i>'.$data['name'].': '.$data['count'].' ('.$percent.'%)</i></td>';
534 // Display unknown statuses if exists
535 if ($total != $total_rel_art_states[$release_value][$artifact_type_name]) {
536 $percent = round((1 - $total / $total_rel_art_states[$release_value][$artifact_type_name]) * 100);
537 $graph .= '<td style="background: #aaaaaa; width: '.$percent.'%;"> </td>';
538 $legend .= '<td style="white-space: nowrap; width: '.$percent.'%;"><i>'._('Unknown').': '.($total_rel_art_states[$release_value][$artifact_type_name] - $total).' ('.$percent.'%)</i></td>';
544 <div class="graph<?php echo $graph_class ?>" <?php echo ($graph_class && $display_graph == 2 ? 'style="display: none"' : '') ?>>
545 <table class="fullwidth">
547 <td class="align-center">
548 <table class="progress halfwidth">
550 <tr><?php echo $graph; ?></tr>
553 <table class="halfwidth">
554 <tr class="align-center"><?php echo $legend ?></tr>
564 echo $templates[$template]['begin_tracker'];
566 echo $templates[$template]['end_tracker'];
567 echo $templates[$template]['separator'];
571 echo $templates[$template]['end_release'];
572 echo '</div><br/>'."\n";
580 <script type="text/javascript">/* <![CDATA[ */
582 if (! $selected_release) {
584 jQuery('#roadmap').change(function() {
588 jQuery('#nb_release').change(function() {
595 jQuery('#display_graph').change(function() {
596 var select_val = jQuery('#display_graph').val();
597 var divs = document.getElementsByTagName('div');
598 for (var i = 0; i < divs.length; i++) {
599 if (divs[i].className == 'graph0') {
600 if (select_val == 1 || select_val == 2) {
601 divs[i].style.display = 'inline';
604 divs[i].style.display = 'none';
607 if (divs[i].className == 'graph1') {
608 if (select_val == 1) {
609 divs[i].style.display = 'inline';
612 divs[i].style.display = 'none';
618 function updatePage() {
619 jQuery('#div_roadmap').empty();
620 jQuery('#div_roadmap').append('<div class="align-center"><img src="<?php echo $gfwww ?>/images/ajax-loader.gif" /></div>');
625 group_id: <?php echo $group_id ?>,
626 roadmap_id: jQuery('#roadmap').val(),
627 nb_release: jQuery('#nb_release').val(),
628 display_graph: jQuery('#display_graph').val(),
631 success: function(rep) {
632 jQuery('#div_roadmap').empty();
633 jQuery('#div_roadmap').append(rep);
638 function getReleaseTxt(release) {
639 var selected_roadmap = $('#roadmap').val();
640 var selected_nb_release = $('#nb_release').val();
641 var selected_display_graph = $('#display_graph').val();
645 data: '<?php echo 'group_id='.$group_id.'&roadmap_id='.$roadmap_id ?>&ajax=1&template=1&release='+release,
646 success: function(rep) {
647 jQuery('#div_options').empty();
648 jQuery('#div_options').append('<a href="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id ?>&roadmap_id='+selected_roadmap+'&nb_release='+selected_nb_release+'&display_graph='+selected_display_graph+'" ><?php echo _('Return to last release(s)') ?></a>');
649 jQuery('#div_roadmap').empty();
650 jQuery('#div_roadmap').append(rep);
655 function hideFormButton() {
656 var element = document.getElementById('noscript');
657 if (element) element.style.display = 'none';