name = '禅道'; if($_SESSION['clientLang'] == 'zh-tw') $this->name = '禪道'; } } /** * Init bot, load model. * * @access public * @return void */ public function init() { $this->lang = $this->im->lang->im->bot->zentaoBot; foreach(array('view', 'start', 'close', 'finish') as $command) { $this->commands[] = array('command' => $command, 'alias' => $this->lang->commands->$command->alias, 'description' => $this->lang->commands->$command->description, 'internal' => true); } /* Backup user model of im module, load user module and restore. Use `$this->userModel` as user module from now on. */ $imUser = $this->im->user; $this->userModel = $this->im->loadModel('user'); $this->im->user = $imUser; $this->im->loadModel('task'); $this->im->app->loadClass('pager', $static = true); $this->users = array_filter($this->userModel->getPairs('noclosed|noletter')); $this->taskStatusList = array_filter($this->im->lang->task->statusList); $this->help = $this->lang->help; $this->inited = true; } /** * Close command. * * @param array $args bot command arguments * @access public * @return object|string */ public function close($args = array()) { $args = $this->removeEqualSign($args); foreach($args as $key => $value) { if(in_array(strtolower($value), $this->lang->condKeywords['task'])) unset($args[$key]); } $task = $this->closeTask($args); return $task; } /** * Finish command. * * @param array $args bot command arguments * @access public * @return object|string */ public function finish($args = array()) { $originArgs = $args; $args = array_filter(explode('=', implode('=', $args))); foreach($args as $key => $value) { if(in_array(strtolower($value), $this->lang->condKeywords['task'])) unset($args[$key]); } return $this->finishTask($args, $originArgs); } /** * View command. * * @param array $args bot command arguments * @param int $userID * @param object $user * @access public * @return object|string */ public function view($args = array(), $userID = 0, $user = null) { $pager = pager::init(0, 10, 1); $pageSearchRegex = $this->lang->pageSearchRegex; $pageIDKeywords = $this->lang->condKeywords['pageID']; $args = array_filter($args, function($arg) use ($pager, $pageSearchRegex, $pageIDKeywords) { $matches = array(); preg_match($pageSearchRegex, $arg, $matches); if(count($matches) > 2) { if(in_array(strtolower($matches[1]), $pageIDKeywords)) { $pager->pageID = $matches[2]; } else { $pager->recPerPage = $matches[2]; } return false; } return true; }); $originArgs = $args; $args = $this->removeEqualSign($args); foreach($args as $key => $value) { if(in_array(strtolower($value), $this->lang->condKeywords['task'])) { unset($args[$key]); $tasks = $this->viewTask($args, $user, $pager); return $this->renderTask($tasks, $originArgs, $pager); } } } /** * Parse bot command arguments. * * @param array $args bot command arguments * @param array $keys * @access public * @return object */ public function parseArguments($args = array(), $keys = array()) { $assignedToList = ''; $priList = ''; $statusList = ''; $idList = ''; $taskName = ''; $comment = ''; while(!empty($args)) { $arg = array_shift($args); if(isset($keys['pri'])) { if(in_array(strtolower($arg), $this->lang->condKeywords['pri'])) { $pris = array_shift($args); $priList = $this->buildCondParamByModifier($pris, $priList, 'p'); continue; } if(preg_match('/p[1-4]/i', $arg) == 1) { $priList = $this->buildCondParamByModifier($arg, $priList, 'p'); continue; } } if(isset($keys['id'])) { if(in_array(strtolower($arg), $this->lang->condKeywords['id'])) { $ids = array_shift($args); $idList .= $this->buildCondParamByModifier($ids, $idList, '#'); continue; } if(preg_match('/#\d+/', $arg) == 1) { $idList .= $this->buildCondParamByModifier($arg, $idList, '#'); continue; } } if(isset($keys['status'])) { if(in_array(strtolower($arg), $this->lang->condKeywords['status'])) { $statuses = array_shift($args); $statusList = $this->buildCondParam($statuses, $statusList, $this->taskStatusList); continue; } if($this->checkCondType($arg, $this->taskStatusList)) { $statusList = $this->buildCondParam($arg, $statusList, $this->taskStatusList); continue; } } if(isset($keys['assignTo'])) { if(in_array(strtolower($arg), $this->lang->condKeywords['assignTo'])) { $usernames = array_shift($args); $assignedToList = $this->buildCondParam($usernames, $assignedToList, $this->users); continue; } if($this->checkCondType($arg, $this->users)) { $assignedToList = $this->buildCondParam($arg, $assignedToList, $this->users); continue; } } if(isset($keys['comment'])) { if(in_array(strtolower($arg), $this->lang->condKeywords['comment'])) { $comment = array_shift($args); } else { $comment = $arg; } continue; } if(isset($keys['taskName'])) { if(in_array(strtolower($arg), $this->lang->condKeywords['taskName'])) { $taskName = array_shift($args); } else { $taskName = $arg; } } } $conds = new stdClass(); if(isset($keys['pri'])) $conds->priList = trim($priList, ','); if(isset($keys['assignTo'])) $conds->assignedToList = trim($assignedToList, ','); if(isset($keys['status'])) $conds->statusList = trim($statusList, ','); if(isset($keys['id'])) $conds->idList = trim($idList, ','); if(isset($keys['taskName'])) $conds->taskName = $taskName; if(isset($keys['comment'])) $conds->comment = $comment; return $conds; } /** * Check condition type. * * @param string $arg * @param array $list * @access public * @return bool */ public function checkCondType($arg, $list) { $args = explode(',', $arg); $array_inter = array_intersect($args, $list); if(!empty($array_inter)) return true; $list = array_flip($list); $array_inter = array_intersect($args, $list); if(!empty($array_inter)) return true; return false; } /** * Build condition param. * * @param string $arg bot command arguments * @param string $condParam * @param array $condArr * @access public * @return string */ public function buildCondParam($arg, $condParam, $condArr) { $arr = explode(',', $arg); foreach($arr as $cond) { if(array_key_exists($cond, $condArr)) { $condParam .= ',' . $cond; } else { $key = array_search($cond, $condArr); if($key !== false) $condParam .= ',' . $key; } } return $condParam; } /** * Remove equal sign from bot command arguments. * * @param array $args bot command arguments * @access public * @return array */ public function removeEqualSign($args) { return array_filter(explode('=', implode('=', $args))); } /** * Build condition param by modifier. * * @param array $args bot command arguments * @param string $condParam * @param string $modifier * @access public * @return string */ public function buildCondParamByModifier($arg, $condParam, $modifier) { $arg = strtolower($arg); $arg = str_replace($modifier, '', $arg); $condParam .= ',' . $arg; return $condParam; } /** * View task command. * * @param array $args bot command arguments * @param object $user * @param object $pager * @access public * @return array */ public function viewTask($args = array(), $user = null, $pager = null) { $keys = array(); foreach(array('pri', 'id', 'status', 'assignTo', 'taskName') as $key) $keys[$key] = true; if(empty($args)) { $args['assignTo'] = is_object($user) ? $user->account : false; $args['status'] = 'wait,doing,done,pause,cancel'; } $conds = $this->parseArguments($args, $keys); $conds->search = 1; $conds->order = 'status_asc'; $conds->limit = 10; if(is_object($pager)) { $conds->page = $pager->pageID; $conds->limit = $pager->recPerPage; } $result = $this->loadEntry('tasks', 'get', array(), (array)$conds); if(isset($result->tasks)) { $pager->setRecTotal($result->total); $pager->setRecPerPage($result->limit); $pager->setPageID($result->page); $pager->setPageTotal(); return $result->tasks; } return array(); } /** * Close task command. * * @param array $args bot command arguments * @access public * @return object|string */ public function closeTask($args) { $keys = array(); $keys['id'] = true; $keys['comment'] = true; $conds = $this->parseArguments($args, $keys); $tasks = array_filter(explode(',', $conds->idList)); $taskID = array_pop($tasks); $comment = $conds->comment; if(!is_numeric($taskID)) return $this->lang->errors->invalidCommand; $task = $this->loadEntry('task', 'get', array('taskID'=> $taskID)); if(empty($task)) return $this->lang->errors->emptyResult; if($task->status != 'done' && $task->status != 'cancel') return sprintf($this->lang->errors->invalidStatus, $this->taskStatusList[$task->status]); $task = $this->loadEntry('taskclose', 'post', array('taskID' => $taskID, 'comment' => $comment)); if(isset($task->result) && $task->result == 'fail') return $task->message; $link = str_replace('x.php', 'index.php', helper::createLink('task', 'view', "taskID=$taskID", 'html')); $messages = new stdClass(); $messages->type = 'url'; $messages->url = common::getSysURL() . $link; return array($this->lang->success, $messages); } /** * Finish task. * * @param array $args * @param array $originArgs * @access public * @return object|string */ private function finishTask($args, $originArgs) { $taskID = ''; $params = array(); while(!empty($args)) { $arg = array_shift($args); if(in_array(strtolower($arg), $this->lang->condKeywords['id'])) { $taskID = array_shift($args); $taskID = str_replace('#', '', $taskID); } elseif(preg_match('/#\d+/', $arg) == 1) { $taskID = str_replace('#', '', $arg); } else { parse_str(str_replace('amp;', '', end($originArgs)), $params); } } if(!is_numeric($taskID)) return $this->lang->errors->invalidCommand; $task = $this->loadEntry('task', 'get', array('taskID'=> $taskID)); if(!common::hasPriv('task', 'finish')) return $this->lang->errors->unauthorized; if(empty($task) || isset($task->error)) return $this->lang->errors->emptyResult; if($task->status != 'wait' && $task->status != 'doing') return sprintf($this->lang->errors->invalidStatus, $this->taskStatusList[$task->status]); $reply = new stdClass(); $reply->messages = array(); $reply->responses = array(); $realStarted = $this->formatDate($task, 'realStarted'); $finishedDate = $this->formatDate($task, 'finishedDate'); if(empty($params)) { $originCommand = rawurlencode($this->lang->finishCommand . ' ' . implode(' ', $originArgs)); $json = (object)array ( 'title' => "#{$taskID} {$task->name}", 'submitLabel' => $this->im->lang->task->finish, 'inputs' => array( (object)array ( 'name' => $this->im->lang->task->hasConsumed, 'type' => 'input', 'label' => $this->im->lang->task->hasConsumed, 'value' => $task->consumed, 'disabled' => true, 'addon' => $this->im->lang->task->hour, ), (object)array ( 'name' => $this->im->lang->task->currentConsumed, 'type' => 'input', 'label' => $this->im->lang->task->currentConsumed, 'value' => 0, 'addon' => $this->im->lang->task->hour, ), (object)array ( 'name' => $this->im->lang->task->realStarted, 'type' => 'datetime-local', 'label' => $this->im->lang->task->realStarted, 'value' => $realStarted, ), (object)array ( 'name' => $this->im->lang->task->finishedDate, 'type' => 'datetime-local', 'label' => $this->im->lang->task->finishedDate, 'value' => $finishedDate, ), ), ); $json = rawurlencode(json_encode($json)); $reply->messages[] = $this->lang->finish->tip; $reply->messages[] = "[{$this->lang->finish->tipLinkTitle}](xxc://openFormAndSendToServerBySendbox/$json/$originCommand)"; } else { $messageID = isset($params['messageId']) ? $params['messageId'] : ''; unset($params['messageId']); $currentConsumed = 0; foreach($params as $key => $value) { if(in_array(strtolower($key), $this->lang->condKeywords['realStarted'])) $realStarted = $value; if(in_array(strtolower($key), $this->lang->condKeywords['finishedDate'])) $finishedDate = $value; if(in_array(strtolower($key), $this->lang->condKeywords['currentConsumed'])) $currentConsumed = $value; }; if(empty($realStarted) || empty($finishedDate)) return ''; $params = array ( 'currentConsumed' => $currentConsumed, 'realStarted' => $realStarted, 'finishedDate' => $finishedDate, ); $task = $this->loadEntry('taskfinish', 'post', array_merge(array('taskID' => $taskID, 'assignedTo' => '', 'comment' => ''), $params)); if(isset($task->error)) { foreach($task->error as $error) $reply->messages[] = is_array($error) ? implode(',', $error): $error; return $reply; } $finishedDate = $this->formatDate($task, 'finishedDate', '', 'Y-m-d H:i:s'); $reply->messages[] = sprintf($this->lang->finish->done, $taskID, $finishedDate, $task->consumed); $reply->messages[] = $this->renderTaskTable(array($task)); $reply->responses[] = $this->disableMarkdownLinkInMessage($messageID); if(!empty($task->fromBug)) { $reply->messages[] = sprintf($this->lang->finish->bugTip, $taskID); $link = commonModel::getSysURL() . str_replace('x.php', 'index.php', helper::createLink('bug', 'view', "bugID={$task->fromBug}", 'html')); $href = urlencode($link); $reply->messages[] = "[{$this->lang->finish->bugTipLinkTitle}](xxc:openInApp/zentao-integrated/{$href})"; } } return $reply; } /** * Format date. * * @param object $task * @param string $field * @param string $default * @param string $format * @return string */ private function formatDate($task, $field, $default = '', $format = 'Y-m-d\TH:i') { try { $datetime = new DateTime(empty($task->$field) ? 'now' : $task->$field, new DateTimeZone('UTC')); $datetime->setTimezone(new DateTimeZone(date_default_timezone_get())); return $datetime->format($format); } catch(Exception $e) { return $default; } } /** * Render tasks as table html. * * @param array $tasks * @param array $originArgs * @param pager $pager * @return string|array */ public function renderTask($tasks, $originArgs, $pager) { $lang = $this->im->lang; $taskCount = $pager->recTotal ? $pager->recTotal : count($tasks); if($taskCount === 0) return $lang->task->noTask; if($taskCount == 1) { $task = current($tasks); $link = str_replace('x.php', 'index.php', helper::createLink('task', 'view', "taskID=$task->id", 'html')); $messages = new stdclass(); $messages->type = 'url'; $messages->url = common::getSysURL() . $link; return array(sprintf($this->lang->tasksFound, $taskCount), $messages); } $messages = array(); if($pager->pageID == 1) $messages[] = sprintf($this->lang->tasksFound, $taskCount); $taskTable = $this->renderTaskTable($tasks); $originCommand = $this->lang->viewCommand . ' ' . implode(' ', $originArgs); $paging = "$pager->pageID / $pager->pageTotal"; if($pager->pageID != 1) { $previousPageCommand = $originCommand . " {$this->lang->condKeywords['pageID'][1]}=" . ($pager->pageID - 1) . " {$this->lang->condKeywords['recPerPage'][1]}=" . $pager->recPerPage; $previousPageCommand = rawurlencode($previousPageCommand); $paging .= " [{$this->lang->prevPage}](xxc://sendContentToServerBySendbox/{$previousPageCommand})"; } if($pager->pageID != $pager->pageTotal) { $nextPageCommand = $originCommand . " {$this->lang->condKeywords['pageID'][1]}=" . ($pager->pageID + 1) . " {$this->lang->condKeywords['recPerPage'][1]}=" . $pager->recPerPage; $nextPageCommand = rawurlencode($nextPageCommand); $paging .= " [{$this->lang->nextPage}](xxc://sendContentToServerBySendbox/{$nextPageCommand})"; } $paging = "