Merge branch 'master' of git@github.com:easysoft/zentaopms

This commit is contained in:
wwccss
2013-08-26 20:46:10 +08:00
46 changed files with 1079 additions and 183 deletions
+3
View File
@@ -89,6 +89,7 @@ $lang->action->desc->editestimate = '$date, <strong>$actor</strong> edited wor
$lang->action->desc->deleteestimate = '$date, <strong>$actor</strong> deleted working hours';
$lang->action->desc->canceled = '$date, canceled by <strong>$actor</strong>.';
$lang->action->desc->svncommited = '$date, <strong>$actor</strong> commited to svnrevision is <strong>#$extra</strong>.' . "\n";
$lang->action->desc->gitcommited = '$date, <strong>$actor</strong> commited to gitrevision is <strong>#$extra</strong>.' . "\n";
$lang->action->desc->finished = '$date, finished by <strong>$actor</strong>.';
$lang->action->desc->diff1 = 'changed <strong><i>%s</i></strong>, old is "%s", new is "%s".<br />';
$lang->action->desc->diff2 = 'changed <strong><i>%s</i></strong>, the diff is:' . "\n" . "<blockquote>%s</blockquote>" . "\n<div class='hidden'>%s</div>";
@@ -119,6 +120,7 @@ $lang->action->label->tostory = 'converted to story';
$lang->action->label->frombug = 'converted from bug';
$lang->action->label->totask = 'converted to task';
$lang->action->label->svncommited = 'commited code';
$lang->action->label->gitcommited = 'commited code';
$lang->action->label->linked2plan = 'link to plan';
$lang->action->label->unlinkedfromplan = 'unlink from plan';
$lang->action->label->marked = 'edited';
@@ -191,6 +193,7 @@ $lang->action->search->label['tostory'] = $lang->action->label->tost
$lang->action->search->label['frombug'] = $lang->action->label->frombug;
$lang->action->search->label['totask'] = $lang->action->label->totask;
$lang->action->search->label['svncommited'] = $lang->action->label->svncommited;
$lang->action->search->label['gitcommited'] = $lang->action->label->gitcommited;
$lang->action->search->label['linked2plan'] = $lang->action->label->linked2plan;
$lang->action->search->label['unlinkedfromplan'] = $lang->action->label->unlinkedfromplan;
$lang->action->search->label['linked2project'] = $lang->action->label->linked2project;
+3
View File
@@ -89,6 +89,7 @@ $lang->action->desc->editestimate = '$date, 由 <strong>$actor</strong> 编辑
$lang->action->desc->deleteestimate = '$date, 由 <strong>$actor</strong> 删除工时。';
$lang->action->desc->canceled = '$date, 由 <strong>$actor</strong> 取消。' . "\n";
$lang->action->desc->svncommited = '$date, 由 <strong>$actor</strong> 提交代码,版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->gitcommited = '$date, 由 <strong>$actor</strong> 提交代码,版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->finished = '$date, 由 <strong>$actor</strong> 完成。' . "\n";
$lang->action->desc->diff1 = '修改了 <strong><i>%s</i></strong>,旧值为 "%s",新值为 "%s"。<br />' . "\n";
$lang->action->desc->diff2 = '修改了 <strong><i>%s</i></strong>,区别为:' . "\n" . "<blockquote>%s</blockquote>" . "\n<div class='hidden'>%s</div>";
@@ -119,6 +120,7 @@ $lang->action->label->tostory = '转需求';
$lang->action->label->frombug = '转需求';
$lang->action->label->totask = '转任务';
$lang->action->label->svncommited = '提交代码';
$lang->action->label->gitcommited = '提交代码';
$lang->action->label->linked2plan = '关联计划';
$lang->action->label->unlinkedfromplan = '移除计划';
$lang->action->label->marked = '编辑了';
@@ -191,6 +193,7 @@ $lang->action->search->label['tostory'] = $lang->action->label->tost
$lang->action->search->label['frombug'] = $lang->action->label->frombug;
$lang->action->search->label['totask'] = $lang->action->label->totask;
$lang->action->search->label['svncommited'] = $lang->action->label->svncommited;
$lang->action->search->label['gitcommited'] = $lang->action->label->gitcommited;
$lang->action->search->label['linked2plan'] = $lang->action->label->linked2plan;
$lang->action->search->label['unlinkedfromplan'] = $lang->action->label->unlinkedfromplan;
$lang->action->search->label['linked2project'] = $lang->action->label->linked2project;
+3 -2
View File
@@ -39,7 +39,7 @@ class actionModel extends model
$action->actor = $actor ? $actor : $this->app->user->account;
$action->action = strtolower($actionType);
$action->date = helper::now();
$action->comment = $comment;
$action->comment = ltrim($comment, "<br />");
$action->extra = $extra;
/* Get product and project for this object. */
@@ -193,6 +193,7 @@ class actionModel extends model
foreach($actions as $actionID => $action)
{
if(strtolower($action->action) == 'svncommited' and isset($commiters[$action->actor])) $action->actor = $commiters[$action->actor];
if(strtolower($action->action) == 'gitcommited' and isset($commiters[$action->actor])) $action->actor = $commiters[$action->actor];
$action->history = isset($histories[$actionID]) ? $histories[$actionID] : array();
$actions[$actionID] = $action;
}
@@ -605,7 +606,7 @@ class actionModel extends model
if($history->diff != '')
{
$history->diff = str_replace(array('<ins>', '</ins>', '<del>', '</del>'), array('[ins]', '[/ins]', '[del]', '[/del]'), $history->diff);
$history->diff = $history->field != 'subversion' ? htmlspecialchars($history->diff) : $history->diff; // Keep the diff link.
$history->diff = ($history->field != 'subversion' and $history->field != 'git') ? htmlspecialchars($history->diff) : $history->diff; // Keep the diff link.
$history->diff = str_replace(array('[ins]', '[/ins]', '[del]', '[/del]'), array('<ins>', '</ins>', '<del>', '</del>'), $history->diff);
$history->diff = nl2br($history->diff);
$history->noTagDiff = preg_replace('/&lt;\/?([a-z][a-z0-9]*)[^\/]*\/?&gt;/Ui', '', $history->diff);
+1 -1
View File
@@ -1312,7 +1312,7 @@ class bugModel extends model
{
$products = array_keys($this->loadModel('product')->getPrivProducts());
$bugQuery = str_replace($allProduct, '1', $this->session->bugQuery);
$bugQuery = $bugQuery . ' AND `product`' . helper::dbIN(array_keys($products));
$bugQuery = $bugQuery . ' AND `product`' . helper::dbIN($products);
}
$bugQuery = $this->loadModel('search')->replaceDynamic($bugQuery);
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where($bugQuery)
+1 -1
View File
@@ -26,7 +26,7 @@ js::set('page', 'assignedto');
</tr>
<tr>
<td class='rowhead'><?php echo $lang->bug->mailto;?></td>
<td><?php echo html::select('mailto[]', $users, str_replace(' ', '', $bug->mailto), 'class="text-1" multiple');?></td>
<td><?php echo html::select('mailto[]', $users, str_replace(' ', '', $bug->mailto), 'class="w-p98" multiple');?></td>
</tr>
<tr>
<td class='rowhead'><?php echo $lang->comment;?></td>
+1 -1
View File
@@ -72,7 +72,7 @@ js::set('customed', $customed);
}
?>
<div class='treeSlider'><span>&nbsp;</span></div>
<div class='treeSlider' id='bugTree'><span>&nbsp;</span></div>
<form method='post'>
<table class='cont-lt1'>
<tr valign='top'>
+1 -1
View File
@@ -30,7 +30,7 @@ js::set('page', 'confirmbug');
</tr>
<tr>
<td class='rowhead'><?php echo $lang->bug->mailto;?></td>
<td><?php echo html::select('mailto[]', $users, str_replace(' ' , '', $bug->mailto), 'class="text-1" multiple');?></td>
<td><?php echo html::select('mailto[]', $users, str_replace(' ' , '', $bug->mailto), 'class="w-p98" multiple');?></td>
</tr>
<tr>
<td class='rowhead'><?php echo $lang->comment;?></td>
+1 -4
View File
@@ -619,7 +619,7 @@ class commonModel extends model
}
else
{
$objects = $this->dbh->query($queryCondition . "OR id=$objectID ORDER BY $orderBy")->fetchAll();
$objects = $this->dbh->query($queryCondition . " ORDER BY $orderBy")->fetchAll();
}
$tmpObjectIDs = array();
@@ -683,9 +683,6 @@ class commonModel extends model
$orderBy = explode('limit', $orderBy[1]);
$orderBy = str_replace('t1.', '', $orderBy[0]);
/* Fix bug #448. I don't know why too. */
if($orderBy == 'yes') $orderBy = '';
$this->session->set($objectType . 'OrderBy', $orderBy);
}
else
+2 -5
View File
@@ -1,8 +1,5 @@
<?php if($extView = $this->getExtViewFile(__FILE__)){include $extView; return helper::cd();}?>
<?php
if($config->debug)
{
css::import($defaultTheme . 'dropmenu.css');
js::import($jsRoot . 'jquery/dropmenu/dropmenu.js');
}
css::import($defaultTheme . 'dropmenu.css');
js::import($jsRoot . 'jquery/dropmenu/dropmenu.js');
?>
+1 -1
View File
@@ -44,7 +44,7 @@ $(document).ready(function()
KindEditor.ready(function(K)
{
keEditor = K.create('#' + editorID,
editor = K.create('#' + editorID,
{
items:editorTool,
filterMode:true,
+26
View File
@@ -0,0 +1,26 @@
<?php
/**
* encodings: 提交日志的编码,比如GBK,可以用逗号连接起来的多个。
* client: Git客户端执行文件的路径,windows下面是git.exe的路径,linux下面比如/usr/bin/git
* repos可以是多个,需要设定某一个库的访问路径。
*
* encodeings: the encoding of the commentcan be a list.
* client: the git client binary path. Unser windows, find the path of git.exe. Under linux, try /usr/bin/git
* Can set multi repos, ervery one should set the path.
*
* 例子:
* $config->git->client = '/usr/bin/git'; // c:\git\git.exe
* $config->git->repos['pms']['path'] = '/home/user/repo/pms'; // c:\repo\pms
*
*/
$config->git = new stdClass();
$config->git->encodings = 'utf-8, gbk';
$config->git->client = '/usr/bin/git';
$i = 1;
$config->git->repos[$i]['path'] = '/home/z/pms';
/*
$i ++;
$config->git->repos[$i]['path'] = '';
*/
+110
View File
@@ -0,0 +1,110 @@
<?php
/**
* The control file of git currentModule of ZenTaoPMS.
*
* @copyright Copyright 2009-2013 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @license LGPL (http://www.gnu.org/licenses/lgpl.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package git
* @version $Id$
* @link http://www.zentao.net
*/
class git extends control
{
/**
* Sync git.
*
* @access public
* @return void
*/
public function run()
{
$this->git->run();
}
/**
* Diff a file.
*
* @param string $path
* @param int $revision
* @access public
* @return void
*/
public function diff($path, $revision)
{
$path = helper::safe64Decode($path);
$this->view->url = $path;
$this->view->revision = $revision;
$this->view->diff = $this->git->diff($path, $revision);
$this->display();
}
/**
* Cat a file.
*
* @param string $path
* @param int $revision
* @access public
* @return void
*/
public function cat($path, $revision)
{
$path = helper::safe64Decode($path);
$this->view->path = $path;
$this->view->revision = $revision;
$this->view->code = $this->git->cat($path, $revision);
$this->display();
}
/**
* Sync from the syncer by api.
*
* @access public
* @return void
*/
public function apiSync()
{
if($this->post->logs)
{
$repoRoot = $this->post->repoRoot;
$list = json_decode($this->post->logs);
$logs = array();
$i = 0;
foreach($list as $line)
{
if(!$line)
{
$i++;
continue;
}
$logs[$i][] = $line;
}
foreach($logs as $log)
{
$parsedLogs[] = $this->convertLog($log);
}
$parsedObjects = array('stories' => array(), 'tasks' => array(), 'bugs' => array());
foreach($parsedLogs as $log)
{
$objects = $this->git->parseComment($log->msg);
if($objects)
{
$this->git->saveAction2PMS($objects, $log, $repoRoot);
if($objects['stories']) $parsedObjects['stories'] = array_merge($parsedObjects['stories'], $objects['stories']);
if($objects['tasks']) $parsedObjects['tasks' ] = array_merge($parsedObjects['tasks'], $objects['tasks']);
if($objects['bugs']) $parsedObjects['bugs'] = array_merge($parsedObjects['bugs'], $objects['bugs']);
}
}
$parsedObjects['stories'] = array_unique($parsedObjects['stories']);
$parsedObjects['tasks'] = array_unique($parsedObjects['tasks']);
$parsedObjects['bugs'] = array_unique($parsedObjects['bugs']);
$this->view->parsedObjects = $parsedObjects;
$this->display();
exit;
}
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
/**
* The git module zh-cn file of ZenTaoPMS.
*
* @copyright Copyright 2009-2013 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @license LGPL (http://www.gnu.org/licenses/lgpl.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package git
* @version $Id$
* @link http://www.zentao.net
*/
/* Methods. */
$lang->git = new stdclass();
$lang->git->common = 'Git';
$lang->git->cat = 'View code';
$lang->git->diff = 'Diff code';
$lang->git->apiSync = 'API: sync git log';
+17
View File
@@ -0,0 +1,17 @@
<?php
/**
* The git module zh-cn file of ZenTaoPMS.
*
* @copyright Copyright 2009-2013 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @license LGPL (http://www.gnu.org/licenses/lgpl.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package git
* @version $Id$
* @link http://www.zentao.net
*/
/* 方法列表。*/
$lang->git = new stdclass();
$lang->git->common = 'Git';
$lang->git->cat = '查看源代码';
$lang->git->diff = '比较源代码';
$lang->git->apiSync = '接口:同步git日志';
+661
View File
@@ -0,0 +1,661 @@
<?php
/**
* The model file of git module of ZenTaoPMS.
*
* @copyright Copyright 2009-2013 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @license LGPL (http://www.gnu.org/licenses/lgpl.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package git
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php
class gitModel extends model
{
/**
* The git binary client.
*
* @var int
* @access public
*/
public $client;
/**
* Repos.
*
* @var array
* @access public
*/
public $repos = array();
/**
* The log root.
*
* @var string
* @access public
*/
public $logRoot = '';
/**
* The restart file.
*
* @var string
* @access public
*/
public $restartFile = '';
/**
* The root path of a repo
*
* @var string
* @access public
*/
public $repoRoot = '';
/**
* Users
*
* @var array
* @access public
*/
public $users = array();
/**
* Construct function.
*
* @access public
* @return void
*/
public function __construct()
{
parent::__construct();
$this->loadModel('action');
}
/**
* Run.
*
* @access public
* @return void
*/
public function run()
{
$this->setRepos();
$this->setLogRoot();
$this->setRestartFile();
foreach($this->repos as $name => $repo)
{
$this->printLog("begin repo $name");
$repo = (object)$repo;
$repo->name = $name;
$this->setRepo($repo);
$savedRevision = $this->getSavedRevision();
$this->printLog("start from revision $savedRevision");
$logs = $this->getRepoLogs($repo, $savedRevision);
$this->printLog("get " . count($logs) . " logs");
$this->printLog('begin parsing logs');
$latestRevision = $logs[0]->revision;
foreach($logs as $log)
{
$this->printLog("parsing log {$log->revision}");
if($log->revision == $savedRevision)
{
$this->printLog("{$log->revision} alread parsed, ommit it");
continue;
}
$this->printLog("comment is\n----------\n" . trim($log->msg) . "\n----------");
$objects = $this->parseComment($log->msg);
if($objects)
{
$this->printLog('extract' .
' story:' . join(' ', $objects['stories']) .
' task:' . join(' ', $objects['tasks']) .
' bug:' . join(',', $objects['bugs']));
$this->saveAction2PMS($objects, $log);
}
else
{
$this->printLog('no objects found' . "\n");
}
}
$this->saveLastRevision($latestRevision);
$this->printLog("save revision $latestRevision");
$this->deleteRestartFile();
$this->printLog("\n\nrepo $name finished");
}
}
/**
* Set the log root.
*
* @access public
* @return void
*/
public function setLogRoot()
{
$this->logRoot = $this->app->getTmpRoot() . 'git/';
if(!is_dir($this->logRoot)) mkdir($this->logRoot);
}
/**
* Set the restart file.
*
* @access public
* @return void
*/
public function setRestartFile()
{
$this->restartFile = dirname(__FILE__) . '/restart';
}
/**
* Delete the restart file.
*
* @access public
* @return void
*/
public function deleteRestartFile()
{
if(is_file($this->restartFile)) unlink($this->restartFile);
}
/**
* Set the repos.
*
* @access public
* @return void
*/
public function setRepos()
{
if(!$this->config->git->repos) die("You must set one git repo.\n");
$this->repos = $this->config->git->repos;
}
/**
* Set repo.
*
* @param object $repo
* @access public
* @return void
*/
public function setRepo($repo)
{
$this->setClient($repo);
$this->setLogFile($repo->name);
$this->setRepoRoot($repo);
}
/**
* Set the git binary client of a repo.
*
* @param object $repo
* @access public
* @return void
*/
public function setClient($repo)
{
if($this->config->git->client == '') die("You must set the git client file.\n");
$this->client = $this->config->git->client;
}
/**
* Set the log file of a repo.
*
* @param string $repoName
* @access public
* @return void
*/
public function setLogFile($repoName)
{
$this->logFile = $this->logRoot . $repoName;
}
/**
* set the root path of a repo.
*
* @param object $repo
* @access public
* @return void
*/
public function setRepoRoot($repo)
{
$this->repoRoot = $repo->path;
}
/**
* Get repo logs.
*
* @param object $repo
* @param int $fromRevision
* @access public
* @return array
*/
public function getRepoLogs($repo, $fromRevision)
{
$parsedLogs = array();
/* The git log command. */
if($fromRevision)
{
$cmd = "cd $this->repoRoot; $this->client log --stat $fromRevision..HEAD --pretty=format:%an*_*%cd*_*%H*_*%s";
}
else
{
$cmd = "cd $this->repoRoot; $this->client log --stat --pretty=format:%an*_*%cd*_*%H*_*%s";
}
exec($cmd, $list, $return);
if(!$list and $return) die("Some error occers: \nThe command is $cmd\n");
if(!$list and !$return) exit;
/* Process logs. */
$logs = array();
$i = 0;
foreach($list as $line)
{
if(!$line)
{
$i++;
continue;
}
$logs[$i][] = $line;
}
foreach($logs as $log)
{
$parsedLogs[] = $this->convertLog($log);
}
return $parsedLogs;
}
/**
* Convert log from xml format to object.
*
* @param object $log
* @access public
* @return ojbect
*/
public function convertLog($log)
{
list($account, $date, $hash, $comment) = explode('*_*', $log[0]);
$parsedLog = new stdClass();
$parsedLog->author = $account;
$parsedLog->revision = $hash;
$parsedLog->msg = $comment;
$parsedLog->date = date('Y-m-d H:i:s', strtotime($date));
$parsedLog->files = array();
unset($log[0]);
foreach($log as $change)
{
if(strpos($change, '|') === false) continue;
list($entry, $modify) = explode('|', $change);
$entry = '/' . trim($entry);
$parsedLog->files['M'][] = $entry;
}
return $parsedLog;
}
/**
* Parse the comment of git, extract object id list from it.
*
* @param string $comment
* @access public
* @return array
*/
public function parseComment($comment)
{
$stories = array();
$tasks = array();
$bugs = array();
// bug|story|task(case insensitive) + some space + #|:|(Chinese) + id lists(maybe join with space or ,)
// $comment = "bug # 1,2,3,4 Bug:1 2 3 4 5 story:9999,1234566 story:456,1234566";
$commonReg = "(?:\s){0,}(?:#|:|){0,}([0-9, ]{1,})";
$taskReg = '/task' . $commonReg . '/i';
$storyReg = '/story' . $commonReg . '/i';
$bugReg = '/bug' . $commonReg . '/i';
if(preg_match_all($storyReg, $comment, $result)) $stories = join(' ', $result[1]);
if(preg_match_all($taskReg, $comment, $result)) $tasks = join(' ', $result[1]);
if(preg_match_all($bugReg, $comment, $result)) $bugs = join(' ', $result[1]);
if($stories) $stories = array_unique(explode(' ', str_replace(',', ' ', $stories)));
if($tasks) $tasks = array_unique(explode(' ', str_replace(',', ' ', $tasks)));
if($bugs) $bugs = array_unique(explode(' ', str_replace(',', ' ', $bugs)));
if(!$stories and !$tasks and !$bugs) return array();
return array('stories' => $stories, 'tasks' => $tasks, 'bugs' => $bugs);
}
/**
* Convert the comment to uft-8.
*
* @param string $comment
* @access public
* @return string
*/
public function iconvComment($comment)
{
/* Get encodings. */
$encodings = str_replace(' ', '', trim($comment));
if($encodings == '') return $comment;
$encodings = explode(',', $encodings);
/* Try convert. */
foreach($encodings as $encoding)
{
$result = @iconv($encoding, 'utf-8', $comment);
if($result) return $result;
}
return $comment;
}
/**
* Diff a url.
*
* @param string $path
* @param int $revision
* @access public
* @return string|bool
*/
public function diff($path, $revision)
{
$repo = $this->getRepoByURL($path);
if(!$repo) return false;
$this->setClient($repo);
putenv('LC_CTYPE=en_US.UTF-8');
$path = str_replace('%2F', '/', urlencode($path));
$path = str_replace('%3A', ':', $path);
$cmd = "cd $repo->path;$this->client diff $revision^ $revision $path";
$diff = `$cmd`;
return $diff;
}
/**
* Cat a url.
*
* @param string $path
* @param int $revision
* @access public
* @return string|bool
*/
public function cat($path, $revision)
{
$repo = $this->getRepoByURL($path);
if(!$repo) return false;
$this->setClient($repo);
putenv('LC_CTYPE=en_US.UTF-8');
$path = str_replace('%2F', '/', urlencode($path));
$path = str_replace('%3A', ':', $path);
$subPath = substr($path, strlen($repo->path) + 1);
$cmd = "cd $repo->path;$this->client show $revision:$subPath";
$code = `$cmd`;
return $code;
}
/**
* Get repo by url.
*
* @param string $url
* @access public
* @return object|bool
*/
public function getRepoByURL($url)
{
foreach($this->config->git->repos as $repo) if(strpos($url, $repo['path']) !== false) return (object)$repo;
return false;
}
/**
* Save action to pms.
*
* @param array $objects
* @param object $log
* @param string $repoRoot
* @access public
* @return void
*/
public function saveAction2PMS($objects, $log, $repoRoot = '')
{
$action = new stdclass();
$action->actor = $log->author;
$action->action = 'gitcommited';
$action->date = $log->date;
$action->comment = $this->iconvComment($log->msg);
$action->extra = substr($log->revision, 0, 10);
$changes = $this->createActionChanges($log, $repoRoot);
if($objects['stories'])
{
$products = $this->getStoryProducts($objects['stories']);
foreach($objects['stories'] as $storyID)
{
$storyID = (int)$storyID;
if(!isset($products[$storyID])) continue;
$action->objectType = 'story';
$action->objectID = $storyID;
$action->product = $products[$storyID];
$action->project = 0;
$this->saveRecord($action, $changes);
}
}
if($objects['tasks'])
{
$productsAndProjects = $this->getTaskProductsAndProjects($objects['tasks']);
foreach($objects['tasks'] as $taskID)
{
$taskID = (int)$taskID;
if(!isset($productsAndProjects[$taskID])) continue;
$action->objectType = 'task';
$action->objectID = $taskID;
$action->product = $productsAndProjects[$taskID]['product'];
$action->project = $productsAndProjects[$taskID]['project'];
$this->saveRecord($action, $changes);
}
}
if($objects['bugs'])
{
$productsAndProjects = $this->getBugProductsAndProjects($objects['bugs']);
foreach($objects['bugs'] as $bugID)
{
$bugID = (int)$bugID;
if(!isset($productsAndProjects[$bugID])) continue;
$action->objectType = 'bug';
$action->objectID = $bugID;
$action->product = $productsAndProjects[$bugID]->product;
$action->project = $productsAndProjects[$bugID]->project;
$this->saveRecord($action, $changes);
}
}
}
/**
* Save an action to pms.
*
* @param object $action
* @param object $log
* @access public
* @return bool
*/
public function saveRecord($action, $changes)
{
$record = $this->dao->select('*')->from(TABLE_ACTION)
->where('objectType')->eq($action->objectType)
->andWhere('objectID')->eq($action->objectID)
->andWhere('extra')->eq($action->extra)
->andWhere('action')->eq('gitcommited')
->fetch();
if($record)
{
$this->dao->update(TABLE_ACTION)->data($action)->where('id')->eq($record->id)->exec();
if($changes)
{
$historyID = $this->dao->findByAction($record->id)->from(TABLE_HISTORY)->fetch('id');
$this->dao->update(TABLE_HISTORY)->data($changes)->where('id')->eq($historyID)->exec();
}
}
else
{
$this->dao->insert(TABLE_ACTION)->data($action)->autoCheck()->exec();
if($changes)
{
$actionID = $this->dao->lastInsertID();
$this->action->logHistory($actionID, array($changes));
}
}
}
/**
* Create changes for action from a log.
*
* @param object $log
* @param string $repoRoot
* @access public
* @return array
*/
public function createActionChanges($log, $repoRoot)
{
if(!$log->files) return array();
$diff = '';
$oldSelf = $this->server->PHP_SELF;
$this->server->set('PHP_SELF', $this->config->webRoot);
if(!$repoRoot) $repoRoot = $this->repoRoot;
foreach($log->files as $action => $actionFiles)
{
foreach($actionFiles as $file)
{
$param = array('url' => helper::safe64Encode($repoRoot . $file), 'revision' => $log->revision);
$catLink = trim(html::a(helper::createLink('git', 'cat', $param, 'html'), 'view', '', "class='gitlink'"));
$diffLink = trim(html::a(helper::createLink('git', 'diff', $param, 'html'), 'diff', '', "class='gitlink'"));
$diff .= $action . " " . $file . " $catLink ";
$diff .= $action == 'M' ? "$diffLink\n" : "\n" ;
}
}
$changes->field = 'git';
$changes->old = '';
$changes->new = '';
$changes->diff = trim($diff);
$this->server->set('PHP_SELF', $oldSelf);
return (array)$changes;
}
/**
* Get products of stories.
*
* @param array $stories
* @access public
* @return array
*/
public function getStoryProducts($stories)
{
return $this->dao->select('id, product')->from(TABLE_STORY)->where('id')->in($stories)->fetchPairs();
}
/**
* Get products and projects of tasks.
*
* @param array $tasks
* @access public
* @return array
*/
public function getTaskProductsAndProjects($tasks)
{
$records = array();
$products = $this->dao->select('t1.id, t2.product')
->from(TABLE_TASK)->alias('t1')
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
->where('t1.id')->in($tasks)->fetchPairs();
$projects = $this->dao->select('id, project')->from(TABLE_TASK)->where('id')->in($tasks)->fetchPairs();
foreach($projects as $taskID => $projectID)
{
$record = array();
$record['project'] = $projectID;
$record['product'] = isset($products[$taskID]) ? $products[$taskID] : 0;
$records[$taskID] = $record;
}
return $records;
}
/**
* Get products and projects of bugs.
*
* @param array $bugs
* @access public
* @return array
*/
public function getBugProductsAndProjects($bugs)
{
return $this->dao->select('id, project, product')->from(TABLE_BUG)->where('id')->in($bugs)->fetchAll('id');
}
/**
* Get the saved revision.
*
* @access public
* @return int
*/
public function getSavedRevision()
{
if(!file_exists($this->logFile)) return 0;
if(file_exists($this->restartFile)) return 0;
return trim(file_get_contents($this->logFile));
}
/**
* Save the last revision.
*
* @param int $revision
* @access public
* @return void
*/
public function saveLastRevision($revision)
{
file_put_contents($this->logFile, $revision);
}
/**
* Pring log.
*
* @param sting $log
* @access public
* @return void
*/
public function printLog($log)
{
echo helper::now() . " $log\n";
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
/**
* The cat view file of git module of ZenTaoPMS.
*
* @copyright Copyright 2009-2013 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @license LGPL (http://www.gnu.org/licenses/lgpl.html)
* @author Congzhi Chen <congzhi@cnezsoft.com>
* @package file
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php';?>
<div class='box-title'><?php echo "$path@$revision";?></div>
<div class='box-content'><xmp><?php echo $code;?></xmp></div>
<?php include '../../common/view/footer.lite.html.php';?>
+17
View File
@@ -0,0 +1,17 @@
<?php
/**
* The diff view file of git module of ZenTaoPMS.
*
* @copyright Copyright 2009-2013 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @license LGPL (http://www.gnu.org/licenses/lgpl.html)
* @author Congzhi Chen <congzhi@cnezsoft.com>
* @package file
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php';?>
<?php $catLink = inlink('cat', 'url=' . helper::safe64Encode($path) . "&revision=$revision");?>
<div class='box-title'><?php echo html::a($catLink, "$path@$revision");?></div>
<div class='box-content'><xmp><?php echo $diff;?></xmp></div>
<?php include '../../common/view/footer.lite.html.php';?>
+18 -7
View File
@@ -46,13 +46,14 @@ $lang->moduleOrder[125] = 'action';
$lang->moduleOrder[130] = 'mail';
$lang->moduleOrder[135] = 'svn';
$lang->moduleOrder[140] = 'search';
$lang->moduleOrder[145] = 'tree';
$lang->moduleOrder[150] = 'api';
$lang->moduleOrder[155] = 'file';
$lang->moduleOrder[160] = 'misc';
$lang->moduleOrder[165] = 'sso';
$lang->moduleOrder[170] = 'webapp';
$lang->moduleOrder[140] = 'git';
$lang->moduleOrder[145] = 'search';
$lang->moduleOrder[150] = 'tree';
$lang->moduleOrder[155] = 'api';
$lang->moduleOrder[160] = 'file';
$lang->moduleOrder[165] = 'misc';
$lang->moduleOrder[170] = 'sso';
$lang->moduleOrder[175] = 'webapp';
$lang->resource = new stdclass();
@@ -513,6 +514,16 @@ $lang->svn->methodOrder[5] = 'diff';
$lang->svn->methodOrder[10] = 'cat';
$lang->svn->methodOrder[15] = 'apiSync';
/* Git. */
$lang->resource->git = new stdclass();
$lang->resource->git->diff = 'diff';
$lang->resource->git->cat = 'cat';
$lang->resource->git->apiSync = 'apiSync';
$lang->git->methodOrder[5] = 'diff';
$lang->git->methodOrder[10] = 'cat';
$lang->git->methodOrder[15] = 'apiSync';
/* Company. */
$lang->resource->company = new stdclass();
$lang->resource->company->index = 'index';
+2 -2
View File
@@ -52,7 +52,7 @@
</div>
<div id='querybox' class='<?php if($browseType !='bysearch') echo 'hidden';?>'></div>
<div class='treeSlider'><span>&nbsp;</span></div>
<div class='treeSlider' id='storyTree'><span>&nbsp;</span></div>
<form method='post' id='productStoryForm'>
<table class='cont-lt1'>
<tr valign='top'>
@@ -153,7 +153,7 @@
<?php
$class = "class='disabled'";
$canBatchClose = common::hasPriv('story', 'batchClose') and strtolower($browseType) != 'closedbyme' and strtolower($browseType) != 'closedstory';
$canBatchClose = common::hasPriv('story', 'batchClose') && strtolower($browseType) != 'closedbyme' && strtolower($browseType) != 'closedstory';
$actionLink = $this->createLink('story', 'batchClose', "productID=$productID&projectID=0");
$misc = $canBatchClose ? "onclick=setFormAction('$actionLink')" : $class;
echo "<li>" . html::a('#', $lang->close, '', $misc) . "</li>";
+1
View File
@@ -43,6 +43,7 @@ $lang->productplan->desc = 'Desc';
$lang->productplan->begin = 'Begin';
$lang->productplan->end = 'End';
$lang->productplan->endList[7] = 'One Weeks';
$lang->productplan->endList[14] = 'Two Weeks';
$lang->productplan->endList[31] = 'One Month';
$lang->productplan->endList[62] = 'Two Months';
+1
View File
@@ -43,6 +43,7 @@ $lang->productplan->desc = '描述';
$lang->productplan->begin = '开始日期';
$lang->productplan->end = '结束日期';
$lang->productplan->endList[7] = '一星期';
$lang->productplan->endList[14] = '两星期';
$lang->productplan->endList[31] = '一个月';
$lang->productplan->endList[62] = '两个月';
+1 -1
View File
@@ -21,7 +21,7 @@
var browseType = '<?php echo $browseType;?>';
</script>
<div id='querybox' class='<?php if($browseType != 'bysearch') echo 'hidden';?>'></div>
<div class='treeSlider'><span>&nbsp;</span></div>
<div class='treeSlider' id='taskTree'><span>&nbsp;</span></div>
<form method='post' id='projectTaskForm'>
<table class='cont-lt2'>
<tr valign='top'>
+8 -8
View File
@@ -87,14 +87,14 @@ class releaseModel extends model
$buildID = $this->dao->lastInsertID();
}
$release = fixer::input('post')
->stripTags('name')
->add('product', (int)$productID)
->join('stories', ',')
->join('bugs', ',')
->remove('allchecker')
->setIF($this->post->build ==false, 'build', $buildID)
->get();
$release = fixer::input('post')
->stripTags('name')
->add('product', (int)$productID)
->join('stories', ',')
->join('bugs', ',')
->setIF($this->post->build == false, 'build', $buildID)
->remove('allchecker')
->get();
$this->dao->insert(TABLE_RELEASE)->data($release)->autoCheck()->batchCheck($this->config->release->create->requiredFields, 'notempty')->check('name','unique')->exec();
$releaseID = $this->dao->lastInsertID();
+2 -1
View File
@@ -644,7 +644,7 @@ class story extends control
*/
public function batchClose($productID = 0, $projectID = 0)
{
if($this->post->closedReasons)
if($this->post->comments)
{
$allChanges = $this->story->batchClose();
@@ -1035,6 +1035,7 @@ class story extends control
if(isset($storyLang->statusList[$story->status])) $story->status = $storyLang->statusList[$story->status];
if(isset($storyLang->stageList[$story->stage])) $story->stage = $storyLang->stageList[$story->stage];
if(isset($storyLang->reasonList[$story->closedReason])) $story->closedReason = $storyLang->reasonList[$story->closedReason];
if(isset($storyLang->sourceList[$story->source])) $story->source = $storyLang->sourceList[$story->source];
if(isset($users[$story->openedBy])) $story->openedBy = $users[$story->openedBy];
if(isset($users[$story->assignedTo])) $story->assignedTo = $users[$story->assignedTo];
+40 -41
View File
@@ -579,51 +579,49 @@ class storyModel extends model
/* Adjust whether the post data is complete, if not, remove the last element of $storyIDList. */
if($this->session->showSuhosinInfo) array_pop($storyIDList);
if(!empty($storyIDList))
foreach($storyIDList as $storyID)
{
foreach($storyIDList as $storyID)
$oldStory = $this->getById($storyID);
if(!$oldStory->status == 'closed') continue;
$story->lastEditedBy = $this->app->user->account;
$story->lastEditedDate = $now;
$story->closedBy = $this->app->user->account;
$story->closedDate = $now;
$story->assignedTo = 'closed';
$story->assignedDate = $now;
$story->status = 'closed';
$story->closedReason = $this->post->closedReasons[$storyID];
$story->duplicateStory = $this->post->duplicateStoryIDList[$storyID] ? $this->post->duplicateStoryIDList[$storyID] : $oldStory->duplicateStory;
$story->childStories = $this->post->childStoriesIDList[$storyID] ? $this->post->childStoriesIDList[$storyID] : $oldStory->childStories;
if($story->closedReason == 'done') $story->stage = 'released';
if($story->closedReason != 'done') $story->plan = 0;
$stories[$storyID] = $story;
unset($story);
}
foreach($stories as $storyID => $story)
{
if(!$story->closedReason) continue;
$oldStory = $this->getById($storyID);
$this->dao->update(TABLE_STORY)->data($story)
->autoCheck()
->checkIF($story->closedReason == 'duplicate', 'duplicateStory', 'notempty')
->checkIF($story->closedReason == 'subdivided', 'childStories', 'notempty')
->where('id')->eq($storyID)->exec();
if(!dao::isError())
{
$oldStory = $this->getById($storyID);
$story->lastEditedBy = $this->app->user->account;
$story->lastEditedDate = $now;
$story->closedBy = $this->app->user->account;
$story->closedDate = $now;
$story->assignedTo = 'closed';
$story->assignedDate = $now;
$story->status = 'closed';
$story->closedReason = $this->post->closedReasons[$storyID];
$story->duplicateStory = $this->post->duplicateStoryIDList[$storyID] ? $this->post->duplicateStoryIDList[$storyID] : $oldStory->duplicateStory;
$story->childStories = $this->post->childStoriesIDList[$storyID] ? $this->post->childStoriesIDList[$storyID] : $oldStory->childStories;
if($story->closedReason == 'done') $story->stage = 'released';
if($story->closedReason != 'done') $story->plan = 0;
$stories[$storyID] = $story;
unset($story);
$allChanges[$storyID] = common::createChanges($oldStory, $story);
}
foreach($stories as $storyID => $story)
else
{
if(!$story->closedReason) continue;
$oldStory = $this->getById($storyID);
$this->dao->update(TABLE_STORY)->data($story)
->autoCheck()
->checkIF($story->closedReason == 'duplicate', 'duplicateStory', 'notempty')
->checkIF($story->closedReason == 'subdivided', 'childStories', 'notempty')
->where('id')->eq($storyID)->exec();
if(!dao::isError())
{
$allChanges[$storyID] = common::createChanges($oldStory, $story);
}
else
{
die(js::error('story#' . $storyID . dao::getError(true)));
}
die(js::error('story#' . $storyID . dao::getError(true)));
}
}
@@ -991,6 +989,7 @@ class storyModel extends model
$queryProductID = 'all';
}
$storyQuery = $storyQuery . ' AND `product`' . helper::dbIN(array_keys($products));
if($projectID != '') $storyQuery .= " AND `status` != 'draft'";
$storyQuery = $this->loadModel('search')->replaceDynamic($storyQuery);
return $this->getBySQL($queryProductID, $storyQuery, $orderBy, $pager);
+2
View File
@@ -936,6 +936,7 @@ class task extends control
/* Get related objects title or names. */
$relatedStories = $this->dao->select('id,title')->from(TABLE_STORY) ->where('id')->in($relatedStoryIdList)->fetchPairs();
$relatedFiles = $this->dao->select('id, objectID, pathname, title')->from(TABLE_FILE)->where('objectType')->eq('task')->andWhere('objectID')->in(@array_keys($tasks))->fetchGroup('objectID');
$relatedModules = $this->loadModel('tree')->getTaskOptionMenu($projectID);
foreach($tasks as $task)
{
@@ -954,6 +955,7 @@ class task extends control
if(isset($taskLang->priList[$task->pri])) $task->pri = $taskLang->priList[$task->pri];
if(isset($taskLang->statusList[$task->status])) $task->status = $taskLang->statusList[$task->status];
if(isset($taskLang->reasonList[$task->closedReason])) $task->closedReason = $taskLang->reasonList[$task->closedReason];
if(isset($relatedModules[$task->module])) $task->module = $relatedModules[$task->module];
if(isset($users[$task->openedBy])) $task->openedBy = $users[$task->openedBy];
if(isset($users[$task->assignedTo])) $task->assignedTo = $users[$task->assignedTo];
+1
View File
@@ -149,6 +149,7 @@ $lang->task->error->consumedNumber = '"Consumed" must be number';
$lang->task->error->consumedSmall = '"Consumed" must be more than consumed before';
$lang->task->error->consumedThisTime = 'Please input "hours"';
$lang->task->error->left = 'Please input "left"';
$lang->task->error->work = '"Comment" must be less than 255 characters';
/* Report. */
$lang->task->report = new stdclass();
+1
View File
@@ -149,6 +149,7 @@ $lang->task->error->consumedNumber = '"已经消耗"必须为数字';
$lang->task->error->consumedSmall = '"已经消耗"必须大于之前消耗';
$lang->task->error->consumedThisTime = '请填写"工时"';
$lang->task->error->left = '请填写"剩余"';
$lang->task->error->work = '"备注"必须小于255个字符';
/* 统计报表。*/
$lang->task->report = new stdclass();
+2 -1
View File
@@ -387,9 +387,9 @@ class taskModel extends model
$now = helper::now();
$task = fixer::input('post')
->setDefault('assignedTo', $this->app->user->account)
->setDefault('assignedDate', $now)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
->setIF($oldTask->assignedTo != $this->app->user->account, 'assignedDate', $now)
->remove('comment')->get();
if($this->post->left == 0)
{
@@ -440,6 +440,7 @@ class taskModel extends model
{
if(!$record->consumed[$id]) die(js::alert($this->lang->task->error->consumedThisTime));
if($record->left[$id] === '') die(js::alert($this->lang->task->error->left));
if(strlen($record->work[$id]) > 255) die(js::alert($this->lang->task->error->work));
$estimates[$id] = new stdclass();
$estimates[$id]->date = $record->dates[$id];
$estimates[$id]->task = $taskID;
+4 -4
View File
@@ -30,6 +30,10 @@
<th class='rowhead'><?php echo $lang->task->assignedTo;?></th>
<td><?php echo html::select('assignedTo[]', $members, $task->assignedTo, 'class=select-3');?></td>
</tr>
<tr>
<th class='rowhead'><?php echo $lang->task->type;?></th>
<td><?php echo html::select('type', $lang->task->typeList, $task->type, 'class=select-3 onchange="setOwners(this.value)"');?></td>
</tr>
<tr>
<th class='rowhead'><?php echo $lang->task->story;?></th>
<td>
@@ -65,10 +69,6 @@
<th class='rowhead'><?php echo $lang->task->deadline;?></th>
<td><?php echo html::input('deadline', $task->deadline, "class='text-3 date'");?></td>
</tr>
<tr>
<th class='rowhead'><?php echo $lang->task->type;?></th>
<td><?php echo html::select('type', $lang->task->typeList, $task->type, 'class=select-3 onchange="setOwners(this.value)"');?></td>
</tr>
<tr>
<th class='rowhead'><?php echo $lang->task->mailto;?></th>
<td>
+3 -14
View File
@@ -115,7 +115,8 @@ class testcase extends control
$queryProductID = $productID;
$allProduct = "`product` = 'all'";
$caseQuery = $this->session->testcaseQuery;
$caseQuery = '(' . $this->session->testcaseQuery;
if(strpos($this->session->testcaseQuery, $allProduct) !== false)
{
$products = array_keys($this->loadModel('product')->getPrivProducts());
@@ -123,6 +124,7 @@ class testcase extends control
$caseQuery = $caseQuery . ' AND `product`' . helper::dbIN(array_keys($products));
$queryProductID = 'all';
}
$caseQuery .= ')';
$caseQuery = $this->loadModel('search')->replaceDynamic($caseQuery);
$this->view->cases = $this->dao->select('*')->from(TABLE_CASE)->where($caseQuery)
@@ -814,8 +816,6 @@ class testcase extends control
$endField = $field;
$caseData = array();
$stepData = array();
$hasEdit = false;
$hasNew = false;
while($csv)
{
$case = new stdclass();
@@ -889,15 +889,6 @@ class testcase extends control
$csv = substr($csv, $pos + strlen($delimiter));
}
if(!empty($case->id))
{
$hasEdit = true;
}
else
{
$hasNew = true;
}
$caseData[$row] = $case;
unset($case);
$row++;
@@ -919,8 +910,6 @@ class testcase extends control
$this->view->stepData = $stepData;
$this->view->productID = $productID;
$this->view->product = $this->products[$productID];
$this->view->hasEdit = $hasEdit;
$this->view->hasNew = $hasNew;
$this->display();
}
}
+1 -4
View File
@@ -106,10 +106,7 @@ function createRow()
{
if(newRowID == 0) newRowID = $('.stepID').size();
newRowID ++;
var lblDelete = '<?php echo $lang->testcase->deleteStep;?>';
var lblBefore = '<?php echo $lang->testcase->insertBefore;?>';
var lblAfter = '<?php echo $lang->testcase->insertAfter;?>';
var newRow = "<tr class='a-center' id='row" + newRowID + "'>";
var newRow = "<tr class='a-center' id='row" + newRowID + "'>";
newRow += "<th class='stepID'></th>";
newRow += "<td class='w-p50'><textarea name='steps[]' rows=3 class='w-p100'></textarea></td>";
newRow += "<td><textarea name='expects[]' rows=3 class='w-p100'></textarea></td>";
+1 -2
View File
@@ -69,8 +69,7 @@ $lang->testcase->export = "Export data";
$lang->testcase->confirmChange = 'Confirm case change';
$lang->testcase->confirmStoryChange = 'Confirm story change';
$lang->testcase->editHaved = 'Modify existing case';
$lang->testcase->addNew = 'Add new case';
$lang->testcase->new = 'New';
$lang->testcase->num = 'The number of cases';
+1 -2
View File
@@ -69,8 +69,7 @@ $lang->testcase->export = "导出数据";
$lang->testcase->confirmChange = '确认用例变动';
$lang->testcase->confirmStoryChange = '确认需求变动';
$lang->testcase->editHaved = '修改已有用例';
$lang->testcase->addNew = '添加新用例';
$lang->testcase->new = '新增';
$lang->testcase->num = '导入用例个数:';
+3
View File
@@ -538,5 +538,8 @@ class testcaseModel extends model
}
}
}
unlink($this->session->importFile);
unset($_SESSION['importFile']);
}
}
+1 -1
View File
@@ -58,7 +58,7 @@ js::set('confirmDelete', $lang->testcase->confirmDelete);
</div>
<div id='querybox' class='<?php if($browseType != 'bysearch') echo 'hidden';?>'></div>
<div class='treeSlider'><span>&nbsp;</span></div>
<div class='treeSlider' id='testcaseTree'><span>&nbsp;</span></div>
<form id='batchForm' method='post'>
<table class='cont-lt1'>
<tr valign='top'>
+3
View File
@@ -13,6 +13,9 @@
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/form.html.php';?>
<?php include '../../common/view/chosen.html.php';?>
<?php js::set('lblDelete', $lang->testcase->deleteStep);?>
<?php js::set('lblBefore', $lang->testcase->insertBefore);?>
<?php js::set('lblAfter', $lang->testcase->insertAfter);?>
<form method='post' enctype='multipart/form-data' id='dataform' class='ajaxForm'>
<table class='table-1'>
<caption><?php echo $lang->testcase->create;?></caption>
+1 -2
View File
@@ -11,13 +11,12 @@ function setDownloading()
function closeWindow()
{
if($.cookie('downloading') == 1 || i >= 30)
if($.cookie('downloading') == 1)
{
parent.$.fn.colorbox.close();
$.cookie('downloading', null);
clearInterval(time);
}
i ++;
}
</script>
<form method='post' target='hiddenwin' onsubmit='setDownloading();'>
+43 -59
View File
@@ -1,8 +1,9 @@
<?php include '../../common/view/header.html.php';?>
<style>
.affix {position:fixed; top:0px; width:95.6%;z-index:10000;}
</style>
<form target='hiddenwin' method='post'>
<?php if($hasEdit):?>
<table class='table-1'>
<caption class='caption-tl'><?php echo $lang->testcase->editHaved?></caption>
<tr class='colhead'>
<th><?php echo $lang->testcase->id?></th>
<th><?php echo $lang->testcase->title?></th>
@@ -24,13 +25,24 @@
</th>
</tr>
<?php foreach($caseData as $key => $case):?>
<?php if(empty($case->id)) continue?>
<tr valign='top' align='center'>
<td><?php echo $case->id . html::hidden("id[$key]", $case->id) . html::hidden("product[$key]", $productID)?></td>
<td>
<?php
if(isset($case->id))
{
echo $case->id . html::hidden("id[$key]", $case->id);
}
else
{
echo $key . " <sub class='gray' style='vertical-align:sub;'>{$lang->testcase->new}</sub>";
}
echo html::hidden("product[$key]", $productID);
?>
</td>
<td><?php echo html::input("title[$key]", $case->title, "class='text-1' style='margin-top:2px'")?></td>
<td><?php echo html::select("module[$key]", $modules, isset($case->module) ? $case->module : $cases[$case->id]->module, "class='select-2'")?></td>
<td><?php echo html::select("story[$key]", $stories, isset($case->story) ? $case->story : $cases[$case->id]->story, "class='select-2'")?></td>
<td><?php echo html::select("pri[$key]", $lang->testcase->priList, isset($case->pri) ? $case->pri : $cases[$case->id]->pri)?></td>
<td><?php echo html::select("module[$key]", $modules, isset($case->module) ? $case->module : (isset($case->id) ? $cases[$case->id]->module : ''), "class='select-2'")?></td>
<td><?php echo html::select("story[$key]", $stories, isset($case->story) ? $case->story : (isset($case->id) ? $cases[$case->id]->story : ''), "class='select-2'")?></td>
<td><?php echo html::select("pri[$key]", $lang->testcase->priList, isset($case->pri) ? $case->pri : (isset($case->id) ? $cases[$case->id]->pri : ''))?></td>
<td><?php echo html::select("type[$key]", $lang->testcase->typeList, $case->type)?></td>
<td><?php echo html::select("status[$key]", $lang->testcase->statusList, isset($case->status) ? $case->status : '')?></td>
<td><?php echo html::input("frequency[$key]", isset($case->frequency) ? $case->frequency : 1, "size='2'")?></td>
@@ -52,58 +64,30 @@
<?php unset($caseData[$key]);?>
<?php endforeach;?>
</table>
<?php endif;?>
<?php if($hasNew):?>
<table class='table-1'>
<caption class='caption-tl'><?php echo $lang->testcase->addNew?></caption>
<tr class='colhead'>
<th><?php echo $lang->testcase->importID?></th>
<th><?php echo $lang->testcase->title?></th>
<th><?php echo $lang->testcase->module?></th>
<th><?php echo $lang->testcase->story?></th>
<th><?php echo $lang->testcase->pri?></th>
<th><?php echo $lang->testcase->type?></th>
<th><?php echo $lang->testcase->status?></th>
<th><?php echo $lang->testcase->frequency?></th>
<th><?php echo $lang->testcase->stage?></th>
<th><?php echo $lang->testcase->precondition?></th>
<th><?php echo $lang->testcase->steps?>
<table class='table-1'>
<tr>
<th><?php echo $lang->testcase->stepDesc?></th>
<th><?php echo $lang->testcase->stepExpect?></th>
</tr>
</table>
</th>
</tr>
<?php foreach($caseData as $key => $case):?>
<tr valign='top' align='center'>
<td><?php echo $key + 1?></td>
<td><?php echo html::input("title[$key]", $case->title, "class='text-1' style='margin-top:2px'")?></td>
<td><?php echo html::select("module[$key]", $modules, isset($case->module) ? $case->module : '', "class='select-2'") . html::hidden("product[$key]", $productID)?></td>
<td><?php echo html::select("story[$key]", $stories, isset($case->story) ? $case->story : '', "class='select-2'")?></td>
<td><?php echo html::select("pri[$key]", $lang->testcase->priList, isset($case->pri) ? $case->pri : '')?></td>
<td><?php echo html::select("type[$key]", $lang->testcase->typeList, $case->type)?></td>
<td><?php echo html::select("status[$key]", $lang->testcase->statusList, isset($case->status) ? $case->status : 'normal')?></td>
<td><?php echo html::input("frequency[$key]", isset($case->frequency) ? $case->frequency : 1, "size='2'")?></td>
<td><?php echo html::select("stage[$key][]", $lang->testcase->stageList, isset($case->stage) ? $case->stage : '', "multiple='multiple'")?></td>
<td><?php echo html::textarea("precondition[$key]", isset($case->precondition) ? $case->precondition : "", "style='margin-top:2px; height:69px'")?></td>
<td>
<?php if(isset($stepData[$key]['desc'])):?>
<table class='table-1'>
<?php foreach($stepData[$key]['desc'] as $id => $desc):?>
<tr>
<td><?php echo html::textarea("desc[$key][$id]", $desc)?></td>
<td><?php echo html::textarea("expect[$key][$id]", isset($stepData[$key]['expect'][$id]) ? $stepData[$key]['expect'][$id] : '')?></td>
</tr>
<?php endforeach;?>
</table>
<?php endif;?>
</td>
</tr>
<?php endforeach;?>
</table>
<?php endif;?>
<p><?php echo html::submitButton() . html::backButton()?></p>
</form>
<script>
$(function(){affix('.colhead')})
function affix(obj)
{
var fixH = $(obj).offset().top;
var first = true;
$(window).scroll(function()
{
var scroH = $(this).scrollTop();
if(scroH>=fixH && first)
{
$(obj).parent().parent().before("<table id='headerClone'></table>");
$('#headerClone').append($(obj).clone()).addClass('affix');
$('.table-1 ' + obj + ' th').each(function(i){$('#headerClone ' + obj + ' th').eq(i).width($(this).width())});
first = false;
}
else if(scroH<fixH)
{
$("#headerClone").remove();
first = true;
}
});
}
</script>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -303,7 +303,7 @@ class tree extends control
*/
public function ajaxGetOptionMenu($rootID, $viewType = 'story', $rootModuleID = 0, $returnType = 'html', $needManage = false)
{
if($viewType = 'task')
if($viewType == 'task')
{
$optionMenu = $this->tree->getTaskOptionMenu($rootID);
}
+3 -3
View File
@@ -16,7 +16,7 @@ $lang->user->dept = '所属部门';
$lang->user->account = '用户名';
$lang->user->password = '密码';
$lang->user->password2 = '请重复密码';
$lang->user->role = '角色';
$lang->user->role = '职位';
$lang->user->group = '分组';
$lang->user->realname = '真实姓名';
$lang->user->nickname = '昵称';
@@ -124,7 +124,7 @@ $lang->user->loginWithDemoUser = '使用demo账号登录:';
$lang->user->placeholder = new stdclass();
$lang->user->placeholder->account = '英文、数字和下划线的组合,三位以上';
$lang->user->placeholder->password1 = '六位以上';
$lang->user->placeholder->role = '角色影响内容和用户列表的顺序。';
$lang->user->placeholder->role = '职位影响内容和用户列表的顺序。';
$lang->user->placeholder->group = '分组决定用户的权限列表。';
$lang->user->placeholder->join = '入职日期';
$lang->user->placeholder->commiter = '版本控制系统(subversion)中的帐号';
@@ -135,7 +135,7 @@ $lang->user->error->accountDupl = "ID %s,该用户名已经存在";
$lang->user->error->realname = "ID %s,必须填写真实姓名";
$lang->user->error->password = "ID %s,密码必须六位以上";
$lang->user->error->mail = "ID %s,请填写正确的邮箱地址";
$lang->user->error->role = "ID %s角色不能为空";
$lang->user->error->role = "ID %s职位不能为空";
$lang->user->contacts = new stdclass();
$lang->user->contacts->common = '联系人';
+8 -1
View File
@@ -115,7 +115,14 @@ class userModel extends model
if(!$rawCommiters) return array();
$commiters = array();
foreach($rawCommiters as $commiter) $commiters[$commiter->commiter] = $commiter->realname ? $commiter->realname : $commiter->account;
foreach($rawCommiters as $commiter)
{
$userCommiters = explode(',', $commiter->commiter);
foreach($userCommiters as $userCommiter)
{
$commiters[$userCommiter] = $commiter->realname ? $commiter->realname : $commiter->account;
}
}
return $commiters;
}
-2
View File
@@ -21,7 +21,6 @@ $jsFiles[] = $jqueryRoot . 'datepicker/date.js';
$jsFiles[] = $jqueryRoot . 'datepicker/min.js';
$jsFiles[] = $jqueryRoot . 'alert/min.js';
$jsFiles[] = $jqueryRoot . 'colorize/min.js';
$jsFiles[] = $jqueryRoot . 'dropmenu/dropmenu.js';
/* Combine these js files. */
$allJSFile = $jsRoot . 'all.js';
@@ -71,7 +70,6 @@ foreach($langs as $lang)
$cssCode .= file_get_contents($themeRoot . 'default/treeview.css');
$cssCode .= file_get_contents($themeRoot . 'default/datepicker.css');
$cssCode .= file_get_contents($themeRoot . 'default/alert.css');
$cssCode .= file_get_contents($themeRoot . 'default/dropmenu.css');
/* Css file for current lang and current them. */
$cssCode .= file_get_contents($themeRoot . "lang/$lang.css");
+4 -1
View File
@@ -13,8 +13,11 @@ $.extend(
{
$.enableForm(formID);
/* try parse to json when response is json's string. */
try{if(typeof(response) == 'string') response = JSON.parse(response);}catch(e){}
/* The response is not an object, some error occers, alert it. */
if($.type(response) != 'object')
if(typeof(response) != 'object')
{
if(response) return alert(response);
return alert('No response.');
+41 -10
View File
@@ -285,6 +285,39 @@ function toggleHelpLink()
if($.cookie('help') == 'on') return $.cookie('help', 'off', {expires:config.cookieLife, path:config.webRoot});
}
/**
* Hide tree box
*
* @param string $treeType
* @access public
* @return void
*/
function hideTreeBox(treeType)
{
$.cookie(treeType, 'hide', {expires:config.cookieLife, path:config.webRoot});
$('.side').hide();
$('.divider').hide();
$('.treeSlider span').css("border-right", "0 none");
$('.treeSlider span').css("border-left", "4px solid #000000");
}
/**
* Show tree box
*
* @param string $treeType
* @access public
* @return void
*/
function showTreeBox(treeType)
{
$.cookie(treeType, 'show', {expires:config.cookieLife, path:config.webRoot});
$('.side').show();
$('.divider').show();
$('.treeSlider span').css("border-right", "4px solid #000000");
$('.treeSlider span').css("border-left", "0 none");
}
/**
* Toggle tree menu.
@@ -293,23 +326,21 @@ function toggleHelpLink()
*/
function toggleTreeBox()
{
var treeSliderH = 50;
$('.treeSlider').height(treeSliderH);
var treeType = $('.treeSlider').attr('id');
if(typeof treeType == 'undefined') return;
if($.cookie(treeType) == 'hide') hideTreeBox(treeType);
$('.treeSlider').toggle
(
function()
{
$('.side').hide()
$('.divider').hide()
$('.treeSlider span').css("border-right", "0 none");
$('.treeSlider span').css("border-left", "4px solid #000000");
if($.cookie(treeType) == 'hide') return showTreeBox(treeType);
hideTreeBox(treeType);
},
function()
{
$('.side').show()
$('.divider').show()
$('.treeSlider span').css("border-right", "4px solid #000000");
$('.treeSlider span').css("border-left", "0 none");
if($.cookie(treeType) == 'show') return hideTreeBox(treeType);
showTreeBox(treeType);
}
);
}
+1
View File
@@ -358,6 +358,7 @@ table.tablesorter thead tr .headerSortDown {background-image: url(./images/table
border-left:none;
cursor: pointer;
width: 8px;
height: 50px;
left: 16px;
border-radius:0px 10px 10px 0px
}