diff --git a/db/update18.1.sql b/db/update18.1.sql index ef6ffc263f..0f4b3dd000 100644 --- a/db/update18.1.sql +++ b/db/update18.1.sql @@ -2,9 +2,9 @@ DELETE FROM `zt_config` WHERE module = 'datatable' AND section = 'executionAll'; update `zt_task` set `assignedTo` = '' where `mode` = 'multi' and `status` != 'done' and `status` != 'closed'; -REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) SELECT `lang`, `module`, 'scrumClassify', `key`, `value`, `system`, `vision` FROM `zt_lang` WHERE `module` = 'process' and `section` = 'classify' ORDER BY id ASC; -REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) SELECT `lang`, `module`, 'agileplusClassify', `key`, `value`, `system`, `vision` FROM `zt_lang` WHERE `module` = 'process' and `section` = 'classify' ORDER BY id ASC; -REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) SELECT `lang`, `module`, 'waterfallplusClassify', `key`, `value`, `system`, `vision` FROM `zt_lang` WHERE `module` = 'process' and `section` = 'classify' ORDER BY id ASC; +REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) SELECT `lang`, `module`, 'scrumClassify', `key`, `value`, `system`, `vision` FROM `zt_lang` WHERE `module` = 'process' and `section` = 'classify' and `system` = '1' ORDER BY id ASC; +REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) SELECT `lang`, `module`, 'agileplusClassify', `key`, `value`, `system`, `vision` FROM `zt_lang` WHERE `module` = 'process' and `section` = 'classify' and `system` = '1' ORDER BY id ASC; +REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) SELECT `lang`, `module`, 'waterfallplusClassify', `key`, `value`, `system`, `vision` FROM `zt_lang` WHERE `module` = 'process' and `section` = 'classify' and `system` = '1' ORDER BY id ASC; UPDATE `zt_project` AS parent INNER JOIN (select `id`,`parent`,`attribute` from `zt_project` where `parent` != 0 and `type` = 'stage') AS child ON parent.`id` = child.`parent` SET parent.`attribute`='mix' where parent.`grade`=1 and parent.`type`='stage' and parent.`attribute` != child.`attribute`; diff --git a/extension/lite/project/ext/view/create.html.php b/extension/lite/project/ext/view/create.html.php index ce3e1f1454..0308b9d310 100644 --- a/extension/lite/project/ext/view/create.html.php +++ b/extension/lite/project/ext/view/create.html.php @@ -40,7 +40,7 @@ project->name;?> - setCode) or $config->setCode == 1):?> + setCode) and $config->setCode == 1):?> project->code;?> diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php new file mode 100644 index 0000000000..7d17873b08 --- /dev/null +++ b/lib/cache/cache.class.php @@ -0,0 +1,53 @@ + + * @package cache + * @link http://www.zentao.net + */ + +helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheInterface.php'); +helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheException.php'); +helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'InvalidArgumentException.php'); +helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'ApcuDriver.php'); +helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'YacDriver.php'); + +use ZenTao\Cache\SimpleCache\InvalidArgumentException; + +class cache +{ + /** + * @var ZenTao\Cache\SimpleCache\CacheInterface + */ + protected $client; + + public function __construct($driver = 'Apcu', $namespace = '', $defaultLifetime = 0) + { + $driver = ucfirst(strtolower($driver)); + switch($driver) + { + case 'Apcu': + $className = 'ZenTao\Cache\Driver\ApcuDriver'; + break; + case 'Yac': + $className = 'ZenTao\Cache\Driver\YacDriver'; + break; + default: + throw new InvalidArgumentException("Driver {$driver} is not supported."); + } + + if(!extension_loaded($driver)) throw new InvalidArgumentException("Driver ext-{$driver} is not loaded."); + + $this->client = new $className($namespace, $defaultLifetime); + } + + public function __call($name, $arguments) + { + if(!method_exists($this->client, $name)) throw new InvalidArgumentException("Method {$name} does not exist."); + + return call_user_func_array(array($this->client, $name), $arguments); + } +} diff --git a/lib/cache/driver/ApcuDriver.php b/lib/cache/driver/ApcuDriver.php new file mode 100644 index 0000000000..7c2def717a --- /dev/null +++ b/lib/cache/driver/ApcuDriver.php @@ -0,0 +1,173 @@ + + * @package cache + * @link http://www.zentao.net + */ + +namespace ZenTao\Cache\Driver; + +use ZenTao\Cache\SimpleCache\CacheInterface; +use ZenTao\Cache\SimpleCache\InvalidArgumentException; + +class ApcuDriver implements CacheInterface +{ + /** + * @var string + */ + private $namespace; + + /** + * @var int + */ + private $defaultLifetime; + + public function __construct($namespace = '', $defaultLifetime = 0) + { + $this->namespace = $namespace; + $this->defaultLifetime = $defaultLifetime; + } + + public function get($key, $default = null) + { + $this->assertKeyName($key); + $key = $this->buildKeyName($key); + + $value = apcu_fetch($key, $success); + + return $success === false ? $default : $value; + } + + public function set($key, $value, $ttl = null) + { + $this->assertKeyName($key); + $key = $this->buildKeyName($key); + + $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; + + return apcu_store($key, $value, (int) $ttl); + } + + public function delete($key) + { + $this->assertKeyName($key); + $key = $this->buildKeyName($key); + + return apcu_delete($key); + } + + public function clear() + { + return apcu_clear_cache(); + } + + public function getMultiple($keys, $default = null) + { + $this->assertKeyNames($keys); + $keys = $this->buildKeyNames($keys); + + $result = apcu_fetch($keys); + + if(!is_null($default) && is_array($result) && count($keys) > count($result)) + { + $notFoundKeys = array_diff($keys, array_keys($result)); + $result = array_merge($result, array_fill_keys($notFoundKeys, $default)); + } + + $mappedResult = array(); + + foreach($result as $key => $value) + { + $key = preg_replace("/^$this->namespace/", '', $key); + + $mappedResult[$key] = $value; + } + + return $mappedResult; + } + + public function setMultiple($values, $ttl = null) + { + $this->assertKeyNames(array_keys($values)); + + $mappedByNamespaceValues = array(); + + foreach($values as $key => $value) + { + $mappedByNamespaceValues[$this->buildKeyName($key)] = $value; + } + + $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; + + $result = apcu_store($mappedByNamespaceValues, (int) $ttl); + + return $result === true ? true : (is_array($result) && count($result) == 0 ? true : false); + } + + public function deleteMultiple($keys) + { + $this->assertKeyNames($keys); + $keys = $this->buildKeyNames($keys); + + $result = apcu_delete($keys); + + return count($result) === count($keys) ? false : true; + } + + public function has($key) + { + $this->assertKeyName($key); + $key = $this->buildKeyName($key); + + return (bool) apcu_exists($key); + } + + /** + * @param string $key + * + * @return string + */ + private function buildKeyName($key) + { + return $this->namespace . $key; + } + + /** + * @param string[] $keys + * + * @return string[] + */ + private function buildKeyNames(array $keys) + { + return array_map(function($key) { + return $this->buildKeyName($key); + }, $keys); + + } + + /** + * @param mixed $key + * + * @throws InvalidArgumentException + */ + private function assertKeyName($key) + { + if(!is_scalar($key) || is_bool($key)) throw new InvalidArgumentException(); + } + + /** + * @param string[] $keys + * + * @throws InvalidArgumentException + */ + private function assertKeyNames(array $keys) + { + array_map(function ($value) { + $this->assertKeyName($value); + }, $keys); + } +} diff --git a/lib/cache/driver/YacDriver.php b/lib/cache/driver/YacDriver.php new file mode 100644 index 0000000000..739582c2fa --- /dev/null +++ b/lib/cache/driver/YacDriver.php @@ -0,0 +1,197 @@ + + * @package cache + * @link http://www.zentao.net + */ + +namespace ZenTao\Cache\Driver; + +use ZenTao\Cache\SimpleCache\CacheInterface; +use ZenTao\Cache\SimpleCache\InvalidArgumentException; + +class YacDriver implements CacheInterface +{ + /** + * @var string + */ + private $namespace; + + /** + * @var int + */ + private $defaultLifetime; + + /** + * yac client + * + * @var \Yac + */ + protected $yac; + + /** + * if your key is longer than this, maybe you can use md5 result as the key + */ + const KEY_MAX_LEN = 48; + + public function __construct($namespace = '', $defaultLifetime = 0) + { + $this->namespace = $namespace; + $this->defaultLifetime = $defaultLifetime; + $this->yac = new \Yac($namespace); + } + + public function get($key, $default = null) + { + $this->assertKeyName($key); + $key = $this->buildKeyName($key); + + return $this->yac->get($key) ?: $default; + } + + public function set($key, $value, $ttl = null) + { + $this->assertKeyName($key); + $key = $this->buildKeyName($key); + + $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; + + return $this->yac->set($key, $value, (int)$ttl); + } + + public function delete($key) + { + $this->assertKeyName($key); + $key = $this->buildKeyName($key); + + return $this->yac->delete($key); + } + + public function clear() + { + return $this->yac->flush(); + } + + public function getMultiple($keys, $default = null) + { + if(!is_array($keys)) { + return array(); + } + + $hashKeyMap = array(); + foreach($keys as $index => $key) + { + $this->assertKeyName($key); + if(strlen($key) > self::KEY_MAX_LEN) + { + $keys[$index] = $this->buildKeyName($key); + $hashKeyMap[$keys[$index]] = $key; + } + } + + $results = $this->yac->get($keys); + if($results !== false) + { + foreach($results as $key => $value) + { + if(isset($hashKeyMap[$key])) + { + $results[$hashKeyMap[$key]] = $value; + unset($results[$key]); + } + } + return $results; + } + + $results = array(); + foreach($keys as $key) + { + $results[$key] = $default; + } + return $results; + } + + public function setMultiple($values, $ttl = null) + { + if(!is_array($values)) return false; + + foreach($values as $key => $value) + { + if(strlen($key) > self::KEY_MAX_LEN) + { + $values[$this->buildKeyName($key)] = $value; + unset($values[$key]); + } + } + + $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; + + return $this->yac->set($values, $ttl); + } + + public function deleteMultiple($keys) + { + foreach($keys as $index => $key) + { + $keys[$index] = $this->buildKeyName($key); + } + return $this->yac->delete($keys); + } + + public function has($key) + { + return $this->get($key) !== null; + } + + /** + * @param string $key + * + * @return string + */ + private function buildKeyName($key) + { + if(strlen($key) > self::KEY_MAX_LEN) + { + $key = md5($key); + } + return $key; + } + + /** + * @param string[] $keys + * + * @return string[] + */ + private function buildKeyNames(array $keys) + { + return array_map(function ($key) { + return $this->buildKeyName($key); + }, $keys); + } + + /** + * @param mixed $key + * + * @throws InvalidArgumentException + */ + private function assertKeyName($key) + { + if(!is_scalar($key) || is_bool($key)) throw new InvalidArgumentException(); + } + + /** + * @param string[] $keys + * + * @throws InvalidArgumentException + */ + private function assertKeyNames(array $keys) + { + array_map(function ($value) { + $this->assertKeyName($value); + }, $keys); + } +} diff --git a/lib/cache/simple-cache/CacheException.php b/lib/cache/simple-cache/CacheException.php new file mode 100644 index 0000000000..330b06084b --- /dev/null +++ b/lib/cache/simple-cache/CacheException.php @@ -0,0 +1,7 @@ + $keys A list of keys that can be obtained in a single operation. + * @param mixed $default Default value to return for keys that do not exist. + * + * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value. + * + * @throws InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function getMultiple($keys, $default = null); + + /** + * Persists a set of key => value pairs in the cache, with an optional TTL. + * + * @param iterable $values A list of key => value pairs for a multiple-set operation. + * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and + * the driver supports TTL then the library may set a default value + * for it or let the driver take care of that. + * + * @return bool True on success and false on failure. + * + * @throws InvalidArgumentException + * MUST be thrown if $values is neither an array nor a Traversable, + * or if any of the $values are not a legal value. + */ + public function setMultiple($values, $ttl = null); + + /** + * Deletes multiple cache items in a single operation. + * + * @param iterable $keys A list of string-based keys to be deleted. + * + * @return bool True if the items were successfully removed. False if there was an error. + * + * @throws InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function deleteMultiple($keys); + + /** + * Determines whether an item is present in the cache. + * + * NOTE: It is recommended that has() is only to be used for cache warming type purposes + * and not to be used within your live applications operations for get/set, as this method + * is subject to a race condition where your has() will return true and immediately after, + * another script can remove it making the state of your app out of date. + * + * @param string $key The cache item key. + * + * @return bool + * + * @throws InvalidArgumentException + * MUST be thrown if the $key string is not a legal value. + */ + public function has($key); +} diff --git a/lib/cache/simple-cache/InvalidArgumentException.php b/lib/cache/simple-cache/InvalidArgumentException.php new file mode 100644 index 0000000000..825bcdf380 --- /dev/null +++ b/lib/cache/simple-cache/InvalidArgumentException.php @@ -0,0 +1,7 @@ + .setting-box > .btn:hover {background: unset;} .settings-list > .setting-box > .btn[disabled] {cursor: not-allowed; pointer-events: unset;} .settings-list > .setting-box > .btn[disabled] > a {pointer-events: none;} +.settings-list > .setting-box > .btn > h4 {padding-right: 5px;} +.settings-list > .setting-box > .btn > h4 > div {gap: 3px;} .settings-list > .setting-box > .btn > h4 > img {padding-right: 8px; padding-bottom: 3px; width: 32px; height: 27px;} -.settings-list > .setting-box > .btn > .setting-desc {padding-bottom: 16px; color: #5E626D; line-height: 25px; height: 50px;} +.settings-list > .setting-box > .btn > .setting-desc {padding-bottom: 16px; color: #5E626D; line-height: 25px; height: 50px; overflow: hidden;} .settings-list.lite-setting > .setting-box {flex: 1 1 30%;} .plugin-list {display: flex; flex-wrap: wrap; padding: 12px 4px 4px 4px;} @@ -71,7 +75,8 @@ .panel.publicClass {padding: 0 10px;} .icon.follow-us {font-size: 20px; padding-left: 10px;} -.setting-help {width: 45px; color: #9EA3B0; padding-top: 15px;} +.setting-help {color: #9EA3B0; padding-left: 5px;} +.setting-help > a {font-size: 15px;} [lang^='zh'] .setting-help {padding-top: 0;} .pointer-none {pointer-events: none;} @@ -86,7 +91,7 @@ .time-block {background: #EDEEF2; padding:2px 4px; border-radius: 4px; color: #0B0F18; font-size: 15px; margin: 0 4px;} .dynamic-block {height: 74px; padding: 12px 16px; display: flex; justify-content: space-between; position: relative; line-height: 20px; border-top: 1px solid #E3E4E9;} .dynamic-content {height: 50px; line-height: 25px; overflow: hidden;} -.dynamic-time {white-space: nowrap; position: absolute; right: 12px; bottom: 10px; padding-right: 12px; background: white; color: #5E626D;} +.dynamic-time {white-space: nowrap; position: absolute; right: 12px; bottom: 12px; padding-left: 10px; padding-right: 12px; background: white; color: #5E626D;} .time-count {letter-spacing: 1px;} .patch-block {padding: 10px 16px 12px; line-height: 20px; border-top: 1px solid #E3E4E9;} diff --git a/module/admin/lang/menu.php b/module/admin/lang/menu.php index deb8d5c52c..ad7bdc8f80 100644 --- a/module/admin/lang/menu.php +++ b/module/admin/lang/menu.php @@ -11,6 +11,7 @@ $lang->admin->menuList->user['order'] = 2; $lang->admin->menuList->switch['name'] = $lang->admin->menuSetting['switch']['name']; $lang->admin->menuList->switch['desc'] = $lang->admin->menuSetting['switch']['desc']; +$lang->admin->menuList->switch['link'] = 'admin|setmodule'; $lang->admin->menuList->switch['order'] = 3; $lang->admin->menuList->model['name'] = $lang->admin->menuSetting['model']['name']; @@ -71,10 +72,6 @@ $lang->admin->menuList->user['menuOrder']['5'] = 'dept'; $lang->admin->menuList->user['menuOrder']['10'] = 'user'; $lang->admin->menuList->user['menuOrder']['15'] = 'group'; -$lang->admin->menuList->switch['subMenu']['setmodule'] = array('link' => "{$lang->admin->module}|admin|setmodule|"); - -$lang->admin->menuList->switch['menuOrder']['5'] = 'setmodule'; - $lang->admin->menuList->model['subMenu']['common'] = array('link' => "{$lang->globalSetting}|custom|required|module=project", 'subModule' => 'custom,subject,holiday,stage', 'exclude' => 'stage-browse,stage-plusbrowse,stage-create,stage-edit,stage-batchcreate'); $lang->admin->menuList->model['subMenu']['scrum'] = array('link' => "{$lang->scrumModel}|auditcl|scrumbrowse|", 'subModule' => 'auditcl'); $lang->admin->menuList->model['subMenu']['waterfall'] = array('link' => "{$lang->waterfallModel}|stage|browse|", 'subModule' => 'stage', 'exclude' => 'stage-settype,stage-plusbrowse'); @@ -165,7 +162,7 @@ $lang->admin->menuList->message['menuOrder']['10'] = 'webhook'; $lang->admin->menuList->message['menuOrder']['20'] = 'browser'; $lang->admin->menuList->message['menuOrder']['25'] = 'setting'; -$lang->admin->menuList->dev['subMenu']['api'] = array('link' => "API|dev|api|module=index"); +$lang->admin->menuList->dev['subMenu']['api'] = array('link' => "{$lang->api->doc}|dev|api|module=index"); $lang->admin->menuList->dev['subMenu']['db'] = array('link' => "{$lang->database}|dev|db|table=" . trim(TABLE_EFFORT, '`')); $lang->admin->menuList->dev['subMenu']['langItem'] = array('link' => "{$lang->langItem}|dev|langitem|"); $lang->admin->menuList->dev['subMenu']['editor'] = array('link' => "{$lang->editor}|dev|editor|"); diff --git a/module/admin/view/index.html.php b/module/admin/view/index.html.php index f695d67c5f..f2b812c35d 100755 --- a/module/admin/view/index.html.php +++ b/module/admin/view/index.html.php @@ -21,9 +21,14 @@ vision == 'lite' and !in_array($menuKey, $config->admin->liteMenuList)) continue;?>
@@ -113,7 +118,7 @@
-
link, $dynamic->title, '_blank');?>
+
title ?>>link, $dynamic->title, '_blank');?>
addedDate, 0, 10);?>
diff --git a/module/build/model.php b/module/build/model.php index 7422b98a30..13e6563603 100644 --- a/module/build/model.php +++ b/module/build/model.php @@ -478,11 +478,13 @@ class buildModel extends model */ public function update($buildID) { - $buildID = (int)$buildID; - $oldBuild = $this->dao->select('*')->from(TABLE_BUILD)->where('id')->eq($buildID)->fetch(); - $build = fixer::input('post')->stripTags($this->config->build->editor->edit['id'], $this->config->allowedTags) + $buildID = (int)$buildID; + $oldBuild = $this->dao->select('*')->from(TABLE_BUILD)->where('id')->eq($buildID)->fetch(); + $newProduct = $this->dao->select('id,type')->from(TABLE_PRODUCT)->where('id')->eq($_POST['product'])->fetchPairs(); + $branch = (!isset($_POST['branch']) or $newProduct == 'normal') ? 0 : $oldBuild->branch; + $build = fixer::input('post')->stripTags($this->config->build->editor->edit['id'], $this->config->allowedTags) ->add('id', $buildID) - ->setIF(!isset($_POST['branch']), 'branch', $oldBuild->branch) + ->setDefault('branch', $branch) ->setDefault('product', $oldBuild->product) ->setDefault('builds', '') ->cleanInt('product,execution') diff --git a/module/common/lang/de.php b/module/common/lang/de.php index 4cae58218c..47f00a9ef5 100644 --- a/module/common/lang/de.php +++ b/module/common/lang/de.php @@ -255,7 +255,8 @@ $lang->redev = 'Develop'; $lang->browser = 'Browser'; $lang->db = 'Database'; $lang->langItem = 'Lang Item'; -$lang->database = 'Database'; +$lang->api->doc = 'API Document'; +$lang->database = 'Data Dictionary'; $lang->editor = 'Editor'; $lang->timezone = 'Timezone'; $lang->security = 'Security'; @@ -305,7 +306,7 @@ $lang->devops->set = 'Set'; $lang->admin->module = 'Module'; $lang->admin->system = 'System'; -$lang->admin->entry = 'Application'; +$lang->admin->entry = 'Access ZenTao'; $lang->admin->data = 'Data'; $lang->admin->cron = 'Cron'; $lang->admin->buildIndex = 'Full Text Search'; diff --git a/module/common/lang/en.php b/module/common/lang/en.php index 932179814c..19c712c728 100644 --- a/module/common/lang/en.php +++ b/module/common/lang/en.php @@ -255,7 +255,8 @@ $lang->redev = 'Develop'; $lang->browser = 'Browser'; $lang->db = 'Database'; $lang->langItem = 'Lang Item'; -$lang->database = 'Database'; +$lang->api->doc = 'API Document'; +$lang->database = 'Data Dictionary'; $lang->editor = 'Editor'; $lang->timezone = 'Timezone'; $lang->security = 'Security'; @@ -305,7 +306,7 @@ $lang->devops->set = 'Set'; $lang->admin->module = 'Module'; $lang->admin->system = 'System'; -$lang->admin->entry = 'Application'; +$lang->admin->entry = 'Access ZenTao'; $lang->admin->data = 'Data'; $lang->admin->cron = 'Cron'; $lang->admin->buildIndex = 'Full Text Search'; diff --git a/module/common/lang/fr.php b/module/common/lang/fr.php index 1ffdc2955d..58d3c6e569 100644 --- a/module/common/lang/fr.php +++ b/module/common/lang/fr.php @@ -255,7 +255,8 @@ $lang->redev = 'Develop'; $lang->browser = 'Browser'; $lang->db = 'Database'; $lang->langItem = 'Lang Item'; -$lang->database = 'Database'; +$lang->api->doc = 'API Document'; +$lang->database = 'Data Dictionary'; $lang->editor = 'Editor'; $lang->timezone = 'Timezone'; $lang->security = 'Security'; @@ -305,7 +306,7 @@ $lang->devops->set = 'Set'; $lang->admin->module = 'Module'; $lang->admin->system = 'System'; -$lang->admin->entry = 'Application'; +$lang->admin->entry = 'Access ZenTao'; $lang->admin->data = 'Data'; $lang->admin->cron = 'Cron'; $lang->admin->buildIndex = 'Full Text Search'; diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index eaa5946aaa..074c2c2e92 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -255,7 +255,8 @@ $lang->redev = '二次开发'; $lang->browser = '浏览器'; $lang->db = '数据库'; $lang->langItem = '语言项'; -$lang->database = '数据库'; +$lang->api->doc = '接口文档'; +$lang->database = '数据字典'; $lang->editor = '编辑器'; $lang->timezone = '时区'; $lang->security = '安全'; @@ -305,7 +306,7 @@ $lang->devops->set = '设置'; $lang->admin->module = '功能配置'; $lang->admin->system = '系统'; -$lang->admin->entry = '应用'; +$lang->admin->entry = '接入禅道'; $lang->admin->data = '数据'; $lang->admin->cron = '定时'; $lang->admin->buildIndex = '重建索引'; diff --git a/module/company/control.php b/module/company/control.php index b978bff34f..16500a0984 100755 --- a/module/company/control.php +++ b/module/company/control.php @@ -226,7 +226,7 @@ class company extends control $executionList = $this->execution->getByIdList(array_keys($executions)); foreach($executionList as $executionID => $execution) { - if(isset($projects[$execution->project])) $executions[$execution->id] = $projects[$execution->project] . $executions[$execution->id]; + if(isset($projects[$execution->project])) $executions[$execution->id] = $projects[$execution->project] . $executions[$execution->id]; } $executions = array($this->lang->execution->common) + $executions; diff --git a/module/custom/model.php b/module/custom/model.php index 112ced5617..415e3f3843 100644 --- a/module/custom/model.php +++ b/module/custom/model.php @@ -210,6 +210,9 @@ class customModel extends model ksort($menuOrder); foreach($menuOrder as $name) { + /* If menu is removed, delete the menuOrder. */ + if(!isset($allMenu->$name)) continue; + $item = new stdclass(); $item->name = $name; $item->hidden = false; diff --git a/module/custom/view/code.html.php b/module/custom/view/code.html.php index 65a80730d4..643aeb940b 100644 --- a/module/custom/view/code.html.php +++ b/module/custom/view/code.html.php @@ -16,7 +16,7 @@ custom->setCode;?> - setCode) ? $config->setCode : 1;?> + setCode) ? $config->setCode : 0;?> custom->conceptOptions->URAndSR as $key => $value):?> diff --git a/module/custom/view/mode.html.php b/module/custom/view/mode.html.php index 5376929d50..7e7dcfc8e8 100644 --- a/module/custom/view/mode.html.php +++ b/module/custom/view/mode.html.php @@ -39,7 +39,7 @@ lang->custom->scrum->common, implode($lang->comma, $disabledScrumFeatures)) : $this->lang->custom->features[$feature];?> - + diff --git a/module/execution/config.php b/module/execution/config.php index 216136c250..89311d721e 100644 --- a/module/execution/config.php +++ b/module/execution/config.php @@ -176,7 +176,7 @@ $config->execution->gantt->linkType['end']['end'] = 2; $config->execution->gantt->linkType['begin']['end'] = 3; $config->execution->datatable = new stdclass(); -if(!isset($config->setCode) or $config->setCode == 1) +if(isset($config->setCode) and $config->setCode == 1) { $config->execution->datatable->defaultField = array('id', 'name', 'code', 'project', 'PM', 'status', 'progress', 'openedDate', 'begin', 'end', 'estimate', 'consumed', 'left', 'burn'); } @@ -201,7 +201,7 @@ $config->execution->datatable->fieldList['name']['nestedToggle'] = true; $config->execution->datatable->fieldList['name']['iconRender'] = true; $config->execution->datatable->fieldList['name']['required'] = 'yes'; -if(!isset($config->setCode) or $config->setCode == 1) +if(isset($config->setCode) and $config->setCode == 1) { $config->execution->datatable->fieldList['code']['title'] = 'execCode'; $config->execution->datatable->fieldList['code']['width'] = '180'; diff --git a/module/execution/control.php b/module/execution/control.php index b97958c31b..9881cf4be7 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -1930,6 +1930,7 @@ class execution extends control $this->app->loadLang('programplan'); $browseExecutionLink = $this->createLink('execution', 'browse', "executionID=$executionID"); $execution = $this->execution->getById($executionID); + $project = $this->project->getById($execution->project); $branches = $this->project->getBranchesByProject($executionID); $linkedProductIdList = empty($branches) ? '' : array_keys($branches); @@ -2103,6 +2104,7 @@ class execution extends control $this->view->position = $position; $this->view->executions = $executions; $this->view->execution = $execution; + $this->view->project = $project; $this->view->poUsers = $poUsers; $this->view->pmUsers = $pmUsers; $this->view->qdUsers = $qdUsers; @@ -2732,12 +2734,17 @@ class execution extends control if($groupBy == 'story' and $browseType == 'task' and !isset($this->lang->kanban->orderList[$orderBy])) $orderBy = 'id_asc'; $kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy); + if(empty($kanbanGroup)) { $this->kanban->createExecutionLane($executionID, $browseType); $kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy); } + /* Show lanes of the attribute: no story&bug in request, no bug in design. */ + if(!isset($this->lang->execution->menu->story)) unset($kanbanGroup['story']); + if(!isset($this->lang->execution->menu->qa)) unset($kanbanGroup['bug']); + /* Determines whether an object is editable. */ $canBeChanged = common::canModify('execution', $execution); diff --git a/module/execution/css/batchedit.css b/module/execution/css/batchedit.css index 5fb6ae17e4..604452605e 100644 --- a/module/execution/css/batchedit.css +++ b/module/execution/css/batchedit.css @@ -4,3 +4,4 @@ .c-method {width: 45px;} .c-type > .icon-help {vertical-align: text-top;} .c-type > .popover .popover-content {font-weight: 400;} +#executionForm tbody td {vertical-align: top;} diff --git a/module/execution/js/common.js b/module/execution/js/common.js index 92ca8e7134..db31593818 100644 --- a/module/execution/js/common.js +++ b/module/execution/js/common.js @@ -285,3 +285,32 @@ function setCardCount(heightType) { heightType != 'custom' ? $('#cardBox').addClass('hidden') : $('#cardBox').removeClass('hidden'); } + +/** + * Hide plan box by stage's attribute. + * + * @param string attribute + * @access public + * @return void + */ +function hidePlanBox(attribute) +{ + if(attribute == 'request' || attribute == 'review') + { + $('.productsBox .planBox').addClass('hide'); + $('.productsBox .planBox select').attr('disabled', 'disabled'); + $('#productTitle').text(manageProductsLang); + + $('#plansBox').closest('tr').addClass('hide'); + $('#plansBox').attr('disabled', 'disabled'); + } + else + { + $('.productsBox .planBox').removeClass('hide'); + $('.productsBox .planBox select').attr('disabled', ''); + $('#productTitle').text(manageProductPlanLang); + + $('#plansBox').closest('tr').removeClass('hide'); + $('#plansBox').attr('disabled', ''); + } +} diff --git a/module/execution/js/create.js b/module/execution/js/create.js index 56ac637410..cb0c0e1091 100644 --- a/module/execution/js/create.js +++ b/module/execution/js/create.js @@ -82,14 +82,7 @@ $(function() $('#attribute').change(function() { var attribute = $(this).val(); - if(attribute == 'request' || attribute == 'design' || attribute == 'review') - { - $('#plansBox').closest('tr').addClass('hide'); - } - else - { - $('#plansBox').closest('tr').removeClass('hide'); - } + hidePlanBox(attribute); }) $('#attribute').change(); diff --git a/module/execution/js/edit.js b/module/execution/js/edit.js index 58f5d31712..05d613bebd 100644 --- a/module/execution/js/edit.js +++ b/module/execution/js/edit.js @@ -114,6 +114,17 @@ $(function() } $('[data-toggle="popover"]').popover(); + + if(isStage) + { + $('#attribute').change(function() + { + var attribute = $(this).val(); + hidePlanBox(attribute); + }) + + $('#attribute').change(); + } }) var lastProjectID = $("#project").val(); diff --git a/module/execution/model.php b/module/execution/model.php index 76f9b07418..0c03e019a4 100755 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -450,7 +450,7 @@ class executionModel extends model ->checkIF($sprint->end != '', 'end', 'ge', $sprint->begin) ->checkFlow() ->exec(); - + /* Add the creater to the team. */ if(!dao::isError()) { @@ -533,7 +533,7 @@ class executionModel extends model $oldExecution = $this->dao->findById($executionID)->from(TABLE_EXECUTION)->fetch(); /* Judgment of required items. */ - if($oldExecution->type != 'stage' and $this->post->code == '' and (!isset($this->config->setCode) or $this->config->setCode == 1)) + if($oldExecution->type != 'stage' and $this->post->code == '' and isset($this->config->setCode) and $this->config->setCode == 1) { dao::$errors['code'] = sprintf($this->lang->error->notempty, $this->lang->execution->code); return false; @@ -5229,12 +5229,11 @@ class executionModel extends model $class = !empty($execution->children) ? 'disabled' : ''; common::printIcon('task', 'create', "executionID={$execution->id}", '', 'list', '', '', $class, false, "data-app='execution'"); - if($execution->type == 'stage' or $this->app->tab == 'project') + if($execution->type == 'stage') { $isCreateTask = $this->loadModel('programplan')->isCreateTask($execution->id); $disabled = ($isCreateTask and $execution->type == 'stage') ? '' : ' disabled'; $title = !$isCreateTask ? $this->lang->programplan->error->createdTask : $this->lang->programplan->createSubPlan; - $title = (!empty($disabled) and $execution->type != 'stage') ? $this->lang->programplan->error->notStage : $title; common::printIcon('programplan', 'create', "program={$execution->project}&productID=$productID&planID=$execution->id", $execution, 'list', 'split', '', $disabled, '', '', $title); } @@ -5729,12 +5728,13 @@ class executionModel extends model $_POST['status'] = 'wait'; $_POST['days'] = $project->days; $_POST['team'] = $project->team; + $_POST['desc'] = $project->desc; $_POST['teamMembers'] = array($this->app->user->account); $_POST['acl'] = 'open'; - $_POST['PO'] = ''; - $_POST['QD'] = ''; - $_POST['PM'] = ''; - $_POST['RD'] = ''; + $_POST['PO'] = $this->app->user->account; + $_POST['QD'] = $this->app->user->account; + $_POST['PM'] = $this->app->user->account; + $_POST['RD'] = $this->app->user->account; $_POST['multiple'] = '0'; $_POST['hasProduct'] = $project->hasProduct; if($project->code) $_POST['code'] = $project->code; @@ -5789,7 +5789,7 @@ class executionModel extends model $_POST['status'] = $project->status; $_POST['acl'] = 'open'; - if(!isset($this->config->setCode) or $this->config->setCode == 1) $_POST['code'] = $project->code; + if(isset($this->config->setCode) and $this->config->setCode == 1) $_POST['code'] = $project->code; $projectProducts = $this->dao->select('*')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($projectID)->fetchAll(); foreach($projectProducts as $projectProduct) diff --git a/module/execution/view/batchedit.html.php b/module/execution/view/batchedit.html.php index d78ab1d11f..7f75310b9d 100755 --- a/module/execution/view/batchedit.html.php +++ b/module/execution/view/batchedit.html.php @@ -55,7 +55,7 @@ execution->method;?> execution->$name;?> - setCode) or $config->setCode == 1):?> + setCode) and $config->setCode == 1):?> execution->$code;?> '>execution->$PM;?> @@ -94,7 +94,7 @@ execution->typeList, $executions[$executionID]->type);?> name, "class='form-control' id='names{$executionID}'");?> - setCode) or $config->setCode == 1):?> + setCode) and $config->setCode == 1):?> code, "id='codes{$executionID}' class='form-control'");?> ' style='overflow:visible'>PM, "class='form-control picker-select'");?> diff --git a/module/execution/view/create.html.php b/module/execution/view/create.html.php index f8e6f80772..429ecf2cb4 100644 --- a/module/execution/view/create.html.php +++ b/module/execution/view/create.html.php @@ -46,6 +46,8 @@ execution->cancelCopy);?> execution->copyNoExecution);?> model) ? $project->model : '');?> +project->manageProducts);?> +project->manageProductPlan);?>
@@ -77,7 +79,7 @@ - setCode) or $config->setCode == 1):?> + setCode) and $config->setCode == 1):?> execution->execCode : $lang->execution->code;?> @@ -148,7 +150,7 @@ project->manageProductPlan;?>
-
+
type != 'normal' and isset($branchGroups[$product->id]);?> @@ -168,7 +170,7 @@
-
+
> product->plan;?> id][]", isset($productPlans[$product->id]) ? $productPlans[$product->id] : array(), isset($product->plans) ? $product->plans : '', "class='form-control chosen' multiple");?> @@ -185,7 +187,7 @@ - hasProduct)):?> + hasProduct) and strpos($project->model, 'waterfall') === false):?> execution->linkPlan;?> @@ -200,7 +202,7 @@ project->manageProductPlan;?>
-
+
@@ -216,7 +218,7 @@
-
+
product->plan;?> diff --git a/module/execution/view/edit.html.php b/module/execution/view/edit.html.php index d5d43fe019..b9c2a16446 100644 --- a/module/execution/view/edit.html.php +++ b/module/execution/view/edit.html.php @@ -14,6 +14,9 @@ +type == 'stage');?> +project->manageProducts);?> +project->manageProductPlan);?>
@@ -36,7 +39,7 @@ id);?> model == 'agileplus'):?> - execution->method;?> + execution->method;?> execution->typeList, $execution->type);?> tab == 'project' and $project->model == 'waterfallplus'):?> @@ -50,7 +53,7 @@ execution->name;?> name, "class='form-control' required");?> - setCode) or $config->setCode == 1):?> + setCode) and $config->setCode == 1):?> execution->code;?> code, "class='form-control' required");?> @@ -161,7 +164,7 @@ project->manageProductPlan;?>
-
+
type != 'normal' and isset($branchGroups[$product->id]);?> @@ -181,7 +184,7 @@
-
+
> product->plan;?> id][]", isset($productPlans[$product->id]) ? $productPlans[$product->id] : array(), $product->plans, "class='form-control chosen' multiple");?> @@ -213,7 +216,7 @@ project->manageProductPlan;?>
-
+
@@ -229,7 +232,7 @@
-
+
product->plan;?> @@ -243,7 +246,7 @@ - type == 'stage' and !in_array($execution->attribute, array('request', 'design', 'review'))) or $execution->type != 'stage'): ?> + hasProduct)):?> @@ -252,7 +255,7 @@ project->manageProductPlan;?>
-
+
type != 'normal' and isset($branchGroups[$product->id]);?> @@ -272,7 +275,7 @@
-
+
> product->plan;?> id][]", isset($productPlans[$product->id]) ? $productPlans[$product->id] : array(), isset($product->plans) ? $product->plans : '', "class='form-control chosen' multiple");?> @@ -283,10 +286,10 @@ - + - + execution->team;?> diff --git a/module/execution/view/grouptask.html.php b/module/execution/view/grouptask.html.php index 4dccd0971f..5633b05d66 100644 --- a/module/execution/view/grouptask.html.php +++ b/module/execution/view/grouptask.html.php @@ -63,10 +63,13 @@ $link = common::hasPriv('execution', 'importTask') ? $this->createLink('execution', 'importTask', "execution=$execution->id") : '#'; echo "
  • " . html::a($link, $lang->execution->importTask, '', $misc) . "
  • "; - $class = common::hasPriv('execution', 'importBug') ? '' : "class=disabled"; - $misc = common::hasPriv('execution', 'importBug') ? "class='import'" : "class=disabled"; - $link = common::hasPriv('execution', 'importBug') ? $this->createLink('execution', 'importBug', "execution=$execution->id") : '#'; - echo "
  • " . html::a($link, $lang->execution->importBug, '', $misc) . "
  • "; + if(isset($this->lang->execution->menu->qa)) + { + $class = common::hasPriv('execution', 'importBug') ? '' : "class=disabled"; + $misc = common::hasPriv('execution', 'importBug') ? "class='import'" : "class=disabled"; + $link = common::hasPriv('execution', 'importBug') ? $this->createLink('execution', 'importBug', "execution=$execution->id") : '#'; + echo "
  • " . html::a($link, $lang->execution->importBug, '', $misc) . "
  • "; + } ?>
    diff --git a/module/execution/view/taskkanban.html.php b/module/execution/view/taskkanban.html.php index b1446f0498..2c87281cd1 100644 --- a/module/execution/view/taskkanban.html.php +++ b/module/execution/view/taskkanban.html.php @@ -17,7 +17,7 @@