<?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;
}
?>