00001 <?php
00002 # This file is part of the Savane project
00003 # <http://gna.org/projects/savane/>
00004 #
00005 # $Id: general.php 5460 2006-02-20 15:08:43Z yeupou $
00006 #
00007 # Copyright 1999-2000 (c) The SourceForge Crew
00008 # Copyright 2001-2002 (c) Laurent Julliard, CodeX Team, Xerox
00009 #
00010 # Copyright 2003-2006 (c) Mathieu Roy <yeupou--gnu.org>
00011 # Yves Perrin <yves.perrin--cern.ch>
00012 #
00013 #
00014 # The Savane project is free software; you can redistribute it and/or
00015 # modify it under the terms of the GNU General Public License
00016 # as published by the Free Software Foundation; either version 2
00017 # of the License, or (at your option) any later version.
00018 #
00019 # The Savane project is distributed in the hope that it will be useful,
00020 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00021 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00022 # GNU General Public License for more details.
00023 #
00024 # You should have received a copy of the GNU General Public License
00025 # along with the Savane project; if not, write to the Free Software
00026 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
00027
00028
00029 # Return the file that should be included, according to the URL
00030 # requested. If the file start with ?, it's an index.
00031 function trackers_include()
00032 {
00033 # Keep the dirname only if it's admin
00034 $dir = get_module_include_dir($GLOBALS['PHP_SELF'], 0, 1);
00035 if ($dir != "admin")
00036 {
00037 unset($dir);
00038 }
00039 else
00040 {
00041 $dir = $dir."/";
00042 $pre = "../";
00043 }
00044
00045 return $pre."../include/trackers_run/".$dir.basename($GLOBALS['PHP_SELF']);
00046 }
00047
00048 # Does like trackers_include() but load an arbitrary page of the common
00049 # tracker code. This is useful for trackers that have non-standard behavior
00050 # needs to present some standard page inside a non standard location
00051 function trackers_bastardinclude($page, $is_admin_page='0')
00052 {
00053 unset($pre);
00054 if ($is_admin_page)
00055 {
00056 $pre = "../";
00057 }
00058
00059 return $pre."../include/trackers_run/".$page.".php";
00060 }
00061
00062
00063 # Generate URL arguments from a variable wether scalar or array
00064 function trackers_convert_to_url_arg($varname, $var)
00065 {
00066
00067 if (is_array($var))
00068 {
00069 reset($var);
00070 while (list(,$v) = each($var))
00071 {
00072 $ret .= '&'.$varname.'[]='.$v;
00073 }
00074 }
00075 else
00076 {
00077 $ret .= '&'.$varname.'='.$var;
00078 }
00079 return $ret;
00080 }
00081
00082 function trackers_header($params)
00083 {
00084 global $group_id,$is_bug_page,$DOCUMENT_ROOT,$advsrch;
00085
00086 #used so the search box will add the necessary element to the pop-up box
00087 # yeupou, 2005-09-11: is that still useful?
00088 $is_bug_page=1;
00089
00090 #required params for site_project_header();
00091 $params['group']=$group_id;
00092 $params['context']=ARTIFACT;
00093
00094 $project=project_get_object($group_id);
00095
00096 #needs to be turned on
00097 if (ARTIFACT == "bugs" && !$project->Uses("bugs") ||
00098 ARTIFACT == "support" && !$project->Uses("support") ||
00099 ARTIFACT == "task" && !$project->Uses("task") ||
00100 ARTIFACT == "patch" && !$project->Uses("patch"))
00101 {
00102 exit_error(_("This project has turned off this tracker."));
00103 }
00104 print site_project_header($params);
00105
00106 }
00107
00108 function trackers_header_admin($params)
00109 {
00110 global $group_id,$is_bug_page,$DOCUMENT_ROOT;
00111
00112 #used so the search box will add the necessary element to the pop-up box
00113 $is_bug_page=1;
00114
00115 #required params for site_project_header();
00116 $params['group']=$group_id;
00117 $params['context']='a'.ARTIFACT;
00118
00119 $project=project_get_object($group_id);
00120
00121 # need to be turned on
00122 if (ARTIFACT == "bugs" && !$project->Uses("bugs") ||
00123 ARTIFACT == "support" && !$project->Uses("support") ||
00124 ARTIFACT == "task" && !$project->Uses("task") ||
00125 ARTIFACT == "patch" && !$project->Uses("patch"))
00126 {
00127 exit_error(_("This project has turned off this tracker."));
00128 }
00129 print site_project_header($params);
00130 }
00131
00132 function trackers_footer($params)
00133 {
00134 site_project_footer($params);
00135 }
00136
00137 function trackers_init($group_id)
00138 {
00139 # Set the global arrays for faster processing at init time
00140 trackers_data_get_all_fields($group_id, true);
00141 }
00142
00143 function trackers_report_init($group_id, $report_id)
00144 {
00145 # Set the global array with report information for faster processing
00146 trackers_data_get_all_report_fields($group_id, $report_id, true);
00147 }
00148
00149 function trackers_list_all_fields($sort_func=false,$by_field_id=false)
00150 {
00151 global $BF_USAGE_BY_ID, $BF_USAGE_BY_NAME, $AT_START;
00152
00153 # If its the first element we fetch then apply the sort
00154 # function
00155 if ($AT_START)
00156 {
00157 if (!$sort_func)
00158 { $sort_func = cmp_place; }
00159 uasort($BF_USAGE_BY_ID, $sort_func);
00160 uasort($BF_USAGE_BY_NAME, $sort_func);
00161 $AT_START=false;
00162 }
00163
00164 # return the next bug field in the list. If the global
00165 # bug field usage array is not set then set it the
00166 # first time.
00167 # by_field_id: true return the list of field id, false returns the
00168 # list of field names
00169
00170 if ( list($key, $field_array) = each($BF_USAGE_BY_ID))
00171 {
00172 return($by_field_id ? $field_array['bug_field_id'] : $field_array['field_name']);
00173 }
00174 else
00175 {
00176 # rewind internal pointer for next time
00177 reset($BF_USAGE_BY_ID);
00178 reset($BF_USAGE_BY_NAME);
00179 $AT_START=true;
00180 return(false);
00181 }
00182 }
00183
00184 function trackers_field_label_display ($field_name, $group_id,$break=false,$ascii=false, $tab=25)
00185 {
00186 $label = trackers_data_get_label($field_name).':';
00187
00188 if (!$ascii)
00189 { $output = '<span class="preinput"><span class="help" title="'.trackers_data_get_description($field_name).'">'.$label.'</span></span>'; }
00190
00191 if ($break)
00192 { $output .= ($ascii?"\n":'<br />'); }
00193 else
00194 {
00195 if (!$ascii)
00196 { $output .= ' '; }
00197 else
00198 {
00199 $output .= sprintf("%".$tab."s", $label).' ';
00200 }
00201 }
00202
00203 return $output;
00204 }
00205
00206 function trackers_field_display ($field_name,
00207 $group_id,
00208 $value='xyxy',
00209 $break=false, #4
00210 $label=true,
00211 $ro=false, #6
00212 $ascii=false,
00213 $show_none=false, #8
00214 $text_none='None',
00215 $show_any=false, #10
00216 $text_any='Any',
00217 $allowed_transition_only=false, #12
00218 $show_unknown=false,
00219 $tab=25)
00220 {
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242 global $sys_datefmt;
00243
00244 if ($label)
00245 {
00246 $output = trackers_field_label_display($field_name,
00247 $group_id,
00248 $break,
00249 $ascii,
00250 $tab);
00251 }
00252
00253 # display depends upon display type of this field
00254 switch (trackers_data_get_display_type($field_name))
00255 {
00256
00257 case 'SB':
00258 if ($ro)
00259 {
00260
00261 # if multiple selected values return a list of <br /> separated values
00262 $arr = ( is_array($value) ? $value : array($value));
00263 for ($i=0;$i < count($arr); $i++)
00264 {
00265 if ($arr[$i] == 0 )
00266 { $arr[$i] = $text_any; }
00267 else if ($arr[$i] == 100 && $field_name != 'percent_complete')
00268 { $arr[$i] = $text_none; }
00269 else
00270 { $arr[$i] = trackers_data_get_value($field_name,$group_id,$arr[$i]); }
00271 }
00272 $output .= join('<br />', $arr);
00273
00274 }
00275 else
00276 {
00277 # If it is a user name field (assigned_to, submitted_by) then make
00278 # sure to add the "None" entry in the menu 'coz it's not in the DB
00279 if (trackers_data_is_username_field($field_name))
00280 {
00281 $show_none=true;
00282 $text_none='None';
00283 }
00284
00285 if (is_array($value))
00286 {
00287 $output .= trackers_multiple_field_box($field_name,'',$group_id, $value,
00288 $show_none,$text_none,$show_any,
00289 $text_any);
00290 }
00291 else
00292 {
00293 $output .= trackers_field_box($field_name,
00294 '',
00295 $group_id,
00296 $value, #4
00297 $show_none,
00298 $text_none,
00299 $show_any,
00300 $text_any, #8
00301 $allowed_transition_only,
00302 $show_unknown);
00303 }
00304 }
00305 break;
00306
00307 case 'DF':
00308 if ($ascii)
00309 {
00310 $output .= ( ($value == 0) ? '' : format_date($sys_datefmt,$value));
00311 }
00312 else
00313 {
00314 if ($ro)
00315 { $output .= format_date($sys_datefmt,$value); }
00316 else
00317 {
00318 $output .= trackers_field_date($field_name,
00319 (($value == 0) ? '' : strftime("%Y-%m-%d",$value)));
00320 }
00321 }
00322 break;
00323
00324 case 'TF':
00325 if ($ascii)
00326 { $output .= utils_unconvert_htmlspecialchars($value); }
00327 else
00328 { $output .= ($ro ? $value: trackers_field_text($field_name,$value)); }
00329 break;
00330
00331 case 'TA':
00332 if ($ascii)
00333 { $output .= utils_unconvert_htmlspecialchars($value); }
00334 else
00335 { $output .= ($ro ? nl2br($value):trackers_field_textarea($field_name,$value)); }
00336 break;
00337
00338 default:
00339 $output .= 'Unknown '.ARTIFACT.' Field Display Type';
00340 }
00341
00342 return($output);
00343 }
00344
00345 function trackers_field_date($field_name,$value='',$size=0,$maxlength=0,$ro=false)
00346 {
00347
00348 # value is formatted as Y-m-d
00349 list($year, $month, $day) = split("-", $value);
00350
00351 if ($ro)
00352 {
00353 $html = $value;
00354 }
00355 else
00356 {
00357 if (!$size || !$maxlength)
00358 { list($size, $maxlength) = trackers_data_get_display_size($field_name);
00359 }
00360
00361 # date part are missing, take the date of the day
00362 $today = localtime();
00363 if (!$day)
00364 { $day = ($today[3]); }
00365 if (!$month)
00366 { $month = ($today[4]+1); }
00367 if (!$year)
00368 { $year = ($today[5]+1900); }
00369
00370 # FIXME: order of year/day/month must be local specific
00371 $html = calendar_selectbox("day", $day, $field_name.'_dayfd').calendar_selectbox("month", $month, $field_name.'_monthfd').' <input type="text" name="'.$field_name.'_yearfd" size="4" maxlength="4" value="'.$year.'" />';
00372 }
00373 return($html);
00374
00375 }
00376
00377 function trackers_multiple_field_date($field_name,$date_begin='',$date_end='',$size=0,$maxlength=0,$ro=false)
00378 {
00379
00380 # CAUTION!!!! The Javascript below assumes that the date always appear
00381 # in a field called 'bug_form'
00382
00383 # FIXME: this is broken, should be made as trackers_field_date
00384
00385 if ($ro)
00386 if ($date_begin || $date_end)
00387 $html = "Start: $date_begin<br />End: $date_end";
00388 else
00389 $html = 'Any time';
00390 else
00391 {
00392 if (!$size || !$maxlength)
00393 list($size, $maxlength) = trackers_data_get_display_size($field_name);
00394
00395 $html = 'Start:<br /><input type="text" name="'.$field_name.
00396 '" size="'.$size.'" MAXLENGTH="'.$maxlength.'" VALUE="'.$date_begin.'">'.
00397 '<a href="javascript:show_calendar(\'document.bug_form.'.$field_name.'\', document.bug_form.'.$field_name.'.value);">'.
00398 '<img src="'.$GLOBALS['sys_home'].'images/'.SV_THEME.'.theme/task.png" border="0" alt="'._("Click Here to Pick up a date").'" /></a>'.
00399 '</td></tr><tr><td>'.
00400 'End:<br /><INPUT TYPE="text" name="'.$field_name.'_end'.
00401 '" size="'.$size.'" MAXLENGTH="'.$maxlength.'" VALUE="'.$date_end.'">'.
00402 '<a href="javascript:show_calendar(\'document.bug_form.'.$field_name.'_end\', document.bug_form.'.$field_name.'_end.value);">'.
00403 '<img src="'.$GLOBALS['sys_home'].'images/'.SV_THEME.'.theme/task.png" border="0" alt="'._("Click Here to Pick up a date").'" /></a>';
00404
00405 $html = '<table><tr><td>'.$html.'</td></tr></table>';
00406 }
00407
00408 return($html);
00409
00410 }
00411
00412 function trackers_field_date_operator($field_name,$value='',$ro=false)
00413 {
00414
00415 if ($ro)
00416 $html = htmlspecialchars($value);
00417 else
00418 $html = '<SELECT name="'.$field_name.'_op">'.
00419 '<OPTION VALUE=">"'.(($value == '>') ? ' SELECTED':'').'>></OPTION>'.
00420 '<OPTION VALUE="="'.(($value == '=') ? ' SELECTED':'').'>=</OPTION>'.
00421 '<OPTION VALUE="<"'.(($value == '<') ? ' SELECTED':'').'><</OPTION>'.
00422 '</SELECT>';
00423 return($html);
00424
00425 }
00426
00427 function trackers_field_text($field_name,$value='',$size=0,$maxlength=0)
00428 {
00429
00430 if (!$size || !$maxlength)
00431 list($size, $maxlength) = trackers_data_get_display_size($field_name);
00432
00433 $html = '<input type="text" name="'.$field_name.
00434 '" size="'.$size.'" maxlength="'.$maxlength.'" value="'.$value.'" />';
00435 return($html);
00436
00437 }
00438
00439 function trackers_field_textarea($field_name,$value='',$cols=0,$rows=0)
00440 {
00441
00442 if (!$cols || !$rows)
00443 {
00444 list($cols, $rows) = trackers_data_get_display_size($field_name);
00445
00446 # Nothing defined for this field? Use hardcoded default values
00447 if (!$cols || !$rows)
00448 {
00449 $cols = "65";
00450 $rows = "16";
00451 }
00452 }
00453
00454 $html = '<textarea name="'.$field_name.
00455 '" rows="'.$rows.'" cols="'.$cols.'" wrap="soft">'.$value.'</textarea>';
00456 return($html);
00457
00458 }
00459
00460 function trackers_field_box ($field_name,
00461 $box_name='',
00462 $group_id,
00463 $checked=false, #4
00464 $show_none=false,
00465 $text_none='None',
00466 $show_any=false,
00467 $text_any='Any', #8
00468 $allowed_transition_only=false,
00469 $show_unknown=false)
00470 {
00471
00472 # Returns a select box populated with field values for this project
00473 # if box_name is given then impose this name in the select box
00474 # of the HTML form otherwise use the field_name
00475 if (!$group_id)
00476 { return _('Error: no group defined'); }
00477 else
00478 {
00479 $result = trackers_data_get_field_predefined_values($field_name,$group_id,$checked);
00480
00481 if ($box_name == '')
00482 { $box_name = $field_name; }
00483
00484 if ($allowed_transition_only)
00485 {
00486 # yeupou--gnu.org 2004-09-12: where the hell is by_field_id set?
00487 $field_id = ($by_field_id ? $field_name : trackers_data_get_field_id($field_name));
00488
00489 # first check if group has defined transitions for this field
00490 $default_auth = db_result(db_query("SELECT transition_default_auth ".
00491 "FROM ".ARTIFACT."_field_usage ".
00492 "WHERE group_id='$group_id' AND bug_field_id='$field_id'"), 0, 'transition_default_auth');
00493 # avoid corrupted database content, if its not F, it must be A.
00494 if ($default_auth != "F")
00495 { $default_auth = "A"; }
00496
00497 $sql="SELECT from_value_id,to_value_id,is_allowed,notification_list ".
00498 "FROM trackers_field_transition ".
00499 "WHERE group_id='$group_id' AND artifact='".ARTIFACT."' AND field_id='$field_id' AND (from_value_id='$checked' OR from_value_id='0')";
00500
00501 $trans_result = db_query($sql);
00502 $forbidden_to_id = array();
00503 $allowed_to_id = array();
00504 $rows = db_numrows($trans_result);
00505 if ($trans_result && $rows > 0 || $default_auth == "F")
00506 {
00507 while ($transition = db_fetch_array($trans_result))
00508 {
00509 if ($transition['is_allowed'] == 'F')
00510 {
00511 $forbidden_to_id[$transition['to_value_id']] = 0;
00512 }
00513 else
00514 {
00515 $allowed_to_id[$transition['to_value_id']] = 0;
00516 }
00517 }
00518
00519 # get all the predefined values for this field
00520 $rows=db_numrows($result);
00521
00522 if ($rows > 0) {
00523 $val_label = array();
00524 while ($val_row = db_fetch_array($result))
00525 {
00526 $value_id = $val_row['value_id'];
00527 $value = $val_row['value'];
00528 if ((($default_auth == 'A') && (!array_key_exists($value_id, $forbidden_to_id))) ||
00529 (($default_auth == 'F') && (array_key_exists($value_id, $allowed_to_id))) ||
00530 ($value_id == $checked))
00531 {
00532 $val_label[$value_id] = $value;
00533 }
00534 }
00535
00536 # always add the any values cases
00537 return html_build_select_box_from_arrays(array_keys($val_label),
00538 array_values($val_label),
00539 $box_name,
00540 $checked, #4
00541 $show_none,
00542 $text_none, #6
00543 $show_any,
00544 $text_any, #8
00545 $show_unknown);
00546 }
00547 }
00548 }
00549
00550 # if no transition defined use 'normal' code
00551
00552 return html_build_select_box ($result,$box_name,$checked,$show_none,$text_none,$show_any, $text_any,$show_unknown);
00553
00554 }
00555 }
00556
00557 function trackers_multiple_field_box($field_name,
00558 $box_name='',
00559 $group_id,
00560 $checked=false,
00561 $show_none=false,
00562 $text_none='None',
00563 $show_any=false,
00564 $text_any='Any',
00565 $show_value=false)
00566 {
00567 # Returns a multiplt select box populated with field values for this project
00568 # if box_name is given then impose this name in the select box
00569 # of the HTML form otherwise use the field_name
00570
00571 if (!$group_id)
00572 { return _("Internal error: no group id"); }
00573 else
00574 {
00575 $result = trackers_data_get_field_predefined_values($field_name,$group_id,$checked);
00576
00577 if ($box_name == '')
00578 {
00579 $box_name = $field_name.'[]';
00580 }
00581 return html_build_multiple_select_box($result,$box_name,$checked,6,$show_none,$text_none, $show_any,$text_any,$show_value);
00582 }
00583 }
00584
00585 # Similar to trackers_multiple_field_box except that it will use checkboxes
00586 # instead of a multiple select field. Multiple select field is nice for
00587 # expert users, but it is not simple user-friendly, unlike checkboxes.
00588 function trackers_multiple_field_box2 ($field_name,
00589 $box_name='',
00590 $group_id,
00591 $checked=false,
00592 $show_none=false,
00593 $text_none='None',
00594 $show_any=false,
00595 $text_any='Any',
00596 $show_value=false)
00597 {
00598 if (!$group_id)
00599 { return _("Internal error: no group id"); }
00600 else
00601 {
00602 $result = trackers_data_get_field_predefined_values($field_name,$group_id,$checked);
00603
00604 if ($box_name == '')
00605 {
00606 $box_name = $field_name.'[]';
00607 }
00608 return html_build_checkbox($result,$box_name,$checked,6,$show_none,$text_none, $show_any,$text_any,$show_value);
00609 }
00610
00611
00612 }
00613
00614 function trackers_extract_field_list($post_method=true)
00615 {
00616
00617 global $HTTP_GET_VARS, $HTTP_POST_VARS, $BF_USAGE_BY_NAME;
00618
00619
00620
00621
00622
00623 # Specific: it must build the date fields if it finds _dayfd, _monthfd
00624 # or _yearfd, because date fields comes from 3 separated input.
00625 $vfl = array();
00626 $date = array();
00627 if ($post_method)
00628 {
00629 reset($HTTP_POST_VARS);
00630 while ( list($key, $val) = each($HTTP_POST_VARS))
00631 {
00632 if (preg_match("/^(.*)_(day|month|year)fd$/", $key, $found))
00633 {
00634 # Must build the date field key.
00635 $field_name = $found[1];
00636 $field_name_part = $found[2];
00637
00638 # We also must increment $day and $month, because the select
00639 # start from zero
00640
00641 # get what we already have
00642 list($year, $month, $day) = split("-", $vfl[$field_name]);
00643 if ($field_name_part == 'day')
00644 { $vfl[$field_name] = "$year-$month-$val"; }
00645 elseif ($field_name_part == 'month')
00646 { $vfl[$field_name] = "$year-$val-$day"; }
00647 elseif ($field_name_part == 'year')
00648 { $vfl[$field_name] = "$val-$month-$day"; }
00649 }
00650 elseif (isset($BF_USAGE_BY_NAME[$key]) || $key == 'comment')
00651 {
00652 $vfl[$key] = $val;
00653 }
00654 else
00655 {
00656 dbg("Rejected key = ".$key." val = $val");
00657 }
00658 }
00659 }
00660 else
00661 {
00662 reset($HTTP_GET_VARS);
00663 while ( list($key, $val) = each($HTTP_GET_VARS))
00664 {
00665 if (preg_match("/^(.*)_(day|month|year)fd$/", $key, $found))
00666 {
00667 # Must build the date field key.
00668 $field_name = $found[1];
00669 $field_name_part = $found[2];
00670
00671 # get what we already have
00672 list($year, $month, $day) = split("-", $vfl[$field_name]);
00673 if ($field_name_part == 'day')
00674 { $vfl[$field_name] = "$year-$month-$val"; }
00675 elseif ($field_name_part == 'month')
00676 { $vfl[$field_name] = "$year-$val-$day"; }
00677 elseif ($field_name_part == 'year')
00678 { $vfl[$field_name] = "$val-$month-$day"; }
00679 }
00680 elseif (isset($BF_USAGE_BY_NAME[$key]) || $key == 'comment')
00681 {
00682 $vfl[$key] = $val;
00683 }
00684 else
00685 {
00686 dbg("Rejected key = ".$key." val = $val");
00687 }
00688 }
00689
00690 }
00691 return($vfl);
00692 }
00693
00694 # Check whether a field was shown to the submitter
00695 # (useful if a field is mandatory if shown to the submitter)
00696 function trackers_check_is_shown_to_submitter ($field_name, $group_id, $submitter_id)
00697 {
00698 if ($submitter_id == 100)
00699 {
00700 # Anonymous user
00701 if (trackers_data_is_showed_on_add_nologin($field_name))
00702 { return true; }
00703 }
00704 else
00705 {
00706 if (!member_check($submitter_id, $group_id))
00707 {
00708 # Not a member of the group
00709 if (trackers_data_is_showed_on_add($field_name))
00710 { return true; }
00711 }
00712 else
00713 {
00714 # Group member
00715 if (trackers_data_is_showed_on_add_members($field_name))
00716 { return true; }
00717 }
00718 }
00719
00720 # if we reach this point, it was not mandatory
00721 return false;
00722 }
00723
00724
00725 function trackers_check_empty_fields($field_array, $new_item=true)
00726 {
00727 # Check whether empty values are allowed for the bug fields
00728 # field_array: associative array of field_name -> value
00729 unset($previous_form_bad_fields);
00730 global $previous_form_bad_fields;
00731 $previous_form_bad_fields = array();
00732
00733
00734 reset($field_array);
00735 while (list($field_name, $val) = each($field_array))
00736 {
00737 # Only the field percent_complete is allowed to use the special value
00738 # hundred.
00739 # FIXME: maybe it should not use that value at all, however it would
00740 # require one more database migration. Something that should indeed be
00741 # done if at some point we feel the need for one more exception.
00742 if ($field_name == "percent_complete")
00743 { continue; }
00744
00745 # Check if it is empty
00746 $is_empty = (trackers_data_is_select_box($field_name) ? ($val==100) : ($val==''));
00747 if (!$is_empty)
00748 { continue; }
00749
00750 # Check if it is mandatory
00751 $mandatory_flag = trackers_data_mandatory_flag($field_name);
00752 unset($is_mandatory);
00753 if ($mandatory_flag == 1)
00754 {
00755 # Not mandatory
00756 continue;
00757 }
00758 elseif ($mandatory_flag == 3)
00759 {
00760 # Mandatory whenever possible
00761 $is_mandatory = 1;
00762 }
00763 elseif ($new_item)
00764 {
00765 # Mandatory when shown to the submitter while we are creating
00766 # a new item.
00767 # ($mandatory_flag = 0)
00768 $is_mandatory = 1;
00769 }
00770 else
00771 {
00772 # Mandatory when shown to the submitter, we are updating an item
00773 # ($mandatory_flag = 0)
00774
00775 global $item_id, $group_id, $mandatorycheck_submitter_id;
00776 if (!$mandatorycheck_submitter_id)
00777 {
00778 # Save that information for further mandatory checks,
00779 # to avoid avoid a SQL request per field checked
00780 $submitter_sql = "SELECT submitted_by FROM ".ARTIFACT." WHERE bug_id='$item_id' AND group_id='$group_id'";
00781 $submitter_res = db_query($submitter_sql);
00782 $mandatorycheck_submitter_id = db_result($submitter_res,0,'submitted_by');
00783 }
00784
00785 if (trackers_check_is_shown_to_submitter($field_name, $group_id, $mandatorycheck_submitter_id))
00786 {
00787 $is_mandatory = 1;
00788 }
00789 }
00790
00791 if ($is_mandatory)
00792 {
00793 $value = trackers_data_get_label($field_name);
00794 $previous_form_bad_fields[$field_name] = $value;
00795 }
00796 }
00797
00798 if (count($previous_form_bad_fields) > 0)
00799 {
00800 # If not_new_item is true, it mean that there was no previous value to
00801 # reset the entry.
00802 if ($new_item)
00803 {
00804 fb(sprintf(ngettext("The field '%s' is mandatory. Fill it and re-submit the form.","The fields '%s' are mandatory. Fill them and re-submit the form.", count($previous_form_bad_fields)), join(', ',$previous_form_bad_fields)), 1);
00805 }
00806 else
00807 {
00808 fb(sprintf(ngettext("The field '%s' is mandatory. It has been reset to its previous value. Check it and re-submit the form.","The fields '%s' are mandatory. They have been reset to their previous value. Check them and re-submit the form.", count($previous_form_bad_fields)), join(', ',$previous_form_bad_fields)), 1);
00809 }
00810
00811 return false;
00812 }
00813 else
00814 {
00815 return true;
00816 }
00817
00818 }
00819
00820 function trackers_canned_response_box ($group_id,$name='canned_response')
00821 {
00822 if (!$group_id)
00823 {
00824 fb(_("Error, no group_id"),1);
00825 return 0;
00826 }
00827 else
00828 {
00829 $vals = array();
00830 $texts = array();
00831 $result = trackers_data_get_canned_responses($group_id);
00832 if (db_numrows($result) > 0)
00833 {
00834 if (db_numrows($result) > 1)
00835 {
00836 $vals[] = '!multiple!';
00837 $texts[] = "> "._("Multiple Canned Responses");
00838 }
00839
00840 while ($entry = db_fetch_array($result))
00841 {
00842 $vals[] = $entry['bug_canned_id'];
00843 $texts[] = $entry['title'];
00844
00845 }
00846
00847 return html_build_select_box_from_arrays($vals, $texts ,$name);
00848 }
00849 else
00850 {
00851 return form_input("hidden", "canned_response", "100")._("No canned response available");
00852 }
00853 }
00854 }
00855
00856 function trackers_build_notification_matrix($user_id)
00857 {
00858 # Build the notif matrix indexed with roles and events labels (not id)
00859 $res_notif = trackers_data_get_notification_with_labels($user_id);
00860 while ($arr = db_fetch_array($res_notif))
00861 {
00862 $arr_notif[$arr['role_label']][$arr['event_label']] = $arr['notify'];
00863 }
00864 return $arr_notif;
00865 }
00866
00867
00868 function trackers_check_notification($user_id, $role, $changes=false)
00869 {
00870
00871 $send = false;
00872 $arr_notif = trackers_build_notification_matrix($user_id);
00873 if (!$arr_notif)
00874 { return true; }
00875
00876 #echo "==== DBG Checking Notif. for $user_id (role=$role)<br />";
00877 $user_name = user_getname($user_id);
00878
00879 #----------------------------------------------------------
00880 # If it's a new bug only (changes is false) check the NEW_ITEM event and
00881 # ignore all other events
00882 if ($changes==false)
00883 {
00884 if ($arr_notif[$role]['NEW_ITEM'])
00885 {
00886 #echo "DBG NEW_ITEM notified<br />";
00887 return true;
00888 }
00889 else
00890 {
00891 #echo "DBG No notification<br />";
00892 return false;
00893 }
00894 }
00895
00896 #----------------------------------------------------------
00897 #Check: I_MADE_IT (I am the author of the change )
00898 # Check this one first because if the user said no she doesn't want to be
00899 # aware of any of her change in this role and we can return immediately.
00900 if (($user_id == user_getid()) && !$arr_notif[$role]['I_MADE_IT'])
00901 {
00902 #echo "DBG Dont want to receive my own changes<br />";
00903 return false;
00904 }
00905
00906 #----------------------------------------------------------
00907 # Check : NEW_COMMENT A new followup comment is added
00908 if ($arr_notif[$role]['NEW_COMMENT'] && isset($changes['details']))
00909 {
00910 #echo "DBG NEW_COMMENT notified<br />";
00911 return true;
00912 }
00913
00914 #----------------------------------------------------------
00915 #Check: NEW_FILE (A new file attachment is added)
00916 if ($arr_notif[$role]['NEW_FILE'] && isset($changes['attach']))
00917 {
00918 #echo "DBG NEW_FILE notified<br />";
00919 return true;
00920 }
00921
00922 #----------------------------------------------------------
00923 #Check: CLOSED (The bug is closed)
00924 # Rk: this one has precedence over PSS_CHANGE. So notify even if PSS_CHANGE
00925 # says no.
00926 if ($arr_notif[$role]['CLOSED'] && ($changes['status_id']['add'] == 'Closed'))
00927 {
00928 #echo "DBG CLOSED bug notified<br />";
00929 return true;
00930 }
00931
00932 #----------------------------------------------------------
00933 #Check: PSS_CHANGE (Priority,Status,Severity changes)
00934 if ($arr_notif[$role]['PSS_CHANGE'] &&
00935 (isset($changes['priority']) || isset($changes['status_id']) || isset($changes['severity'])) )
00936 {
00937 #echo "DBG PSS_CHANGE notified<br />";
00938 return true;
00939 }
00940
00941
00942 #----------------------------------------------------------
00943 # Check : ROLE_CHANGE (I'm added to or removed from this role)
00944 # Rk: This event is meanningless for Commenters. It also is for submitter but may be
00945 # one day the submitter will be changeable by the project admin so test it.
00946 # Rk #2: check this one at the end because it is the most CPU intensive and this
00947 # event seldomly happens
00948 if ($arr_notif['SUBMITTER']['ROLE_CHANGE'] &&
00949 (($changes['submitted_by']['add'] == $user_name) || ($changes['submitted_by']['del'] == $user_name)) &&
00950 ($role == 'SUBMITTER') )
00951 {
00952 #echo "DBG ROLE_CHANGE for submitter notified<br />";
00953 return true;
00954 }
00955
00956 if ($arr_notif['ASSIGNEE']['ROLE_CHANGE'] &&
00957 (($changes['assigned_to']['add'] == $user_name) || ($changes['assigned_to']['del'] == $user_name)) &&
00958 ($role == 'ASSIGNEE') )
00959 {
00960 #echo "DBG ROLE_CHANGE for role assignee notified<br />";
00961 return true;
00962 }
00963
00964 $arr_cc_changes = array();
00965 if (isset($changes['CC']['add']))
00966 $arr_cc_changes = split(',',$changes['CC']['add']);
00967 $arr_cc_changes[] = $changes['CC']['del'];
00968 $is_user_in_cc_changes = in_array($user_name,$arr_cc_changes);
00969 $are_anyother_user_in_cc_changes =
00970 (!$is_user_in_cc_changes || count($arr_cc_changes)>1);
00971
00972 if ($arr_notif['CC']['ROLE_CHANGE'] && ($role == 'CC'))
00973 {
00974 if ($is_user_in_cc_changes)
00975 {
00976 #echo "DBG ROLE_CHANGE for cc notified<br />";
00977 return true;
00978 }
00979 }
00980
00981 #----------------------------------------------------------
00982 #Check: CC_CHANGE (CC_CHANGE is added or removed)
00983 # check this right after because role cahange for cc can contradict
00984 # thee cc_change notification. If the role change on cc says no notification
00985 # then it has precedence over a cc_change
00986 if ($arr_notif[$role]['CC_CHANGE'] && isset($changes['CC']))
00987 {
00988 # its enough to test role against 'CC' because if we are at that point
00989 # it means that the role_change for CC was false or that role is not CC
00990 # So if role is 'CC' and we are here it means that the user asked to not be
00991 # notified on role_change as CC, unless other users are listed in the cc changes
00992 if (($role != 'CC') || (($role == 'CC') && $are_anyother_user_in_cc_changes))
00993 {
00994 #echo "DBG CC_CHANGE notified<br />";
00995 return true;
00996 }
00997 }
00998
00999
01000 #----------------------------------------------------------
01001 #Check: CHANGE_OTHER (Any changes not mentioned above)
01002 # *** THIS ONE MUST ALWAYS BE TESTED LAST
01003
01004 # Delete all tested fields from the $changes array. If any remains then it
01005 # means a notification must be sent
01006 unset($changes['details']);
01007 unset($changes['attach']);
01008 unset($changes['priority']);
01009 unset($changes['severity']);
01010 unset($changes['status_id']);
01011 unset($changes['CC']);
01012
01013 # assignee and submitter related changes can't be unset globally.
01014 # What needs to be unset is only what has been handled in this particular
01015 # call of the function
01016
01017 if (($changes['assigned_to']['add'] == $user_name) && ($role == 'ASSIGNEE')) { unset($changes['assigned_to']['add']);
01018 }
01019 if (($changes['assigned_to']['del'] == $user_name) && ($role == 'ASSIGNEE')) { unset($changes['assigned_to']['del']);
01020 }
01021 if (count($changes['assigned_to']) == 0) { unset($changes['assigned_to']);}
01022
01023 if (($changes['submitted_by']['add'] == $user_name) && ($role == 'SUBMITTER')) {
01024 unset($changes['submitted_by']['add']);
01025 }
01026 if (($changes['submitted_by']['del'] == $user_name) && ($role == 'SUBMITTER')) {
01027 unset($changes['submitted_by']['del']);
01028 }
01029 if (count($changes['submitted_by']) == 0) { unset($changes['submitted_by']);}
01030
01031 if ($arr_notif[$role]['ANY_OTHER_CHANGE'] && count($changes))
01032 {
01033 #echo "DBG ANY_OTHER_CHANGE notified<br />";
01034 return true;
01035 }
01036
01037 # Sorry, no notification...
01038 #echo "DBG No notification!!<br />";
01039 return false;
01040 }
01041
01042 function trackers_build_notification_list($item_id, $group_id, $changes)
01043 {
01044
01045 $sql="SELECT assigned_to, submitted_by from ".ARTIFACT." WHERE bug_id='$item_id'";
01046 $res_as=db_query($sql);
01047
01048 # Rk: we store email addresses in a hash to make sure they are only
01049 # stored once. Normally if an email is repeated several times sendmail
01050 # would take care of it but I prefer taking care of it now.
01051 # Same for user ids.
01052 # We also use the user_ids hash to check if a user has already been selected for
01053 # notification. If so it is not necessary to check it again in another role.
01054 $addresses = array();
01055 $user_ids = array();
01056
01057 # check submitter notification preferences
01058 $user_id = db_result($res_as,0,'submitted_by');
01059 if ($user_id != 100)
01060 {
01061 if (trackers_check_notification($user_id, 'SUBMITTER', $changes))
01062 {
01063 $user_ids[$user_id] = true;
01064 }
01065 }
01066
01067 # check assignee notification preferences
01068 $user_id = db_result($res_as,0,'assigned_to');
01069 if ($user_id != 100)
01070 {
01071 if (!$user_ids[$user_id] && trackers_check_notification($user_id, 'ASSIGNEE', $changes))
01072 {
01073 $user_ids[$user_id] = true;
01074 }
01075 }
01076
01077 # check old assignee notification preferences if assignee was just changed
01078 $user_name = $changes['assigned_to']['del'];
01079 if ($user_name)
01080 {
01081 $res_oa = user_get_result_set_from_unix($user_name);
01082 $user_id = db_result($res_oa,0,'user_id');
01083 if ($user_id != 100 && !$user_ids[$user_id] && trackers_check_notification($user_id, 'ASSIGNEE', $changes))
01084 {
01085 $user_ids[$user_id] = true;
01086 }
01087 }
01088
01089 # check all CC
01090 # a) check all the people in the current CC list
01091 # b) check the CC that has just been removed if any and see if she
01092 # wants to be notified as well
01093 # if the CC indentifier is an email address then notify in any case
01094 # because this user has no personal setting
01095 $res_cc = trackers_data_get_cc_list($item_id);
01096 $arr_cc = array();
01097 if ($res_cc && (db_numrows($res_cc) > 0))
01098 {
01099 while ($row = db_fetch_array($res_cc))
01100 {
01101 $arr_cc[] = $row['email'];
01102 }
01103 }
01104 # Only one CC can be deleted at once so just append it to the list....
01105 $arr_cc[] = $changes['CC']['del'];
01106
01107 while (list(,$cc) = each($arr_cc))
01108 {
01109 if (validate_email($cc))
01110 {
01111 $addresses[utils_normalize_email($cc)] = true;
01112 }
01113 else
01114 {
01115 $res = user_get_result_set_from_unix($cc);
01116 $user_id = db_result($res,0,'user_id');
01117 if (!$user_ids[$user_id] && trackers_check_notification($user_id, 'CC', $changes))
01118 {
01119 $user_ids[$user_id] = true;
01120 }
01121 }
01122 } # while
01123
01124
01125 # check all commenters
01126 $res_com = trackers_data_get_commenters($item_id);
01127 if (db_numrows($res_com) > 0)
01128 {
01129 while ($row = db_fetch_array($res_com))
01130 {
01131 $user_id = $row['mod_by'];
01132 if ($user_id != 100)
01133 {
01134 if (!$user_ids[$user_id] && trackers_check_notification($user_id, 'COMMENTER', $changes))
01135 {
01136 $user_ids[$user_id] = true;
01137 }
01138 }
01139 }
01140 }
01141
01142 # build the final list of email addresses
01143 reset($user_ids);
01144 while (list($user_id,) = each($user_ids))
01145 {
01146 if ($user_id)
01147 {
01148 # Dirty hack: for a reason need to be cleared out,
01149 # a user_id = 0 arrived here.
01150 # Must not define email address so soon. Just passing user_id
01151 $addresses[$user_id] = true;
01152 }
01153 }
01154
01155 # return an array with all the email addresses the notification must be sent to
01156 return (array_keys($addresses));
01157
01158 }
01159
01160 function trackers_mail_followup ($item_id,$more_addresses=false,$changes=false,$force_exclude_list=false, $artifact=0)
01161 {
01162 global $sys_datefmt;
01163
01164 if (!$artifact)
01165 { $artifact = ARTIFACT; }
01166
01167 $sql="SELECT * from $artifact WHERE bug_id='$item_id'";
01168
01169 $result = db_query($sql);
01170 $bug_href = "http://".$GLOBALS['sys_default_domain'].$GLOBALS['sys_home']."$artifact/?$item_id";
01171
01172 if ($result && db_numrows($result) > 0)
01173 {
01174
01175 $group_id = db_result($result,0,'group_id');
01176
01177 unset($content_type);
01178 # CERN SPECIFIC (at least for now) BEGIN '
01179 # Maybe later we ll implement a way to select mail templates, or prepared
01180 # mail format (like: text / html).
01181 # But it will have to be done in a well planned way that take into account
01182 # necessary cases and is not encumbered by very very specific things.
01183 # Until this happen, cern will use its own functions to deals with notif.
01184 # Indeed, this part will maintained and modified by CERN only.
01185 #
01186 # To ease maintainance, such specific things should usually not be added.
01187 # Please write to savane-dev if you intend to make such changes.
01188 # The upstream code cannot be cluttered by tons of things like that.
01189 # This is a one time exception, or almost, needed because this cannot
01190 # be directly merged in a generic way right now.
01191 if ($GLOBALS['sys_default_domain'] == "savannah.cern.ch" || $GLOBALS['sys_debug_cerntest'])
01192 {
01193 $content_type = group_get_preference($group_id, "notif_content");
01194 if ($content_type == "")
01195 {
01196 $content_type = '2'; # by default select maximum
01197 }
01198
01199 # Now, if the content type is 0, go on with Savane standard notif.
01200 # If it s something else, use trackers_mail_followup_cernspecifichack()
01201 if ($content_type > 0)
01202 {
01203 return trackers_mail_followup_cernspecifichack($group_id,$bug_href,$result,$content_type,$item_id,$more_addresses,$changes,$force_exclude_list);
01204 }
01205 }
01206 # CERN SPECIFIC (at least for now) END
01207
01208 # CONTENT OF THE MAIL MUST NOT BE TRANSLATED
01209
01210 unset($body);
01211
01212 if ($changes)
01213 {
01214 $body = format_item_changes($changes, $item_id, $group_id)."\n";
01215 }
01216 else
01217 {
01218 $body .= "URL:\n <".$bug_href.">\n\n";
01219 $body .= trackers_field_display('summary', $group_id, db_result($result,0,'summary'),false,true,true,true)."\n";
01220 $body .= sprintf("%25s", "Project:").' '.group_getname($group_id)."\n";
01221 $body .= trackers_field_display('submitted_by', $group_id, db_result($result,0,'submitted_by'),false,true,true,true)."\n";
01222 $body .= trackers_field_display('date', $group_id, db_result($result,0,'date'),false,true,true,true)."\n";
01223
01224 # All other regular fields now
01225 $i=0;
01226 while ($field_name = trackers_list_all_fields())
01227 {
01228
01229 # if the field is a special field or if not used by his project
01230 # then skip it. Otherwise print it in ASCII format.
01231 if (!trackers_data_is_special($field_name) &&
01232 trackers_data_is_used($field_name))
01233 {
01234
01235 $body .= trackers_field_display($field_name,
01236 $group_id,
01237 db_result($result,0,$field_name),
01238 false,
01239 true,
01240 true,
01241 true);
01242
01243 $i++;
01244 $body .= "\n";
01245 }
01246 }
01247 $body .= "\n";
01248
01249 # Now display other special fields
01250 $body .= " _______________________________________________________\n\nDetails:\n".trackers_field_display('details',
01251 $group_id,
01252 db_result($result,0,'details'),
01253 true,true,true,true);
01254
01255 # Then output the history of bug details from newest to oldest
01256 $body .= "\n\n".format_item_details($item_id, $group_id, true);
01257
01258 # Then output the CC list
01259 $body .= "\n\n".format_item_cc_list($item_id, $group_id, true);
01260
01261 # Then output the history of bug details from newest to oldest
01262 $body .= "\n\n".format_item_attached_files($item_id, $group_id, true);
01263 }
01264
01265 # Finally output the message trailer
01266 $body .= "\n _______________________________________________________\n\n";
01267 $body .= "Reply to this item at:";
01268 $body .= "\n\n <".$bug_href.">";
01269
01270
01271
01272
01273 # See who is going to receive the notification.
01274 # Plus append any other email
01275 # given at the end of the list.
01276 $arr_addresses = trackers_build_notification_list($item_id,$group_id,$changes);
01277 $to = join(',',$arr_addresses);
01278 $from = user_getrealname(0,1).' <'.$GLOBALS['sys_mail_replyto'].'@'.$GLOBALS['sys_mail_domain'].'>';
01279 $subject = utils_unconvert_htmlspecialchars(db_result($result,0,'summary'));
01280
01281 if ($more_addresses)
01282 {
01283 $to .= ($to ? ',':'').$more_addresses;
01284 }
01285
01286 # If the item is private, take into account the exclude-list
01287 if (db_result($result,0,'privacy') == '2')
01288 {
01289 $exclude_list = db_result(db_query("SELECT ".$artifact."_private_exclude_address FROM groups WHERE group_id='$group_id'"),0, $artifact."_private_exclude_address");
01290
01291 }
01292
01293 # Disallow mail notification for an address, private or not
01294 if ($force_exclude_list)
01295 {
01296 if ($exclude_list)
01297 { $exclude_list .= ",".$force_exclude_list; }
01298 else
01299 { $exclude_list = $force_exclude_list; }
01300 }
01301
01302
01303 sendmail_mail($from, $to, $subject, $body, group_getunixname($group_id), $artifact, $item_id, 0, 0, $exclude_list);
01304 # Useless feedback, already added by sendmail_mail()
01305 #fb(_("Item update sent."));
01306
01307 }
01308 else
01309 {
01310 fb(_("Could not send item update."), 0);
01311 }
01312 }
01313
01314
01315 function trackers_attach_file($item_id,
01316 $group_id,
01317 $input_file,
01318 $input_file_name, # 4
01319 $input_file_type,
01320 $input_file_size, # 6
01321 $file_description,
01322 &$changes)
01323 {
01324
01325
01326 $user_id = (user_isloggedin() ? user_getid(): 100);
01327
01328 # Open the file
01329 $data = fopen($input_file, 'r');
01330 if (!$data)
01331 {
01332 fb(_("File not attached: unable to open it."), 1);
01333 return false;
01334 }
01335
01336 # Check file size:
01337 # - first with filesize(), that can result appropriate result for big files
01338 # unlike strlen. If it is too big, we stop here.
01339 # - then with strlen after the call the addslashes(): the check must be
01340 # made on the final size for import in the database.
01341 #
01342 if (filesize($input_file) > 524288) {
01343 fb(sprintf(_("File not attached: the allowed size is 512 kB, after escaping characters as required. The file you were trying to attach is %s kB large."), filesize($input_file)/1024), 1);
01344 return false;
01345 }
01346 $data = addslashes(fread($data, filesize($input_file)));
01347 if (strlen($data) > 524288)
01348 {
01349 fb(sprintf(_("File not attached: the allowed size is 512 kB. After escaping characters as required, it sized %s kB."), strlen($data)/1024), 1);
01350 return false;
01351 }
01352
01353 $sql = 'INSERT into trackers_file (item_id,artifact,submitted_by,date,description, file,filename,filesize,filetype) '.
01354 "VALUES ($item_id,'".ARTIFACT."',$user_id,'".time()."','".htmlspecialchars($file_description).
01355 "','$data','$input_file_name','$input_file_size','$input_file_type')";
01356
01357 $res = db_query($sql);
01358
01359 if (!$res)
01360 {
01361 fb(_("Error while attaching file:").' '.db_error($res), 1);
01362 return false;
01363 }
01364 else
01365 {
01366 $file_id = db_insertid($res);
01367 fb(sprintf(_("file #%s attached"), $file_id));
01368 $changes['attach']['description'] = $file_description;
01369 $changes['attach']['name'] = $input_file_name;
01370 $changes['attach']['size'] = $input_file_size;
01371 $changes['attach']['href'] = 'http:
01372 trackers_data_add_history("Attached File",
01373 "-",
01374 "Added ".$input_file_name.", #".$file_id,
01375 $item_id,
01376 0,0,1);
01377 return true;
01378 }
01379 }
01380
01381 function trackers_exist_cc($item_id,$cc)
01382 {
01383 $sql = "SELECT bug_cc_id FROM ".ARTIFACT."_cc WHERE bug_id='$item_id' AND email='$cc'";
01384 $res = db_query($sql);
01385 return (db_numrows($res) >= 1);
01386 }
01387
01388 function trackers_insert_cc($item_id,$cc,$added_by,$comment,$date)
01389 {
01390 $sql = "INSERT INTO ".ARTIFACT."_cc (bug_id,email,added_by,comment,date) ".
01391 "VALUES ('$item_id','$cc','$added_by','".htmlspecialchars($comment)."','$date')";
01392 $res = db_query($sql);
01393
01394 trackers_data_add_history("Carbon-Copy",
01395 "-",
01396 "Added ".utils_email($cc,1),
01397 $item_id,
01398 0,0,1);
01399 return ($res);
01400
01401 }
01402
01403 function trackers_add_cc($item_id,$group_id,$email,$comment,&$changes)
01404 {
01405 global $feedback,$ffeedback;
01406
01407 $user_id = (user_isloggedin() ? user_getid(): 100);
01408
01409 $arr_email = utils_split_emails($email);
01410 $date = time();
01411 $ok = true;
01412 $changed = false;
01413 while (list(,$cc) = each($arr_email))
01414 {
01415 # Add this cc only if not there already
01416 if (!trackers_exist_cc($item_id,$cc))
01417 {
01418 $changed = true;
01419 $res = trackers_insert_cc($item_id,$cc,$user_id,$comment,$date);
01420 if (!$res)
01421 { $ok = false; }
01422 }
01423 }
01424
01425 if (!$ok)
01426 {
01427 fb(_("CC addition failed."), 0);
01428 }
01429 else
01430 {
01431 fb(_("CC added."));
01432 }
01433 return $ok;
01434 }
01435
01436 function trackers_delete_cc($group_id=false,$item_id=false,$item_cc_id=false)
01437 {
01438 global $feedback,$ffeedback;
01439
01440 # If both bug_id and bug_cc_id are given make sure the cc belongs
01441 # to this bug (it's a bit paranoid but...)
01442 if ($item_id)
01443 {
01444 $res1 = db_query("SELECT bug_id,email from ".ARTIFACT."_cc WHERE bug_cc_id='$item_cc_id'");
01445 if ((db_numrows($res1) <= 0) || (db_result($res1,0,'bug_id') != $item_id) )
01446 {
01447 " This $item_cc_id doesn't belong to this item, nothing will be done.";
01448 return false;
01449 }
01450 }
01451
01452 # Now delete the CC address
01453 $res2 = db_query("DELETE FROM ".ARTIFACT."_cc WHERE bug_cc_id='$item_cc_id'");
01454 if (!$res2)
01455 {
01456 fb(_("Failed to remove CC.").db_error($res2), 0);
01457 return false;
01458 }
01459 else
01460 {
01461 fb(_("CC Removed."));
01462 trackers_data_add_history("Carbon-Copy",
01463 "Removed ".utils_email(db_result($res1, 0, 'email'),1),
01464 "-",
01465 $item_id,
01466 0,0,1);
01467 return true;
01468 }
01469 }
01470
01471
01472 function trackers_delete_dependancy ($group_id, $item_id, $item_depends_on, $item_depends_on_artifact, &$changes)
01473 {
01474
01475 # Can be done only by at least technicians
01476 # Note that is it possible to fake the system by providing a false group_id.
01477 # But well, consequences would be small an it will be easy to identify
01478 # the criminal.
01479
01480 if (member_check(0,$group_id, member_create_tracker_flag(ARTIFACT).'1'))
01481 {
01482 $result = db_query("DELETE FROM ".ARTIFACT."_dependencies WHERE item_id='$item_id' AND is_dependent_on_item_id='$item_depends_on' AND is_dependent_on_item_id_artifact='$item_depends_on_artifact'");
01483 }
01484
01485 if (!$result)
01486 {
01487 fb(_("Failed to delete dependancy.").db_error($result), 0);
01488 return false;
01489 }
01490 else
01491 {
01492 fb(_("Dependency Removed."));
01493 trackers_data_add_history("Dependencies",
01494 "Removed dependancy to ".$item_depends_on_artifact." #".$item_depends_on,
01495 "-",
01496 $item_id,
01497 0,0,1);
01498 trackers_data_add_history("Dependencies",
01499 "Removed dependancy from ".ARTIFACT." #".$item_id,
01500 "-",
01501 $item_depends_on,
01502 0,0,1);
01503
01504 $changes['Dependency Removed']['add'] = $item_depends_on_artifact." #".$item_depends_on;
01505
01506 return true;
01507 }
01508 }
01509
01510
01511
01512
01513
01514
01515
01516
01517 function trackers_isvarany($var)
01518 {
01519 if (is_array($var))
01520 {
01521 reset($var);
01522 while (list(,$v) = each($var))
01523 {
01524 if ($v == 0)
01525 { return true; }
01526 }
01527 return false;
01528 }
01529 else
01530 {
01531 return ($var == 0);
01532 }
01533
01534 }
01535
01536
01537 # Check is a sort criteria is already in the list of comma
01538 # separated criterias. If so invert the sort order, if not then
01539 # simply add it
01540 function trackers_add_sort_criteria($criteria_list, $order, $msort)
01541 {
01542 #echo "<br />DBG \$criteria_list=$criteria_list,\$order=$order";
01543
01544 if ($criteria_list)
01545 {
01546 $arr = explode(',',$criteria_list);
01547 $i = 0;
01548 while (list(,$attr) = each($arr))
01549 {
01550 preg_match("/\s*([^<>]*)([<>]*)/", $attr,$match);
01551 list(,$mattr,$mdir) = $match;
01552 #echo "<br />DBG \$mattr=$mattr,\$mdir=$mdir";
01553 if ($mattr == $order)
01554 {
01555 if ( ($mdir == '>') || (!isset($mdir)) )
01556 {
01557 $arr[$i] = $order.'<';
01558 } else
01559 {
01560 $arr[$i] = $order.'>';
01561 }
01562 $found = true;
01563 }
01564 $i++;
01565 }
01566 }
01567
01568 if (!$found)
01569 {
01570 if (!$msort)
01571 { unset($arr); }
01572 if ( ($order == 'severity') || ($order == 'hours') || (trackers_data_is_date_field($order)) )
01573 {
01574 # severity, effort and dates sorted in descending order by default
01575 $arr[] = $order.'<';
01576 }
01577 else
01578 {
01579 $arr[] = $order.'>';
01580 }
01581 }
01582
01583 #echo "<br />DBG \$arr[]=".join(',',$arr);
01584
01585 return(join(',', $arr));
01586
01587 }
01588
01589 # Transform criteria list to SQL query (+ means ascending
01590 # - is descending)
01591 function trackers_criteria_list_to_query($criteria_list)
01592 {
01593
01594 $criteria_list = str_replace('>',' ASC',$criteria_list);
01595 $criteria_list = str_replace('<',' DESC',$criteria_list);
01596 return $criteria_list;
01597 }
01598
01599 # Transform criteria list to readable text statement
01600 # $url must not contain the morder parameter
01601 function trackers_criteria_list_to_text($criteria_list, $url)
01602 {
01603
01604 if ($criteria_list)
01605 {
01606
01607 $arr = explode(',',$criteria_list);
01608
01609 while (list(,$crit) = each($arr))
01610 {
01611
01612 $morder .= ($morder ? ",".$crit : $crit);
01613 $attr = str_replace('>','',$crit);
01614 $attr = str_replace('<','',$attr);
01615 $morder = htmlspecialchars($morder);
01616
01617 $arr_text[] = '<a href="'.$url.'&morder='.$morder.'#results">'.
01618 trackers_data_get_label($attr).'</a><img class="icon" src="'.$GLOBALS['sys_home'].'images/'.SV_THEME.'.theme/'.
01619 ((substr($crit, -1) == '>') ? 'down' : 'up').
01620 '.png" border="0" />';
01621 }
01622 }
01623
01624 return join(' > ',$arr_text);
01625 }
01626
01627 function trackers_build_match_expression($field, &$to_match)
01628 {
01629
01630 # First get the field type
01631 $res = db_query("SHOW COLUMNS FROM ".ARTIFACT." LIKE '$field'");
01632 $type = db_result($res,0,'Type');
01633
01634 #echo "<br />DBG '$field' field type = $type";
01635
01636 if (preg_match('/text|varchar|blob/i', $type))
01637 {
01638
01639 # If it is sourrounded by /.../ the assume a regexp
01640 # else transform into a series of LIKE %word%
01641 if (preg_match('/\/(.*)\#/', $to_match, $matches))
01642 $expr = "$field RLIKE '".$matches[1]."' ";
01643 else
01644 {
01645 $words = preg_split('/\s+/', $to_match);
01646 reset($words);
01647 while ( list($i,$w) = each($words))
01648 {
01649 #echo "<br />DBG $i, $w, $words[$i]";
01650 $words[$i] = "$field LIKE '%$w%'";
01651 }
01652 $expr = join(' AND ', $words);
01653 }
01654
01655 }
01656 else if (preg_match('/int/i', $type))
01657 {
01658
01659 # If it is sourrounded by /.../ then assume a regexp
01660 # else assume an equality
01661 if (preg_match('/\/(.*)\#/', $to_match, $matches))
01662 {
01663 $expr = "$field RLIKE '".$matches[1]."' ";
01664 }
01665 else
01666 {
01667 $int_reg = '[+\-]*[0-9]+';
01668 if (preg_match("/\s*(<|>|>=|<=)\s*($int_reg)/", $to_match, $matches))
01669 {
01670 # It's < or >, = and a number then use as is
01671 $matches[2] = (string)((int)$matches[2]);
01672 $expr = "$field ".$matches[1]." '".$matches[2]."' ";
01673 $to_match = $matches[1].' '.$matches[2];
01674
01675 }
01676 else if (preg_match("/\s*($int_reg)\s*-\s*($int_reg)/", $to_match, $matches))
01677 {
01678 # it's a range number1-number2
01679 $matches[1] = (string)((int)$matches[1]);
01680 $matches[2] = (string)((int)$matches[2]);
01681 $expr = "$field >= '".$matches[1]."' AND $field <= '". $matches[2]."' ";
01682 $to_match = $matches[1].'-'.$matches[2];
01683
01684 }
01685 else if (preg_match("/\s*($int_reg)/", $to_match, $matches))
01686 {
01687 # It's a number so use equality
01688 $matches[1] = (string)((int)$matches[1]);
01689 $expr = "$field = '".$matches[1]."'";
01690 $to_match = $matches[1];
01691
01692 }
01693 else
01694 {
01695 # Invalid syntax - no condition
01696 $expr = '1';
01697 $to_match = '';
01698 }
01699 }
01700
01701 }
01702 else if (preg_match('/float/i', $type))
01703 {
01704
01705 # If it is sourrounded by /.../ the assume a regexp
01706 # else assume an equality
01707 if (preg_match('/\/(.*)\#', $to_match, $matches))
01708 {
01709 $expr = "$field RLIKE '".$matches[1]."' ";
01710 }
01711 else
01712 {
01713 $flt_reg = '[+\-0-9.eE]+';
01714
01715 if (preg_match("/\s*(<|>|>=|<=)\s*($flt_reg)/", $to_match, $matches))
01716 {
01717 # It's < or >, = and a number then use as is
01718 $matches[2] = (string)((float)$matches[2]);
01719 $expr = "$field ".$matches[1]." '".$matches[2]."' ";
01720 $to_match = $matches[1].' '.$matches[2];
01721
01722 }
01723 else if (preg_match("/\s*($flt_reg)\s*-\s*($flt_reg)/", $to_match, $matches) )
01724 {
01725 # it's a range number1-number2
01726 $matches[1] = (string)((float)$matches[1]);
01727 $matches[2] = (string)((float)$matches[2]);
01728 $expr = "$field >= '".$matches[1]."' AND $field <= '". $matches[2]."' ";
01729 $to_match = $matches[1].'-'.$matches[2];
01730
01731 }
01732 else if (preg_match("/\s*($flt_reg)/", $to_match, $matches))
01733 {
01734
01735 # It's a number so use equality
01736 $matches[1] = (string)((float)$matches[1]);
01737 $expr = "$field = '".$matches[1]."'";
01738 $to_match = $matches[1];
01739 }
01740 else
01741 {
01742 # Invalid syntax - no condition
01743 $expr = '1';
01744 $to_match = '';
01745 }
01746 }
01747
01748 }
01749 else
01750 {
01751 # All the rest (???) use =
01752 $expr = "$field = '$to_match'";
01753 }
01754
01755 #echo "<br />DBG expr to match for '$field' = $expr";
01756 return ' ('.$expr.') ';
01757
01758 }
01759
01760 # function moved to data.
01761 function trackers_delete_file($group_id=false,$item_id=false,$item_file_id=false)
01762 {
01763
01764 return trackers_data_delete_file($group_id, $item_id, $item_file_id);
01765 }
01766
01767 # register a msg id for an item update notification
01768 function trackers_register_msgid ($msgid, $artifact, $item_id)
01769 {
01770 return db_affected_rows(db_query("INSERT INTO trackers_msgid (msg_id,artifact,item_id) VALUES ('$msgid','$artifact','$item_id')"));
01771 }
01772
01773 # Get a list, separated a msg id for an item update notification
01774 function trackers_get_msgid ($artifact, $item_id, $latest="")
01775 {
01776 if ($latest)
01777 { $latest = "ORDER BY id DESC LIMIT 1"; }
01778
01779 $result = db_query("SELECT msg_id FROM trackers_msgid WHERE artifact='$artifact' AND item_id='$item_id' $latest");
01780 unset($list);
01781 while ($id = db_fetch_array($result))
01782 {
01783 if (isset($list))
01784 { $list .= " "; }
01785 $list .= "<".$id['msg_id'].">";
01786 }
01787 return $list;
01788 }
01789
01790 ############## NASTY HACK
01791
01792 # CERN SPECIFIC (at least for now) BEGIN '
01793 # Maybe later we ll implement a way to select mail templates, or prepared
01794 # mail format (like: text / html).
01795 # But it will have to be done in a well planned way that take into account
01796 # necessary cases and is not encumbered by very very specific things.
01797 # Until this happen, cern will use its own functions to deals with notif.
01798 # Indeed, this part will maintained and modified by CERN only.
01799 #
01800 # To ease maintainance, such specific things should usually not be added.
01801 # Please write to savane-dev if you intend to make such changes.
01802 # The upstream code cannot be cluttered by tons of things like that.
01803 # This is a one time exception, or almost, needed because this cannot
01804 # be directly merged in a generic way right now.
01805 function trackers_mail_followup_cernspecifichack ($group_id, $bug_href, $result,$content_type, $item_id, $more_addresses=false, $changes=false,$force_exclude_list=false) {
01806 global $sys_datefmt;
01807
01808 # MUST BE DEFINED HERE, dont ask me why
01809 $subject = utils_unconvert_htmlspecialchars(db_result($result,0,'summary'));
01810
01811 if ($content_type == '2') { # for now means CERN present format
01812 # (last + overview + all followups)
01813
01814 $body = "This is an automated notification sent by ".$GLOBALS['sys_name']. ".
01815 It relates to:\n\t\t".ARTIFACT." #".$item_id.", project ".group_getname($group_id)."\n";
01816
01817 if ($changes)
01818 {
01819 $body .= "\n==============================================================================\n";
01820 $body .= " LATEST MODIFICATIONS of ".ARTIFACT." #".$item_id.":\n";
01821 $body .= "==============================================================================\n\n";
01822
01823
01824 ### format_item_changes of savane 1.0.6
01825 # FIXME: strange, with %25s it does not behave exactly like
01826 # trackers_field_label_display
01827 $fmt = "%24s: %23s => %-23s\n";
01828
01829 $separator = "\n _______________________________________________________\n\n";
01830
01831 # Process most of the fields
01832 reset($changes);
01833 while (list($field,$h) = each($changes))
01834 {
01835
01836 # If both removed and added items are empty skip - Sanity check
01837 if (!$h['del'] && !$h['add'])
01838 { continue; }
01839
01840 if ($field == "details" || $field == "attach")
01841 { continue; }
01842
01843 $label = trackers_data_get_label($field);
01844 if (!$label)
01845 { $label = $field; }
01846 $out .= sprintf($fmt, $label, $h['del'],$h['add']);
01847 }
01848
01849 if ($out)
01850 {
01851 $out = "Update of ".utils_get_tracker_prefix(ARTIFACT)." #".$item_id." (project ".group_getunixname($group_id)."):\n\n".$out;
01852 }
01853
01854
01855 # Process special cases: follow-up comments
01856 if ($changes['details'])
01857 {
01858
01859 if ($out)
01860 { $out .= $separator; }
01861
01862 $out_com = "Follow-up Comment #".db_numrows(trackers_data_get_followups($item_id));
01863 if (!$out)
01864 {
01865 $out_com .= ", ".utils_get_tracker_prefix(ARTIFACT)." #".$item_id." (project ".group_getunixname($group_id).")";
01866 }
01867
01868 $out_com .= ":\n\n";
01869 if ($changes['details']['type'] != 'None' && $changes['details']['type'] != '(Error - Not Found)')
01870 {
01871 $out_com .= '['.$changes['details']['type']."]\n";
01872 }
01873 $out_com .= utils_unconvert_htmlspecialchars($changes['details']['add']);
01874 unset($changes['details']);
01875
01876 $out .= $out_com;
01877 }
01878
01879 # Process special cases: file attachment
01880 if ($changes['attach'])
01881 {
01882 if ($out)
01883 { $out .= $separator; }
01884
01885 $out_att = "Additional Item Attachment";
01886 if (!$out)
01887 {
01888 $out_att .= ", ".utils_get_tracker_prefix(ARTIFACT)." #".$item_id." (project ".group_getunixname($group_id).")";
01889 }
01890 $out_att .= ":\n\n";
01891 $out_att .= sprintf("File name: %-30s Size:%d KB\n",$changes['attach']['name'],
01892 intval($changes['attach']['size']/1024) );
01893 $out_att .= $changes['attach']['description']."\n".'<'.$changes['attach']['href'].'>';
01894 unset($changes['attach']);
01895
01896 $out .= $out_att;
01897 }
01898
01899 $body .= $out;
01900 ### format_item_changes of savane 1.0.6
01901
01902 $body .= "\n";
01903 }
01904
01905 $body .= "\n==============================================================================\n";
01906 $body .= " OVERVIEW of ".ARTIFACT." #".$item_id.":\n";
01907 $body .= "==============================================================================\n\n";
01908 $body .= "URL:\n <".$bug_href.">\n\n";
01909 $body .= trackers_field_display('summary', $group_id, db_result($result,0,'summary'),false,true,true,true)."\n";
01910 $body .= sprintf("%25s", "Project:").' '.group_getname($group_id)."\n";
01911 $body .= trackers_field_display('submitted_by', $group_id, db_result($result,0,'submitted_by'),false,true,true,true)."\n";
01912 $body .= trackers_field_display('date', $group_id, db_result($result,0,'date'),false,true,true,true)."\n";
01913
01914 # All other regular fields now
01915 $i=0;
01916 while ($field_name = trackers_list_all_fields())
01917 {
01918
01919 # if the field is a special field or if not used by his project
01920 # then skip it. Otherwise print it in ASCII format.
01921 if (!trackers_data_is_special($field_name) &&
01922 trackers_data_is_used($field_name))
01923 {
01924
01925 $body .= trackers_field_display($field_name,
01926 $group_id,
01927 db_result($result,0,$field_name),
01928 false,
01929 true,
01930 true,
01931 true);
01932
01933 $i++;
01934 $body .= "\n";
01935 }
01936 }
01937 $body .= "\n";
01938
01939 # Now display other special fields
01940 $body .= " _______________________________________________________\n\n".trackers_field_display('details',
01941 $group_id,
01942 db_result($result,0,'details'),
01943 true,true,true,true);
01944
01945 # Then output the history of bug details from newest to oldest
01946 $body .= "\n\n";
01947 # format_item_details($item_id, $group_id, true); of savane 1.0.6
01948 $result=trackers_data_get_followups($item_id);
01949 $rows=db_numrows($result);
01950
01951 # No followup comment -> return now
01952 if ($rows > 0)
01953 {
01954 unset($out);
01955 $out .= " _______________________________________________________\n\nFollow-up Comments:\n\n";
01956
01957 # Loop throuh the follow-up comments and format them
01958 for ($i=0; $i < $rows; $i++)
01959 {
01960
01961 $comment_type = db_result($result, $i, 'comment_type');
01962 if ($comment_type == 'None')
01963 { $comment_type = ''; }
01964 else
01965 { $comment_type = '['.$comment_type.']'; }
01966
01967 $fmt = "\n-------------------------------------------------------\n".
01968 "Date: %-30sBy: %s\n";
01969 if ($comment_type)
01970 { $fmt .= "%s\n%s"; }
01971 else
01972 { $fmt .= "%s%s"; }
01973 $fmt .= "\n";
01974
01975
01976 # I wish we had sprintf argument swapping in PHP3 but
01977 # we dont so do it the ugly way...
01978
01979 if (db_result($result, $i, 'realname'))
01980 {
01981 $name = db_result($result, $i, 'realname')." <".db_result($result, $i, 'user_name').">";
01982 }
01983 else
01984 {
01985 $name = "Anonymous"; # must no be translated, part of mails notifs
01986 }
01987 $out .= sprintf($fmt,
01988 format_date($sys_datefmt,db_result($result, $i, 'date')),
01989 $name,
01990 $comment_type,
01991 utils_unconvert_htmlspecialchars(db_result($result, $i, 'old_value'))
01992 );
01993
01994 }
01995 # final touch...
01996 $out .= "\n\n\n";
01997 $body .= $out;
01998 }
01999 # format_item_details($item_id, $group_id, true); of savane 1.0.6 end
02000
02001
02002 # Then output the CC list
02003 $body .= "\n\n";
02004 # format_item_cc_list($item_id, $group_id, true); of savane 1.0.6
02005 $result=trackers_data_get_cc_list($item_id);
02006 $rows=db_numrows($result);
02007
02008 # No file attached -> return now
02009 if ($rows > 0)
02010 {
02011 unset($out);
02012 $out .= " _______________________________________________________\n\n"."Carbon-Copy List:\n\n";
02013 $fmt = "%-35s | %s\n";
02014 $out .= sprintf($fmt, 'CC Address', 'Comment');
02015 $out .= "------------------------------------+-----------------------------\n";
02016
02017 # Loop through the cc and format them
02018 for ($i=0; $i < $rows; $i++)
02019 {
02020
02021 $email = db_result($result, $i, 'email');
02022 $item_cc_id = db_result($result, $i, 'bug_cc_id');
02023
02024 # If the CC is a user point to its user page.
02025 # Do not build mailto, we do not need to help spammers.
02026 $res_username = user_get_result_set_from_unix($email);
02027 if ($res_username && (db_numrows($res_username) == 1))
02028 { $href_cc = utils_user_link($email); }
02029 else
02030 { $href_cc = $email; }
02031
02032 $out .= sprintf($fmt, $email, db_result($result, $i, 'comment'));
02033
02034 }
02035 # final touch...
02036 $out .= "\n";
02037 $body .= $out;
02038 }
02039 # format_item_cc_list($item_id, $group_id, true); of savane 1.0.6 end
02040
02041 # Then output the history of bug details from newest to oldest
02042 $body .= "\n\n";
02043 # format_item_attached_files of savane 1.0.6
02044 #format_item_attached_files($item_id, $group_id, true);
02045 $result=trackers_data_get_attached_files($item_id);
02046 $rows=db_numrows($result);
02047
02048 # No file attached -> return now
02049 if ($rows > 0)
02050 {
02051 unset($out);
02052 $out .= " _______________________________________________________\n\nFile Attachments:\n\n";
02053 $fmt = "\n-------------------------------------------------------\n".
02054 "Date: %s Name: %s Size: %s By: %s\n%s\n%s";
02055 # Loop throuh the attached files and format them
02056
02057 for ($i=0; $i < $rows; $i++)
02058 {
02059
02060 $item_file_id = db_result($result, $i, 'bug_file_id');
02061 $href = $GLOBALS['sys_home'].ARTIFACT."/download.php?item_id=$item_id&item_file_id=$item_file_id";
02062
02063 $out .= sprintf($fmt,
02064 format_date($sys_datefmt,db_result($result, $i, 'date')),
02065 db_result($result, $i, 'filename'),
02066 utils_filesize(0, intval(db_result($result, $i, 'filesize'))),
02067 db_result($result, $i, 'user_name'),
02068 db_result($result, $i, 'description'),
02069 '<http:
02070
02071 }
02072
02073 # final touch...
02074 $out .= "\n";
02075 $body .= $out;
02076 }
02077
02078 # format_item_attached_files of savane 1.0.6 end
02079
02080
02081 # Finally output the message trailer
02082 $body .= "\n==============================================================================\n\n";
02083 $body .= "This item URL is:";
02084 $body .= "\n <".$bug_href.">";
02085 }
02086
02087 if ($content_type == '1') { # for now means ROOT wishes (UGLY ... I know!)
02088 $body = "";
02089 $was_new_item = false;
02090
02091 if (user_isloggedin()) {
02092 $body .= "Posted by: ".user_getrealname($user_id).' <'.user_getname($user_id).">";
02093 } else {
02094 $body .= "Posted by an anonymous user";
02095 }
02096 $body .= "\n";
02097 $body .= "Related to: [".group_getname($group_id)." ".ARTIFACT." #".$item_id."] ".utils_unconvert_htmlspecialchars(db_result($result,0,'summary'))."\n";
02098 $body .= "URL: <".$bug_href.">\n\n";
02099
02100 if ($changes) {
02101 # $body .= format_item_changes($changes)."\n";
02102
02103 #Process special cases first: follow-up comment
02104 $fmt = "%s: %23s -> %-23s\n";
02105 $was_followup = false;
02106 if ($changes['details']) {
02107 $body .= "Follow-up Comment:\n\n";
02108 if ($changes['details']['type'] != 'None' && $changes['details']['type'] != '(Error - Not Found)') {
02109 $body .= '['.$changes['details']['type']."]\n";
02110 }
02111 $body .= utils_unconvert_htmlspecialchars($changes['details']['add']);
02112 $body .= "\n";
02113 unset($changes['details']);
02114 # set flag to skip output of this followup comment at the end
02115 $was_followup = true;
02116 }
02117 } else {
02118 # if new submission start with description
02119 $body .= trackers_field_display('details',
02120 $group_id,
02121 db_result($result,0,'details'),
02122 true,true,true,true);
02123 $body .= "\n";
02124 # set flag to skip output of original submission at the end
02125 $was_new_item = true;
02126 }
02127
02128 if ($changes) {
02129 #Process special cases first: bug file attachment
02130 if ($changes['attach']) {
02131 $body .= sprintf("Attachment of file: %s Size:%d KB ",$changes['attach']['name'], intval($changes['attach']['size']/1024) );
02132 $body .= $changes['attach']['description']."\n".'<'.$changes['attach']['href'].'>';
02133 unset($changes['attach']);
02134 $body .= "\n";
02135 }
02136
02137 # All the rest of the fields now
02138 reset($changes);
02139 if (count($changes)) {
02140 $body .= "\n";
02141 while ( list($field,$h) = each($changes)) {
02142 # If both removed and added items are empty skip - Sanity check
02143 if (!$h['del'] && !$h['add']) { continue; }
02144 $label = trackers_data_get_label($field);
02145 if (!$label) { $label = $field; }
02146 $off = sprintf("%d", 23-strlen($label)-2);
02147 $fmt = "%s: %".$off."s -> %-23s\n";
02148 $body .= sprintf($fmt, $label, $h['del'],$h['add']);
02149 }
02150 } else {
02151 $body .= "\n";
02152 }
02153 $body .= "\n";
02154 } else {
02155 $body .= "\n";
02156 }
02157
02158 $body .= trackers_field_display('submitted_by', $group_id, db_result($result,0,'submitted_by'),false,true,true,true,false,'',false,'',false,false,-3)."\n";
02159
02160 # All other regular fields now
02161 $i=0;
02162 while ($field_name = trackers_list_all_fields()) {
02163 # if the field is a special field or if not used by his project
02164 # then skip it. Otherwise print it in ASCII format.
02165 if (!trackers_data_is_special($field_name) && trackers_data_is_used($field_name)) {
02166 $body .= trackers_field_display($field_name, $group_id,
02167 db_result($result,0,$field_name),
02168 false, true, true, true, false,
02169 '',false,'',false,false,-3);
02170 $i++;
02171 $body .= "\n";
02172 }
02173 }
02174
02175 # Then output the history of bug details from newest to oldest
02176
02177 # $body .= format_item_details($item_id, $group_id, true);
02178
02179 $fu_result=trackers_data_get_followups($item_id);
02180 $fu_rows=db_numrows($fu_result);
02181 if ($fu_rows > 0) {
02182 # Loop throuh the follow-up comments and format them
02183 $fmt = "\n-----Reply from %s on %s-----\n%s\n";
02184 for ($i=0; $i < $fu_rows; $i++) {
02185 # prevent output of most recent if already shown
02186 if ($was_followup && ($i == 0)) { continue; }
02187 if (db_result($fu_result, $i, 'realname')) {
02188 $name = db_result($fu_result, $i, 'realname')." <".db_result($fu_result, $i, 'user_name').">";
02189 } else {
02190 $name = "Anonymous"; # must no be translated, part of mails notifs
02191 }
02192 if (user_get_timezone()) {
02193 $tz = ' ('.user_get_timezone().')';
02194 } else {
02195 $tz = '';
02196 }
02197 $body .= sprintf($fmt, $name,
02198 format_date($sys_datefmt,db_result($fu_result, $i, 'date')).$tz,
02199 utils_unconvert_htmlspecialchars(db_result($fu_result, $i, 'old_value'))
02200 );
02201 }
02202 }
02203
02204 if (!$was_new_item) {
02205 # Now display Original Submission
02206 $body .= "\n -----Original Message-----".trackers_field_display('details', $group_id, db_result($result,0,'details'), true,true,true,true);
02207 }
02208 }
02209
02210 # stuff is formatted
02211
02212 # See who is going to receive the notification.
02213 # Plus append any other email
02214 # given at the end of the list.
02215 $arr_addresses = trackers_build_notification_list($item_id,$group_id,$changes);
02216 $to = join(',',$arr_addresses);
02217 # CERN SPECIFIC HACK
02218 $from = '"noreply ['.user_getrealname(0,0).']" <'.$GLOBALS['sys_mail_replyto'].'@'.$GLOBALS['sys_mail_domain'].'>';
02219 # $from = user_getrealname(0,1).' <'.$GLOBALS['sys_mail_replyto'].'@'.$GLOBALS['sys_mail_domain'].'>';
02220
02221 # replace usernames with user_ids (as expected by sendmail_mail)
02222 $repl_addresses = '';
02223 $more_addr_arr = explode(',',$more_addresses);
02224 while (list(,$maddr) = each($more_addr_arr)) {
02225 $maddr = ereg_replace(" ","", $maddr);
02226 if (validate_email($maddr)) {
02227 $repl_addresses .= ($repl_addresses ? ',':'').$maddr;
02228 } else {
02229 $maddr_user_id = user_getid($maddr);
02230 if (user_exists($maddr_user_id)) {
02231 $repl_addresses .= ($repl_addresses ? ',':'').$maddr_user_id;
02232 } else {
02233 $repl_addresses .= ($repl_addresses ? ',':'').$maddr;
02234 }
02235 }
02236 } # while
02237
02238 if ($repl_addresses)
02239 {
02240 $to .= ($to ? ',':'').$repl_addresses;
02241 }
02242
02243 # If the item is private, take into account the exclude-list
02244 if (db_result($result,0,'privacy') == '2')
02245 {
02246 $exclude_list = db_result(db_query("SELECT ".ARTIFACT."_private_exclude_address FROM groups WHERE group_id='$group_id'"),0, ARTIFACT."_private_exclude_address");
02247
02248 }
02249
02250 # Disallow mail notification for an address, private or not
02251 if ($force_exclude_list)
02252 {
02253 if ($exclude_list)
02254 { $exclude_list .= ",".$force_exclude_list; }
02255 else
02256 { $exclude_list = $force_exclude_list; }
02257 }
02258
02259 sendmail_mail($from, $to, $subject, $body, group_getunixname($group_id), ARTIFACT, $item_id, 0, 0, $exclude_list);
02260
02261 # Done it upstream function already
02262 # else
02263 # {
02264 # fb(_("Could not send item update."), 0);
02265 # }
02266 return true;
02267 }
02268 # CERN SPECIFIC (at least for now) END
02269
02270 ?>