* @package action * @version $Id$ * @link https://www.zentao.net */ class action extends control { /** * 创建一个动作或者删除所有的补丁动作,此方法由Ztools使用。 * Create a action or delete all patch actions, this method is used by the Ztools. * * @param string $objectType * @param string $actionType * @param string $objectName * @access public * @return void */ public function create($objectType, $actionType, $objectName) { $actionID = $this->action->create($objectType, 0, $actionType, '', $objectName); if($actionID) { $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess)); } else { $this->send(array('result' => 'fail', 'message' => 'error')); } } /** * 回收站。 * Trash. * * @param string $browseType * @param string $type all|hidden * @param bool $byQuery * @param int $queryID * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function trash($browseType = 'all', $type = 'all', $byQuery = false, $queryID = 0, $orderBy = 'date_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1) { /* Url存入session。 */ /* Save url into session. */ $this->actionZen->saveUrlIntoSession(); /* 保存用于替换搜索语言项的对象名称。 */ /* Save the object name used to replace the search language item. */ $this->session->set('objectName', zget($this->lang->action->objectTypes, $browseType, ''), 'admin'); /* 生成表单搜索数据。 */ /* Build the search form. */ $actionURL = $this->createLink('action', 'trash', "browseType=$browseType&type=$type&byQuery=true&queryID=myQueryID"); $this->action->buildTrashSearchForm($queryID, $actionURL); /* 分页初始化。 */ /* Load paper. */ $this->app->loadClass('pager', true); $pager = pager::init($recTotal, $recPerPage, $pageID); /* 生成排序规则。 */ /* Generate the sort rules. */ $sort = common::appendOrder($orderBy); $trashes = $byQuery ? $this->action->getTrashesBySearch($browseType, $type, $queryID, $sort, $pager) : $this->action->getTrashes($browseType, $type, $sort, $pager); $objectTypeList = array_keys($this->action->getTrashObjectTypes($type)); /* 获取头部模块标题导航。 */ /* Build the header navigation title. */ $preferredType = $this->actionZen->getTrashesHeaderNavigation($objectTypeList); /* 初始化项目、产品、执行列表。 */ /* Initialize the project, product, execution list. */ $projectList = array(); $productList = array(); $executionList = array(); /* 获取执行所属的项目名称。 */ /* Get the projects name of executions. */ if($browseType == 'execution') $this->view->projectList = $projectList = $this->loadModel('project')->getByIdList(array_column($trashes, 'project'), 'all'); /* 获取用户故事所属的产品名称。 */ /* Get the products name of story. */ if(in_array($browseType, array('story', 'requirement'))) { $storyIdList = array(); foreach($trashes as $trash) $storyIdList[] = $trash->objectID; $this->view->productList = $this->loadModel('story')->getByList($storyIdList, 'all'); } /* 获取任务的执行名称。 */ /* Get the executions name of task. */ if($browseType == 'task') $this->view->executionList = $executionList = $this->loadModel('execution')->getByIdList(array_column($trashes, 'execution'), 'all'); /* 补充操作记录的信息。 */ /* Supplement the information recorded by the operation. */ foreach($trashes as $trash) $this->actionZen->processTrash($trash, $projectList, $productList, $executionList); $this->view->title = $this->lang->action->trash; $this->view->trashes = $trashes; $this->view->type = $type; $this->view->currentObjectType = $browseType; $this->view->orderBy = $orderBy; $this->view->pager = $pager; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->preferredType = $preferredType; $this->view->byQuery = $byQuery; $this->view->queryID = $queryID; $this->display(); } /** * 恢复一个回收站对象。 * Undelete an object. * * @param int $actionID * @param string $browseType * @param string $confirmChange * @access public * @return void */ public function undelete($actionID, $browseType = 'all', $confirmChange = 'no') { $result = $this->actionZen->checkActionExist($actionID); if(is_array($result) && $result['result'] == 'fail') return $this->send($result); $oldAction = $result; /* 当对象类型为program、project、execution、product时,需要检查是否有重复的对象。 */ /* When the object type is program, project, execution, product, you need to check if there are duplicate objects. */ if(in_array($oldAction->objectType, array('program', 'project', 'execution', 'product', 'build'))) { $table = $this->config->objectTables[$oldAction->objectType]; list($repeatObject, $object) = $this->action->getRepeatObject($oldAction, $table); if($repeatObject) { list($replaceName, $replaceCode) = $this->actionZen->getReplaceNameAndCode($repeatObject, $object, $table); if($confirmChange == 'no') { $message = $this->actionZen->getConfirmNoMessage($repeatObject, $object, $oldAction, $replaceName, $replaceCode); if($message) { $url = $this->createLink('action', 'undelete', "action={$actionID}&browseType={$browseType}&confirmChange=yes"); return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$message}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'}).then((res) => {if(res) $.ajaxSubmit({url: '{$url}'}); });")); } } elseif($confirmChange == 'yes') { $this->actionZen->recoverObject($repeatObject, $object, $replaceName, $replaceCode, $table, $oldAction); } } if($oldAction->objectType == 'execution') { $confirmLang = $this->actionZen->restoreStages($oldAction, $confirmChange); $url = $this->createLink('action', 'undelete', "action={$actionID}&browseType={$browseType}&confirmChange=yes"); if($confirmLang !== true) return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$confirmLang}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'}).then((res) => {if(res) $.ajaxSubmit({url: '{$url}'});});")); } } if($oldAction->objectType == 'module' && $confirmChange == 'no') { $module = $this->loadModel('tree')->getById($oldAction->objectID); $repeatName = $this->tree->checkUnique($module); if($module->type == 'doc' && $module->parent > 0 && !$repeatName) { $parents = $this->tree->getParents($oldAction->objectID, true); $isDeletedParent = false; foreach($parents as $parent) { if($parent->id == $oldAction->objectID) continue; if($parent->deleted) { $isDeletedParent = true; break; } } if($isDeletedParent) { $url = $this->createLink('action', 'undelete', "action={$actionID}&browseType={$browseType}&confirmChange=yes"); return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$this->lang->action->undeleteModuleTip}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'}).then((res) => {if(res) $.ajaxSubmit({url: '{$url}'});});")); } } } elseif($oldAction->objectType == 'task' && $confirmChange == 'no') { $task = $this->loadModel('task')->getById($oldAction->objectID); $isDeleted = $this->dao->select('deleted')->from(TABLE_EXECUTION)->where('id')->eq($task->execution)->fetch('deleted'); $url = $this->createLink('action', 'undelete', "action={$actionID}&browseType={$browseType}&confirmChange=yes"); if($isDeleted) return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$this->lang->action->undeleteTaskTip}'}).then((res) => {if(res) $.ajaxSubmit({url: '{$url}'});});")); } elseif($oldAction->objectType == 'board') { $board = $this->loadModel('board')->getCanvas($oldAction->objectID); $isDeleted = $this->dao->select('deleted')->from(TABLE_BOARD_LIB)->where('id')->eq($board->lib)->fetch('deleted'); $url = $this->createLink('action', 'trash', "browseType=boardspace"); if($isDeleted) return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$this->lang->action->undeleteBoardTip}'}).then((res) => {if(res) loadPage('{$url}');});")); } elseif($oldAction->objectType == 'doctemplate') { $templateScopeAndType = $this->dao->select('id,lib,module')->from(TABLE_DOC)->where('id')->eq($oldAction->objectID)->fetchAll('id'); $scopeID = zget($templateScopeAndType[$oldAction->objectID], 'lib', 0); $typeID = zget($templateScopeAndType[$oldAction->objectID], 'module', 0); if($confirmChange == 'no') { $isDeletedType = $this->dao->select('deleted')->from(TABLE_MODULE)->where('type')->eq('docTemplate')->andWhere('id')->eq($typeID)->fetch('deleted'); $isDeletedScope = $this->dao->select('deleted')->from(TABLE_DOCLIB)->where('type')->eq('template')->andWhere('id')->eq($scopeID)->fetch('deleted'); if($isDeletedType == '1' || $isDeletedScope == '1') { $url = $this->createLink('action', 'undelete', "action={$actionID}&browseType={$browseType}&confirmChange=yes"); return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$this->lang->action->undeleteTemplateTip}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'}).then((res) => {if(res) $.ajaxSubmit({url: '{$url}'});});")); } } if($confirmChange == 'yes') { $this->dao->update(TABLE_DOCLIB)->set('deleted')->eq('0')->where('id')->eq($scopeID)->exec(); $this->dao->update(TABLE_MODULE)->set('deleted')->eq('0')->where('id')->eq($typeID)->exec(); $parentType = $this->dao->select('parent')->from(TABLE_MODULE)->where('id')->eq($typeID)->fetch('parent'); if(!empty($parentType)) $this->dao->update(TABLE_MODULE)->set('deleted')->eq('0')->where('id')->eq($parentType)->exec(); } } $result = $this->action->undelete($actionID); if(true !== $result) return $this->send(array('result' => 'fail', 'load' => array('confirm' => $result))); $extra = $oldAction->extra == actionModel::BE_HIDDEN ? 'hidden' : 'all'; $sameTypeObjects = $this->action->getTrashes($oldAction->objectType, $extra, 'id_desc', null); $browseType = ($sameTypeObjects && $browseType != 'all') ? $oldAction->objectType : 'all'; return $this->send(array('result' => 'success', 'load' => $this->createLink('action', 'trash', "browseType=$browseType&type=$extra"))); } /** * 隐藏一个已经被删除的对象。 * Hide an deleted object. * * @param int $actionID * @param string $browseType * @access public * @return void */ public function hideOne($actionID, $browseType = 'all') { $result = $this->actionZen->checkActionExist($actionID); if(is_array($result) && isset($result['result']) && $result['result'] == 'fail') return $this->send($result); $this->action->hideOne($actionID); $oldAction = $result; $sameTypeObjects = $this->action->getTrashes($oldAction->objectType, 'all', 'id_desc', null); $browseType = ($sameTypeObjects && $browseType != 'all') ? $oldAction->objectType : 'all'; return $this->send(array('result' => 'success', 'load' => $this->createLink('action', 'trash', "browseType={$browseType}"))); } /** * 隐藏所有被删除的对象。 * Hide all deleted objects. * * @param string $confirm yes|no * @access public * @return void */ public function hideAll($confirm = 'no') { if($confirm == 'no') { $url = inlink('hideAll', "confirm=yes"); $message = $this->lang->action->confirmHideAll; return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$message}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'}).then((res) => {if(res) $.ajaxSubmit({url: '{$url}'});});")); } $this->action->hideAll(); return $this->send(array('result' => 'success', 'load' => true)); } /** * 评论。 * Comment. * * @param string $objectType * @param int $objectID * @access public * @return void */ public function comment($objectType, $objectID) { if(!empty($_POST)) { $isInZinPage = isInModal() || in_array($objectType, $this->config->action->newPageModule); /* 当评论的是任务,需判断当前用户是否拥有任务的权限。 */ /* When commenting on a task, you need to determine whether the current user has the permission of the task. */ if(strtolower($objectType) == 'task') { $task = $this->loadModel('task')->getById($objectID); $executions = explode(',', $this->app->user->view->sprints); if(!in_array($task->execution, $executions)) return $this->send(array('result' => 'fail', 'message' => $this->lang->error->accessDenied)); } /* 当评论的是用户故事,需判断当前用户是否有此用户故事的权限。 */ /* When commenting on a story, you need to determine whether the current user has the permission of the story. */ elseif(strtolower($objectType) == 'story') { $story = $this->loadModel('story')->getById($objectID); $executions = explode(',', $this->app->user->view->sprints); $products = explode(',', $this->app->user->view->products); if(!array_intersect(array_keys($story->executions), $executions) && !in_array($story->product, $products) && empty($story->lib)) return $this->send(array('result' => 'fail', 'message' => $this->lang->error->accessDenied)); } /* 获取评论内容并生成一条action数据。 */ $commentData = form::data($this->config->action->form->comment) ->setIF($this->post->comment, 'actioncomment', $this->post->comment) ->get(); if(!$commentData->actioncomment && $_FILES) { $files = array_filter($_FILES['files']['name']); if(!empty($files)) $commentData->actioncomment = implode(',', $files); } if($commentData->actioncomment) { $actionID = $this->action->create($objectType, $objectID, 'Commented', $commentData->actioncomment); if(empty($actionID)) { if($isInZinPage) return $this->send(array('result' => 'fail', 'message' => $this->lang->error->accessDenied)); return print(js::error($this->lang->error->accessDenied)); } if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success', 'data' => $actionID)); } if($isInZinPage) return $this->send(array('status' => 'success', 'closeModal' => true, 'callback' => array('name' => 'zui.HistoryPanel.update', 'params' => array('objectType' => $objectType, 'objectID' => (int)$objectID)))); /* 用于旧页面。*/ /* For oldPage. */ return print(js::reload('parent')); } $this->view->title = $this->lang->action->create; $this->view->objectType = $objectType; $this->view->objectID = $objectID; $this->display(); } /** * 编辑评论。 * Edit comment. * * @param int $actionID * @access public * @return void */ public function editComment($actionID) { $action = $this->action->getById($actionID); if(!empty($_POST)) { /* 获取表单内的数据。 */ /* Get form data. */ $commentData = form::data($this->config->action->form->editComment)->get(); $error = false; /* 判断是否符合更新的条件。 */ /* Determine whether the update conditions are met. */ if(strlen(trim(strip_tags($commentData->lastComment, ''))) != 0) { $error = $this->action->updateComment($actionID, $commentData); } if(!$error) { /* 不符合更新条件,返回错误。 */ /* The update conditions are not met and an error is returned. */ dao::$errors['submit'][] = $this->lang->action->historyEdit; return $this->send(array('result' => 'fail', 'message' => dao::getError())); } $action = $this->action->getById($actionID); if(isInModal() || in_array($action->objectType, $this->config->action->newPageModule)) { return $this->send(array('status' => 'success', 'closeModal' => true, 'callback' => array('name' => 'zui.HistoryPanel.update', 'params' => array('objectType' => $action->objectType, 'objectID' => (int)$action->objectID)))); } return $this->send(array('result' => 'success', 'locate' => 'reload')); } $action = $this->loadModel('file')->replaceImgURL($action, 'comment'); $this->view->title = $this->lang->action->editComment; $this->view->actionID = $actionID; $this->view->objectType = $action->objectType; $this->view->comment = $this->action->formatActionComment($action->comment); $this->view->files = $this->file->getByObject('comment', $actionID); $this->display(); } /** * 通过 Ajax 获取操作记录列表。 * Get action list by ajax. * * @param string $objectType * @param int $objectID * @access public * @return void */ public function ajaxGetList($objectType, $objectID) { $this->app->loadLang($objectType); $actions = $this->action->getList($objectType, $objectID); $actions = $this->action->buildActionList($actions); return $this->send($actions); } /** * Clear dynamic records older than one month. * * @access public * @return void */ public function cleanActions() { $this->action->cleanActions(); } /** * 通过 Ajax 获取该日期的更多记录。 * Get more actions by ajax. * * @param int $lastActionID * @access public * @return void */ public function ajaxGetMoreActions($lastActionID) { $actions = $this->action->getMoreActions($lastActionID); $lastAction = end($actions); $this->view->actions = $actions; $this->view->hasMore = $lastAction ? $this->action->hasMoreAction($lastAction) : false; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->display(); } }