/** * 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); }
$changes = Flyspray::compare_tasks($task, $new_details_full); if (count($changes) > 0) { $notify->Create(NOTIFY_TASK_CHANGED, $task['task_id'], $changes); } if ($assignees_changed) { // Log to task history Flyspray::logEvent($task['task_id'], 14, implode(' ', $assignees), implode(' ', $task['assigned_to']), '', $time); // Notify the new assignees what happened. This obviously won't happen if the task is now assigned to no-one. if (count($assignees)) { $new_assignees = array_diff($task['assigned_to'], $assignees); // Remove current user from notification list if (!$user->infos['notify_own']) { $new_assignees = array_filter($new_assignees, create_function('$u', 'global $user; return $user->id != $u;')); } if (count($new_assignees)) { $notify->Create(NOTIFY_NEW_ASSIGNEE, $task['task_id'], null, $notify->SpecificAddresses($new_assignees)); } } } Backend::add_comment($task, Post::val('comment_text'), $time); Backend::delete_files(Post::val('delete_att')); Backend::upload_files($task['task_id'], '0', 'usertaskfile'); Backend::delete_links(Post::val('delete_link')); Backend::upload_links($task['task_id'], '0', 'userlink'); $_SESSION['SUCCESS'] = L('taskupdated'); break; // ################## // closing a task // ################## // ################## // closing a task
$changes = Flyspray::compare_tasks($task, $new_details_full); if (count($changes) > 0) { $notify->Create(NOTIFY_TASK_CHANGED, $task['task_id'], $changes, null, NOTIFY_BOTH, $proj->prefs['lang_code']); } if ($assignees_changed) { // Log to task history Flyspray::logEvent($task['task_id'], 14, implode(' ', $assignees), implode(' ', $task['assigned_to']), '', $time); // Notify the new assignees what happened. This obviously won't happen if the task is now assigned to no-one. if (count($assignees)) { $new_assignees = array_diff($task['assigned_to'], $assignees); // Remove current user from notification list if (!$user->infos['notify_own']) { $new_assignees = array_filter($new_assignees, create_function('$u', 'global $user; return $user->id != $u;')); } if (count($new_assignees)) { $notify->Create(NOTIFY_NEW_ASSIGNEE, $task['task_id'], null, $notify->SpecificAddresses($new_assignees), NOTIFY_BOTH, $proj->prefs['lang_code']); } } } Backend::add_comment($task, Post::val('comment_text'), $time); Backend::delete_files(Post::val('delete_att')); Backend::upload_files($task['task_id'], '0', 'usertaskfile'); Backend::delete_links(Post::val('delete_link')); Backend::upload_links($task['task_id'], '0', 'userlink'); $_SESSION['SUCCESS'] = L('taskupdated'); Flyspray::Redirect(CreateURL('details', $task['task_id'])); break; // ################## // closing a task // ################## // ##################
/** * 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); }