diff --git a/module/group/control.php b/module/group/control.php index 14d2f9e9dc..06078463d4 100644 --- a/module/group/control.php +++ b/module/group/control.php @@ -133,7 +133,7 @@ class group extends control $this->group->updateView($groupID); if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError())); - $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'reload')); + $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse'))); } $group = $this->group->getById($groupID); diff --git a/module/my/control.php b/module/my/control.php index 9bd379a8b0..1f72c3595c 100644 --- a/module/my/control.php +++ b/module/my/control.php @@ -184,7 +184,8 @@ class my extends control /* Append id for secend sort. */ $sort = $this->loadModel('common')->appendOrder($orderBy); - + $this->loadModel('product'); + /* Assign. */ $this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->task; $this->view->position[] = $this->lang->my->task; diff --git a/module/my/css/managecontacts.css b/module/my/css/managecontacts.css index 851d840f30..a94b78812d 100644 --- a/module/my/css/managecontacts.css +++ b/module/my/css/managecontacts.css @@ -1,3 +1,5 @@ body.body-modal {padding-bottom: 20px; min-height: 400px} .side-col {padding-right: 40px; padding-top: 25px;} + +.btn-block {display: block; width: 100%; text-overflow: ellipsis; white-space: nowrap; overflow: hidden;} \ No newline at end of file diff --git a/module/my/view/managecontacts.html.php b/module/my/view/managecontacts.html.php index 5edcaa0200..5f1c438fea 100644 --- a/module/my/view/managecontacts.html.php +++ b/module/my/view/managecontacts.html.php @@ -19,7 +19,7 @@ { $listClass = ($id == $listID) ? 'btn btn-block active' : 'btn btn-block'; $shareIcon = in_array($id, $globalContacts) ? ' ' : ''; - echo html::a(inlink('managecontacts', "listID=$id&mode=edit"), $shareIcon . $listName, '', "class='{$listClass}'"); + echo html::a(inlink('managecontacts', "listID=$id&mode=edit"), $shareIcon . $listName, '', "class='{$listClass}' title='$listName'"); } ?> ' . $lang->user->contacts->createList, '', "class='btn btn-block'"); ?> diff --git a/module/my/view/task.html.php b/module/my/view/task.html.php index f218106392..bc64c852a2 100644 --- a/module/my/view/task.html.php +++ b/module/my/view/task.html.php @@ -84,7 +84,10 @@ consumed;?> left;?> '>deadline, 0, 4) > 0) echo $task->deadline;?> - processStatus('task', $task);?> + + storyStatus) and $task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion);?> + {$this->lang->story->changed}") : print(" " . $this->processStatus('task', $task) . "");?> + needConfirm) diff --git a/module/project/model.php b/module/project/model.php index 64803f2d2b..4e33e346e5 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -2774,6 +2774,9 @@ class projectModel extends model if(!isset($node->id))$node->id = 0; if($node->type == 'story') { + static $users; + if(empty($users)) $users = $this->loadModel('user')->getPairs('noletter'); + $node->type = 'module'; $stories = isset($storyGroups[$node->root][$node->id]) ? $storyGroups[$node->root][$node->id] : array(); foreach($stories as $story) @@ -2785,8 +2788,8 @@ class projectModel extends model $storyItem->color = $story->color; $storyItem->pri = $story->pri; $storyItem->storyId = $story->id; - $storyItem->openedBy = $story->openedBy; - $storyItem->assignedTo = $story->assignedTo; + $storyItem->openedBy = $users[$story->openedBy]; + $storyItem->assignedTo = zget($users, $story->assignedTo); $storyItem->url = helper::createLink('story', 'view', "storyID=$story->id&version=$story->version&from=project¶m=$projectID"); $storyItem->taskCreateUrl = helper::createLink('task', 'batchCreate', "projectID={$projectID}&story={$story->id}"); diff --git a/module/search/model.php b/module/search/model.php index 41999a98c1..f113cad889 100644 --- a/module/search/model.php +++ b/module/search/model.php @@ -158,6 +158,11 @@ class searchModel extends model $condition = '`' . $this->post->$fieldName . "` >= '$value' AND `" . $this->post->$fieldName . "` <= '$value 23:59:59'"; $where .= " $andOr ($condition)"; } + elseif ($operator == '<=' and preg_match('/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/', $value)) + { + /* Fix bug #2896. */ + $where .= " $andOr " . '`' . $this->post->$fieldName . "` <= '$value 23:59:59'"; + } elseif($condition) { $where .= " $andOr " . '`' . $this->post->$fieldName . '` ' . $condition; diff --git a/module/todo/control.php b/module/todo/control.php index 7e7764b3f5..df1032eb9a 100644 --- a/module/todo/control.php +++ b/module/todo/control.php @@ -450,11 +450,16 @@ class todo extends control { if(!empty($_POST['todoIDList'])) { + $waitID = array(); + $todoIDList = array(); foreach($_POST['todoIDList'] as $todoID) { $todo = $this->todo->getById($todoID); - if($todo->status == 'done') $this->todo->close($todoID); + ($todo->status != 'done' && $todo->status != 'closed') ? $waitID[] = $todoID : $todoIDList[] = $todoID; } + if(!empty($waitID)) die(js::execute('alert("ID: ' .implode(',', $waitID). '的待办处于未完成状态");')); + + foreach($todoIDList as $todoID) $this->todo->close($todoID); die(js::reload('parent')); } } diff --git a/sdk/php/zentao.php b/sdk/php/zentao.php new file mode 100644 index 0000000000..28bfbea1b0 --- /dev/null +++ b/sdk/php/zentao.php @@ -0,0 +1,606 @@ + 0, 'msg' => 'error', 'result' => array()); // Return result. + + /** + * Get the session ID required for the session. + * + * @access public + * @return void + */ + public function __construct() + { + $this->login(); + } + + /** + * User login verification. + * + * @access public + * @return void + */ + public function login() + { + /* Get token. */ + $result = $this->setParams(array('m' => 'api', 'f' => 'getSessionID')) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $result = json_decode($result->data); + $this->tokenAuth = $result->sessionName . '=' . $result->sessionID; + + /* User authentication login. */ + $this->setParams(array('account' => self::ztAccount, 'password' => self::ztPassword)) + ->setRequestMethod('post') + ->sendRequest(array('get' => self::ztURL . '?m=user&f=login&t=json', 'path_info' => self::ztURL . '/user-login.json')); + } + + /** + * Get a list of departments. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getDeptList($optionalParams = array(), $extraFields = array()) + { + $result = $this->setParams(array('m' => 'dept', 'f' => 'browse'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'deptID', 'parentDepts', 'sons', 'tree'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Add a new department. + * + * @param array $optionalParams + * @access public + * @return string + */ + public function addDept($optionalParams = array()) + { + $result = $this->setParams(array(), $optionalParams) + ->setRequestMethod('post') + ->sendRequest(array('get' => self::ztURL . '?m=dept&f=manageChild&t=json', 'path_info' => self::ztURL . '/dept-manageChild.json')); + + $returnResult = $this->returnResult; + if (strpos($result, 'reload')) $returnResult = array('status' => 1, 'msg' => 'success', 'result' => array()); + + return json_encode($returnResult, JSON_UNESCAPED_UNICODE); + } + + /** + * Get user list. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getUserList($optionalParams = array(), $extraFields = array()) + { + $optionalParams = array_merge(array('param'=> 0, 'type'=> 'bydept', 'orderBy'=> 'id', 'recTotal'=> 999999, 'recPerPage' => 999999), $optionalParams); + $result = $this->setParams(array('m' => 'company', 'f' => 'browse'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'users'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Add user optional information. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getUserCreateParams($optionalParams = array(), $extraFields = array()) + { + $result = $this->setParams(array('m' => 'user', 'f' => 'create'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'depts', 'groupList', 'roleGroup'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + $result = json_decode($result->data); + $this->sessionRand = $result->rand; + return $returnResult; + } + + /** + * New users. + * + * @param array $optionalParams + * @access public + * @return string + */ + public function addUser($optionalParams = array()) + { + // Get the random number required for encryption. + $this->getUserCreateParams(); + + $optionalParams['password1'] = md5($optionalParams['password1'] . $this->sessionRand); + $optionalParams['password2'] = md5($optionalParams['password2'] . $this->sessionRand); + $optionalParams['verifyPassword'] = md5(md5(self::ztPassword) . $this->sessionRand); + $requestURL['get'] = self::ztURL . '?m=user&f=create&dept=' . $optionalParams['dept'] . '&t=json'; + $requestURL['path_info'] = self::ztURL . '/user-create-' . $optionalParams['dept'] . '.json'; + + $responseData = $this->setParams(array(), $optionalParams) + ->setRequestMethod('post') + ->sendRequest($requestURL, true); + + $returnResult = $this->getResultData($responseData); + return $returnResult; + } + + /** + * Get product list. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getProductList($optionalParams = array(), $extraFields = array()) + { + $optionalParams = array_merge(array('productID ' => 0, 'line' => 0, 'status' => 'all', 'orderBy' => 'order_desc', 'recTotal' => 999999, 'recPerPage' => 999999), $optionalParams); + $result = $this->setParams(array('m' => 'product', 'f' => 'all'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'products', 'productStats'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Get added product optional information. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getProductCreateParams($optionalParams = array(), $extraFields = array()) + { + $result = $this->setParams(array('m' => 'product', 'f' => 'create'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'products', 'lines', 'poUsers', 'qdUsers', 'rdUsers', 'groups'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Add a single product. + * + * @param array $optionalParams + * @access public + * @return string + */ + public function addProduct($optionalParams = array()) + { + $requestURL['get'] = self::ztURL . '?m=product&f=create&t=json'; + $requestURL['path_info'] = self::ztURL . '/product-create.json'; + + $responseData = $this->setParams(array(), $optionalParams) + ->setRequestMethod('post') + ->sendRequest($requestURL, true); + + $returnResult = $this->getResultData($responseData); + return $returnResult; + } + + /** + * Get project list. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getProjectList($optionalParams = array(), $extraFields = array()) + { + $optionalParams = array_merge(array('status' => 'all', 'projectID' => 0, 'orderBy' => 'order_desc', 'productID' => 0, 'recTotal' => 999999, 'recPerPage' => 999999), $optionalParams); + $result = $this->setParams(array('m' => 'project', 'f' => 'all'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'projects', 'projectStats', 'teamMembers', 'users'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Get optional information for adding items. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getProjectCreateParams($optionalParams = array(), $extraFields = array()) + { + $result = $this->setParams(array('m' => 'project', 'f' => 'create'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'projects', 'groups', 'allProducts'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Add a single item. + * + * @param array $optionalParams + * @access public + * @return string + */ + public function addProject($optionalParams = array()) + { + $requestURL['get'] = self::ztURL . '?m=project&f=create&t=json'; + $requestURL['path_info'] = self::ztURL . '/project-create.json'; + + $responseData = $this->setParams(array(), $optionalParams) + ->setRequestMethod('post') + ->sendRequest($requestURL, true); + + $returnResult = $this->getResultData($responseData); + return $returnResult; + } + + /** + * Get task list. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getTaskList($optionalParams = array(), $extraFields = array()) + { + $optionalParams = array_merge(array('projectID' => 0, 'status' => 'all', 'param' => 0, 'orderBy' => '', 'recTotal' => 999999, 'recPerPage' => 999999), $optionalParams); + $result = $this->setParams(array('m' => 'project', 'f' => 'task'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'projects', 'project', 'products', 'tasks'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Add task optional information. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getTaskCreateParams($optionalParams = array(), $extraFields = array()) + { + $result = $this->setParams(array('m' => 'task', 'f' => 'create'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'projects', 'users', 'stories', 'moduleOptionMenu', 'project'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Add a single task. + * + * @param array $optionalParams + * @access public + * @return string + */ + public function addTask($optionalParams = array()) + { + $requestURL['get'] = self::ztURL . '?m=task&f=create&project=' . $optionalParams['project'] . '&t=json'; + $requestURL['path_info'] = self::ztURL . '/task-create-' . $optionalParams['project'] . '.json'; + + $responseData = $this->setParams(array('status' => 'wait', 'after' => 'toTaskList'), $optionalParams) + ->setRequestMethod('post') + ->sendRequest($requestURL, true); + + $returnResult = $this->getResultData($responseData); + return $returnResult; + } + + /** + * Optional information for completing a single task. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getTaskFinishParams($optionalParams = array(), $extraFields = array()) + { + $result = $this->setParams(array('m' => 'task', 'f' => 'finish'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'users', 'task', 'project', 'actions'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Complete a single task. + * + * @param array $optionalParams + * @access public + * @return string + */ + public function addTaskFinish($optionalParams = array()) + { + $taskID = $optionalParams['taskID']; + unset($optionalParams['taskID']); + $requestURL['get'] = self::ztURL . '?m=task&f=finish&taskID=' . $taskID . '&t=json'; + $requestURL['path_info'] = self::ztURL . '/task-finish-' . $taskID . '.json'; + + $result = $this->setParams(array('status' => 'done'), $optionalParams) + ->setRequestMethod('post') + ->sendRequest($requestURL, false); + + $returnResult = $this->returnResult; + if (strpos($result, 'task-view-' . $taskID . '.json') || strpos($result, 'taskID=' . $taskID)) $returnResult = array('status' => 1, 'msg' => 'success', 'result' => array()); + + return json_encode($returnResult, JSON_UNESCAPED_UNICODE); + } + + /** + * Get BUG List. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getBugList($optionalParams = array(), $extraFields = array()) + { + $optionalParams = array_merge(array('productID' => 0, 'branch' => 0, 'browseType' => 'all', 'param' => 0, 'orderBy' => '', 'recTotal' => 999999, 'recPerPage' => 999999), $optionalParams); + $result = $this->setParams(array('m' => 'bug', 'f' => 'browse'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'products', 'productID', 'productName', 'product', 'moduleName', 'modules', 'browseType', 'bugs'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Add single BUG optional information. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getBugCreateParams($optionalParams = array(), $extraFields = array()) + { + $result = $this->setParams(array('m' => 'bug', 'f' => 'create'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'productID', 'productName', 'projects', 'moduleOptionMenu', 'users', 'stories', 'builds'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Add a single bug. + * + * @param array $optionalParams + * @access public + * @return string + */ + public function addBug($optionalParams = array()) + { + $requestURL['get'] = self::ztURL . '?m=bug&f=create&productID=' . $optionalParams['product'] . '&t=json'; + $requestURL['path_info'] = self::ztURL . '/bug-create-' . $optionalParams['product'] . '.json'; + + $responseData = $this->setParams(array('status' => 'active'), $optionalParams) + ->setRequestMethod('post') + ->sendRequest($requestURL, true); + + $returnResult = $this->getResultData($responseData); + return $returnResult; + } + + /** + * Optional information for solving a single bug. + * + * @param array $optionalParams + * @param array $extraFields + * @access public + * @return string + */ + public function getBugResolveParams($optionalParams = array(), $extraFields = array()) + { + $result = $this->setParams(array('m' => 'bug', 'f' => 'resolve'), $optionalParams) + ->setRequestMethod('get') + ->sendRequest(array('get' => self::ztURL), true); + + $extraFields = array_merge(array('title', 'products', 'bug', 'users', 'builds', 'actions'), $extraFields); + $returnResult = $this->getResultData($result, $extraFields); + return $returnResult; + } + + /** + * Solve a single bug. + * + * @param array $optionalParams + * @access public + * @return string + */ + public function addBugResolve($optionalParams = array()) + { + $bugID = $optionalParams['bugID']; + $requestURL['get'] = self::ztURL . '?m=bug&f=resolve&bugID=' . $bugID . '&t=json'; + $requestURL['path_info'] = self::ztURL . '/bug-resolve-' . $bugID . '.json'; + unset($optionalParams['bugID']); + + $result = $this->setParams(array('status' => 'resolved'), $optionalParams) + ->setRequestMethod('post') + ->sendRequest($requestURL); + + $returnResult = $this->returnResult; + if (strpos($result, 'bug-view-' . $bugID . '.json') || strpos($result, 'bugID=' . $bugID)) $returnResult = array('status' => 1, 'msg' => 'success', 'result' => array()); + + return json_encode($returnResult, JSON_UNESCAPED_UNICODE); + } + + /** + * Send a get request. + * + * @param string $url + * @access public + * @return string + */ + public function getUrl($url) + { + $ch = curl_init(); + if (self::ztAccessMode == 'GET') + { + $this->params = array_merge($this->params, array('t' => 'json')); + $url .= strpos($url, '?') ? http_build_query($this->params) : '?' . http_build_query($this->params); + } elseif (self::ztAccessMode == 'PATH_INFO') + { + $params = implode('-', $this->params); + $url = $url . '/' . $params . '.json'; + } + curl_setopt($ch, CURLOPT_COOKIE, $this->tokenAuth); + curl_setopt($ch, CURLOPT_REFERER, self::ztURL); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($ch); + curl_close($ch); + return $result; + } + + /** + * Send a post request. + * + * @param string $url + * @access public + * @return string + */ + public function postUrl($url) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_COOKIE, $this->tokenAuth); + curl_setopt($ch, CURLOPT_REFERER, self::ztURL); + curl_setopt($ch, CURLOPT_URL, $url); + if (count($this->params)) curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->params)); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POST, true); + $result = curl_exec($ch); + curl_close($ch); + return $result; + } + + /** + * Processing request parameters. + * + * @param array $requiredParams + * @param array $optionalParams + * @access public + * @return $this + */ + public function setParams($requiredParams = array(), $optionalParams = array()) + { + $this->params = array(); + $this->params = array_merge($requiredParams, $optionalParams); + return $this; + } + + /** + * Set request method. + * + * @param string $requestMethod + * @access public + * @return $this + */ + public function setRequestMethod($requestMethod = 'get') + { + $this->requestMethod = strcmp($requestMethod, 'get') === 0 ? 'get' : 'post'; + return $this; + } + + /** + * Send a request for response results. + * + * @param array $requestURL + * @param bool $isDecode + * @access public + * @return string $result + */ + public function sendRequest($requestURL = array(), $isDecode = false) + { + if ($this->requestMethod == 'get') $result = $this->getUrl($requestURL['get']); + if ($this->requestMethod == 'post') $result = self::ztAccessMode == 'GET' ? $this->postUrl($requestURL['get']) : $this->postUrl($requestURL['path_info']); + $result = $isDecode ? json_decode($result) : $result; + return $result; + } + + /** + * Get the specified result. + * + * @param string $responseData + * @param array $extraFields + * @access public + * @return string $result + */ + public function getResultData($responseData = '', $extraFields = array()) + { + $returnResult = $this->returnResult; + if (!empty($responseData->status) && strcmp($responseData->status, 'success') === 0) + { + $returnResult = array('status' => 1, 'msg' => 'success'); + $responseData = json_decode($responseData->data); + foreach ($extraFields as $k => $v) + { + $returnResult['result'][$v] = $responseData->$v; + } + if (count($extraFields) == 0) $returnResult['result'] = $responseData; + } elseif (!empty($responseData->result)) + { + if (strcmp($responseData->result, 'success') === 0) $returnResult = array('status' => 1, 'msg' => 'success'); + $returnResult['result'] = $responseData->message; + } + return json_encode($returnResult, JSON_UNESCAPED_UNICODE); + } +} \ No newline at end of file