diff --git a/lib/scm/gitlab.class.php b/lib/scm/gitlab.class.php
index ecf9871531..aad2bbb639 100644
--- a/lib/scm/gitlab.class.php
+++ b/lib/scm/gitlab.class.php
@@ -501,7 +501,8 @@ class gitlab
if($version)
{
$lastCommit = $this->getSingleCommit($version);
- if(empty($lastCommit)) return array();
+ if(!isset($lastCommit->committed_date)) return array('commits' => array(), 'files' => array());
+
$params['until'] = $lastCommit->committed_date;
}
@@ -644,7 +645,13 @@ class gitlab
$api = ltrim($api, '/');
$api = $this->root . $api . '?' . http_build_query($params);
- $response = file_get_contents($api);
+ $response = commonModel::http($api);
+ if(!empty(commonModel::$requestErrors))
+ {
+ commonModel::$requestErrors = array();
+ return array();
+ }
+
return json_decode($response);
}
diff --git a/module/bug/control.php b/module/bug/control.php
index fb35aca121..aa08869665 100644
--- a/module/bug/control.php
+++ b/module/bug/control.php
@@ -401,7 +401,7 @@ class bug extends control
else
{
setcookie('bugModule', 0, 0, $this->config->webRoot, '', $this->config->cookieSecure, false);
- $location = $this->createLink('bug', 'browse', "productID={$this->post->product}&branch=$branch&browseType=unclosed¶m=0&orderBy=id_desc");
+ $location = $this->createLink('bug', 'browse', "productID={$this->post->product}&branch=$branch&browseType=byModule¶m={$this->post->module}&orderBy=id_desc");
}
if($this->app->getViewType() == 'xhtml') $location = $this->createLink('bug', 'view', "bugID=$bugID");
$response['locate'] = $location;
diff --git a/module/bug/js/common.js b/module/bug/js/common.js
index 92ff94764e..a3699ef386 100644
--- a/module/bug/js/common.js
+++ b/module/bug/js/common.js
@@ -209,7 +209,8 @@ function loadProductModules(productID)
{
branch = $('#branch').val();
if(typeof(branch) == 'undefined') branch = 0;
- link = createLink('tree', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=bug&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=&needManage=true');
+ if(typeof(moduleID) == 'undefined') moduleID = 0;
+ link = createLink('tree', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=bug&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=&needManage=true&extra=¤tModuleID=' + moduleID);
$('#moduleIdBox').load(link, function()
{
$(this).find('select').chosen()
diff --git a/module/bug/view/create.html.php b/module/bug/view/create.html.php
index 7dba03779c..a742898300 100644
--- a/module/bug/view/create.html.php
+++ b/module/bug/view/create.html.php
@@ -25,6 +25,7 @@ js::set('stepsNotEmpty', $lang->bug->stepsNotEmpty);
js::set('isStepsTemplate', $isStepsTemplate);
js::set('oldProjectID', $projectID);
js::set('blockID', $blockID);
+js::set('moduleID', $moduleID);
?>
diff --git a/module/cron/lang/zh-cn.php b/module/cron/lang/zh-cn.php
index 5f526482c7..c7919b7a71 100644
--- a/module/cron/lang/zh-cn.php
+++ b/module/cron/lang/zh-cn.php
@@ -54,3 +54,4 @@ $lang->cron->notice->mon = '取值范围:1-12';
$lang->cron->notice->dow = '取值范围:0-6';
$lang->cron->notice->help = '注:如果服务器重启,或者发现计划任务没有正常工作,那么计划任务已经停止工作。需要手动点击【重启】按钮,或者一分钟后刷新页面,来开启计划任务。如果任务列表中第一条记录的最后执行时间改变,说明任务开启成功。';
$lang->cron->notice->errorRule = '"%s" 填写的不是合法的值';
+$lang->cron->notice->errorType = '不能创建操作系统命令类型的计划任务';
diff --git a/module/cron/model.php b/module/cron/model.php
index 6690222c3b..ab9822d8fc 100644
--- a/module/cron/model.php
+++ b/module/cron/model.php
@@ -13,8 +13,8 @@ class cronModel extends model
{
/**
* Get by Id.
- *
- * @param int $cronID
+ *
+ * @param int $cronID
* @access public
* @return object
*/
@@ -25,8 +25,8 @@ class cronModel extends model
/**
* Get crons.
- *
- * @param string $params
+ *
+ * @param string $params
* @access public
* @return array
*/
@@ -40,8 +40,8 @@ class cronModel extends model
/**
* Parse crons.
- *
- * @param array $crons
+ *
+ * @param array $crons
* @access public
* @return array
*/
@@ -64,12 +64,12 @@ class cronModel extends model
$parsedCron['cron'] = CronExpression::factory($parsedCron['schema']);
$parsedCron['time'] = $parsedCron['cron']->getNextRunDate();
$parsedCrons[$cron->id] = $parsedCron;
- }
- catch(InvalidArgumentException $e)
+ }
+ catch(InvalidArgumentException $e)
{
$this->dao->update(TABLE_CRON)->set('status')->eq('stop')->where('id')->eq($cron->id)->exec();
continue;
- }
+ }
}
}
$this->dao->update(TABLE_CRON)->set('lastTime')->eq(date(DT_DATETIME1))->where('lastTime')->eq('0000-00-00 00:00:00')->andWhere('status')->ne('stop')->exec();
@@ -78,10 +78,10 @@ class cronModel extends model
/**
* Change cron status.
- *
- * @param int $cronID
- * @param string $status
- * @param bool $changeTime
+ *
+ * @param int $cronID
+ * @param string $status
+ * @param bool $changeTime
* @access public
* @return bool
*/
@@ -96,8 +96,8 @@ class cronModel extends model
/**
* Log cron.
- *
- * @param string $log
+ *
+ * @param string $log
* @access public
* @return void
*/
@@ -115,7 +115,7 @@ class cronModel extends model
/**
* Get last execed time.
- *
+ *
* @access public
* @return string
*/
@@ -127,7 +127,7 @@ class cronModel extends model
/**
* Runable cron.
- *
+ *
* @access public
* @return bool
*/
@@ -145,7 +145,7 @@ class cronModel extends model
/**
* Check change cron.
- *
+ *
* @access public
* @return bool
*/
@@ -156,8 +156,8 @@ class cronModel extends model
}
/**
- * Create cron.
- *
+ * Create cron.
+ *
* @access public
* @return int
*/
@@ -169,6 +169,12 @@ class cronModel extends model
->skipSpecial('m,h,dom,mon,dow,command')
->get();
+ if(!$this->config->features->cronSystemCall and $cron->type == 'system')
+ {
+ dao::$errors[] = $this->lang->cron->notice->errorType;
+ return false;
+ }
+
$result = $this->checkRule($cron);
if(!empty($result))
{
@@ -182,9 +188,9 @@ class cronModel extends model
}
/**
- * Update cron.
- *
- * @param int $cronID
+ * Update cron.
+ *
+ * @param int $cronID
* @access public
* @return bool
*/
@@ -195,6 +201,12 @@ class cronModel extends model
->skipSpecial('m,h,dom,mon,dow,command')
->get();
+ if(!$this->config->features->cronSystemCall and $cron->type == 'system')
+ {
+ dao::$errors[] = $this->lang->cron->notice->errorType;
+ return false;
+ }
+
$result = $this->checkRule($cron);
if(!empty($result))
{
@@ -208,8 +220,8 @@ class cronModel extends model
/**
* Check cron rule.
- *
- * @param object $cron
+ *
+ * @param object $cron
* @access public
* @return string
*/
@@ -256,7 +268,7 @@ class cronModel extends model
/**
* Get current cron status.
- *
+ *
* @access public
* @return int
*/
diff --git a/module/execution/model.php b/module/execution/model.php
index 68f135c88c..ee2c4fb8ce 100644
--- a/module/execution/model.php
+++ b/module/execution/model.php
@@ -72,7 +72,6 @@ class executionModel extends model
if($execution->type == 'stage')
{
global $lang;
- $this->loadModel('execution');
$this->app->loadLang('project');
$lang->executionCommon = $lang->project->stage;
include $this->app->getModulePath('execution') . 'lang/' . $this->app->getClientLang() . '.php';
diff --git a/module/repo/control.php b/module/repo/control.php
index 9580fd9d4f..411c1b0bc5 100644
--- a/module/repo/control.php
+++ b/module/repo/control.php
@@ -867,7 +867,7 @@ class repo extends control
set_time_limit(0);
$repo = $this->repo->getRepoByID($repoID);
if(empty($repo)) die();
- //if($repo->synced) die('finish');
+ if($repo->synced) die('finish');
$this->commonAction($repoID);
$this->scm->setEngine($repo);
diff --git a/module/repo/model.php b/module/repo/model.php
index fd22ec2338..03ec69715a 100644
--- a/module/repo/model.php
+++ b/module/repo/model.php
@@ -281,15 +281,18 @@ class repoModel extends model
$repo = $this->getRepoByID($id);
$data = fixer::input('post')
+ ->setIf($this->post->SCM == 'Gitlab', 'password', $this->post->gitlabToken)
+ ->setIf($this->post->SCM == 'Gitlab', 'client', $this->post->gitlabHost)
+ ->setIf($this->post->SCM == 'Gitlab', 'extra', $this->post->gitlabProject)
->setDefault('client', 'svn')
->setDefault('prefix', $repo->prefix)
->setDefault('product', '')
- ->setIF($this->post->path != $repo->path, 'synced', 0)
->skipSpecial('path,client,account,password')
->join('product', ',')
->get();
if($this->post->SCM == 'Gitlab') $data->path = sprintf($this->config->repo->gitlab->apiPath, $data->gitlabHost, $this->post->gitlabProject);
+ if($data->path != $repo->path) $data->synced = 0;
$data->acl = empty($data->acl) ? '' : json_encode($data->acl);
@@ -313,7 +316,7 @@ class repoModel extends model
$this->dao->update(TABLE_REPO)->data($data, $skip = 'gitlabHost,gitlabToken,gitlabProject')
->batchCheck($this->config->repo->edit->requiredFields, 'notempty')
->checkIF($data->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty')
- ->checkIF($data->SCM == 'GitLab', 'gitlabProject', 'notempty')
+ ->checkIF($data->SCM == 'GitLab', 'extra', 'notempty')
->autoCheck()
->where('id')->eq($id)->exec();
diff --git a/module/todo/css/batchcreate.css b/module/todo/css/batchcreate.css
index 222fe0e42e..19576bc2ab 100755
--- a/module/todo/css/batchcreate.css
+++ b/module/todo/css/batchcreate.css
@@ -16,3 +16,7 @@ html[lang="en"] .main-header > .input-group {width: 250px;}
#formSettingBtn {position: absolute; top: 14px; right: 20px; z-index: 1000;}
#formSettingBtn .dropdown {margin-right: 0px;}
+#formSettingBtn #formSetting {padding: 10px 12px;}
+#formSettingBtn .checkboxes {padding: 10px 3px;}
+#formSettingBtn .checkbox-primary {width: 50%; float: left; margin: 3px 0;}
+#formSetting .btn {margin-right: 8px;}
diff --git a/module/tree/control.php b/module/tree/control.php
index d1d3686718..eaf069b278 100644
--- a/module/tree/control.php
+++ b/module/tree/control.php
@@ -419,10 +419,12 @@ class tree extends control
* @param string $returnType
* @param string $fieldID
* @param bool $needManage
+ * @param string $extra
+ * @param int $currentModuleID
* @access public
* @return string the html select string.
*/
- public function ajaxGetOptionMenu($rootID, $viewType = 'story', $branch = 0, $rootModuleID = 0, $returnType = 'html', $fieldID = '', $needManage = false, $extra = '')
+ public function ajaxGetOptionMenu($rootID, $viewType = 'story', $branch = 0, $rootModuleID = 0, $returnType = 'html', $fieldID = '', $needManage = false, $extra = '', $currentModuleID = 0)
{
if($viewType == 'task')
{
@@ -448,7 +450,7 @@ class tree extends control
$changeFunc = '';
if($viewType == 'task' or $viewType == 'bug' or $viewType == 'case') $changeFunc = "onchange='loadModuleRelated()'";
$field = $fieldID ? "modules[$fieldID]" : 'module';
- $output = html::select("$field", $optionMenu, '', "class='form-control' $changeFunc");
+ $output = html::select("$field", $optionMenu, $currentModuleID, "class='form-control' $changeFunc");
if(count($optionMenu) == 1 and $needManage)
{
$output .= "";
diff --git a/module/user/model.php b/module/user/model.php
index ee703dba6e..3b68f42bee 100644
--- a/module/user/model.php
+++ b/module/user/model.php
@@ -191,7 +191,7 @@ class userModel extends model
/**
* Get user info by ID.
*
- * @param int $userID
+ * @param mix $userID
* @access public
* @return object|bool
*/
diff --git a/test/init.php b/test/init.php
index 23acb0c613..93c3aa8626 100644
--- a/test/init.php
+++ b/test/init.php
@@ -1,6 +1,6 @@
loadCommon() 方法创建名为 tester 的commonModel对象。
+ * 本文件创建一个 app 实例,并且通过执行 $app->loadCommon() 方法创建名为 tester 的commonModel对象。
* This file build a app instance and provide a instance of commonModel named as tester by $app->loadCommon().
*
* All request of entries should be routed by this router.
@@ -13,13 +13,89 @@
* @link http://www.zentao.net
*/
/* Set the error reporting. */
-error_reporting(0);
+error_reporting(E_ALL & E_STRICT);
+
+$frameworkRoot = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'framework' . DIRECTORY_SEPARATOR;
/* Load the framework. */
-include '../framework/router.class.php';
-include '../framework/control.class.php';
-include '../framework/model.class.php';
-include '../framework/helper.class.php';
+include $frameworkRoot . 'router.class.php';
+include $frameworkRoot . 'control.class.php';
+include $frameworkRoot . 'model.class.php';
+include $frameworkRoot . 'helper.class.php';
$app = router::createApp('pms', dirname(dirname(__FILE__)), 'router');
$tester = $app->loadCommon();
+
+$config->zendataRoot = dirname(__FILE__) . DS . 'zendata';
+$config->zdPath = "/usr/local/zd/zd";
+
+/**
+ * Save variable to $_result.
+ *
+ * @param mix $result
+ * @access public
+ * @return bool true
+ */
+function run($result)
+{
+ global $_result;
+ $_result = $result;
+ return true;
+}
+
+/**
+ * Print expect data.
+ *
+ * @param int $key
+ * @param string $delimiter
+ * @access public
+ * @return void
+ */
+function expect($key, $delimiter = ',')
+{
+ global $_result;
+ echo ">> ";
+ $result = "";
+
+ if(!is_array($_result) and !is_object($_result))
+ {
+ $result = (string) $_result;
+ }
+ else
+ {
+ $keyList = explode(',', $key);
+ $dimension = 1;
+ foreach($_result as $value)
+ {
+ if(is_array($value) or is_object($value)) $dimension = 2;
+ }
+
+ if($dimension == 1) $_result = array($_result);
+ foreach($_result as $object)
+ {
+ foreach($keyList as $key) $result .= zget($object, $key, '') . $delimiter;
+ }
+ $result = trim($result, $delimiter);
+ }
+ echo $result . "\n";
+ echo "\n";
+}
+
+/**
+ * Import data create by zendata to one table.
+ *
+ * @param string $table
+ * @param string $yaml
+ * @param int $count
+ * @access public
+ * @return void
+ */
+function zdImport($table, $yaml, $count = 10)
+{
+ chdir(dirname(__FILE__));
+ global $app, $config;
+ $dns = "mysql://{$config->db->user}:{$config->db->password}@{$config->db->host}:{$config->db->port}/{$config->db->name}#utf8";
+ $table = trim($table, '`');
+ $commad = "$config->zdPath -c $yaml -t $table -T -dns $dns --clear -n $count";
+ system($commad);
+}
diff --git a/test/model/user/getbyid.php b/test/model/user/getbyid.php
new file mode 100755
index 0000000000..b1643434ab
--- /dev/null
+++ b/test/model/user/getbyid.php
@@ -0,0 +1,31 @@
+#!/usr/bin/env php
+> `\d,\w+`
+ 2. 使用account获取一个存在的用户 >> `\d`
+ 3. 使用account获取一个存在的用户 >> ``
+[esac]
+*/
+$user = $tester->loadModel('user');
+
+$app->dbh->query("truncate zt_user");
+zdImport(TABLE_USER, "zendata/user.yaml", 10);
+
+$randUser = $tester->dao->select('*,rand() as rand')->from(TABLE_USER)->orderBy('rand')->fetch();
+if(!$randUser) exit("Prepair data error.");
+unset($randUser->rand);
+
+/* Step 1.*/
+run($user->getByID($randUser->id, 'id')) and expect('id,account');
+
+/* Step 2.*/
+run($user->getByID($randUser->account, 'account')) and expect('id');
+
+/* Step 3.*/
+run($user->getByID(null, 'id')) and expect('id');
diff --git a/test/zendata/user.yaml b/test/zendata/user.yaml
new file mode 100644
index 0000000000..5acb5c1855
--- /dev/null
+++ b/test/zendata/user.yaml
@@ -0,0 +1,256 @@
+title: table zt_user
+desc: "用户信息"
+author: automated export
+version: "1.0"
+fields:
+ - field: id
+ note: "ID"
+ range: 1-10000
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: dept
+ note: "所属部门"
+ range: 1-10000
+ prefix: ""
+ postfix: "\t"
+ format: ""
+ - field: account
+ note: "用户名"
+ from: name.enaccount.v1.yaml
+ use: common_underline
+ postfix: "\t" # 后缀,特殊字符加引号,否则无法解析。
+ format: ""
+ - field: password
+ note: "密码"
+ range: 123456
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: role
+ note: "职位"
+ range: qa,dev,pm,po,td,pd,qd,top,market,service,operation,support,others
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: realname
+ note: "真实姓名"
+ from: name.cnreal.v1.yaml
+ use: three
+ format: ""
+ - field: nickname
+ note: "昵称"
+ from: name.enaccount.v1.yaml
+ use: common
+ postfix: "\t"
+ loop: 0
+ - field: commiter
+ note: "源代码帐号"
+ from: name.enaccount.v1.yaml
+ use: common
+ postfix: "@zentao.net"
+ loop: 0
+ format: ""
+ - field: avatar
+ note: ""
+ range: 0
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: birthday
+ note: "生日"
+ from: time.date.v1.yaml
+ use: date
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: gender
+ note: "性别"
+ range: f,m
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: email
+ note: "邮箱"
+ range: 1-10000
+ prefix: "这里是邮箱"
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: skype
+ note: "Skype"
+ range: Skype
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: qq
+ note: "QQ"
+ range: QQ
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: mobile
+ note: "手机"
+ range: mobile
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: phone
+ note: "电话"
+ range: phone
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: weixin
+ note: "微信"
+ range: weixin
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: dingding
+ note: "钉钉"
+ range: dingding
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: slack
+ note: "Slack"
+ range: slack
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: whatsapp
+ note: "WhatsApp"
+ range: whatsApp
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: address
+ note: "通讯地址"
+ range: 1-10000
+ prefix: "这是通讯地址"
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: zipcode
+ note: "邮编"
+ range: 1-10000
+ prefix: "这是邮编"
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: join
+ note: "入职日期"
+ from: time.date.v1.yaml
+ use: date
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: visits
+ note: "访问次数"
+ range: 0-10000:R
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: ip
+ note: "最后IP"
+ from: ip.v1.yaml
+ use: ipA,ipB
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: last
+ note: "最后登录"
+ range: 1-10000
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: fails
+ note: "失败次数"
+ range: 1-10000
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: locked
+ note: "锁定时间"
+ from: time.date.v1.yaml
+ use: date
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: feedback
+ note: "反馈[非研发]"
+ range: 0
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: ranzhi
+ note: "ZDOO账号"
+ range: 0
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: ldap
+ note: ""
+ range: 0
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: score
+ note: "积分"
+ range: 1-10000:R
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: scoreLevel
+ note: "积分等级"
+ range: 0
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: deleted
+ note: "是否删除"
+ range: 0
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: clientStatus
+ note: "连接状态"
+ range: offline
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""
+ - field: clientLang
+ note: "连接语言"
+ range: en
+ prefix: ""
+ postfix: "\t"
+ loop: 0
+ format: ""