This commit is contained in:
孙广明
2023-02-22 15:53:02 +08:00
70 changed files with 724 additions and 84 deletions
+3
View File
@@ -25,3 +25,6 @@ ALTER table `zt_reviewcl` ADD `type` varchar(255) NOT NULL DEFAULT '' AFTER `cat
UPDATE `zt_reviewcl` SET `type` = 'waterfall' WHERE `type` = '';
UPDATE `zt_activity` SET `order` = `id` * 5 WHERE `order` = '0';
ALTER table `zt_cmcl` ADD `projectType` varchar(255) NOT NULL DEFAULT '' AFTER `type`;
UPDATE `zt_cmcl` SET `projectType` = 'waterfall' WHERE `projectType` = '';
+1
View File
@@ -9780,6 +9780,7 @@ CREATE TABLE IF NOT EXISTS `zt_reviewlist` (
CREATE TABLE IF NOT EXISTS `zt_cmcl` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`type` char(30) NOT NULL,
`projectType` varchar(255) NOT NULL,
`title` int(11) NOT NULL,
`contents` text NOT NULL,
`assignedTo` varchar(30) NOT NULL,
@@ -40,7 +40,7 @@
<th><?php echo $lang->project->name;?></th>
<td class="col-main"><?php echo html::input('name', $name, "class='form-control' required");?></td>
</tr>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<tr>
<th><?php echo $lang->project->code;?></th>
<td><?php echo html::input('code', '', "class='form-control' required");?></td>
+6 -6
View File
@@ -339,12 +339,12 @@ class helper extends baseHelper
$dateInterval->hour = $interval->format('%H');
$dateInterval->minute = $interval->format('%i');
$dateInterval->secound = $interval->format('%s');
$dateInterval->year = ltrim($dateInterval->year, '0');
$dateInterval->month = ltrim($dateInterval->month, '0');
$dateInterval->day = ltrim($dateInterval->day, '0');
$dateInterval->hour = ltrim($dateInterval->hour, '0');
$dateInterval->minute = ltrim($dateInterval->minute, '0');
$dateInterval->secound = ltrim($dateInterval->secound, '0');
$dateInterval->year = $dateInterval->year == '00' ? 0 : ltrim($dateInterval->year, '0');
$dateInterval->month = $dateInterval->month == '00' ? 0 : ltrim($dateInterval->month, '0');
$dateInterval->day = $dateInterval->day == '00' ? 0 : ltrim($dateInterval->day, '0');
$dateInterval->hour = $dateInterval->hour == '00' ? 0 : ltrim($dateInterval->hour, '0');
$dateInterval->minute = $dateInterval->minute == '00' ? 0 : ltrim($dateInterval->minute, '0');
$dateInterval->secound = $dateInterval->secound == '00' ? 0 : ltrim($dateInterval->secound, '0');
}
return $dateInterval;
}
+53
View File
@@ -0,0 +1,53 @@
<?php
/**
* The cache library of zentaopms.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Lu Fei <lufei@easycorp.ltd>
* @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);
}
}
+173
View File
@@ -0,0 +1,173 @@
<?php
/**
* The cache library of zentaopms.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Lu Fei <lufei@easycorp.ltd>
* @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);
}
}
+197
View File
@@ -0,0 +1,197 @@
<?php
/**
* The cache library of zentaopms.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Lu Fei <lufei@easycorp.ltd>
* @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);
}
}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace ZenTao\Cache\SimpleCache;
class CacheException extends \RuntimeException
{
}
+114
View File
@@ -0,0 +1,114 @@
<?php
namespace ZenTao\Cache\SimpleCache;
interface CacheInterface
{
/**
* Fetches a value from the cache.
*
* @param string $key The unique key of this item in the cache.
* @param mixed $default Default value to return if the key does not exist.
*
* @return mixed The value of the item from the cache, or $default in case of cache miss.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function get($key, $default = null);
/**
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
*
* @param string $key The key of the item to store.
* @param mixed $value The value of the item to store, must be serializable.
* @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 the $key string is not a legal value.
*/
public function set($key, $value, $ttl = null);
/**
* Delete an item from the cache by its unique key.
*
* @param string $key The unique cache key of the item to delete.
*
* @return bool True if the item was successfully removed. False if there was an error.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function delete($key);
/**
* Wipes clean the entire cache's keys.
*
* @return bool True on success and false on failure.
*/
public function clear();
/**
* Obtains multiple cache items by their unique keys.
*
* @param iterable<string> $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<string, mixed> 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<string> $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);
}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace ZenTao\Cache\SimpleCache;
class InvalidArgumentException extends CacheException
{
}
+7 -3
View File
@@ -20,6 +20,8 @@
.font-20 {font-size: 20px;}
.font-24 {font-size: 24px;}
.radius-4 {border-radius: 4px;}
.white-nowrap {white-space: nowrap;}
.w-full {width: 100%;}
#notice {margin: -20px -20px 20px; padding: 20px;}
#zentaoLinks {margin-top: 20px;}
@@ -40,8 +42,10 @@
.settings-list > .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,7 @@
.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;}
[lang^='zh'] .setting-help {padding-top: 0;}
.pointer-none {pointer-events: none;}
@@ -86,7 +90,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: 5px; 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;}
+2 -5
View File
@@ -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}|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']['editor'] = array('link' => "{$lang->editor}|dev|editor|");
$lang->admin->menuList->dev['subMenu']['entry'] = array('link' => "{$lang->admin->entry}|entry|browse|", 'subModule' => 'entry');
+7 -2
View File
@@ -21,9 +21,14 @@
<?php if($config->vision == 'lite' and !in_array($menuKey, $config->admin->liteMenuList)) continue;?>
<div class="setting-box">
<button class="btn shadow-primary-hover" <?php if($menu['disabled']) echo 'disabled';?> data-link='<?php echo $menu['link'];?>'>
<h4><img src="/static/svg/admin-<?php echo $menuKey;?>.svg"/><?php echo $menu['name'];?></h4>
<h4 class="flex align-center justify-between w-full">
<div class="flex align-center">
<img src="/static/svg/admin-<?php echo $menuKey;?>.svg"/>
<?php echo $menu['name'];?>
</div>
<?php echo html::a($config->admin->helpURL[$menuKey], "<i class='icon icon-help'></i> ", '_blank', 'class="text-muted setting-help"');?>
</h4>
<p class="text-muted setting-desc" title="<?php echo $menu['desc'];?>"><?php echo $menu['desc'];?></p>
<?php echo html::a($config->admin->helpURL[$menuKey], "<i class='icon icon-help'></i> {$lang->help}", '_blank', 'class="text-muted setting-help"');?>
</button>
</div>
<?php endforeach;?>
+3 -2
View File
@@ -253,7 +253,8 @@ $lang->indexPage = 'Index';
$lang->model = 'Model';
$lang->redev = 'Develop';
$lang->browser = 'Browser';
$lang->database = 'Database';
$lang->api = 'API Document';
$lang->database = 'Data Dictionary';
$lang->editor = 'Editor';
$lang->timezone = 'Timezone';
$lang->security = 'Security';
@@ -303,7 +304,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';
+3 -2
View File
@@ -253,7 +253,8 @@ $lang->indexPage = 'Index';
$lang->model = 'Model';
$lang->redev = 'Develop';
$lang->browser = 'Browser';
$lang->database = 'Database';
$lang->api = 'API Document';
$lang->database = 'Data Dictionary';
$lang->editor = 'Editor';
$lang->timezone = 'Timezone';
$lang->security = 'Security';
@@ -303,7 +304,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';
+3 -2
View File
@@ -253,7 +253,8 @@ $lang->indexPage = 'Index';
$lang->model = 'Model';
$lang->redev = 'Develop';
$lang->browser = 'Browser';
$lang->database = 'Database';
$lang->api = 'API Document';
$lang->database = 'Data Dictionary';
$lang->editor = 'Editor';
$lang->timezone = 'Timezone';
$lang->security = 'Security';
@@ -303,7 +304,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';
+3 -2
View File
@@ -253,7 +253,8 @@ $lang->indexPage = '首页';
$lang->model = '模型';
$lang->redev = '二次开发';
$lang->browser = '浏览器';
$lang->database = '数据库';
$lang->api = '接口文档';
$lang->database = '数据字典';
$lang->editor = '编辑器';
$lang->timezone = '时区';
$lang->security = '安全';
@@ -303,7 +304,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 = '重建索引';
+10 -5
View File
@@ -204,6 +204,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;
@@ -1134,13 +1137,15 @@ class customModel extends model
$disabledFeatures = $this->setting->getItem('owner=system&module=common&section=&key=disabledFeatures');
$disabledFeatures = $disabledFeatures . ',' . $closedFeatures;
$hasWaterfall = strpos(",{$disabledFeatures},", ',waterfall,') === false;
$hasWaterfallPlus = strpos(",{$disabledFeatures},", ',waterfallplus,') === false;
$hasScrumMeasrecord = strpos(",{$disabledFeatures},", ',scrumMeasrecord,') === false;
$hasWaterfallMeasrecord = (strpos(",{$disabledFeatures},", ',waterfallMeasrecord,') === false and ($hasWaterfall or $hasWaterfallPlus));
$hasWaterfall = strpos(",{$disabledFeatures},", ',waterfall,') === false;
$hasWaterfallPlus = strpos(",{$disabledFeatures},", ',waterfallplus,') === false;
$hasScrumMeasrecord = strpos(",{$disabledFeatures},", ',scrumMeasrecord,') === false;
$hasAgilePlusMeasrecord = strpos(",{$disabledFeatures},", ',agileMeasrecord,') === false;
$hasWaterfallMeasrecord = (strpos(",{$disabledFeatures},", ',waterfallMeasrecord,') === false and $hasWaterfall);
$hasWaterfallPlusMeasrecord = (strpos(",{$disabledFeatures},", ',waterfallplusMeasrecord,') === false and $hasWaterfallPlus);
$cronStatus = 'normal';
if(!$hasScrumMeasrecord and !$hasWaterfallMeasrecord) $cronStatus = 'stop';
if(!$hasScrumMeasrecord and !$hasAgilePlusMeasrecord and !$hasWaterfallMeasrecord and $hasWaterfallPlusMeasrecord) $cronStatus = 'stop';
$this->loadModel('cron');
$cron = $this->dao->select('id,status')->from(TABLE_CRON)->where('command')->like('%methodName=initCrontabQueue')->fetch();
+1 -1
View File
@@ -16,7 +16,7 @@
<tr>
<th class='c-setCode'><?php echo $lang->custom->setCode;?></th>
<td class='c-code text-left'>
<?php $checkedKey = isset($config->setCode) ? $config->setCode : 1;?>
<?php $checkedKey = isset($config->setCode) ? $config->setCode : 0;?>
<?php foreach($lang->custom->conceptOptions->URAndSR as $key => $value):?>
<label class="radio-inline"><input type="radio" name="code" value="<?php echo $key?>"<?php echo $key == $checkedKey ? " checked='checked'" : ''?> id="code<?php echo $key;?>"><?php echo $value;?></label>
<?php endforeach;?>
+1 -1
View File
@@ -39,7 +39,7 @@
<?php if(is_array($feature) and empty($disabledScrumFeatures)) continue;?>
<tr class='text-center'>
<td class='text-left'><?php echo (is_array($feature) and !empty($disabledScrumFeatures)) ? sprintf($this->lang->custom->scrum->common, implode($lang->comma, $disabledScrumFeatures)) : $this->lang->custom->features[$feature];?></td>
<td><i class='icon text-red icon-close'></i></td>
<td><i class='icon text-red icon-ban-circle'></i></td>
<td><i class='icon text-success icon-check'></i></td>
</tr>
<?php endforeach;?>
+2 -2
View File
@@ -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';
+5
View File
@@ -2732,12 +2732,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);
+2
View File
@@ -2,3 +2,5 @@
.c-team-name {width: 120px;}
.c-project, .c-user, .c-code, .c-desc, .c-days {width: 150px;}
.c-method {width: 45px;}
.c-type > .icon-help {vertical-align: text-top;}
.c-type > .popover .popover-content {font-weight: 400;}
+1
View File
@@ -1,3 +1,4 @@
.c-user {width: 150px !important;}
.c-date {width: 125px;}
.c-method {width: 60px;}
.c-type {width: 130px;}
+1
View File
@@ -39,3 +39,4 @@
.productsBox .required + .text-danger.help-text {position: relative; left: 10px;}
.productsBox > #productNameLabel {padding-top: 8px;}
.productsBox div[id^='branch'].chosen-disabled {pointer-events: none;}
.methodTip icon:before {margin-bottom: 5px;}
+5
View File
@@ -27,3 +27,8 @@ $('#executionForm').on('change input mousedown', '.has-error', function()
$(this).parent().find('.text-danger').remove();
$(this).removeClass('has-error');
})
$(function()
{
$('[data-toggle="popover"]').popover();
})
+2
View File
@@ -153,6 +153,8 @@ $(function()
{
$('.disabledBranch div[id^="branch"]').addClass('chosen-disabled');
}
$('[data-toggle="popover"]').popover();
});
function showLifeTimeTips()
+2
View File
@@ -112,6 +112,8 @@ $(function()
{
$('.disabledBranch div[id^="branch"]').addClass('chosen-disabled');
}
$('[data-toggle="popover"]').popover();
})
var lastProjectID = $("#project").val();
+2
View File
@@ -151,6 +151,8 @@ $lang->execution->left = 'Left';
$lang->execution->copyTeamTip = "copy Project/project team members";
$lang->execution->daysGreaterProject = 'Days cannot be greater than days of execution 『%s』';
$lang->execution->errorHours = 'Hours/Day cannot be greater than『24』';
$lang->execution->agileplusMethodTip = 'When creating executions in an Agile Plus project, both Iteration and Kanban management methods are supported.';
$lang->execution->typeTip = "The sub-stages of other types can be created under the parent stage of the 'mix' type, while the type of other parent-child levels is consistent.";
$lang->execution->start = 'Start';
$lang->execution->activate = 'Aktivieren';
+2
View File
@@ -151,6 +151,8 @@ $lang->execution->left = 'Left';
$lang->execution->copyTeamTip = "copy Project/project team members";
$lang->execution->daysGreaterProject = 'Days cannot be greater than days of execution 『%s』';
$lang->execution->errorHours = 'Hours/Day cannot be greater than『24』';
$lang->execution->agileplusMethodTip = 'When creating executions in an Agile Plus project, both Iteration and Kanban management methods are supported.';
$lang->execution->typeTip = "The sub-stages of other types can be created under the parent stage of the 'mix' type, while the type of other parent-child levels is consistent.";
$lang->execution->start = 'Start';
$lang->execution->activate = 'Activate';
+2
View File
@@ -151,6 +151,8 @@ $lang->execution->left = 'Left';
$lang->execution->copyTeamTip = "copy Project/project team members";
$lang->execution->daysGreaterProject = 'Days cannot be greater than days of execution 『%s』';
$lang->execution->errorHours = 'Hours/Day cannot be greater than『24』';
$lang->execution->agileplusMethodTip = 'When creating executions in an Agile Plus project, both Iteration and Kanban management methods are supported.';
$lang->execution->typeTip = "The sub-stages of other types can be created under the parent stage of the 'mix' type, while the type of other parent-child levels is consistent.";
$lang->execution->start = 'Démarrer';
$lang->execution->activate = 'Activer';
+2
View File
@@ -151,6 +151,8 @@ $lang->execution->left = '剩余';
$lang->execution->copyTeamTip = "可以选择复制项目或{$lang->execution->common}团队的成员";
$lang->execution->daysGreaterProject = '可用工日不能大于执行的可用工日『%s』';
$lang->execution->errorHours = '可用工时/天不能大于『24』';
$lang->execution->agileplusMethodTip = '融合敏捷项目创建执行时,支持迭代和看板两种管理方法。';
$lang->execution->typeTip = '“综合”类型的父阶段可以创建其它类型的子级,其它父子层级的类型均一致。';
$lang->execution->start = "开始";
$lang->execution->activate = "激活";
+2 -2
View File
@@ -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;
@@ -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)
+6 -3
View File
@@ -55,14 +55,17 @@
<th class='c-method'><?php echo $lang->execution->method;?></th>
<?php endif;?>
<th class='required <?php echo $minWidth?>' style="width:100%"><?php echo $lang->execution->$name;?></th>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<th class='c-code required'><?php echo $lang->execution->$code;?></th>
<?php endif;?>
<th class='c-user<?php echo zget($visibleFields, 'PM', ' hidden') . zget($requiredFields, 'PM', '', ' required');?>'><?php echo $lang->execution->$PM;?></th>
<th class='c-user<?php echo zget($visibleFields, 'PO', ' hidden') . zget($requiredFields, 'PO', '', ' required');?>'><?php echo $lang->execution->PO;?></th>
<th class='c-user<?php echo zget($visibleFields, 'QD', ' hidden') . zget($requiredFields, 'QD', '', ' required');?>'><?php echo $lang->execution->QD;?></th>
<th class='c-user<?php echo zget($visibleFields, 'RD', ' hidden') . zget($requiredFields, 'RD', '', ' required');?>'><?php echo $lang->execution->RD;?></th>
<th class='c-type<?php echo zget($visibleFields, 'type', ' hidden') . zget($requiredFields, 'type', '', ' required');?>'><?php echo $lang->execution->$type;?></th>
<th class='c-type<?php echo zget($visibleFields, 'type', ' hidden') . zget($requiredFields, 'type', '', ' required');?>'>
<?php echo $lang->execution->$type;?>
<icon class='icon icon-help' data-toggle='popover' data-trigger='focus hover' data-placement='right' data-tip-class='text-muted popover-sm' data-content="<?php echo $lang->execution->typeTip;?>"></icon>
</th>
<th class='c-date required'><?php echo $lang->execution->begin;?></th>
<th class='c-date required'><?php echo $lang->execution->end;?></th>
<th class='c-desc <?php echo zget($visibleFields, 'desc', ' hidden') . zget($requiredFields, 'desc', '', ' required');?>'><?php echo $lang->execution->$desc;?></th>
@@ -91,7 +94,7 @@
<td title='<?php echo zget($lang->execution->typeList, $executions[$executionID]->type);?>'><?php echo zget($lang->execution->typeList, $executions[$executionID]->type);?></td>
<?php endif;?>
<td title='<?php echo $executions[$executionID]->name?>'><?php echo html::input("names[$executionID]", $executions[$executionID]->name, "class='form-control' id='names{$executionID}'");?></td>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<td><?php echo html::input("codes[$executionID]", $executions[$executionID]->code, "id='codes{$executionID}' class='form-control'");?></td>
<?php endif;?>
<td class='text-left<?php echo zget($visibleFields, 'PM', ' hidden')?>' style='overflow:visible'><?php echo html::select("PMs[$executionID]", $pmUsers, $executions[$executionID]->PM, "class='form-control picker-select'");?></td>
+5 -2
View File
@@ -66,7 +66,10 @@
<tr>
<th><?php echo $lang->execution->method;?></th>
<td class="col-main"><?php echo html::select("type", $lang->execution->typeList, $type, "class='form-control chosen' required onchange='setType(this.value)'");?></td>
<td colspan='2'></td>
<td class='methodTip'>
<icon class='icon icon-help' data-toggle='popover' data-trigger='focus hover' data-placement='right' data-tip-class='text-muted popover-sm' data-content="<?php echo $lang->execution->agileplusMethodTip;?>"></icon>
</td>
<td></td>
</tr>
<?php endif;?>
<tr>
@@ -74,7 +77,7 @@
<td class="col-main"><?php echo html::input('name', $name, "class='form-control' required");?></td>
<td colspan='2'></td>
</tr>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<tr>
<th><?php echo $showExecutionExec ? $lang->execution->execCode : $lang->execution->code;?></th>
<td><?php echo html::input('code', $code, "class='form-control' required");?></td><td></td><td></td>
+4 -1
View File
@@ -50,7 +50,7 @@
<th class='w-120px'><?php echo $lang->execution->name;?></th>
<td><?php echo html::input('name', $execution->name, "class='form-control' required");?></td><td></td>
</tr>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<tr>
<th><?php echo $lang->execution->code;?></th>
<td><?php echo html::input('code', $execution->code, "class='form-control' required");?></td>
@@ -98,6 +98,9 @@
}
?>
</td>
<td>
<icon class='icon icon-help' data-toggle='popover' data-trigger='focus hover' data-placement='right' data-tip-class='text-muted popover-sm' data-content="<?php echo $lang->execution->typeTip;?>"></icon>
</td>
</tr>
<?php endif;?>
<tr>
+7 -4
View File
@@ -63,10 +63,13 @@
$link = common::hasPriv('execution', 'importTask') ? $this->createLink('execution', 'importTask', "execution=$execution->id") : '#';
echo "<li $class>" . html::a($link, $lang->execution->importTask, '', $misc) . "</li>";
$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 "<li $class id='importBug'>" . html::a($link, $lang->execution->importBug, '', $misc) . "</li>";
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 "<li $class id='importBug'>" . html::a($link, $lang->execution->importBug, '', $misc) . "</li>";
}
?>
</ul>
</div>
+11 -6
View File
@@ -17,7 +17,7 @@
<?php endif;?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php if($execution->lifetime != 'ops' and !in_array($execution->attribute, array('request', 'review'))):?>
<?php if(isset($lang->execution->menu->qa)):?>
<div class="input-control space c-type">
<?php echo html::select('type', $lang->kanban->type, $browseType, 'class="form-control chosen" data-max_drop_width="215"');?>
</div>
@@ -62,7 +62,7 @@
$link = common::hasPriv('execution', 'importTask') ? $this->createLink('execution', 'importTask', "execution=$execution->id") : '#';
echo "<li $misc>" . html::a($link, $lang->execution->importTask, '', $misc) . "</li>";
if($execution->lifetime != 'ops' and !in_array($execution->attribute, array('request', 'review')))
if(isset($lang->execution->menu->qa))
{
$misc = common::hasPriv('execution', 'importBug') ? '' : "class=disabled";
$link = common::hasPriv('execution', 'importBug') ? $this->createLink('execution', 'importBug', "execution=$execution->id") : '#';
@@ -102,12 +102,16 @@
<div class='dropdown' id='createDropdown'>
<button class='btn btn-primary' type='button' data-toggle='dropdown'><i class='icon icon-plus'></i> <?php echo $this->lang->create;?> <span class='caret'></span></button>
<ul class='dropdown-menu pull-right'>
<?php if($execution->lifetime != 'ops' and !in_array($execution->attribute, array('request', 'review'))):?>
<?php $showDivider = false;?>
<?php if(isset($lang->execution->menu->story)):?>
<?php if($canCreateStory) echo '<li>' . html::a(helper::createLink('story', 'create', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id", '', true), $lang->execution->createStory, '', "class='iframe' data-width='80%'") . '</li>';?>
<?php if($canBatchCreateStory) echo '<li>' . html::a(helper::createLink('story', 'batchCreate', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id", '', true), $lang->execution->batchCreateStory, '', "class='iframe' data-width='90%'") . '</li>';?>
<?php if($canLinkStory) echo '<li>' . html::a(helper::createLink('execution', 'linkStory', "execution=$execution->id", '', true), $lang->execution->linkStory, '', "class='iframe' data-width='90%'") . '</li>';?>
<?php if($canLinkStoryByPlan) echo '<li>' . html::a('#linkStoryByPlan', $lang->execution->linkStoryByPlan, '', 'data-toggle="modal"') . '</li>';?>
<?php if($hasStoryButton and $hasBugButton) echo '<li class="divider"></li>';?>
<?php $showDivider = true;?>
<?php endif;?>
<?php if(isset($lang->execution->menu->qa)):?>
<?php if($showDivider) echo '<li class="divider"></li>';?>
<?php if($canCreateBug) echo '<li>' . html::a(helper::createLink('bug', 'create', "productID=$productID&branch=0&extra=executionID=$execution->id", '', true), $lang->bug->create, '', "class='iframe'") . '</li>';?>
<?php if($canBatchCreateBug)
{
@@ -115,9 +119,9 @@
if($productNum > 1) $batchCreateBugLink = '<li>' . html::a('#batchCreateBug', $lang->bug->batchCreate, '', "data-toggle='modal'") . '</li>';
echo $batchCreateBugLink;
}?>
<?php if(($hasStoryButton or $hasBugButton) and $hasTaskButton) echo '<li class="divider"></li>';?>
<?php if($canImportBug) echo '<li>' . html::a(helper::createLink('execution', 'importBug', "execution=$execution->id", '', true), $lang->execution->importBug, '', "class='iframe' data-width='90%'") . '</li>';?>
<?php endif;?>
<?php if($showDivider) echo '<li class="divider"></li>';?>
<?php if($canCreateTask) echo '<li>' . html::a(helper::createLink('task', 'create', "execution=$execution->id", '', true), $lang->task->create, '', "class='iframe' data-width='80%'") . '</li>';?>
<?php if($canBatchCreateTask) echo '<li>' . html::a(helper::createLink('task', 'batchCreate', "execution=$execution->id", '', true), $lang->execution->batchCreateTask, '', "class='iframe' data-width=90%") . '</li>';?>
</ul>
@@ -234,5 +238,6 @@ js::set('priv',
<?php js::set('defaultMinColWidth', $this->config->minColWidth);?>
<?php js::set('defaultMaxColWidth', $this->config->maxColWidth);?>
<?php js::set('teamWords', $lang->execution->teamWords);?>
<?php js::set('canImportBug', ($execution->lifetime != 'ops' and !in_array($execution->attribute, array('request', 'review'))))?>
<?php js::set('canImportBug', isset($lang->execution->menu->qa));?>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -269,7 +269,7 @@
<div class="col-sm-12">
<div class="cell">
<div class="detail">
<?php $hiddenCode = (isset($config->setCode) and $config->setCode == 0) ? 'hidden' : '';?>
<?php $hiddenCode = (!isset($config->setCode) or $config->setCode == 0) ? 'hidden' : '';?>
<h2 class="detail-title"><span class="label-id"><?php echo $execution->id;?></span> <span class="label label-light label-outline <?php echo $hiddenCode;?>"><?php echo $execution->code;?></span> <?php echo $execution->name;?></h2>
<div class="detail-content article-content">
<div><span class="text-limit hidden" data-limit-size="40"><?php echo $execution->desc;?></span><a class="text-primary text-limit-toggle small" data-text-expand="<?php echo $lang->expand;?>" data-text-collapse="<?php echo $lang->collapse;?>"></a></div>
+1 -1
View File
@@ -279,7 +279,7 @@ if($isCustomExport)
</tr>
<tr>
<th><?php echo $lang->file->extension;?></th>
<td><?php echo html::select('fileType', $lang->exportFileTypeList, '', 'onchange=switchEncode(this.value) class="form-control chosen"');?></td>
<td><?php echo html::select('fileType', $lang->exportFileTypeList, '', 'onchange=switchEncode(this.value) class="form-control chosen" data-drop_direction="down"');?></td>
</tr>
<tr>
<th><?php echo $lang->file->encoding;?></th>
+2 -2
View File
@@ -254,9 +254,9 @@ class group extends control
foreach($this->lang->resource as $module => $moduleActions)
{
$modules[$module] = $this->lang->$module->common;
foreach($moduleActions as $action)
foreach($moduleActions as $key => $action)
{
$actions[$module][$action] = $this->lang->$module->$action;
$actions[$module][$key] = $this->lang->$module->$action;
}
}
$this->view->groups = $this->group->getPairs();
+1 -1
View File
@@ -51,7 +51,7 @@
<?php if(is_array($feature) && empty($disabledScrumFeatures)) continue;?>
<tr class='text-center'>
<td class='text-left'><?php echo (is_array($feature) && !empty($disabledScrumFeatures)) ? sprintf($this->lang->custom->scrum->common, implode($lang->comma, $disabledScrumFeatures)) : $this->lang->custom->features[$feature];?></td>
<td><i class='icon text-red icon-close'></i></td>
<td><i class='icon text-red icon-ban-circle'></i></td>
<td><i class='icon text-success icon-check'></i></td>
</tr>
<?php endforeach;?>
+3
View File
@@ -198,6 +198,8 @@ class my extends control
$meetingCount = $pager->recTotal;
}
if($this->app->viewType != 'json')
{
echo <<<EOF
<script>
var taskCount = $taskCount;
@@ -227,6 +229,7 @@ if(isMax !== 0)
}
</script>
EOF;
}
}
/**
+2 -2
View File
@@ -96,7 +96,7 @@ $app->loadLang('product');
$config->product->all = new stdclass();
$config->product->all->search['module'] = 'product';
$config->product->all->search['fields']['name'] = $lang->product->name;
if(!isset($config->setCode) or $config->setCode == 1) $config->product->all->search['fields']['code'] = $lang->product->code;
if(isset($config->setCode) and $config->setCode == 1) $config->product->all->search['fields']['code'] = $lang->product->code;
$config->product->all->search['fields']['id'] = $lang->product->id;
if($config->systemMode == 'ALM')
{
@@ -113,7 +113,7 @@ $config->product->all->search['fields']['createdDate'] = $lang->product->created
$config->product->all->search['fields']['createdBy'] = $lang->product->createdBy;
$config->product->all->search['params']['name'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
if(!isset($config->setCode) or $config->setCode == 1) $config->product->all->search['params']['code'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
if(isset($config->setCode) and $config->setCode == 1) $config->product->all->search['params']['code'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->product->all->search['params']['id'] = array('operator' => '=', 'control' => 'input', 'values' => '');
if($config->systemMode == 'ALM')
{
+1 -1
View File
@@ -56,7 +56,7 @@
<th><?php echo $lang->product->name;?></th>
<td><?php echo html::input('name', '', "class='form-control input-product-title' required");?></td><td></td>
</tr>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<tr>
<th><?php echo $lang->product->code;?></th>
<td><?php echo html::input('code', '', "class='form-control' required");?></td>
+1 -1
View File
@@ -47,7 +47,7 @@
<th class='w-140px'><?php echo $lang->product->name;?></th>
<td class='w-p40-f'><?php echo html::input('name', $product->name, "class='form-control' required");?></td><td></td>
</tr>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<tr>
<th><?php echo $lang->product->code;?></th>
<td><?php echo html::input('code', $product->code, "class='form-control' required");?></td><td></td>
+1 -1
View File
@@ -19,7 +19,7 @@
<div class="col-sm-12">
<div class="cell">
<div class="detail">
<?php $hiddenCode = (isset($config->setCode) and $config->setCode == 0) ? 'hidden' : '';?>
<?php $hiddenCode = (!isset($config->setCode) or $config->setCode == 0) ? 'hidden' : '';?>
<h2 class="detail-title"><span class="label-id"><?php echo $product->id;?></span> <span class="label label-light label-outline <?php echo $hiddenCode;?>"><?php echo $product->code;?></span> <?php echo $product->name;?></h2>
<div class="detail-content article-content">
<p><?php echo $product->desc;?></p>
Regular → Executable
+1 -1
View File
@@ -20,4 +20,4 @@ div.checkbox-primary + span.table-nest-icon {margin-left: 0px !important;}
#checkAll + label.hover:after {border-width: 0px;}
.c-budget {width:100px; text-align: right; padding-right:16px !important;}
.c-status {width: 65px !important;}
.icon-scrum.table-nest-toggle:after, .icon-waterfall.table-nest-toggle:after, .icon-kanban.table-nest-toggle:after {content: ''; border: 0;}
.icon-scrum.table-nest-toggle:after, .icon-waterfall.table-nest-toggle:after, .icon-kanban.table-nest-toggle:after, .icon-waterfallplus.table-nest-toggle:after, .icon-aglieplus.table-nest-toggle:after {content: ''; border: 0;}
+2
View File
@@ -122,6 +122,8 @@ th.table-nest-title > .table-nest-toggle-global:before {width: 100%; left: 0 !im
#programTableList .icon-scrum:before {content: '\e9a2';}
#programTableList .icon-waterfall:before {content: '\e9a4';}
#programTableList .icon-kanban:before {content: '\e983';}
#programTableList .icon-waterfallplus:before {content: '\e9ed';}
#programTableList .icon-agileplus:before {content: '\e9ee';}
#programTableList > tr[data-type="program"] > .c-name > a {color: #0b0f18;}
#programTableList > tr[data-nest-parent] {background: #f8f8f8;}
</style>
+1
View File
@@ -1 +1,2 @@
#planForm tr td {vertical-align: top;}
#mainContent .methodTitle, #mainContent .methodTip {display: inline-block; vertical-align: middle;}
+2
View File
@@ -5,6 +5,8 @@ $(function()
$(this).removeClass('has-error');
$(this).closest('td').find('.text-danger.help-text').remove();
});
$('[data-toggle="popover"]').popover();
});
/**
* Add item to create view of programplan.
+2
View File
@@ -57,4 +57,6 @@ $(function()
if(!result) return false;
}
})
$('[data-toggle="popover"]').popover();
})
+2
View File
@@ -20,6 +20,7 @@ $lang->programplan->delete = 'Delete Stage';
$lang->programplan->close = 'Close Stage';
$lang->programplan->activate = 'Activate Stage';
$lang->programplan->createSubPlan = 'Create Sub Plan';
$lang->programplan->subPlanManage = 'Sub-stages management';
$lang->programplan->parent = 'Parent Stage';
$lang->programplan->emptyParent = 'N/A';
@@ -72,6 +73,7 @@ $lang->programplan->emptyBegin = '『Begin』should not be blank';
$lang->programplan->emptyEnd = '『End』should not be blank';
$lang->programplan->checkBegin = '『Begin』should be valid date';
$lang->programplan->checkEnd = '『End』should be valid date';
$lang->programplan->methodTip = 'When creating sub-stages in a Waterfall Plus project stage, you can choose to create them as stages or as Iterations/Kanban. If the sub-stage is created as an Iteration/Kanban, it does not support creating sub-levels further down.';
$lang->programplan->milestoneList[1] = 'Yes';
$lang->programplan->milestoneList[0] = 'No';
+2
View File
@@ -20,6 +20,7 @@ $lang->programplan->delete = 'Delete Stage';
$lang->programplan->close = 'Close Stage';
$lang->programplan->activate = 'Activate Stage';
$lang->programplan->createSubPlan = 'Create Sub Stage';
$lang->programplan->subPlanManage = 'Sub-stages management';
$lang->programplan->parent = 'Parent Stage';
$lang->programplan->emptyParent = 'N/A';
@@ -72,6 +73,7 @@ $lang->programplan->emptyBegin = '『Begin』should not be blank';
$lang->programplan->emptyEnd = '『End』should not be blank';
$lang->programplan->checkBegin = '『Begin』should be valid date';
$lang->programplan->checkEnd = '『End』should be valid date';
$lang->programplan->methodTip = 'When creating sub-stages in a Waterfall Plus project stage, you can choose to create them as stages or as Iterations/Kanban. If the sub-stage is created as an Iteration/Kanban, it does not support creating sub-levels further down.';
$lang->programplan->milestoneList[1] = 'Yes';
$lang->programplan->milestoneList[0] = 'No';
+2
View File
@@ -20,6 +20,7 @@ $lang->programplan->delete = 'Delete Stage';
$lang->programplan->close = 'Close Stage';
$lang->programplan->activate = 'Activate Stage';
$lang->programplan->createSubPlan = 'Create Sub Plan';
$lang->programplan->subPlanManage = 'Sub-stages management';
$lang->programplan->parent = 'Parent Stage';
$lang->programplan->emptyParent = 'N/A';
@@ -72,6 +73,7 @@ $lang->programplan->emptyBegin = '『Begin』should not be blank';
$lang->programplan->emptyEnd = '『End』should not be blank';
$lang->programplan->checkBegin = '『Begin』should be valid date';
$lang->programplan->checkEnd = '『End』should be valid date';
$lang->programplan->methodTip = 'When creating sub-stages in a Waterfall Plus project stage, you can choose to create them as stages or as Iterations/Kanban. If the sub-stage is created as an Iteration/Kanban, it does not support creating sub-levels further down.';
$lang->programplan->milestoneList[1] = 'Yes';
$lang->programplan->milestoneList[0] = 'No';
+2
View File
@@ -20,6 +20,7 @@ $lang->programplan->delete = '删除阶段';
$lang->programplan->close = '关闭阶段';
$lang->programplan->activate = '激活阶段';
$lang->programplan->createSubPlan = '创建子阶段';
$lang->programplan->subPlanManage = '子阶段的管理方法';
$lang->programplan->parent = '父阶段';
$lang->programplan->emptyParent = '无';
@@ -72,6 +73,7 @@ $lang->programplan->emptyBegin = '『计划开始』日期不能为空';
$lang->programplan->emptyEnd = '『计划完成』日期不能为空';
$lang->programplan->checkBegin = '『计划开始』应当为合法的日期';
$lang->programplan->checkEnd = '『计划完成』应当为合法的日期';
$lang->programplan->methodTip = '融合瀑布项目阶段子阶段创建时,可以选择创建为阶段或创建为迭代/看板。子阶段为迭代/看板,不支持继续向下创建子级。';
$lang->programplan->milestoneList[1] = '是';
$lang->programplan->milestoneList[0] = '否';
+2 -2
View File
@@ -714,7 +714,7 @@ class programplanModel extends model
$sameNames = array_diff_assoc($names, array_unique($names));
$project = $this->loadModel('project')->getByID($projectID);
$setCode = (!isset($this->config->setCode) or $this->config->setCode == 1) ? true : false;
$setCode = (isset($this->config->setCode) and $this->config->setCode == 1) ? true : false;
$sameCodes = $this->checkCodeUnique($codes, isset($planIDList) ? $planIDList : '');
$datas = array();
@@ -1075,7 +1075,7 @@ class programplanModel extends model
if($projectID) $this->loadModel('execution')->checkBeginAndEndDate($projectID, $plan->begin, $plan->end);
if(dao::isError()) return false;
$setCode = (!isset($this->config->setCode) or $this->config->setCode == 1) ? true : false;
$setCode = (isset($this->config->setCode) and $this->config->setCode == 1) ? true : false;
if($setCode and empty($plan->code))
{
dao::$errors['code'][] = sprintf($this->lang->error->notempty, $this->lang->execution->code);
+7 -5
View File
@@ -45,6 +45,7 @@
<div class='main-header'>
<?php if(!empty($planID) and $project->model == 'waterfallplus'):?>
<div class="pull-left">
<div class='methodTitle'><strong><?php echo $lang->programplan->subPlanManage . ':'?></strong></div>
<div class='btn-group'>
<a href='javascript:;' class='btn btn-link btn-limit' data-toggle='dropdown'><span class='text' title='<?php echo zget($lang->programplan->typeList, $executionType);?>'><?php echo zget($lang->programplan->typeList, $executionType);?></span> <span class='caret'></span></a>
<ul class='dropdown-menu' style='max-height:240px; max-width: 300px; overflow-y:auto'>
@@ -57,6 +58,7 @@
?>
</ul>
</div>
<div class='methodTip'><icon class='icon icon-help' data-toggle='popover' data-trigger='focus hover' data-placement='right' data-tip-class='text-muted popover-sm' data-content="<?php echo $lang->programplan->methodTip;?>"></icon></div>
</div>
<?php endif;?>
<div class="btn-toolbar pull-right">
@@ -76,7 +78,7 @@
<tr class='text-center'>
<th class='c-type<?php echo $typeClass;?> required'><?php echo $lang->execution->method;?></th>
<th class='c-name required'><?php echo $executionType == 'stage' ? $name : $lang->nameAB;?></th>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<th class='c-code required'><?php echo $executionType == 'stage' ? $lang->execution->code : $lang->code;?></th>
<?php endif;?>
<th class='c-pm <?php echo zget($visibleFields, 'PM', ' hidden') . zget($requiredFields, 'PM', '', ' required');?>'><?php echo $executionType == 'stage' ? $lang->programplan->PM : $lang->programplan->PMAB;?></th>
@@ -106,7 +108,7 @@
<?php foreach($stages as $stage):?>
<tr>
<td><input type='text' name='names[<?php echo $i;?>]' id='names<?php echo $i;?>' value='<?php echo $stage->name;?>' class='form-control' /></td>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<td><?php echo html::input("codes[$i]", isset($stage->code) ? $stage->code : '', "class='form-control'");?></td>
<?php endif;?>
<td <?php echo zget($visibleFields, 'PM', ' hidden') . zget($requiredFields, 'PM', '', ' required');?>><?php echo html::select("PM[$i]", $PMUsers, '', "class='form-control picker-select'");?></td>
@@ -142,7 +144,7 @@
<tr>
<td class='<?php echo $typeClass . ' text-center ' .zget($lang->execution->typeList, $plan->type);?>'><?php echo zget($lang->execution->typeList, $plan->type);?></td>
<td><input type='text' name="names[<?php echo $i;?>]" id='names<?php echo $i;?>' value='<?php echo $plan->name;?>' class='form-control' /></td>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<td><?php echo html::input("codes[$i]", $plan->code, "class='form-control'");?></td>
<?php endif;?>
<td <?php echo zget($visibleFields, 'PM', ' hidden') . zget($requiredFields, 'PM', '', ' required');?>><?php echo html::select("PM[$i]", $PMUsers, $plan->PM, "class='form-control picker-select'");?></td>
@@ -178,7 +180,7 @@
<tr class='addedItem'>
<td class='<?php echo $typeClass;?>'><?php echo html::select("type[$i]", $lang->execution->typeList, '', "class='form-control chosen'");?></td>
<td><input type='text' name='names[<?php echo $i;?>]' id='names<?php echo $i;?>' value='' class='form-control' /></td>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<td><?php echo html::input("codes[$i]", '', "class='form-control'");?></td>
<?php endif;?>
<td <?php echo zget($visibleFields, 'PM', ' hidden') . zget($requiredFields, 'PM', '', ' required');?>><?php echo html::select("PM[$i]", $PMUsers, '', "class='form-control picker-select'");?></td>
@@ -225,7 +227,7 @@
<tr id='addItem' class='hidden'>
<td class='<?php echo $typeClass;?>'><?php echo html::select("type[$i]", $lang->execution->typeList, '', "class='form-control chosen'");?></td>
<td><input type='text' name='<?php echo "names[$i]";?>' id='names<?php echo $i;?>' class='form-control' /></td>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<td><?php echo html::input("codes[$i]", '', "class='form-control'");?></td>
<?php endif;?>
<td <?php echo zget($visibleFields, 'PM', ' hidden') . zget($requiredFields, 'PM', '', ' required');?>><?php echo html::select("PM[$i]", $PMUsers, '', "class='form-control' id='PM$i'");?></td>
+4 -1
View File
@@ -36,7 +36,7 @@
<th class='w-100px'><?php echo $lang->programplan->name;?> </th>
<td colspan='2'><?php echo html::input('name', $plan->name, "class='form-control'");?></td>
</tr>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<tr>
<th class='w-100px'><?php echo $lang->execution->code;?> </th>
<td class='required' colspan='2'><?php echo html::input('code', $plan->code, "class='form-control'");?></td>
@@ -60,6 +60,9 @@
<td colspan='2'>
<?php echo $enableOptionalAttr ? html::select('attribute', $lang->stage->typeList, $plan->attribute, "class='form-control'") : zget($lang->stage->typeList, $plan->attribute);?>
</td>
<td>
<icon class='icon icon-help' data-toggle='popover' data-trigger='focus hover' data-placement='right' data-tip-class='text-muted popover-sm' data-content="<?php echo $lang->execution->typeTip;?>"></icon>
</td>
</tr>
<?php if($plan->setMilestone):?>
<tr>
+1 -1
View File
@@ -136,7 +136,7 @@ $config->project->datatable->fieldList['actions']['width'] = '165';
$config->project->datatable->fieldList['actions']['required'] = 'yes';
$config->project->datatable->fieldList['actions']['pri'] = '1';
if(isset($config->setCode) and $config->setCode == 0) unset($config->project->datatable->fieldList['code']);
if(!isset($config->setCode) or $config->setCode == 0) unset($config->project->datatable->fieldList['code']);
$config->project->checkList = new stdclass();
$config->project->checkList->scrum = array('bug', 'execution', 'build', 'doc', 'release', 'testtask', 'case');
+1 -1
View File
@@ -68,7 +68,7 @@ class project extends control
$fields[$fieldName] = zget($projectLang, $fieldName);
unset($fields[$key]);
}
if(isset($this->config->setCode) and empty($this->config->setCode)) unset($fields['code']);
if(!isset($this->config->setCode) or empty($this->config->setCode)) unset($fields['code']);
if(isset($fields['hasProduct'])) $fields['hasProduct'] = $projectLang->type;
+2 -2
View File
@@ -1023,7 +1023,7 @@ class projectModel extends model
$programPairs += $this->loadModel('program')->getPairs();
$this->config->project->search['params']['parent']['values'] = $programPairs;
if(isset($this->config->setCode) and $this->config->setCode == 0) unset($this->config->project->search['fields']['code'], $this->config->project->search['params']['code']);
if(!isset($this->config->setCode) or $this->config->setCode == 0) unset($this->config->project->search['fields']['code'], $this->config->project->search['params']['code']);
if($this->config->systemMode == 'light') unset($this->config->project->search['fields']['parent'], $this->config->project->search['params']['parent']);
$this->loadModel('search')->setSearchParams($this->config->project->search);
@@ -1250,7 +1250,7 @@ class projectModel extends model
->stripTags($this->config->project->editor->create['id'], $this->config->allowedTags)
->remove('products,branch,plans,delta,newProduct,productName,future,contactListMenu,teamMembers')
->get();
if(isset($this->config->setCode) and $this->config->setCode == 0) unset($project->code);
if(!isset($this->config->setCode) or $this->config->setCode == 0) unset($project->code);
/* Lean mode relation defaultProgram. */
if($this->config->systemMode == 'light') $project->parent = $this->config->global->defaultProgram;
+2 -2
View File
@@ -34,7 +34,7 @@
<th class='c-parent'><?php echo $lang->project->parent;?></th>
<?php endif;?>
<th class='c-name required'><?php echo $lang->project->name;?></th>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<th class='c-name required'><?php echo $lang->project->code;?></th>
<?php endif?>
<th class="c-user-box <?php echo strpos($requiredFields, 'PM') !== false ? 'required' : '';?>"> <?php echo $lang->project->PM;?></th>
@@ -63,7 +63,7 @@
<?php endif;?>
<?php endif;?>
<td title='<?php echo $project->name;?>'><?php echo html::input("names[$projectID]", $project->name, "class='form-control'");?></td>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<td title='<?php echo $project->code;?>'><?php echo html::input("codes[$projectID]", $project->code, "class='form-control'");?></td>
<?php endif;?>
<td><?php echo html::select("PMs[$projectID]", $PMUsers, $project->PM, "class='form-control chosen'");?></td>
+5 -1
View File
@@ -48,7 +48,11 @@
<h2><?php echo $createTitle;?></h2>
<?php if(!commonModel::isTutorialMode()): ?>
<div class="pull-right btn-toolbar">
<?php if($config->edition != 'max' or $model == 'kanban'):?>
<button type='button' class='btn btn-link' data-toggle='modal' data-target='#copyProjectModal'><?php echo html::icon($lang->icons['copy'], 'muted') . ' ' . $lang->project->copy;?></button>
<?php else: ?>
<button type='button' class='btn btn-link open-btn' data-toggle='modal' data-target='#maxCopyProjectModal'><?php echo html::icon($lang->icons['copy'], 'muted') . ' ' . $lang->project->copy;?></button>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
@@ -69,7 +73,7 @@
<td class="col-main"><?php echo html::input('name', $name, "class='form-control' required");?></td>
<td></td>
</tr>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<tr>
<th><?php echo $lang->project->code;?></th>
<td><?php echo html::input('code', $code, "class='form-control' required");?></td>
+1 -1
View File
@@ -75,7 +75,7 @@
<td class="col-main"><?php echo html::input('name', $project->name, "class='form-control' required");?></td>
<td colspan='2'></td>
</tr>
<?php if(!isset($config->setCode) or $config->setCode == 1):?>
<?php if(isset($config->setCode) and $config->setCode == 1):?>
<tr>
<th><?php echo $lang->project->code;?></th>
<td><?php echo html::input('code', $project->code, "class='form-control' required");?></td>
+1 -1
View File
@@ -114,7 +114,7 @@
<div class="col-sm-12">
<div class="cell">
<div class="detail">
<?php $hiddenCode = (isset($config->setCode) and $config->setCode == 0) ? 'hidden' : '';?>
<?php $hiddenCode = (!isset($config->setCode) or $config->setCode == 0) ? 'hidden' : '';?>
<h2 class="detail-title"><span class="label-id"><?php echo $project->id;?></span> <span class="label label-light label-outline <?php echo $hiddenCode;?>"><?php echo $project->code;?></span> <?php echo $project->name;?></h2>
<div class="detail-content article-content">
<div><span class="text-limit hidden" data-limit-size="40"><?php echo $project->desc;?></span><a class="text-primary text-limit-toggle small" data-text-expand="<?php echo $lang->expand;?>" data-text-collapse="<?php echo $lang->collapse;?>"></a></div>
+1 -1
View File
@@ -599,7 +599,7 @@ class repo extends control
}
/* Refresh repo. */
if($refresh) $this->repo->updateCommit($repoID, $originBranchID);
if($refresh) $this->repo->updateCommit($repoID, $objectID, $originBranchID);
/* Get files info. */
$infos = $this->repo->getFileCommits($repo, $branchID, $path);
+1 -1
View File
@@ -2810,7 +2810,7 @@ class repoModel extends model
* @access public
* @return void
*/
public function updateCommit($repoID, $branchID = 0)
public function updateCommit($repoID, $objectID = 0, $branchID = 0)
{
$repo = $this->getRepoByID($repoID);
/* Update code commit history. */
+1 -1
View File
@@ -199,7 +199,7 @@ class stageModel extends model
$moduleName = $this->app->rawModule;
$methodName = $this->app->rawMethod;
if(!isset($this->lang->admin->menuList->model['subMenu']['waterfall']['exclude'])) $this->lang->admin->menuList->model['subMenu']['waterfall']['exclude'] = '';
if(!isset($this->lang->admin->menuList->model['subMenu']['waterfallplus']['exclude'])) $this->lang->admin->menuList->model['subMenu']['agileplus']['exclude'] = '';
if(!isset($this->lang->admin->menuList->model['subMenu']['waterfallplus']['exclude'])) $this->lang->admin->menuList->model['subMenu']['waterfallplus']['exclude'] = '';
if($type == 'waterfall')
{
$this->lang->admin->menuList->model['subMenu']['waterfallplus']['exclude'] .= ",{$moduleName}-{$methodName}";
+1 -1
View File
@@ -36,7 +36,7 @@
<?php if(is_array($feature) && empty($disabledScrumFeatures)) continue;?>
<tr class='text-center'>
<td class='text-left'><?php echo (is_array($feature) && !empty($disabledScrumFeatures)) ? sprintf($this->lang->custom->scrum->common, implode($lang->comma, $disabledScrumFeatures)) : $this->lang->custom->features[$feature];?></td>
<td><i class='icon text-red icon-close'></i></td>
<td><i class='icon text-red icon-ban-circle'></i></td>
<td><i class='icon text-success icon-check'></i></td>
</tr>
<?php endforeach;?>