/** * Adds a new task * @param array $args array containing all task properties. unknown properties will be ignored * @access public * @return integer the task ID on success * @version 1.0 * @notes $args is POST data, bad..bad user.. */ public static function create_task($args) { global $db, $user, $proj; if (!isset($args)) { return 0; } // these are the POST variables that the user MUST send, if one of // them is missing or if one of them is empty, then we have to abort $requiredPostArgs = array('item_summary', 'project_id'); //modify: made description not required foreach ($requiredPostArgs as $required) { if (empty($args[$required])) { return 0; } } $notify = new Notifications(); if ($proj->id != $args['project_id']) { $proj = new Project($args['project_id']); } if (!$user->can_open_task($proj)) { return 0; } // first populate map with default values $sql_args = array('project_id' => $proj->id, 'date_opened' => time(), 'last_edited_time' => time(), 'opened_by' => intval($user->id), 'percent_complete' => 0, 'mark_private' => 0, 'supertask_id' => 0, 'closedby_version' => 0, 'closure_comment' => '', 'task_priority' => 2, 'due_date' => 0, 'anon_email' => '', 'item_status' => STATUS_UNCONFIRMED); // POST variables the user is ALLOWED to provide $allowedPostArgs = array('task_type', 'product_category', 'product_version', 'operating_system', 'task_severity', 'estimated_effort', 'supertask_id', 'item_summary', 'detailed_desc'); // these POST variables the user is only ALLOWED to provide if he got the permissions if ($user->perms('modify_all_tasks')) { $allowedPostArgs[] = 'closedby_version'; $allowedPostArgs[] = 'task_priority'; $allowedPostArgs[] = 'due_date'; $allowedPostArgs[] = 'item_status'; } if ($user->perms('manage_project')) { $allowedPostArgs[] = 'mark_private'; } // now copy all over all POST variables the user is ALLOWED to provide // (but only if they are not empty) foreach ($allowedPostArgs as $allowed) { if (!empty($args[$allowed])) { $sql_args[$allowed] = $args[$allowed]; } } // Process the due_date if (isset($args['due_date']) && ($due_date = $args['due_date']) || ($due_date = 0)) { $due_date = Flyspray::strtotime($due_date); } $sql_params[] = 'mark_private'; $sql_values[] = intval($user->perms('manage_project') && isset($args['mark_private']) && $args['mark_private'] == '1'); $sql_params[] = 'due_date'; $sql_values[] = $due_date; $sql_params[] = 'closure_comment'; $sql_values[] = ''; // Process estimated effort $estimated_effort = 0; if ($proj->prefs['use_effort_tracking'] && isset($sql_args['estimated_effort'])) { if (($estimated_effort = effort::EditStringToSeconds($sql_args['estimated_effort'], $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format'])) === FALSE) { Flyspray::show_error(L('invalideffort')); $estimated_effort = 0; } $sql_args['estimated_effort'] = $estimated_effort; } // Token for anonymous users $token = ''; if ($user->isAnon()) { if (empty($args['anon_email'])) { return 0; } $token = md5(function_exists('openssl_random_pseudo_bytes') ? openssl_random_pseudo_bytes(32) : uniqid(mt_rand(), true)); $sql_args['task_token'] = $token; $sql_args['anon_email'] = $args['anon_email']; } // ensure all variables are in correct format if (!empty($sql_args['due_date'])) { $sql_args['due_date'] = Flyspray::strtotime($sql_args['due_date']); } if (isset($sql_args['mark_private'])) { $sql_args['mark_private'] = intval($sql_args['mark_private'] == '1'); } // split keys and values into two separate arrays $sql_keys = array(); $sql_values = array(); foreach ($sql_args as $key => $value) { $sql_keys[] = $key; $sql_values[] = $value; } /* * TODO: At least with PostgreSQL, this has caused the sequence to be * out of sync with reality. Must be fixed in upgrade process. Check * what's the situation with MySQL. (It's fine, it updates the value even * if the column was manually adjusted. Remove this whole block later.) $result = $db->Query('SELECT MAX(task_id)+1 FROM {tasks}'); $task_id = $db->FetchOne($result); $task_id = $task_id ? $task_id : 1; */ //now, $task_id is always the first element of $sql_values #array_unshift($sql_keys, 'task_id'); #array_unshift($sql_values, $task_id); $sql_keys_string = join(', ', $sql_keys); $sql_placeholder = $db->fill_placeholders($sql_values); $result = $db->Query("INSERT INTO {tasks}\n ({$sql_keys_string})\n VALUES ({$sql_placeholder})", $sql_values); $task_id = $db->Insert_ID(); Backend::upload_links($task_id); // create tags if (isset($args['tags'])) { $tagList = explode(';', $args['tags']); $tagList = array_map('strip_tags', $tagList); $tagList = array_map('trim', $tagList); $tagList = array_unique($tagList); # avoid duplicates for inputs like: "tag1;tag1" or "tag1; tag1<p></p>" foreach ($tagList as $tag) { if ($tag == '') { continue; } # old tag feature #$result2 = $db->Query("INSERT INTO {tags} (task_id, tag) VALUES (?,?)",array($task_id,$tag)); # new tag feature. let's do it in 2 steps, it is getting too complicated to make it cross database compatible, drawback is possible (rare) race condition (use transaction?) $res = $db->Query("SELECT tag_id FROM {list_tag} WHERE (project_id=0 OR project_id=?) AND tag_name LIKE ? ORDER BY project_id", array($proj->id, $tag)); if ($t = $db->FetchRow($res)) { $tag_id = $t['tag_id']; } else { if ($proj->prefs['freetagging'] == 1) { # add to taglist of the project $db->Query("INSERT INTO {list_tag} (project_id,tag_name) VALUES (?,?)", array($proj->id, $tag)); $tag_id = $db->Insert_ID(); } else { continue; } } $db->Query("INSERT INTO {task_tag}(task_id,tag_id) VALUES(?,?)", array($task_id, $tag_id)); } } // Log the assignments and send notifications to the assignees if (isset($args['rassigned_to']) && is_array($args['rassigned_to'])) { // Convert assigned_to and store them in the 'assigned' table foreach ($args['rassigned_to'] as $val) { $db->Replace('{assigned}', array('user_id' => $val, 'task_id' => $task_id), array('user_id', 'task_id')); } // Log to task history Flyspray::logEvent($task_id, 14, implode(' ', $args['rassigned_to'])); // Notify the new assignees what happened. This obviously won't happen if the task is now assigned to no-one. $notify->Create(NOTIFY_NEW_ASSIGNEE, $task_id, null, $notify->SpecificAddresses($args['rassigned_to']), NOTIFY_BOTH, $proj->prefs['lang_code']); } // Log that the task was opened Flyspray::logEvent($task_id, 1); $result = $db->Query('SELECT * FROM {list_category} WHERE category_id = ?', array($args['product_category'])); $cat_details = $db->FetchRow($result); // We need to figure out who is the category owner for this task if (!empty($cat_details['category_owner'])) { $owner = $cat_details['category_owner']; } else { // check parent categories $result = $db->Query('SELECT * FROM {list_category} WHERE lft < ? AND rgt > ? AND project_id = ? ORDER BY lft DESC', array($cat_details['lft'], $cat_details['rgt'], $cat_details['project_id'])); while ($row = $db->FetchRow($result)) { // If there's a parent category owner, send to them if (!empty($row['category_owner'])) { $owner = $row['category_owner']; break; } } } if (!isset($owner)) { $owner = $proj->prefs['default_cat_owner']; } if ($owner) { if ($proj->prefs['auto_assign'] && ($args['item_status'] == STATUS_UNCONFIRMED || $args['item_status'] == STATUS_NEW)) { Backend::add_to_assignees($owner, $task_id, true); } Backend::add_notification($owner, $task_id, true); } // Reminder for due_date field if (!empty($sql_args['due_date'])) { Backend::add_reminder($task_id, L('defaultreminder') . "\n\n" . CreateURL('details', $task_id), 2 * 24 * 60 * 60, time()); } // Create the Notification if (Backend::upload_files($task_id)) { $notify->Create(NOTIFY_TASK_OPENED, $task_id, 'files', null, NOTIFY_BOTH, $proj->prefs['lang_code']); } else { $notify->Create(NOTIFY_TASK_OPENED, $task_id, null, null, NOTIFY_BOTH, $proj->prefs['lang_code']); } // If the reporter wanted to be added to the notification list if (isset($args['notifyme']) && $args['notifyme'] == '1' && $user->id != $owner) { Backend::add_notification($user->id, $task_id, true); } if ($user->isAnon()) { $anonuser = array(); $anonuser[$email] = array('recipient' => $args['anon_email'], 'lang' => $fs->prefs['lang_code']); $recipients = array($anonuser); $notify->Create(NOTIFY_ANON_TASK, $task_id, $token, $recipients, NOTIFY_EMAIL, $proj->prefs['lang_code']); } return array($task_id, $token); }
/** * Adds a new task * @param array $args array containing all task properties. unknown properties will be ignored * @access public * @return array(error type, msg, false) or array(task ID, token, true) * @version 1.0 * @notes $args is POST data, bad..bad user.. */ function create_task($args) { global $db, $user, $proj, $fs; if ($proj->id != $args['project_id']) { $proj = new Project($args['project_id']); } if (!$user->can_open_task($proj) || count($args) < 3) { return array(ERROR_RECOVER, L('missingrequired'), false); } // check required fields if (!(($item_summary = $args['item_summary']) && ($detailed_desc = $args['detailed_desc']))) { return array(ERROR_RECOVER, L('summaryanddetails'), false); } foreach ($proj->fields as $field) { if ($field->prefs['value_required'] && !array_get($args, 'field' . $field->id) && !($field->prefs['force_default'] && !$user->perms('modify_all_tasks'))) { return array(ERROR_RECOVER, L('missingrequired') . ' (' . $field->prefs['field_name'] . ')', false); } } if ($user->isAnon() && $fs->prefs['use_recaptcha']) { include_once BASEDIR . '/includes/external/recaptchalib.php'; $solution = new reCAPTCHA_Solution(); $solution->privatekey = $fs->prefs['recaptcha_priv_key']; $solution->challenge = Post::val('recaptcha_challenge_field'); $solution->response = Post::val('recaptcha_response_field'); $solution->remoteip = $_SERVER['REMOTE_ADDR']; if (!$solution->isValid()) { return array(ERROR_RECOVER, $solution->error_code, false); } } $sql_values = array(time(), time(), $args['project_id'], $item_summary, $detailed_desc, intval($user->id), 0); $sql_params[] = 'mark_private'; $sql_values[] = isset($args['mark_private']) && $args['mark_private'] == '1'; $sql_params[] = 'closure_comment'; $sql_values[] = ''; $sql_params[] = 'syntax_plugins'; $plugins = trim(implode(' ', array_get($args, 'detailed_desc_syntax_plugins', array()))); if (!$plugins) { $plugins = $proj->prefs['syntax_plugins']; } $sql_values[] = $plugins; // Token for anonymous users $token = ''; if ($user->isAnon()) { $token = md5(uniqid(mt_rand(), true)); $sql_params[] = 'task_token'; $sql_values[] = $token; } $sql_params[] = 'anon_email'; $sql_values[] = array_get($args, 'anon_email', ''); $sql_cols = array_merge(array('date_opened', 'last_edited_time', 'project_id', 'item_summary', 'detailed_desc', 'opened_by', 'percent_complete'), $sql_params); $db->x->autoExecute('{tasks}', array_combine($sql_cols, $sql_values)); $task_id = $db->lastInsertID(); // [RED] Add task to redundancy table (opened by, last_changed_time) $db->x->autoExecute('{redundant}', array('task_id' => $task_id, 'last_changed_time' => time(), 'opened_by_real_name' => $user->infos['real_name'], 'opened_by_user_name' => $user->infos['user_name'], 'last_changed_by_real_name' => $user->infos['real_name'], 'last_changed_by_user_name' => $user->infos['user_name'])); // Per project task ID $prefix_id = $db->x->GetOne('SELECT MAX(prefix_id)+1 FROM {tasks} WHERE project_id = ?', null, $proj->id); $db->x->execParam('UPDATE {tasks} SET prefix_id = ? WHERE task_id = ?', array($prefix_id, $task_id)); // Now the custom fields if (count($proj->fields)) { $stmt = $db->x->autoPrepare('{field_values}', array('task_id', 'field_id', 'field_value')); foreach ($proj->fields as $field) { $stmt->execute(array($task_id, $field->id, $field->read(array_get($args, 'field' . $field->id, 0)))); } $stmt->free(); } $assignees = array(); if (isset($args['assigned_to'])) { // Prepare assignee list $assignees = explode(';', trim($args['assigned_to'])); $assignees = array_map(array('Flyspray', 'UserNameToId'), $assignees); $assignees = array_filter($assignees, create_function('$x', 'return ($x > 0);')); // Log the assignments and send notifications to the assignees if (count($assignees)) { // Convert assigned_to and store them in the 'assigned' table foreach ($assignees as $val) { $fields = array('user_id' => array('value' => $val, 'key' => true), 'task_id' => array('value' => $task_id, 'key' => true)); $db->Replace('{assigned}', $fields); } Flyspray::logEvent($task_id, 14, implode(' ', $assignees)); // Notify the new assignees what happened. This obviously won't happen if the task is now assigned to no-one. Notifications::send($assignees, ADDRESS_USER, NOTIFY_NEW_ASSIGNEE, array('task_id' => $task_id)); } } // Log that the task was opened Flyspray::logEvent($task_id, 1); // find category owners $owners = array(); foreach ($proj->fields as $field) { if ($field->prefs['list_type'] != LIST_CATEGORY) { continue; } $cat = $db->x->getRow('SELECT * FROM {list_category} WHERE category_id = ?', null, array_get($args, 'field' . $field->id, 0)); if ($cat['category_owner']) { $owners[] = $cat['category_owner']; } else { // check parent categories $sql = $db->x->getAll('SELECT * FROM {list_category} WHERE lft < ? AND rgt > ? AND list_id = ? ORDER BY lft DESC', null, array($cat['lft'], $cat['rgt'], $cat['list_id'])); foreach ($sql as $row) { // If there's a parent category owner, send to them if ($row['category_owner']) { $owners[] = $row['category_owner']; break; } } } } // last try... if (!count($owners) && $proj->prefs['default_cat_owner']) { $owners[] = $proj->prefs['default_cat_owner']; } if (count($owners)) { foreach ($owners as $owner) { if ($proj->prefs['auto_assign'] && !in_array($owner, $assignees)) { Backend::add_to_assignees($owner, $task_id, true); } Backend::add_notification($owner, $task_id, true); } } // Create the Notification if (Backend::upload_files($task_id)) { Notifications::send($task_id, ADDRESS_TASK, NOTIFY_TASK_OPENED, array('files' => true)); } else { Notifications::send($task_id, ADDRESS_TASK, NOTIFY_TASK_OPENED); } // If the reporter wanted to be added to the notification list if (isset($args['notifyme']) && $args['notifyme'] == '1' && !in_array($user->id, $owners)) { Backend::add_notification($user->id, $task_id, true); } // this is relaxed, if the anonymous email is not valid, just dont bother.. if ($user->isAnon() && Flyspray::check_email($args['anon_email'])) { Notifications::send($args['anon_email'], ADDRESS_EMAIL, NOTIFY_ANON_TASK, array('task_id' => $task_id, 'token' => $token)); } return array($task_id, $token, true); }
/** * Adds a new task * @param array $args array containing all task properties. unknown properties will be ignored * @access public * @return integer the task ID on success * @version 1.0 * @notes $args is POST data, bad..bad user.. */ public static function create_task($args) { global $db, $user, $proj; $notify = new Notifications(); if ($proj->id != $args['project_id']) { $proj = new Project($args['project_id']); } if (!$user->can_open_task($proj) || count($args) < 3) { return 0; } if (!(($item_summary = $args['item_summary']) && ($detailed_desc = $args['detailed_desc']))) { return 0; } // Some fields can have default values set if (!$user->perms('modify_all_tasks')) { $args['closedby_version'] = 0; $args['task_priority'] = 2; $args['due_date'] = 0; $args['item_status'] = STATUS_UNCONFIRMED; } $param_names = array('task_type', 'item_status', 'product_category', 'product_version', 'closedby_version', 'operating_system', 'task_severity', 'task_priority'); $sql_values = array(time(), time(), $args['project_id'], $item_summary, $detailed_desc, intval($user->id), 0); $sql_params = array(); foreach ($param_names as $param_name) { if (isset($args[$param_name])) { $sql_params[] = $param_name; $sql_values[] = $args[$param_name]; } } // Process the due_date if (isset($args['due_date']) && ($due_date = $args['due_date']) || ($due_date = 0)) { $due_date = Flyspray::strtotime($due_date); } $sql_params[] = 'mark_private'; $sql_values[] = intval($user->perms('manage_project') && isset($args['mark_private']) && $args['mark_private'] == '1'); $sql_params[] = 'due_date'; $sql_values[] = $due_date; $sql_params[] = 'closure_comment'; $sql_values[] = ''; // Token for anonymous users $token = ''; if ($user->isAnon()) { $token = md5(function_exists('openssl_random_pseudo_bytes') ? openssl_random_pseudo_bytes(32) : uniqid(mt_rand(), true)); $sql_params[] = 'task_token'; $sql_values[] = $token; $sql_params[] = 'anon_email'; $sql_values[] = $args['anon_email']; } else { $sql_params[] = 'anon_email'; $sql_values[] = ''; } $sql_params = join(', ', $sql_params); // +1 for the task_id column; $sql_placeholder = $db->fill_placeholders($sql_values, 1); $result = $db->Query('SELECT MAX(task_id)+1 FROM {tasks}'); $task_id = $db->FetchOne($result); $task_id = $task_id ? $task_id : 1; //now, $task_id is always the first element of $sql_values array_unshift($sql_values, $task_id); $result = $db->Query("INSERT INTO {tasks}\n ( task_id, date_opened, last_edited_time,\n project_id, item_summary,\n detailed_desc, opened_by,\n percent_complete, {$sql_params} )\n VALUES ({$sql_placeholder})", $sql_values); // Log the assignments and send notifications to the assignees if (isset($args['rassigned_to']) && is_array($args['rassigned_to'])) { // Convert assigned_to and store them in the 'assigned' table foreach ($args['rassigned_to'] as $val) { $db->Replace('{assigned}', array('user_id' => $val, 'task_id' => $task_id), array('user_id', 'task_id')); } // Log to task history Flyspray::logEvent($task_id, 14, implode(' ', $args['rassigned_to'])); // Notify the new assignees what happened. This obviously won't happen if the task is now assigned to no-one. $notify->Create(NOTIFY_NEW_ASSIGNEE, $task_id, null, $notify->SpecificAddresses($args['rassigned_to'])); } // Log that the task was opened Flyspray::logEvent($task_id, 1); $result = $db->Query('SELECT * FROM {list_category} WHERE category_id = ?', array($args['product_category'])); $cat_details = $db->FetchRow($result); // We need to figure out who is the category owner for this task if (!empty($cat_details['category_owner'])) { $owner = $cat_details['category_owner']; } else { // check parent categories $result = $db->Query('SELECT * FROM {list_category} WHERE lft < ? AND rgt > ? AND project_id = ? ORDER BY lft DESC', array($cat_details['lft'], $cat_details['rgt'], $cat_details['project_id'])); while ($row = $db->FetchRow($result)) { // If there's a parent category owner, send to them if (!empty($row['category_owner'])) { $owner = $row['category_owner']; break; } } } if (!isset($owner)) { $owner = $proj->prefs['default_cat_owner']; } if ($owner) { if ($proj->prefs['auto_assign'] && ($args['item_status'] == STATUS_UNCONFIRMED || $args['item_status'] == STATUS_NEW)) { Backend::add_to_assignees($owner, $task_id, true); } Backend::add_notification($owner, $task_id, true); } // Reminder for due_date field if ($due_date) { Backend::add_reminder($task_id, L('defaultreminder') . "\n\n" . CreateURL('details', $task_id), 2 * 24 * 60 * 60, time()); } // Create the Notification if (Backend::upload_files($task_id)) { $notify->Create(NOTIFY_TASK_OPENED, $task_id, 'files'); } else { $notify->Create(NOTIFY_TASK_OPENED, $task_id); } // If the reporter wanted to be added to the notification list if (isset($args['notifyme']) && $args['notifyme'] == '1' && $user->id != $owner) { Backend::add_notification($user->id, $task_id, true); } if ($user->isAnon()) { $notify->Create(NOTIFY_ANON_TASK, $task_id, $token, $args['anon_email'], NOTIFY_EMAIL); } return array($task_id, $token); }
// ################## // taking ownership // ################## case 'takeownership': Backend::assign_to_me($user->id, Req::val('ids')); // TODO: Log event in a later version. $_SESSION['SUCCESS'] = L('takenownershipmsg'); break; // ################## // add to assignees list // ################## // ################## // add to assignees list // ################## case 'addtoassignees': Backend::add_to_assignees($user->id, Req::val('ids')); // TODO: Log event in a later version. $_SESSION['SUCCESS'] = L('addedtoassignees'); break; // ################## // admin request // ################## // ################## // admin request // ################## case 'requestclose': case 'requestreopen': if ($action == 'requestclose') { Flyspray::AdminRequest(1, $proj->id, $task['task_id'], $user->id, Post::val('reason_given')); Flyspray::logEvent($task['task_id'], 20, Post::val('reason_given')); } elseif ($action == 'requestreopen') {
function action_addtoassignees() { global $user; Backend::add_to_assignees($user->id, Req::val('ids')); return array(SUBMIT_OK, L('addedtoassignees')); }