index : flyspray | |
Archlinux32 customized Flyspray installation | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | js/callbacks/checkrelated.php | 20 | ||||
-rw-r--r-- | js/callbacks/checksave.php | 16 | ||||
-rw-r--r-- | js/callbacks/deletesearches.php | 30 | ||||
-rw-r--r-- | js/callbacks/gethistory.php | 69 | ||||
-rw-r--r-- | js/callbacks/getpreview.php | 21 | ||||
-rw-r--r-- | js/callbacks/getsearches.php | 30 | ||||
-rw-r--r-- | js/callbacks/quickedit.php | 170 | ||||
-rw-r--r-- | js/callbacks/savesearches.php | 27 | ||||
-rw-r--r-- | js/callbacks/searchnames.php | 55 | ||||
-rw-r--r-- | js/callbacks/searchtask.php | 43 | ||||
-rw-r--r-- | js/callbacks/testemail.php | 44 | ||||
-rw-r--r-- | js/callbacks/usersearch.php | 45 |
diff --git a/js/callbacks/checkrelated.php b/js/callbacks/checkrelated.php new file mode 100644 index 0000000..3ce3ee8 --- /dev/null +++ b/js/callbacks/checkrelated.php @@ -0,0 +1,20 @@ +<?php +/* + Checks if a related tasks belongs to a different project. +*/ + +define('IN_FS', true); + +require_once('../../header.php'); + +$sql = $db->query('SELECT project_id + FROM {tasks} + WHERE task_id = ?', + array(Get::val('related_task'))); + +$relatedproject = $db->fetchOne($sql); + +if (Get::val('project') == $relatedproject || !$relatedproject) { + echo 'ok'; +} +?> diff --git a/js/callbacks/checksave.php b/js/callbacks/checksave.php new file mode 100644 index 0000000..a5fd1a4 --- /dev/null +++ b/js/callbacks/checksave.php @@ -0,0 +1,16 @@ +<?php +/* + Checks if a task can be saved without danger or not. +*/ + +define('IN_FS', true); + +require_once('../../header.php'); + +$res = $db->query('SELECT last_edited_time FROM {tasks} WHERE task_id = ?', array(Get::val('task_id'))); +$last_edit = $db->fetchOne($res); + +if (Get::val('time') >= $last_edit) { + echo 'ok'; +} +?> diff --git a/js/callbacks/deletesearches.php b/js/callbacks/deletesearches.php new file mode 100644 index 0000000..2ff9e3b --- /dev/null +++ b/js/callbacks/deletesearches.php @@ -0,0 +1,30 @@ +<?php +/* + This script is the AJAX callback that deletes a user's saved search +*/ + +define('IN_FS', true); + +require_once('../../header.php'); + +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); + + if( !Post::has('csrftoken') ){ + http_response_code(428); # 'Precondition Required' + die('missingtoken'); + }elseif( Post::val('csrftoken')==$_SESSION['csrftoken']){ + # empty + }else{ + http_response_code(412); # 'Precondition Failed' + die('wrongtoken'); + } + + if (!$user->isAnon()) { + $db->query('DELETE FROM {searches} WHERE id = ? AND user_id = ?', array(Post::num('id'), $user->id)); + echo $db->affectedRows(); + } +} + +?> diff --git a/js/callbacks/gethistory.php b/js/callbacks/gethistory.php new file mode 100644 index 0000000..617b992 --- /dev/null +++ b/js/callbacks/gethistory.php @@ -0,0 +1,69 @@ +<?php +/* + This script gets the history of a task and + returns it for HTML display in a page. +*/ + +define('IN_FS', true); + +header('Content-type: text/html; charset=utf-8'); + +require_once('../../header.php'); +require_once('../../includes/events.inc.php'); + +$csp->emit(); + +if( !isset($_GET['task_id']) or !is_numeric($_GET['task_id'])){ + die(); +} else { + $task_id = Get::num('task_id'); +} + +# recalculate $proj for permission check +$result = $db->query('SELECT project_id FROM {tasks} WHERE task_id = ?', array($task_id)); +$project_id = $db->fetchOne($result); +if (!$project_id) { + die(); +} +$proj = new Project($project_id); + +// Initialise user +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); +} else { + $user = new User(0, $proj); +} + +load_translations(); + +# set project of task asked for and then check permissions based on that +if ( !($task = Flyspray::getTaskDetails($task_id)) ) { + die(); +} + +# also check the calculated view task permission in addition to view_history permission +if (!$user->can_view_task($task) or !$user->perms('view_history')) { + die(); +} + +if ($details = Get::num('details')) { + $details = " AND h.history_id = $details"; +} else { + $details = null; +} + +$sql = get_events($task_id, $details); +$histories = $db->fetchAllArray($sql); + +$page = new FSTpl; +$page->setTheme($proj->prefs['theme_style']); +$page->uses('histories', 'details'); +if ($details) { + event_description($histories[0]); // modifies global variables + $page->assign('details_previous', $GLOBALS['details_previous']); + $page->assign('details_new', $GLOBALS['details_new']); +} +$page->display('details.tabs.history.callback.tpl'); + +?> diff --git a/js/callbacks/getpreview.php b/js/callbacks/getpreview.php new file mode 100644 index 0000000..94d973a --- /dev/null +++ b/js/callbacks/getpreview.php @@ -0,0 +1,21 @@ +<?php +define('IN_FS', true); + +header('Content-type: text/html; charset=utf-8'); + +$webdir = dirname(dirname(dirname(htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES, 'utf-8')))); +require_once('../../header.php'); + +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); +} else { + $user = new User(0, $proj); +} + +# TODO csrftoken checking + + +echo TextFormatter::render(Post::val('text')); + +?> diff --git a/js/callbacks/getsearches.php b/js/callbacks/getsearches.php new file mode 100644 index 0000000..215d2d8 --- /dev/null +++ b/js/callbacks/getsearches.php @@ -0,0 +1,30 @@ +<?php +/* + This script gets the searches of current user and + returns it for HTML display in a page. +*/ + +define('IN_FS', true); + +header('Content-type: text/html; charset=utf-8'); + +require_once('../../header.php'); + +// Initialise user +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); +} else { + $user = new User(0, $proj); +} + +// don't allow anonymous users to access this page at all +if ($user->isAnon()) { + die(); +} + +$user->save_search(); # currently used for loading user searches from db into user object ... +$page = new FSTpl; +$page->setTheme($proj->prefs['theme_style']); +$page->display('links.searches.tpl'); +?> diff --git a/js/callbacks/quickedit.php b/js/callbacks/quickedit.php new file mode 100644 index 0000000..fda26ea --- /dev/null +++ b/js/callbacks/quickedit.php @@ -0,0 +1,170 @@ +<?php + +define('IN_FS', true); + +header('Content-type: text/html; charset=utf-8'); + +require_once('../../header.php'); +global $proj, $fs; + +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); +} else { + $user = new User(0, $proj); +} + +// don't allow anonymous users to access this page at all +if ($user->isAnon()) { + die(); +} +load_translations(); + +if( !Post::has('csrftoken') ){ + http_response_code(428); # 'Precondition Required' + die('missingtoken'); +} elseif( Post::val('csrftoken')==$_SESSION['csrftoken']){ + # ok +} else{ + http_response_code(412); # 'Precondition Failed' + die('wrongtoken'); +} + +$task = Flyspray::getTaskDetails(Post::val('task_id')); +if (!$user->can_edit_task($task)){ + http_response_code(403); # 'Forbidden' + die(L('nopermission')); +} + +# check field for update against allowed dbfields for quickedit. +# maybe FUTURE: add (dynamic read from database) allowed CUSTOM FIELDS checks for the project and user +# (if there is urgent request for implementing custom fields into Flyspray +# and using of tag-feature isn't enough to accomplish - like numbers/dates/timestamps as custom fields) +$allowedFields=array( + 'due_date', + 'item_status', + 'percent_complete', + 'task_type', + 'product_category', + 'operating_system', + 'task_severity', + 'task_priority', + 'product_version', + 'closedby_version' +); +if ($proj->prefs['use_effort_tracking'] && $user->perms('track_effort')){ + $allowedFields[]='estimated_effort'; +} + +if (!in_array(Post::val('name'), $allowedFields)){ + http_response_code(403); + die(L('invalidfield')); +} + +$value = Post::val('value'); + +# check if user is not sending manipulated invalid values +switch(Post::val('name')){ + case 'due_date': + $value = Flyspray::strtotime(Post::val('value')); + $value = intval($value); + break; + + case 'estimated_effort': + $value = effort::editStringToSeconds(Post::val('value'), $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']); + $value = intval($value); + break; + + case 'task_severity': + if(!preg_match("/^[1-5]$/", $value)){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + + case 'task_priority': + if(!preg_match("/^[1-6]$/", $value)){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + + case 'percent_complete': + if(!is_numeric($value) || $value<0 || $value>100){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + + case 'item_status': + $res=$db->query('SELECT * FROM {list_status} WHERE (project_id=0 OR project_id=?) AND show_in_list=1 AND status_id=?', array($task['project_id'], $value) ); + if($db->countRows($res)<1){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + + case 'task_type': + $res=$db->query('SELECT * FROM {list_tasktype} WHERE (project_id=0 OR project_id=?) AND show_in_list=1 AND tasktype_id=?', array($task['project_id'], $value) ); + if($db->countRows($res)<1){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + + case 'operating_system': + $res=$db->query('SELECT * FROM {list_os} WHERE (project_id=0 OR project_id=?) AND show_in_list=1 AND os_id=?', array($task['project_id'], $value) ); + if($db->countRows($res)<1){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + + case 'product_category': + $res=$db->query('SELECT * FROM {list_category} WHERE (project_id=0 OR project_id=?) AND show_in_list=1 AND category_id=?', array($task['project_id'], $value) ); + if($db->countRows($res)<1){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + + case 'product_version': + $res=$db->query('SELECT * FROM {list_version} WHERE (project_id=0 OR project_id=?) AND show_in_list=1 AND version_id=? AND version_tense=2', array($task['project_id'], $value) ); + if($db->countRows($res)<1){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + case 'closedby_version': + $res=$db->query('SELECT * FROM {list_version} WHERE (project_id=0 OR project_id=?) AND show_in_list=1 AND version_id=? AND version_tense=3', array($task['project_id'], $value) ); + if($db->countRows($res)<1){ + http_response_code(403); + die(L('invalidvalue')); + } + break; + default: + http_response_code(403); + die(L('invalidfield')); + break; +} + +$oldvalue = $task[Post::val('name')]; + +$time=time(); +$sql = $db->query("UPDATE {tasks} SET ".Post::val('name')." = ?,last_edited_time = ? WHERE task_id = ?", array($value, $time, Post::val('task_id'))); + +# load $proj again of task with correct project_id for getting active notification types in notification class +$proj= new Project($task['project_id']); + +// Log the changed field in task history +Flyspray::logEvent($task['task_id'], 3, $value, $oldvalue, Post::val('name'), $time); + +// Get the details of the task we just updated to generate the changed-task message +$new_details_full = Flyspray::getTaskDetails($task['task_id']); +$changes = Flyspray::compare_tasks($task, $new_details_full); +if (count($changes) > 0) { + $notify = new Notifications; + $notify->create(NOTIFY_TASK_CHANGED, $task['task_id'], $changes, null, NOTIFY_BOTH, $proj->prefs['lang_code']); +} + +?> diff --git a/js/callbacks/savesearches.php b/js/callbacks/savesearches.php new file mode 100644 index 0000000..e656a0a --- /dev/null +++ b/js/callbacks/savesearches.php @@ -0,0 +1,27 @@ +<?php +/** + * This script is the AJAX callback that saves a user's search + */ + +define('IN_FS', true); + +require_once('../../header.php'); + +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); + + if( !Post::has('csrftoken') ){ + http_response_code(428); # 'Precondition Required' + die('missingtoken'); + }elseif( Post::val('csrftoken')==$_SESSION['csrftoken']){ + # empty + }else{ + http_response_code(412); # 'Precondition Failed' + die('wrongtoken'); + } + + $user->save_search(); +} + +?> diff --git a/js/callbacks/searchnames.php b/js/callbacks/searchnames.php new file mode 100644 index 0000000..f696955 --- /dev/null +++ b/js/callbacks/searchnames.php @@ -0,0 +1,55 @@ +<?php + +/** + * This script is the AJAX callback that performs a search + * for users, and returns true if the user_name is not given. + */ + +define('IN_FS', true); + +header('Content-type: text/html; charset=utf-8'); + +require_once('../../header.php'); + + +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); +} else { + $user = new User(0, $proj); +} + +if ($user->isAnon()) { + # at least allow for guests when user registration is enabled, fix FS#2528 + if( !($user->can_register() or $user->can_self_register()) ){ + die(); + } +} + +if (Req::has('name')) { + $searchterm = strtolower(Req::val('name')); +} else { + die(); +} + +// Get the list of users from the global groups above +$get_users = $db->query(' + SELECT count(u.user_name) AS anz_u_user, count(r.user_name) AS anz_r_user + FROM {users} u + LEFT JOIN {registrations} r ON u.user_name = r.user_name + WHERE LOWER(u.user_name) = ? OR LOWER(r.user_name) = ?', + array($searchterm, $searchterm) +); + +load_translations(); + +while ($row = $db->fetchRow($get_users)){ + if ($row['anz_u_user'] > '0' || $row['anz_r_user'] > '0') { + $html = 'false|' . eL('usernametaken'); + } else { + $html = 'true'; + } +} + +echo $html; +?> diff --git a/js/callbacks/searchtask.php b/js/callbacks/searchtask.php new file mode 100644 index 0000000..47b0241 --- /dev/null +++ b/js/callbacks/searchtask.php @@ -0,0 +1,43 @@ +<?php +define('IN_FS', true); +require_once('../../header.php'); + + +// Require inputs +if(!Post::has('detail') || !Post::has('summary') || !Post::has('project_id')) +{ + return; +} + + +// Load user profile +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')){ + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); +} else { + $user = new User(0, $proj); +} + +// Require right to open a task on current project +if(!$user->can_open_task($proj)){ + return; +} + + +// Prepare SQL params +$params = array( + 'project_id' => Post::num('project_id'), + 'summary' => "%" . trim(Post::val('summary')) . "%", + 'details' => "%" . trim(Post::val('detail')) . "%" +); + +$sql = $db->query('SELECT count(*) + FROM {tasks} t + WHERE t.project_id = ? + AND t.item_summary like ? + AND t.detailed_desc like ?', + $params); +$sametask = $db->fetchOne($sql); +echo $sametask; + +?> diff --git a/js/callbacks/testemail.php b/js/callbacks/testemail.php new file mode 100644 index 0000000..788a12f --- /dev/null +++ b/js/callbacks/testemail.php @@ -0,0 +1,44 @@ +<?php + +define('IN_FS', true); + +header('Content-type: text/html; charset=utf-8'); + +require_once('../../header.php'); +global $proj, $fs; + +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); +} else { + $user = new User(0, $proj); +} + +// don't allow anonymous users to access this page at all +if ($user->isAnon()) { + die(L('nopermission')); +} +load_translations(); + +if( !Post::has('csrftoken') ){ + http_response_code(428); # 'Precondition Required' + die('missingtoken'); +}elseif( Post::val('csrftoken')==$_SESSION['csrftoken']){ + # empty +}else{ + http_response_code(412); # 'Precondition Failed' + die('wrongtoken'); +} +if (!$user->perms('is_admin')){ + http_response_code(403); # 'Forbidden' + die(L('nopermission')); +} + +$notify = new Notifications; +$result=$notify->sendEmail($user->infos['email_address'],'test','testcontent',1); + +if($result !=1){ + http_response_code(406); # 'Not Acceptable' +} +echo 'ok'; +?> diff --git a/js/callbacks/usersearch.php b/js/callbacks/usersearch.php new file mode 100644 index 0000000..a17833d --- /dev/null +++ b/js/callbacks/usersearch.php @@ -0,0 +1,45 @@ +<?php +/* + This script is the AJAX callback that performs a search + for users, and returns them in an ordered list. +*/ + +define('IN_FS', true); +header('Content-type: text/html; charset=utf-8'); +require_once('../../header.php'); + +if (Cookie::has('flyspray_userid') && Cookie::has('flyspray_passhash')) { + $user = new User(Cookie::val('flyspray_userid')); + $user->check_account_ok(); +} else { + $user = new User(0, $proj); +} + +// don't allow anonymous users to access this page at all +if ($user->isAnon()) { + die(); +} +$first = reset($_POST); +if (is_array($first)) { + $first = reset($first); +} +$searchterm = '%' . $first . '%'; + +// Get the list of users from the global groups above +$get_users = $db->query('SELECT real_name, user_name, profile_image + FROM {users} u + WHERE u.user_name LIKE ? OR u.real_name LIKE ?', + array($searchterm, $searchterm), 20); + +$html = '<ul class="autocomplete">'; + +while ($row = $db->fetchRow($get_users)) { + $data = array_map(array('Filters','noXSS'), $row); + $html .= '<li title="' . $data['real_name'] . '">'.($data['profile_image']!='' ? '<img src="avatars/'.$data['profile_image'].'" />' : '<span class="noavatar"></span>' ). $data['user_name'] . '<span class="informal"> ' . $data['real_name'] . '</span></li>'; +} + +$html .= '</ul>'; + +echo $html; + +?> |