<?php require_once($INC . "/util.inc.php"); require_once($INC . "/log.inc.php"); require_once($INC . "/sql.inc.php"); require_once($INC . "/user.inc.php"); require_once($INC . "/repo.inc.php"); require_once($INC . "/prof.inc.php"); require_once($INC . "/mail.inc.php"); require_once($INC . "/events.inc.php"); require_once($INC . "/watch.inc.php"); $rg_bug_error = ""; function rg_bug_set_error($str) { global $rg_bug_error; $rg_bug_error = $str; rg_log('set_error: ' . $str); } function rg_bug_error() { global $rg_bug_error; return $rg_bug_error; } /* States for a bug */ $rg_bug_states = array( "0" => "Any", "1" => "Open", "2" => "Closed" ); /* * Event functions */ $rg_bug_functions = array( 4000 => "rg_bug_event_note_add_all", 4001 => "rg_bug_event_note_add_one", 4100 => "rg_bug_event_add_all", 4101 => "rg_bug_event_add_one", // new new style 'bug_event_note_add_all' => 'rg_bug_event_note_add_all', 'bug_event_note_add_one' => 'rg_bug_event_note_add_one', 'bug_event_add_all' => 'rg_bug_event_add_all', 'bug_event_add_one' => 'rg_bug_event_add_one' ); rg_event_register_functions($rg_bug_functions); /* * Notify one user when a bug is added */ function rg_bug_event_add_one($db, $event) { rg_log_enter("DEBUG: bug_event_add_one event=" . rg_array2string($event)); $ret = FALSE; while (1) { $r = rg_mail_template('mail/user/repo/bug/new', $event); if ($r === FALSE) break; $ret = array(); break; } rg_log_exit(); return $ret; } /* * Notify when somebody adds a bug */ function rg_bug_event_add_all($db, $event) { rg_prof_start("bug_event_add"); $ret = array(); $x = $event; $x['category'] = 'bug_event_add_one'; $x['prio'] = 100; $x['ui'] = array(); // We will sent notifications to all watchers of a repo $r = rg_watch_load_by_obj_id($db, "repo", $event['ri']['repo_id'], 0); if ($r === FALSE) return FALSE; $full = rg_user_list_to_full_info($db, $r); if ($full === FALSE) return FALSE; foreach ($full as $uid => $ui) { $x['ui'] = $ui; $ret[$uid] = $x; } // We will sent notifications to all watchers of a bug $r = rg_watch_load_by_obj_id($db, 'bug', $event['ri']['repo_id'], $event['bug']['bug_id']); if ($r === FALSE) return FALSE; $full = rg_user_list_to_full_info($db, $r); if ($full === FALSE) return FALSE; foreach ($full as $uid => $ui) { $x['ui'] = $ui; $ret[$uid] = $x; } rg_prof_end("bug_event_add"); return $ret; } /* * Notify one user when a note is added to a bug */ function rg_bug_event_note_add_one($db, $event) { rg_log_enter("DEBUG: bug_event_note_add_one event=" . rg_array2string($event)); $ret = FALSE; while (1) { // lookup user email $ui = rg_user_info($db, $event['ui']['uid'], '', ''); if ($ui['exists'] != 1) { rg_internal_error("User does not exists!"); break; } $event['ui']['email'] = $ui['email']; $r = rg_mail_template("mail/user/repo/bug/new_note", $event); if ($r === FALSE) break; $ret = array(); break; } rg_log_exit(); return $ret; } /* * Notify users when a note is added to a bug */ function rg_bug_event_note_add_all($db, $event) { rg_prof_start("bug_event_note_add_all"); $ret = array(); $x = $event; $x['category'] = 'bug_event_note_add_one'; $x['prio'] = 100; $x['ui'] = array(); // Now, build the list of users that will receive notification $r = rg_watch_load_by_obj_id($db, 'bug', $event['ri']['repo_id'], $event['bug']['bug_id']); if ($r === FALSE) return FALSE; $full = rg_user_list_to_full_info($db, $r); if ($full === FALSE) return FALSE; foreach ($full as $uid => $ui) { $x['ui'] = $ui; $ret[] = $x; } rg_log_ml("DEBUG: ret: " . print_r($ret, TRUE)); rg_prof_end("bug_event_note_add_all"); return $ret; } /* * Return the state of a bug, as string */ function rg_bug_state($v) { global $rg_bug_states; if (!isset($rg_bug_states[$v])) return "?"; return $rg_bug_states[$v]; } /* * Returns a select for state * @exclude - array that contains keys that must be excluded */ function rg_bug_state_select($value, $exclude) { global $rg_bug_states; $ret = ""; $ret .= "<select name=\"state\" id=\"state\">\n"; foreach ($rg_bug_states as $key => $name) { if (in_array($key, $exclude)) continue; $add = ""; if (strcmp($value, $key) == 0) $add = " selected"; $ret .= "\t<option value=\"" . $key . "\"" . $add . ">" . $name . "</option>\n"; } $ret .= "</select>\n"; return $ret; } /* * Helper for loading default values for a bug. */ function rg_bug_vars_defaults($rg) { $ret = array(); $ret['bug_id'] = 0; $ret['title'] = ''; $ret['body'] = $rg['ri']['template']; $ret['state'] = 1; $ret['labels'] = ''; $ret['assigned_to'] = $rg['page_ui']['username']; return $ret; } /* * Helper for loading POST variables into an array, with validation. */ function rg_bug_vars() { $ret = array(); $ret['title'] = trim(rg_var_str('title')); $ret['body'] = trim(rg_var_str('body')); $ret['state'] = rg_var_uint('state'); $ret['labels'] = trim(rg_var_str('labels')); $ret['assigned_to'] = trim(rg_var_str('assigned_to')); return $ret; } /* * Helper function to populate some fields for a bug */ function rg_bug_cosmetic($db, &$row) { if (isset($row['uid'])) { $_ui = rg_user_info($db, $row['uid'], "", ""); if ($_ui['exists'] != 1) $row['owner'] = "?"; else $row['owner'] = $_ui['username']; } if (isset($row['body']) && !empty($row['body'])) $row['HTML:body_nlbr'] = nl2br(rg_xss_safe($row['body'])); else $row['HTML:body_nlbr'] = 'n/a'; if (isset($row['itime'])) $row['creation'] = gmdate("Y-m-d H:i", $row['itime']); if (isset($row['utime'])) { if ($row['utime'] > 0) $row['updated'] = gmdate("Y-m-d H:i", $row['utime']); else $row['updated'] = "-"; } if (isset($row['assigned_uid'])) { $row['assigned_to'] = ""; if ($row['assigned_uid'] > 0) { $_ui = rg_user_info($db, $row['assigned_uid'], "", ""); if ($_ui['exists'] == 1) $row['assigned_to'] = $_ui['username']; } } if (isset($row['deleted'])) { $row['deleted_text'] = ""; $row['deleted_who_name'] = ""; if (isset($row['deleted_who']) && ($row['deleted_who'] > 0)) { $_ui = rg_user_info($db, $row['deleted_who'], "", ""); if ($_ui['exists'] == 1) $row['deleted_who_name'] = $_ui['username']; $row['deleted_text'] = gmdate("Y-m-d H:i", $row['deleted']); } } if (isset($row['state'])) $row['state_text'] = rg_bug_state($row['state']); } /* * Return info about a bug */ function rg_bug_info($db, $repo_id, $bug_id) { rg_prof_start("bug_info"); rg_log_enter("bug_info: repo_id=$repo_id bug_id=$bug_id"); $ret = array(); $ret['ok'] = 0; $ret['exists'] = 0; while (1) { $key = 'bugs' . '::' . $repo_id . '::' . $bug_id; $c = rg_cache_get($key); if ($c !== FALSE) { $ret = $c; rg_bug_cosmetic($db, $ret); break; } $params = array("repo_id" => $repo_id, "bug_id" => $bug_id); $sql = "SELECT * FROM bugs" . " WHERE repo_id = @@repo_id@@" . " AND bug_id = @@bug_id@@"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("cannot list bugs (" . rg_sql_error() . ")"); break; } $ret['ok'] = 1; $rows = rg_sql_num_rows($res); if ($rows == 1) { $row = rg_sql_fetch_array($res); $ret = array_merge($ret, $row); $ret['exists'] = 1; rg_cache_set($key, $ret, RG_SOCKET_NO_WAIT); rg_bug_cosmetic($db, $ret); } rg_sql_free_result($res); break; } rg_log_exit(); rg_prof_end("bug_info"); return $ret; } /* * Add/edit a bug * If bug_id > 0 - edit, else add */ function rg_bug_edit($db, $login_ui, $ri, $data) { rg_prof_start("bug_edit"); rg_log_enter("bug_edit: data: " . rg_array2string($data)); $data['labels'] = isset($data['labels']) ? $data['labels'] : ""; $now = time(); $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ""; $ret = FALSE; $rollback = 0; while (1) { if (empty($data['title'])) { rg_bug_set_error("title cannot be empty"); break; } if (($data['state'] < 1) || ($data['state'] > 3)) { rg_bug_set_error("invalid state"); break; } if (empty($data['assigned_to'])) { $data['assigned_uid'] = 0; $assigned_to_text = "N/A"; } else { $aui = rg_user_info($db, 0, $data['assigned_to'], ""); if ($aui['exists'] != 1) { rg_bug_set_error("user you assigned to does not exists"); break; } $data['assigned_uid'] = $aui['uid']; $assigned_to_text = $aui['username']; } if (rg_sql_begin($db) !== TRUE) { rg_bug_set_error("start transaction failed"); break; } $rollback = 1; $add = 0; if ($data['bug_id'] == 0) { $add = 1; $data['bug_id'] = rg_repo_next_id($db, 'bug', $ri['repo_id']); if ($data['bug_id'] === FALSE) break; } if (!empty($data['labels'])) { $err = rg_bug_label_insert($db, $ri['repo_id'], $data['bug_id'], $data['labels']); if ($err !== TRUE) break; } $data['itime'] = $now; $data['utime'] = $now; $data['ip'] = $ip; $data['repo_id'] = $ri['repo_id']; $data['uid'] = $login_ui['uid']; if ($add == 1) { $data['deleted'] = 0; $sql = "INSERT INTO bugs (bug_id, itime, utime, repo_id" . ", uid, ip, title, body, state, assigned_uid" . ", deleted)" . " VALUES (@@bug_id@@, @@itime@@, 0, @@repo_id@@" . ", @@uid@@, @@ip@@, @@title@@, @@body@@" . ", @@state@@, @@assigned_uid@@, @@deleted@@)"; } else { $sql = "UPDATE bugs SET utime = @@itime@@" . ", title = @@title@@" . ", body = @@body@@" . ", state = @@state@@" . ", assigned_uid = @@assigned_uid@@" . " WHERE repo_id = @@repo_id@@" . " AND bug_id = @@bug_id@@"; } $res = rg_sql_query_params($db, $sql, $data); if ($res === FALSE) { rg_bug_set_error("cannot insert bug (" . rg_sql_error() . ")"); break; } rg_sql_free_result($res); // Add reporter and assignee to the watch list if ($add == 1) { $r = rg_watch_add($db, 'bug', $login_ui['uid'], $ri['repo_id'], $data['bug_id']); if ($r === FALSE) { rg_bug_set_error("cannot add to watch list" . " (" . rg_watch_error() . ")"); break; } } if ($data['assigned_uid'] > 0) { $r = rg_watch_add($db, 'bug', $data['assigned_uid'], $ri['repo_id'], $data['bug_id']); if ($r === FALSE) { rg_bug_set_error("cannot add to watch list" . " (" . rg_watch_error() . ")"); break; } } // TODO: seems I do not distinguish between 'add' and 'edit' $event = array( 'category' => 'bug_event_add_all', 'prio' => 200, 'ui' => $login_ui, 'ri' => array( 'repo_id' => $ri['repo_id'], 'name' => $ri['name']), 'bug' => array( 'who_added' => $login_ui['uid'], 'who_added_text' => $login_ui['username'], 'url' => rg_base_url() . rg_re_bugpage($login_ui, $ri['name'], $data['bug_id']), 'assigned_to_text' => $assigned_to_text, 'state_text' => rg_bug_state($data['state']))); $event = rg_array_merge($event, 'bug', $data); $r = rg_event_add($db, $event); if ($r !== TRUE) { rg_bug_set_error("cannot add event" . " ( . rg_event_error() . )"); break; } if (rg_sql_commit($db) !== TRUE) { rg_bug_set_error("cannot commit (" . rg_sql_error() . ")"); break; } $rollback = 0; // update cache $data['ok'] = 1; $data['exists'] = 1; $key = 'bugs' . '::' . $ri['repo_id'] . '::' . $data['bug_id']; rg_cache_set($key, $data, RG_SOCKET_NO_WAIT); rg_event_signal_daemon("", 0); $ret = $data['bug_id']; break; } if ($rollback == 1) rg_sql_rollback($db); rg_log_exit(); rg_prof_end("bug_edit"); return $ret; } /* * Delete/undelete a bug * @op: 1=delete, 2=undelete */ function rg_bug_delete_undelete($db, $who, $repo_id, $bug_id, $op) { rg_prof_start("bug_delete"); rg_log_enter("bug_delete_undelete: who=$who repo_id=$repo_id bug_id=$bug_id op=$op"); $ret = FALSE; while (1) { $now = time(); if ($op == 1) $deleted = $now; else $deleted = 0; // Only mark it as such, deletion will happen in background $params = array("deleted" => $deleted, "repo_id" => $repo_id, "bug_id" => $bug_id, "utime" => $now, "deleted_who" => $who); $sql = "UPDATE bugs SET deleted = @@deleted@@" . ", utime = @@utime@@" . ", deleted_who = @@deleted_who@@" . " WHERE repo_id = @@repo_id@@" . " AND bug_id = @@bug_id@@"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("cannot delete bug (" . rg_sql_error() . ")"); break; } rg_sql_free_result($res); // update cache $new = array(); $new['deleted'] = $deleted; $new['utime'] = $now; $new['deleted_who'] = $who; $key = 'bugs' . '::' . $repo_id . '::' . $bug_id; rg_cache_merge($key, $new, RG_SOCKET_NO_WAIT); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("bug_delete"); return $ret; } /* * List bugs */ function rg_bug_list_query($db, $sql, $params) { rg_prof_start("bug_list_query"); rg_log_enter("bug_list_query: sql=$sql..."); $ret = FALSE; while (1) { $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("cannot list by query (" . rg_sql_error() . ")"); break; } $ret = array(); while (($row = rg_sql_fetch_array($res))) { rg_bug_cosmetic($db, $row); $ret[] = array("bug" => $row); } rg_sql_free_result($res); break; } rg_log("DEBUG: list_query return " . rg_array2string($ret)); rg_log_exit(); rg_prof_end("bug_list_query"); return $ret; } /* * Loads all saved searches */ function rg_bug_search_load_all($db, $repo_id, $uid) { rg_prof_start("bug_search_load_all"); rg_log_enter("bug_search_load_all: repo_id=$repo_id uid=$uid"); $ret = FALSE; while (1) { $params = array("repo_id" => $repo_id, "uid" => $uid); $sql = "SELECT name FROM bug_search" . " WHERE (repo_id = @@repo_id@@ OR repo_id = 0)" . " AND uid = @@uid@@" . " ORDER BY repo_id, name"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("cannot load searches (" . rg_sql_error() . ")"); break; } $data = array(); // Add predefined $data['All'] = "All"; $data['Open'] = "Open"; $data['Closed'] = "Closed"; while (($row = rg_sql_fetch_array($res))) { $name = $row['name']; $data[$name] = $name; } $ret = array(); foreach ($data as $name => $junk) $ret[] = array("name" => $name); rg_sql_free_result($res); break; } rg_log_exit(); rg_prof_end("bug_search_load_all"); return $ret; } /* * Loads a saved search */ function rg_bug_search_load($db, $repo_id, $uid, $name) { rg_prof_start("bug_search_load"); rg_log_enter("bug_search_load: repo_id=$repo_id uid=$uid name=$name"); $ret = FALSE; while (1) { $template = array( "name" => $name, "reported_by" => 0, "assigned_to" => 0, "state" => 0, "start" => 0, "end" => 0, "title_string" => "", "body_string" => "", "bugs_per_page" => 25, "for_all_users" => 1, "global" => 0, "standard" => 1, "uid" => 0 ); // Pre-defined if (strcmp($name, "All") == 0) { $ret = $template; break; } if (strcmp($name, "Open") == 0) { $template['state'] = 1; $ret = $template; break; } if (strcmp($name, "Closed") == 0) { $template['state'] = 2; $ret = $template; break; } $params = array("repo_id" => $repo_id, "uid" => $uid, "name" => $name); $sql = "SELECT uid, name, data, for_all_users" . " FROM bug_search" . " WHERE (repo_id = @@repo_id@@ OR repo_id = 0)" . " AND (uid = @@uid@@ OR for_all_users = 1)" . " AND name = @@name@@" . " ORDER BY name"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("cannot search load (" . rg_sql_error() . ")"); break; } $rows = rg_sql_num_rows($res); if ($rows > 0) { $row = rg_sql_fetch_array($res); $_data = rg_unserialize($row['data']); if ($_data === FALSE) { rg_bug_set_error('cannot unserialize search data: ' . rg_util_error()); break; } $ret = $_data; $ret['uid'] = $row['uid']; $ret['name'] = $row['name']; $ret['for_all_users'] = $row['for_all_users']; $ret['standard'] = 0; } else { $ret = array(); } rg_sql_free_result($res); break; } rg_log_exit(); rg_prof_end("bug_search_load"); return $ret; } /* * Saves the search filtering * TODO: name should not be inside data? */ function rg_bug_search_save($db, $repo_id, $uid, $q) { rg_prof_start("bug_search_save"); rg_log_enter("rg_bug_search_save: repo_id=$repo_id uid=$uid" . " q=" . rg_array2string($q)); $ret = FALSE; while (1) { $name = $q['name']; unset($q['name']); // Test if is already present $old = rg_bug_search_load($db, $repo_id, $uid, $name); if ($old === FALSE) break; $data = rg_serialize($q); // Global? if (isset($q['global']) && (strcmp($q['global'], "on") == 0)) $repo_id = 0; else $repo_id = $repo_id; if (isset($q['for_all_users']) && (strcmp($q['for_all_users'], "on") == 0)) $for_all_users = 1; else $for_all_users = 0; // We will not overwrite somebody else's search // TODO: race? rg_log("DEBUG: old: " . rg_array2string($old)); $params = array("repo_id" => $repo_id, "uid" => $uid, "name" => $name, "data" => $data, "for_all_users" => $for_all_users); if (empty($old) || ($old['uid'] != $uid)) { $sql = "INSERT INTO bug_search (repo_id, uid, name" . ", data, for_all_users)" . " VALUES (@@repo_id@@, @@uid@@, @@name@@" . ", @@data@@, @@for_all_users@@)"; } else { $sql = "UPDATE bug_search" . " SET data = @@data@@" . ", for_all_users = @@for_all_users@@" . " WHERE repo_id = @@repo_id@@" . " AND uid = @@uid@@" . " AND name = @@name@@"; } $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("cannot save search (" . rg_sql_error() . ")"); break; } rg_sql_free_result($res); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("bug_search_save"); return $ret; } /* * Search for bugs */ function rg_bug_search($db, $repo_id, $uid, $q) { rg_prof_start("bug_search"); rg_log_enter("bug_search: repo_id=$repo_id uid=$uid" . " q=" . rg_array2string($q)); $params = array("repo_id" => $repo_id); $add = array(); $limit = 25; $ret = FALSE; while (1) { // reported_by if (!empty($q['reported_by'])) { $_ui = rg_user_info($db, "", $q['reported_by'], "", ""); if ($_ui['exists'] != 1) { rg_bug_set_error("cannot lookup user (reported_by)"); break; } $add[] = "AND uid = @@reported_by@@"; $params['reported_by'] = $_ui['uid']; } // assigned to if (!empty($q['assigned_to'])) { $_ui = rg_user_info($db, "", $q['assigned_to'], "", ""); if ($_ui['exists'] != 1) { rg_bug_set_error("cannot lookup user (assigned_to)"); break; } $add[] = "AND assigned_uid = @@assigned_uid@@"; $params['assigned_uid'] = $_ui['uid']; } // state if (isset($q['state']) && ($q['state'] > 0)) { $add[] = "AND state = @@state@@"; $params['state'] = $q['state']; } // start if (!empty($q['start'])) { $ts = rg_date2ts($q['start'], 0, 0, 0); if ($ts === FALSE) { rg_bug_set_error("invalid start date format"); break; } $add[] = "AND itime >= @@start@@"; $params['start'] = $ts; } // end if (!empty($q['end'])) { $ts = rg_date2ts_last_second($q['end']); if ($ts === FALSE) { rg_bug_set_error("invalid end date format"); break; } $add[] = "AND itime <= @@end@@"; $params['end'] = $ts; } // title_string if (!empty($q['title_string'])) { $add[] = "AND title ILIKE @@title@@"; $params['title'] = "%" . $q['title_string'] . "%"; } // body_string if (!empty($q['body_string'])) { $add[] = "AND body ILIKE @@body@@"; $params['body'] = "%" . $q['body_string'] . "%"; } // bugs_per_page if (!empty($q['bugs_per_page'])) $limit = $q['bugs_per_page']; // Only if we have a name and the user is logged in if (!empty($q['name']) && ($uid > 0) && ($q['standard'] == 0)) { $r = rg_bug_search_save($db, $repo_id, $uid, $q); if ($r === FALSE) break; } $sql = "SELECT * FROM bugs" . " WHERE repo_id = @@repo_id@@" . " AND deleted = 0" . " " . implode(" ", $add) . " ORDER BY itime" . " LIMIT $limit"; // TODO: order $ret = rg_bug_list_query($db, $sql, $params); break; } rg_log_exit(); rg_prof_end("bug_search"); return $ret; } /* * Delete a saved search * TODO: Check rights! */ function rg_bug_search_remove($db, $repo_id, $uid, $name) { rg_prof_start("bug_search_delete"); rg_log_enter("bug_search_remove: repo_id=$repo_id uid=$uid name=$name"); $ret = FALSE; while (1) { $params = array("repo_id" => $repo_id, "uid" => $uid, "name" => $name); $sql = "DELETE FROM bug_search" . " WHERE (repo_id = 0 OR repo_id = @@repo_id@@)" . " AND uid = @@uid@@" . " AND name = @@name@@"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("cannot remove search (" . rg_sql_error() . ")"); break; } rg_sql_free_result($res); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("bug_search_delete"); return $ret; } /*** NOTES ***/ /* * Add a note for a bug */ function rg_bug_note_add($db, $repo_id, $bug_id, $login_uid, $data) { rg_prof_start("bug_note_add"); rg_log_enter("bug_note_add: repo_id=$repo_id bug_id=$bug_id" . " login_uid=$login_uid data: " . rg_array2string($data)); $ret = FALSE; while (1) { $itime = time(); $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "?"; $params = array("repo_id" => $repo_id, "bug_id" => $bug_id, "itime" => $itime, "uid" => $login_uid, "ip" => $ip, "note" => $data['note']); $sql = "INSERT INTO bug_notes (repo_id, bug_id, itime, uid, ip" . ", note)" . " VALUES (@@repo_id@@, @@bug_id@@, @@itime@@, @@uid@@" . ", @@ip@@, @@note@@)"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error('cannot insert bug note'); break; } rg_sql_free_result($res); $_ri = rg_repo_info($db, $repo_id, 0, ""); rg_log_ml("DEBUG: _ri: " . print_r($_ri, TRUE)); if ($_ri['exists'] != 1) { rg_bug_set_error("cannot lookup repo" . " (" . rg_repo_error() . ")"); break; } $_bi = rg_bug_info($db, $repo_id, $bug_id); if ($_bi['exists'] != 1) { rg_bug_set_error("bug does not exists"); break; } $who_added_text = '?'; $_ui = rg_user_info($db, $login_uid, "", ""); if ($_ui['exists'] == 1) $who_added_text = $_ui['username']; $event = array( 'category' => 'bug_event_note_add_all', 'prio' => 200, 'ui' => $_ui, 'bug' => array( 'bug_id' => $bug_id, 'title' => $_bi['title'], 'url' => rg_base_url() . rg_re_bugpage($_ui, $_ri['name'], $bug_id) ), 'ri' => array( 'repo_id' => $repo_id, 'name' => $_ri['name']), 'note' => array( 'body' => $data['note'], 'who_added' => $login_uid, 'who_added_text' => $who_added_text)); $r = rg_event_add($db, $event); if ($r !== TRUE) { rg_bug_set_error("cannot add event" . " ( . rg_event_error() . )"); break; } rg_event_signal_daemon('', 0); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("bug_note_add"); return $ret; } /* * List notes for a bug */ function rg_bug_note_list($db, $repo_id, $bug_id, $offset) { rg_prof_start("bug_note_list"); rg_log_enter("bug_note_list: repo_id=$repo_id bug_id=$bug_id"); $ret = FALSE; while (1) { // TODO: test if user is allowed to see a note $params = array("repo_id" => $repo_id, "bug_id" => $bug_id); $sql = "SELECT * FROM bug_notes" . " WHERE repo_id = @@repo_id@@" . " AND bug_id = @@bug_id@@" . " ORDER BY itime DESC" . " OFFSET $offset"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("Cannot select bug notes (" . rg_sql_error() . ")"); break; } $ret = array(); while (($row = rg_sql_fetch_array($res))) { $row['note_creation'] = gmdate("Y-m-d H:i", $row['itime']) . ' UTC'; $row['note_id'] = substr(md5($row['note']), 0, 6); $_ui = rg_user_info($db, $row['uid'], "", ""); if ($_ui['exists'] == 1) { $row['note_owner'] = $_ui['username']; $row['note_owner_url'] = rg_re_userpage($_ui); $row['HTML:note_gravatar'] = $_ui['HTML:gravatar']; } else { $row['note_owner'] = "?"; $row['note_owner_url'] = ''; $row['HTML:note_gravatar'] = ''; } $row['HTML:note_nlbr'] = nl2br(rg_xss_safe($row['note'])); $ret[] = $row; } rg_sql_free_result($res); break; } rg_log_exit(); rg_prof_end("bug_note_list"); return $ret; } /*** LABELS ***/ /* * Build an array from a string of labels */ function rg_bug_label_string2array($s) { $ret = array(); $s = preg_replace('/[\r\n\t ]/u', ',', $s); rg_log("DEBUG: s=[$s]"); $list = explode(",", $s); rg_log("DEBUG: list: " . rg_array2string($list)); if (empty($list)) return array(); foreach ($list as $label) { if (empty($label)) continue; $ret[] = $label; } return $ret; } /* * Builds a string of labels, from an array */ function rg_bug_label_array2string($a) { return implode(",", $a); } /* * Returns labels that are in the first set but not in the second */ function rg_bug_label_diff($a, $b) { $ret = array(); if (empty($a)) return array(); foreach ($a as $label) if (!isset($b[$label])) $ret[] = $label; return $ret; } /* * Get all labels for a project */ function rg_bug_label_get($db, $repo_id, $bug_id) { rg_prof_start("bug_label_get"); rg_log_enter("bug_label_get: repo_id=$repo_id bug_id=$bug_id"); $ret = FALSE; while (1) { $params = array("repo_id" => $repo_id, "bug_id" => $bug_id); $sql = "SELECT DISTINCT label FROM bug_labels" . " WHERE repo_id = @@repo_id@@" . " AND bug_id = @@bug_id@@" . " ORDER BY label"; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("Cannot select labels (" . rg_sql_error() . ")"); break; } $ret = array(); while (($row = rg_sql_fetch_array($res))) $ret[] = $row['label']; rg_sql_free_result($res); break; } rg_log_exit(); rg_prof_end("bug_label_get"); return $ret; } /* * Inserts labels */ function rg_bug_label_insert($db, $repo_id, $bug_id, $labels) { rg_prof_start("bug_label_insert"); rg_log_enter("bug_label_insert: repo_id=$repo_id bug_id=$bug_id labels=$labels"); $ret = FALSE; while (1) { $labels = rg_bug_label_string2array($labels); rg_log("DEBUG: labels: " . rg_array2string($labels)); if (empty($labels)) { $ret = TRUE; break; } $existing = rg_bug_label_get($db, $repo_id, $bug_id); rg_log("DEBUG: existing: " . rg_array2string($existing)); if ($existing === FALSE) break; $diff = rg_bug_label_diff($labels, $existing); rg_log("DEBUG: diff: " . rg_array2string($diff)); if (empty($diff)) { $ret = TRUE; break; } $params = array("repo_id" => $repo_id, "bug_id" => $bug_id); $index = 1; $list = array(); foreach ($diff as $label) { $params["label_" . $index] = $label; $list[] = "(@@repo_id@@, @@bug_id@@, @@label_" . $index . "@@)"; $index++; } $sql = "INSERT INTO bug_labels (repo_id, bug_id, label)" . " VALUES " . implode(", ", $list); $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_bug_set_error("Cannot insert labels (" . rg_sql_error() . ")"); break; } rg_sql_free_result($res); $ret = TRUE; break; } rg_log_exit(); rg_prof_end("bug_label_insert"); return $ret; } /* * Returns labels as HTML */ function rg_bug_label_html($db, $labels) { rg_prof_start("bug_label_html"); $a = array(); if (!empty($labels)) { foreach ($labels as $label) $a[] = array("HTML:label" => rg_xss_safe($label)); } $ret = rg_template_table("repo/bug/list_labels", $a, array()); rg_prof_end("bug_label_html"); return $ret; } /* High level functions */ /* * High level function for adding/creating a bug */ function rg_bug_edit_high_level($db, &$rg) { rg_log_enter("rg_bug_edit_high_level"); rg_log_ml("DEBUG: rg: " . print_r($rg, TRUE)); $ret = ""; $errmsg = array(); $show_form = FALSE; while (1) { $x = array(); $x['obj_id'] = $rg['ri']['repo_id']; $x['type'] = 'repo'; $x['owner'] = $rg['ri']['uid']; $x['uid'] = $rg['login_ui']['uid']; $x['username'] = $rg['login_ui']['username']; $x['needed_rights'] = 'B'; $x['ip'] = $rg['ip']; $x['misc'] = ''; if (rg_rights_allow($db, $x) !== TRUE) { $ret .= rg_template("repo/bug/deny_edit.html", $rg, TRUE /* xss */); break; } $show_form = TRUE; if ($rg['doit'] == 0) { if ($rg['bug']['bug_id'] == 0) $rg['bug'] = rg_bug_vars_defaults($rg); break; } $rg['bug'] = rg_array_merge($rg['bug'], '', rg_bug_vars()); if (!rg_valid_referer()) { $errmsg[] = "invalid referer; try again"; break; } if (!rg_token_valid($db, $rg, 'bug_edit_hl', FALSE)) { $errmsg[] = "invalid token; try again"; break; } $bug_id = rg_bug_edit($db, $rg['login_ui'], $rg['ri'], $rg['bug']); if ($bug_id === FALSE) { $errmsg[] = rg_bug_error(); break; } $rg['bug']['bug_id'] = $bug_id; $url = rg_re_bugpage($rg['page_ui'], $rg['ri']['name'], $bug_id); rg_redirect($url); $show_form = FALSE; break; } if ($show_form) { $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $rg['rg_form_token'] = rg_token_get($db, $rg, 'bug_edit_hl'); $rg['rg_form_token_tag'] = 'bug_edit_hl'; $exclude = array(0); $rg['bug']['HTML:state_select'] = rg_bug_state_select($rg['bug']['state'], $exclude); $hints = array(); $hints[]['HTML:hint'] = rg_template("hints/repo/bug/add.html", $rg, TRUE /* xss */); $rg['HTML:bug_edit_hints'] = rg_template_table("hints/list", $hints, $rg); $ret .= rg_template("repo/bug/bug_add_edit.html", $rg, TRUE /* xss */); } rg_log_exit(); return $ret; } ?>