diff --git a/trunk/module/common/lang/zh-cn.php b/trunk/module/common/lang/zh-cn.php index 84b458c941..1684fc90ea 100644 --- a/trunk/module/common/lang/zh-cn.php +++ b/trunk/module/common/lang/zh-cn.php @@ -32,6 +32,7 @@ $lang->colon = '::'; $lang->reset = '重填'; $lang->edit = '编辑'; $lang->delete = '删除'; +$lang->close = '关闭'; $lang->activate = '激活'; $lang->delete = '删除'; $lang->save = '保存'; @@ -48,6 +49,7 @@ $lang->year = '年'; $lang->downArrow = '↓'; $lang->goback = '返回'; $lang->selectAll = '全选'; +$lang->attatch = '附件'; /* 主导航菜单。*/ $lang->menu->index = '首页|index|index'; diff --git a/trunk/module/product/lang/zh-cn.php b/trunk/module/product/lang/zh-cn.php index b1cc07b360..5f7a801610 100644 --- a/trunk/module/product/lang/zh-cn.php +++ b/trunk/module/product/lang/zh-cn.php @@ -32,10 +32,11 @@ $lang->product->delete = "删除产品"; $lang->product->roadmap = '路线图'; -$lang->product->selectProduct = "请选择产品"; -$lang->product->saveButton = " 保存 (S) "; -$lang->product->confirmDelete = " 您确定删除该产品吗?"; -$lang->product->ajaxGetProjects= "接口:获得项目列表"; +$lang->product->selectProduct = "请选择产品"; +$lang->product->saveButton = " 保存 (S) "; +$lang->product->confirmDelete = " 您确定删除该产品吗?"; +$lang->product->ajaxGetProjects = "接口:获得项目列表"; +$lang->product->ajaxGetPlans = "接口:获得计划列表"; $lang->product->errorFormat = '产品数据格式不正确'; $lang->product->errorEmptyName = '产品名称不能为空'; diff --git a/trunk/module/product/view/browse.html.php b/trunk/module/product/view/browse.html.php index 43dc36cb9e..fe85bdd326 100644 --- a/trunk/module/product/view/browse.html.php +++ b/trunk/module/product/view/browse.html.php @@ -83,13 +83,16 @@ function search(active) recTotal}&recPerPage={$pager->recPerPage}";?> story->id);?> story->pri);?> - story->title);?> + story->title);?> story->plan);?> - story->assignedTo);?> story->openedBy);?> + story->assignedTo);?> story->estimate);?> story->status);?> - story->lastEditedDate);?> + story->stage);?> + story->closedBy);?> + story->closedReason);?> + story->lastEdited);?> action;?> @@ -102,12 +105,15 @@ function search(active) id)); else printf('%03d', $story->id);?> pri;?> - title;?> + title);?> planTitle;?> - assignedTo];?> openedBy];?> + assignedTo];?> estimate;?> - story->statusList; echo $statusList[$story->status];?> + story->statusList[$story->status];?> + story->stageList[$story->stage];?> + closedBy];?> + story->reasonList[$story->closedReason];?> lastEditedDate, 5, 11);?> createLink('story', 'edit', "story={$story->id}"), $lang->edit);?> diff --git a/trunk/module/story/control.php b/trunk/module/story/control.php index b12fc13193..83b20eab24 100644 --- a/trunk/module/story/control.php +++ b/trunk/module/story/control.php @@ -45,7 +45,7 @@ class story extends control } /* 设置产品相关数据。*/ - $product = $this->product->findByID($productID); + $product = $this->product->getById($productID); $products = $this->product->getPairs(); $users = $this->user->getPairs(); $moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'product'); @@ -66,29 +66,12 @@ class story extends control $this->display(); } - /* 编辑需求:生成表单。*/ - public function edit($storyID) + /* 变更、编辑时的共同操作。 */ + private function commonAction($storyID) { - if(!empty($_POST)) - { - $this->loadModel('action'); - $changes = $this->story->update($storyID); - if(dao::isError()) die(js::error(dao::getError())); - $files = $this->loadModel('file')->saveUpload('story', $storyID); - if($this->post->comment != '' or !empty($changes) or !empty($files)) - { - $action = !empty($changes) ? 'Edited' : 'Commented'; - $fileAction = ''; - if(!empty($files)) $fileAction = "Add Files " . join(',', $files) . "\n" ; - $actionID = $this->action->create('story', $storyID, $action, $fileAction . $this->post->comment); - $this->action->logHistory($actionID, $changes); - } - die(js::locate($this->createLink('story', 'view', "storyID=$storyID"), 'parent')); - } - /* 获取数据。*/ - $story = $this->story->findByID($storyID); - $product = $this->product->findByID($story->product); + $story = $this->story->getById($storyID); + $product = $this->product->getById($story->product); $products = $this->product->getPairs(); $users = $this->user->getPairs(); $moduleOptionMenu = $this->tree->getOptionMenu($product->id, $viewType = 'product'); @@ -97,24 +80,75 @@ class story extends control $this->product->setMenu($products, $product->id); /* 赋值到模板。*/ - $this->view->header->title = $product->name . $this->lang->colon . $this->lang->story->edit . $this->lang->colon . $story->title; $this->view->position[] = html::a($this->createLink('product', 'browse', "product=$product->id"), $product->name); - $this->view->position[] = $this->lang->story->edit; $this->view->product = $product; $this->view->products = $products; $this->view->story = $story; $this->view->users = $users; $this->view->moduleOptionMenu = $moduleOptionMenu; $this->view->plans = $this->loadModel('productplan')->getPairs($product->id); + $this->view->actions = $this->action->getList('story', $storyID); + } + + /* 编辑需求。*/ + public function edit($storyID) + { + $this->loadModel('action'); + if(!empty($_POST)) + { + $changes = $this->story->update($storyID); + if(dao::isError()) die(js::error(dao::getError())); + if($this->post->comment != '' or !empty($changes)) + { + $action = !empty($changes) ? 'Changed' : 'Commented'; + $actionID = $this->action->create('story', $storyID, $action, $this->post->comment); + $this->action->logHistory($actionID, $changes); + } + die(js::locate($this->createLink('story', 'view', "storyID=$storyID"), 'parent')); + } + + $this->commonAction($storyID); + + /* 赋值到模板。*/ + $this->view->header->title = $this->view->product->name . $this->lang->colon . $this->lang->story->edit . $this->lang->colon . $this->view->story->title; + $this->view->position[] = $this->lang->story->edit; + $this->display(); + } + + /* 变更需求。*/ + public function change($storyID) + { + $this->loadModel('action'); + if(!empty($_POST)) + { + $changes = $this->story->change($storyID); + if(dao::isError()) die(js::error(dao::getError())); + $files = $this->loadModel('file')->saveUpload('story', $storyID); + if($this->post->comment != '' or !empty($changes) or !empty($files)) + { + $action = (!empty($changes) or !empty($files)) ? 'Changed' : 'Commented'; + $fileAction = ''; + if(!empty($files)) $fileAction = "Add Files " . join(',', $files) . "\n" ; + $actionID = $this->action->create('story', $storyID, $action, $fileAction . $this->post->comment); + $this->action->logHistory($actionID, $changes); + } + die(js::locate($this->createLink('story', 'view', "storyID=$storyID"), 'parent')); + } + + $this->commonAction($storyID); + + /* 赋值到模板。*/ + $this->view->header->title = $this->view->product->name . $this->lang->colon . $this->lang->story->change . $this->lang->colon . $this->view->story->title; + $this->view->position[] = $this->lang->story->change; $this->display(); } /* 需求详情。*/ - public function view($storyID) + public function view($storyID, $version = 0) { $this->loadModel('action'); $storyID = (int)$storyID; - $story = $this->dao->findByID((int)$storyID)->from(TABLE_STORY)->fetch(); + $story = $this->story->getById($storyID, $version); $story->files = $this->loadModel('file')->getByObject('story', $storyID); $product = $this->dao->findById($story->product)->from(TABLE_PRODUCT)->fields('name, id')->fetch(); $plan = $this->dao->findById($story->plan)->from(TABLE_PRODUCTPLAN)->fetch('title'); @@ -149,13 +183,50 @@ class story extends control } else { - $story = $this->story->findById($storyID); + $story = $this->story->getById($storyID); $this->story->delete($storyID); echo js::locate($this->createLink('product', 'browse', "productID=$story->product"), 'parent'); exit; } } + /* 评审一条需求。*/ + public function review($storyID) + { + $this->loadModel('action'); + + if(!empty($_POST)) + { + $this->story->reivew($storyID); + $action = "Reviewed"; + $actionID = $this->action->create('story', $storyID, $action, $this->post->comment); + $this->action->logHistory($actionID); + die(js::locate(inlink('view', "storyID=$storyID"), 'parent')); + } + + /* 获取需求和产品信息。*/ + $story = $this->story->getById($storyID); + $product = $this->dao->findById($story->product)->from(TABLE_PRODUCT)->fields('name, id')->fetch(); + + /* 设置菜单。*/ + $this->product->setMenu($this->product->getPairs(), $product->id); + + /* 设置评审结果可选值。*/ + if($story->status == 'draft') unset($this->lang->story->reviewResultList['revert']); + + /* 导航信息。*/ + $this->view->header->title = $product->name . $this->lang->colon . $this->lang->story->view . $this->lang->colon . $story->title; + $this->view->position[] = html::a($this->createLink('product', 'browse', "product=$product->id"), $product->name); + $this->view->position[] = $this->lang->story->view; + + /* 赋值。*/ + $this->view->product = $product; + $this->view->story = $story; + $this->view->actions = $this->action->getList('story', $storyID); + $this->view->users = $this->loadModel('user')->getPairs(); + $this->display(); + } + /* 需求的任务列表。*/ public function tasks($storyID, $projectID = 0) { diff --git a/trunk/module/story/lang/zh-cn.php b/trunk/module/story/lang/zh-cn.php index e7a57411e2..ed4c1525d4 100644 --- a/trunk/module/story/lang/zh-cn.php +++ b/trunk/module/story/lang/zh-cn.php @@ -24,51 +24,71 @@ $lang->story->common = '需求'; $lang->story->create = "新增需求"; $lang->story->edit = "编辑需求"; +$lang->story->change = "变更"; $lang->story->delete = "删除需求"; $lang->story->browse = "需求列表"; $lang->story->view = "需求详情"; $lang->story->manage = "操作"; -$lang->story->tasks = "任务"; +$lang->story->tasks = "相关任务"; $lang->story->bugs = "Bug"; -$lang->story->confirmDelete = "您确认删除该需求吗?"; +$lang->story->reviewDelete = "您确认删除该需求吗?"; $lang->story->errorFormat = '需求数据有误'; $lang->story->errorEmptyTitle = '标题不能为空'; $lang->story->linkStory = '关联需求'; $lang->story->ajaxGetProjectStories = '接口:获取项目需求列表'; $lang->story->ajaxGetProductStories = '接口:获取产品需求列表'; -$lang->story->statusList->wait = '未开始'; -$lang->story->statusList->doing = '进行中'; -$lang->story->statusList->done = '已完成'; +$lang->story->statusList[''] = ''; +$lang->story->statusList['draft'] = '草稿'; +$lang->story->statusList['active'] = '激活'; +$lang->story->statusList['closed'] = '已关闭'; +$lang->story->statusList['changed'] = '已变更'; + +$lang->story->stageList[''] = ''; +$lang->story->stageList['wait'] = '未开始'; +$lang->story->stageList['planned'] = '已安排'; +$lang->story->stageList['developing'] = '研发中'; +$lang->story->stageList['developed'] = '研发完毕'; +$lang->story->stageList['testing'] = '测试中'; +$lang->story->stageList['tested'] = '测试完毕'; +$lang->story->stageList['verified'] = '已验收'; +$lang->story->stageList['released'] = '已发布'; + +$lang->story->reasonList[''] = ''; +$lang->story->reasonList['done'] = '完成'; +$lang->story->reasonList['subdivided'] = '已细分'; +$lang->story->reasonList['duplicate'] = '重复'; +$lang->story->reasonList['postponed'] = '延期'; +$lang->story->reasonList['willnotdo'] = '不做'; +$lang->story->reasonList['cancel'] = '已取消'; +//$lang->story->reasonList['isbug'] = '是个Bug'; + +$lang->story->reviewResultList[''] = ''; +$lang->story->reviewResultList['pass'] = '通过'; +$lang->story->reviewResultList['revert'] = '撤销变更'; +$lang->story->reviewResultList['reject'] = '拒绝'; $lang->story->priList[3] = '一般'; $lang->story->priList[1] = '最高'; $lang->story->priList[2] = '较高'; $lang->story->priList[4] = '最低'; -$lang->story->labProductAndModule = '产品::模块'; - -$lang->story->legendRelated = '相关信息'; $lang->story->legendBasicInfo = '基本信息'; +$lang->story->legendLifeTime = '需求的一生'; +$lang->story->legendRelated = '相关信息'; $lang->story->legendMailto = '抄送给'; $lang->story->legendAttatch = '附件'; -$lang->story->legendLinkBugs = '相关Bug'; -$lang->story->legendCases = '相关用例'; -$lang->story->legendOpenInfo = '创建信息'; -$lang->story->legendResolveInfo = '解决信息'; -$lang->story->legendCloseInfo = '关闭信息'; -$lang->story->legendProjectAndTask = '项目::任务'; +$lang->story->legendProjectAndTask = '项目任务'; +$lang->story->legendLinkStories = '相关需求'; +$lang->story->legendChildStories = '细分需求'; $lang->story->legendSpec = '需求描述'; -$lang->story->legendAction = '操作'; $lang->story->legendHistory = '历史记录'; -$lang->story->legendComment = '备注'; +$lang->story->legendVersion = '历史版本'; +$lang->story->legendMisc = '其他相关'; -$lang->story->buttonEdit = '编辑'; -$lang->story->buttonActivate = '激活'; -$lang->story->buttonResolve = '解决'; -$lang->story->buttonClose = '关闭'; -$lang->story->buttonToList = '返回'; +$lang->story->review = '评审'; +$lang->story->close = '关闭'; $lang->story->id = '编号'; $lang->story->product = '所属产品'; @@ -80,7 +100,8 @@ $lang->story->spec = '需求描述'; $lang->story->type = '需求类型 '; $lang->story->pri = '优先级'; $lang->story->estimate = '预计工时'; -$lang->story->status = '状态'; +$lang->story->status = '当前状态'; +$lang->story->stage = '所处阶段'; $lang->story->mailto = '抄送给'; $lang->story->openedBy = '由谁创建'; $lang->story->openedDate = '创建日期'; @@ -88,9 +109,17 @@ $lang->story->assignedTo = '指派给'; $lang->story->assignedDate = '指派日期'; $lang->story->lastEditedBy = '最后修改'; $lang->story->lastEditedDate = '最后修改日期'; +$lang->story->lastEdited = '最后修改'; $lang->story->closedBy = '由谁关闭'; $lang->story->closedDate = '关闭日期'; +$lang->story->closedReason = '关闭原因'; +$lang->story->reviewedBy = '由谁评审'; +$lang->story->reviewedDate = '评审时间'; $lang->story->version = '版本号'; -$lang->story->attatchment = '附件'; -$lang->story->project = '项目'; -$lang->story->plan = '计划'; +$lang->story->project = '所属项目'; +$lang->story->plan = '所属计划'; +$lang->story->comment = '备注'; +$lang->story->linkStories = '相关需求'; +$lang->story->childStories = '细分需求'; +$lang->story->duplicateStory = '重复需求'; +$lang->story->reviewResult = '评审结果'; diff --git a/trunk/module/story/model.php b/trunk/module/story/model.php index 726fa61fb6..49a2783c92 100644 --- a/trunk/module/story/model.php +++ b/trunk/module/story/model.php @@ -26,66 +26,163 @@ class storyModel extends model { /* 获取某一条需求的信息。*/ - public function findByID($storyID) + public function getById($storyID, $version = 0) { - return $this->dao->findById((int)$storyID)->from(TABLE_STORY)->fetch(); + $story = $this->dao->findById((int)$storyID)->from(TABLE_STORY)->fetch(); + if(!$story) return false; + if(substr($story->closedDate, 0, 4) == '0000') $story->closedDate = ''; + if($version == 0) $version = $story->version; + $story->spec = $this->dao->select('spec')->from(TABLE_STORYSPEC)->where('story')->eq($storyID)->andWhere('version')->eq($version)->fetch('spec'); + $story->projects = $this->dao->select('t1.project, t2.name') + ->from(TABLE_PROJECTSTORY)->alias('t1') + ->leftJoin(TABLE_PROJECT)->alias('t2') + ->on('t1.project = t2.id') + ->where('t1.story')->eq($storyID) + ->orderBy('t1.project DESC') + ->fetchPairs(); + $story->tasks = $this->dao->select('id,name,project')->from(TABLE_TASK)->where('story')->eq($storyID)->orderBy('id DESC')->fetchAll(); + //$story->bugCount = $this->dao->select('COUNT(*)')->alias('count')->from(TABLE_BUG)->where('story')->eq($storyID)->fetch('count'); + //$story->caseCount = $this->dao->select('COUNT(*)')->alias('count')->from(TABLE_CASE)->where('story')->eq($storyID)->fetch('count'); + if($story->toBug) $story->toBugTitle = $this->dao->findById($story->toBug)->from(TABLE_BUG)->fetch('title'); + if($story->plan) $story->planTitle = $this->dao->findById($story->plan)->from(TABLE_PRODUCTPLAN)->fetch('title'); + $extraStories = array(); + if($story->duplicateStory) $extraStories = array($story->duplicateStory); + if($story->linkStories) $extraStories = explode(',', $story->linkStories); + if($story->childStories) $extraStories = array_merge($extraStories, explode(',', $story->childStories)); + $extraStories = array_unique($extraStories); + if(!empty($extraStories)) $story->extraStories = $this->dao->select('id,title')->from(TABLE_STORY)->where('id')->in($extraStories)->fetchPairs(); + return $story; } /* 新增需求。*/ - function create() + public function create() { $now = date('Y-m-d H:i:s', time()); $story = fixer::input('post') ->cleanInt('product,module,pri,plan') ->cleanFloat('estimate') ->stripTags('title') - ->specialChars('spec') ->setDefault('plan', 0) ->add('openedBy', $this->app->user->account) ->add('openedDate', $now) ->add('assignedDate', 0) + ->add('version', 1) + ->add('status', 'draft') ->setIF($this->post->assignedTo != '', 'assignedDate', $now) - ->remove('files,labels') + ->join('mailto', ',') + ->remove('files,labels,spec') ->get(); - $this->dao->insert(TABLE_STORY)->data($story)->autoCheck()->check('title', 'notempty')->exec(); + $this->dao->insert(TABLE_STORY)->data($story)->autoCheck()->batchCheck('title,estimate', 'notempty')->exec(); if(!dao::isError()) { $storyID = $this->dao->lastInsertID(); $this->loadModel('file')->saveUpload('story', $storyID); + $spec = htmlspecialchars($this->post->spec); + $this->dao->insert(TABLE_STORYSPEC)->set('story')->eq($storyID)->set('version')->eq(1)->set('spec')->eq($spec)->exec(); return $storyID; } return false; } - /* 更新需求。*/ - function update($storyID) + /* 变更需求。*/ + public function change($storyID) { - $now = date('Y-m-d H:i:s', time()); - $oldStory = $this->findByID($storyID); - $story = fixer::input('post') + $now = date('Y-m-d H:i:s', time()); + $oldStory = $this->getById($storyID); + $specChanged = false; + if($this->post->spec != $oldStory->spec or $this->post->title != $oldStory->title or $this->loadModel('file')->getCount()) $specChanged = true; + + $story = fixer::input('post') + ->stripTags('title') + ->add('lastEditedBy', $this->app->user->account) + ->add('lastEditedDate', $now) + ->setIF($this->post->assignedTo != $oldStory->assignedTo, 'assignedDate', $now) + ->setIF($specChanged, 'version', $oldStory->version + 1) + ->setIF($specChanged, 'status', 'changed') + ->setIF($specChanged, 'reviewedBy', '') + ->setIF($specChanged, 'closedBy', '') + ->setIF($specChanged, 'closedReason', '') + ->setIF($specChanged and $oldStory->reviewedBy, 'reviewedDate', '') + ->setIF($specChanged and $oldStory->closedBy, 'closedDate', '') + ->remove('files,labels,spec,comment') + ->get(); + + $this->dao->update(TABLE_STORY) + ->data($story) + ->autoCheck() + ->check('title', 'notempty') + ->where('id')->eq((int)$storyID)->exec(); + if(!dao::isError()) + { + if($specChanged) + { + $spec = htmlspecialchars($this->post->spec); + $this->dao->insert(TABLE_STORYSPEC)->set('story')->eq($storyID)->set('version')->eq($oldStory->version + 1)->set('spec')->eq($spec)->exec(); + $story->spec = $this->post->spec; + } + else + { + unset($oldStory->spec); + } + return common::createChanges($oldStory, $story); + } + } + + /* 更新需求。*/ + public function update($storyID) + { + $now = date('Y-m-d H:i:s', time()); + $oldStory = $this->getById($storyID); + + $story = fixer::input('post') ->cleanInt('product,module,pri,plan') ->stripTags('title') - ->specialChars('spec') - ->remove('comment') ->add('assignedDate', $oldStory->assignedDate) ->add('lastEditedBy', $this->app->user->account) ->add('lastEditedDate', $now) ->setDefault('plan', 0) - ->setIF($this->post->assignedTo != $oldStory->assignedTo, 'assignedDate', $now) - ->remove('files,labels') + ->setDefault('status', $oldStory->status) + ->setIF($this->post->assignedTo != $oldStory->assignedTo, 'assignedDate', $now) + ->setIF($this->post->closedBy != false and $oldStory->closedDate == '', 'closedDate', $now) + ->setIF($this->post->closedReason != false and $oldStory->closedDate == '', 'closedDate', $now) + ->setIF($this->post->closedBy != false or $this->post->closedReason != false, 'status', 'closed') + ->setIF($this->post->closedReason != false and $this->post->closedBy == false, 'closedBy', $this->app->user->account) + ->setIF($oldStory->status == 'draft', 'stage', '') + ->remove('files,labels,comment') ->get(); - $this->dao->update(TABLE_STORY)->data($story)->autoCheck()->check('title', 'notempty')->where('id')->eq((int)$storyID)->exec(); + + $this->dao->update(TABLE_STORY) + ->data($story) + ->autoCheck() + ->check('title', 'notempty') + ->checkIF($story->closedBy, 'closedReason', 'notempty') + ->checkIF($story->status == 'closed', 'stage', 'notempty') + ->checkIF($story->closedReason == 'duplicate', 'duplicateStory', 'notempty') + ->checkIF($story->closedReason == 'subdivided', 'childStories', 'notempty') + ->where('id')->eq((int)$storyID)->exec(); if(!dao::isError()) return common::createChanges($oldStory, $story); } /* 删除一条需求。*/ - function delete($storyID) + public function delete($storyID) { $this->dao->delete()->from(TABLE_STORY)->where('id')->eq((int)$storyID)->limit(1)->exec(); } + + /* 评审需求。*/ + public function review($storyID) + { + $oldStory = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch(); + $story->confirmedBy = $this->app->user->account; + $story->confirmedDate = date('Y-m-d H:i:s'); + $story->status = 'active'; + if($oldStory->status == 'changed') $story->version = $oldStory->version + 1; + $this->dao->update(TABLE_STORY)->data($story)->where('id')->eq($storyID)->exec(); + return true; + } /* 获得某一个产品某一个模块下面的所有需求列表。*/ - function getProductStories($productID = 0, $moduleIds = 0, $status = 'all', $orderBy = 'id|desc', $pager = null) + public function getProductStories($productID = 0, $moduleIds = 0, $status = 'all', $orderBy = 'id|desc', $pager = null) { return $this->dao->select('t1.*, t2.title as planTitle') ->from(TABLE_STORY)->alias('t1') @@ -97,7 +194,7 @@ class storyModel extends model } /* 获得某一个产品某一个模块下面的所有需求id=>title列表。*/ - function getProductStoryPairs($productID = 0, $moduleIds = 0, $status = 'all', $order = 'id|desc') + public function getProductStoryPairs($productID = 0, $moduleIds = 0, $status = 'all', $order = 'id|desc') { $sql = $this->dao->select('t1.id, t1.title, t1.module, t2.name AS product') ->from(TABLE_STORY)->alias('t1')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id') @@ -128,7 +225,7 @@ class storyModel extends model } /* 获得某一个项目相关的所有需求列表。*/ - function getProjectStories($projectID = 0, $orderBy='id|desc', $pager = null) + public function getProjectStories($projectID = 0, $orderBy='id|desc', $pager = null) { return $this->dao->select('t1.*, t2.*')->from(TABLE_PROJECTSTORY)->alias('t1') ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id') @@ -138,7 +235,7 @@ class storyModel extends model } /* 获得某一个项目相关的需求id=>title的列表。*/ - function getProjectStoryPairs($projectID = 0, $productID = 0) + public function getProjectStoryPairs($projectID = 0, $productID = 0) { $sql = $this->dao->select('t2.id, t2.title, t2.module, t3.name AS product') ->from(TABLE_PROJECTSTORY)->alias('t1') @@ -168,7 +265,7 @@ class storyModel extends model } /* 获得指派给某一个用户的需求列表。*/ - function getUserStories($account, $status = 'all', $orderBy = 'id|desc', $pager = null) + public function getUserStories($account, $status = 'all', $orderBy = 'id|desc', $pager = null) { return $this->dao->select('t1.*, t2.title as planTitle, t3.name as productTitle') ->from(TABLE_STORY)->alias('t1') diff --git a/trunk/module/story/view/change.html.php b/trunk/module/story/view/change.html.php new file mode 100644 index 0000000000..fc8d6264d0 --- /dev/null +++ b/trunk/module/story/view/change.html.php @@ -0,0 +1,59 @@ +. + * + * @copyright Copyright: 2009 Chunsheng Wang + * @author Chunsheng Wang + * @package story + * @version $Id$ + * @link http://www.zentao.cn + */ +?> + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
story->change;?>
story->reviewedBy;?>
story->title;?>title, 'class="text-1"');?>
story->spec;?>spec, 'rows=5 class="area-1"');?>
story->comment;?>
attatch;?>fetch('file', 'buildform', 'filecount=2');?>
+ goback, inlink('view', "storyID=$story->id"));?> +
+ +
+
+ diff --git a/trunk/module/story/view/create.html.php b/trunk/module/story/view/create.html.php index 93e7d7ab4c..a254425d81 100644 --- a/trunk/module/story/view/create.html.php +++ b/trunk/module/story/view/create.html.php @@ -22,72 +22,54 @@ * @link http://www.zentao.cn */ ?> - - -
+ + +
- - + - + - - - - - + - - - + + + - + - + + + + + - + - - - - +
story->create;?>
story->product;?> - id, "onchange=loadModuleMenu(this.value); class='select-3'");?> + + id, "onchange=loadProduct(this.value); class='select-3'");?>
story->plan;?> - -
story->pri;?> - story->priList, '', 'class=select-3');?> - story->priList, '', 'class=select-3');?>
story->assignedTo;?> - user->account, 'class=select-3');?> -
story->estimate;?>
story->status;?> - story->statusList, '', 'class=select-3');?> -
story->assignedTo;?>
story->title;?>
story->spec;?>
story->mailto;?>
story->legendAttatch;?>fetch('file', 'buildform');?>fetch('file', 'buildform');?>
- - -
diff --git a/trunk/module/story/view/edit.html.php b/trunk/module/story/view/edit.html.php index 59904a3237..7221b14e28 100644 --- a/trunk/module/story/view/edit.html.php +++ b/trunk/module/story/view/edit.html.php @@ -1,12 +1,12 @@ - - + + +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
story->edit;?>
story->product;?> - product, "onchange=loadModuleMenu(this.value); class='select-3'");?> - module);?> -
story->plan;?> - plan, 'class=select-3');?> -
story->pri;?> - story->priList, $story->pri, 'class=select-3');?> -
story->openedBy;?> - openedBy, 'class=select-3');?> -
story->assignedTo;?> - assignedTo, 'class=select-3');?> -
story->estimate;?>
story->status;?> - story->statusList, $story->status, 'class=select-3');?> -
story->title;?>
story->spec;?>
comment;?>
story->legendAttatch;?>fetch('file', 'buildform');?>
- -
+
+
STORY #id . $lang->colon . $story->title;?>
+
+
+
+ +
+
+
+
+ story->legendSpec;?> +
spec);?>
+
+
+ story->comment;?> + +
+
goback, inlink('view', "storyID=$story->id"));?>
+ +
+
+ +
+
+ story->legendBasicInfo;?> + + + + + + + + + + + + + + + + + + status != 'draft'):?> + + + + + +
story->product;?>product, 'class="select-1" onchange="loadProduct(this.value)";');?>
story->module;?>module, 'class="select-1"');?>
story->plan;?>plan, 'class=select-1');?>
story->status;?>story->statusList[$story->status];?>
story->stage;?>story->stageList, $story->stage, 'class=select-1');?>
+
+
+ story->legendLifeTime;?> + + + + + + + + + + reviewedBy):?> + + + + + + closedBy):?> + + + + + + + + + +
story->openedBy;?>openedBy];?>
story->assignedTo;?>assignedTo, 'class="select-1"');?>
story->reviewedBy;?>reviewedBy, 'class="area-1"');?>
story->closedBy;?>closedBy, 'class="select-1"');?>
story->closedReason;?>story->reasonList, $story->closedReason, 'class="select-1"');?>
+
+ +
+ story->legendMisc;?> + + + + + + + + + + + + + + + + + +
story->duplicateStory;?>duplicateStory, "class='text-1'");?>
story->linkStories;?>linkStories, "class='text-1'");?>
story->childStories;?>childStories, "class='text-1'");?>
story->mailto;?>mailto, "class='area-1'");?>
+
+
+
+ diff --git a/trunk/module/story/view/header.html.php b/trunk/module/story/view/header.html.php new file mode 100644 index 0000000000..5e5af07ecb --- /dev/null +++ b/trunk/module/story/view/header.html.php @@ -0,0 +1,16 @@ + + + diff --git a/trunk/module/story/view/review.html.php b/trunk/module/story/view/review.html.php new file mode 100644 index 0000000000..fb0adc55de --- /dev/null +++ b/trunk/module/story/view/review.html.php @@ -0,0 +1,73 @@ +. + * + * @copyright Copyright: 2009 Chunsheng Wang + * @author Chunsheng Wang + * @package story + * @version $Id$ + * @link http://www.zentao.cn + */ +?> + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
title;?>
story->reviewResult;?>story->reviewResultList, '', 'class=select-3 onchange="setClosedReason(this.value)"');?>
story->assignedTo;?>openedBy, 'class=select-3');?>
story->reviewedBy;?>user->account . ', ', 'class=text-1');?>
story->comment;?>
+ + goback, inlink('view', "storyID=$story->id"));?> +
+
+ +
+ diff --git a/trunk/module/story/view/view.html.php b/trunk/module/story/view/view.html.php index 15f253f76a..e879da63a1 100644 --- a/trunk/module/story/view/view.html.php +++ b/trunk/module/story/view/view.html.php @@ -29,18 +29,18 @@
STORY #id . $lang->colon . $story->title;?>
createLink('story', 'edit', "storyID=$story->id"), $lang->story->buttonEdit); - if(common::hasPriv('product', 'browse')) - { - if($app->session->storyList != '') echo html::a($app->session->storyList, $lang->story->buttonToList); - else echo html::a($this->createLink('product', 'browse', "productID=$story->product&moduleID=$story->module"), $lang->story->buttonToList); - } + $browseLink = $app->session->storyList != false ? $app->session->storyList : $this->createLink('product', 'browse', "productID=$story->product&moduleID=$story->module"); + common::printLink('story', 'change', "storyID=$story->id", $lang->story->change); + if($story->status == 'draft' or $story->status == 'changed') common::printLink('story', 'review', "storyID=$story->id", $lang->story->review); else echo $lang->story->review . ' '; + common::printLink('story', 'close', "storyID=$story->id", $lang->close); + common::printLink('story', 'edit', "storyID=$story->id", $lang->edit); + echo html::a($browseLink, $lang->goback); ?>
-
+
@@ -52,34 +52,32 @@
files as $file) echo html::a($file->fullPath, $file->title, '_blank');?>
-
- story->legendAction;?> -
- createLink('story', 'edit', "storyID=$story->id"), $lang->story->buttonEdit); - if(common::hasPriv('product', 'browse')) - { - if($app->session->storyList != '') echo html::a($app->session->storyList, $lang->story->buttonToList); - else echo html::a($this->createLink('product', 'browse', "productID=$story->product&moduleID=$story->module"), $lang->story->buttonToList); - } - ?> -
-
+
+ id", $lang->edit); + common::printLink('story', 'review', "storyID=$story->id", $lang->story->review); + common::printLink('story', 'close', "storyID=$story->id", $lang->story->close); + echo html::a($browseLink, $lang->goback); + ?> +
+
story->legendBasicInfo;?> - +
- - + + + + - + - - - + - - - - - - - - - - - - - - + +
story->labProductAndModule;?> + story->product;?>product", $product->name);?> +
story->module;?> name; - if(!empty($modulePath)) echo $lang->arrow; foreach($modulePath as $key => $module) { - echo $module->name; + if(!common::printLink('product', 'browse', "productID=$story->product&browseType=byModule¶m=$module->id", $module->name)) echo $module->name; if(isset($modulePath[$key + 1])) echo $lang->arrow; } ?> @@ -87,95 +85,105 @@
story->plan;?>planTitle)) if(!common::printLink('productplan', 'view', "planID=$story->plan", $story->planTitle)) echo $story->planTitle;?>
story->status;?>show($lang->story->statusList, $story->status);?>story->statusList[$story->status];?>
story->assignedTo;?>assignedTo];?>
story->assignedDate;?>assignedDate;?>
story->lastEditedBy;?>lastEditedBy];?>
story->lastEditedDate;?>lastEditedDate;?>story->stage;?>story->stageList[$story->stage];?>
-
- story->legendMailto;?> -
-
- -
- story->legendOpenInfo;?> - + story->legendLifeTime;?> +
- - + + - - + + + + + + + + + + + + + + + + + +
story->openedBy;?>openedBy];?>story->openedBy;?>openedBy] . $lang->at . $story->openedDate;?>
story->openedDate;?>openedDate;?>story->assignedTo;?>assignedTo) echo $users[$story->assignedTo] . $lang->at . $story->assignedDate;?>
story->reviewedBy;?>reviewedBy); foreach($reviewedBy as $account) echo ' ' . $users[trim($account)]; ?>
story->closedBy;?>closedBy) echo $users[$story->closedBy] . $lang->at . $story->closedDate;?>
story->closedReason;?> + closedReason) echo $lang->story->reasonList[$story->closedReason]; + if($story->duplicateStory) + { + echo html::a(inlink('view', "storyID=$story->duplicateStory", '#' . $story->duplicateStory . ' ' . $story->extraStories[$story->duplicateStory])); + } + ?> +
story->lastEditedBy;?>lastEditedBy) echo $users[$story->lastEditedBy] . $lang->at . $story->lastEditedDate;?>
story->legendProjectAndTask;?> - +
- - - - - - +
story->project;?>project;?>
story->tasks;?>tasks;?> + tasks as $task) + { + $projectName = $story->projects[$task->project]; + echo html::a($this->createLink('project', 'browse', "projectID=$task->project"), $projectName); + echo html::a($this->createLink('task', 'view', "taskID=$task->id"), "#$task->id $task->name") . '
'; + } + ?> +
- -
- story->legendLinkBugs;?> -
 
+ story->legendChildStories;?> +
+ childStories) ; + foreach($childStories as $childStoryID) + { + if(isset($story->extraStories[$childStoryID])) echo html::a(inlink('view', "storyID=$childStoryID"), "#$childStoryID " . $story->extraStories[$childStoryID]) . '
'; + } + ?> +
-
- story->legendCases;?> -
 
+ story->legendMailto;?> +
mailto); foreach($mailto as $account) echo ' ' . $users[trim($account)]; ?>
+
+
+ story->legendVersion;?> +
version; $i >= 1; $i --) echo html::a(inlink('view', "storyID=$story->id&version=$i"), "#$i");?>