diff --git a/module/my/view/program.html.php b/module/my/view/program.html.php
index a4a2e52c92..6f1cee7213 100644
--- a/module/my/view/program.html.php
+++ b/module/my/view/program.html.php
@@ -43,7 +43,7 @@
| id;?> |
code;?> |
- createLink('program', 'index', "programID=$program->id"), $program->name);?> |
+ createLink('program', 'index', "programID=$program->id", '', '', $program->id), $program->name);?> |
begin;?> |
end;?> |
diff --git a/module/product/js/browse.js b/module/product/js/browse.js
index 095bd1e2a0..40326da473 100644
--- a/module/product/js/browse.js
+++ b/module/product/js/browse.js
@@ -1,5 +1,8 @@
$(function()
{
+ $('#subNavbar .nav li').removeClass('active');
+ $("#subNavbar .nav li[data-id=" + storyType + ']').addClass('active');
+
if($('#storyList thead th.c-title').width() < 150) $('#storyList thead th.c-title').width(150);
$('#storyList td.has-child .story-toggle').each(function()
{
diff --git a/module/product/view/browse.html.php b/module/product/view/browse.html.php
index 6d52f38860..f5aa3b4af6 100644
--- a/module/product/view/browse.html.php
+++ b/module/product/view/browse.html.php
@@ -23,6 +23,8 @@ $config->product->browse->unfoldID = zget($config->product->browse->unfoldID, $p
js::set('unfoldID', $config->product->browse->unfoldID);
js::set('unfoldAll', $lang->project->treeLevel['all']);
js::set('foldAll', $lang->project->treeLevel['root']);
+js::set('storyType', $storyType);
+$lang->story->create = $storyType == 'story' ? $lang->story->createStory : $lang->story->createRequirement;
?>
- createLink('story', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID"), " {$lang->story->batchCreate}", '', "class='btn btn btn-secondary'");?>
+ createLink('story', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&storyID=0&project=0&plan=0&type=$storyType"), " {$lang->story->batchCreate}", '', "class='btn btn btn-secondary'");?>
project->treeLevel['root']);
}
else
{
- $link = $this->createLink('story', 'create', "productID=$productID&branch=$branch&moduleID=$moduleID");
+ $link = $this->createLink('story', 'create', "productID=$productID&branch=$branch&moduleID=$moduleID&storyID=0&projectID=0&bugID=0&planID=0&todoID=0&extra=&type=$storyType");
if(common::hasPriv('story', 'create')) echo html::a($link, " {$lang->story->create}", '', "class='btn btn-primary'");
}
?>
@@ -120,7 +122,7 @@ js::set('foldAll', $lang->project->treeLevel['root']);
}
else
{
- $link = $this->createLink('story', 'create', "product=$productID&branch=$branch&moduleID=$moduleID");
+ $link = $this->createLink('story', 'create', "product=$productID&branch=$branch&moduleID=$moduleID&storyID=0&projectID=0&bugID=0&planID=0&todoID=0&extra=&type=$storyType");
$disabled = '';
if(!common::hasPriv('story', 'create'))
{
@@ -134,7 +136,7 @@ js::set('foldAll', $lang->project->treeLevel['root']);
>
createLink('story', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID");
+ $batchLink = $this->createLink('story', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&storyID=0&project=0&plan=0&type=$storyType");
echo "" . html::a($batchLink, "" . $lang->story->batchCreate) . "";
?>
@@ -168,7 +170,7 @@ js::set('foldAll', $lang->project->treeLevel['root']);
story->noStory;?>
- createLink('story', 'create', "productID={$productID}&branch={$branch}&moduleID={$moduleID}"), " " . $lang->story->create, '', "class='btn btn-info'");?>
+ createLink('story', 'create', "productID={$productID}&branch={$branch}&moduleID={$moduleID}&storyID=0&projectID=0&bugID=0&planID=0&todoID=0&extra=&type=$storyType"), " " . $lang->story->create, '', "class='btn btn-info'");?>
diff --git a/module/program/config.php b/module/program/config.php
index 96c7144e8e..296bc728b4 100644
--- a/module/program/config.php
+++ b/module/program/config.php
@@ -11,3 +11,8 @@ $config->program->editor->suspend = array('id' => 'comment', 'tools' => 'simple
$config->program->list = new stdclass();
$config->program->list->exportFields = 'id,name,code,template,category,status,begin,end,budget,PM,end,desc';
+
+$config->program->create = new stdclass();
+$config->program->edit = new stdclass();
+$config->program->create->requiredFields = 'name,code,begin,end';
+$config->program->edit->requiredFields = 'name,code,begin,end';
diff --git a/module/program/control.php b/module/program/control.php
index 76ecc3157a..be71345a23 100644
--- a/module/program/control.php
+++ b/module/program/control.php
@@ -44,6 +44,8 @@ class program extends control
{
if(common::hasPriv('program', 'createGuide')) $this->lang->pageActions = html::a($this->createLink('program', 'createGuide'), " " . $this->lang->program->create, '', "class='btn btn-primary' data-toggle=modal");
+ $this->app->session->set('programList', $this->app->getURI(true));
+
$programType = $this->cookie->programType ? $this->cookie->programType : 'bylist';
$this->app->loadClass('pager', $static = true);
@@ -151,30 +153,33 @@ class program extends control
/**
* Edit a program.
*
- * @param int $projectID
+ * @param int $programID
* @access public
* @return void
*/
- public function edit($projectID = 0)
+ public function edit($programID = 0)
{
- $project = $this->project->getByID($projectID);
+ $program = $this->project->getByID($programID);
if($_POST)
{
- $changes = $this->project->update($projectID);
+ $changes = $this->program->update($programID);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => $this->processErrors(dao::getError())));
if($changes)
{
- $actionID = $this->loadModel('action')->create('project', $projectID, 'edited');
+ $actionID = $this->loadModel('action')->create('program', $programID, 'edited');
$this->action->logHistory($actionID, $changes);
}
- $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
+
+ $url = $this->session->programList ? $this->session->programList : inlink('browse');
+ $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $url));
}
- $this->view->pmUsers = $this->loadModel('user')->getPairs('noclosed|nodeleted|pmfirst', $project->PM);
- $this->view->title = $this->lang->project->edit;
- $this->view->position[] = $this->lang->project->edit;
- $this->view->project = $project;
+ $this->view->pmUsers = $this->loadModel('user')->getPairs('noclosed|nodeleted|pmfirst', $program->PM);
+ $this->view->title = $this->lang->program->edit;
+ $this->view->position[] = $this->lang->program->edit;
+ $this->view->program = $program;
+ $this->view->parents = $this->program->getParentPairs();
$this->view->groups = $this->loadModel('group')->getPairs();
$this->display();
}
@@ -689,7 +694,7 @@ class program extends control
public function ajaxGetDropMenu($programID, $module, $method, $extra)
{
$this->loadModel('project');
- $this->view->link = $this->createLink('program', 'index', "programID=$programID");
+ $this->view->link = $this->createLink('program', 'index', "programID=$programID", '', '', $programID);
$this->view->programID = $programID;
$this->view->module = $module;
$this->view->method = $method;
diff --git a/module/program/lang/zh-cn.php b/module/program/lang/zh-cn.php
index 2424e68554..062e9de845 100644
--- a/module/program/lang/zh-cn.php
+++ b/module/program/lang/zh-cn.php
@@ -103,3 +103,5 @@ $lang->program->cmmiDesc = '简介:按阶段规范
$lang->program->cannotCreateChild = '该项目已经有实际的内容,无法直接添加子项目。您可以为当前项目创建一个父项目,然后在新的父项目下面添加子项目。';
$lang->program->hasChildren = '该项目有子项目存在,不能删除。';
$lang->program->confirmDelete = "您确定删除项目[%s]吗?";
+$lang->program->cannotChangeToCat = "该项目已经有实际的内容,无法修改为父项目";
+$lang->program->cannotCancelCat = "该项目下已经有子项目,无法取消父项目标记";
diff --git a/module/program/model.php b/module/program/model.php
index 4a8bfc9e15..82f796acd6 100644
--- a/module/program/model.php
+++ b/module/program/model.php
@@ -1,6 +1,15 @@
dao->select('*')->from(TABLE_PROJECT)
@@ -20,6 +29,12 @@ class programModel extends model
->fetchAll('id');
}
+ /**
+ * Get program pairs.
+ *
+ * @access public
+ * @return void
+ */
public function getPairs()
{
return $this->dao->select('id, name')->from(TABLE_PROJECT)
@@ -30,6 +45,13 @@ class programModel extends model
->fetchPairs();
}
+ /**
+ * Get program pairs by template.
+ *
+ * @param varchar $template
+ * @access public
+ * @return void
+ */
public function getPairsByTemplate($template)
{
return $this->dao->select('id, name')->from(TABLE_PROJECT)
@@ -41,6 +63,15 @@ class programModel extends model
->fetchPairs();
}
+ /**
+ * Get user programs for block.
+ *
+ * @param varchar $status
+ * @param varchar $orderBy
+ * @param int $limit
+ * @access public
+ * @return void
+ */
public function getUserPrograms($status = 'all', $orderBy = 'id_desc', $limit = 15)
{
$programs = $this->dao->select('*')->from(TABLE_PROJECT)
@@ -57,7 +88,7 @@ class programModel extends model
if(empty($programs)) return array();
$programIdList = array_keys($programs);
- $consumes = $this->dao->select('program, sum(consumed) as consumed')->from(TABLE_TASK)
+ $hours = $this->dao->select('program, sum(consumed) as consumed, sum(estimate) as estimate')->from(TABLE_TASK)
->where('program')->in($programIdList)
->andWhere('deleted')->eq(0)
->andWhere('parent')->lt(1)
@@ -76,6 +107,21 @@ class programModel extends model
->groupBy('program')
->fetchAll('program');
+ $allStories = $this->dao->select('program, count(*) as allStories')->from(TABLE_STORY)
+ ->where('program')->in($programIdList)
+ ->andWhere('deleted')->eq(0)
+ ->andWhere('status')->ne('draft')
+ ->groupBy('program')
+ ->fetchAll('program');
+
+ $doneStories = $this->dao->select('program, count(*) as doneStories')->from(TABLE_STORY)
+ ->where('program')->in($programIdList)
+ ->andWhere('deleted')->eq(0)
+ ->andWhere('status')->eq('closed')
+ ->andWhere('closedReason')->eq('done')
+ ->groupBy('program')
+ ->fetchAll('program');
+
$leftStories = $this->dao->select('program, count(*) as leftStories')->from(TABLE_STORY)
->where('program')->in($programIdList)
->andWhere('deleted')->eq(0)
@@ -93,10 +139,13 @@ class programModel extends model
foreach($programs as $programID => $program)
{
$program->teamCount = isset($teams[$programID]) ? $teams[$programID]->count : 0;
- $program->consumed = isset($consumes[$programID]) ? $consumes[$programID] : 0;
- $program->leftTasks = isset($leftTasks[$programID]) ? $leftTasks[$programID] : 0;
- $program->leftStories = isset($leftStories[$programID]) ? $leftStories[$programID] : 0;
- $program->leftBugs = isset($leftBugs[$programID]) ? $leftBugs[$programID] : 0;
+ $program->consumed = isset($hours[$programID]) ? $hours[$programID]->consumed : 0;
+ $program->estimate = isset($hours[$programID]) ? $hours[$programID]->estimate : 0;
+ $program->leftTasks = isset($leftTasks[$programID]) ? $leftTasks[$programID]->leftTasks : 0;
+ $program->allStories = isset($allStories[$programID]) ? $allStories[$programID]->allStories : 0;
+ $program->doneStories = isset($doneStories[$programID]) ? $doneStories[$programID]->doneStories : 0;
+ $program->leftStories = isset($leftStories[$programID]) ? $leftStories[$programID]->leftStories : 0;
+ $program->leftBugs = isset($leftBugs[$programID]) ? $leftBugs[$programID]->leftBugs : 0;
}
return $programs;
@@ -166,9 +215,14 @@ class programModel extends model
die(js::locate('back'));
}
+ /**
+ * Create a program.
+ *
+ * @access private
+ * @return void
+ */
public function create()
{
- $this->lang->project->team = $this->lang->project->teamname;
$project = fixer::input('post')
->setDefault('status', 'wait')
->add('type', 'program')
@@ -178,14 +232,14 @@ class programModel extends model
->setDefault('team', substr($this->post->name,0, 30))
->join('whitelist', ',')
->cleanInt('budget')
- ->stripTags($this->config->project->editor->create['id'], $this->config->allowedTags)
+ ->stripTags($this->config->program->editor->create['id'], $this->config->allowedTags)
->remove('products, workDays, delta, branch, uid, plans')
->get();
- $project = $this->loadModel('file')->processImgURL($project, $this->config->project->editor->create['id'], $this->post->uid);
+ $project = $this->loadModel('file')->processImgURL($project, $this->config->program->editor->create['id'], $this->post->uid);
$this->dao->insert(TABLE_PROJECT)->data($project)
->autoCheck()
- ->batchcheck($this->config->project->create->requiredFields, 'notempty')
+ ->batchcheck($this->config->program->create->requiredFields, 'notempty')
->check('name', 'unique', "deleted='0'")
->check('code', 'unique', "deleted='0'")
->exec();
@@ -249,6 +303,72 @@ class programModel extends model
}
}
+ /**
+ * Update program.
+ *
+ * @param int $programID
+ * @access public
+ * @return array
+ */
+ public function update($programID)
+ {
+ $programID = (int)$programID;
+ $oldProgram = $this->dao->findById($programID)->from(TABLE_PROJECT)->fetch();
+
+ $program = fixer::input('post')
+ ->setIF($this->post->begin == '0000-00-00', 'begin', '')
+ ->setIF($this->post->end == '0000-00-00', 'end', '')
+ ->setIF($this->post->acl != 'custom', 'whitelist', '')
+ ->setIF($this->post->acl == 'custom' and !isset($_POST['whitelist']), 'whitelist', '')
+ ->setDefault('team', $this->post->name)
+ ->join('whitelist', ',')
+ ->stripTags($this->config->program->editor->edit['id'], $this->config->allowedTags)
+ ->remove('products, branch, uid, plans')
+ ->get();
+ $program = $this->loadModel('file')->processImgURL($program, $this->config->program->editor->edit['id'], $this->post->uid);
+
+ if(!empty($program->isCat) and $this->checkHasContent($programID)) dao::$errors['isCat'] = $this->lang->program->cannotChangeToCat;
+ if(empty($program->isCat) and $this->checkHasChildren($programID)) dao::$errors['isCat'] = $this->lang->program->cannotCancelCat;
+ if(dao::isError()) return false;
+
+
+ $this->dao->update(TABLE_PROJECT)->data($program)
+ ->autoCheck($skipFields = 'begin,end')
+ ->batchcheck($this->config->program->edit->requiredFields, 'notempty')
+ ->checkIF($program->begin != '', 'begin', 'date')
+ ->checkIF($program->end != '', 'end', 'date')
+ ->checkIF($program->end != '', 'end', 'gt', $program->begin)
+ ->check('name', 'unique', "id!=$programID and deleted='0'")
+ ->check('code', 'unique', "id!=$programID and deleted='0'")
+ ->where('id')->eq($programID)
+ ->limit(1)
+ ->exec();
+
+ if(!dao::isError())
+ {
+ $this->file->updateObjectID($this->post->uid, $programID, 'project');
+ if($program->acl != 'open' and ($program->acl != $oldProject->acl or $program->whitelist != $oldProgram->whitelist))
+ {
+ $this->loadModel('user')->updateUserView($programID, 'program');
+ }
+
+ if($oldProgram->parent != $program->parent) $this->moveNode($programID, $program->parent, $oldProgram->path, $oldProgram->grade);
+
+ return common::createChanges($oldProgram, $program);
+ }
+ }
+
+ /*
+ * Get program swapper.
+ *
+ * @param object $programs
+ * @param int $programID
+ * @param varchar $currentModule
+ * @param varchar $currentMethod
+ * @param varchar $extra
+ * @access private
+ * @return void
+ */
public function getSwapper($programs, $programID, $currentModule, $currentMethod, $extra = '')
{
$this->loadModel('project');
@@ -270,24 +390,36 @@ class programModel extends model
return $output;
}
- public static function isClickable($project, $action)
+ /**
+ * Judge an action is clickable or not.
+ *
+ * @param object $project
+ * @param string $action
+ * @access public
+ * @return bool
+ */
+ public static function isClickable($program, $action)
{
$action = strtolower($action);
- if($action == 'start') return $project->status == 'wait' or $project->status == 'suspended';
- if($action == 'finish') return $project->status == 'wait' or $project->status == 'doing';
- if($action == 'close') return $project->status != 'closed';
- if($action == 'suspend') return $project->status == 'wait' or $project->status == 'doing';
- if($action == 'activate') return $project->status == 'done';
+ if(empty($program)) return true;
+
+ if($action == 'start') return $program->status == 'wait' or $program->status == 'suspended';
+ if($action == 'finish') return $program->status == 'wait' or $program->status == 'doing';
+ if($action == 'close') return $program->status != 'closed';
+ if($action == 'suspend') return $program->status == 'wait' or $program->status == 'doing';
+ if($action == 'activate') return $program->status == 'done';
return true;
}
- public function getProducts($program)
- {
- return $this->dao->select('*')->from(TABLE_PRODUCT)->where('project')->eq($program)->fetchAll('id');
- }
-
+ /**
+ * Check has content for program
+ *
+ * @param int $programID
+ * @access public
+ * @return bool
+ */
public function checkHasContent($programID)
{
$count = 0;
@@ -312,6 +444,26 @@ class programModel extends model
return $count > 0;
}
+ /**
+ * Check has children project.
+ *
+ * @param int $programID
+ * @access public
+ * @return bool
+ */
+ public function checkHasChildren($programID)
+ {
+ $count = $this->dao->select('count(*) as count')->from(TABLE_PROJECT)->where('parent')->eq($programID)->fetch('count');
+ return $count > 0;
+ }
+
+ /**
+ * Set program tree path.
+ *
+ * @param int $programID
+ * @access public
+ * @return bool
+ */
public function setTreePath($programID)
{
$program = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->eq($programID)->fetch();
@@ -327,5 +479,69 @@ class programModel extends model
$path['grade'] = $parent->grade + 1;
}
$this->dao->update(TABLE_PROJECT)->set('path')->eq($path['path'])->set('grade')->eq($path['grade'])->where('id')->eq($program->id)->exec();
+ return !dao::isError();
+ }
+
+ /**
+ * Get program parent pairs
+ *
+ * @access public
+ * @return array
+ */
+ public function getParentPairs()
+ {
+ $stmt = $this->dao->select('id,name,parent,path,grade')->from(TABLE_PROJECT)->where('isCat')->eq(1)->andWhere('deleted')->eq(0)->orderBy('grade, `order`')->query();
+
+ $pairs = array();
+ $pairs[0] = '/';
+ while($program = $stmt->fetch())
+ {
+ if($program->grade == 1)
+ {
+ $pairs[$program->id] = '/' . $program->name;
+ continue;
+ }
+
+ $programName = '/' . $program->name;
+ $pairs[$program->id] = isset($pairs[$program->parent]) ? $pairs[$program->parent] . $programName : $programName;
+ }
+
+ return $pairs;
+ }
+
+ /**
+ * Move project node.
+ *
+ * @param int $programID
+ * @param int $parentID
+ * @param string $oldPath
+ * @param int $oldGrade
+ * @access public
+ * @return bool
+ */
+ public function moveNode($programID, $parentID, $oldPath, $oldGrade)
+ {
+ $parent = $this->dao->select('id,parent,path,grade')->from(TABLE_PROJECT)->where('id')->eq($parentID)->fetch();
+
+ $childNodes = $this->dao->select('id,parent,path,grade')->from(TABLE_PROJECT)
+ ->where('path')->like("{$oldPath}%")
+ ->andWhere('deleted')->eq(0)
+ ->andWhere('template')->ne('')
+ ->orderBy('grade')
+ ->fetchAll();
+ /* Process child node path and grade field. */
+ foreach($childNodes as $childNode)
+ {
+ $path = substr($childNode->path, strpos($childNode->path, ",{$programID},"));
+ $grade = $oldGrade - $childNode->grade + 1;
+ if($parent)
+ {
+ $path = rtrim($parent->path, ',') . $path;
+ $grade = $parent->grade + $grade;
+ }
+ $this->dao->update(TABLE_PROJECT)->set('path')->eq($path)->set('grade')->eq($grade)->where('id')->eq($childNode->id)->exec();
+ }
+
+ return !dao::isError();
}
}
diff --git a/module/program/view/browsebygrid.html.php b/module/program/view/browsebygrid.html.php
index 0e82d8ce78..5934b4a48d 100644
--- a/module/program/view/browsebygrid.html.php
+++ b/module/program/view/browsebygrid.html.php
@@ -2,7 +2,7 @@
$program):?>
- id");?>'>
+ id", '', '', $program->id);?>'>
name;?>
template === 'cmmi'): ?>
diff --git a/module/program/view/browsebylist.html.php b/module/program/view/browsebylist.html.php
index 7d7c5a6e40..07c63c163d 100644
--- a/module/program/view/browsebylist.html.php
+++ b/module/program/view/browsebylist.html.php
@@ -28,7 +28,7 @@
|
code;?> |
- id"), $program->name);?>
+ createLink('program', 'index', "programID=$program->id", '', '', $program->id), $program->name);?>
|
project->statusList, $program->status, '');?> |
begin;?> |
diff --git a/module/program/view/edit.html.php b/module/program/view/edit.html.php
index 52c5b9df39..5adb750992 100644
--- a/module/program/view/edit.html.php
+++ b/module/program/view/edit.html.php
@@ -1,11 +1,11 @@
- * @package project
+ * @package program
* @version $Id: create.html.php 4728 2013-05-03 06:14:34Z chencongzhi520@gmail.com $
* @link http://www.zentao.net
*/
@@ -21,46 +21,56 @@