diff --git a/api/v1/entries/issue.php b/api/v1/entries/issue.php new file mode 100644 index 0000000000..8716099236 --- /dev/null +++ b/api/v1/entries/issue.php @@ -0,0 +1,51 @@ +loadController('issue', 'view'); + $control->view($issueID); + + $data = $this->getData(); + if(!$data or (isset($data->message) and $data->message == '404 Not found')) return $this->send404(); + if(isset($data->status) and $data->status == 'success') $this->send(200, $this->format($data->data->issue, 'createdDate:time,editedDate:time,assignedDate:time')); + if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message); + + $this->sendError(400, 'error'); + } + + public function put($issueID) + { + $oldIssue = $this->loadModel('issue')->getByID($issueID); + + /* Set $_POST variables. */ + $fields = 'type,title,severity,pri,assignedTo,deadline,desc'; + $this->batchSetPost($fields, $oldIssue); + + $control = $this->loadController('issue', 'edit'); + $control->edit($issueID); + + $data = $this->getData(); + if(!$data or (isset($data->message) and $data->message == '404 Not found')) return $this->send404(); + if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message); + + $issue = $this->issue->getByID($issueID); + $this->send(200, $this->format($issue, 'createdDate:time,editedDate:time,assignedDate:time')); + } + + public function delete($issueID) + { + $control = $this->loadController('issue', 'delete'); + $control->delete($issueID, 'true'); + + $this->getData(); + $this->sendSuccess(200, 'success'); + } +} diff --git a/api/v1/entries/issues.php b/api/v1/entries/issues.php new file mode 100644 index 0000000000..3cfb8d9df4 --- /dev/null +++ b/api/v1/entries/issues.php @@ -0,0 +1,50 @@ +loadController('my', 'issue'); + $control->issue($this->param('type', 'assignedTo'), $this->param('order', 'id_desc'), $this->param('total', 0), $this->param('limit', 20), $this->param('page', 1)); + $data = $this->getData(); + + if(!isset($data->status)) return $this->sendError(400, 'error'); + if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message); + + $pager = $data->data->pager; + $result = array(); + foreach($data->data->issues as $issue) + { + $result[] = $this->format($issue, 'createdDate:time,editedDate:time,assignedDate:time'); + } + return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'issues' => $result)); + } + + public function post($projectID = 0) + { + if((int) $projectID <= 0) $this->sendError(400, 'The id of project is wrong.'); + + $fields = 'type,title,severity,pri,assignedTo,deadline,desc'; + $this->batchSetPost($fields); + + $control = $this->loadController('issue', 'create'); + $this->requireFields('type,title,severity'); + + $control->create($projectID); + + $data = $this->getData(); + if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message); + if(isset($data->result) and!isset($data->id)) return $this->sendError(400, $data->message); + + $issue = $this->loadModel('issue')->getByID($data->id); + + $this->send(201, $this->format($issue, 'createdDate:time,editedDate:time,assignedDate:time')); + } +} diff --git a/config/filter.php b/config/filter.php index f5da683e22..4aec7e207e 100644 --- a/config/filter.php +++ b/config/filter.php @@ -222,7 +222,8 @@ $filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy'; $filter->project->task->cookie['windowWidth'] = 'int'; $filter->project->export->cookie['checkedItem'] = 'reg::checked'; -$filter->projectstory->story->cookie['storyModuleParam'] = 'int'; +$filter->projectstory->story->cookie['storyModuleParam'] = 'int'; +$filter->projectstory->story->cookie['pagerProductBrowse'] = 'int'; $filter->qa->default->cookie['lastProduct'] = 'int'; $filter->qa->default->cookie['preBranch'] = 'int'; diff --git a/config/routes.php b/config/routes.php index ce9d1326b1..32f5743b6e 100644 --- a/config/routes.php +++ b/config/routes.php @@ -55,4 +55,8 @@ $routes['/projects/:project/risks'] = 'risks'; $routes['/risks'] = 'risks'; $routes['/risks/:id'] = 'risk'; +$routes['/projects/:project/questions'] = 'issues'; +$routes['/questions'] = 'issues'; +$routes['/questions/:id'] = 'issue'; + $config->routes = $routes; diff --git a/framework/base/control.class.php b/framework/base/control.class.php index ac94b47fc7..4a20b7cd9e 100644 --- a/framework/base/control.class.php +++ b/framework/base/control.class.php @@ -897,7 +897,8 @@ class baseControl foreach($data as $key => $value) { if(!is_string($value)) continue; - $data[$key] = urlencode($value); + /* Retain ["] for json encode when value is jsoned string. */ + $data[$key] = str_replace('%22', '"', urlencode($value)); } print(urldecode(json_encode($data))); diff --git a/lib/front/front.class.php b/lib/front/front.class.php index 102c8cb7bf..671e077dd2 100644 --- a/lib/front/front.class.php +++ b/lib/front/front.class.php @@ -211,6 +211,115 @@ class html extends baseHTML $value = str_replace("'", ''', $value); return "\n"; } + + /** + * Convert a string to a uni code + * + * @param string $string + * @return int + */ + static public function stringToCode($string) + { + $stringLength = strlen($string); + if($stringLength == 0) return 0; + + $code = 0; + for($i = 0; $i < $stringLength; ++$i) + { + $code += ($i + 1) * ord($string[$i]); + } + return $code; + } + + /** + * Create user avatar. + * + * @param string|object|array $user User object or user avatar address or user account + * @param string|int $size Avatar size, can be a number or preset sizes: "xs", "sm", "", "lg", "xl", default is "" + * @param string $className Avatar element class name, default is "avatar-circle" + * @param string $attrib Extra attributes on avatar element + * @param string $tag Avatar element tag name, default is "div" + * @static + * @access public + * @return string + */ + static public function avatar($user, $size = '', $className = 'avatar-circle', $attrib = '', $tag = 'div') + { + if(is_string($user)) + { + $userObj = new stdClass(); + if(strlen($user) > 1) $userObj->avatar = $user; + else $userObj->account = $user; + $user = $userObj; + } + else if(is_array($user)) + { + $userObj = new stdClass(); + $userObj->avatar = $user['avatar']; + $userObj->account = $user['account']; + $user = $userObj; + } + + $hasImage = !empty($user->avatar); + + $extraClassName = $hasImage ? ' has-img' : ' has-text'; + $style = ''; + + if($size) + { + if(is_numeric($size)) $style .= "width: $size" . "px; height: $size" . "px; line-height: $size" . 'px;'; + $extraClassName .= " avatar-$size"; + } + + if(!$hasImage) + { + $colorHue = (html::stringToCode($user->account) * 43) % 360; + $style .= "background: hsl($colorHue, 100%, 58%);"; + } + + if(!empty($style)) $style = "style='$style'"; + + $html = "<$tag class='avatar$extraClassName $className' $attrib $style>"; + + if($hasImage) $html .= html::image($user->avatar); + else $html .= '' . strtoupper($user->account[0]) . ''; + + $html .= "$tag>"; + + return $html; + } + + /** + * Create a small user avatar. + * + * @param string|object $user User object or user avatar address or user account + * @param string $className Avatar element class name, default is "avatar-circle" + * @param string $attrib Extra attributes on avatar element + * @param string $tag Avatar element tag name, default is "div" + * @static + * @access public + * @return string + */ + static public function smallAvatar($user, $className = 'avatar-circle', $attrib = '', $tag = 'div') + { + return html::avatar($user, 'sm', $className, $attrib, $tag); + } + + /** + * Create a large user avatar. + * + * @param string|object $user User object or user avatar address or user account + * @param string $className Avatar element class name, default is "avatar-circle" + * @param string $attrib Extra attributes on avatar element + * @param string $tag Avatar element tag name, default is "div" + * @static + * @access public + * @return string + */ + static public function largeAvatar($user, $className = 'avatar-circle', $attrib = '', $tag = 'div') + { + return html::avatar($user, 'lg', $className, $attrib, $tag); + } } /** diff --git a/module/block/view/recentprojectblock.html.php b/module/block/view/recentprojectblock.html.php index b0fa93205c..f4bdb3bdf8 100644 --- a/module/block/view/recentprojectblock.html.php +++ b/module/block/view/recentprojectblock.html.php @@ -23,7 +23,7 @@ #cards .project-detail .progress-text-left .progress-text {width: 50px; left: -50px;} #cards .panel-heading {cursor: pointer;} #cards .project-stages-container {margin: 0 0 -16px 0; padding: 0 4px; height: 46px; overflow-x: auto; position: relative;} -#cards .project-stages:after {content: ' '; width: 30px; display: block; right: -16px; top: 16px; bottom: -6px; z-index: 1; background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%); position: absolute;} +#cards .project-stages:after {content: ' '; width: 30px; display: block; right: -5px; top: 16px; bottom: -5px; z-index: 1; background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%); position: absolute;} #cards .project-stages-row {position: relative; height: 30px; z-index: 0;} #cards .project-stage-item {white-space: nowrap; position: absolute; top: 0; min-width: 48px; padding-top: 13px; color: #838A9D;} #cards .project-stage-item > div {white-space: nowrap; overflow: visible; text-align: center; text-overflow: ellipsis;} @@ -73,7 +73,7 @@ project->hoursUnit, $project->estimate); ?> systemMode == 'new'):?> - model === 'waterfall'): ?> + model !== 'waterfall'): ?>
| - color) ? '#333' : $bug->color;?> - config->mail, 'domain', common::getSysURL()) . helper::createLink('bug', 'view', "bugID=$bug->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?> + color) ? '#333' : $object->color;?> + config->mail, 'domain', common::getSysURL()) . helper::createLink('bug', 'view', "bugID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?> |