Merge branch 'main' into feature/doctemplate
This commit is contained in:
+47
-13
@@ -1,10 +1,46 @@
|
||||
<?php
|
||||
|
||||
/* 缓存设置。Cache settings. */
|
||||
$config->cache = new stdclass();
|
||||
$config->cache->enable = false; // 是否开启缓存。Enable cache or not.
|
||||
$config->cache->lifetime = 5 * 60; // 缓存生存时间。The lifetime of cache.
|
||||
$config->cache->driver = 'File'; // 缓存驱动。 The driver of cache. can be File|Yac|Apcu.
|
||||
$config->cache->enable = false; // 是否开启缓存。Enable cache or not.
|
||||
$config->cache->driver = 'apcu'; // 缓存驱动。 The driver of cache. Can be file|yac|apcu|redis.
|
||||
$config->cache->scope = ''; // 缓存服务范围。The scope of cache. Can be private|shared.
|
||||
$config->cache->namespace = ''; // 缓存命名空间。The namespace of cache.
|
||||
$config->cache->lifetime = 0; // 缓存生存时间,默认永不过期。The lifetime of cache. Default is no expiration.
|
||||
|
||||
$config->cache->dao = new stdClass();
|
||||
$config->cache->dao->enable = true; // 是否开启 DAO 缓存。Enable DAO cache or not.
|
||||
$config->cache->dao->lifetime = 604800; // DAO 缓存生存时间,默认为 7 天。The lifetime of DAO cache. Default is 7 days.
|
||||
|
||||
$config->cache->client = new stdClass();
|
||||
$config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not.
|
||||
|
||||
// Format : $config->cache->raw[TABLE_NAME] = 'KEY_FIELD';
|
||||
// The TABLE_NAME is the name of the table in the database.
|
||||
// The KEY_FIELD is the field of the table which is used to generate the key of the cache. It must be unique in the table.
|
||||
|
||||
$config->cache->raw = [];
|
||||
$config->cache->raw[TABLE_CONFIG] = 'id';
|
||||
$config->cache->raw[TABLE_BUILD] = 'id';
|
||||
$config->cache->raw[TABLE_MODULE] = 'id';
|
||||
$config->cache->raw[TABLE_PRODUCT] = 'id';
|
||||
$config->cache->raw[TABLE_PROJECT] = 'id';
|
||||
$config->cache->raw[TABLE_RELEASE] = 'id';
|
||||
$config->cache->raw[TABLE_USER] = 'account';
|
||||
|
||||
$config->cache->res = [];
|
||||
$config->cache->res[TABLE_MODULE][] = ['name' => 'CACHE_MODULE_TREE', 'fields' => ['type', 'root', 'branch']];
|
||||
|
||||
$config->cache->keys = [];
|
||||
foreach($config->cache->res as $table => $caches)
|
||||
{
|
||||
foreach($caches as $cache)
|
||||
{
|
||||
$cache = (object)$cache;
|
||||
$cache->table = $table;
|
||||
$config->cache->keys[$cache->name] = $cache;
|
||||
define($cache->name, $cache->name);
|
||||
}
|
||||
}
|
||||
|
||||
$config->cache->enableFullPage = false; // 是否开启整页缓存。Enable full page cache or not.
|
||||
$config->cache->fullPageLifetime = 5 * 60;
|
||||
@@ -32,12 +68,10 @@ $config->cacheKeys->execution->ajaxGetDropMenuExecutions = 'ajaxDropMenuExecutio
|
||||
$config->cacheKeys->bug = new stdclass();
|
||||
$config->cacheKeys->bug->browse = 'bugBrowse%s';
|
||||
|
||||
$config->cache->dao = new stdClass();
|
||||
$config->cache->dao->enable = false; // 是否开启 dao 缓存。Enable dao cache or not.
|
||||
$config->cache->dao->lifetime = 60 * 60 * 24 * 30; // 缓存生存时间,默认为 30 天。The lifetime of cache, default is 30 days.
|
||||
$config->cache->dao->driver = 'Apcu'; // 缓存驱动,可以为 File|Yac|Apcu。The driver of cache. can be File|Yac|Apcu.
|
||||
|
||||
$config->cache->client = new stdClass();
|
||||
$config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not.
|
||||
|
||||
$config->redis = null;
|
||||
$config->redis = new stdClass();
|
||||
$config->redis->host = '';
|
||||
$config->redis->port = '';
|
||||
$config->redis->username = '';
|
||||
$config->redis->password = '';
|
||||
$config->redis->database = 0;
|
||||
$config->redis->serializer = 'igbinary'; // php|igbinary
|
||||
|
||||
+9
-9
@@ -205,10 +205,6 @@ if(file_exists($filterConfig)) include $filterConfig;
|
||||
$dbConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'db.php';
|
||||
if(file_exists($dbConfig)) include $dbConfig;
|
||||
|
||||
/* 引用缓存的配置。 Include the cache config file. */
|
||||
$cacheConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cache.php';
|
||||
if(file_exists($cacheConfig)) include $cacheConfig;
|
||||
|
||||
/* 读取环境变量的配置。 Read the env config. */
|
||||
if($config->inContainer || $config->inQuickon)
|
||||
{
|
||||
@@ -229,11 +225,6 @@ if($config->inContainer || $config->inQuickon)
|
||||
$config->default->lang = getenv('ZT_DEFAULT_LANG');
|
||||
}
|
||||
|
||||
/* 引用自定义的配置。 Include the custom config file. */
|
||||
$myConfigRoot = (defined('RUN_MODE') and in_array(RUN_MODE, array('test', 'uitest'))) ? dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'test' . DIRECTORY_SEPARATOR . 'config' : dirname(__FILE__);
|
||||
$myConfig = $myConfigRoot . DIRECTORY_SEPARATOR . 'my.php';
|
||||
if(file_exists($myConfig)) include $myConfig;
|
||||
|
||||
/* 禅道配置文件。zentaopms settings. */
|
||||
$zentaopmsConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'zentaopms.php';
|
||||
if(file_exists($zentaopmsConfig)) include $zentaopmsConfig;
|
||||
@@ -246,10 +237,19 @@ if(file_exists($actionsMapConfig)) include $actionsMapConfig;
|
||||
$routesConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'routes.php';
|
||||
if(file_exists($routesConfig)) include $routesConfig;
|
||||
|
||||
/* 引用缓存的配置。 Include the cache config file. */
|
||||
$cacheConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cache.php';
|
||||
if(file_exists($cacheConfig)) include $cacheConfig;
|
||||
|
||||
/* Include extension config files. */
|
||||
$extConfigFiles = glob(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ext/*.php');
|
||||
if($extConfigFiles) foreach($extConfigFiles as $extConfigFile) include $extConfigFile;
|
||||
|
||||
/* 引用自定义的配置。 Include the custom config file. */
|
||||
$myConfigRoot = (defined('RUN_MODE') and in_array(RUN_MODE, array('test', 'uitest'))) ? dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'test' . DIRECTORY_SEPARATOR . 'config' : dirname(__FILE__);
|
||||
$myConfig = $myConfigRoot . DIRECTORY_SEPARATOR . 'my.php';
|
||||
if(file_exists($myConfig)) include $myConfig;
|
||||
|
||||
/* Set version. */
|
||||
if($config->edition != 'open')
|
||||
{
|
||||
|
||||
@@ -464,6 +464,7 @@ define('TABLE_JOB', '`' . $config->db->prefix . 'job`');
|
||||
define('TABLE_COMPILE', '`' . $config->db->prefix . 'compile`');
|
||||
define('TABLE_MR', '`' . $config->db->prefix . 'mr`');
|
||||
define('TABLE_MRAPPROVAL', '`' . $config->db->prefix . 'mrapproval`');
|
||||
define('TABLE_MARK', '`' . $config->db->prefix . 'mark`');
|
||||
|
||||
define('TABLE_SERVERROOM', '`' . $config->db->prefix . 'serverroom`');
|
||||
define('TABLE_HOST', '`' . $config->db->prefix . 'host`');
|
||||
@@ -490,6 +491,7 @@ if(!defined('TABLE_SEARCHDICT')) define('TABLE_SEARCHDICT', $config->db->prefi
|
||||
define('TABLE_SCREEN', '`' . $config->db->prefix . 'screen`');
|
||||
define('TABLE_CHART', '`' . $config->db->prefix . 'chart`');
|
||||
define('TABLE_PIVOT', '`' . $config->db->prefix . 'pivot`');
|
||||
define('TABLE_PIVOTSPEC', '`' . $config->db->prefix . 'pivotspec`');
|
||||
define('TABLE_PIVOTDRILL', '`' . $config->db->prefix . 'pivotdrill`');
|
||||
define('TABLE_DASHBOARD', '`' . $config->db->prefix . 'dashboard`');
|
||||
define('TABLE_DATASET', '`' . $config->db->prefix . 'dataset`');
|
||||
@@ -726,6 +728,7 @@ $config->objectTables['workflowgroup'] = TABLE_WORKFLOWGROUP;
|
||||
$config->objectTables['productline'] = TABLE_MODULE;
|
||||
$config->objectTables['repocommit'] = TABLE_REPOHISTORY;
|
||||
$config->objectTables['system'] = TABLE_SYSTEM;
|
||||
$config->objectTables['mark'] = TABLE_MARK;
|
||||
|
||||
$config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions', 'aiPrompts', 'promptDesign', 'promptExec');
|
||||
$config->disabledFeatures = '';
|
||||
|
||||
@@ -30,3 +30,50 @@ ALTER TABLE `zt_review` ADD `toAuditBy` varchar(30) not NULL default '' AFTER `l
|
||||
ALTER TABLE `zt_review` ADD `toAuditDate` datetime NULL AFTER `toAuditBy`;
|
||||
|
||||
ALTER TABLE `zt_design` ADD `storyVersion` smallint(6) UNSIGNED NOT NULL DEFAULT '1' AFTER `story`;
|
||||
|
||||
ALTER TABLE zt_dataview MODIFY `fields` text NULL;
|
||||
ALTER TABLE zt_dataview MODIFY `objects` text NULL;
|
||||
ALTER TABLE zt_dataview MODIFY `mode` varchar(50) NOT NULL DEFAULT 'builder';
|
||||
ALTER TABLE zt_dataview ADD `driver` enum('mysql','duckdb') NOT NULL DEFAULT 'mysql' AFTER `code`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `zt_pivotspec` (
|
||||
`pivot` mediumint(8) NOT NULL,
|
||||
`version` varchar(10) NOT NULL,
|
||||
`driver` enum('mysql', 'duckdb') NOT NULL default 'mysql',
|
||||
`mode` varchar(10) NOT NULL default 'builder',
|
||||
`name` text NULL,
|
||||
`desc` text NULL,
|
||||
`sql` text NULL,
|
||||
`fields` text NULL,
|
||||
`langs` text NULL,
|
||||
`vars` text NULL,
|
||||
`objects` text NULL,
|
||||
`settings` text NULL,
|
||||
`filters` text NULL,
|
||||
`createdDate` datetime NULL
|
||||
) ENGINE = InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `idx_pivot_version` ON `zt_pivotspec`(`pivot`, `version`);
|
||||
|
||||
ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL DEFAULT '0' AFTER `builtin`;
|
||||
ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varchar(10) NOT NULL DEFAULT 'builder';
|
||||
ALTER TABLE `zt_pivot` CHANGE `sql` `sql` text NULL;
|
||||
ALTER TABLE `zt_pivot` CHANGE `fields` `fields` text NULL;
|
||||
ALTER TABLE `zt_pivot` CHANGE `langs` `langs` text NULL;
|
||||
ALTER TABLE `zt_pivot` CHANGE `vars` `vars` text NULL;
|
||||
ALTER TABLE `zt_pivot` CHANGE `objects` `objects` text NULL;
|
||||
ALTER TABLE `zt_pivot` CHANGE `settings` `settings` text NULL;
|
||||
ALTER TABLE `zt_pivot` CHANGE `filters` `filters` text NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `zt_mark` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`objectType` varchar(50) NOT NULL DEFAULT '',
|
||||
`objectID` mediumint(8) unsigned NOT NULL DEFAULT 0,
|
||||
`version` varchar(50) NOT NULL DEFAULT '',
|
||||
`account` char(30) NOT NULL DEFAULT '',
|
||||
`date` datetime NULL,
|
||||
`mark` varchar(50) NOT NULL DEFAULT '',
|
||||
`extra` varchar(255) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `idx_object` ON `zt_mark`(`objectType`,`objectID`);
|
||||
CREATE INDEX `idx_account` ON `zt_mark`(`account`);
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
ALTER TABLE zt_dataview MODIFY `fields` text NULL;
|
||||
ALTER TABLE zt_dataview MODIFY `objects` text NULL;
|
||||
ALTER TABLE zt_dataview MODIFY `mode` varchar(50) NOT NULL DEFAULT 'builder';
|
||||
ALTER TABLE zt_dataview ADD `driver` enum('mysql','duckdb') NOT NULL DEFAULT 'mysql' AFTER `code`;
|
||||
+45
-9
@@ -15355,22 +15355,23 @@ CREATE TABLE IF NOT EXISTS `zt_pivot` (
|
||||
`dimension` mediumint(8) unsigned NOT NULL DEFAULT 0,
|
||||
`group` varchar(255) NOT NULL DEFAULT '',
|
||||
`code` varchar(255) NOT NULL DEFAULT '',
|
||||
`driver` enum('mysql', 'duckdb') not NULL default 'mysql',
|
||||
`mode` enum('text', 'builder') not NULL default 'builder',
|
||||
`driver` enum('mysql', 'duckdb') NOT NULL default 'mysql',
|
||||
`mode` varchar(10) NOT NULL default 'builder',
|
||||
`name` text NULL,
|
||||
`desc` text NULL,
|
||||
`acl` enum('open','private') NOT NULL DEFAULT 'open',
|
||||
`whitelist` text NULL,
|
||||
`sql` mediumtext NULL,
|
||||
`fields` mediumtext NULL,
|
||||
`langs` mediumtext NULL,
|
||||
`vars` mediumtext NULL,
|
||||
`objects` mediumtext NULL,
|
||||
`settings` mediumtext NULL,
|
||||
`filters` mediumtext NULL,
|
||||
`sql` text NULL,
|
||||
`fields` text NULL,
|
||||
`langs` text NULL,
|
||||
`vars` text NULL,
|
||||
`objects` text NULL,
|
||||
`settings` text NULL,
|
||||
`filters` text NULL,
|
||||
`step` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`stage` enum('draft','published') NOT NULL DEFAULT 'draft',
|
||||
`builtin` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
`version` varchar(10) NOT NULL DEFAULT '0',
|
||||
`createdBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`createdDate` datetime NULL,
|
||||
`editedBy` varchar(30) NOT NULL DEFAULT '',
|
||||
@@ -15381,6 +15382,25 @@ CREATE TABLE IF NOT EXISTS `zt_pivot` (
|
||||
CREATE INDEX `dimension` ON `zt_pivot` (`dimension`);
|
||||
CREATE INDEX `group` ON `zt_pivot` (`group`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_pivotspec`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_pivotspec` (
|
||||
`pivot` mediumint(8) NOT NULL,
|
||||
`version` varchar(10) NOT NULL,
|
||||
`driver` enum('mysql', 'duckdb') NOT NULL default 'mysql',
|
||||
`mode` varchar(10) NOT NULL default 'builder',
|
||||
`name` text NULL,
|
||||
`desc` text NULL,
|
||||
`sql` text NULL,
|
||||
`fields` text NULL,
|
||||
`langs` text NULL,
|
||||
`vars` text NULL,
|
||||
`objects` text NULL,
|
||||
`settings` text NULL,
|
||||
`filters` text NULL,
|
||||
`createdDate` datetime NULL
|
||||
) ENGINE = InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `idx_pivot_version` ON `zt_pivotspec`(`pivot`, `version`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_sqlbuilder`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_sqlbuilder` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
@@ -16106,6 +16126,7 @@ REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `
|
||||
('zh-tw', 'custom', 'relationList', '3', '{\"relation\":\"\\u91cd\\u8907\",\"relativeRelation\":\"\\u91cd\\u8907\"}', '0', 'all'),
|
||||
('zh-tw', 'custom', 'relationList', '4', '{\"relation\":\"\\u5f15\\u7528\",\"relativeRelation\":\"\\u88ab\\u5f15\\u7528\"}', '0', 'all');
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_system`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_system` (
|
||||
`id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
@@ -16125,3 +16146,18 @@ CREATE TABLE IF NOT EXISTS `zt_system` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `idx_product` ON `zt_system`(`product`);
|
||||
CREATE INDEX `idx_status` ON `zt_system`(`status`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_mark`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_mark` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`objectType` varchar(50) NOT NULL DEFAULT '',
|
||||
`objectID` mediumint(8) unsigned NOT NULL DEFAULT 0,
|
||||
`version` varchar(50) NOT NULL DEFAULT '',
|
||||
`account` char(30) NOT NULL DEFAULT '',
|
||||
`date` datetime NULL,
|
||||
`mark` varchar(50) NOT NULL DEFAULT '',
|
||||
`extra` varchar(255) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `idx_object` ON `zt_mark`(`objectType`,`objectID`);
|
||||
CREATE INDEX `idx_account` ON `zt_mark`(`account`);
|
||||
|
||||
@@ -168,6 +168,15 @@ class baseControl
|
||||
*/
|
||||
public $devicePrefix;
|
||||
|
||||
/**
|
||||
* $mao对象,用于访问缓存。
|
||||
* The $mao object, used to access the cache.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $mao;
|
||||
|
||||
/**
|
||||
* 构造方法。
|
||||
*
|
||||
@@ -198,6 +207,7 @@ class baseControl
|
||||
$this->config = $config;
|
||||
$this->lang = $lang;
|
||||
$this->dbh = $dbh;
|
||||
$this->mao = $app->mao;
|
||||
$this->viewType = $this->app->getViewType();
|
||||
$this->appName = $appName ?: $this->app->getAppName();
|
||||
|
||||
@@ -324,7 +334,6 @@ class baseControl
|
||||
|
||||
$this->{$moduleName} = $model;
|
||||
$this->dao = $model->dao;
|
||||
$this->cache = $model->cache;
|
||||
return $model;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,13 +124,13 @@ class baseModel
|
||||
public $global;
|
||||
|
||||
/**
|
||||
* $cache对象,用于访问缓存。
|
||||
* The $cache object, used to access the cache.
|
||||
* $mao对象,用于访问缓存。
|
||||
* The $mao object, used to access the cache.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $cache;
|
||||
public $mao;
|
||||
|
||||
/**
|
||||
* 构造方法。
|
||||
@@ -153,6 +153,7 @@ class baseModel
|
||||
$this->lang = $lang;
|
||||
$this->dbh = $dbh;
|
||||
$this->dao = $dao;
|
||||
$this->mao = $app->mao;
|
||||
$this->appName = empty($appName) ? $this->app->getAppName() : $appName;
|
||||
|
||||
$moduleName = $this->getModuleName();
|
||||
@@ -160,7 +161,6 @@ class baseModel
|
||||
if($moduleName != 'common') $this->app->loadModuleConfig($moduleName, $this->appName);
|
||||
|
||||
$this->setSuperVars();
|
||||
if($this->config->cache->enable) $this->loadCache();
|
||||
|
||||
/**
|
||||
* 读取当前模块的tao类。
|
||||
@@ -310,20 +310,6 @@ class baseModel
|
||||
return $extensionObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载缓存类。
|
||||
* Load cache class.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function loadCache()
|
||||
{
|
||||
$this->app->loadClass('cache', $static = true);
|
||||
$namespace = isset($this->session->user->account) ? $this->session->user->account : 'guest';
|
||||
$this->cache = cache::create($this->config->cache->driver, $namespace, $this->config->cache->lifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除记录。
|
||||
* Delete one record.
|
||||
|
||||
@@ -306,6 +306,15 @@ class baseRouter
|
||||
*/
|
||||
public $lang;
|
||||
|
||||
/**
|
||||
* 全局缓存对象,用于操作缓存。
|
||||
* The global cache object, used to operate cache.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $cache = null;
|
||||
|
||||
/**
|
||||
* 全局$dbh对象,数据库连接句柄。
|
||||
* The global $dbh object, the database connection handler.
|
||||
@@ -324,6 +333,15 @@ class baseRouter
|
||||
*/
|
||||
public $dao;
|
||||
|
||||
/**
|
||||
* $mao对象,用于访问或者更新缓存。
|
||||
* The $mao object, used to access or update cache.
|
||||
*
|
||||
* @var mao
|
||||
* @access public
|
||||
*/
|
||||
public $mao;
|
||||
|
||||
/**
|
||||
* 从数据库的句柄。
|
||||
* The slave database handler.
|
||||
@@ -436,6 +454,14 @@ class baseRouter
|
||||
*/
|
||||
public $clientCacheTime = 0;
|
||||
|
||||
/**
|
||||
* 缓存Model。
|
||||
* The cache model.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
public $cacheModel;
|
||||
|
||||
/**
|
||||
* 构造方法, 设置路径,类,超级变量等。注意:
|
||||
* 1.应该使用createApp()方法实例化router类;
|
||||
@@ -486,8 +512,7 @@ class baseRouter
|
||||
$this->setTimezone();
|
||||
|
||||
if($this->config->framework->autoConnectDB) $this->connectDB();
|
||||
|
||||
if($this->config->redis) $this->redis = $this->connectToRedis();
|
||||
$this->loadCache();
|
||||
|
||||
$this->setupProfiling();
|
||||
$this->setupXhprof();
|
||||
@@ -495,8 +520,6 @@ class baseRouter
|
||||
$this->setEdition();
|
||||
|
||||
$this->setClient();
|
||||
|
||||
$this->loadCacheConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2798,29 +2821,6 @@ class baseRouter
|
||||
include $mainConfigFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库加载缓存配置。
|
||||
* Load the cache config from the database.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function loadCacheConfig()
|
||||
{
|
||||
if(!$this->checkInstalled()) return false;
|
||||
|
||||
$globalCache = $this->dao->select('`value`')->from(TABLE_CONFIG)->where('`module`')->eq('common')->andWhere('`section`')->eq('global')->andWhere('`key`')->eq('cache')->limit(1)->fetch('value');
|
||||
if(!$globalCache) return false;
|
||||
|
||||
$caches = json_decode($globalCache);
|
||||
foreach($caches as $cacheKey => $cache)
|
||||
{
|
||||
if(!isset($this->config->cache->$cacheKey)) $this->config->cache->$cacheKey = new stdClass();
|
||||
|
||||
foreach($cache as $key => $value) $this->config->cache->$cacheKey->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当multiSite功能打开的时候,加载额外的配置文件。
|
||||
* When multiSite feature enabled, load extra config file.
|
||||
@@ -3088,6 +3088,38 @@ class baseRouter
|
||||
|
||||
$dao = new $driver($this);
|
||||
$this->dao = $dao;
|
||||
|
||||
$this->loadClass('mao', true);
|
||||
$this->mao = new mao($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载缓存类,初始化全局缓存对象。
|
||||
* Load the cache class and init the global cache object.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
private function loadCache()
|
||||
{
|
||||
if(!$this->checkInstalled()) return false;
|
||||
|
||||
$cacheConfig = $this->dao->select('`key`, value')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('cache')->fetchPairs();
|
||||
foreach($cacheConfig as $key => $value) $this->config->cache->$key = $value;
|
||||
if(!$this->config->cache->enable) return;
|
||||
|
||||
if($this->config->cache->driver == 'redis')
|
||||
{
|
||||
$redisConfig = $this->dao->select('`key`, value')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('redis')->fetchPairs();
|
||||
foreach($redisConfig as $key => $value) $this->config->redis->$key = $value;
|
||||
}
|
||||
|
||||
$this->loadClass('cache', true);
|
||||
$this->cache = new Zentao\Cache\cache($this);
|
||||
|
||||
/* 为 dao 和 mao 设置访问缓存的对象。 Set the cache object for dao and mao. */
|
||||
$this->dao->cache = $this->cache;
|
||||
$this->mao->cache = $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3205,6 +3237,9 @@ class baseRouter
|
||||
*/
|
||||
public function shutdown()
|
||||
{
|
||||
/* 如果开启了缓存则关闭缓存连接,主要用于 Redis 等缓存服务。Close the cache connection if it's open. */
|
||||
if(!empty($this->cache)) $this->cache->close();
|
||||
|
||||
/* 如果debug模式开启,保存sql语句(If debug on, save sql queries) */
|
||||
if(!empty($this->config->debug)) $this->saveSQL();
|
||||
|
||||
@@ -3509,45 +3544,6 @@ class baseRouter
|
||||
|
||||
return $installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to Redis Server.
|
||||
* @link https://pecl.php.net/package/redis
|
||||
* @link https://github.com/phpredis/phpredis/
|
||||
* @return Redis|object|false
|
||||
*/
|
||||
public function connectToRedis()
|
||||
{
|
||||
if(!extension_loaded('redis')) return false;
|
||||
|
||||
global $config;
|
||||
|
||||
if(empty($config->redis)) return false;
|
||||
|
||||
try
|
||||
{
|
||||
$redis = new Redis();
|
||||
|
||||
$version = phpversion('redis');
|
||||
if(version_compare($version, '5.3.0', 'ge'))
|
||||
{
|
||||
$redis->connect($config->redis->host , $config->redis->port, $config->redis->timeout, '', 0, 0, ['auth' => [$config->redis->username, $config->redis->password]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$redis->connect($config->redis->host , $config->redis->port, $config->redis->timeout, '', 0, 0);
|
||||
$redis->auth(['pass' => $config->redis->password]);
|
||||
}
|
||||
|
||||
if(!$redis->ping()) return false;
|
||||
}
|
||||
catch(RedisException $e)
|
||||
{
|
||||
$this->triggerError($e->getMessage(), __FILE__, __LINE__, true);
|
||||
}
|
||||
|
||||
if($redis) return $redis;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
include __DIR__ . '/base/control.class.php';
|
||||
class control extends baseControl
|
||||
{
|
||||
public $redis = false;
|
||||
|
||||
/**
|
||||
* Check requiredFields and set exportFields for workflow.
|
||||
*
|
||||
@@ -37,8 +35,6 @@ class control extends baseControl
|
||||
|
||||
$this->app->setOpenApp();
|
||||
|
||||
if($this->config->redis) $this->redis = $this->app->redis;
|
||||
|
||||
if($this->config->edition == 'open') return false;
|
||||
|
||||
/* Code for task #9224. Set requiredFields for workflow. */
|
||||
|
||||
@@ -433,6 +433,47 @@ class helper extends baseHelper
|
||||
$checkFunc = 'check' . $operator;
|
||||
return validater::$checkFunc($value1, $value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接 Redis 服务器。
|
||||
* Connect to Redis server.
|
||||
*
|
||||
* @param object $setting
|
||||
* @static
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public static function connectRedis(object $setting)
|
||||
{
|
||||
if(!class_exists('Redis')) throw new Exception('The Redis extension is not installed.');
|
||||
|
||||
try
|
||||
{
|
||||
$redis = new Redis();
|
||||
|
||||
$version = phpversion('redis');
|
||||
if(version_compare($version, '5.3.0', 'ge'))
|
||||
{
|
||||
$redis->connect($setting->host, (int)$setting->port, 1, null, 0, 0, ['auth' => [$setting->username ?: null, $setting->password ?: null]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$redis->connect($setting->host, (int)$setting->port, 1, null, 0, 0);
|
||||
$redis->auth(['pass' => $setting->password ?: null]);
|
||||
}
|
||||
|
||||
if(!$redis->ping()) throw new Exception('Can not connect to Redis server.');
|
||||
|
||||
$databases = $redis->config('GET', 'databases');
|
||||
if($setting->database >= $databases['databases']) throw new Exception("The database number is out of range. Your Redis server's max database number is " . $databases['databases'] - 1 . '.');
|
||||
|
||||
return $redis;
|
||||
}
|
||||
catch(RedisException $e)
|
||||
{
|
||||
throw new Exception('Can not connect to Redis server. The error message is: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,15 +20,6 @@
|
||||
include __DIR__ . '/base/model.class.php';
|
||||
class model extends baseModel
|
||||
{
|
||||
public $redis = false;
|
||||
|
||||
public function __construct($appName = '')
|
||||
{
|
||||
parent::__construct($appName);
|
||||
|
||||
if($this->config->redis) $this->redis = $this->app->redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业版部分功能是从然之合并过来的。ZDOO代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。
|
||||
* 调用父类的loadModel方法来避免这个错误。
|
||||
@@ -99,7 +90,7 @@ class model extends baseModel
|
||||
$table = zget($this->config->objectTables, $moduleName, '');
|
||||
if(empty($table)) return false;
|
||||
|
||||
return $this->dao->findById($objectID)->from($table)->fetch();
|
||||
return $this->mao->findById($objectID)->from($table)->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,7 +413,7 @@ class model extends baseModel
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
public function __call(string $method, array $arguments)
|
||||
{
|
||||
$moduleName = $this->getModuleName();
|
||||
$taoClass = $moduleName . 'Tao';
|
||||
|
||||
+59
-42
@@ -89,6 +89,15 @@ class baseDAO
|
||||
*/
|
||||
public $slaveDBH;
|
||||
|
||||
/**
|
||||
* 全局对象$cache。
|
||||
* The global cache object.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $cache = null;
|
||||
|
||||
/**
|
||||
* sql对象,用于生成sql语句。
|
||||
* The sql object, used to create the query sql.
|
||||
@@ -211,24 +220,12 @@ class baseDAO
|
||||
public function __construct($app)
|
||||
{
|
||||
global $config, $lang, $dbh, $slaveDBH;
|
||||
$this->app = $app;
|
||||
$this->config = $config;
|
||||
$this->lang = $lang;
|
||||
$this->dbh = $dbh;
|
||||
$this->slaveDBH = $slaveDBH ? $slaveDBH : false;
|
||||
|
||||
if($config->cache->dao->enable)
|
||||
{
|
||||
$this->app->loadClass('cache', true);
|
||||
try
|
||||
{
|
||||
$this->cache = new cache($config->cache->dao->driver, $config->db->name . '-dao-', $config->cache->dao->lifetime);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
die($e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->app = $app;
|
||||
$this->config = $config;
|
||||
$this->lang = $lang;
|
||||
$this->dbh = $dbh;
|
||||
$this->cache = $app->cache;
|
||||
$this->slaveDBH = $slaveDBH ? $slaveDBH : false;
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
@@ -331,6 +328,21 @@ class baseDAO
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缓存的 key。
|
||||
* Create the cache key.
|
||||
*
|
||||
* @param mixed $args
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function createCacheKey(...$args): string
|
||||
{
|
||||
if(empty($this->cache)) return implode('-', $args);
|
||||
|
||||
return $this->cache->createKey('dao', ...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存。
|
||||
* Get the cache.
|
||||
@@ -344,7 +356,7 @@ class baseDAO
|
||||
{
|
||||
if(!$this->app->isServing() || empty($this->cache)) return self::CACHE_MISS;
|
||||
|
||||
$cache = $this->cache->get($key);
|
||||
$cache = $this->cache->getByKey($key);
|
||||
if($cache === null) return self::CACHE_MISS;
|
||||
|
||||
/* 解析缓存的更新时间和值到变量中。 */
|
||||
@@ -362,7 +374,8 @@ class baseDAO
|
||||
{
|
||||
if(strpos($table, 'boardlayer') !== false) return self::CACHE_MISS;
|
||||
|
||||
$tableCache = $this->cache->get($table);
|
||||
$tableKey = $this->createCacheKey('table', $table);
|
||||
$tableCache = $this->cache->getByKey($tableKey);
|
||||
if($tableCache === null) continue;
|
||||
|
||||
if($tableCache[0] > $cachedTime) return self::CACHE_MISS;
|
||||
@@ -381,16 +394,17 @@ class baseDAO
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $ttl
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setCache($key, $value = null)
|
||||
public function setCache($key, $value = null, int $ttl = null)
|
||||
{
|
||||
if(!$this->app->isServing() || empty($this->cache)) return false;
|
||||
|
||||
$this->app->useClientCache = false;
|
||||
|
||||
$this->cache->set($key, array(microtime(true), $value));
|
||||
$this->cache->saveByKey($key, array(microtime(true), $value), $ttl ?? $this->config->cache->dao->lifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,7 +427,8 @@ class baseDAO
|
||||
/* 更新表的缓存时间。*/
|
||||
/* Update the table cache time. */
|
||||
$table = str_replace(array('`', '"'), '', $table);
|
||||
$this->setCache($table);
|
||||
$key = $this->createCacheKey('table', $table);
|
||||
$this->setCache($key, $table, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1047,26 +1062,33 @@ class baseDAO
|
||||
/* Real-time save log. */
|
||||
if(dao::$realTimeLog && dao::$realTimeFile) file_put_contents(dao::$realTimeFile, $sql . "\n", FILE_APPEND);
|
||||
|
||||
$table = trim($this->table, '`');
|
||||
$table = $this->table;
|
||||
$method = $this->method;
|
||||
$this->reset();
|
||||
|
||||
/* Force to query from master db, if db has been changed. */
|
||||
$this->slaveDBH = false;
|
||||
|
||||
if($this->cache) $this->cache->prepare($table, $method, $sql);
|
||||
|
||||
$result = $this->dbh->exec($sql);
|
||||
|
||||
/* See: https://www.php.net/manual/en/pdo.lastinsertid.php .*/
|
||||
$this->_lastInsertID = $this->dbh->lastInsertId();
|
||||
if($method == 'insert') $this->_lastInsertID = $this->dbh->lastInsertID();
|
||||
|
||||
if($this->cache) $result ? $this->cache->sync() : $this->cache->reset();
|
||||
|
||||
$this->setTableCache($sql);
|
||||
|
||||
if($this->config->enableDuckdb)
|
||||
{
|
||||
$now = helper::now();
|
||||
$queueTable = trim(TABLE_DUCKDBQUEUE, '`');
|
||||
$queueTable = TABLE_DUCKDBQUEUE;
|
||||
if(!empty($table) && $table != $queueTable)
|
||||
{
|
||||
$this->dbh->exec("UPDATE {$queueTable} SET updatedTime = '$now' WHERE object = '$table'");
|
||||
$this->dbh->exec("INSERT INTO {$queueTable} (object, updatedTime, syncTime) SELECT '$table', '$now', NULL WHERE NOT EXISTS (SELECT 1 FROM {$queueTable} WHERE object = '$table' );");
|
||||
$now = helper::now();
|
||||
$object = trim($table, '`');
|
||||
$this->dbh->exec("UPDATE {$queueTable} SET updatedTime = '$now' WHERE object = '$object'");
|
||||
$this->dbh->exec("INSERT INTO {$queueTable} (`object`, `updatedTime`, `syncTime`) SELECT '$object', '$now', NULL WHERE NOT EXISTS (SELECT 1 FROM {$queueTable} WHERE `object` = '$object' );");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1091,9 +1113,8 @@ class baseDAO
|
||||
*/
|
||||
public function fetch($field = '')
|
||||
{
|
||||
$sql = $this->processSQL();
|
||||
$key = 'fetch-' . md5($sql);
|
||||
|
||||
$sql = $this->processSQL();
|
||||
$key = $this->createCacheKey('fetch', md5($sql));
|
||||
$result = $this->getCache($key, $sql);
|
||||
if($result === self::CACHE_MISS)
|
||||
{
|
||||
@@ -1117,9 +1138,8 @@ class baseDAO
|
||||
*/
|
||||
public function fetchAll($keyField = '')
|
||||
{
|
||||
$sql = $this->processSQL();
|
||||
$key = 'fetchAll-' . md5($sql);
|
||||
|
||||
$sql = $this->processSQL();
|
||||
$key = $this->createCacheKey('fetchAll', md5($sql));
|
||||
$rows = $this->getCache($key, $sql);
|
||||
if($rows === self::CACHE_MISS)
|
||||
{
|
||||
@@ -1145,10 +1165,8 @@ class baseDAO
|
||||
*/
|
||||
public function fetchGroup($groupField, $keyField = '')
|
||||
{
|
||||
$sql = $this->processSQL();
|
||||
$table = $this->table;
|
||||
$key = 'fetchGroup-' . md5($sql);
|
||||
|
||||
$sql = $this->processSQL();
|
||||
$key = $this->createCacheKey('fetchAll', md5($sql));
|
||||
$rows = $this->getCache($key, $sql);
|
||||
if($rows === self::CACHE_MISS)
|
||||
{
|
||||
@@ -1178,9 +1196,8 @@ class baseDAO
|
||||
*/
|
||||
public function fetchPairs($keyField = '', $valueField = '')
|
||||
{
|
||||
$sql = $this->processSQL();
|
||||
$key = 'fetchPairs-' . md5($sql);
|
||||
|
||||
$sql = $this->processSQL();
|
||||
$key = $this->createCacheKey('fetchAll', md5($sql));
|
||||
$rows = $this->getCache($key, $sql);
|
||||
if($rows === self::CACHE_MISS)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,772 @@
|
||||
<?php
|
||||
/**
|
||||
* ZenTaoPHP的mao类。
|
||||
* The mao class file of ZenTaoPHP framework.
|
||||
*
|
||||
* The author disclaims copyright to this source code. In place of
|
||||
* a legal notice, here is a blessing:
|
||||
*
|
||||
* May you do good and not evil.
|
||||
* May you find forgiveness for yourself and forgive others.
|
||||
* May you share freely, never taking more than you give.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mao类。
|
||||
* Mao class.
|
||||
*
|
||||
* @package framework
|
||||
*/
|
||||
class baseMao
|
||||
{
|
||||
/**
|
||||
* 全局对象$app
|
||||
* The global app object.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $app;
|
||||
|
||||
/**
|
||||
* 全局对象$config
|
||||
* The global config object.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $config;
|
||||
|
||||
/**
|
||||
* 全局对象$dao
|
||||
* The global dao object.
|
||||
*
|
||||
* @var object
|
||||
* @access protected
|
||||
*/
|
||||
protected $dao;
|
||||
|
||||
/**
|
||||
* 全局对象$cache
|
||||
* The global cache object.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $cache = null;
|
||||
|
||||
/**
|
||||
* 正在使用的表。
|
||||
* The table of current query.
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* 查询的字段。
|
||||
* The fields will be returned.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $fields;
|
||||
|
||||
/**
|
||||
* 查询条件。
|
||||
* Conditions.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $conditions;
|
||||
|
||||
/**
|
||||
* 正在组装的查询条件。
|
||||
* Condition.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $condition;
|
||||
|
||||
/**
|
||||
* 是否在判断条件成立。
|
||||
* Checking condition.
|
||||
*
|
||||
* @var bool
|
||||
* @access public
|
||||
*/
|
||||
public $isConditionChecking;
|
||||
|
||||
/**
|
||||
* 条件是否成立。
|
||||
* Condition is true.
|
||||
*
|
||||
* @var bool
|
||||
* @access public
|
||||
*/
|
||||
public $conditionIsTrue;
|
||||
|
||||
/**
|
||||
* 待处理的数据。
|
||||
* The data to be processed.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* 待处理数据的column。
|
||||
* The column of data.
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $dataColumn;
|
||||
|
||||
/**
|
||||
* 构造方法。
|
||||
* The construct method.
|
||||
*
|
||||
* @access public
|
||||
* @param object $app
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(object $app)
|
||||
{
|
||||
global $config;
|
||||
$this->app = $app;
|
||||
$this->config = $config;
|
||||
$this->dao = $app->dao;
|
||||
$this->cache = $app->cache;
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置$table属性。
|
||||
* Set the $table property.
|
||||
*
|
||||
* @param string $table
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setTable(string $table)
|
||||
{
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置$fields属性。
|
||||
* Set the $fields property.
|
||||
*
|
||||
* @param array $fields
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setFields(array $fields)
|
||||
{
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置条件。
|
||||
* Set the $conditions property.
|
||||
*
|
||||
* @param array $conditions
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setConditions(array $conditions)
|
||||
{
|
||||
$this->conditions = $conditions;
|
||||
$this->resetCondition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置组装条件。
|
||||
* Reset the $condition property.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function resetCondition()
|
||||
{
|
||||
$this->condition = array('field' => '', 'operator' => '', 'value' => null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加条件。
|
||||
* Add the $conditions property.
|
||||
*
|
||||
* @param string $condition
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function addCondition()
|
||||
{
|
||||
$this->conditions[] = $this->condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置属性。
|
||||
* Reset the vars.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->setFields(array());
|
||||
$this->setTable('');
|
||||
$this->setConditions([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* select方法,调用sql::select()。
|
||||
* The select method, call sql::select().
|
||||
*
|
||||
* @param string $fields
|
||||
* @access public
|
||||
* @return static|sql|baseDAO the dao object self.
|
||||
*/
|
||||
public function select(string $fields = '*')
|
||||
{
|
||||
$this->conditions = [];
|
||||
|
||||
$fields = explode(',', $fields);
|
||||
|
||||
$alias = [];
|
||||
foreach($fields as $field)
|
||||
{
|
||||
$fieldInfo = explode(' ', trim($field));
|
||||
if(count($fieldInfo) == 1)
|
||||
{
|
||||
$alias[$fieldInfo[0]] = $fieldInfo[0];
|
||||
}
|
||||
elseif(count($fieldInfo) == 2)
|
||||
{
|
||||
$alias[$fieldInfo[0]] = $fieldInfo[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$alias[$fieldInfo[0]] = $fieldInfo[2];
|
||||
}
|
||||
}
|
||||
|
||||
$this->setFields($alias);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置要操作的表。
|
||||
* Set the from table.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @access public
|
||||
* @return static|sql the dao object self.
|
||||
*/
|
||||
public function from(string $tableName)
|
||||
{
|
||||
$this->setTable($tableName);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始条件判断。
|
||||
* Begin condition judge.
|
||||
*
|
||||
* @param bool|string $condition
|
||||
* @access public
|
||||
* @return static|sql the sql object.
|
||||
*/
|
||||
public function beginIF(bool|string $conditionResult)
|
||||
{
|
||||
$this->isConditionChecking = true;
|
||||
$this->conditionIsTrue = (bool)$conditionResult;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束条件判断。
|
||||
* End the condition judge.
|
||||
*
|
||||
* @access public
|
||||
* @return static|sql the sql object.
|
||||
*/
|
||||
public function fi()
|
||||
{
|
||||
$this->isConditionChecking = false;
|
||||
|
||||
if(!$this->conditionIsTrue)
|
||||
{
|
||||
$this->resetCondition();
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->addCondition();
|
||||
$this->resetCondition();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建WHERE部分。
|
||||
* Create the where part.
|
||||
*
|
||||
* @param string $field the field name
|
||||
* @access public
|
||||
* @return static the dao object.
|
||||
*/
|
||||
public function where(string $field)
|
||||
{
|
||||
$this->resetCondition();
|
||||
$this->condition['field'] = trim($field, '`');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建andWHERE部分。
|
||||
* Create the andWhere part.
|
||||
*
|
||||
* @param string $field the field name
|
||||
* @access public
|
||||
* @return static the dao object.
|
||||
*/
|
||||
public function andWhere(string $field)
|
||||
{
|
||||
$this->resetCondition();
|
||||
$this->condition['field'] = trim($field, '`');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建eq部分。
|
||||
* Create the eq part.
|
||||
*
|
||||
* @param string $value
|
||||
* @access public
|
||||
* @return static the dao object.
|
||||
*/
|
||||
public function eq($value)
|
||||
{
|
||||
$this->condition['operator'] = 'eq';
|
||||
$this->condition['value'] = $value;
|
||||
|
||||
if(!$this->isConditionChecking)
|
||||
{
|
||||
$this->addCondition();
|
||||
$this->resetCondition();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建ne部分。
|
||||
* Create the ne part.
|
||||
*
|
||||
* @param string $value
|
||||
* @access public
|
||||
* @return static the dao object.
|
||||
*/
|
||||
public function ne($value)
|
||||
{
|
||||
$this->condition['operator'] = 'ne';
|
||||
$this->condition['value'] = $value;
|
||||
|
||||
if(!$this->isConditionChecking)
|
||||
{
|
||||
$this->addCondition();
|
||||
$this->resetCondition();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建in部分。
|
||||
* Create the in part.
|
||||
*
|
||||
* @param string|array $value
|
||||
* @access public
|
||||
* @return static the dao object.
|
||||
*/
|
||||
public function in(string|array $value)
|
||||
{
|
||||
$this->condition['operator'] = 'in';
|
||||
$this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value;
|
||||
|
||||
if(!$this->isConditionChecking)
|
||||
{
|
||||
$this->addCondition();
|
||||
$this->resetCondition();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建notin部分。
|
||||
* Create the in part.
|
||||
*
|
||||
* @param string|array $value
|
||||
* @access public
|
||||
* @return static the dao object.
|
||||
*/
|
||||
public function notin(string|array $value)
|
||||
{
|
||||
$this->condition['operator'] = 'notin';
|
||||
$this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value;
|
||||
|
||||
if(!$this->isConditionChecking)
|
||||
{
|
||||
$this->addCondition();
|
||||
$this->resetCondition();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建ORDER BY部分。
|
||||
* Create the order by part.
|
||||
*
|
||||
* @param string $order
|
||||
* @access public
|
||||
* @return static|sql the sql object.
|
||||
*/
|
||||
public function orderBy(string $order)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建LIMIT部分。
|
||||
* Create the limit part.
|
||||
*
|
||||
* @param int $limit
|
||||
* @access public
|
||||
* @return static|sql the sql object.
|
||||
*/
|
||||
public function limit(int $limit)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断匹配条件。
|
||||
* Check condition is matched.
|
||||
*
|
||||
* @param object $object
|
||||
* @param array $conditions
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
private function isConditionMatched(object $object, array $conditions)
|
||||
{
|
||||
foreach($conditions as $condition)
|
||||
{
|
||||
$value = $object->{$condition['field']};
|
||||
if($condition['operator'] == 'eq')
|
||||
{
|
||||
if($condition['value'] != $value) return false;
|
||||
}
|
||||
elseif($condition['operator'] == 'ne')
|
||||
{
|
||||
if($condition['value'] == $value) return false;
|
||||
}
|
||||
elseif($condition['operator'] == 'in')
|
||||
{
|
||||
if(!in_array($value, $condition['value'])) return false;
|
||||
}
|
||||
elseif($condition['operator'] == 'notin')
|
||||
{
|
||||
if(in_array($value, $condition['value'])) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个记录。
|
||||
* Fetch one record.
|
||||
*
|
||||
* @param string $keyField 如果已经设置获取的字段,则只返回这个字段的值,否则返回这个记录。
|
||||
* if the field is set, only return the value of this field, else return this record
|
||||
* @access public
|
||||
* @return object|mixed
|
||||
*/
|
||||
public function fetch(string $keyField = '')
|
||||
{
|
||||
if(empty($this->cache) || empty($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetch', $keyField);
|
||||
|
||||
$rawResult = [];
|
||||
|
||||
/* 如果条件中有主键字段,则尝试通过主键字段从缓存中获取。If the primary key field is in the condition, try to get from the cache by the primary key field. */
|
||||
$field = $this->config->cache->raw[$this->table];
|
||||
foreach($this->conditions as $condition)
|
||||
{
|
||||
if($condition['field'] == $field && $condition['operator'] == 'eq')
|
||||
{
|
||||
$rawResult[] = $this->cache->fetch($this->table, $condition['value']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($rawResult)) $rawResult = $this->cache->fetchAll($this->table);
|
||||
if(empty($rawResult)) return $this->fetchFromDB('fetch', $keyField);
|
||||
|
||||
foreach($rawResult as $row)
|
||||
{
|
||||
if(!$this->isConditionMatched($row, $this->conditions)) continue;
|
||||
|
||||
if(!$keyField) return $row;
|
||||
|
||||
$keyField = trim($keyField, '`');
|
||||
return $row->$keyField;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有记录。
|
||||
* Fetch all records.
|
||||
*
|
||||
* @param string $keyField 返回以该字段做键的记录
|
||||
* the key field, thus the return records is keyed by this field
|
||||
* @access public
|
||||
* @return array the records
|
||||
*/
|
||||
public function fetchAll(string $keyField = ''): array
|
||||
{
|
||||
if(empty($this->cache) || empty($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchAll', $keyField);
|
||||
|
||||
$rawResult = [];
|
||||
|
||||
/* 如果条件中有主键字段,则尝试通过主键字段从缓存中获取。If the primary key field is in the condition, try to get from the cache by the primary key field. */
|
||||
$field = $this->config->cache->raw[$this->table];
|
||||
foreach($this->conditions as $condition)
|
||||
{
|
||||
if($condition['field'] == $field && $condition['operator'] == 'in')
|
||||
{
|
||||
$value = $condition['value'];
|
||||
if(is_numeric($value)) $value = [$value];
|
||||
if(is_string($value)) $value = explode(',', str_replace(' ', '', $value));
|
||||
if(is_array($value))
|
||||
{
|
||||
$rawResult = $this->cache->fetchAll($this->table, $value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($rawResult)) $rawResult = $this->cache->fetchAll($this->table);
|
||||
if(empty($rawResult)) return $this->fetchFromDB('fetchAll', $keyField);
|
||||
|
||||
$result = [];
|
||||
foreach($rawResult as $row)
|
||||
{
|
||||
if(!$this->isConditionMatched($row, $this->conditions)) continue;
|
||||
|
||||
$data = new stdclass();
|
||||
foreach($this->fields as $field => $alias)
|
||||
{
|
||||
if($field == '*')
|
||||
{
|
||||
$data = $row;
|
||||
break;
|
||||
}
|
||||
$data->$alias = $row->$field;
|
||||
}
|
||||
|
||||
if($keyField)
|
||||
{
|
||||
$result[$row->$keyField] = $data;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result[] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取的记录是以关联数组的形式
|
||||
* Fetch array like key=>value.
|
||||
*
|
||||
* 如果没有设置参数,用首末两键作为参数。
|
||||
* If the keyFiled and valueField not set, use the first and last in the record.
|
||||
*
|
||||
* @param string $keyField
|
||||
* @param string $valueField
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function fetchPairs(string $keyField = '', string $valueField = '')
|
||||
{
|
||||
if(empty($this->cache) || empty($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchPairs', $keyField, $valueField);
|
||||
|
||||
$rows = $this->fetchAll();
|
||||
if(empty($rows)) return [];
|
||||
|
||||
if(empty($keyField)) $keyField = $this->fields[0];
|
||||
if(empty($valueField)) $valueField = $this->fields[1];
|
||||
|
||||
$pairs = [];
|
||||
foreach($rows as $row)
|
||||
{
|
||||
$pairs[$row->$keyField] = $row->$valueField;
|
||||
}
|
||||
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库中获取数据。
|
||||
* Fetch data from database.
|
||||
*
|
||||
* @param string $fetchFunc fetch|fetchAll
|
||||
* @param string $keyField
|
||||
* @param string $valueField
|
||||
* @access private
|
||||
* @return mixed
|
||||
*/
|
||||
private function fetchFromDB(string $fetchFunc, string $keyField, string $valueField = '')
|
||||
{
|
||||
$fields = implode(',', $this->fields);
|
||||
$this->dao->select($fields)->from($this->table)->where('1=1');
|
||||
|
||||
foreach($this->conditions as $condition)
|
||||
{
|
||||
$func = $condition['operator'];
|
||||
$this->dao->andWhere($condition['field'])->$func($condition['value']);
|
||||
}
|
||||
|
||||
return $this->dao->$fetchFunc($keyField, $valueField);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把名为 findByXXX 的方法转换为 where 条件。
|
||||
* Convert the method findByXXX to where condition.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @access private
|
||||
* @return object the mao object.
|
||||
*/
|
||||
private function findBy(string $method, array $args)
|
||||
{
|
||||
$field = str_replace('findby', '', $method);
|
||||
if(count($args) == 1)
|
||||
{
|
||||
$operator = 'eq';
|
||||
$value = $args[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$operator = $args[0];
|
||||
$value = $args[1];
|
||||
}
|
||||
|
||||
$this->setFields(['*']);
|
||||
$this->conditions = [['field' => $field, 'operator' => $operator, 'value' => $value]];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存数据拼接到已有数据。
|
||||
* Append cache fields to data.
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $keyField
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function into(array $data, $keyField)
|
||||
{
|
||||
if(empty($data) || empty($keyField)) return $data;
|
||||
|
||||
/* Get data keys as conditions. */
|
||||
$keyList = [];
|
||||
foreach($data as $index => $row) $keyList[$index] = $row->$keyField;
|
||||
|
||||
if(empty($this->cache))
|
||||
{
|
||||
/* 如果缓存关闭,从数据库中获取。If the cache is off, get from the database. */
|
||||
$fields = [];
|
||||
foreach($this->fields as $field => $alias) $fields[] = $field == $alias ? $field : "$field AS $alias";
|
||||
$fields = implode(',', $fields);
|
||||
|
||||
$primaryKey = $this->config->cache->raw[$this->table];
|
||||
$cacheResult = $this->dao->select($fields)->from($this->table)->where($primaryKey)->in(array_unique($keyList))->fetchAll($primaryKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
$cacheResult = $this->cache->fetchAll($this->table, array_unique($keyList));
|
||||
}
|
||||
|
||||
foreach($data as $index => $row)
|
||||
{
|
||||
$key = $keyList[$index];
|
||||
$cacheRow = $cacheResult[$key];
|
||||
|
||||
foreach($this->fields as $field => $alias) $row->$alias = $cacheRow->$field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存。
|
||||
* Clear cache.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function clearCache()
|
||||
{
|
||||
if(!empty($this->cache)) $this->cache->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 魔术方法。
|
||||
* 1. 转换 findByxxx 为 where 条件。
|
||||
* 2. 调用cache对象的方法。
|
||||
* Magic method.
|
||||
* 1. Convert findByxxx to where condition.
|
||||
* 2. Call the cache object method.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
$method = strtolower($method);
|
||||
|
||||
/*
|
||||
* 如果是findByxxx,转换为where条件语句。
|
||||
* findByxxx, xxx as will be in the where.
|
||||
**/
|
||||
if(strpos($method, 'findby') !== false) return $this->findBy($method, $args);
|
||||
|
||||
if(empty($this->cache)) return false;
|
||||
|
||||
if(method_exists($this->cache, $method)) return call_user_func_array([$this->cache, $method], $args);
|
||||
|
||||
$this->app->triggerError("Method $method not found in class baseMao.", __FILE__, __LINE__, $this->config->debug >= 2);
|
||||
}
|
||||
}
|
||||
Vendored
+905
-42
@@ -2,20 +2,21 @@
|
||||
/**
|
||||
* The cache library of zentaopms.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Lu Fei <lufei@easycorp.ltd>
|
||||
* @author Gang Liu <liugang@chandao.com>
|
||||
* @package cache
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace Zentao\Cache;
|
||||
|
||||
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 . 'RedisDriver.php');
|
||||
helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'YacDriver.php');
|
||||
helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'FileDriver.php');
|
||||
\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 . 'RedisDriver.php');
|
||||
\helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'YacDriver.php');
|
||||
\helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'FileDriver.php');
|
||||
|
||||
use ZenTao\Cache\SimpleCache\InvalidArgumentException;
|
||||
|
||||
@@ -29,56 +30,918 @@ class cache
|
||||
|
||||
const DRIVER_REDIS = 'Redis';
|
||||
|
||||
const DRIVER_LIST = [self::DRIVER_APCU, self::DRIVER_FILE, self::DRIVER_YAC, self::DRIVER_REDIS];
|
||||
|
||||
/**
|
||||
* @var ZenTao\Cache\SimpleCache\CacheInterface
|
||||
* 全局应用程序对象。
|
||||
* Global application object.
|
||||
*
|
||||
* @access private
|
||||
* @var object
|
||||
*/
|
||||
protected $client;
|
||||
private $app;
|
||||
|
||||
public function __construct($driver = 'File', $namespace = '', $defaultLifetime = 0)
|
||||
/**
|
||||
* 全局数据库操作对象。
|
||||
* Global database operation object.
|
||||
*
|
||||
* @access private
|
||||
* @var object
|
||||
*/
|
||||
private $dao;
|
||||
|
||||
/**
|
||||
* 全局配置对象。
|
||||
* Global configuration object.
|
||||
*
|
||||
* @access private
|
||||
* @var object
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* 全局缓存对象。
|
||||
* Global cache object.
|
||||
*
|
||||
* @access private
|
||||
* @var object
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* 缓存命名空间。
|
||||
* Cache namespace.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* 缓存键连接符。
|
||||
* Cache key connector.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $connector;
|
||||
|
||||
/**
|
||||
* 缓存键。
|
||||
* Cache key.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $key = '';
|
||||
|
||||
/**
|
||||
* 缓存标签。
|
||||
* Cache label.
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $labels = [];
|
||||
|
||||
/**
|
||||
* 影响缓存的表名。
|
||||
* Table name that affects cache.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $table = '';
|
||||
|
||||
/**
|
||||
* 影响缓存的事件。
|
||||
* Event that affects cache.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $event = '';
|
||||
|
||||
/**
|
||||
* 影响缓存的 WHERE 子句。
|
||||
* WHERE clause that affects cache.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $where = '';
|
||||
|
||||
/**
|
||||
* 受影响的对象列表。
|
||||
* Affected object list.
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $objects = [];
|
||||
|
||||
/**
|
||||
* 构造函数,根据配置文件初始化缓存对象。
|
||||
* Constructor, initialize cache object according to the configuration file.
|
||||
*
|
||||
* @param object $app 全局应用程序对象。
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(object $app)
|
||||
{
|
||||
$driver = ucfirst(strtolower($driver));
|
||||
switch($driver)
|
||||
{
|
||||
case self::DRIVER_APCU:
|
||||
$className = 'ZenTao\Cache\Driver\ApcuDriver';
|
||||
break;
|
||||
case self::DRIVER_YAC:
|
||||
$className = 'ZenTao\Cache\Driver\YacDriver';
|
||||
break;
|
||||
case self::DRIVER_FILE:
|
||||
$className = 'ZenTao\Cache\Driver\FileDriver';
|
||||
break;
|
||||
case self::DRIVER_REDIS:
|
||||
$className = 'ZenTao\Cache\Driver\RedisDriver';
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException("Driver {$driver} is not supported.");
|
||||
}
|
||||
$this->app = $app;
|
||||
$this->dao = $app->dao;
|
||||
$this->config = $app->config;
|
||||
|
||||
if($driver != self::DRIVER_FILE && !extension_loaded($driver)) throw new InvalidArgumentException("Driver ext-{$driver} is not loaded.");
|
||||
if(empty($this->config->cache->enable)) return $this->log('The cache is not enabled', __FILE__, __LINE__);
|
||||
|
||||
global $app;
|
||||
$this->client = new $className($namespace, $defaultLifetime, $app->getCacheRoot());
|
||||
$driver = ucfirst(strtolower($this->config->cache->driver));
|
||||
|
||||
if(!in_array($driver, self::DRIVER_LIST)) return $this->log("Driver {$driver} is not supported.", __FILE__, __LINE__);
|
||||
if($driver != self::DRIVER_FILE && !extension_loaded($driver)) return $this->log("Driver ext-{$driver} is not loaded.", __FILE__, __LINE__);
|
||||
|
||||
$connector = $driver == self::DRIVER_REDIS ? ':' : '-';
|
||||
$className = "ZenTao\Cache\Driver\\{$driver}Driver";
|
||||
$scope = $this->config->cache->scope;
|
||||
$namespace = $this->config->cache->namespace;
|
||||
$lifetime = $this->config->cache->lifetime;
|
||||
$redis = $this->config->redis;
|
||||
|
||||
$this->setNamespace($namespace);
|
||||
$this->setConnector($connector);
|
||||
|
||||
if($driver == self::DRIVER_APCU) return $this->cache = new $className($namespace, $lifetime, $scope, $connector);
|
||||
if($driver == self::DRIVER_REDIS) return $this->cache = new $className($namespace, $lifetime, $scope, $connector, $redis);
|
||||
if($driver == self::DRIVER_YAC) return $this->cache = new $className($namespace, $lifetime);
|
||||
if($driver == self::DRIVER_FILE) return $this->cache = new $className($namespace, $lifetime, $app->getCacheRoot());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a cache instance.
|
||||
* 设置缓存命名空间。
|
||||
* Set cache namespace.
|
||||
*
|
||||
* @param string $driver
|
||||
* @param string $namespace
|
||||
* @param int $defaultLifetime
|
||||
* @param string $namespace 缓存命名空间。
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function setNamespace(string $namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存键连接符。
|
||||
* Set cache key connector.
|
||||
*
|
||||
* @param string $connector 缓存键连接符。
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function setConnector(string $connector)
|
||||
{
|
||||
$this->connector = $connector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存键。
|
||||
* Set cache key.
|
||||
*
|
||||
* @param string $key 缓存键。
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function setKey(string $key)
|
||||
{
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置影响缓存的表名。
|
||||
* Set the table name that affects the cache.
|
||||
*
|
||||
* @param string $table 表名。
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function setTable(string $table)
|
||||
{
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置影响缓存的事件。
|
||||
* Set the event that affects the cache.
|
||||
*
|
||||
* @param string $event 事件。
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function setEvent(string $event)
|
||||
{
|
||||
$this->event = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置影响缓存的 WHERE 子句。
|
||||
* Set the WHERE clause that affects the cache.
|
||||
*
|
||||
* @param string $where WHERE 子句。
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function setWhere(string $where)
|
||||
{
|
||||
$this->where = $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置受影响的对象列表。
|
||||
* Set the list of affected objects.
|
||||
*
|
||||
* @param array $objects 对象列表。
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function setObjects(array $objects)
|
||||
{
|
||||
$this->objects = $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表的主键字段。
|
||||
* Get the primary key field of the table.
|
||||
*
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function getTableField(): string
|
||||
{
|
||||
return $this->config->cache->raw[$this->table];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表的缓存代号。
|
||||
* Get the cache code of the table.
|
||||
*
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function getTableCode(): string
|
||||
{
|
||||
return str_replace(['`', $this->config->db->prefix], '', $this->table);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始数据类型缓存的键。该缓存用于保存表的原始数据。
|
||||
* Get the key of the raw data type cache. This cache is used to save the original data of the table.
|
||||
*
|
||||
* @param string $code 缓存代号。
|
||||
* @param int|string $id 主键值。
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function getRawCacheKey(string $code, int|string $id): string
|
||||
{
|
||||
return $this->createKey('raw', $code, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取集合类型缓存的键。该缓存用于保存表的主键字段。
|
||||
* Get the key of the set type cache. This cache is used to save the primary key field of the table.
|
||||
*
|
||||
* @param string $code 缓存代号。
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function getSetCacheKey(string $code): string
|
||||
{
|
||||
return $this->createKey('set', $code, 'list');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取结果类型缓存的键。该缓存用于保存表的计算结果。
|
||||
* Get the key of the result type cache. This cache is used to save the calculation results of the table.
|
||||
*
|
||||
* @param string $key 缓存键。
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function getResCacheKey(string $key): string
|
||||
{
|
||||
$args = explode('_', str_replace('cache', 'res', strtolower($key)));
|
||||
return $this->createKey(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增数据时更新缓存。
|
||||
* Update cache when adding data.
|
||||
*
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function create()
|
||||
{
|
||||
$objectID = $this->dao->lastInsertID();
|
||||
if(!$objectID) return $this->log('Failed to fetch last insert id.', __FILE__, __LINE__);
|
||||
|
||||
$field = $this->getTableField();
|
||||
$code = $this->getTableCode();
|
||||
|
||||
/* 获取新增的数据。Get the new data. */
|
||||
$object = $this->dao->select('*')->from($this->table)->where('id')->eq($objectID)->fetch();
|
||||
if(!$object) return $this->log('Failed to fetch new object. The sql is: ' . $this->dao->get(), __FILE__, __LINE__);
|
||||
|
||||
/* 把新增的数据保存到缓存中。Save the new data to cache. */
|
||||
$rawCacheKey = $this->getRawCacheKey($code, $object->$field);
|
||||
$this->cache->set($rawCacheKey, $object);
|
||||
|
||||
/* 把新增的数据的 id 保存到缓存中。Save the id of the new data to cache. */
|
||||
$setCacheKey = $this->getSetCacheKey($code);
|
||||
$objectIdList = $this->cache->get($setCacheKey);
|
||||
$this->cache->set($setCacheKey, $objectIdList ? array_merge($objectIdList, [$object->$field]) : [$object->$field]);
|
||||
|
||||
if(empty($this->config->cache->res[$this->table])) return;
|
||||
|
||||
/* 删除受影响的缓存。Delete the affected cache. */
|
||||
$this->deleteAffectedCache([$object]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据时更新缓存。
|
||||
* Update cache when updating data.
|
||||
*
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function update()
|
||||
{
|
||||
if(empty($this->objects)) return $this->log('No objects to update.', __FILE__, __LINE__);
|
||||
|
||||
$field = $this->getTableField();
|
||||
$code = $this->getTableCode();
|
||||
|
||||
/* 获取被更新数据的 id 列表。Get the id list of the updated data. */
|
||||
$objectIdList = array_map(function($object) use ($field) { return $object->$field; }, $this->objects);
|
||||
|
||||
/* 获取更新后的数据。Get the updated data. */
|
||||
$objects = $this->dao->select('*')->from($this->table)->where($field)->in($objectIdList)->fetchAll($field);
|
||||
if(!$objects) return $this->log('Failed to fetch updated objects. The sql is: ' . $this->dao->get(), __FILE__, __LINE__);
|
||||
|
||||
/* 把更新后的数据保存到缓存中。Save the updated data to cache. */
|
||||
$values = [];
|
||||
foreach($objects as $object)
|
||||
{
|
||||
$rawCacheKey = $this->getRawCacheKey($code, $object->$field);
|
||||
$values[$rawCacheKey] = $object;
|
||||
}
|
||||
$this->cache->setMultiple($values);
|
||||
|
||||
if(empty($this->config->cache->res[$this->table])) return;
|
||||
|
||||
/* 获取受影响的数据。Get the affected data. */
|
||||
foreach($objectIdList as $objectID)
|
||||
{
|
||||
if(isset($this->objects[$objectID]) && isset($objects[$objectID]) && $this->objects[$objectID] == $objects[$objectID]) unset($this->objects[$objectID], $objects[$objectID]);
|
||||
}
|
||||
$affectedObjects = array_merge($this->objects, $objects);
|
||||
|
||||
/* 删除受影响的缓存。Delete the affected cache. */
|
||||
$this->deleteAffectedCache($affectedObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据时更新缓存。
|
||||
* Update cache when deleting data.
|
||||
*
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function delete()
|
||||
{
|
||||
if(empty($this->objects)) return $this->log('No objects to delete.', __FILE__, __LINE__);
|
||||
|
||||
$field = $this->getTableField();
|
||||
$code = $this->getTableCode();
|
||||
|
||||
/* 把被删除的数据从缓存中删除。Delete the deleted data from cache. */
|
||||
$affectedKeys = [];
|
||||
foreach($this->objects as $object) $affectedKeys[] = $this->getRawCacheKey($code, $object->$field);
|
||||
$this->cache->deleteMultiple($affectedKeys);
|
||||
|
||||
/* 把被删除的数据的 id 从缓存中删除。Delete the id of the deleted data from cache. */
|
||||
$setCacheKey = $this->getSetCacheKey($code);
|
||||
$objectIdList = $this->cache->get($setCacheKey);
|
||||
$this->cache->set($setCacheKey, array_diff($objectIdList, array_map(function($object) use ($field) { return $object->$field; }, $this->objects)));
|
||||
|
||||
if(empty($this->config->cache->res[$this->table])) return;
|
||||
|
||||
/* 删除受影响的缓存。Delete the affected cache. */
|
||||
$this->deleteAffectedCache($this->objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除受影响的缓存。
|
||||
* Delete the affected cache.
|
||||
*
|
||||
* @param array $objects 受影响的对象列表。
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function deleteAffectedCache(array $objects)
|
||||
{
|
||||
/* 根据受影响的数据查找受影响的缓存。Find the affected cache by the affected data. */
|
||||
$keys = [];
|
||||
foreach($this->config->cache->res[$this->table] as $res)
|
||||
{
|
||||
$res = (object)$res;
|
||||
|
||||
/* 如果没有设置关联字段则整个缓存都受影响。If no associated fields are set, the entire cache is affected. */
|
||||
if(empty($res->fields))
|
||||
{
|
||||
$keys = $this->getResCacheKey($res->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 根据关联字段查找受影响的缓存。Find the affected cache by the associated fields. */
|
||||
foreach($objects as $object)
|
||||
{
|
||||
$key = $this->getResCacheKey($res->name);
|
||||
foreach($res->fields as $field)
|
||||
{
|
||||
if(!isset($object->$field)) return $this->log("Field {$field} does not exist in table {$this->table}.", __FILE__, __LINE__);
|
||||
|
||||
$key .= $this->connector . $object->$field;
|
||||
}
|
||||
$keys[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
/* 删除受影响的缓存。Delete the affected cache. */
|
||||
$this->cache->deleteMultiple($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查表是否有缓存设置。
|
||||
* Check if the table has cache settings.
|
||||
*
|
||||
* @param string $table 表名。
|
||||
* @access private
|
||||
* @return bool
|
||||
*/
|
||||
private function checkTable(string $table = ''): bool
|
||||
{
|
||||
if(empty($table)) $table = $this->table;
|
||||
return !empty($this->config->cache->raw[$table]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录日志信息。
|
||||
* Record log information.
|
||||
*
|
||||
* @param string $message 日志信息。
|
||||
* @param string $file 文件名。
|
||||
* @param string $line 行号。
|
||||
* @access private
|
||||
* @return false
|
||||
*/
|
||||
private function log(string $message, string $file, string $line): bool
|
||||
{
|
||||
if(!$this->config->debug) return false;
|
||||
|
||||
$runMode = PHP_SAPI == 'cli' ? '_cli' : '';
|
||||
$logFile = $this->app->getLogRoot() . 'cache' . $runMode . '.' . date('Ymd') . '.log.php';
|
||||
if(!file_exists($logFile)) file_put_contents($logFile, '<?php die(); ?' . ">\n");
|
||||
|
||||
$content = date('Ymd H:i:s') . ': ' . $this->getURI() . "\nError: {$message} in $file on line $line\n";
|
||||
file_put_contents($logFile, $content, FILE_APPEND);
|
||||
|
||||
if($this->config->debug >= 2) $this->app->triggerError($message, __FILE__, __LINE__, true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 URI。
|
||||
* Get URI.
|
||||
*
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function getURI(): string
|
||||
{
|
||||
$uri = $this->app->getURI();
|
||||
if($uri) return $uri;
|
||||
|
||||
if($this->config->requestType == 'GET') return $_SERVER['REQUEST_URI'];
|
||||
|
||||
if($this->config->requestType == 'PATH_INFO' || $this->config->requestType == 'PATH_INFO2')
|
||||
{
|
||||
$pathInfo = $this->app->getPathInfo();
|
||||
if(empty($pathInfo)) return '';
|
||||
|
||||
$dotPos = strrpos($pathInfo, '.');
|
||||
if($dotPos) return substr($pathInfo, 0, $dotPos);
|
||||
return $pathInfo;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定表的缓存。
|
||||
* Initialize the cache of the specified table.
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function initTableCache(): array
|
||||
{
|
||||
$field = $this->getTableField();
|
||||
$objects = $this->dao->select('*')->from($this->table)->fetchAll($field);
|
||||
if(!$objects) return [];
|
||||
|
||||
$values = [];
|
||||
$code = $this->getTableCode();
|
||||
foreach($objects as $key => $object)
|
||||
{
|
||||
$rawCacheKey = $this->getRawCacheKey($code, $key);
|
||||
$values[$rawCacheKey] = $object;
|
||||
}
|
||||
|
||||
$this->cache->setMultiple($values);
|
||||
|
||||
$setCacheKey = $this->getSetCacheKey($code);
|
||||
$this->cache->set($setCacheKey, array_keys($objects));
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缓存键。
|
||||
* Generate cache key.
|
||||
*
|
||||
* @param mixed ...$args 缓存键的参数。
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function createKey(...$args): string
|
||||
{
|
||||
return $this->namespace . $this->connector . implode($this->connector, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存中获取指定表指定 id 的数据。
|
||||
* Get the data of the specified table and id from the cache.
|
||||
*
|
||||
* @param string $table 表名。
|
||||
* @param int|string $id 主键值。
|
||||
* @access public
|
||||
* @return object|false
|
||||
*/
|
||||
public function fetch(string $table, int|string $id): object|bool
|
||||
{
|
||||
if(!$this->checkTable($table)) return $this->log("Table {$table} is not set in the cache configuration", __FILE__, __LINE__);
|
||||
|
||||
if(empty($table)) return $this->log('The table name is empty', __FILE__, __LINE__);
|
||||
if(empty($id)) return $this->log('The id is empty', __FILE__, __LINE__);
|
||||
|
||||
$this->setTable($table);
|
||||
|
||||
$code = $this->getTableCode();
|
||||
$key = $this->getRawCacheKey($code, $id);
|
||||
$object = $this->cache->get($key);
|
||||
if($object) return $object;
|
||||
|
||||
$objects = $this->initTableCache();
|
||||
return isset($objects[$id]) ? $objects[$id] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存中获取指定表的所有数据。
|
||||
* Get all data of the specified table from the cache.
|
||||
*
|
||||
* @param string $table 表名。
|
||||
* @param array $objectIdList 主键值列表。
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll(string $table, array $objectIdList = []): array
|
||||
{
|
||||
if(!$this->checkTable($table)) return $this->log("Table {$table} is not set in the cache configuration", __FILE__, __LINE__);
|
||||
|
||||
if(empty($table)) return $this->log('The table name is empty', __FILE__, __LINE__);
|
||||
|
||||
$this->setTable($table);
|
||||
|
||||
$code = $this->getTableCode();
|
||||
|
||||
/* 尝试获取指定表的所有主键字段的值。Try to get the values of all primary key fields of the specified table. */
|
||||
$setCacheKey = $this->getSetCacheKey($code);
|
||||
$allObjectIdList = $this->cache->get($setCacheKey);
|
||||
|
||||
/* 如果主键字段的值为空,则初始化指定表的缓存。If the value of the primary key field is empty, initialize the cache of the specified table. */
|
||||
if(!$allObjectIdList)
|
||||
{
|
||||
$allData = $this->initTableCache();
|
||||
if(empty($objectIdList)) return $allData;
|
||||
|
||||
return array_intersect_key($allData, $objectIdList);
|
||||
}
|
||||
|
||||
/* 如果主键字段的值不为空,则从缓存中获取数据。If the value of the primary key field is not empty, get the data from the cache. */
|
||||
if(empty($objectIdList)) $objectIdList = $allObjectIdList;
|
||||
|
||||
$keys = [];
|
||||
foreach($objectIdList as $objectID) $keys[$objectID] = $this->getRawCacheKey($code, $objectID);
|
||||
|
||||
$objects = $this->cache->getMultiple(array_values($keys));
|
||||
|
||||
/* 如果缓存中没有全部的数据,则从数据库中获取缺失的数据。If not all data in cache, get the missing data from the database. */
|
||||
if(count($keys) > count($objects))
|
||||
{
|
||||
$lostObjects = [];
|
||||
$diffIdList = array_keys(array_diff($keys, array_keys($objects)));
|
||||
$diffObjects = $this->dao->select('*')->from($table)->where('id')->in($diffIdList)->fetchAll();
|
||||
foreach($diffObjects as $object)
|
||||
{
|
||||
$rawCacheKey = $this->getRawCacheKey($code, $object->id);
|
||||
$lostObjects[$rawCacheKey] = $object;
|
||||
}
|
||||
|
||||
/* 把缺失的数据保存到缓存中。Save the missing data to cache. */
|
||||
$this->cache->setMultiple($lostObjects);
|
||||
|
||||
$objects += $lostObjects;
|
||||
}
|
||||
|
||||
if(!$objects) return [];
|
||||
|
||||
$result = [];
|
||||
$field = $this->getTableField();
|
||||
foreach($objects as $object) $result[$object->$field] = $object;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存键。
|
||||
* Set cache key.
|
||||
*
|
||||
* @param string $key 缓存键。
|
||||
* @param mixed ...$args 缓存键的参数。
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public static function create($driver = 'File', $namespace = '', $defaultLifetime = 0)
|
||||
public function key($key, ...$args)
|
||||
{
|
||||
return new self($driver, $namespace, $defaultLifetime);
|
||||
if(empty($this->config->cache->keys[$key])) return $this->log("Key {$key} is not defined", __FILE__, __LINE__);
|
||||
|
||||
$cache = $this->config->cache->keys[$key];
|
||||
if(!empty($cache->fields) && !empty($args))
|
||||
{
|
||||
$tableFields = $this->dao->descTable($cache->table);
|
||||
foreach($cache->fields as $index => $field)
|
||||
{
|
||||
if(!isset($tableFields[$field])) return $this->log("Field {$field} does not exist in table {$cache->table}", __FILE__, __LINE__);
|
||||
if(!isset($args[$index])) continue;
|
||||
|
||||
$tableField = $tableFields[$field];
|
||||
if(stripos($tableField->type, 'int') !== false) $args[$index] = (int) $args[$index];
|
||||
if(stripos($tableField->type, 'float') !== false) $args[$index] = (float)$args[$index];
|
||||
if(stripos($tableField->type, 'decimal') !== false) $args[$index] = (float)$args[$index];
|
||||
if(stripos($tableField->type, 'double') !== false) $args[$index] = (float)$args[$index];
|
||||
}
|
||||
}
|
||||
|
||||
$key = $this->getResCacheKey(constant($key));
|
||||
foreach($args as $arg) $key .= $this->connector . $arg;
|
||||
|
||||
$this->setKey($key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
/**
|
||||
* 设置缓存标签。
|
||||
* Set cache label.
|
||||
*
|
||||
* @param string $label 缓存标签。
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function label(string $label)
|
||||
{
|
||||
if(!method_exists($this->client, $name)) throw new InvalidArgumentException("Method {$name} does not exist.");
|
||||
if(empty($label)) return $this->log('The label is empty', __FILE__, __LINE__);
|
||||
if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__);
|
||||
if(isset($this->labels[$label])) return $this->log("Label {$label} already used", __FILE__, __LINE__);
|
||||
$this->labels[$label] = $this->key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
return call_user_func_array(array($this->client, $name), $arguments);
|
||||
/**
|
||||
* 根据当前缓存键获取缓存。
|
||||
* Get cache according to the current cache key.
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__);
|
||||
return $this->cache->get($this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定缓存键获取缓存。
|
||||
* Get cache according to the specified cache key.
|
||||
*
|
||||
* @param string $key 缓存键。
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function getByKey(string $key)
|
||||
{
|
||||
if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__);
|
||||
|
||||
return $this->cache->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前缓存键保存缓存。
|
||||
* Save cache according to the current cache key.
|
||||
*
|
||||
* @param mixed $value 缓存值。
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function save($value)
|
||||
{
|
||||
if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__);
|
||||
return $this->cache->set($this->key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定缓存建保存缓存。
|
||||
* Save cache according to the specified cache key.
|
||||
*
|
||||
* @param string $key 缓存键。
|
||||
* @param mixed $value 缓存值。
|
||||
* @param int $ttl 缓存时间。
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function saveByKey(string $key, $value, int $ttl = 0)
|
||||
{
|
||||
if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__);
|
||||
|
||||
return $this->cache->set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定缓存标签保存缓存。
|
||||
* Save cache according to the specified cache label.
|
||||
*
|
||||
* @param string $label 缓存标签。
|
||||
* @param mixed $value 缓存值。
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function saveByLabel(string $label, $value)
|
||||
{
|
||||
if(empty($label)) return $this->log('The label is empty', __FILE__, __LINE__);
|
||||
if(empty($this->labels[$label])) return $this->log("Label {$label} does not exist", __FILE__, __LINE__);
|
||||
|
||||
return $this->cache->set($this->labels[$label], $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置缓存相关设置项。
|
||||
* Reset cache related settings.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->setKey('');
|
||||
$this->setTable('');
|
||||
$this->setEvent('');
|
||||
$this->setWhere('');
|
||||
$this->setObjects([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库操作前准备更新缓存需要的设置项。
|
||||
* Prepare the settings required to update the cache before executing the database operation.
|
||||
*
|
||||
* @param string $table 表名。
|
||||
* @param string $event 事件。
|
||||
* @param string $sql SQL 语句。
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function prepare(string $table, string $event, string $sql)
|
||||
{
|
||||
$this->reset();
|
||||
|
||||
if(!$this->checkTable($table)) return;
|
||||
|
||||
if(empty($table)) return $this->log('Table name is required.', __FILE__, __LINE__);
|
||||
if(empty($event)) return $this->log('Event type is required.', __FILE__, __LINE__);
|
||||
if(empty($sql)) return $this->log('SQL statement is required.', __FILE__, __LINE__);
|
||||
|
||||
$this->setTable($table);
|
||||
$this->setEvent($event);
|
||||
|
||||
if($event == 'update' || $event == 'delete')
|
||||
{
|
||||
/* 获取 WHERE 子句的内容。Get the content of WHERE clause. */
|
||||
$whereLen = strlen(\DAO::WHERE);
|
||||
$wherePOS = strrpos($sql, \DAO::WHERE);
|
||||
$groupPOS = strrpos($sql, \DAO::GROUPBY);
|
||||
$havingPOS = strrpos($sql, \DAO::HAVING);
|
||||
$orderPOS = strrpos($sql, \DAO::ORDERBY);
|
||||
$limitPOS = strrpos($sql, \DAO::LIMIT);
|
||||
$splitPOS = $orderPOS ? $orderPOS : $limitPOS;
|
||||
$splitPOS = $havingPOS ? $havingPOS : $splitPOS;
|
||||
$splitPOS = $groupPOS ? $groupPOS : $splitPOS;
|
||||
|
||||
$where = '';
|
||||
if($wherePOS)
|
||||
{
|
||||
if($splitPOS)
|
||||
{
|
||||
$where = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
$where = substr($sql, $wherePOS + $whereLen);
|
||||
}
|
||||
}
|
||||
|
||||
/* 执行操作后数据已经被修改,所以需要提前获取被影响的数据。*/
|
||||
$field = $this->getTableField();
|
||||
$objects = $this->dao->select('*')->from($table)->beginIF($where)->where($where)->fi()->fetchAll($field);
|
||||
|
||||
$this->setWhere($where);
|
||||
$this->setObjects($objects);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库操作后更新缓存。
|
||||
* Update the cache after executing the database operation.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function sync()
|
||||
{
|
||||
if(!$this->checkTable()) return;
|
||||
|
||||
if(empty($this->table)) return $this->log('Table name is required.', __FILE__, __LINE__);
|
||||
if(empty($this->event)) return $this->log('Event type is required.', __FILE__, __LINE__);
|
||||
|
||||
if($this->event == 'insert') $this->create();
|
||||
if($this->event == 'update') $this->update();
|
||||
if($this->event == 'delete') $this->delete();
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空缓存。
|
||||
* Clear cache.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
return $this->cache->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭缓存连接。
|
||||
* Close cache connection.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if(method_exists($this->cache, 'close')) $this->cache->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内存使用情况。
|
||||
* Get memory usage.
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public function memory(string $type)
|
||||
{
|
||||
return $this->cache->memory($type);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+59
-88
@@ -17,26 +17,51 @@ use ZenTao\Cache\SimpleCache\InvalidArgumentException;
|
||||
class ApcuDriver implements CacheInterface
|
||||
{
|
||||
/**
|
||||
* 缓存命名空间。
|
||||
* The cache namespace.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* 缓存默认生命周期。
|
||||
* The cache default lifetime.
|
||||
*
|
||||
* @access private
|
||||
* @var int
|
||||
*/
|
||||
private $defaultLifetime;
|
||||
|
||||
public function __construct($namespace = '', $defaultLifetime = 0)
|
||||
/**
|
||||
* 缓存服务范围。private 独享|public 共享。
|
||||
* The cache scope.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $scope;
|
||||
|
||||
/**
|
||||
* 缓存键连接符。
|
||||
* Cache key connector.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $connector;
|
||||
|
||||
public function __construct($namespace = '', $defaultLifetime = 0, $scope = '', $connector = '')
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->namespace = $namespace;
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
$this->scope = $scope;
|
||||
$this->connector = $connector;
|
||||
}
|
||||
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$this->assertKeyName($key);
|
||||
$key = $this->buildKeyName($key);
|
||||
|
||||
$value = apcu_fetch($key, $success);
|
||||
|
||||
return $success === false ? $default : $value;
|
||||
@@ -44,75 +69,51 @@ class ApcuDriver implements CacheInterface
|
||||
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
$this->assertKeyName($key);
|
||||
$key = $this->buildKeyName($key);
|
||||
$ttl = (int)($ttl ?: $this->defaultLifetime);
|
||||
|
||||
$ttl = is_null($ttl) ? $this->defaultLifetime : $ttl;
|
||||
|
||||
return apcu_store($key, $value, (int) $ttl);
|
||||
return apcu_store($key, $value, $ttl);
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
$this->assertKeyName($key);
|
||||
$key = $this->buildKeyName($key);
|
||||
|
||||
return apcu_delete($key);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
return apcu_clear_cache();
|
||||
if($this->scope == 'private') return apcu_clear_cache();
|
||||
|
||||
$keys = [];
|
||||
$info = apcu_cache_info();
|
||||
$cacheList = $info['cache_list'];
|
||||
foreach($cacheList as $cache)
|
||||
{
|
||||
if(strpos($cache['info'], $this->namespace . $this->connector) === 0) $keys[] = $cache['info'];
|
||||
}
|
||||
if(!$keys) return true;
|
||||
|
||||
return $this->deleteMultiple($keys);
|
||||
}
|
||||
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
$this->assertKeyNames($keys);
|
||||
$keys = $this->buildKeyNames($keys);
|
||||
$values = apcu_fetch($keys);
|
||||
if($values === false) return [];
|
||||
|
||||
$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;
|
||||
return $values;
|
||||
}
|
||||
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
$this->assertKeyNames(array_keys($values));
|
||||
$ttl = (int)($ttl ?: $this->defaultLifetime);
|
||||
|
||||
$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);
|
||||
$result = apcu_store($values, $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;
|
||||
@@ -120,54 +121,24 @@ class ApcuDriver implements CacheInterface
|
||||
|
||||
public function has($key)
|
||||
{
|
||||
$this->assertKeyName($key);
|
||||
$key = $this->buildKeyName($key);
|
||||
|
||||
return (bool) apcu_exists($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* 获取内存使用情况。
|
||||
* Get memory usage.
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
private function buildKeyName($key)
|
||||
public function memory($type)
|
||||
{
|
||||
return $this->namespace . $key;
|
||||
}
|
||||
$info = apcu_sma_info(true);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
if($type == 'total') return \helper::formatKB($info['seg_size']);
|
||||
if($type == 'free') return \helper::formatKB($info['avail_mem']);
|
||||
if($type == 'used') return \helper::formatKB($info['seg_size'] - $info['avail_mem']);
|
||||
if($type == 'rate') return round(($info['seg_size'] - $info['avail_mem']) / $info['seg_size'] * 100, 2);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+110
-117
@@ -17,22 +17,87 @@ use ZenTao\Cache\SimpleCache\InvalidArgumentException;
|
||||
class RedisDriver implements CacheInterface
|
||||
{
|
||||
/**
|
||||
* 缓存命名空间,用来区分不同的缓存。
|
||||
* The cache namespace, used to distinguish different caches.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* 缓存过期时间,单位为秒。
|
||||
* The cache expiration time, in seconds.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $defaultLifetime;
|
||||
|
||||
public function __construct($namespace = '', $defaultLifetime = 0)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
/**
|
||||
* 缓存服务范围。private 独享|public 共享。
|
||||
* The cache scope.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $scope;
|
||||
|
||||
global $app;
|
||||
$this->redis = $app->redis;
|
||||
/**
|
||||
* 缓存键连接符。
|
||||
* Cache key connector.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $connector;
|
||||
|
||||
public function __construct($namespace = '', $defaultLifetime = 0, $scope = '', $connector = '', $setting = null)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
$this->scope = $scope;
|
||||
$this->connector = $connector;
|
||||
|
||||
$this->connectRedis($setting);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接 Redis 服务器。
|
||||
* Connect to the Redis server.
|
||||
*
|
||||
* @param object $setting
|
||||
* @access private
|
||||
* @return object
|
||||
*/
|
||||
private function connectRedis($setting)
|
||||
{
|
||||
global $config;
|
||||
|
||||
try
|
||||
{
|
||||
$this->redis = \helper::connectRedis($setting);
|
||||
$this->redis->setOption(\Redis::OPT_SERIALIZER, $this->getSerializer($setting->serializer));
|
||||
$this->redis->select($setting->database);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
\helper::end($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置序列化器。
|
||||
* Set the serializer.
|
||||
*
|
||||
* @param string $serializer
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
private function getSerializer($serializer)
|
||||
{
|
||||
if($serializer == 'igbinary') return \Redis::SERIALIZER_IGBINARY;
|
||||
if($serializer == 'php') return \Redis::SERIALIZER_PHP;
|
||||
if($serializer == 'msgpack') return \Redis::SERIALIZER_MSGPACK;
|
||||
if($serializer == 'json') return \Redis::SERIALIZER_JSON;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,12 +110,9 @@ class RedisDriver implements CacheInterface
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$this->assertKeyName($key);
|
||||
$key = $this->buildKeyName($key);
|
||||
|
||||
$value = $this->redis->get($key);
|
||||
|
||||
return $value ? unserialize($value) : $default;
|
||||
return $value ? $value : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,12 +125,9 @@ class RedisDriver implements CacheInterface
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
$this->assertKeyName($key);
|
||||
$key = $this->buildKeyName($key);
|
||||
$ttl = (int)($ttl ?: $this->defaultLifetime);
|
||||
|
||||
$ttl = is_null($ttl) ? $this->defaultLifetime : $ttl;
|
||||
|
||||
return $this->redis->set($key, serialize($value), (int)$ttl);
|
||||
return $this->redis->set($key, $value, $ttl ?: null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,16 +135,10 @@ class RedisDriver implements CacheInterface
|
||||
*
|
||||
* @link https://github.com/phpredis/phpredis?tab=readme-ov-file#del-delete-unlink
|
||||
* @param mixed $key
|
||||
* @param bool $prefix
|
||||
* @return int
|
||||
*/
|
||||
public function delete($key, $prefix = true)
|
||||
public function delete($key)
|
||||
{
|
||||
if(!$prefix) return $this->redis->del($key);
|
||||
|
||||
$this->assertKeyName($key);
|
||||
$key = $this->buildKeyName($key);
|
||||
|
||||
return $this->redis->del($key);
|
||||
}
|
||||
|
||||
@@ -97,24 +150,20 @@ class RedisDriver implements CacheInterface
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
global $config;
|
||||
if($this->scope == 'private') return $this->redis->flushDB();
|
||||
|
||||
/* With Redis::SCAN_RETRY enabled */
|
||||
$this->redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);
|
||||
$it = NULL;
|
||||
|
||||
while($cachedKeys = $this->redis->scan($it))
|
||||
$it = null;
|
||||
$keys = [];
|
||||
|
||||
while($cachedKeys = $this->redis->scan($it, $this->namespace . $this->connector . '*'))
|
||||
{
|
||||
foreach ($cachedKeys as $key)
|
||||
{
|
||||
if(strpos($key, $config->db->name) !== false)
|
||||
{
|
||||
$this->delete($key, false);
|
||||
}
|
||||
}
|
||||
$keys = array_merge($keys, $cachedKeys);
|
||||
}
|
||||
|
||||
return true;
|
||||
return $this->deleteMultiple($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,27 +175,7 @@ class RedisDriver implements CacheInterface
|
||||
*/
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
$this->assertKeyNames($keys);
|
||||
$keys = $this->buildKeyNames($keys);
|
||||
|
||||
$result = $this->redis->mget($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] = unserialize($value);
|
||||
}
|
||||
|
||||
return $mappedResult;
|
||||
return array_filter(array_combine($keys, $this->redis->mget($keys)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,26 +188,11 @@ class RedisDriver implements CacheInterface
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
$this->assertKeyNames(array_keys($values));
|
||||
$ttl = (int)($ttl ?: $this->defaultLifetime);
|
||||
|
||||
$mappedByNamespaceValues = array();
|
||||
if(!$ttl) return $this->redis->mset($values);
|
||||
|
||||
foreach($values as $key => $value)
|
||||
{
|
||||
$mappedByNamespaceValues[$this->buildKeyName($key)] = serialize($value);
|
||||
}
|
||||
|
||||
$ttl = is_null($ttl) ? $this->defaultLifetime : $ttl;
|
||||
|
||||
if(!empty($ttl))
|
||||
{
|
||||
foreach($mappedByNamespaceValues as $key => $value)
|
||||
{
|
||||
$this->redis->set($key, $value, $ttl);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->redis->mset($mappedByNamespaceValues);
|
||||
foreach($values as $key => $value) $this->redis->set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,17 +203,9 @@ class RedisDriver implements CacheInterface
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
$this->assertKeyNames($keys);
|
||||
$keys = $this->buildKeyNames($keys);
|
||||
$result = $this->redis->del($keys);
|
||||
|
||||
$result = array();
|
||||
foreach($keys as $key)
|
||||
{
|
||||
$isDeleted = $this->delete($key);
|
||||
if($isDeleted) $result[] = $isDeleted;
|
||||
}
|
||||
|
||||
return count($result) === count($keys) ? true : false;
|
||||
return $result === count($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,49 +217,36 @@ class RedisDriver implements CacheInterface
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
$this->assertKeyName($key);
|
||||
$key = $this->buildKeyName($key);
|
||||
|
||||
return (bool) $this->redis->exists($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* 关闭 Redis 连接。
|
||||
* Close the Redis connection.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return $this->redis->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内存使用情况。
|
||||
* Get memory usage.
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
private function buildKeyName($key)
|
||||
public function memory($type)
|
||||
{
|
||||
return $this->namespace . $key;
|
||||
}
|
||||
$info = $this->redis->info();
|
||||
|
||||
/**
|
||||
* @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);
|
||||
if($type == 'total') return $info['total_system_memory_human'];
|
||||
if($type == 'free') return \helper::formatKB($info['total_system_memory'] - $info['used_memory']);
|
||||
if($type == 'used') return $info['used_memory_human'];
|
||||
if($type == 'rate') return round(($info['used_memory'] / $info['total_system_memory']) * 100, 2);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* The cache library of zentaopms.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://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 https://www.zentao.net
|
||||
*/
|
||||
|
||||
helper::import(dirname(__FILE__, 2) . '/base/mao/mao.class.php');
|
||||
|
||||
class mao extends baseMao
|
||||
{
|
||||
}
|
||||
@@ -71,6 +71,7 @@ $config->action->objectNameFields['solution'] = 'name';
|
||||
$config->action->objectNameFields['prompt'] = 'name';
|
||||
$config->action->objectNameFields['miniprogram'] = 'name';
|
||||
$config->action->objectNameFields['holiday'] = 'name';
|
||||
$config->action->objectNameFields['system'] = 'name';
|
||||
|
||||
$config->action->commonImgSize = 870;
|
||||
|
||||
|
||||
@@ -361,6 +361,7 @@ $lang->action->desc->closeautobackup = '$date, Disable automatic backup by <
|
||||
$lang->action->desc->autodeletebackups = '$date, Automatically clean up backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->savebackupsettings = '$date, Save backup settings backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->deleteexpiredbackup = '$date, Clean up expired backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->manualdeletebackup = '$date, Manually clean up backup by <strong>$actor</strong>.' . "\n";
|
||||
|
||||
/* Used to display dynamic information. */
|
||||
$lang->action->label = new stdclass();
|
||||
@@ -561,7 +562,7 @@ $lang->action->label->createbackup = 'Created system backup';
|
||||
$lang->action->label->deletebackup = 'Deleted system backup';
|
||||
$lang->action->label->restorebackup = 'Restored system backup';
|
||||
$lang->action->label->upgradesystem = 'Executed system upgrade';
|
||||
$lang->action->label->system = '';
|
||||
$lang->action->label->system = 'Application';
|
||||
$lang->action->label->manualbackup = 'Manual backup';
|
||||
$lang->action->label->autobackup = 'Automatic backup';
|
||||
$lang->action->label->openautobackup = 'Enable automatic backup';
|
||||
@@ -569,6 +570,7 @@ $lang->action->label->closeautobackup = 'Disable automatic backup';
|
||||
$lang->action->label->autodeletebackups = 'Automatically clean up backups';
|
||||
$lang->action->label->savebackupsettings = 'Save backup settings';
|
||||
$lang->action->label->deleteexpiredbackup = 'Clean up expired backups';
|
||||
$lang->action->label->manualdeletebackup = 'Manually clean up backup';
|
||||
|
||||
/* Dynamic information is grouped by object. */
|
||||
$lang->action->dynamicAction = new stdclass;
|
||||
|
||||
@@ -361,6 +361,7 @@ $lang->action->desc->closeautobackup = '$date, Disable automatic backup by <
|
||||
$lang->action->desc->autodeletebackups = '$date, Automatically clean up backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->savebackupsettings = '$date, Save backup settings backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->deleteexpiredbackup = '$date, Clean up expired backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->manualdeletebackup = '$date, Manually clean up backup by <strong>$actor</strong>.' . "\n";
|
||||
|
||||
/* Used to display dynamic information. */
|
||||
$lang->action->label = new stdclass();
|
||||
@@ -561,7 +562,7 @@ $lang->action->label->createbackup = 'Created system backup';
|
||||
$lang->action->label->deletebackup = 'Deleted system backup';
|
||||
$lang->action->label->restorebackup = 'Restored system backup';
|
||||
$lang->action->label->upgradesystem = 'Executed system upgrade';
|
||||
$lang->action->label->system = '';
|
||||
$lang->action->label->system = 'Application';
|
||||
$lang->action->label->manualbackup = 'Manual backup';
|
||||
$lang->action->label->autobackup = 'Automatic backup';
|
||||
$lang->action->label->openautobackup = 'Enable automatic backup';
|
||||
@@ -569,6 +570,7 @@ $lang->action->label->closeautobackup = 'Disable automatic backup';
|
||||
$lang->action->label->autodeletebackups = 'Automatically clean up backups';
|
||||
$lang->action->label->savebackupsettings = 'Save backup settings';
|
||||
$lang->action->label->deleteexpiredbackup = 'Clean up expired backups';
|
||||
$lang->action->label->manualdeletebackup = 'Manually clean up backup';
|
||||
|
||||
/* Dynamic information is grouped by object. */
|
||||
$lang->action->dynamicAction = new stdclass;
|
||||
|
||||
@@ -361,6 +361,7 @@ $lang->action->desc->closeautobackup = '$date, Disable automatic backup by <
|
||||
$lang->action->desc->autodeletebackups = '$date, Automatically clean up backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->savebackupsettings = '$date, Save backup settings backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->deleteexpiredbackup = '$date, Clean up expired backups by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->manualdeletebackup = '$date, Manually clean up backup by <strong>$actor</strong>.' . "\n";
|
||||
|
||||
/* Used to display dynamic information. */
|
||||
$lang->action->label = new stdclass();
|
||||
@@ -561,7 +562,7 @@ $lang->action->label->createbackup = 'Created system backup';
|
||||
$lang->action->label->deletebackup = 'Deleted system backup';
|
||||
$lang->action->label->restorebackup = 'Restored system backup';
|
||||
$lang->action->label->upgradesystem = 'Executed system upgrade';
|
||||
$lang->action->label->system = '';
|
||||
$lang->action->label->system = 'Application';
|
||||
$lang->action->label->manualbackup = 'Manual backup';
|
||||
$lang->action->label->autobackup = 'Automatic backup';
|
||||
$lang->action->label->openautobackup = 'Enable automatic backup';
|
||||
@@ -569,6 +570,7 @@ $lang->action->label->closeautobackup = 'Disable automatic backup';
|
||||
$lang->action->label->autodeletebackups = 'Automatically clean up backups';
|
||||
$lang->action->label->savebackupsettings = 'Save backup settings';
|
||||
$lang->action->label->deleteexpiredbackup = 'Clean up expired backups';
|
||||
$lang->action->label->manualdeletebackup = 'Manually clean up backup';
|
||||
|
||||
/* Dynamic information is grouped by object. */
|
||||
$lang->action->dynamicAction = new stdclass();
|
||||
|
||||
@@ -189,6 +189,7 @@ $lang->action->objectTypes['projectbuild'] = '版本';
|
||||
$lang->action->objectTypes['board'] = '白板';
|
||||
$lang->action->objectTypes['boardspace'] = '白板空间';
|
||||
$lang->action->objectTypes['productline'] = '产品线';
|
||||
$lang->action->objectTypes['system'] = '应用';
|
||||
|
||||
/* 用来描述操作历史记录。*/
|
||||
$lang->action->desc = new stdclass();
|
||||
@@ -358,9 +359,10 @@ $lang->action->desc->manualbackup = '$date, 由 <strong>$actor</strong>
|
||||
$lang->action->desc->autobackup = '$date, 由 <strong>$actor</strong> 自动备份。' . "\n";
|
||||
$lang->action->desc->openautobackup = '$date, 由 <strong>$actor</strong> 开启自动备份。' . "\n";
|
||||
$lang->action->desc->closeautobackup = '$date, 由 <strong>$actor</strong> 关闭自动备份。' . "\n";
|
||||
$lang->action->desc->autodeletebackups = '$date, 由 <strong>$actor</strong> 自动清理备份。' . "\n";
|
||||
$lang->action->desc->autodeletebackups = '$date, 由 <strong>$actor</strong> 自动删除备份。' . "\n";
|
||||
$lang->action->desc->savebackupsettings = '$date, 由 <strong>$actor</strong> 保存备份设置。' . "\n";
|
||||
$lang->action->desc->deleteexpiredbackup = '$date, 由 <strong>$actor</strong> 清理过期备份。' . "\n";
|
||||
$lang->action->desc->deleteexpiredbackup = '$date, 由 <strong>$actor</strong> 删除过期备份。' . "\n";
|
||||
$lang->action->desc->manualdeletebackup = '$date, 由 <strong>$actor</strong> 手动删除备份。' . "\n";
|
||||
|
||||
/* 用来显示动态信息。*/
|
||||
$lang->action->label = new stdclass();
|
||||
@@ -561,15 +563,15 @@ $lang->action->label->createbackup = '创建了系统备份';
|
||||
$lang->action->label->deletebackup = '删除了系统备份';
|
||||
$lang->action->label->restorebackup = '还原了系统备份';
|
||||
$lang->action->label->upgradesystem = '执行了系统升级';
|
||||
$lang->action->label->system = '';
|
||||
$lang->action->label->system = '应用';
|
||||
$lang->action->label->manualbackup = '手动备份了';
|
||||
$lang->action->label->autobackup = '自动备份了';
|
||||
$lang->action->label->openautobackup = '开启自动备份';
|
||||
$lang->action->label->closeautobackup = '关闭自动备份';
|
||||
$lang->action->label->autodeletebackups = '自动清理备份';
|
||||
$lang->action->label->autodeletebackups = '自动删除备份';
|
||||
$lang->action->label->savebackupsettings = '保存备份设置';
|
||||
$lang->action->label->deleteexpiredbackup = '清理过期备份';
|
||||
|
||||
$lang->action->label->deleteexpiredbackup = '删除过期备份';
|
||||
$lang->action->label->manualdeletebackup = '手动删除备份';
|
||||
|
||||
|
||||
/* 动态信息按照对象分组 */
|
||||
@@ -924,7 +926,7 @@ $lang->action->label->chartgroup = '分组';
|
||||
$lang->action->label->serverroom = '机房|serverroom|browse|';
|
||||
$lang->action->label->host = '主机|host|view|id=%s';
|
||||
$lang->action->label->account = "账号|account|view|id=%s";
|
||||
$lang->action->label->instance = '应用|instance|view|id=%s';
|
||||
$lang->action->label->instance = '服务|instance|view|id=%s';
|
||||
$lang->action->label->prompt = '提词|ai|promptview|id=%s';
|
||||
$lang->action->label->miniprogram = '小程序|aiapp|browseminiprogram|id=%s';
|
||||
$lang->action->label->holiday = '节假日|holiday|browse|';
|
||||
|
||||
@@ -366,6 +366,19 @@ class actionModel extends model
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户第一条操作。
|
||||
* Get user first action.
|
||||
*
|
||||
* @param string $account
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function getAccountFirstAction(string $account): object
|
||||
{
|
||||
return $this->dao->select('*')->from(TABLE_ACTION)->where('actor')->eq($account)->orderBy('id')->limit(1)->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已删除的对象。
|
||||
* Get deleted objects.
|
||||
@@ -1200,6 +1213,8 @@ class actionModel extends model
|
||||
$actionObjectLabel = $this->lang->doc->menuTitle;
|
||||
}
|
||||
}
|
||||
|
||||
if($objectType == 'system' && strpos(strtolower($actionType), 'backup') !== false) $actionObjectLabel = '';
|
||||
}
|
||||
|
||||
if(in_array($this->config->edition, array('max', 'ipd')) && $objectType == 'assetlib')
|
||||
|
||||
@@ -5,7 +5,7 @@ $config->admin->log->saveDays = 30;
|
||||
if(!isset($config->safe)) $config->safe = new stdclass();
|
||||
if(!isset($config->safe->weak)) $config->safe->weak = '123456,password,12345,12345678,qwerty,123456789,1234,1234567,abc123,111111,123123';
|
||||
|
||||
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'admin|cache', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup');
|
||||
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup');
|
||||
$config->admin->menuGroup['company'] = array('dept', 'company', 'user', 'group', 'tutorial');
|
||||
$config->admin->menuGroup['switch'] = array('admin|setmodule');
|
||||
$config->admin->menuGroup['model'] = array('auditcl', 'stage', 'design', 'cmcl', 'reviewcl', 'custom|required', 'custom|set', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting', 'custom|project');
|
||||
|
||||
@@ -24,6 +24,3 @@ $config->admin->form->log['days'] = array('type' => 'int', 'required' => true, '
|
||||
|
||||
$config->admin->form->resetpwdsetting = array();
|
||||
$config->admin->form->resetpwdsetting['resetPWDByMail'] = array('type' => 'int', 'required' => false, 'default' => 0);
|
||||
|
||||
$config->admin->form->cache = array();
|
||||
$config->admin->form->cache['dao'] = array('type' => 'array', 'default' => array('enable' => 0));
|
||||
|
||||
@@ -590,50 +590,4 @@ class admin extends control
|
||||
|
||||
echo 'success';
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否启用缓存。
|
||||
* Set cache enable.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function cache()
|
||||
{
|
||||
if($_POST)
|
||||
{
|
||||
if(!extension_loaded('apcu')) return $this->send(array('result' => 'fail', 'message' => $this->lang->admin->apcuNotLoaded));
|
||||
if(!ini_get('apc.enabled')) return $this->send(array('result' => 'fail', 'message' => $this->lang->admin->apcuNotEnabled));
|
||||
|
||||
$cache = form::data()->get();
|
||||
$this->loadModel('setting')->setItem('system.common.global.cache', json_encode($cache));
|
||||
|
||||
if($cache->dao['enable'] != $this->config->cache->dao->enable) $this->dao->clearCache();
|
||||
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
|
||||
}
|
||||
|
||||
if(helper::isAPCuEnabled())
|
||||
{
|
||||
$this->view->rate = $this->adminZen->getAPCuMemory('rate');
|
||||
$this->view->used = $this->adminZen->getAPCuMemory('used');
|
||||
$this->view->total = $this->adminZen->getAPCuMemory('total');
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->admin->cache;
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除数据缓存。
|
||||
* Clear data cache.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxClearCache()
|
||||
{
|
||||
$this->dao->clearCache();
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->admin->clearSuccess, 'load' => true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,18 +53,6 @@ $lang->admin->engineInfo = "The <strong>%s</strong> table engine is <strong>
|
||||
$lang->admin->engineSummary['hasMyISAM'] = "There are %s tables that are not InnoDB engines";
|
||||
$lang->admin->engineSummary['allInnoDB'] = "All tables are InnoDB engines";
|
||||
|
||||
$lang->admin->daoCache = 'Data Cache';
|
||||
$lang->admin->clearCache = 'Clear';
|
||||
$lang->admin->clearSuccess = 'Cleared';
|
||||
$lang->admin->memory = 'Memory';
|
||||
$lang->admin->usedMemory = 'Total %s, %s used';
|
||||
$lang->admin->apcuNotice = 'Data cache is based on the PHP-APCu extension, which needs to be loaded before enabling.';
|
||||
$lang->admin->apcuNotLoaded = 'Please load the APCu extension before enabling DAO cache';
|
||||
$lang->admin->apcuNotEnabled = 'Please enable the apc.enabled option before enabling DAO cache';
|
||||
|
||||
$lang->admin->cacheStatusList[1] = 'On';
|
||||
$lang->admin->cacheStatusList[0] = 'Off';
|
||||
|
||||
$lang->admin->info = new stdclass();
|
||||
$lang->admin->info->version = 'Aktuelle Version ist %s. ';
|
||||
$lang->admin->info->links = 'You can visit links below';
|
||||
|
||||
@@ -53,18 +53,6 @@ $lang->admin->engineInfo = "The <strong>%s</strong> table engine
|
||||
$lang->admin->engineSummary['hasMyISAM'] = "There are %s tables that are not InnoDB engines";
|
||||
$lang->admin->engineSummary['allInnoDB'] = "All tables are InnoDB engines";
|
||||
|
||||
$lang->admin->daoCache = 'Data Cache';
|
||||
$lang->admin->clearCache = 'Clear';
|
||||
$lang->admin->clearSuccess = 'Cleared';
|
||||
$lang->admin->memory = 'Memory';
|
||||
$lang->admin->usedMemory = 'Total %s, %s used';
|
||||
$lang->admin->apcuNotice = 'Data cache is based on the PHP-APCu extension, which needs to be loaded before enabling.';
|
||||
$lang->admin->apcuNotLoaded = 'Please load the APCu extension before enabling DAO cache';
|
||||
$lang->admin->apcuNotEnabled = 'Please enable the apc.enabled option before enabling DAO cache';
|
||||
|
||||
$lang->admin->cacheStatusList[1] = 'On';
|
||||
$lang->admin->cacheStatusList[0] = 'Off';
|
||||
|
||||
$lang->admin->info = new stdclass();
|
||||
$lang->admin->info->version = 'Current Version is %s. ';
|
||||
$lang->admin->info->links = 'You can visit links below';
|
||||
|
||||
@@ -53,18 +53,6 @@ $lang->admin->engineInfo = "The <strong>%s</strong> table engine is <strong>
|
||||
$lang->admin->engineSummary['hasMyISAM'] = "There are %s tables that are not InnoDB engines";
|
||||
$lang->admin->engineSummary['allInnoDB'] = "All tables are InnoDB engines";
|
||||
|
||||
$lang->admin->daoCache = 'Data Cache';
|
||||
$lang->admin->clearCache = 'Clear';
|
||||
$lang->admin->clearSuccess = 'Cleared';
|
||||
$lang->admin->memory = 'Memory';
|
||||
$lang->admin->usedMemory = 'Total %s, %s used';
|
||||
$lang->admin->apcuNotice = 'Data cache is based on the PHP-APCu extension, which needs to be loaded before enabling.';
|
||||
$lang->admin->apcuNotLoaded = 'Please load the APCu extension before enabling DAO cache';
|
||||
$lang->admin->apcuNotEnabled = 'Please enable the apc.enabled option before enabling DAO cache';
|
||||
|
||||
$lang->admin->cacheStatusList[1] = 'On';
|
||||
$lang->admin->cacheStatusList[0] = 'Off';
|
||||
|
||||
$lang->admin->info = new stdclass();
|
||||
$lang->admin->info->version = 'La version actuelle est %s customisée. ';
|
||||
$lang->admin->info->links = 'Vous pouvez visiter les liens ci-dessous';
|
||||
|
||||
@@ -47,7 +47,7 @@ $lang->admin->menuList->convert['order'] = 50;
|
||||
$lang->admin->menuList->system['subMenu']['mode'] = array('link' => "{$lang->custom->mode}|custom|mode|");
|
||||
$lang->admin->menuList->system['subMenu']['trash'] = array('link' => "{$lang->action->trash}|action|trash|");
|
||||
$lang->admin->menuList->system['subMenu']['safe'] = array('link' => "{$lang->security}|admin|safe|", 'alias' => 'checkweak,resetpwdsetting', 'links' => array('admin|resetpwdsetting|', 'admin|checkweak|'));
|
||||
$lang->admin->menuList->system['subMenu']['cache'] = array('link' => "{$lang->admin->cache}|admin|cache|");
|
||||
$lang->admin->menuList->system['subMenu']['cache'] = array('link' => "{$lang->cache->common}|cache|setting|");
|
||||
$lang->admin->menuList->system['subMenu']['cron'] = array('link' => "{$lang->admin->cron}|cron|index|", 'subModule' => 'cron');
|
||||
$lang->admin->menuList->system['subMenu']['timezone'] = array('link' => "{$lang->timezone}|custom|timezone|");
|
||||
$lang->admin->menuList->system['subMenu']['buildindex'] = array('link' => "{$lang->admin->buildIndex}|search|buildindex|");
|
||||
|
||||
@@ -53,18 +53,6 @@ $lang->admin->engineInfo = "表<strong>%s</strong>的引擎是<s
|
||||
$lang->admin->engineSummary['hasMyISAM'] = "有%s个表不是InnoDB引擎";
|
||||
$lang->admin->engineSummary['allInnoDB'] = "所有的表都是InnoDB引擎了";
|
||||
|
||||
$lang->admin->daoCache = '数据缓存';
|
||||
$lang->admin->clearCache = '清除缓存';
|
||||
$lang->admin->clearSuccess = '清除成功';
|
||||
$lang->admin->memory = '内存使用';
|
||||
$lang->admin->usedMemory = '总计 %s,已使用 %s';
|
||||
$lang->admin->apcuNotice = '数据缓存基于 PHP-APCu 扩展实现,开启前需要先加载 APCu 扩展。';
|
||||
$lang->admin->apcuNotLoaded = '请加载 APCu 扩展后再开启数据缓存';
|
||||
$lang->admin->apcuNotEnabled = '请启用 apc.enabled 选项后再开启数据缓存';
|
||||
|
||||
$lang->admin->cacheStatusList[1] = '开启';
|
||||
$lang->admin->cacheStatusList[0] = '关闭';
|
||||
|
||||
$lang->admin->info = new stdclass();
|
||||
$lang->admin->info->version = '当前系统的版本是%s,';
|
||||
$lang->admin->info->links = '您可以访问以下链接:';
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The cache view file of admin module of ZenTaoPMS.
|
||||
* @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Gang Liu <liugang@easycorp.ltd>
|
||||
* @package admin
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
formPanel
|
||||
(
|
||||
set::actions
|
||||
([
|
||||
'submit',
|
||||
$config->cache->dao->enable ? ['text' => $lang->admin->clearCache, 'url' => inlink('ajaxClearCache'), 'class' => 'secondary ajax-submit'] : null,
|
||||
'cancel'
|
||||
]),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->admin->daoCache),
|
||||
radioList
|
||||
(
|
||||
set::name('dao[enable]'),
|
||||
set::items($lang->admin->cacheStatusList),
|
||||
set::value($config->cache->dao->enable),
|
||||
set::inline(true)
|
||||
),
|
||||
span(setClass('ml-4 mt-1.5'), icon('info text-warning mr-2'), $lang->admin->apcuNotice)
|
||||
),
|
||||
helper::isAPCuEnabled() && $this->config->cache->dao->driver == 'Apcu' ? formRow
|
||||
(
|
||||
formGroup
|
||||
(
|
||||
setClass('w-1/2'),
|
||||
setStyle(array('align-items' => 'center')),
|
||||
set::label($lang->admin->memory),
|
||||
progressBar
|
||||
(
|
||||
set::percent($rate),
|
||||
set::width('100%'),
|
||||
set::color('rgb(var(--color-' . ($rate <= 50 ? 'success' : ($rate <= 80 ? 'warning' : 'danger')) . '-500-rgb))')
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('w-1/2 ml-4 gap-4'),
|
||||
setStyle(array('align-items' => 'center')),
|
||||
span($rate . '%'),
|
||||
span(sprintf($lang->admin->usedMemory, $total, $used))
|
||||
)
|
||||
) : null
|
||||
);
|
||||
|
||||
render();
|
||||
@@ -302,25 +302,4 @@ class adminZen extends admin
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取APCu内存使用数据。
|
||||
* Get APCu memory data.
|
||||
*
|
||||
* @param string $type total|free|used
|
||||
* @access protected
|
||||
* @return string|float
|
||||
*/
|
||||
public function getAPCuMemory(string $type = 'total'): string|float
|
||||
{
|
||||
if(!helper::isAPCuEnabled()) return '';
|
||||
|
||||
$info = apcu_sma_info(true);
|
||||
|
||||
if($type == 'total') return helper::formatKB($info['seg_size']);
|
||||
if($type == 'free') return helper::formatKB($info['avail_mem']);
|
||||
if($type == 'used') return helper::formatKB($info['seg_size'] - $info['avail_mem']);
|
||||
if($type == 'rate') return round(($info['seg_size'] - $info['avail_mem']) / $info['seg_size'] * 100, 2);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
+25
-12
@@ -1,4 +1,4 @@
|
||||
$('.form-group').on('click', '.btn-add', function()
|
||||
$(document).off('click', '.form-group .btn-add').on('click', '.form-group .btn-add', function()
|
||||
{
|
||||
let $newRow = $(this).closest('tr').clone();
|
||||
|
||||
@@ -26,7 +26,7 @@ $('.form-group').on('click', '.btn-add', function()
|
||||
}
|
||||
});
|
||||
|
||||
$('.form-group').on('click', '.btn-split', function()
|
||||
$(document).off('click', '.form-group .btn-split').on('click', '.form-group .btn-split', function()
|
||||
{
|
||||
let $newRow = $(this).closest('tr').clone();
|
||||
$newRow.find('input').val('');
|
||||
@@ -41,7 +41,7 @@ $('.form-group').on('click', '.btn-split', function()
|
||||
$(this).closest('tr').after($newRow);
|
||||
});
|
||||
|
||||
$('.form-group').on('click', '.btn-delete', function()
|
||||
$(document).off('click', '.form-group .btn-delete').on('click', '.form-group .btn-delete', function()
|
||||
{
|
||||
if($(this).closest('table').find('.input-row').length == 1) return false;
|
||||
|
||||
@@ -59,20 +59,24 @@ $('.form-group').on('click', '.btn-delete', function()
|
||||
}
|
||||
});
|
||||
|
||||
$('.params-group').on('keyup', 'input,textarea', function(){
|
||||
$(document).off('keyup', '.params-group input, .params-group textarea').on('keyup', '.params-group input, .params-group textarea', function()
|
||||
{
|
||||
generateParams($(this));
|
||||
})
|
||||
|
||||
$('.params-group').on('change', 'input[type=checkbox]', function(){
|
||||
$(document).off('change', '.params-group input[type=checkbox]').on('change', '.params-group input[type=checkbox]', function()
|
||||
{
|
||||
generateParams($(this));
|
||||
})
|
||||
|
||||
$('.params-group').on('change', 'select', function(){
|
||||
$(document).off('change', '.params-group select').on('change', '.params-group select', function()
|
||||
{
|
||||
generateParams($(this));
|
||||
})
|
||||
|
||||
/* 变更请求类型时,判断是否隐藏拆分按钮. */
|
||||
$('.form-group').on('change', '.objectType', function(){
|
||||
$(document).off('change', '.form-group .objectType').on('change', '.form-group .objectType', function()
|
||||
{
|
||||
/* 变更请求类型时,判断是否隐藏拆分按钮. */
|
||||
if($(this).val() != 'array' && $(this).val() != 'object')
|
||||
{
|
||||
$(this).closest('tr').find('.btn-split').addClass('hidden');
|
||||
@@ -84,12 +88,21 @@ $('.form-group').on('change', '.objectType', function(){
|
||||
})
|
||||
|
||||
/* 请求响应单独绑定事件. */
|
||||
$('#form-response').on('keyup', 'input,textarea', function(){generateResponse($(this))});
|
||||
$('#form-response').on('change', 'input[type=checkbox]', function(){generateResponse($(this))});
|
||||
$('#form-response').on('change', 'select', function(){generateResponse($(this))});
|
||||
$(document).off('keyup', '#form-response input, #form-response textarea').on('keyup', '#form-response input, #form-response textarea', function()
|
||||
{
|
||||
generateResponse($(this));
|
||||
});
|
||||
$(document).off('change', '#form-response input[type=checkbox]').on('change', '#form-response input[type=checkbox]', function()
|
||||
{
|
||||
generateResponse($(this));
|
||||
});
|
||||
$(document).off('change', '#form-response select').on('change', '#form-response select', function()
|
||||
{
|
||||
generateResponse($(this));
|
||||
});
|
||||
|
||||
/* 更改请求体类型. */
|
||||
$('.params-group').on('change', 'input[type=radio]', function()
|
||||
$(document).off('change', '.params-group input[type=radio]').on('change', '.params-group input[type=radio]', function()
|
||||
{
|
||||
const isStruct = $(this).closest('div.form-group').hasClass('struct');
|
||||
if(!isStruct)
|
||||
|
||||
+15
-13
@@ -856,27 +856,29 @@ class bugModel extends model
|
||||
$query = preg_replace('/`(\w+)`/', 't1.`$1`', $query);
|
||||
|
||||
if($moduleName == 'contributeBug') $bugsAssignedByMe = $this->loadModel('my')->getAssignedByMe($account, null, $orderBy, 'bug');
|
||||
return $this->dao->select("t1.*, t2.name AS productName, t2.shadow, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) AS priOrder, IF(t1.`severity` = 0, {$this->config->maxPriValue}, t1.`severity`) AS severityOrder")->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id')
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('t2.deleted')->eq('0')
|
||||
|
||||
$bugs = $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) AS priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) AS severityOrder")->from(TABLE_BUG)
|
||||
->where('deleted')->eq(0)
|
||||
->beginIF($type == 'bySearch')->andWhere($query)->fi()
|
||||
->beginIF($executionID)->andWhere('t1.execution')->eq($executionID)->fi()
|
||||
->beginIF($type != 'closedBy' and $this->app->moduleName == 'block')->andWhere('t1.status')->ne('closed')->fi()
|
||||
->beginIF($type != 'all' and $type != 'bySearch')->andWhere("t1.`$type`")->eq($account)->fi()
|
||||
->beginIF($type == 'bySearch' and $moduleName == 'workBug')->andWhere("t1.assignedTo")->eq($account)->fi()
|
||||
->beginIF($type == 'assignedTo' and $moduleName == 'workBug')->andWhere('t1.status')->ne('closed')->fi()
|
||||
->beginIF($executionID)->andWhere('execution')->eq($executionID)->fi()
|
||||
->beginIF($type != 'closedBy' and $this->app->moduleName == 'block')->andWhere('status')->ne('closed')->fi()
|
||||
->beginIF($type != 'all' and $type != 'bySearch')->andWhere("`$type`")->eq($account)->fi()
|
||||
->beginIF($type == 'bySearch' and $moduleName == 'workBug')->andWhere("assignedTo")->eq($account)->fi()
|
||||
->beginIF($type == 'assignedTo' and $moduleName == 'workBug')->andWhere('status')->ne('closed')->fi()
|
||||
->beginIF($type == 'bySearch' and $moduleName == 'contributeBug')
|
||||
->andWhere('t1.openedBy', 1)->eq($account)
|
||||
->orWhere('t1.closedBy')->eq($account)
|
||||
->orWhere('t1.resolvedBy')->eq($account)
|
||||
->orWhere('t1.id')->in(!empty($bugsAssignedByMe) ? array_keys($bugsAssignedByMe) : array())
|
||||
->andWhere('openedBy', 1)->eq($account)
|
||||
->orWhere('closedBy')->eq($account)
|
||||
->orWhere('resolvedBy')->eq($account)
|
||||
->orWhere('id')->in(!empty($bugsAssignedByMe) ? array_keys($bugsAssignedByMe) : array())
|
||||
->markRight(1)
|
||||
->fi()
|
||||
->orderBy($orderBy)
|
||||
->beginIF($limit > 0)->limit($limit)->fi()
|
||||
->page($pager)
|
||||
->fetchAll();
|
||||
|
||||
$this->mao->select('name AS productName, shadow')->from(TABLE_PRODUCT)->into($bugs, 'product');
|
||||
return $bugs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
$config->cache->form = new stdClass();
|
||||
$config->cache->form->setting = array();
|
||||
$config->cache->form->setting['enable'] = array('type' => 'int', 'default' => 0);
|
||||
$config->cache->form->setting['driver'] = array('type' => 'string', 'default' => '');
|
||||
$config->cache->form->setting['scope'] = array('type' => 'string', 'default' => '');
|
||||
$config->cache->form->setting['namespace'] = array('type' => 'string', 'default' => '');
|
||||
$config->cache->form->setting['redis'] = array('type' => 'array', 'default' => array('host' => '', 'port' => 0, 'username' => '', 'password' => '', 'database' => 0, 'serializer' => 'igbinary'));
|
||||
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* The control file of cache module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Gang Liu <liugang@chandao.com>
|
||||
* @package cache
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
class cache extends control
|
||||
{
|
||||
/**
|
||||
* 设置是否启用缓存。
|
||||
* Set cache enable.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setting()
|
||||
{
|
||||
if($_POST)
|
||||
{
|
||||
$redis = null;
|
||||
$cache = form::data()->get();
|
||||
if(isset($cache->redis))
|
||||
{
|
||||
$redis = (object)$cache->redis;
|
||||
unset($cache->redis);
|
||||
}
|
||||
if(!isset($redis->host)) $redis->host = '';
|
||||
if(!isset($redis->port)) $redis->port = '';
|
||||
if(!isset($redis->username)) $redis->username = '';
|
||||
if(!isset($redis->password)) $redis->password = '';
|
||||
if(!isset($redis->database)) $redis->database = '';
|
||||
if(!isset($redis->serializer)) $redis->serializer = '';
|
||||
|
||||
if($cache->enable)
|
||||
{
|
||||
$errors = [];
|
||||
if(empty($cache->driver)) $errors['driver'] = sprintf($this->lang->error->notempty, $this->lang->cache->driver);
|
||||
if(empty($cache->scope)) $errors['scope'] = sprintf($this->lang->error->notempty, $this->lang->cache->scope);
|
||||
if(empty($cache->namespace)) $errors['namespace'] = sprintf($this->lang->error->notempty, $this->lang->cache->namespace);
|
||||
if($cache->driver == 'redis' && empty($redis->host)) $errors['redis[host]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->host);
|
||||
if($cache->driver == 'redis' && empty($redis->port)) $errors['redis[port]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->port);
|
||||
if($cache->driver == 'redis' && empty($redis->database) && $redis->database !== '0') $errors['redis[database]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->database);
|
||||
if($errors) return $this->send(array('result' => 'fail', 'message' => $errors));
|
||||
|
||||
if($cache->driver == 'apcu')
|
||||
{
|
||||
/* 检查是否加载了 APCu 扩展。Check if the APCu extension is loaded. */
|
||||
if(!extension_loaded('apcu')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->apcu->notLoaded));
|
||||
if(!ini_get('apc.enabled')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->apcu->notEnabled));
|
||||
}
|
||||
if($cache->driver == 'redis')
|
||||
{
|
||||
/* 检查是否加载了 Redis 扩展。Check if the Redis extension is loaded. */
|
||||
if(!extension_loaded('redis')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->redis->notLoaded));
|
||||
if(!extension_loaded('igbinary') && $redis->serializer == 'igbinary') return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->redis->igbinaryNotLoaded));
|
||||
|
||||
/* 检查 Redis 连接是否正常。Check if the Redis connection is normal. */
|
||||
try
|
||||
{
|
||||
helper::connectRedis($redis);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
return $this->send(array('result' => 'fail', 'message' => $e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 如果缓存配置发生变化,清空缓存。If the cache configuration changes, clear the cache. */
|
||||
if($cache->enable != $this->config->cache->enable
|
||||
|| $cache->driver != $this->config->cache->driver
|
||||
|| $cache->namespace != $this->config->cache->namespace
|
||||
|| $redis->database != $this->config->redis->database
|
||||
|| $redis->serializer != $this->config->redis->serializer)
|
||||
{
|
||||
$this->cache->clear();
|
||||
}
|
||||
|
||||
$this->loadModel('setting')->setItems('system.common.cache', $cache);
|
||||
if($cache->driver == 'redis') $this->setting->setItems('system.common.redis', $redis);
|
||||
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
|
||||
}
|
||||
|
||||
if($this->config->cache->enable)
|
||||
{
|
||||
$this->view->rate = $this->mao->memory('rate');
|
||||
$this->view->used = $this->mao->memory('used');
|
||||
$this->view->total = $this->mao->memory('total');
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->cache->common;
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除数据缓存。
|
||||
* Clear data cache.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxClear()
|
||||
{
|
||||
$this->cache->clear();
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->cache->clearSuccess, 'load' => true));
|
||||
}
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
function toggleCache()
|
||||
{
|
||||
$('.cache').toggleClass('hidden', $(this).val() != '1');
|
||||
toggleDriver.call($('[name=driver]:checked'));
|
||||
}
|
||||
|
||||
function toggleDriver()
|
||||
{
|
||||
$('.apcu').toggleClass('hidden', $(this).val() != 'apcu');
|
||||
$('.redis').toggleClass('hidden', $(this).val() != 'redis');
|
||||
}
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
$lang->cache->clear = 'Clear Cache';
|
||||
$lang->cache->clearSuccess = 'Cache cleared successfully.';
|
||||
$lang->cache->status = 'Status';
|
||||
$lang->cache->driver = 'Cache Type';
|
||||
$lang->cache->namespace = 'Namespace';
|
||||
$lang->cache->scope = 'Scope';
|
||||
$lang->cache->memory = 'Memory';
|
||||
$lang->cache->usedMemory = 'Total %s, used %s';
|
||||
|
||||
$lang->cache->statusList[1] = 'On';
|
||||
$lang->cache->statusList[0] = 'Off';
|
||||
|
||||
$lang->cache->driverList['apcu'] = 'APCu';
|
||||
$lang->cache->driverList['redis'] = 'Redis';
|
||||
|
||||
$lang->cache->scopeList['private'] = 'Exclusively for this application';
|
||||
$lang->cache->scopeList['shared'] = 'Shared by multiple applications';
|
||||
|
||||
$lang->cache->apcu = new stdClass();
|
||||
$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.';
|
||||
$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.';
|
||||
$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.';
|
||||
|
||||
$lang->cache->redis = new stdClass();
|
||||
$lang->cache->redis->host = 'Redis Host';
|
||||
$lang->cache->redis->port = 'Redis Port';
|
||||
$lang->cache->redis->username = 'Redis User';
|
||||
$lang->cache->redis->password = 'Redis Password';
|
||||
$lang->cache->redis->serializer = 'Redis Serializer';
|
||||
$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.';
|
||||
$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.';
|
||||
$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.';
|
||||
|
||||
$lang->cache->redis->serializerList['php'] = 'PHP Serialize';
|
||||
$lang->cache->redis->serializerList['igbinary'] = 'igbinary';
|
||||
|
||||
$lang->cache->redis->tips = new stdClass();
|
||||
$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.';
|
||||
$lang->cache->redis->tips->database = 'Fill in the number of the Redis database, the default is 0.';
|
||||
$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.';
|
||||
|
||||
$lang->cache->tips = new stdClass();
|
||||
$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace after caching is enabled will clear the cache data.';
|
||||
$lang->cache->tips->scope = 'If the cache service is only used by this application, please select "Exclusively for this application", otherwise select "Shared by multiple applications".';
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
$lang->cache->clear = 'Clear Cache';
|
||||
$lang->cache->clearSuccess = 'Cache cleared successfully.';
|
||||
$lang->cache->status = 'Status';
|
||||
$lang->cache->driver = 'Cache Type';
|
||||
$lang->cache->namespace = 'Namespace';
|
||||
$lang->cache->scope = 'Scope';
|
||||
$lang->cache->memory = 'Memory';
|
||||
$lang->cache->usedMemory = 'Total %s, used %s';
|
||||
|
||||
$lang->cache->statusList[1] = 'On';
|
||||
$lang->cache->statusList[0] = 'Off';
|
||||
|
||||
$lang->cache->driverList['apcu'] = 'APCu';
|
||||
$lang->cache->driverList['redis'] = 'Redis';
|
||||
|
||||
$lang->cache->scopeList['private'] = 'Exclusively for this application';
|
||||
$lang->cache->scopeList['shared'] = 'Shared by multiple applications';
|
||||
|
||||
$lang->cache->apcu = new stdClass();
|
||||
$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.';
|
||||
$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.';
|
||||
$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.';
|
||||
|
||||
$lang->cache->redis = new stdClass();
|
||||
$lang->cache->redis->host = 'Redis Host';
|
||||
$lang->cache->redis->port = 'Redis Port';
|
||||
$lang->cache->redis->username = 'Redis User';
|
||||
$lang->cache->redis->password = 'Redis Password';
|
||||
$lang->cache->redis->serializer = 'Redis Serializer';
|
||||
$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.';
|
||||
$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.';
|
||||
$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.';
|
||||
|
||||
$lang->cache->redis->serializerList['php'] = 'PHP Serialize';
|
||||
$lang->cache->redis->serializerList['igbinary'] = 'igbinary';
|
||||
|
||||
$lang->cache->redis->tips = new stdClass();
|
||||
$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.';
|
||||
$lang->cache->redis->tips->database = 'Fill in the number of the Redis database, the default is 0.';
|
||||
$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.';
|
||||
|
||||
$lang->cache->tips = new stdClass();
|
||||
$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace after caching is enabled will clear the cache data.';
|
||||
$lang->cache->tips->scope = 'If the cache service is only used by this application, please select "Exclusively for this application", otherwise select "Shared by multiple applications".';
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
$lang->cache->clear = 'Clear Cache';
|
||||
$lang->cache->clearSuccess = 'Cache cleared successfully.';
|
||||
$lang->cache->status = 'Status';
|
||||
$lang->cache->driver = 'Cache Type';
|
||||
$lang->cache->namespace = 'Namespace';
|
||||
$lang->cache->scope = 'Scope';
|
||||
$lang->cache->memory = 'Memory';
|
||||
$lang->cache->usedMemory = 'Total %s, used %s';
|
||||
|
||||
$lang->cache->statusList[1] = 'On';
|
||||
$lang->cache->statusList[0] = 'Off';
|
||||
|
||||
$lang->cache->driverList['apcu'] = 'APCu';
|
||||
$lang->cache->driverList['redis'] = 'Redis';
|
||||
|
||||
$lang->cache->scopeList['private'] = 'Exclusively for this application';
|
||||
$lang->cache->scopeList['shared'] = 'Shared by multiple applications';
|
||||
|
||||
$lang->cache->apcu = new stdClass();
|
||||
$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.';
|
||||
$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.';
|
||||
$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.';
|
||||
|
||||
$lang->cache->redis = new stdClass();
|
||||
$lang->cache->redis->host = 'Redis Host';
|
||||
$lang->cache->redis->port = 'Redis Port';
|
||||
$lang->cache->redis->username = 'Redis User';
|
||||
$lang->cache->redis->password = 'Redis Password';
|
||||
$lang->cache->redis->serializer = 'Redis Serializer';
|
||||
$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.';
|
||||
$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.';
|
||||
$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.';
|
||||
|
||||
$lang->cache->redis->serializerList['php'] = 'PHP Serialize';
|
||||
$lang->cache->redis->serializerList['igbinary'] = 'igbinary';
|
||||
|
||||
$lang->cache->redis->tips = new stdClass();
|
||||
$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.';
|
||||
$lang->cache->redis->tips->database = 'Fill in the number of the Redis database, the default is 0.';
|
||||
$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.';
|
||||
|
||||
$lang->cache->tips = new stdClass();
|
||||
$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace after caching is enabled will clear the cache data.';
|
||||
$lang->cache->tips->scope = 'If the cache service is only used by this application, please select "Exclusively for this application", otherwise select "Shared by multiple applications".';
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
$lang->cache->clear = '清除缓存';
|
||||
$lang->cache->clearSuccess = '清除成功';
|
||||
$lang->cache->status = '缓存状态';
|
||||
$lang->cache->driver = '缓存服务';
|
||||
$lang->cache->namespace = '命名空间';
|
||||
$lang->cache->scope = '服务范围';
|
||||
$lang->cache->memory = '内存使用';
|
||||
$lang->cache->usedMemory = '总计 %s,已使用 %s';
|
||||
|
||||
$lang->cache->statusList[1] = '开启';
|
||||
$lang->cache->statusList[0] = '关闭';
|
||||
|
||||
$lang->cache->driverList['apcu'] = 'APCu';
|
||||
$lang->cache->driverList['redis'] = 'Redis';
|
||||
|
||||
$lang->cache->scopeList['private'] = '本应用独享';
|
||||
$lang->cache->scopeList['shared'] = '多应用共享';
|
||||
|
||||
$lang->cache->apcu = new stdClass();
|
||||
$lang->cache->apcu->notice = '使用 APCu 缓存需要先加载 APCu 扩展。';
|
||||
$lang->cache->apcu->notLoaded = '请加载 APCu 扩展后再开启数据缓存';
|
||||
$lang->cache->apcu->notEnabled = '请启用 apc.enabled 选项后再开启数据缓存';
|
||||
|
||||
$lang->cache->redis = new stdClass();
|
||||
$lang->cache->redis->host = 'Redis 主机';
|
||||
$lang->cache->redis->port = 'Redis 端口';
|
||||
$lang->cache->redis->username = 'Redis 用户名';
|
||||
$lang->cache->redis->password = 'Redis 密码';
|
||||
$lang->cache->redis->database = 'Redis 数据库';
|
||||
$lang->cache->redis->serializer = 'Redis 序列化器';
|
||||
$lang->cache->redis->notice = '使用 Redis 缓存需要先加载 Redis 扩展。';
|
||||
$lang->cache->redis->notLoaded = '请加载 Redis 扩展后再开启数据缓存。';
|
||||
$lang->cache->redis->igbinaryNotLoaded = '请加载 igbinary 扩展后再开启数据缓存。';
|
||||
|
||||
$lang->cache->redis->serializerList['php'] = 'PHP 内置序列化器';
|
||||
$lang->cache->redis->serializerList['igbinary'] = 'igbinary';
|
||||
|
||||
$lang->cache->redis->tips = new stdClass();
|
||||
$lang->cache->redis->tips->host = '填写域名或 IP 地址,无需填写协议和端口号。';
|
||||
$lang->cache->redis->tips->database = '填写 Redis 数据库的编号,默认为 0。';
|
||||
$lang->cache->redis->tips->serializer = '数据需要序列化后缓存。更改序列化器会清空缓存数据。';
|
||||
|
||||
$lang->cache->tips = new stdClass();
|
||||
$lang->cache->tips->namespace = '命名空间用来防止不同应用间缓存数据冲突。启用缓存后更改命名空间会清空缓存数据。';
|
||||
$lang->cache->tips->scope = '如果缓存服务只有本应用使用请选择『本应用独享』,否则选择『多应用共享』。';
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The model file of cache module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Gang Liu <liugang@chandao.com>
|
||||
* @package cache
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
class cacheModel extends model
|
||||
{
|
||||
/**
|
||||
* 清空缓存。
|
||||
* Clear the cache.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
/* Redis 采用遍历删除的方式,所以需要先关闭缓存,清空之后再打开。Redis uses the method of traversing deletion, so you need to turn off the cache first, clear it, and then turn it on. */
|
||||
$needStop = $this->config->cache->driver == 'redis';
|
||||
if($needStop) $this->loadModel('setting')->setItem('system.common.cache.enable', 0);
|
||||
$this->mao->clearCache();
|
||||
if($needStop) $this->setting->setItem('system.common.cache.enable', 1);
|
||||
}
|
||||
}
|
||||
Vendored
+219
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The setting view file of cache module of ZenTaoPMS.
|
||||
* @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Gang Liu <liugang@easycorp.ltd>
|
||||
* @package cache
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
$hiddenCache = $config->cache->enable ? '' : ' hidden';
|
||||
$hiddenApcu = !$hiddenCache && $config->cache->driver == 'apcu' ? '' : ' hidden';
|
||||
$hiddenRedis = !$hiddenCache && $config->cache->driver == 'redis' ? '' : ' hidden';
|
||||
|
||||
formPanel
|
||||
(
|
||||
set::actions
|
||||
([
|
||||
'submit',
|
||||
$config->cache->enable ? ['text' => $lang->cache->clear, 'url' => inlink('ajaxClear'), 'class' => 'secondary ajax-submit'] : null,
|
||||
'cancel'
|
||||
]),
|
||||
on::change('input[name=enable]', 'toggleCache'),
|
||||
on::change('input[name=driver]', 'toggleDriver'),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->cache->status),
|
||||
set::required(),
|
||||
radioList
|
||||
(
|
||||
set::name('enable'),
|
||||
set::items($lang->cache->statusList),
|
||||
set::value($config->cache->enable),
|
||||
set::inline(true)
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('cache' . $hiddenCache),
|
||||
set::label($lang->cache->driver),
|
||||
set::required(),
|
||||
radioList
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::name('driver'),
|
||||
set::items($lang->cache->driverList),
|
||||
set::value($config->cache->driver),
|
||||
set::inline(true)
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('apcu ml-4 mt-1.5' . $hiddenApcu),
|
||||
icon('info text-warning mr-2'),
|
||||
$lang->cache->apcu->notice
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('redis ml-4 mt-1.5' . $hiddenRedis),
|
||||
icon('info text-warning mr-2'),
|
||||
$lang->cache->redis->notice
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('cache' . $hiddenCache),
|
||||
set::label($lang->cache->scope),
|
||||
set::required(),
|
||||
radioList
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::name('scope'),
|
||||
set::items($lang->cache->scopeList),
|
||||
set::value($config->cache->scope),
|
||||
set::inline(true)
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('ml-4 mt-1.5'),
|
||||
icon('info text-warning mr-2'),
|
||||
$lang->cache->tips->scope
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('cache' . $hiddenCache),
|
||||
set::label($lang->cache->namespace),
|
||||
set::required(),
|
||||
input
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::name('namespace'),
|
||||
set::value($config->cache->namespace ?: $config->db->name)
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('ml-4 mt-1.5'),
|
||||
icon('info text-warning mr-2'),
|
||||
$lang->cache->tips->namespace
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('redis' . $hiddenRedis),
|
||||
set::label($lang->cache->redis->host),
|
||||
set::required(),
|
||||
input
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::name('redis[host]'),
|
||||
set::value($config->redis->host)
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('ml-4 mt-1.5'),
|
||||
icon('info text-warning mr-2'),
|
||||
$lang->cache->redis->tips->host
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('redis' . $hiddenRedis),
|
||||
set::label($lang->cache->redis->port),
|
||||
set::required(),
|
||||
input
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::name('redis[port]'),
|
||||
set::value($config->redis->port)
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('redis' . $hiddenRedis),
|
||||
set::label($lang->cache->redis->username),
|
||||
input
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::name('redis[username]'),
|
||||
set::value($config->redis->username)
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('redis' . $hiddenRedis),
|
||||
set::label($lang->cache->redis->password),
|
||||
input
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::name('redis[password]'),
|
||||
set::type('password'),
|
||||
set::value($config->redis->password)
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('redis' . $hiddenRedis),
|
||||
set::label($lang->cache->redis->database),
|
||||
set::required(),
|
||||
input
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::type('number'),
|
||||
set::min(0),
|
||||
set::step(1),
|
||||
set::name('redis[database]'),
|
||||
set::value($config->redis->database)
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('ml-4 mt-1.5'),
|
||||
icon('info text-warning mr-2'),
|
||||
$lang->cache->redis->tips->database
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('redis' . $hiddenRedis),
|
||||
set::label($lang->cache->redis->serializer),
|
||||
radioList
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
set::name('redis[serializer]'),
|
||||
set::items($lang->cache->redis->serializerList),
|
||||
set::value($config->redis->serializer),
|
||||
set::inline(true)
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('ml-4 mt-1.5'),
|
||||
icon('info text-warning mr-2'),
|
||||
$lang->cache->redis->tips->serializer
|
||||
)
|
||||
),
|
||||
$config->cache->enable ? formGroup
|
||||
(
|
||||
setStyle(array('align-items' => 'center')),
|
||||
set::label($lang->cache->memory),
|
||||
div
|
||||
(
|
||||
setClass('w-1/3'),
|
||||
progressBar
|
||||
(
|
||||
set::percent($rate),
|
||||
set::width('100%'),
|
||||
set::color('rgb(var(--color-' . ($rate <= 50 ? 'success' : ($rate <= 80 ? 'warning' : 'danger')) . '-500-rgb))')
|
||||
)
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('flex ml-4 gap-4'),
|
||||
span($rate . '%'),
|
||||
span(sprintf($lang->cache->usedMemory, $total, $used))
|
||||
)
|
||||
) : null
|
||||
);
|
||||
|
||||
render();
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
$lang->common = new stdclass();
|
||||
$lang->cache = new stdclass();
|
||||
$lang->index = new stdclass();
|
||||
$lang->my = new stdclass();
|
||||
$lang->todo = new stdclass();
|
||||
|
||||
@@ -204,6 +204,7 @@ $lang->contactUs->wechat = 'Wechat';
|
||||
|
||||
$lang->common->common = 'Standard Module';
|
||||
$lang->common->story = 'Story';
|
||||
$lang->cache->common = 'Cache';
|
||||
$lang->my->common = 'My';
|
||||
$lang->todo->common = 'Todo';
|
||||
$lang->block->common = 'InfoBlock';
|
||||
@@ -441,7 +442,6 @@ $lang->admin->data = 'Data';
|
||||
$lang->admin->cron = 'Cron';
|
||||
$lang->admin->buildIndex = 'Full Text Search';
|
||||
$lang->admin->tableEngine = 'Table Engine';
|
||||
$lang->admin->cache = 'Cache';
|
||||
|
||||
$lang->convert->importJira = 'Import Jira';
|
||||
|
||||
|
||||
@@ -204,6 +204,7 @@ $lang->contactUs->wechat = 'Wechat';
|
||||
|
||||
$lang->common->common = 'Common Module';
|
||||
$lang->common->story = 'Story';
|
||||
$lang->cache->common = 'Cache';
|
||||
$lang->my->common = 'My';
|
||||
$lang->todo->common = 'Todo';
|
||||
$lang->block->common = 'Block';
|
||||
@@ -441,7 +442,6 @@ $lang->admin->data = 'Data';
|
||||
$lang->admin->cron = 'Cron';
|
||||
$lang->admin->buildIndex = 'Full Text Search';
|
||||
$lang->admin->tableEngine = 'Table Engine';
|
||||
$lang->admin->cache = 'Cache';
|
||||
|
||||
$lang->convert->importJira = 'Import Jira';
|
||||
|
||||
|
||||
@@ -204,6 +204,7 @@ $lang->contactUs->wechat = 'Wechat';
|
||||
|
||||
$lang->common->common = 'Module Commun';
|
||||
$lang->common->story = 'Story';
|
||||
$lang->cache->common = 'Cache';
|
||||
$lang->my->common = 'My';
|
||||
$lang->todo->common = 'Agenda';
|
||||
$lang->block->common = 'Bloc';
|
||||
@@ -441,7 +442,6 @@ $lang->admin->data = 'Data';
|
||||
$lang->admin->cron = 'Cron';
|
||||
$lang->admin->buildIndex = 'Full Text Search';
|
||||
$lang->admin->tableEngine = 'Table Engine';
|
||||
$lang->admin->cache = 'Cache';
|
||||
|
||||
$lang->convert->importJira = 'Import Jira';
|
||||
|
||||
|
||||
@@ -784,6 +784,7 @@ $lang->navGroup->account = 'admin';
|
||||
$lang->navGroup->ops = 'admin';
|
||||
$lang->navGroup->service = 'admin';
|
||||
$lang->navGroup->domain = 'admin';
|
||||
$lang->navGroup->cache = 'admin';
|
||||
|
||||
$lang->navGroup->aiapp = 'aiapp';
|
||||
|
||||
|
||||
@@ -204,6 +204,7 @@ $lang->contactUs->wechat = '微信';
|
||||
|
||||
$lang->common->common = '公有模块';
|
||||
$lang->common->story = '需求';
|
||||
$lang->cache->common = '缓存';
|
||||
$lang->my->common = '地盘';
|
||||
$lang->todo->common = '待办';
|
||||
$lang->block->common = '区块';
|
||||
@@ -441,7 +442,6 @@ $lang->admin->data = '数据';
|
||||
$lang->admin->cron = '定时';
|
||||
$lang->admin->buildIndex = '重建索引';
|
||||
$lang->admin->tableEngine = '表引擎';
|
||||
$lang->admin->cache = '缓存';
|
||||
|
||||
$lang->convert->importJira = '导入Jira数据';
|
||||
|
||||
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
class viewPage extends page
|
||||
{
|
||||
public function __construct($webdriver)
|
||||
{
|
||||
parent::__construct($webdriver);
|
||||
|
||||
$xpath = array(
|
||||
/*需求详情页*/
|
||||
'status' => "//*[@class='tab-content']/div/div/div[6]/div[2]/span",
|
||||
/*激活弹窗的激活按钮*/
|
||||
'activate' => "//*[@type='submit']"
|
||||
);
|
||||
$this->dom->xpath = array_merge($this->dom->xpath, $xpath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/ui.php';
|
||||
class viewTester extends tester
|
||||
{
|
||||
/**
|
||||
* 检查执行概况页面执行基础信息。
|
||||
* Check the basic information of the execution.
|
||||
*
|
||||
* @paran array $basic
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function checkBasic($basic)
|
||||
{
|
||||
$form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution');
|
||||
if($form->dom->executionName->getText() != $basic['executionName']) return $this->failed('执行名称不正确');
|
||||
if($form->dom->programName->getText() != $basic['programName']) return $this->failed('项目集名称不正确');
|
||||
if($form->dom->projectName->getText() != $basic['projectName']) return $this->failed('项目名称不正确');
|
||||
if($form->dom->storyNum->getText() != $basic['storyNum']) return $this->failed('需求数不正确');
|
||||
if($form->dom->taskNum->getText() != $basic['taskNum']) return $this->failed('任务数不正确');
|
||||
if($form->dom->bugNum->getText() != $basic['bugNum']) return $this->failed('Bug数不正确');
|
||||
return $this->success('执行基础信息正确');
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查执行概况页面产品信息。
|
||||
* Check the product information of the execution.
|
||||
*
|
||||
* @param string $product
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function checkProduct($product)
|
||||
{
|
||||
$form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution');
|
||||
if($form->dom->linckedProducta->getText() != $product) return $this->failed('关联产品不正确');
|
||||
$form->dom->moreProducts->click();
|
||||
$form->wait(1);
|
||||
$url = $this->response();
|
||||
if($url['module'] != 'execution') return $this->failed('页面跳转后module不正确');
|
||||
if($url['method'] != 'manageproducts') return $this->failed('页面跳转后method不正确');
|
||||
return $this->success('产品信息正确');
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查执行概况页面团队成员信息。
|
||||
* Check the team members of the execution.
|
||||
*
|
||||
* @param string $membera
|
||||
* @param string $memberb
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function checkMember($membera, $memberb)
|
||||
{
|
||||
$form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution');
|
||||
if($form->dom->teamMembera->getText() != $membera) return $this->failed('团队成员不正确');
|
||||
if($form->dom->teamMemberb->getText() != $memberb) return $this->failed('团队成员不正确');
|
||||
$form->dom->moreTeamMembers->click();
|
||||
$form->wait(1);
|
||||
$url = $this->response();
|
||||
if($url['module'] != 'execution') return $this->failed('点击更多按钮,页面跳转后module不正确');
|
||||
if($url['method'] != 'team') return $this->failed('点击更多按钮,页面跳转后method不正确');
|
||||
$form->dom->btn($this->lang->overview)->click();
|
||||
$form->wait(1);
|
||||
$form->dom->manageMembers->click();
|
||||
$form->wait(1);
|
||||
$url = $this->response();
|
||||
if($url['module'] != 'execution') return $this->failed('点击管理按钮,页面跳转后module不正确');
|
||||
if($url['method'] != 'manageMembers') return $this->failed('点击管理按钮,页面跳转后method不正确');
|
||||
return $this->success('团队成员信息正确');
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查执行概况页面文档库信息。
|
||||
* Check the doclib information of the execution.
|
||||
*
|
||||
* @param string $doclaba
|
||||
* @param string $doclibb
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function checkDoclib($doclaba, $doclibb)
|
||||
{
|
||||
$form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution');
|
||||
if($form->dom->docliba->getText() != $doclaba) return $this->failed('文档库名称不正确');
|
||||
if($form->dom->doclibb->getText() != $doclibb) return $this->failed('文档库名称不正确');
|
||||
$form->dom->moreDoclibs->click();
|
||||
$form->wait(1);
|
||||
$url = $this->response();
|
||||
if($url['module'] != 'execution') return $this->failed('点击更多按钮,页面跳转后module不正确');
|
||||
if($url['method'] != 'doc') return $this->failed('点击更多按钮,页面跳转后method不正确');
|
||||
$form->dom->btn($this->lang->settings)->click();
|
||||
$form->wait(1);
|
||||
$form->dom->createDoclib->click();
|
||||
$form->wait(1);
|
||||
if(!is_object($form->dom->doclibModal)) return $this->failed('创建文档库模态框不存在');
|
||||
return $this->success('文档库信息正确');
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,13 @@ class viewPage extends page
|
||||
$xpath = array(
|
||||
/* 执行概况页元素 */
|
||||
'executionName' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[1]/div",
|
||||
'programName' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[2]/div[1]/div/a",
|
||||
'projectName' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[2]/div[2]/a",
|
||||
'status' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[1]/span[2]",
|
||||
'acl' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[1]/span[3]",
|
||||
'storyNum' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/div",
|
||||
'taskNum' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[2]/div",
|
||||
'bugNum' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[3]/div",
|
||||
'plannedBegin' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[1]/span[2]",
|
||||
'plannedEnd' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[2]/span[2]",
|
||||
'realBeganView' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[3]/span[2]",
|
||||
@@ -17,6 +22,16 @@ class viewPage extends page
|
||||
'linckedProducta' => "//*[@id='mainContent']/div[2]/div[1]/div/table[1]/tbody/tr[1]",
|
||||
'linckedProductb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[1]/tbody/tr[2]",
|
||||
'productsNev' => "//*[@data-id='products']",
|
||||
'moreProducts' => "//*[@id='mainContent']/div[2]/div[1]/div/table[1]/thead/tr/th[1]/div/a/span",
|
||||
'teamMembera' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[1]/span[1]",
|
||||
'teamMemberb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[2]/span[1]",
|
||||
'moreTeamMembers' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/thead/tr/th/div/a/span",
|
||||
'manageMembers' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/a",
|
||||
'docliba' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[1]/a",
|
||||
'doclibb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[2]/a",
|
||||
'moreDoclibs' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/thead/tr/th/div/a/span",
|
||||
'createDoclib' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[3]/a/span",
|
||||
'doclibModal' => "//*[@class='modal-dialog']",
|
||||
/* 编辑执行弹窗中元素 */
|
||||
'products' => "//*[@name='products[0]']",
|
||||
'productsTip' => "//*[@id='products[0]Tip']",
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
title=概况页面
|
||||
timeout=0
|
||||
cid=1
|
||||
*/
|
||||
|
||||
chdir(__DIR__);
|
||||
include '../lib/view.ui.class.php';
|
||||
|
||||
$product = zenData('product');
|
||||
$product->id->range('1-100');
|
||||
$product->name->range('产品1, 产品2');
|
||||
$product->type->range('normal');
|
||||
$product->gen(2);
|
||||
|
||||
$project = zenData('project');
|
||||
$project->id->range('1-100');
|
||||
$project->project->range('0, 0, 2');
|
||||
$project->model->range('[], scrum, []');
|
||||
$project->type->range('program, project, sprint');
|
||||
$project->auth->range('[], extend, []');
|
||||
$project->storyType->range('[], story, []');
|
||||
$project->parent->range('0, 1, 2');
|
||||
$project->path->range('`,1,`, `,1,2,`, `,1,2,3`');
|
||||
$project->grade->range('1, 2, 1');
|
||||
$project->name->range('项目集, 项目, 执行');
|
||||
$project->openedBy->range('user1');
|
||||
$project->acl->range('open');
|
||||
$project->status->range('doing');
|
||||
$project->gen(3);
|
||||
|
||||
$projectproduct = zenData('projectproduct');
|
||||
$projectproduct->project->range('2{2}, 3');
|
||||
$projectproduct->product->range('1, 2, 1');
|
||||
$projectproduct->gen(3);
|
||||
|
||||
$user = zenData('user');
|
||||
$user->id->range('1-100');
|
||||
$user->dept->range('0');
|
||||
$user->account->range('admin, user1, user2');
|
||||
$user->realname->range('admin, USER1, USER2');
|
||||
$user->password->range($config->uitest->defaultPassword)->format('md5');
|
||||
$user->gen(3);
|
||||
|
||||
$team = zenData('team');
|
||||
$team->id->range('1-100');
|
||||
$team->root->range('2{3}, 3{2}');
|
||||
$team->type->range('project{3}, execution{2}');
|
||||
$team->account->range('admin, user1, user2, admin, user1');
|
||||
$team->gen(5);
|
||||
|
||||
$story = zenData('story');
|
||||
$story->id->range('1-100');
|
||||
$story->parent->range('0');
|
||||
$story->isParent->range('0');
|
||||
$story->root->range('1-100');
|
||||
$story->path->range('`,1,`, `,2,`, `,3,`');
|
||||
$story->grade->range('1');
|
||||
$story->product->range('1');
|
||||
$story->module->range('0');
|
||||
$story->plan->range('0');
|
||||
$story->title->range('1-100');
|
||||
$story->type->range('story');
|
||||
$story->estimate->range('0');
|
||||
$story->status->range('active');
|
||||
$story->stage->range('projected');
|
||||
$story->assignedTo->range('[]');
|
||||
$story->version->range('1');
|
||||
$story->gen(3);
|
||||
|
||||
$storySpec = zenData('storyspec');
|
||||
$storySpec->story->range('1-15');
|
||||
$storySpec->version->range('1');
|
||||
$storySpec->title->range('1-15');
|
||||
$storySpec->gen(3);
|
||||
|
||||
$projectStory = zenData('projectstory');
|
||||
$projectStory->project->range('2{3}, 3{2}');
|
||||
$projectStory->product->range('1');
|
||||
$projectStory->branch->range('0');
|
||||
$projectStory->story->range('1-3, 1, 2');
|
||||
$projectStory->version->range('1');
|
||||
$projectStory->gen(5);
|
||||
|
||||
$task = zenData('task');
|
||||
$task->id->range('1-100');
|
||||
$task->project->range('2');
|
||||
$task->execution->range('3');
|
||||
$task->story->range('0');
|
||||
$task->storyVersion->range('1');
|
||||
$task->name->range('1-100');
|
||||
$task->type->range('devel');
|
||||
$task->deadline->range(' (-5D)-(-4D):1D, []{11}')->type('timestamp')->format('YY/MM/DD');
|
||||
$task->status->range('wait{3}, doing{3}, done{2}, cancel, closed{3}');
|
||||
$task->openedBy->range('admin{6}, user1{6}');
|
||||
$task->assignedTo->range('[]{3}, user1{4}, admin{2}, closed{3}');
|
||||
$task->finishedBy->range('[]{6}, admin, user1, [], admin, user1{2}');
|
||||
$task->canceledBy->range('[]{8}, admin, []{3}');
|
||||
$task->closedBy->range('[]{9}, admin{2}, user1');
|
||||
$task->deleted->range('0{11}, 1');
|
||||
$task->gen(12);
|
||||
|
||||
$taskSpec = zenData('taskspec');
|
||||
$taskSpec->task->range('1-100');
|
||||
$taskSpec->version->range('0');
|
||||
$taskSpec->name->range('1-100');
|
||||
$taskSpec->gen(12);
|
||||
|
||||
$bug = zenData('bug');
|
||||
$bug->id->range('1-100');
|
||||
$bug->project->range('2{12}, 0{100}');
|
||||
$bug->product->range('1{8}, 2{100}');
|
||||
$bug->execution->range('3{5}, 0{100}');
|
||||
$bug->title->range('1-100');
|
||||
$bug->status->range('active{3}, resolved{3}, closed{100}');
|
||||
$bug->assignedTo->range('[]');
|
||||
$bug->gen(12);
|
||||
|
||||
$doclib = zenData('doclib');
|
||||
$doclib->id->range('1-100');
|
||||
$doclib->type->range('project, execution{2}');
|
||||
$doclib->product->range('0');
|
||||
$doclib->project->range('2');
|
||||
$doclib->execution->range('0, 3, 3');
|
||||
$doclib->name->range('项目库, 执行库1, 执行库2');
|
||||
$doclib->gen(3);
|
||||
|
||||
$tester = new viewTester();
|
||||
$tester->login();
|
||||
|
||||
$basic = array(
|
||||
'executionName' => '执行',
|
||||
'programName' => '项目集',
|
||||
'projectName' => '项目',
|
||||
'storyNum' => '2',
|
||||
'taskNum' => '11',
|
||||
'bugNum' => '5',
|
||||
);
|
||||
|
||||
r($tester->checkBasic($basic)) && p('status,message') && e('SUCCESS,执行基础信息正确');
|
||||
r($tester->checkProduct('产品1')) && p('status,message') && e('SUCCESS,产品信息正确');
|
||||
r($tester->checkMember('admin', 'USER1')) && p('status,message') && e('SUCCESS,团队成员信息正确');
|
||||
r($tester->checkDoclib('执行库1', '执行库2')) && p('status,message') && e('SUCCESS,文档库信息正确');
|
||||
$tester->closeBrowser();
|
||||
@@ -601,6 +601,6 @@ $lang->group->package->deleteJob = 'Delete PipeLine';
|
||||
$lang->group->package->browseApplication = 'Application List';
|
||||
$lang->group->package->mangeApplication = 'Manage Application';
|
||||
$lang->group->package->trainPracticeLib = 'Practice Library';
|
||||
$lang->group->package->other = 'Other';
|
||||
$lang->group->package->application = 'Manage Application';
|
||||
|
||||
include (dirname(__FILE__) . '/resource.php');
|
||||
|
||||
@@ -601,5 +601,6 @@ $lang->group->package->deleteJob = 'Delete PipeLine';
|
||||
$lang->group->package->browseApplication = 'Application List';
|
||||
$lang->group->package->mangeApplication = 'Manage Application';
|
||||
$lang->group->package->trainPracticeLib = 'Practice Library';
|
||||
$lang->group->package->application = 'Manage Application';
|
||||
|
||||
include (dirname(__FILE__) . '/resource.php');
|
||||
|
||||
@@ -601,5 +601,6 @@ $lang->group->package->deleteJob = 'Delete PipeLine';
|
||||
$lang->group->package->browseApplication = 'Application List';
|
||||
$lang->group->package->mangeApplication = 'Manage Application';
|
||||
$lang->group->package->trainPracticeLib = 'Practice Library';
|
||||
$lang->group->package->application = 'Manage Application';
|
||||
|
||||
include (dirname(__FILE__) . '/resource.php');
|
||||
|
||||
@@ -1495,6 +1495,12 @@ $lang->resource->system->dashboard = 'dashboard';
|
||||
$lang->resource->system->dblist = 'dbList';
|
||||
$lang->resource->system->configdomain = 'configDomain';
|
||||
$lang->resource->system->ossview = 'ossView';
|
||||
$lang->resource->system->browse = 'browse';
|
||||
$lang->resource->system->create = 'create';
|
||||
$lang->resource->system->edit = 'edit';
|
||||
$lang->resource->system->delete = 'delete';
|
||||
$lang->resource->system->active = 'active';
|
||||
$lang->resource->system->inactive = 'inactive';
|
||||
|
||||
$lang->resource->ops = new stdclass();
|
||||
$lang->resource->ops->provider = 'provider';
|
||||
|
||||
@@ -601,5 +601,6 @@ $lang->group->package->deleteJob = '删除流水线';
|
||||
$lang->group->package->browseApplication = '浏览应用';
|
||||
$lang->group->package->mangeApplication = '管理应用';
|
||||
$lang->group->package->trainPracticeLib = '实践库';
|
||||
$lang->group->package->application = '管理应用';
|
||||
|
||||
include (dirname(__FILE__) . '/resource.php');
|
||||
|
||||
@@ -941,6 +941,17 @@ $config->group->package->releaseNotify->subset = 'release';
|
||||
$config->group->package->releaseNotify->privs = array();
|
||||
$config->group->package->releaseNotify->privs['release-notify'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 70, 'depend' => array('release-view'), 'recommend' => array('release-create', 'release-edit', 'release-linkBug', 'release-linkStory', 'release-unlinkBug', 'release-unlinkStory'));
|
||||
|
||||
$config->group->package->application = new stdclass();
|
||||
$config->group->package->application->order = 30;
|
||||
$config->group->package->application->subset = 'release';
|
||||
$config->group->package->application->privs = array();
|
||||
$config->group->package->application->privs['system-browse'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('release-browse'), 'recommend' => array('system-create', 'system-edit', 'system-active', 'system-inactive', 'system-delete'));
|
||||
$config->group->package->application->privs['system-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-edit', 'system-active', 'system-inactive', 'system-delete'));
|
||||
$config->group->package->application->privs['system-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-create', 'system-active', 'system-inactive', 'system-delete'));
|
||||
$config->group->package->application->privs['system-active'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-create', 'system-edit', 'system-inactive', 'system-delete'));
|
||||
$config->group->package->application->privs['system-inactive'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-create', 'system-edit', 'system-active', 'system-delete'));
|
||||
$config->group->package->application->privs['system-delete'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-create', 'system-edit', 'system-active', 'system-inactive'));
|
||||
|
||||
$config->group->package->projectPlan = new stdclass();
|
||||
$config->group->package->projectPlan->order = 5;
|
||||
$config->group->package->projectPlan->subset = 'projectplan';
|
||||
|
||||
@@ -381,17 +381,7 @@ class install extends control
|
||||
|
||||
$this->install->importBIData();
|
||||
|
||||
$this->install->enableDaoCache();
|
||||
|
||||
/**
|
||||
* 安装完成后清除缓存。
|
||||
* 通过 dao 的 exec 方法更新数据库会自动更新缓存。
|
||||
* 通过 dbh 执行 sql 语句的方式更新数据库不会自动更新缓存,应该在清除缓存之前执行,否则可能导致缓存命中但数据已过期。
|
||||
* Clear the cache after the installation is complete.
|
||||
* Update the database through the exec method of dao will automatically update the cache.
|
||||
* Update the database by executing sql statements through dbh will not automatically update the cache, should be executed before clearing the cache, otherwise it may cause cache hits but the data has expired.
|
||||
*/
|
||||
$this->dao->clearCache();
|
||||
$this->install->enableCache();
|
||||
|
||||
$skipApp = (string)getenv('ZT_SKIP_DEVOPS_INIT');
|
||||
$link = ($this->config->inQuickon && (!$skipApp || $skipApp == 'false')) ? inlink('app') : inlink('step6');
|
||||
|
||||
+14
-11
@@ -372,7 +372,7 @@ class installModel extends model
|
||||
$config->section = 'global';
|
||||
$config->key = 'showDemoUsers';
|
||||
$config->value = '1';
|
||||
$this->dao->replace(TABLE_CONFIG)->data($config)->exec();
|
||||
$this->dao->insert(TABLE_CONFIG)->data($config)->exec();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -423,24 +423,27 @@ class installModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启 dao 缓存。
|
||||
* Enable dao cache.
|
||||
* 开启缓存。
|
||||
* Enable cache.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function enableDaoCache(): bool
|
||||
public function enableCache(): bool
|
||||
{
|
||||
if(!helper::isAPCuEnabled()) return false;
|
||||
|
||||
$cache = new stdclass();
|
||||
$cache->owner = 'system';
|
||||
$cache->module = 'common';
|
||||
$cache->section = 'global';
|
||||
$cache->key = 'cache';
|
||||
$cache->value = '{"dao":{"enable":"1"}}';
|
||||
$this->dao->replace(TABLE_CONFIG)->data($cache)->exec();
|
||||
$cache->status = true;
|
||||
$cache->driver = 'apcu';
|
||||
$cache->scope = 'shared';
|
||||
$cache->namespace = $this->config->db->name;
|
||||
|
||||
return !dao::isError();
|
||||
$this->loadModel('setting')->setItems('system.common.cache', $cache);
|
||||
if(dao::isError()) return false;
|
||||
|
||||
$this->mao->clearCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,16 @@ if(!isset($config->backup))
|
||||
|
||||
$config->backup->dtable->fieldList['actions']['type'] = 'actions';
|
||||
$config->backup->dtable->fieldList['actions']['width'] = '100';
|
||||
$config->backup->dtable->fieldList['actions']['menu'] = array('restore');
|
||||
$config->backup->dtable->fieldList['actions']['menu'] = array('restore', 'delete');
|
||||
|
||||
$config->backup->dtable->fieldList['actions']['list']['restore']['icon'] = 'icon-restart';
|
||||
$config->backup->dtable->fieldList['actions']['list']['restore']['hint'] = $lang->instance->restore->common;
|
||||
$config->backup->dtable->fieldList['actions']['list']['restore']['url'] = array('module' => 'instance', 'method' => 'ajaxRestore', 'params' => 'instanceID={instanceId}&backupName={name}');
|
||||
$config->backup->dtable->fieldList['actions']['list']['restore']['className'] = 'ajax-submit';
|
||||
|
||||
$config->backup->dtable->fieldList['actions']['list']['delete']['icon'] = 'icon-trash';
|
||||
$config->backup->dtable->fieldList['actions']['list']['delete']['hint'] = $lang->instance->backup->delete;
|
||||
$config->backup->dtable->fieldList['actions']['list']['delete']['url'] = array('module' => 'instance', 'method' => 'ajaxDeleteBackup', 'params' => 'instanceID={instanceId}&backupName={name}');
|
||||
$config->backup->dtable->fieldList['actions']['list']['delete']['className'] = 'ajax-submit';
|
||||
$config->backup->dtable->fieldList['actions']['list']['delete']['data-confirm'] = array('message' => $lang->instance->backup->confirmDeleteTip, 'icon' => 'icon-exclamation-sign', 'iconClass' => 'warning-pale rounded-full icon-2x');
|
||||
}
|
||||
|
||||
@@ -652,4 +652,24 @@ class instance extends control
|
||||
}
|
||||
return $this->send(array('result' => 'success', 'message' => zget($this->lang->instance->notices, 'backupSuccess')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete backup by ajax.
|
||||
* 删除备份。
|
||||
* @param int $backupID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxDeleteBackup(string $instanceID, string $backupName)
|
||||
{
|
||||
$instance = $this->instance->getByID((int)$instanceID);
|
||||
if(empty($instance)) $this->send(array('result' => 'success', 'message' => $this->lang->instance->instanceNotExists));
|
||||
|
||||
$success = $this->instance->deleteBackup($instance, $backupName);
|
||||
if(!$success) return $this->send(array('result' => 'fail', 'message' => zget($this->lang->instance->notices, 'deleteFail')));
|
||||
|
||||
$this->action->create('instance', $instance->id, 'manualdeletebackup', '', json_encode(array('result' => 'success')));
|
||||
$locate = $this->createLink('instance', 'view', 'id=' . $instanceID);
|
||||
return $this->send(array('result' => 'success', 'load' => array('alert' => zget($this->lang->instance->notices, 'deleteSuccess'), 'locate' => $locate, 'closeModal' => true)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,8 @@ $lang->instance->backup->keepBackupBySystem = 'The system will only delete expi
|
||||
$lang->instance->backup->backupSize = 'Size';
|
||||
$lang->instance->backup->confirmTip = 'Are you sure you want to back up?';
|
||||
$lang->instance->backup->cronRemark = 'DevOps service automatic backup task';
|
||||
$lang->instance->backup->backupSettingsTips = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.';
|
||||
$lang->instance->backup->backupSettingsTip = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.';
|
||||
$lang->instance->backup->confirmDeleteTip = 'Are you sure you want to delete the backup?';
|
||||
|
||||
$lang->instance->backup->cycleList[1] = 'Daily';
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ $lang->instance->backup->volSpentSeconds = 'Time taken (seconds)';
|
||||
$lang->instance->backup->volSize = 'Size';
|
||||
$lang->instance->backup->lastRestore = 'Last rollback';
|
||||
$lang->instance->backup->restoreDate = 'Restore time';
|
||||
$lang->instance->backup->latestBackupAt = 'Last backup time';
|
||||
$lang->instance->backup->latestBackupAt = 'Last backup time';
|
||||
$lang->instance->backup->backupBeforeRestore = 'We recommend that you backup before rolling back!';
|
||||
$lang->instance->backup->enableAutoBackup = 'Enable automatic backup';
|
||||
$lang->instance->backup->autoBackup = 'Automatic backup';
|
||||
@@ -132,7 +132,8 @@ $lang->instance->backup->keepBackupBySystem = 'The system will only delete expi
|
||||
$lang->instance->backup->backupSize = 'Size';
|
||||
$lang->instance->backup->confirmTip = 'Are you sure you want to back up?';
|
||||
$lang->instance->backup->cronRemark = 'DevOps service automatic backup task';
|
||||
$lang->instance->backup->backupSettingsTips = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.';
|
||||
$lang->instance->backup->backupSettingsTip = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.';
|
||||
$lang->instance->backup->confirmDeleteTip = 'Are you sure you want to delete the backup?';
|
||||
|
||||
$lang->instance->backup->cycleList[1] = 'Daily';
|
||||
|
||||
|
||||
@@ -132,7 +132,8 @@ $lang->instance->backup->keepBackupBySystem = 'The system will only delete expi
|
||||
$lang->instance->backup->backupSize = 'Size';
|
||||
$lang->instance->backup->confirmTip = 'Are you sure you want to back up?';
|
||||
$lang->instance->backup->cronRemark = 'DevOps service automatic backup task';
|
||||
$lang->instance->backup->backupSettingsTips = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.';
|
||||
$lang->instance->backup->backupSettingsTip = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.';
|
||||
$lang->instance->backup->confirmDeleteTip = 'Are you sure you want to delete the backup?';
|
||||
|
||||
$lang->instance->backup->cycleList[1] = 'Daily';
|
||||
|
||||
|
||||
@@ -132,7 +132,8 @@ $lang->instance->backup->keepBackupBySystem = '备份数据超过1条时系统
|
||||
$lang->instance->backup->backupSize = '大小';
|
||||
$lang->instance->backup->confirmTip = '确认要备份吗?';
|
||||
$lang->instance->backup->cronRemark = 'Devops服务自动备份任务';
|
||||
$lang->instance->backup->backupSettingsTips = '系统会删除超过设置天数的备份,但会保留至少一个有效备份。';
|
||||
$lang->instance->backup->backupSettingsTip = '系统会删除超过设置天数的备份,但会保留至少一个有效备份。';
|
||||
$lang->instance->backup->confirmDeleteTip = '确认要删除备份吗?';
|
||||
|
||||
$lang->instance->backup->cycleList[1] = '每日';
|
||||
|
||||
|
||||
@@ -1379,4 +1379,17 @@ class instanceModel extends model
|
||||
if(count($deleteData) > 0) $this->action->create('instance', $instance->id, 'deleteexpiredbackup', '', json_encode(array('result' => 'success', 'data' =>$deleteData)));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete backup.
|
||||
* 删除备份。
|
||||
* @param object $instance
|
||||
* @param string $backupName
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteBackup($instance, $backupName)
|
||||
{
|
||||
return $this->cne->deleteBackup($instance, $backupName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ formPanel
|
||||
set::label($this->lang->instance->backup->keepDays),
|
||||
set::control('input'),
|
||||
set::value((int)$instance->backupKeepDays),
|
||||
span($this->lang->instance->backup->backupSettingsTips, set::className('text-warning inline-block mt-2'))
|
||||
span($this->lang->instance->backup->backupSettingsTip, set::className('text-warning inline-block mt-2'))
|
||||
),
|
||||
),
|
||||
formRow
|
||||
|
||||
+1
-1
@@ -237,7 +237,7 @@ class mailTao extends mailModel
|
||||
$objectModel = $this->loadModel($objectType);
|
||||
if(!$objectModel) return false;
|
||||
|
||||
if(strpos(',story,meeting,review,', ",{$objectType},") !== false) return $objectModel->getToAndCcList($object, $action->action);
|
||||
if(strpos(',story,meeting,review,deploy,', ",{$objectType},") !== false) return $objectModel->getToAndCcList($object, $action->action);
|
||||
if($objectType == 'ticket') return $objectModel->getToAndCcList($object, $action);
|
||||
return $objectModel->getToAndCcList($object);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The model file of mark module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(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 Xinzhi Qi <qixinzhi@chandao.com>
|
||||
* @package mail
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
<?php
|
||||
class markModel extends model
|
||||
{
|
||||
/**
|
||||
* 获取需要标记的对象。
|
||||
* Get needed mark sobjects.
|
||||
*
|
||||
* @param array $objectIDs
|
||||
* @param string $objectType
|
||||
* @param string $version
|
||||
* @param string $mark
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getNeededMarks(array $objectIDs, string $objectType, string $version, string $mark): array
|
||||
{
|
||||
return $this->dao->select('objectID, version')->from(TABLE_MARK)
|
||||
->where('objectType')->eq($objectType)
|
||||
->andWhere('objectID')->in($objectIDs)
|
||||
->beginIF($version != 'all')->andWhere('version')->eq($version)->fi()
|
||||
->andWhere('account')->eq($this->app->user->account)
|
||||
->andWhere('mark')->eq($mark)
|
||||
->fetchAll();
|
||||
}
|
||||
|
||||
public function getMarks(array $objects, string $objectType, string $mark): array
|
||||
{
|
||||
$objectIDs = array_column($objects, 'id');
|
||||
$marks = $this->getNeededMarks($objectIDs, $objectType, 'all', $mark);
|
||||
|
||||
foreach($objects as $object)
|
||||
{
|
||||
$objectMarks = array_filter($marks, function($mark) use($object)
|
||||
{
|
||||
return $mark->objectID == $object->id && $mark->version == $object->version;
|
||||
});
|
||||
|
||||
$object->mark = !empty($objectMarks);
|
||||
}
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对象的标记。
|
||||
* Set object marks.
|
||||
*
|
||||
* @param array $objectIDs
|
||||
* @param string $objectType
|
||||
* @param string $version
|
||||
* @param string $mark
|
||||
* @param string $extra
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function setMark(array $objectIDs, string $objectType, string $version, string $mark, string $extra = ''): bool
|
||||
{
|
||||
$data = new stdclass();
|
||||
$data->objectType = $objectType;
|
||||
$data->version = $version;
|
||||
$data->account = $this->app->user->account;
|
||||
$data->mark = $mark;
|
||||
$data->extra = $extra;
|
||||
$data->date = helper::now();
|
||||
|
||||
foreach($objectIDs as $objectID)
|
||||
{
|
||||
$data->objectID = $objectID;
|
||||
$this->dao->insert(TABLE_MARK)->data($data)->autocheck()->exec();
|
||||
}
|
||||
|
||||
return dao::isError();
|
||||
}
|
||||
}
|
||||
@@ -271,7 +271,7 @@ class messageModel extends model
|
||||
if($toList == 'closed') $toList = '';
|
||||
if($objectType == 'feedback' && $object->status == 'replied') $toList = ',' . $object->openedBy . ',';
|
||||
|
||||
if(strpos(',story,epic,requirement,ticket,review,', ",{$objectType},") !== false && $actionID)
|
||||
if(strpos(',story,epic,requirement,ticket,review,deploy,', ",{$objectType},") !== false && $actionID)
|
||||
{
|
||||
$action = $this->loadModel('action')->getById($actionID);
|
||||
list($toList, $ccList) = $this->loadModel($objectType)->getToAndCcList($object, $action->action);
|
||||
|
||||
@@ -26,6 +26,7 @@ $lang->pivot->exportRange = 'Export Range';
|
||||
$lang->pivot->story = 'Story';
|
||||
$lang->pivot->clear = 'Clear';
|
||||
$lang->pivot->keep = 'Keep';
|
||||
$lang->pivot->new = 'New';
|
||||
|
||||
$lang->pivot->accessDenied = 'You do not have access to this pivot';
|
||||
$lang->pivot->acl = 'Access Control';
|
||||
@@ -490,3 +491,6 @@ $lang->pivot->drill->productName = "Product Name";
|
||||
$lang->pivot->drill->activatedBug = "Activated Bug Count";
|
||||
$lang->pivot->drill->auto = "Auto";
|
||||
$lang->pivot->drill->designChangedTip = 'Setting columns field change, please check';
|
||||
|
||||
$lang->pivot->tipNewVersion = 'Has New Version!';
|
||||
$lang->pivot->checkNewVersion = 'Click to check';
|
||||
|
||||
@@ -26,6 +26,7 @@ $lang->pivot->exportRange = 'Export Range';
|
||||
$lang->pivot->story = 'Story';
|
||||
$lang->pivot->clear = 'Clear';
|
||||
$lang->pivot->keep = 'Keep';
|
||||
$lang->pivot->new = 'New';
|
||||
|
||||
$lang->pivot->accessDenied = 'You do not have access to this pivot';
|
||||
$lang->pivot->acl = 'Access Control';
|
||||
@@ -490,3 +491,6 @@ $lang->pivot->drill->productName = "Product Name";
|
||||
$lang->pivot->drill->activatedBug = "Activated Bug Count";
|
||||
$lang->pivot->drill->auto = "Auto";
|
||||
$lang->pivot->drill->designChangedTip = 'Setting columns field change, please check';
|
||||
|
||||
$lang->pivot->tipNewVersion = 'Has New Version!';
|
||||
$lang->pivot->checkNewVersion = 'Click to check';
|
||||
|
||||
@@ -26,6 +26,7 @@ $lang->pivot->exportRange = 'Export Range';
|
||||
$lang->pivot->story = 'Story';
|
||||
$lang->pivot->clear = 'Clear';
|
||||
$lang->pivot->keep = 'Keep';
|
||||
$lang->pivot->new = 'New';
|
||||
|
||||
$lang->pivot->accessDenied = 'You do not have access to this pivot';
|
||||
$lang->pivot->acl = 'Access Control';
|
||||
@@ -490,3 +491,6 @@ $lang->pivot->drill->productName = "Product Name";
|
||||
$lang->pivot->drill->activatedBug = "Activated Bug Count";
|
||||
$lang->pivot->drill->auto = "Auto";
|
||||
$lang->pivot->drill->designChangedTip = 'Setting columns field change, please check';
|
||||
|
||||
$lang->pivot->tipNewVersion = 'Has New Version!';
|
||||
$lang->pivot->checkNewVersion = 'Click to check';
|
||||
|
||||
@@ -26,6 +26,7 @@ $lang->pivot->exportRange = '导出范围';
|
||||
$lang->pivot->story = '需求';
|
||||
$lang->pivot->clear = '清空';
|
||||
$lang->pivot->keep = '保留设计';
|
||||
$lang->pivot->new = '新';
|
||||
|
||||
$lang->pivot->accessDenied = '您无权访问该透视表';
|
||||
$lang->pivot->acl = '访问控制';
|
||||
@@ -490,3 +491,6 @@ $lang->pivot->drill->productName = "产品名称";
|
||||
$lang->pivot->drill->activatedBug = "激活的Bug数";
|
||||
$lang->pivot->drill->auto = "自动";
|
||||
$lang->pivot->drill->designChangedTip = '设计变更,请检查';
|
||||
|
||||
$lang->pivot->tipNewVersion = '有新版本更新';
|
||||
$lang->pivot->checkNewVersion = '点击查看';
|
||||
|
||||
@@ -12,10 +12,14 @@ class pivotTao extends pivotModel
|
||||
*/
|
||||
protected function fetchPivot(int $id): object|bool
|
||||
{
|
||||
return $this->dao->select('*')->from(TABLE_PIVOT)
|
||||
->where('id')->eq($id)
|
||||
->andWhere('deleted')->eq('0')
|
||||
->fetch();
|
||||
$pivot = $this->dao->select('*')->from(TABLE_PIVOT)->where('id')->eq($id)->andWhere('deleted')->eq('0')->fetch();
|
||||
if(!$pivot) return false;
|
||||
|
||||
$specData = $this->dao->select('*')->from(TABLE_PIVOTSPEC)->where('pivot')->eq($id)->andWhere('version')->eq($pivot->version)->fetch();
|
||||
if(!$specData) return $pivot;
|
||||
|
||||
foreach($specData as $specKey => $specValue) $pivot->$specKey = $specValue;
|
||||
return $pivot;
|
||||
}
|
||||
/**
|
||||
* 获取产品列表。
|
||||
|
||||
@@ -83,14 +83,26 @@ $generateData = function() use ($lang, $pivotName, $pivot, $data, $configs, $sho
|
||||
set::shadow(false),
|
||||
set::headingClass('h-12'),
|
||||
set::bodyClass('pt-0'),
|
||||
$pivot->desc ? to::titleSuffix(
|
||||
icon
|
||||
to::titleSuffix
|
||||
(
|
||||
$pivot->desc ? icon
|
||||
(
|
||||
setClass('cursor-pointer'),
|
||||
setData(array('toggle' => 'tooltip', 'title' => $pivot->desc, 'placement' => 'right', 'className' => 'text-gray border border-light', 'type' => 'white')),
|
||||
'help'
|
||||
) : null,
|
||||
span
|
||||
(
|
||||
set::style(array('font-weight' => 'normal')),
|
||||
$lang->pivot->tipNewVersion . $lang->comma,
|
||||
h::a
|
||||
(
|
||||
$lang->pivot->checkNewVersion,
|
||||
set('data-toggle', 'modal'),
|
||||
set::url($this->createLink('pivot', 'versions', "pivotID={$pivot->id}"))
|
||||
)
|
||||
)
|
||||
) : null,
|
||||
),
|
||||
toolbar
|
||||
(
|
||||
item
|
||||
|
||||
@@ -80,6 +80,7 @@ class pivotZen extends pivot
|
||||
$groups = $this->pivot->getGroupsByDimensionAndPath($dimensionID, $currentGroup->path);
|
||||
if(!$groups) return array();
|
||||
|
||||
$firstAction = $this->loadModel('action')->getAccountFirstAction($this->app->user->account);
|
||||
$menus = array();
|
||||
foreach($groups as $group)
|
||||
{
|
||||
@@ -87,6 +88,7 @@ class pivotZen extends pivot
|
||||
|
||||
$pivots = $this->pivot->getAllPivotByGroupID($group->id);
|
||||
$pivots = $this->pivot->filterInvisiblePivot($pivots);
|
||||
$pivots = $this->loadModel('mark')->getMarks($pivots, 'pivot', 'view');
|
||||
if(empty($pivots)) continue;
|
||||
|
||||
if($group->grade > 1) $menus[] = (object)array('id' => $group->id, 'parent' => 0, 'name' => $group->name);
|
||||
@@ -95,6 +97,7 @@ class pivotZen extends pivot
|
||||
|
||||
foreach($pivots as $pivot)
|
||||
{
|
||||
$this->setNewMark($pivot, $firstAction);
|
||||
$params = helper::safe64Encode("groupID={$group->id}&pivotID={$pivot->id}");
|
||||
$url = inlink('preview', "dimension={$dimensionID}&group={$currentGroup->id}&method=show¶ms={$params}");
|
||||
$menus[] = (object)array('id' => $group->id . '_' . $pivot->id, 'parent' => $group->grade > 1 ? $group->id : 0, 'name' => $pivot->name, 'url' => $url);
|
||||
@@ -108,6 +111,21 @@ class pivotZen extends pivot
|
||||
return array_merge($menus, $builtinMenus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 “新” 标签。
|
||||
* Set new mark.
|
||||
*
|
||||
* @param object $pivot
|
||||
* @param object $firstAction
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function setNewMark(object $pivot, object $firstAction): void
|
||||
{
|
||||
if(!$pivot->mark && $pivot->createdDate < $firstAction->date) $pivot->mark = true;
|
||||
if(!$pivot->mark) $pivot->name = array('html' => $pivot->name . ' <span class="label ghost size-sm bg-secondary-50 text-secondary-500 rounded-full">' . $this->lang->pivot->new . '</span>');
|
||||
}
|
||||
|
||||
/**
|
||||
* 在第一个维度上显示内置透视表。
|
||||
* Display the built-in pivots in the first dimension.
|
||||
|
||||
@@ -843,6 +843,14 @@ class product extends control
|
||||
$tracks['cols'] = $cols;
|
||||
}
|
||||
|
||||
$tasks = array();
|
||||
foreach($tracks['items'] as $lane)
|
||||
{
|
||||
$taskIdList = array_column($lane['task'], 'id');
|
||||
$taskList = array_combine($taskIdList, $lane['task']);
|
||||
$tasks += $taskList;
|
||||
}
|
||||
|
||||
/* Build search form. */
|
||||
$this->productZen->buildSearchFormForTrack($productID, $branch, $projectID, $browseType, $param, $storyType);
|
||||
|
||||
@@ -862,6 +870,7 @@ class product extends control
|
||||
$this->view->storyTypeList = $storyTypeList;
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
|
||||
$this->view->projectProducts = $this->product->getProductPairsByProject($projectID);
|
||||
$this->view->tasks = $tasks;
|
||||
|
||||
$this->display();
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ window.itemRender = function(info)
|
||||
const col = info.col;
|
||||
if(col == 'task')
|
||||
{
|
||||
if(info.item.parent > '0') info.item.className.push('hidden childTask parent-' + info.item.parent);
|
||||
if(info.item.parent > '0' && tasks[info.item.parent] !== undefined) info.item.className.push('hidden childTask parent-' + info.item.parent);
|
||||
if(info.item.parent == '-1') info.item.className.push('parentTask');
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ jsVar('orderByTitle', $orderByTitle);
|
||||
jsVar('storyType', $storyType);
|
||||
jsVar('users', $users);
|
||||
jsVar('privs', $privs);
|
||||
jsVar('tasks', $tasks);
|
||||
|
||||
empty($tracks) ? div(setClass('dtable-empty-tip bg-white shadow'), span(setClass('text-gray'), $lang->noData)) : div
|
||||
(
|
||||
|
||||
@@ -129,4 +129,24 @@ class bugTester extends tester
|
||||
if($form->dom->status->getText() == '已解决') return $this->success('解决Bug成功');
|
||||
return $this->failed('解决Bug失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭bug。
|
||||
* Close bug.
|
||||
*
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function closeBug()
|
||||
{
|
||||
$form = $this->initForm('project', 'bug', array('project' => 1), 'appIframe-project');
|
||||
$form->dom->closeBtn->click();
|
||||
$title = $form->dom->closeTitle->getText();
|
||||
$form->dom->close->click();
|
||||
$form->wait(1);
|
||||
$form->dom->search(array("{$this->lang->bug->name},=,{$title}"));
|
||||
$form->wait(1);
|
||||
if($form->dom->status->getText() == '已关闭') return $this->success('关闭Bug成功');
|
||||
return $this->failed('关闭Bug失败');
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=项目下关闭Bug操作检查
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行tester模块的closeBug方法▫
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @关闭Bug成功
|
||||
|
||||
*/
|
||||
|
||||
chdir(__DIR__);
|
||||
include '../lib/bug.ui.class.php';
|
||||
|
||||
$product = zenData('product');
|
||||
$product->id->range('1-2');
|
||||
$product->name->range('产品1, 产品2');
|
||||
$product->type->range('normal');
|
||||
$product->gen(2);
|
||||
|
||||
$project = zenData('project');
|
||||
$project->id->range('1');
|
||||
$project->project->range('0');
|
||||
$project->model->range('scrum');
|
||||
$project->type->range('project');
|
||||
$project->auth->range('extend');
|
||||
$project->storytype->range('`story,epic,requirement`');
|
||||
$project->path->range('`,1,`');
|
||||
$project->grade->range('1');
|
||||
$project->name->range('敏捷项目1');
|
||||
$project->hasProduct->range('1');
|
||||
$project->status->range('wait');
|
||||
$project->acl->range('open');
|
||||
$project->gen(1);
|
||||
|
||||
$projectProduct = zenData('projectproduct');
|
||||
$projectProduct->project->range('1');
|
||||
$projectProduct->product->range('1{1}, 2{1}');
|
||||
$projectProduct->gen(2);
|
||||
|
||||
$bug = zenData('bug');
|
||||
$bug->id->range('1-10');
|
||||
$bug->project->range('1');
|
||||
$bug->product->range('1{2}, 2{2}');
|
||||
$bug->execution->range('0');
|
||||
$bug->title->range('Bug1, Bug2, Bug3, Bug4');
|
||||
$bug->status->range('active{2}, resolved{2}');
|
||||
$bug->assignedTo->range('[]');
|
||||
$bug->gen(4);
|
||||
|
||||
$team = zendata('team');
|
||||
$team->id->range('1');
|
||||
$team->root->range('1');
|
||||
$team->type->range('project');
|
||||
$team->account->range('admin');
|
||||
$team->join->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD');
|
||||
$team->gen(1);
|
||||
|
||||
$tester = new bugTester();
|
||||
$tester->login();
|
||||
|
||||
r($tester->closeBug()) && p('status,message') && e('SUCCESS,关闭Bug成功');
|
||||
|
||||
$tester->closeBrowser();
|
||||
@@ -20,8 +20,16 @@ class bugPage extends page
|
||||
'assignToAdmin' => "//*[@class='popover show fade dropdown in']/menu/menu/li[1]/a/div/div",
|
||||
'confirmBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[1]/div/nav/a[1]",
|
||||
'firstConfirm' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[6]/div",
|
||||
'firstAssign' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[7]/div/a/span",
|
||||
'secondCheckbox' => "//*[@id='table-project-bug']/div[2]/div[1]/div/div[3]/div/div/label",
|
||||
'batchAssignTo' => "//*[@id='table-project-bug']/div[3]/nav[1]/button/span[2]",
|
||||
'assignToAdmin' => "//*[@class='popover show fade dropdown in']/menu/menu/li[1]/a/div/div",
|
||||
'confirmBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[1]/div/nav/a[1]",
|
||||
'firstConfirm' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[6]/div",
|
||||
'resolveBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[2]/div/nav/a[1]/i",
|
||||
'secondStatus' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[11]/div/span",
|
||||
'closeBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[3]/div/nav/a[1]/i",
|
||||
'activeBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[4]/div/nav/a[1]/i",
|
||||
/* 指派页面 */
|
||||
'assignTo' => "//*[@id='assignedTo']/div/input",
|
||||
'submitBtn' => "//*[@class='form load-indicator form-ajax no-morph form-horz']/div[4]/div/button/span",
|
||||
@@ -32,9 +40,13 @@ class bugPage extends page
|
||||
'resolution' => "//*[@name='resolution']",
|
||||
'build' => "//*[@name='resolvedBuild']",
|
||||
'resolve' => "//*[@class='toolbar form-actions form-group no-label']/button/span",
|
||||
'resolveTitle' => "//*[@class='modal modal-async load-indicator modal-trans show in']/div/div/div[1]/div/div[2]/span[1]",
|
||||
'resolveTitle' => "//*[@class='modal modal-async load-indicator modal-trans show in']/div/div/div[1]/div/div[2]/span[1]",
|
||||
/* 搜索列表 */
|
||||
'status' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[3]/div/span",
|
||||
'resolveTitle' => "//*[@class='modal modal-async load-indicator modal-trans show in']/div/div/div[1]/div/div[2]/span[1]",
|
||||
/* 关闭页面 */
|
||||
'close' => "//*[@class='form load-indicator form-ajax no-morph form-horz']/div[2]/div/button",
|
||||
'closeTitle' => "//*[@class='modal modal-async load-indicator modal-trans show in']/div/div/div[1]/div/div[2]/span[1]",
|
||||
);
|
||||
$this->dom->xpath = array_merge($this->dom->xpath, $xpath);
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/ui.php';
|
||||
class checkTabTester extends tester
|
||||
{
|
||||
/**
|
||||
* 检查项目需求Tab标签下的数据。
|
||||
* Check the data of the Tab.
|
||||
*
|
||||
* @param string $tab
|
||||
* @param string $expectNum
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function checkTab($tab, $expectNum)
|
||||
{
|
||||
$form = $this->initForm('projectstory', 'story', array('project' => '1'), 'appIframe-project');
|
||||
$tabs = array('allTab', 'unclosedTab', 'draftTab', 'reviewingTab', 'changingTab');
|
||||
if(!in_array($tab, $tabs)) $form->dom->moreTab->click();
|
||||
$form->dom->$tab->click();
|
||||
$form->wait(2);
|
||||
if($form->dom->num->getText() == $expectNum) return $this->success($tab . '下显示条数正确');
|
||||
return $this->failed($tab . '下显示条数不正确');
|
||||
}
|
||||
}
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=项目下需求列表标签切换检查
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 检查全部标签下显示条数
|
||||
- 测试结果 @allTab下显示条数正确
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 检查未关闭标签下显示条数
|
||||
- 测试结果 @unclosedTab下显示条数正确
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 检查草稿标签下显示条数
|
||||
- 测试结果 @draftTab下显示条数正确
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 检查评审中标签下显示条数
|
||||
- 测试结果 @reviewingTab下显示条数正确
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 检查变更中标签下显示条数
|
||||
- 测试结果 @changingTab下显示条数正确
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 检查已关闭标签下显示条数
|
||||
- 测试结果 @closedTab下显示条数正确
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 检查已关联执行标签下显示条数
|
||||
- 测试结果 @linkedExecutionTab下显示条数正确
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 检查未关联执行标签下显示条数
|
||||
- 测试结果 @unlinkedExecutionTab下显示条数正确
|
||||
- 最终测试状态 @SUCCESS
|
||||
|
||||
*/
|
||||
|
||||
chdir(__DIR__);
|
||||
include '../lib/checktab.ui.class.php';
|
||||
|
||||
$product = zenData('product');
|
||||
$product->id->range('1');
|
||||
$product->name->range('产品1');
|
||||
$product->type->range('normal');
|
||||
$product->gen(1);
|
||||
|
||||
$project = zenData('project');
|
||||
$project->id->range('1-2');
|
||||
$project->project->range('0,1');
|
||||
$project->model->range('scrum');
|
||||
$project->type->range('project,sprint');
|
||||
$project->auth->range('extend,[]');
|
||||
$project->storytype->range('`story,epic,requirement`,`story`');
|
||||
$project->path->range('`,1,`, `,1,2,`');
|
||||
$project->grade->range('1');
|
||||
$project->name->range('项目1,执行1');
|
||||
$project->hasProduct->range('1');
|
||||
$project->status->range('wait');
|
||||
$project->acl->range('open');
|
||||
$project->gen(2);
|
||||
|
||||
$projectProduct = zenData('projectproduct');
|
||||
$projectProduct->project->range('1');
|
||||
$projectProduct->product->range('1');
|
||||
$projectProduct->gen(1);
|
||||
|
||||
$story = zenData('story');
|
||||
$story->id->range('1-10');
|
||||
$story->parent->range('0');
|
||||
$story->isParent->range('0');
|
||||
$story->root->range('1-10');
|
||||
$story->path->range('`,1,`, `,2,`, `,3,`, `,4,`, `,5,`, `,6,`, `,7,`, `,8,`, `,9,`, `,10,`');
|
||||
$story->grade->range('1');
|
||||
$story->product->range('1');
|
||||
$story->module->range('0');
|
||||
$story->plan->range('0');
|
||||
$story->title->range('需求001,需求002,需求003,需求004,需求005,需求006,需求007,需求008,需求009,需求010');
|
||||
$story->type->range('story');
|
||||
$story->estimate->range('0');
|
||||
$story->status->range('active{3}, closed{1}, reviewing{2}, draft{1}, changing{3}');
|
||||
$story->stage->range('projected');
|
||||
$story->assignedTo->range('[]');
|
||||
$story->version->range('1');
|
||||
$story->gen(10);
|
||||
|
||||
$storySpec = zenData('storyspec');
|
||||
$storySpec->story->range('1-10');
|
||||
$storySpec->version->range('1');
|
||||
$storySpec->title->range('1-10');
|
||||
$storySpec->gen(10);
|
||||
|
||||
$projectStory = zenData('projectstory');
|
||||
$projectStory->project->range('1{10},2{1}');
|
||||
$projectStory->product->range('1');
|
||||
$projectStory->branch->range('0');
|
||||
$projectStory->story->range('1-10,3');
|
||||
$projectStory->version->range('1');
|
||||
$projectStory->order->range('1{10},2{1}');
|
||||
$projectStory->gen(11);
|
||||
|
||||
$tester = new checkTabTester();
|
||||
$tester->login();
|
||||
|
||||
/* 标签统计 */
|
||||
r($tester->checkTab('allTab', '10')) && p('message,status') && e('allTab下显示条数正确,SUCCESS'); //检查全部标签下显示条数
|
||||
r($tester->checkTab('unclosedTab', '9')) && p('message,status') && e('unclosedTab下显示条数正确,SUCCESS'); //检查未关闭标签下显示条数
|
||||
r($tester->checkTab('draftTab', '1')) && p('message,status') && e('draftTab下显示条数正确,SUCCESS'); //检查草稿标签下显示条数
|
||||
r($tester->checkTab('reviewingTab', '2')) && p('message,status') && e('reviewingTab下显示条数正确,SUCCESS'); //检查评审中标签下显示条数
|
||||
r($tester->checkTab('changingTab', '3')) && p('message,status') && e('changingTab下显示条数正确,SUCCESS'); //检查变更中标签下显示条数
|
||||
r($tester->checkTab('closedTab', '1')) && p('message,status') && e('closedTab下显示条数正确,SUCCESS'); //检查已关闭标签下显示条数
|
||||
r($tester->checkTab('linkedExecutionTab', '1')) && p('message,status') && e('linkedExecutionTab下显示条数正确,SUCCESS'); //检查已关联执行标签下显示条数
|
||||
r($tester->checkTab('unlinkedExecutionTab', '9')) && p('message,status') && e('unlinkedExecutionTab下显示条数正确,SUCCESS'); //检查未关联执行标签下显示条数
|
||||
|
||||
$tester->closeBrowser();
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
class storyPage extends page
|
||||
{
|
||||
public function __construct($webdriver)
|
||||
{
|
||||
parent::__construct($webdriver);
|
||||
$xpath = array(
|
||||
/* 标签 */
|
||||
'allTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[1]/a/span[1]",
|
||||
'unclosedTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[2]/a/span[1]",
|
||||
'draftTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[3]/a/span[1]",
|
||||
'reviewingTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[4]/a/span[1]",
|
||||
'changingTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[5]/a/span[1]",
|
||||
'moreTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[6]/a/span[1]",
|
||||
'closedTab' => "//*[@id='more']/menu/menu/li[1]/a/div/div",
|
||||
'linkedExecutionTab' => "//*[@id='more']/menu/menu/li[2]/a/div/div",
|
||||
'unlinkedExecutionTab' => "//*[@id='more']/menu/menu/li[3]/a/div/div",
|
||||
'num' => "//*[@id='stories_table']/div/div[3]/div[2]/strong[1]"
|
||||
);
|
||||
$this->dom->xpath = array_merge($this->dom->xpath, $xpath);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ featureBar
|
||||
$canCreateRelease = hasPriv('release', 'create') && common::canModify('product', $product);
|
||||
$canManageSystem = hasPriv('system', 'browse') && common::canModify('product', $product);
|
||||
if($canCreateRelease) $createItem = array('icon' => 'plus', 'class' => 'primary', 'text' => $lang->release->create, 'url' => $this->createLink('release', 'create', "productID={$product->id}&branch={$branch}"));
|
||||
if($canManageSystem) $manageSystemItem = array('icon' => 'plus', 'class' => 'primary', 'text' => $lang->release->manageSystem, 'url' => $this->createLink('system', 'browse', "productID={$product->id}&branch={$branch}"), 'data-app' => 'product');
|
||||
if($canManageSystem) $manageSystemItem = array('class' => 'ghost', 'text' => $lang->release->manageSystem, 'url' => $this->createLink('system', 'browse', "productID={$product->id}&branch={$branch}"), 'data-app' => 'product');
|
||||
toolbar
|
||||
(
|
||||
!empty($manageSystemItem) ? item(set($manageSystemItem)) : null,
|
||||
|
||||
@@ -53,8 +53,17 @@ class settingModel extends model
|
||||
$item = $this->parseItemPath($path);
|
||||
if(empty($item)) return false;
|
||||
|
||||
$vision = zget($item, 'vision', '');
|
||||
|
||||
$item->value = strval($value);
|
||||
$this->dao->replace(TABLE_CONFIG)->data($item)->exec();
|
||||
$this->dao->delete()->from(TABLE_CONFIG)
|
||||
->where('owner')->eq($item->owner)
|
||||
->andWhere('module')->eq($item->module)
|
||||
->andWhere('section')->eq($item->section)
|
||||
->andWhere('`key`')->eq($item->key)
|
||||
->beginIF($vision)->andWhere('vision')->eq($vision)->fi()
|
||||
->exec();
|
||||
$this->dao->insert(TABLE_CONFIG)->data($item)->exec();
|
||||
|
||||
return !dao::isError();
|
||||
}
|
||||
@@ -226,7 +235,8 @@ class settingModel extends model
|
||||
$params['section'] = isset($params['section']) ? $params['section'] : '';
|
||||
$params['key'] = isset($params['key']) ? $params['key'] : '';
|
||||
|
||||
return $this->dao->$method('*')->from(TABLE_CONFIG)->where('1 = 1')
|
||||
$access = $method == 'select' ? $this->mao : $this->dao;
|
||||
return $access->$method('*')->from(TABLE_CONFIG)->where('1 = 1')
|
||||
->beginIF($params['vision'])->andWhere('vision')->in($params['vision'])->fi()
|
||||
->beginIF($params['owner'])->andWhere('owner')->in($params['owner'])->fi()
|
||||
->beginIF($params['module'])->andWhere('module')->in($params['module'])->fi()
|
||||
@@ -244,8 +254,8 @@ class settingModel extends model
|
||||
*/
|
||||
public function getSysAndPersonalConfig(string $account = ''): array
|
||||
{
|
||||
$owner = 'system,' . ($account ? $account : '');
|
||||
$records = $this->dao->select('*')->from(TABLE_CONFIG)
|
||||
$owner = 'system,' . ($account ? $account : '');
|
||||
$records = $this->mao->select('*')->from(TABLE_CONFIG)
|
||||
->where('owner')->in($owner)
|
||||
->beginIF(!$this->app->upgrading)->andWhere('vision')->in(array('', $this->config->vision))->fi()
|
||||
->orderBy('id')
|
||||
|
||||
+28
-1
@@ -1900,6 +1900,33 @@ class storyTao extends storyModel
|
||||
return $story;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查操作按钮的触发条件。
|
||||
* Check action conditions.
|
||||
*
|
||||
* @param string $method
|
||||
* @param object $story
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkConditions(string $method, object $story)
|
||||
{
|
||||
static $flowActions = [];
|
||||
if(empty($flowActions)) $flowActions = $this->loadModel('workflowaction')->getList($story->type);
|
||||
$this->loadModel('flow');
|
||||
|
||||
$isClickable = true;
|
||||
foreach($flowActions as $flowAction)
|
||||
{
|
||||
if($flowAction->action == $method && $flowAction->extensionType != 'none' && $flowAction->status == 'enable' && !empty($flowAction->conditions))
|
||||
{
|
||||
$isClickable = $this->flow->checkConditions($flowAction->conditions, $story);
|
||||
}
|
||||
}
|
||||
|
||||
return $isClickable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建需求列表中的操作按钮。
|
||||
* Build action buttons on the browse page.
|
||||
@@ -1962,7 +1989,7 @@ class storyTao extends storyModel
|
||||
/* Change button. */
|
||||
$canChange = common::hasPriv($story->type, 'change') && $this->isClickable($story, 'change');
|
||||
$title = $canChange ? $lang->story->change : $this->lang->story->changeTip;
|
||||
if(common::hasPriv($story->type, 'change')) $actions[] = array('name' => 'change', 'url' => $canChange ? $changeLink : null, 'hint' => $title, 'disabled' => !$canChange, 'class' => 'story-change-btn');
|
||||
if(common::hasPriv($story->type, 'change') && $this->checkConditions('change', $story)) $actions[] = array('name' => 'change', 'url' => $canChange ? $changeLink : null, 'hint' => $title, 'disabled' => !$canChange, 'class' => 'story-change-btn');
|
||||
|
||||
/* Submitreview, review, recall buttons. */
|
||||
if(strpos('draft,changing', $story->status) !== false)
|
||||
|
||||
@@ -33,6 +33,28 @@ class activateStoryTester extends tester
|
||||
$viewPage = $this->loadPage('story', 'view'); //进入需求详情页查看状态是否与关闭前一致
|
||||
if($viewPage->dom->status->getText() != $status) return $this->failed('激活需求后状态不正确');
|
||||
|
||||
return $this->success('激活需求成功');
|
||||
return $this->success('激活研发需求成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* check the information after activate an epic
|
||||
* @param string $storyID
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function activateEpic($storyID, $status)
|
||||
{
|
||||
$form = $this->initform('epic', 'view', array('id' => $storyID), 'appIframe-product'); //进入业务需求详情页
|
||||
$form->dom->btn($this->lang->story->activate)->click(); //点击激活需求按钮
|
||||
$form->wait(1);
|
||||
|
||||
$form->dom->assignedTo->picker('admin'); //选择指派人
|
||||
$form->dom->activate->click(); //点击激活按钮
|
||||
$form->wait(1);
|
||||
|
||||
$viewPage = $this->loadPage('epic', 'view'); //进入需求详情页查看状态是否与关闭前一致
|
||||
if($viewPage->dom->status->getText() != $status) return $this->failed('激活需求后状态不正确');
|
||||
|
||||
return $this->success('激活业务需求成功');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,42 +24,44 @@ $product->vision->range('rnd');
|
||||
$product->gen(1);
|
||||
|
||||
$story = zenData('story');
|
||||
$story->id->range('1-2');
|
||||
$story->root->range('1-2');
|
||||
$story->path->range('`,1,`, `,2,`');
|
||||
$story->id->range('1-6');
|
||||
$story->root->range('1-6');
|
||||
$story->path->range('`,1,`, `,2,`, `,3,`, `,4,`, `,5,`, `,6,`');
|
||||
$story->grade->range('1');
|
||||
$story->product->range('1');
|
||||
$story->module->range('0');
|
||||
$story->title->range('激活研发需求, 草稿研发需求');
|
||||
$story->type->range('story');
|
||||
$story->title->range('激活研发需求, 草稿研发需求, 激活用户需求, 草稿用户需求, 激活业务需求, 草稿业务需求');
|
||||
$story->type->range('story{2}, requirement{2}, epic{2}');
|
||||
$story->stage->range('closed');
|
||||
$story->status->range('closed');
|
||||
$story->openedBy->range('admin');
|
||||
$story->version->range('1');
|
||||
$story->gen(2);
|
||||
$story->gen(6);
|
||||
|
||||
$storyspec = zenData('storyspec');
|
||||
$storyspec->story->range('1-2');
|
||||
$storyspec->story->range('1-6');
|
||||
$storyspec->version->range('1');
|
||||
$storyspec->title->range('激活研发需求, 草稿研发需求');
|
||||
$storyspec->gen(2);
|
||||
$storyspec->title->range('激活研发需求, 草稿研发需求, 激活用户需求, 草稿用户需求, 激活业务需求, 草稿业务需求');
|
||||
$storyspec->gen(6);
|
||||
|
||||
$action = zenData('action');
|
||||
$action->id->range('1-2');
|
||||
$action->id->range('1-6');
|
||||
$action->objectType->range('story');
|
||||
$action->objectID->range('1-2');
|
||||
$action->objectID->range('1-6');
|
||||
$action->product->range('`,1`');
|
||||
$action->actor->range('admin');
|
||||
$action->action->range('closed');
|
||||
$action->date->range('(-2D)-(-D):60m')->type('timestamp')->format('YY/MM/DD hh:mm:ss');
|
||||
$action->extra->range('Done|active, Done|draft');
|
||||
$action->gen(2);
|
||||
$action->gen(6);
|
||||
|
||||
$tester = new activateStoryTester();
|
||||
$tester->login();
|
||||
|
||||
$stutus = array('激活', '草稿');
|
||||
r($tester->activateStory(1, $stutus[0])) && p('message') && e('激活需求成功');
|
||||
r($tester->activateStory(2, $stutus[1])) && p('message') && e('激活需求成功');
|
||||
r($tester->activateStory(1, $stutus[0])) && p('message') && e('激活研发需求成功');
|
||||
r($tester->activateStory(2, $stutus[1])) && p('message') && e('激活研发需求成功');
|
||||
|
||||
r($tester->activateEpic(5, $stutus[0])) && p('message') && e('激活业务需求成功');
|
||||
r($tester->activateEpic(6, $stutus[1])) && p('message') && e('激活业务需求成功');
|
||||
$tester->closeBrowser();
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
global $lang;
|
||||
$config->system = new stdclass();
|
||||
|
||||
$config->system->create = new stdclass();
|
||||
$config->system->create->requiredFields = 'name';
|
||||
|
||||
$config->system->edit = new stdclass();
|
||||
$config->system->edit->requiredFields = 'name';
|
||||
|
||||
$config->system->groupPrivs = array();
|
||||
$config->system->groupPrivs['dashboard'] = 'backup|index';
|
||||
$config->system->groupPrivs['deletebackup'] = 'backup|delete';
|
||||
|
||||
@@ -20,15 +20,17 @@ $config->system->dtable->fieldList['latestRelease']['title'] = $lang->system->la
|
||||
$config->system->dtable->fieldList['latestRelease']['name'] = 'latestRelease';
|
||||
$config->system->dtable->fieldList['latestRelease']['type'] = 'text';
|
||||
|
||||
$config->system->dtable->fieldList['children']['title'] = $lang->system->children;
|
||||
$config->system->dtable->fieldList['children']['name'] = 'children';
|
||||
$config->system->dtable->fieldList['children']['type'] = 'text';
|
||||
$config->system->dtable->fieldList['children']['title'] = $lang->system->children;
|
||||
$config->system->dtable->fieldList['children']['name'] = 'children';
|
||||
$config->system->dtable->fieldList['children']['type'] = 'text';
|
||||
$config->system->dtable->fieldList['children']['delimiter'] = ',';
|
||||
|
||||
$config->system->dtable->fieldList['status']['title'] = $lang->system->status;
|
||||
$config->system->dtable->fieldList['status']['name'] = 'status';
|
||||
$config->system->dtable->fieldList['status']['type'] = 'status';
|
||||
$config->system->dtable->fieldList['status']['map'] = $lang->system->statusList;
|
||||
$config->system->dtable->fieldList['status']['sortType'] = true;
|
||||
$config->system->dtable->fieldList['status']['title'] = $lang->system->status;
|
||||
$config->system->dtable->fieldList['status']['name'] = 'status';
|
||||
$config->system->dtable->fieldList['status']['type'] = 'status';
|
||||
$config->system->dtable->fieldList['status']['statusMap'] = $lang->system->statusList;
|
||||
$config->system->dtable->fieldList['status']['width'] = 100;
|
||||
$config->system->dtable->fieldList['status']['sortType'] = true;
|
||||
|
||||
$config->system->dtable->fieldList['actions']['name'] = 'actions';
|
||||
$config->system->dtable->fieldList['actions']['title'] = $lang->actions;
|
||||
|
||||
@@ -3,3 +3,14 @@ $config->system->form = new stdclass();
|
||||
$config->system->form->editDomain['customDomain'] = array('type' => 'string', 'required' => true, 'default' => '');
|
||||
$config->system->form->editDomain['certPem'] = array('type' => 'string', 'required' => false, 'default' => '');
|
||||
$config->system->form->editDomain['certKey'] = array('type' => 'string', 'required' => false, 'default' => '');
|
||||
|
||||
$config->system->form->create['integrated'] = array('type' => 'int', 'required' => false, 'default' => 0);
|
||||
$config->system->form->create['name'] = array('type' => 'string', 'required' => true, 'filter' => 'trim');
|
||||
$config->system->form->create['children'] = array('type' => 'array', 'required' => false, 'default' => array(), 'filter' => 'join');
|
||||
$config->system->form->create['desc'] = array('type' => 'string', 'required' => false, 'default' => '', 'filter' => 'trim');
|
||||
$config->system->form->create['createdDate'] = array('type' => 'datetime', 'required' => false, 'default' => helper::now());
|
||||
|
||||
$config->system->form->edit['name'] = array('type' => 'string', 'required' => true, 'filter' => 'trim');
|
||||
$config->system->form->edit['children'] = array('type' => 'array', 'required' => false, 'default' => array(), 'filter' => 'join');
|
||||
$config->system->form->edit['desc'] = array('type' => 'string', 'required' => false, 'default' => '', 'filter' => 'trim');
|
||||
$config->system->form->edit['editedDate'] = array('type' => 'datetime', 'required' => false, 'default' => helper::now());
|
||||
|
||||
+137
-4
@@ -542,10 +542,143 @@ class system extends control
|
||||
$this->app->loadClass('pager', true);
|
||||
$pager = pager::init($recTotal, $recPerPage, $pageID);
|
||||
|
||||
$this->view->title = $this->lang->system->browse;
|
||||
$this->view->appList = $this->system->getList($orderBy, $pager);
|
||||
$this->view->orderBy = $orderBy;
|
||||
$this->view->pager = $pager;
|
||||
$systems = $this->system->getList($orderBy, $pager);
|
||||
foreach($systems as &$system)
|
||||
{
|
||||
$system->latestRelease = $system->latestRelease ? $system->latestRelease : '';
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->system->browse;
|
||||
$this->view->productID = $productID;
|
||||
$this->view->appList = $systems;
|
||||
$this->view->appPairs = $this->system->getPairs();
|
||||
$this->view->orderBy = $orderBy;
|
||||
$this->view->pager = $pager;
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建应用。
|
||||
* Create application.
|
||||
*
|
||||
* @param int $productID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function create(int $productID)
|
||||
{
|
||||
if($_POST)
|
||||
{
|
||||
$integrated = $this->post->integrated;
|
||||
if($integrated) $this->config->system->create->requiredFields .= ',children';
|
||||
|
||||
$formData = form::data($this->config->system->form->create)
|
||||
->setDefault('product', $productID)
|
||||
->setDefault('status', 'active')
|
||||
->setDefault('createdBy', $this->app->user->account)
|
||||
->setIF($integrated == '0', 'children', '')
|
||||
->get();
|
||||
|
||||
$systemID = $this->system->create($formData);
|
||||
if(dao::isError()) return $this->sendError(dao::getError());
|
||||
|
||||
if($systemID) $this->loadModel('action')->create('system', $systemID, 'created');
|
||||
$this->sendSuccess(array('load' => true));
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->system->create;
|
||||
$this->view->systemList = $this->system->getPairs('0');
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑应用。
|
||||
* Edit application.
|
||||
*
|
||||
* @param int $id
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function edit(int $id)
|
||||
{
|
||||
$system = $this->system->fetchByID($id);
|
||||
if($_POST)
|
||||
{
|
||||
$integrated = $system->integrated;
|
||||
if($integrated) $this->config->system->edit->requiredFields .= ',children';
|
||||
|
||||
$formData = form::data($this->config->system->form->edit)
|
||||
->setDefault('editedBy', $this->app->user->account)
|
||||
->setDefault('integrated', $integrated)
|
||||
->setDefault('editedBy', $this->app->user->account)
|
||||
->get();
|
||||
|
||||
$this->system->update($id, $formData);
|
||||
if(dao::isError()) return $this->sendError(dao::getError());
|
||||
|
||||
$this->loadModel('action')->create('system', $id, 'edited');
|
||||
$this->sendSuccess(array('load' => true));
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->system->edit;
|
||||
$this->view->system = $system;
|
||||
$this->view->systemList = $this->system->getPairs('0');
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* 上架应用。
|
||||
* Active application.
|
||||
*
|
||||
* @param int $id
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function active(int $id)
|
||||
{
|
||||
$system = new stdclass();
|
||||
$system->status = 'active';
|
||||
|
||||
$this->system->update($id, $system, false);
|
||||
if(dao::isError()) return $this->sendError(dao::getError());
|
||||
|
||||
$this->loadModel('action')->create('system', $id, 'active');
|
||||
$this->sendSuccess(array('load' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下架应用。
|
||||
* Inactive application.
|
||||
*
|
||||
* @param int $id
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function inactive(int $id)
|
||||
{
|
||||
$system = new stdclass();
|
||||
$system->status = 'inactive';
|
||||
|
||||
$this->system->update($id, $system, false);
|
||||
if(dao::isError()) return $this->sendError(dao::getError());
|
||||
|
||||
$this->loadModel('action')->create('system', $id, 'inactive');
|
||||
$this->sendSuccess(array('load' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除应用。
|
||||
* Delete application.
|
||||
*
|
||||
* @param int $id
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function delete(int $id)
|
||||
{
|
||||
$this->system->delete(TABLE_SYSTEM, $id);
|
||||
|
||||
if(dao::isError()) return $this->sendError(dao::getError());
|
||||
$this->sendSuccess(array('load' => true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +30,12 @@ $lang->system->serviceQuantity = 'Number of services';
|
||||
$lang->system->cpuUsage = 'CPU(Core)';
|
||||
$lang->system->memUsage = 'Memory(GB)';
|
||||
$lang->system->name = 'Application name';
|
||||
$lang->system->integrated = 'Integrated application';
|
||||
$lang->system->latestRelease = 'Latest version';
|
||||
$lang->system->children = 'Included applications';
|
||||
$lang->system->latestRelease = 'Latest version';
|
||||
$lang->system->status = 'Status';
|
||||
$lang->system->desc = 'Description';
|
||||
$lang->system->browse = 'Application list';
|
||||
$lang->system->create = 'Create application';
|
||||
$lang->system->edit = 'Edit application';
|
||||
@@ -41,6 +43,10 @@ $lang->system->delete = 'Delete application';
|
||||
$lang->system->active = 'Online application';
|
||||
$lang->system->inactive = 'Offline application';
|
||||
|
||||
$lang->system->integratedList = array();
|
||||
$lang->system->integratedList[0] = 'No';
|
||||
$lang->system->integratedList[1] = 'Yes';
|
||||
|
||||
$lang->system->statusList = array();
|
||||
$lang->system->statusList['active'] = 'Active';
|
||||
$lang->system->statusList['inactive'] = 'Inactive';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user