5 * Copyright 1999-2001 (c) VA Linux Systems
6 * Copyright 2010 Roland Mas
7 * Copyright 2010 Alain Peyrat, Alcatel-Lucent
10 require_once $gfcommon.'tracker/ArtifactFactory.class.php';
11 require_once $gfcommon.'tracker/ArtifactQuery.class.php';
13 // make sure this person has permission to view artifacts
15 if (!$ath->userCanView()) {
16 exit_permission_denied();
19 $query_id = getIntFromRequest('query_id');
20 $start = getIntFromRequest('start');
25 // The browse page can be powered by a pre-saved query
26 // or by select boxes chosen by the user
28 // If there is a $query_id coming from the request OR the pref
29 // was already saved, use the artifact factory that way.
31 // If the query_id = -1, unset the pref and use regular browse boxes
33 if (session_loggedin()) {
35 if ($query_id == '-1') {
36 $u =& session_get_user();
37 $u->setPreference('art_query'.$ath->getID(),'');
39 $aq = new ArtifactQuery($ath,$query_id);
40 if (!$aq || !is_object($aq)) {
41 exit_error('Error',$aq->getErrorMessage());
46 $u =& session_get_user();
47 $query_id=$u->getPreference('art_query'.$ath->getID(),'');
49 } elseif ($query_id) {
50 // If user is not logged, then use a cookie to store the current query.
51 if (isset($_COOKIE["GFTrackerQuery"])) {
52 $gf_tracker = unserialize($_COOKIE["GFTrackerQuery"]);
54 $gf_tracker = array();
56 $gf_tracker[$ath->getID()] = $query_id;
57 // Send the query_id as a cookie to save it.
58 setcookie("GFTrackerQuery", serialize($gf_tracker));
59 $_COOKIE["GFTrackerQuery"] = serialize($gf_tracker);
60 } elseif (isset($_COOKIE["GFTrackerQuery"])) {
61 $gf_tracker = unserialize($_COOKIE["GFTrackerQuery"]);
62 $query_id = (int)$gf_tracker[$ath->getID()];
65 $af = new ArtifactFactory($ath);
67 if (!$af || !is_object($af)) {
68 exit_error('Error','Could Not Get Factory');
69 } elseif ($af->isError()) {
70 exit_error('Error',$af->getErrorMessage());
73 if (!isset($_sort_col)) {
74 /* default sort order: highest priority first */
75 $_sort_col = 'priority';
78 $offset = getStringFromRequest('offset');
79 $_sort_col = getStringFromRequest('_sort_col',$_sort_col);
80 $_sort_ord = getStringFromRequest('_sort_ord',$_sort_ord);
81 $max_rows = getIntFromRequest('max_rows', 25);
82 $set = getStringFromRequest('set');
83 $_assigned_to = getIntFromRequest('_assigned_to');
84 $_status = getIntFromRequest('_status');
85 $_extra_fields = array() ;
86 $aux_extra_fields = array() ;
87 if ($set == 'custom') {
89 //may be past in next/prev url
91 if (isset($_GET['extra_fields'][$ath->getCustomStatusField()])) {
92 $_extra_fields[$ath->getCustomStatusField()] = $_GET['extra_fields'][$ath->getCustomStatusField()];
93 } elseif (isset($_POST['extra_fields'][$ath->getCustomStatusField()])) {
94 $_extra_fields[$ath->getCustomStatusField()] = $_POST['extra_fields'][$ath->getCustomStatusField()];
98 if (is_array($_extra_fields)){
99 $keys=array_keys($_extra_fields);
100 foreach ($keys as $key) {
101 if ($_extra_fields[$key] != 'Array') {
102 $aux_extra_fields[$key] = $_extra_fields[$key];
106 if (isset($_extra_fields)){
107 $aux_extra_fields = $_extra_fields;
109 $aux_extra_fields = '';
113 $af->setup($offset,$_sort_col,$_sort_ord,$pagelength,$set,$_assigned_to,$_status,$aux_extra_fields);
115 // These vals are sanitized and/or retrieved from ArtifactFactory stored settings
117 $_sort_col=$af->order_col;
118 $_sort_ord=$af->sort;
119 $_status=$af->status;
120 $_assigned_to=$af->assigned_to;
121 $_extra_fields=$af->extra_fields;
123 $art_arr =& $af->getArtifacts();
125 if (!$art_arr && $af->isError()) {
126 exit_error('Error',$af->getErrorMessage());
129 //build page title to make bookmarking easier
130 //if a user was selected, add the user_name to the title
132 $ath->header(array('atid'=>$ath->getID(), 'title'=>$ath->getName()));
136 * Build the powerful browsing options pop-up boxes
141 // creating a custom technician box which includes "any" and "unassigned"
143 $res_tech= $ath->getTechnicians();
145 $tech_id_arr=util_result_column_to_array($res_tech,0);
146 $tech_id_arr[]='0'; //this will be the 'any' row
148 $tech_name_arr=util_result_column_to_array($res_tech,1);
149 $tech_name_arr[]=_('Any');
151 if (is_array($_assigned_to)) {
154 $tech_box=html_build_select_box_from_arrays ($tech_id_arr,$tech_name_arr,'_assigned_to',$_assigned_to,true,_('Unassigned'));
158 // custom order by arrays to build a pop-up box
160 $order_name_arr=array();
161 $order_name_arr[]=_('ID');
162 $order_name_arr[]=_('Priority');
163 $order_name_arr[]=_('Summary');
164 $order_name_arr[]=_('Open Date');
165 $order_name_arr[]=_('Close Date');
166 $order_name_arr[]=_('Submitter');
167 $order_name_arr[]=_('Assignee');
171 $order_arr[]='artifact_id';
172 $order_arr[]='priority';
173 $order_arr[]='summary';
174 $order_arr[]='open_date';
175 $order_arr[]='close_date';
176 $order_arr[]='submitted_by';
177 $order_arr[]='assigned_to';
180 // custom sort arrays to build pop-up box
182 $sort_name_arr=array();
183 $sort_name_arr[]=_('Ascending');
184 $sort_name_arr[]=_('Descending');
191 // custom changed arrays to build pop-up box
193 $changed_name_arr=array();
194 $changed_name_arr[]=_('Any changes');
195 $changed_name_arr[]=_('Last 24H');
196 $changed_name_arr[]=_('Last 7days');
197 $changed_name_arr[]=_('Last 2weeks');
198 $changed_name_arr[]=_('Last 1month');
200 $changed_arr=array();
201 $changed_arr[]= 0x7fffffff; // Any
202 $changed_arr[]= 3600 * 24; // 24 hour
203 $changed_arr[]= 3600 * 24 * 7; // 1 week
204 $changed_arr[]= 3600 * 24 * 14;// 2 week
205 $changed_arr[]= 3600 * 24 * 30;// 1 month
209 * Show the free-form text submitted by the project admin
211 echo $ath->renderBrowseInstructions();
214 // statuses can be custom in GForge 4.5+
216 if ($ath->usesCustomStatuses()) {
217 $aux_extra_fields = array();
218 if (is_array($_extra_fields)){
219 $keys=array_keys($_extra_fields);
220 foreach ($keys as $key) {
221 if (!is_array($_extra_fields[$key])) {
222 $aux_extra_fields[$key] = $_extra_fields[$key];
226 $aux_extra_fields = $_extra_fields;
228 $status_box=$ath->renderSelect ($ath->getCustomStatusField(),$aux_extra_fields[$ath->getCustomStatusField()],false,'',true,_('Any'));
230 if (is_array($_status)) {
233 $status_box = $ath->statusBox('_status',$_status,true,_('Any'));
235 echo '<script type="text/javascript" src="/tabber/tabber.js"></script>'."\n";
238 $proj_name = $group->getUnixName();
239 $proj_url = util_make_url_g($group->getUnixName(),$group_id);
240 // the tracker's URIs are constructed in order to support addition of an OSLC-CM REST server
241 // inside /tracker/cm/. There each tracker has a URL in the form .../project/PROJ_NAME/atid/ATID
242 $tracker_stdzd_uri = util_make_url('/tracker/cm/project/'. $proj_name .'/atid/'. $ath->getID());
243 print '<div about="'. $tracker_stdzd_uri
244 .'" typeof="sioc:Container" xmlns:sioc="http://rdfs.org/sioc/ns#" xmlns:doap="http://usefulinc.com/ns/doap#">'."\n";
245 print '<span rel="http://www.w3.org/2002/07/owl#sameAs" resource="" />'."\n";
246 print '<span rev="doap:bug-database sioc:space_of" resource="'. $proj_url .'" />'."\n";
247 print "</div>\n"; // end of about
250 <div id="tabber" class="tabber">
251 <div class="tabbertab" title="'._('Advanced queries').'">';
253 if (session_loggedin()) {
254 $res = db_query_params ('SELECT artifact_query_id,query_name, CASE WHEN query_type>0 THEN 1 ELSE 0 END as type
256 WHERE group_artifact_id=$1 AND (user_id=$2 OR query_type>0)
257 ORDER BY type ASC, query_name ASC',
258 array ($ath->getID(),
261 $res = db_query_params ('SELECT artifact_query_id,query_name, CASE WHEN query_type>0 THEN 1 ELSE 0 END as type
263 WHERE group_artifact_id=$1 AND query_type>0
264 ORDER BY type ASC, query_name ASC',
265 array ($ath->getID()));
269 if (db_numrows($res)>0) {
270 echo '<form action="'. getStringFromServer('PHP_SELF') .'" method="get">';
271 echo ' <table width="100%" cellspacing="0">
275 echo '<input type="hidden" name="group_id" value="'.$group_id.'" />';
276 echo '<input type="hidden" name="atid" value="'.$ath->getID().'" />';
277 echo '<input type="hidden" name="power_query" value="1" />';
278 $optgroup['key'] = 'type';
279 $optgroup['values'][0] = 'Private queries';
280 $optgroup['values'][1] = 'Project queries';
281 echo '<span style="font-size:smaller">';
282 echo '<select name="query_id">';
283 echo '<option value="100">Select One</option>';
285 $selected = $af->getDefaultQuery();
286 while ($row = db_fetch_array($res)) {
287 if ($current != $row['type']) {
290 $label = $row['type'] ? 'Project' : 'Private';
291 echo '<optgroup label="'.$label.'">';
292 $current = $row['type'];
294 echo '<option value="'.$row['artifact_query_id'].'"';
295 if ($row['artifact_query_id'] == $selected)
296 echo ' selected="selected"';
297 echo '>'. $row['query_name'] .'</option>'."\n";
303 <input type="submit" name="run" value="'._('Power Query').'" />
304 <a href="/tracker/?atid='. $ath->getID().'&group_id='.$group_id.'&func=query">'.
305 _('Build Query').'</a>
310 <a href="/tracker/?atid='. $ath->getID().'&group_id='.$group_id.'&func=query">'._('Build Query').'</a></strong>';
314 <div class="tabbertab'.($af->query_type == 'custom' ? ' tabbertabdefault' : '').'" title="'._('Simple Filtering and Sorting').'">
315 <form action="'. getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&atid='.$ath->getID().'" method="post">
316 <table width="100%" cellspacing="0">
319 <input type="hidden" name="query_id" value="-1" />
320 <input type="hidden" name="set" value="custom" />
321 '._('Assignee').': '. $tech_box .'
324 '._('State').': '. $status_box .'
328 // Compute the list of fields which can be sorted.
329 // Currently, only text & integer are taken (for simplicity only).
330 $efarr = $ath->getExtraFields(ARTIFACT_EXTRAFIELDTYPE_TEXT.",".ARTIFACT_EXTRAFIELDTYPE_INTEGER);
331 $keys=array_keys($efarr);
332 for ($k=0; $k<count($keys); $k++) {
334 $order_name_arr[] = $efarr[$i]['field_name'];
335 $order_arr[] = $efarr[$i]['extra_field_id'];
339 ': <a href="javascript:help_window(\'/help/tracker.php?helpname=sort_by\')">' .
340 '<strong>(?)</strong></a>'.
341 html_build_select_box_from_arrays($order_arr,$order_name_arr,'_sort_col',$_sort_col,false) .
342 html_build_select_box_from_arrays($sort_arr,$sort_name_arr,'_sort_ord',$_sort_ord,false) .
343 '<input type="submit" name="submit" value="'._('Quick Browse').'" />';
351 if ($af->query_type == 'default') {
352 echo '<div class="tabbertab tabbertabdefault" title="'._('Default').'">';
353 echo '<strong>'._('Viewing only opened records by default, use \'Advanced queries\' or \'Simple Filtering and Sorting\' to change.').'</strong>';
359 if ($art_arr && count($art_arr) > 0) {
362 $aq = new ArtifactQuery($ath,$query_id);
363 $has_bargraph = (in_array('bargraph', $aq->getQueryOptions()));
365 $has_bargraph = false;
369 // Display the roadmap block based on the values of the Status field.
370 $colors = array('#a71d16', '#ffa0a0', '#f5f5b5', '#bae0ba', '#16a716');
373 foreach($art_arr as $art) {
374 if ($ath->usesCustomStatuses()) {
375 $custom_id = $ath->getCustomStatusField();
376 $extra_data = $art->getExtraFieldDataText();
377 $count[ $extra_data[$custom_id]['value'] ]++;
379 $count[ $art->getStatusName()]++;
382 foreach($count as $n => $c) {
383 $percent[$n] = round(100*$c/count($art_arr));
387 $efarr =& $ath->getExtraFields(ARTIFACT_EXTRAFIELDTYPE_STATUS);
388 $keys=array_keys($efarr);
389 $field_id = $keys[0];
390 $states = $ath->getExtraFieldElements($field_id);
393 if (is_array($states)) {
394 foreach($states as $state) {
395 $name = $state['element_name'];
397 $graph .= '<td style="background: '.$colors[$i].'; width: '.$percent[$name].'%;"> </td>';
398 $legend .= '<td style="white-space: nowrap; width: '.$percent[$name].'%;">'."<i>$name: $count[$name] ($percent[$name]%)</i></td>";
406 <table class="progress">
408 <tr><?php echo $graph; ?></tr>
411 <table class="progress_legend">
412 <tr><?php echo $legend ?></tr>
418 if ($set=='custom') {
419 $set .= '&_assigned_to='.$_assigned_to.'&_status='.$_status.'&_sort_col='.$_sort_col.'&_sort_ord='.$_sort_ord;
420 if (array_key_exists($ath->getCustomStatusField(),$_extra_fields)) {
421 $set .= '&extra_fields['.$ath->getCustomStatusField().']='.$_extra_fields[$ath->getCustomStatusField()];
426 $IS_ADMIN=$ath->userIsAdmin();
430 <form name="artifactList" action="'. getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&atid='.$ath->getID().'" method="post">
431 <input type="hidden" name="form_key" value="'.form_generate_key().'" />
432 <input type="hidden" name="func" value="massupdate" />';
435 $browse_fields = explode(',', "id,".$ath->getBrowseList());
437 foreach ($browse_fields as $f) {
439 if (intval($f) > 0) {
440 $title = $ath->getExtraFieldName($f);
447 $title=_('Description');
448 if ($f == 'open_date')
449 $title=_('Open Date');
450 if ($f == 'close_date')
451 $title=_('Close Date');
452 if ($f == 'status_id')
454 if ($f == 'priority')
455 $title=_('Priority');
456 if ($f == 'assigned_to')
457 $title=_('Assigned to');
458 if ($f == 'submitted_by')
459 $title=_('Submitted by');
460 if ($f == 'related_tasks')
461 $title=_('Related tasks');
463 $title_arr[] = $title;
466 echo $GLOBALS['HTML']->listTableTop ($title_arr);
468 $then=(time()-$ath->getDuePeriod());
470 $max = ((count($art_arr) > ($start + $pagelength)) ? ($start+$pagelength) : count($art_arr) );
472 for ($i=$start; $i<$max; $i++) {
473 $extra_data = $art_arr[$i]->getExtraFieldDataText();
475 <tr '. $HTML->boxGetAltRowStyle($i) . '>';
476 foreach ($browse_fields as $f) {
478 echo '<td style="white-space: nowrap;">'.
479 ($IS_ADMIN?'<input type="checkbox" name="artifact_id_list[]" value="'.
480 $art_arr[$i]->getID() .'" /> ':'').
481 '<a href="'.getStringFromServer('PHP_SELF').'?func=detail&aid='.
482 $art_arr[$i]->getID() .
483 '&group_id='. $group_id .'&atid='.
484 $ath->getID().'">'.$art_arr[$i]->getID() .
486 } else if ($f == 'summary') {
487 echo '<td><a href="'.getStringFromServer('PHP_SELF').'?func=detail&aid='.
488 $art_arr[$i]->getID() .
489 '&group_id='. $group_id .'&atid='.
491 $art_arr[$i]->getSummary().
493 } else if ($f == 'open_date') {
494 echo '<td>'. (($set != 'closed' && $art_arr[$i]->getOpenDate() < $then)?'* ':' ') .
495 date(_('Y-m-d H:i'),$art_arr[$i]->getOpenDate()) .'</td>';
496 } else if ($f == 'status_id') {
497 echo '<td>'. $art_arr[$i]->getStatusName() .'</td>';
498 } else if ($f == 'priority') {
499 echo '<td class="priority'.$art_arr[$i]->getPriority() .'">'. $art_arr[$i]->getPriority() .'</td>';
500 } else if ($f == 'assigned_to') {
501 echo '<td>'. $art_arr[$i]->getAssignedRealName() .'</td>';
502 } else if ($f == 'submitted_by') {
503 echo '<td>'. $art_arr[$i]->getSubmittedRealName() .'</td>';
504 } else if ($f == 'close_date') {
505 echo '<td>'. ($art_arr[$i]->getCloseDate() ?
506 date(_('Y-m-d H:i'),$art_arr[$i]->getCloseDate()) :' ') .'</td>';
507 } else if ($f == 'details') {
508 echo '<td>'. $art_arr[$i]->getDetails() .'</td>';
509 } else if ($f == 'related_tasks') {
511 $tasks_res = $art_arr[$i]->getRelatedTasks();
513 while ($rest = db_fetch_array($tasks_res)) {
514 $link = '/pm/task.php?func=detailtask&project_task_id='.$rest['project_task_id'].
515 '&group_id='.$group_id.'&group_project_id='.$rest['group_project_id'];
516 $title = '[T'.$rest['project_task_id'].']';
517 if ($rest['status_id'] == 2) {
518 $title = '<strike>'.$title.'</strike>';
520 print $s.'<a href="'.$link.'" title="'.$rest['summary'].'">'.$title.'</a>';
524 } else if (intval($f) > 0) {
525 // Now display extra-fields (fields are numbers).
526 $value = $extra_data[$f]['value'];
527 if ($extra_data[$f]['type'] == 9) {
528 $value = preg_replace('/\b(\d+)\b/e', "_artifactid2url('\\1')", $value);
529 } else if ($extra_data[$f]['type'] == 7) {
530 if ($art_arr[$i]->getStatusID() == 2) {
531 $value = '<strike>'.$value.'</strike>';
535 echo '<td>' . $value .'</td>';
537 // Display ? for unknown values.
545 Show extra rows for <-- Prev / Next -->
547 //only show this if we're not using a power query
548 if ($af->max_rows > 0) {
549 if (($offset > 0) || ($rows >= 50)) {
551 <tr><td colspan="2">';
553 echo '<a href="'.getStringFromServer('PHP_SELF').'?func=browse&group_id='.$group_id.'&atid='.$ath->getID().'&set='.
554 $set.'&offset='.($offset-50).'"><strong><-- '._('Previous 50').'</strong></a>';
558 echo '</td><td> </td><td colspan="2">';
560 echo '<a href="'.getStringFromServer('PHP_SELF').'?func=browse&group_id='.$group_id.'&atid='.$ath->getID().'&set='.
561 $set.'&offset='.($offset+50).'"><strong>'._('Next 50').' --></strong></a>';
569 echo $GLOBALS['HTML']->listTableBottom();
570 $pages = count($art_arr) / $pagelength;
571 $currentpage = intval($start / $pagelength);
572 //echo "Item Count: ".count($arr)."Pages: $pages";
574 $skipped_pages=false;
575 for ($j=0; $j<$pages; $j++) {
577 if ((($j > 4) && ($j < ($currentpage-5))) || (($j > ($currentpage+5)) && ($j < ($pages-5)))) {
578 if (!$skipped_pages) {
584 $skipped_pages=false;
587 if ($j == $currentpage) {
588 echo '<strong>'.($j+1).'</strong> ';
590 echo '<a href="'.getStringFromServer('PHP_SELF')."?func=browse&group_id=".$group_id.'&atid='.$ath->getID().'&set='. $set.'&start='.($j*$pagelength).'"><strong>'.($j+1).'</strong></a> ';
599 echo '<script type="text/javascript">
601 function checkAll(val) {
602 al=document.artifactList;
603 len = al.elements.length;
605 for( i=0 ; i<len ; i++) {
606 if (al.elements[i].name==\'artifact_id_list[]\') {
607 al.elements[i].checked=val;
614 <table width="100%" border="0" id="admin_mass_update">
617 <a href="javascript:checkAll(1)">'._('Check all').'</a>
619 <a href="javascript:checkAll(0)">'._('Clear all').'</a>
622 <span class="important">'._('<strong>Admin:</strong> If you wish to apply changes to all items selected above, use these controls to change their properties and click once on "Mass Update".').'</span></p>
627 // build custom fields
629 $ef =& $ath->getExtraFields(ARTIFACT_EXTRAFIELD_FILTER_INT);
630 $keys=array_keys($ef);
633 for ($i=0; $i<count($keys); $i++) {
634 if (($ef[$keys[$i]]['field_type']==ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) || ($ef[$keys[$i]]['field_type']==ARTIFACT_EXTRAFIELDTYPE_MULTISELECT)) {
635 $sel[$keys[$i]]=array('100');
637 $sel[$keys[$i]]='100';
640 $ath->renderExtraFields($sel,true,_('No Change'),false,'',ARTIFACT_EXTRAFIELD_FILTER_INT,true);
642 <td><strong>'._('Priority').': <a href="javascript:help_window(\'/help/tracker.php?helpname=priority\')"><strong>(?)</strong></a>
644 echo build_priority_select_box ('priority', '100', true);
651 <td><strong>'._('Assigned to').': <a href="javascript:help_window(\'/help/tracker.php?helpname=assignee\')"><strong>(?)</strong></a>
652 </strong><br />'. $ath->technicianBox ('assigned_to','100.1',true,_('Nobody'),'100.1',_('No Change')) .'</td>
654 if (!$ath->usesCustomStatuses()) {
655 echo '<strong>'._('State').': <a href="javascript:help_window(\'/help/tracker.php?helpname=status\')"><strong>(?)</strong></a></strong>
656 <br />'. $ath->statusBox ('status_id','xzxz',true,_('No Change'));
661 <tr><td colspan="2"><strong>'._('Canned Response').':
662 <a href="javascript:help_window(\'/help/tracker.php?helpname=canned_response\')"><strong>(?)</strong></a>
663 </strong><br />'. $ath->cannedResponseBox ('canned_response') .'</td></tr>
665 <tr><td colspan="3" align="center"><input type="submit" name="submit" value="'._('Mass update').'" /></td></tr>
670 printf(_('* Denotes requests > %1$s Days Old'), ($ath->getDuePeriod()/86400));
672 if (in_array('priority', $browse_fields)) {
673 show_priority_colors_key();
676 echo '<div class="warning_msg">'._('No items found').'</div>';
680 $ath->footer(array());
684 // c-file-style: "bsd"