Merge branch '18.x' into ipd1.1
This commit is contained in:
Vendored
+7
-13
@@ -106,6 +106,9 @@ pipeline {
|
||||
env.GIT_TAG_BUILD_GROUP = sh(returnStdout: true, script: 'misc/parse_tag.sh $TAG_NAME group').trim()
|
||||
env.GIT_TAGGER_NAME = sh(returnStdout: true, script: 'git for-each-ref --format="%(taggername)" refs/tags/$(git tag --points-at HEAD)').trim()
|
||||
|
||||
env.CI_PUBLIC_IMAGE_NAMESPACE = sh(returnStdout: true,script: 'jq -r .image.public.namespace.' + env.GIT_TAG_BUILD_TYPE + ' < ci.json').trim()
|
||||
env.CI_INTERNAL_IMAGE_NAMESPACE = sh(returnStdout: true,script: 'jq -r .image.internal.namespace.' + env.GIT_TAG_BUILD_TYPE + ' < ci.json').trim()
|
||||
|
||||
def ximUsers = sh(returnStdout: true,script: 'jq -r .notice.users < ci.json').trim()
|
||||
env.XIM_USERS = ximUsers + ',' + env.GIT_TAGGER_NAME
|
||||
env.XIM_GROUPS = sh(returnStdout: true,script: 'jq -r .notice.groups < ci.json').trim()
|
||||
@@ -115,13 +118,14 @@ pipeline {
|
||||
env.MAX_VERSION = sh(returnStdout: true, script: 'cat ${SRC_ZENTAOEXT_PATH}/MAXVERSION').trim()
|
||||
env.IPD_VERSION = sh(returnStdout: true, script: 'cat ${SRC_ZENTAOEXT_PATH}/IPDVERSION').trim()
|
||||
|
||||
env.DOWNGRADE_ENABLED = sh(returnStdout: true, script: 'test -n "${DOWNGRADE_ENABLED}" && echo ${DOWNGRADE_ENABLED} || (jq -r .downgrade.enabled < ci.json)').trim()
|
||||
env.CI_DOWNGRADE_ENABLED = sh(returnStdout: true, script: 'test -n "${DOWNGRADE_ENABLED}" && echo ${DOWNGRADE_ENABLED} || (jq -r .downgrade.enabled < ci.json)').trim()
|
||||
env.QINIU_BUCKET = sh(returnStdout: true, script: 'jq -r .upload.bucket < ci.json').trim()
|
||||
env.ARTIFACT_REPOSITORY = sh(returnStdout: true, script: 'misc/parse_tag.sh $TAG_NAME type | grep release >/dev/null && echo easycorp || echo easycorp-snapshot').trim()
|
||||
env.ARTIFACT_HOST = "nexus.qc.oop.cc"
|
||||
env.ARTIFACT_PROTOCOL = "https"
|
||||
env.ARTIFACT_CRED_ID = "nexus-jenkins"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +144,7 @@ pipeline {
|
||||
container('package') {
|
||||
sh 'mkdir ${ZENTAO_RELEASE_PATH} && chown 1000:1000 ${ZENTAO_RELEASE_PATH}'
|
||||
sh 'git config --global pull.ff only'
|
||||
sh 'cp -av ${ZENTAO_BUILD_PATH}/adminer www/'
|
||||
sh 'pwd && ls -l && make ciCommon'
|
||||
sh 'ls -l ${ZENTAO_RELEASE_PATH}'
|
||||
}
|
||||
@@ -700,18 +705,7 @@ pipeline {
|
||||
|
||||
environment {
|
||||
REGISTRY_HOST="hub.zentao.net"
|
||||
CI_BUILD_PUBLIC_IMAGE="""${sh(
|
||||
returnStdout: true,
|
||||
script: 'test "$GIT_TAG_BUILD_TYPE" = release && echo true || echo false'
|
||||
).trim()}"""
|
||||
CI_PUBLIC_IMAGE_NAMESPACE="""${sh(
|
||||
returnStdout: true,
|
||||
script: "echo $GIT_URL | grep demo/zentao >/dev/null && echo test || echo app"
|
||||
).trim()}"""
|
||||
CI_INTERNAL_IMAGE_NAMESPACE="""${sh(
|
||||
returnStdout: true,
|
||||
script: "echo $GIT_URL | grep demo/zentao >/dev/null && echo test || echo app"
|
||||
).trim()}"""
|
||||
CI_BUILD_PUBLIC_IMAGE="true"
|
||||
}
|
||||
|
||||
stages() {
|
||||
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
pipeline {
|
||||
agent {
|
||||
kubernetes {
|
||||
inheritFrom "xuanim"
|
||||
yamlFile 'misc/ci/normal.yaml'
|
||||
}
|
||||
}
|
||||
|
||||
options {
|
||||
skipDefaultCheckout()
|
||||
}
|
||||
|
||||
environment {
|
||||
TZ="Asia/Shanghai"
|
||||
|
||||
ZENTAO_RELEASE_PATH = "${WORKSPACE}/release"
|
||||
XUANXUAN_SRC_PATH = "${WORKSPACE}/xuansrc"
|
||||
SRC_ZDOO_PATH = "${WORKSPACE}/zdoo"
|
||||
SRC_ZDOOEXT_PATH = "${WORKSPACE}/zdooext"
|
||||
SRC_ZENTAOEXT_PATH = "${WORKSPACE}/zentaoext"
|
||||
|
||||
// set to blank for auto-detect from ci.json
|
||||
DOWNGRADE_ENABLED = "false"
|
||||
DOWNGRADE_VERSIONS = "7.2,7.1,7.0,5.4"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
stage("Test Code") {
|
||||
|
||||
agent {
|
||||
kubernetes {
|
||||
inheritFrom "zentao-package build-docker xuanim"
|
||||
yamlFile 'misc/ci/basic-build.yaml'
|
||||
}
|
||||
}
|
||||
|
||||
stages {
|
||||
stage("Pull") {
|
||||
steps {
|
||||
checkout scm
|
||||
script {
|
||||
env.XUANVERSION = sh(returnStdout: true,script: 'jq -r .pkg.xuanxuan.gitVersion < ci.json').trim()
|
||||
env.ZENTAOEXT_VERSION = sh(returnStdout: true,script: 'jq -r .pkg.zentaoext.gitVersion < ci.json').trim()
|
||||
env.ZENTAOEXT_GIT_REPO = sh(returnStdout: true,script: 'jq -r .pkg.zentaoext.gitRepo < ci.json').trim()
|
||||
env.ZDOO_VERSION = sh(returnStdout: true,script: 'jq -r .pkg.zdoo.gitVersion < ci.json').trim()
|
||||
env.ZDOOEXT_VERSION = sh(returnStdout: true,script: 'jq -r .pkg.zdooext.gitVersion < ci.json').trim()
|
||||
}
|
||||
|
||||
dir('xuansrc') {
|
||||
checkout scmGit(branches: [[name: "${env.XUANVERSION}"]],
|
||||
userRemoteConfigs: [[credentialsId: 'git-zcorp-cc-jenkins-bot-http', url: 'https://git.zcorp.cc/easycorp/xuanxuan.git']]
|
||||
)
|
||||
}
|
||||
|
||||
dir('zdoo') {
|
||||
checkout scmGit(branches: [[name: "${env.ZDOO_VERSION}"]],
|
||||
extensions: [cloneOption(depth: 2, noTags: false, reference: '', shallow: true)],
|
||||
userRemoteConfigs: [[credentialsId: 'git-zcorp-cc-jenkins-bot-http', url: 'https://git.zcorp.cc/easycorp/zdoo.git']]
|
||||
)
|
||||
}
|
||||
|
||||
dir('zdooext') {
|
||||
checkout scmGit(branches: [[name: "${env.ZDOOEXT_VERSION}"]],
|
||||
extensions: [cloneOption(depth: 2, noTags: false, reference: '', shallow: true)],
|
||||
userRemoteConfigs: [[credentialsId: 'git-zcorp-cc-jenkins-bot-http', url: 'https://git.zcorp.cc/easycorp/zdooext.git']]
|
||||
)
|
||||
}
|
||||
|
||||
dir('zentaoext') {
|
||||
checkout scmGit(branches: [[name: "${env.ZENTAOEXT_VERSION}"]],
|
||||
extensions: [cloneOption(depth: 2, noTags: false, reference: '', shallow: true)],
|
||||
userRemoteConfigs: [[credentialsId: 'git-zcorp-cc-jenkins-bot-http', url: "${env.ZENTAOEXT_GIT_REPO}"]]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
stage("Setup Global Env") {
|
||||
steps {
|
||||
script {
|
||||
def ximUsers = sh(returnStdout: true,script: 'jq -r .notice.users < ci.json').trim()
|
||||
env.XIM_USERS = "qishiyao"
|
||||
env.XIM_GROUPS = "31a0008b-6e3e-4b7f-9b7b-396a46b1f8f4"
|
||||
|
||||
env.PMS_VERSION = sh(returnStdout: true, script: 'cat ${SRC_ZENTAOEXT_PATH}/VERSION').trim()
|
||||
env.BIZ_VERSION = sh(returnStdout: true, script: 'cat ${SRC_ZENTAOEXT_PATH}/BIZVERSION').trim()
|
||||
env.MAX_VERSION = sh(returnStdout: true, script: 'cat ${SRC_ZENTAOEXT_PATH}/MAXVERSION').trim()
|
||||
env.IPD_VERSION = sh(returnStdout: true, script: 'cat ${SRC_ZENTAOEXT_PATH}/IPDVERSION').trim()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
stage("Downgrade") {
|
||||
stages {
|
||||
stage("make Package") {
|
||||
steps {
|
||||
withCredentials([gitUsernamePassword(credentialsId: 'git-zcorp-cc-jenkins-bot-http',gitToolName: 'git-tool')]) {
|
||||
container('package') {
|
||||
sh 'mkdir ${ZENTAO_RELEASE_PATH} && chown 1000:1000 ${ZENTAO_RELEASE_PATH}'
|
||||
sh 'git config --global pull.ff only'
|
||||
sh 'pwd && ls -l && make ciCommon'
|
||||
sh 'ls -l ${ZENTAO_RELEASE_PATH}'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
stage("test downgrade") {
|
||||
steps {
|
||||
container('package') {
|
||||
sh 'unzip -q ZenTaoPMS.${PMS_VERSION}.zip'
|
||||
sh 'ls -l zentaopms'
|
||||
sh './misc/downgrade.sh -p "$DOWNGRADE_VERSIONS" -r zentaopms -i -s -o "$ZENTAO_RELEASE_PATH" framework lib module/*'
|
||||
}
|
||||
|
||||
publishHTML([
|
||||
allowMissing: true,
|
||||
alwaysLinkToLastBuild: false,
|
||||
keepAll: true,
|
||||
reportDir: env.ZENTAO_RELEASE_PATH,
|
||||
reportFiles: 'downgradeReport.html',
|
||||
reportName: 'DowngradeReport'
|
||||
])
|
||||
|
||||
container('xuanimbot') {
|
||||
sh 'git config --global --add safe.directory $(pwd)'
|
||||
sh 'test -f $ZENTAO_RELEASE_PATH/downgradeReport.html && /usr/local/bin/xuanimbot --title "`echo -n 6ZmN57qn5aSx6LSl | base64 --decode`" --url "${RUN_DISPLAY_URL}" --content "[PHP Syntax Report]($BUILD_URL/DowngradeReport/)" --debug --custom || echo "No syntax found"'
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // End Downgrade
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -157,6 +157,10 @@ zentaoxx:
|
||||
sed -i "s/'..\/..\/common\/view\/header.html.php'/\$$app->getModuleRoot() . 'common\/view\/header.html.php'/g" zentaoxx/extension/xuan/conference/view/admin.html.php
|
||||
sed -i "s/'..\/..\/common\/view\/footer.html.php'/\$$app->getModuleRoot() . 'common\/view\/footer.html.php'/g" zentaoxx/extension/xuan/conference/view/admin.html.php
|
||||
sed -i "s/\$$this->im->userGetChangedPassword()/array()/" zentaoxx/extension/xuan/im/control.php
|
||||
sed -i "s/->app->getModuleExtPath('', /->app->getModuleExtPath(/g" zentaoxx/extension/xuan/im/model/bot.php
|
||||
sed -i "s/\$$this->getModuleExtPath('', /\$$this->getModuleExtPath(/g" zentaoxx/framework/xuanxuan.class.php
|
||||
sed -i "s/, \$$version)\$$/, \$$version = '')/g" zentaoxx/extension/xuan/im/model.php
|
||||
sed -i "s/, \$$version)\$$/, \$$version = '')/g" zentaoxx/extension/xuan/im/model/conference.php
|
||||
sed -i "/.*->getAllDepts();/d" zentaoxx/extension/xuan/im/ext/bot/default.bot.php
|
||||
sed -i "s/lang->user->status/lang->user->clientStatus/" zentaoxx/extension/xuan/im/ext/bot/default.bot.php
|
||||
sed -i "s/.*->getRoleList();/\$$depts = \$$this->im->loadModel('dept')->getDeptPairs();\n\$$deptList = array_map(function(\$$k, \$$v) {return (object)array('id' => \$$k, 'name' => \$$v);}, array_keys(\$$depts), \$$depts);\n\$$roleList = \$$this->im->lang->user->roleList;/" zentaoxx/extension/xuan/im/ext/bot/default.bot.php
|
||||
@@ -282,7 +286,7 @@ ciCommon:
|
||||
zip -rq -9 ZenTaoALM.$(VERSION).int.zip zentaoalm
|
||||
|
||||
# downgrade
|
||||
@test "$(DOWNGRADE_ENABLED)" != "true" && echo "skip downgrade" || ./misc/downgrade.sh -p "$(DOWNGRADE_VERSIONS)" -i -r zentaopms -o "$(RELEASE_PATH)" framework/ module/*
|
||||
@test "$(DOWNGRADE_ENABLED)" != "true" && echo "skip downgrade" || ./misc/downgrade.sh -p "$(DOWNGRADE_VERSIONS)" -i -r zentaopms -o "$(RELEASE_PATH)" framework/ lib/ module/*
|
||||
|
||||
rm -fr zentaopms zentaoalm
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class gitlabWebhookEntry extends baseEntry
|
||||
|
||||
$this->loadModel('repo');
|
||||
|
||||
$repo = $this->repo->getRepoByID($repoID);
|
||||
$repo = $this->repo->getByID($repoID);
|
||||
if(empty($repo)) return;
|
||||
|
||||
$headers = getallheaders(); /* Fetch all HTTP request headers. */
|
||||
|
||||
@@ -108,6 +108,11 @@ class taskEntry extends entry
|
||||
$fields = 'name,type,desc,assignedTo,pri,estimate,left,consumed,story,parent,execution,module,closedReason,status,estStarted,deadline,team,teamEstimate,teamConsumed,teamLeft,multiple,mailto,uid';
|
||||
$this->batchSetPost($fields, $oldTask);
|
||||
|
||||
if($_POST['status'] == 'done')
|
||||
{
|
||||
if(!empty($oldTask->finishedBy)) $this->setPost('finishedBy', $oldTask->finishedBy);
|
||||
if(!empty($oldTask->finishedDate)) $this->setPost('finishedDate', $oldTask->finishedDate);
|
||||
}
|
||||
$control = $this->loadController('task', 'edit');
|
||||
$control->edit($taskID);
|
||||
|
||||
|
||||
@@ -15,8 +15,22 @@
|
||||
"gitVersion": "master"
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"public": {
|
||||
"namespace": {
|
||||
"release": "app",
|
||||
"snapshot": "test"
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"namespace": {
|
||||
"release": "app",
|
||||
"snapshot": "test"
|
||||
}
|
||||
}
|
||||
},
|
||||
"downgrade": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"versions": "7.2,7.1,7.0,5.4"
|
||||
},
|
||||
"upload": {
|
||||
|
||||
+33
-5
@@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}}
|
||||
if(!function_exists('getWebRoot')){function getWebRoot(){}}
|
||||
|
||||
/* 基本设置。Basic settings. */
|
||||
$config->version = '18.6'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
|
||||
$config->version = '18.7'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
|
||||
$config->liteVersion = '1.2'; // 迅捷版版本。 The version of Lite.
|
||||
$config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP.
|
||||
$config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time.
|
||||
@@ -66,10 +66,7 @@ $config->db->driver = 'mysql'; // 目前只支持MySQL数据库。Mus
|
||||
$config->db->encoding = 'UTF8'; // 数据库编码。 Encoding of database.
|
||||
$config->db->strictMode = false; // 关闭MySQL的严格模式。 Turn off the strict mode of MySQL.
|
||||
$config->db->prefix = 'zt_'; // 数据库表名前缀。 The prefix of the table name.
|
||||
$config->slaveDB->persistant = false;
|
||||
$config->slaveDB->driver = 'mysql';
|
||||
$config->slaveDB->encoding = 'UTF8';
|
||||
$config->slaveDB->strictMode = false;
|
||||
$config->slaveDBList = array(); // 支持多个从库。 Support multiple slave dbs.
|
||||
|
||||
/* 可用域名后缀列表。Domain postfix lists. */
|
||||
$config->domainPostfix = "|com|com.cn|com.hk|com.tw|com.vc|edu.cn|es|";
|
||||
@@ -164,6 +161,32 @@ $config->maxCount = 500;
|
||||
$config->batchMaxCount = 20;
|
||||
$config->moreLinks = array();
|
||||
|
||||
/* 渠成平台设置。CNE Api settings. */
|
||||
$config->inQuickon = getenv('IN_QUICKON');
|
||||
$config->inContainer = getenv('IN_CONTAINER');
|
||||
$config->k8space = 'quickon-system';
|
||||
$config->demoAccounts = ''; // 用于演示的账号列表,该账号安装的应用30钟后会自动删除。 In account list for demo, app instance of demo will be removed in 30 minutes.
|
||||
$config->demoAppLife = 30; // Demo安装的应用实例存续时长(分钟)。The minutes life of instance which demo account installed.
|
||||
$config->CNE = new stdclass();
|
||||
$config->CNE->api = new stdclass();
|
||||
$config->CNE->api->host = getenv('CNE_API_HOST');
|
||||
$config->CNE->api->auth = 'X-Auth-Token';
|
||||
$config->CNE->api->token = getenv('CNE_API_TOKEN'); // Please set token in my.php.
|
||||
$config->CNE->api->headers = array('Content-Type: application/json');
|
||||
$config->CNE->api->channel = 'stable';
|
||||
|
||||
$config->CNE->app = new stdclass;
|
||||
$config->CNE->app->domain = 'dev.haogs.cn';
|
||||
|
||||
$config->cloud = new stdclass;
|
||||
$config->cloud->api = new stdclass;
|
||||
$config->cloud->api->host = 'https://api.qucheng.com';
|
||||
$config->cloud->api->auth = 'X-Auth-Token';
|
||||
$config->cloud->api->token = 'gwaN4KynqNqQoPD7eN8s'; // Please set token in my.php.
|
||||
$config->cloud->api->headers = array('Content-Type: application/json');
|
||||
$config->cloud->api->channel = 'stable';
|
||||
$config->cloud->api->switchChannel = false;
|
||||
|
||||
/* 配置参数过滤。Filter param settings. */
|
||||
$filterConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'filter.php';
|
||||
if(file_exists($filterConfig)) include $filterConfig;
|
||||
@@ -205,3 +228,8 @@ else
|
||||
unset($config->maxVersion);
|
||||
unset($config->ipdVersion);
|
||||
}
|
||||
|
||||
/* Set zin config. */
|
||||
$config->zin = new stdClass();
|
||||
$config->zin->mode = 'compatible'; // 启用兼容 18.x 模式。
|
||||
$config->zin->extraCSS = 'compatible.css'; // 额外的 CSS 样式文件。
|
||||
|
||||
+92
-1
@@ -240,6 +240,73 @@ $config->openMethods[] = 'custom.index';
|
||||
$config->openMethods[] = 'testcase.getxmindimport';
|
||||
$config->openMethods[] = 'testcase.showxmindimport';
|
||||
$config->openMethods[] = 'testcase.savexmindimport';
|
||||
$config->openMethods[] = 'search.buildzinform';
|
||||
$config->openMethods[] = 'search.buildzinquery';
|
||||
$config->openMethods[] = 'search.savezinquery';
|
||||
$config->openMethods[] = 'space.createapplication';
|
||||
$config->openMethods[] = 'ai.adminindex';
|
||||
$config->openMethods[] = 'sonarqube.create';
|
||||
$config->openMethods[] = 'sonarqube.edit';
|
||||
$config->openMethods[] = 'sonarqube.browseproject';
|
||||
$config->openMethods[] = 'sonarqube.createproject';
|
||||
$config->openMethods[] = 'sonarqube.deleteproject';
|
||||
$config->openMethods[] = 'sonarqube.execjob';
|
||||
$config->openMethods[] = 'sonarqube.reportview';
|
||||
$config->openMethods[] = 'sonarqube.browseissue';
|
||||
$config->openMethods[] = 'gitlab.create';
|
||||
$config->openMethods[] = 'gitlab.edit';
|
||||
$config->openMethods[] = 'gitlab.importissue';
|
||||
$config->openMethods[] = 'gitlab.binduser';
|
||||
$config->openMethods[] = 'gitlab.browseproject';
|
||||
$config->openMethods[] = 'gitlab.createproject';
|
||||
$config->openMethods[] = 'gitlab.editproject';
|
||||
$config->openMethods[] = 'gitlab.deleteproject';
|
||||
$config->openMethods[] = 'gitlab.browsegroup';
|
||||
$config->openMethods[] = 'gitlab.creategroup';
|
||||
$config->openMethods[] = 'gitlab.editgroup';
|
||||
$config->openMethods[] = 'gitlab.deletegroup';
|
||||
$config->openMethods[] = 'gitlab.managegroupmembers';
|
||||
$config->openMethods[] = 'gitlab.browseuser';
|
||||
$config->openMethods[] = 'gitlab.createuser';
|
||||
$config->openMethods[] = 'gitlab.edituser';
|
||||
$config->openMethods[] = 'gitlab.createbranch';
|
||||
$config->openMethods[] = 'gitlab.browsebranch';
|
||||
$config->openMethods[] = 'gitlab.webhook';
|
||||
$config->openMethods[] = 'gitlab.createwebhook';
|
||||
$config->openMethods[] = 'gitlab.manageprojectmembers';
|
||||
$config->openMethods[] = 'gitlab.managebranchpriv';
|
||||
$config->openMethods[] = 'gitlab.managetagpriv';
|
||||
$config->openMethods[] = 'gitlab.browsetag';
|
||||
$config->openMethods[] = 'gitlab.createtag';
|
||||
$config->openMethods[] = 'gitlab.deletetag';
|
||||
$config->openMethods[] = 'gogs.create';
|
||||
$config->openMethods[] = 'gogs.edit';
|
||||
$config->openMethods[] = 'gogs.binduser';
|
||||
$config->openMethods[] = 'gitea.create';
|
||||
$config->openMethods[] = 'gitea.edit';
|
||||
$config->openMethods[] = 'gitea.binduser';
|
||||
$config->openMethods[] = 'jenkins.create';
|
||||
$config->openMethods[] = 'jenkins.edit';
|
||||
$config->openMethods[] = 'instance.createexternalapp';
|
||||
$config->openMethods[] = 'instance.editexternalapp';
|
||||
$config->openMethods[] = 'instance.deleteexternalapp';
|
||||
$config->openMethods[] = 'instance.setting';
|
||||
$config->openMethods[] = 'instance.ajaxdbauthurl';
|
||||
$config->openMethods[] = 'instance.view';
|
||||
$config->openMethods[] = 'instance.install';
|
||||
$config->openMethods[] = 'instance.visit';
|
||||
$config->openMethods[] = 'instance.ajaxstatus';
|
||||
$config->openMethods[] = 'instance.ajaxstart';
|
||||
$config->openMethods[] = 'instance.ajaxstop';
|
||||
$config->openMethods[] = 'instance.upgrade';
|
||||
$config->openMethods[] = 'instance.ajaxuninstall';
|
||||
$config->openMethods[] = 'search.deletezinquery';
|
||||
$config->openMethods[] = 'space.edit';
|
||||
$config->openMethods[] = 'space.binduser';
|
||||
$config->openMethods[] = 'space.getstoreappinfo';
|
||||
$config->openMethods[] = 'store.browse';
|
||||
$config->openMethods[] = 'store.appview';
|
||||
$config->openMethods[] = 'system.editdomain';
|
||||
|
||||
$config->openModules = array();
|
||||
$config->openModules[] = 'install';
|
||||
@@ -342,6 +409,9 @@ define('TABLE_COMPILE', '`' . $config->db->prefix . 'compile`');
|
||||
define('TABLE_MR', '`' . $config->db->prefix . 'mr`');
|
||||
define('TABLE_MRAPPROVAL', '`' . $config->db->prefix . 'mrapproval`');
|
||||
|
||||
define('TABLE_SERVERROOM', '`' . $config->db->prefix . 'serverroom`');
|
||||
define('TABLE_ACCOUNT', '`' . $config->db->prefix . 'account`');
|
||||
define('TABLE_HOST', '`' . $config->db->prefix . 'host`');
|
||||
define('TABLE_REPO', '`' . $config->db->prefix . 'repo`');
|
||||
define('TABLE_RELATION', '`' . $config->db->prefix . 'relation`');
|
||||
define('TABLE_REPOHISTORY', '`' . $config->db->prefix . 'repohistory`');
|
||||
@@ -376,6 +446,13 @@ define('TABLE_PRIVLANG', '`' . $config->db->prefix . 'privlang`');
|
||||
define('TABLE_PRIVMANAGER', '`' . $config->db->prefix . 'privmanager`');
|
||||
define('TABLE_PRIVRELATION', '`' . $config->db->prefix . 'privrelation`');
|
||||
|
||||
define('TABLE_SPACE', '`' . $config->db->prefix . 'space`');
|
||||
define('TABLE_INSTANCE', '`' . $config->db->prefix . 'instance`');
|
||||
define('TABLE_SOLUTION', '`' . $config->db->prefix . 'solution`');
|
||||
define('TABLE_ARTIFACTREPO', '`' . $config->db->prefix . 'artifactrepo`');
|
||||
define('TABLE_PROMPT', '`' . $config->db->prefix . 'prompt`');
|
||||
define('TABLE_PROMPTROLE', '`' . $config->db->prefix . 'promptrole`');
|
||||
|
||||
$config->objectTables['product'] = TABLE_PRODUCT;
|
||||
$config->objectTables['productplan'] = TABLE_PRODUCTPLAN;
|
||||
$config->objectTables['story'] = TABLE_STORY;
|
||||
@@ -434,8 +511,16 @@ $config->objectTables['privlang'] = TABLE_PRIVLANG;
|
||||
$config->objectTables['privmanager'] = TABLE_PRIVMANAGER;
|
||||
$config->objectTables['privrelation'] = TABLE_PRIVRELATION;
|
||||
$config->objectTables['scene'] = TABLE_SCENE;
|
||||
$config->objectTables['account'] = TABLE_ACCOUNT;
|
||||
$config->objectTables['serverroom'] = TABLE_SERVERROOM;
|
||||
$config->objectTables['host'] = TABLE_ZAHOST;
|
||||
$config->objectTables['instance'] = TABLE_INSTANCE;
|
||||
$config->objectTables['space'] = TABLE_SPACE;
|
||||
$config->objectTables['solution'] = TABLE_SOLUTION;
|
||||
$config->objectTables['artifactrepo'] = TABLE_ARTIFACTREPO;
|
||||
$config->objectTables['prompt'] = TABLE_PROMPT;
|
||||
|
||||
$config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions');
|
||||
$config->newFeatures = array('aiPrompts', 'promptDesign', 'promptExec');
|
||||
$config->disabledFeatures = '';
|
||||
$config->closedFeatures = '';
|
||||
|
||||
@@ -470,3 +555,9 @@ $config->featureGroup->other = array('devops', 'kanban');
|
||||
|
||||
$config->bi = new stdclass();
|
||||
$config->bi->pickerHeight = 150;
|
||||
|
||||
$config->hasDropmenuApps = array('program', 'project', 'product', 'execution', 'qa', 'admin');
|
||||
$config->hasBranchMenuModules = array('product', 'story', 'release', 'bug', 'testcase');
|
||||
$config->excludeDropmenuList = array('program-browse', 'product-all', 'product-index', 'execution-all', 'project-browse', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit');
|
||||
$config->hasSwitcherModules = array('design');
|
||||
$config->excludeSwitcherList = array();
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,812 @@
|
||||
-- DROP TABLE IF EXISTS `zt_space`;
|
||||
CREATE TABLE `zt_space` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(200) NOT NULL,
|
||||
`k8space` char(64) NOT NULL,
|
||||
`owner` char(30) NOT NULL,
|
||||
`default` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`createdAt` datetime NOT NULL,
|
||||
`deleted` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_instance`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_instance` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`space` mediumint(8) unsigned NOT NULL DEFAULT 0,
|
||||
`solution` mediumint(8) unsigned NOT NULL DEFAULT 0,
|
||||
`name` char(50) DEFAULT '',
|
||||
`appID` mediumint(8) unsigned NOT NULL DEFAULT 0,
|
||||
`appName` char(50) NOT NULL DEFAULT '',
|
||||
`appVersion` char(20) NOT NULL DEFAULT '',
|
||||
`chart` char(50) NOT NULL DEFAULT '',
|
||||
`logo` varchar(255) DEFAULT '',
|
||||
`version` char(50) NOT NULL DEFAULT '',
|
||||
`desc` text,
|
||||
`introduction` varchar(500) DEFAULT '',
|
||||
`source` char(20) NOT NULL DEFAULT '',
|
||||
`channel` char(20) DEFAULT '',
|
||||
`k8name` char(64) NOT NULL DEFAULT '',
|
||||
`status` char(20) NOT NULL DEFAULT '',
|
||||
`pinned` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
`domain` char(255) NOT NULL DEFAULT '',
|
||||
`smtpSnippetName` char(30) NULL DEFAULT '',
|
||||
`ldapSnippetName` char(30) NULL DEFAULT '',
|
||||
`ldapSettings` text,
|
||||
`dbSettings` text,
|
||||
`autoBackup` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`backupKeepDays` int unsigned NOT NULL DEFAULT 1,
|
||||
`autoRestore` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`env` text,
|
||||
`createdBy` char(30) NOT NULL DEFAULT '',
|
||||
`createdAt` datetime NOT NULL,
|
||||
`deleted` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `space` (`space`),
|
||||
KEY `k8name` (`k8name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_solution`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_solution` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` char(50),
|
||||
`appID` mediumint(8) unsigned NOT NULL,
|
||||
`appName` char(50) NOT NULL,
|
||||
`appVersion` char(20) NOT NULL,
|
||||
`version` char(50) NOT NULL,
|
||||
`chart` char(50) NOT NULL,
|
||||
`cover` varchar(255),
|
||||
`desc` text,
|
||||
`introduction` varchar(500),
|
||||
`source` char(20) NOT NULL,
|
||||
`channel` char(20),
|
||||
`components` text,
|
||||
`status` char(20) NOT NULL,
|
||||
`deleted` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`createdBy` char(30) NOT NULL,
|
||||
`createdAt` datetime NOT NULL,
|
||||
`updatedDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_artifactrepo`;
|
||||
CREATE TABLE `zt_artifactrepo` (
|
||||
`id` smallint(8) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(45) CHARACTER SET utf8 NOT NULL,
|
||||
`products` varchar(1000) CHARACTER SET utf8 NOT NULL,
|
||||
`serverID` smallint(8) NOT NULL,
|
||||
`repoName` varchar(45) CHARACTER SET utf8 NOT NULL,
|
||||
`format` varchar(10) CHARACTER SET utf8 NOT NULL,
|
||||
`type` char(7) CHARACTER SET utf8 NOT NULL,
|
||||
`status` varchar(10) CHARACTER SET utf8 NOT NULL,
|
||||
`createdBy` varchar(30) CHARACTER SET utf8 NOT NULL,
|
||||
`createdDate` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`editedBy` varchar(30) CHARACTER SET utf8 NOT NULL,
|
||||
`editedDate` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`deleted` tinyint(4) UNSIGNED NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
ALTER TABLE `zt_build` ADD `artifactRepoID` MEDIUMINT(8) UNSIGNED NOT NULL AFTER `bugs`;
|
||||
|
||||
REPLACE INTO
|
||||
`zt_privmanager` (`id`, `parent`, `code`, `type`, `edition`, `vision`, `order`)
|
||||
VALUES
|
||||
(557, 457, 'ops', 'module', ',open,biz,max,ipd,', ',rnd,', 1920),
|
||||
(658, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2160),
|
||||
(126, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2180),
|
||||
(659, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2200),
|
||||
(127, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2220),
|
||||
(128, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2240),
|
||||
(129, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2260),
|
||||
(131, 557, '', 'package', ',biz,max,ipd,', ',rnd,', 2280),
|
||||
(245, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2380),
|
||||
(660, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2300),
|
||||
(661, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2320),
|
||||
(662, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2340),
|
||||
(663, 557, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2360),
|
||||
(517, 502, 'repo', 'module', ',open,biz,max,ipd,', ',rnd,', 2400),
|
||||
(664, 517, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2420),
|
||||
(665, 517, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2440),
|
||||
(300, 517, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2460),
|
||||
(299, 517, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2480),
|
||||
(666, 517, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2500),
|
||||
(506, 502, 'repocode', 'module', ',open,biz,max,ipd,', ',rnd,', 2520),
|
||||
(667, 506, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2540),
|
||||
(668, 506, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2560),
|
||||
(669, 502, 'codereview', 'module', ',biz,max,ipd,', ',rnd,', 2580),
|
||||
(227, 669, '', 'package', ',biz,max,ipd,', ',rnd,lite,or,', 2600),
|
||||
(670, 669, '', 'package', ',biz,max,ipd,', ',rnd,', 2620),
|
||||
(671, 669, '', 'package', ',biz,max,ipd,', ',rnd,', 2640),
|
||||
(505, 502, 'mr', 'module', ',open,biz,max,ipd,', ',rnd,', 2660),
|
||||
(672, 505, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2680),
|
||||
(516, 502, 'pipeline', 'module', ',open,biz,max,ipd,', ',rnd,', 2700),
|
||||
(673, 516, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2720),
|
||||
(507, 502, 'ci', 'module', ',open,biz,max,ipd,', ',rnd,', 2740),
|
||||
(674, 502, 'artifactrepo', 'module', ',open,biz,max,ipd,', ',rnd,', 2760),
|
||||
(675, 674, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2780),
|
||||
(676, 674, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2800),
|
||||
(677, 674, '', 'package', ',open,biz,max,ipd,', ',rnd,', 2820),
|
||||
(678, 515, 'application', 'package', ',open,biz,max,ipd,', ',rnd,', 2840),
|
||||
(679, 515, 'application', 'package', ',open,biz,max,ipd,', ',rnd,', 2860),
|
||||
(515, 502, 'app', 'module', ',open,biz,max,ipd,', ',rnd,', 3000),
|
||||
(563, 502, 'deploy', 'module', ',biz,max,ipd,', ',rnd,', 3020),
|
||||
(130, 557, '', 'package', ',biz,max,ipd,', ',rnd,lite,or,', 2270);
|
||||
|
||||
DELETE FROM `zt_priv` WHERE id=834;
|
||||
DELETE FROM `zt_priv` WHERE `module` IN ('gitlab', 'jenkins', 'gitea', 'gogs', 'sonarqube');
|
||||
UPDATE `zt_priv` SET `edition`=',open,biz,max,ipd,' WHERE `module`='host';
|
||||
UPDATE `zt_priv` SET `edition`=',open,biz,max,ipd,' WHERE `module`='account';
|
||||
UPDATE `zt_priv` SET `edition`=',open,biz,max,ipd,' WHERE `module`='serverroom';
|
||||
REPLACE INTO
|
||||
`zt_priv` (`id`, `module`, `method`, `parent`, `edition`, `vision`, `system`, `order`)
|
||||
VALUES
|
||||
(2136, 'system', 'dashboard', 658, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(1285, 'system', 'dblist', 126, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(1286, 'system', 'configdomain', 126, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(2137, 'system', 'ossview', 126, ',open,biz,max,ipd,', ',rnd,', '1', 15),
|
||||
(2138, 'ops', 'provide', 660, ',open,biz,max,ipd,', ',rnd,', '1', 20),
|
||||
(2139, 'ops', 'city', 661, ',open,biz,max,ipd,', ',rnd,', '1', 25),
|
||||
(2140, 'ops', 'cpubrand', 662, ',open,biz,max,ipd,', ',rnd,', '1', 30),
|
||||
(2141, 'ops', 'os', 663, ',open,biz,max,ipd,', ',rnd,', '1', 35),
|
||||
(1051, 'repo', 'maintain', 664, ',open,biz,max,ipd,', ',rnd,', '1', 0),
|
||||
(1052, 'repo', 'browse', 664, ',open,biz,max,ipd', ',rnd,', '1', 30),
|
||||
(1047, 'repo', 'create', 665, ',open,biz,max,ipd', ',rnd,', '1', 5),
|
||||
(1048, 'repo', 'edit', 665, ',open,biz,max,ipd', ',rnd,', '1', 10),
|
||||
(1060, 'repo', 'apiGetRepoByUrl', 665, ',open,biz,max,ipd', ',rnd,', '1', 15),
|
||||
(1694, 'repo', 'import', 665, ',open,biz,max,ipd', ',rnd,', '1', 20),
|
||||
(1049, 'repo', 'delete', 666, ',open,biz,max,ipd', ',rnd,', '1', 25),
|
||||
(1066, 'repo', 'review', 227, ',biz,max,ipd', ',rnd,', '1', 0),
|
||||
(1067, 'repo', 'addBug', 670, ',biz,max,ipd', ',rnd,', '1', 5),
|
||||
(1068, 'repo', 'editBug', 670, ',biz,max,ipd', ',rnd,', '1', 15),
|
||||
(1070, 'repo', 'addComment', 670, ',biz,max,ipd', ',rnd,', '1', 20),
|
||||
(1071, 'repo', 'editComment', 670, ',biz,max,ipd', ',rnd,', '1', 25),
|
||||
(1069, 'repo', 'deleteBug', 671, ',biz,max,ipd', ',rnd,', '1', 30),
|
||||
(1072, 'repo', 'deleteComment', 670, ',biz,max,ipd', ',rnd,', '1', 35),
|
||||
(1050, 'repo', 'showSyncCommit', 667, ',open,biz,max,ipd', ',rnd,', '1', 0),
|
||||
(1055, 'repo', 'log', 667, ',open,biz,max,ipd', ',rnd,', '1', 5),
|
||||
(1056, 'repo', 'revision', 667, ',open,biz,max,ipd', ',rnd,', '1', 10),
|
||||
(1053, 'repo', 'view', 667, ',open,biz,max,ipd', ',rnd,', '1', 15),
|
||||
(1061, 'repo', 'downloadCode', 668, ',open,biz,max,ipd', ',rnd,', '1', 5),
|
||||
(1058, 'repo', 'download', 668, ',open,biz,max,ipd', ',rnd,', '1', 10),
|
||||
(1054, 'repo', 'diff', 668, ',open,biz,max,ipd', ',rnd,', '1', 15),
|
||||
(1057, 'repo', 'blame', 668, ',open,biz,max,ipd', ',rnd,', '1', 20),
|
||||
(1062, 'repo', 'linkStory', 668, ',open,biz,max,ipd', ',rnd,', '1', 25),
|
||||
(1063, 'repo', 'linkBug', 668, ',open,biz,max,ipd', ',rnd,', '1', 30),
|
||||
(1064, 'repo', 'linkTask', 668, ',open,biz,max,ipd', ',rnd,', '1', 35),
|
||||
(1065, 'repo', 'unlink', 668, ',open,biz,max,ipd', ',rnd,', '1', 40),
|
||||
(820, 'mr', 'browse', 230, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(823, 'mr', 'view', 230, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(825, 'mr', 'diff', 230, ',open,biz,max,ipd,', ',rnd,', '1', 15),
|
||||
(826, 'mr', 'link', 230, ',open,biz,max,ipd,', ',rnd,', '1', 20),
|
||||
(819, 'mr', 'create', 248, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(821, 'mr', 'edit', 248, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(824, 'mr', 'accept', 248, ',open,biz,max,ipd,', ',rnd,', '1', 20),
|
||||
(827, 'mr', 'linkStory', 248, ',open,biz,max,ipd,', ',rnd,', '1', 25),
|
||||
(828, 'mr', 'linkBug', 248, ',open,biz,max,ipd,', ',rnd,', '1', 30),
|
||||
(829, 'mr', 'linkTask', 248, ',open,biz,max,ipd,', ',rnd,', '1', 35),
|
||||
(830, 'mr', 'unlink', 248, ',open,biz,max,ipd,', ',rnd,', '1', 40),
|
||||
(831, 'mr', 'approval', 248, ',open,biz,max,ipd,', ',rnd,', '1', 45),
|
||||
(832, 'mr', 'close', 248, ',open,biz,max,ipd,', ',rnd,', '1', 50),
|
||||
(833, 'mr', 'reopen', 248, ',open,biz,max,ipd,', ',rnd,', '1', 55),
|
||||
(822, 'mr', 'delete', 672, ',open,biz,max,ipd,', ',rnd,', '1', 4),
|
||||
(1082, 'job', 'browse', 246, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(1087, 'job', 'view', 246, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(1083, 'job', 'create', 247, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(1084, 'job', 'edit', 247, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(1086, 'job', 'exec', 247, ',open,biz,max,ipd,', ',rnd,', '1', 15),
|
||||
(1075, 'compile', 'browse', 247, ',open,biz,max,ipd,', ',rnd,', '1', 20),
|
||||
(1076, 'compile', 'logs', 247, ',open,biz,max,ipd,', ',rnd,', '1', 25),
|
||||
(1077, 'compile', 'syncCompile', 247, ',open,biz,max,ipd,', ',rnd,', '1', 30),
|
||||
(2142, 'artifactrepo', 'browse', 675, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(2143, 'artifactrepo', 'ajaxGetArtifactRepos', 675, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(2144, 'artifactrepo', 'create', 676, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(2145, 'artifactrepo', 'edit', 676, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(2146, 'artifactrepo', 'ajaxUpdateArtifactRepos', 676, ',open,biz,max,ipd,', ',rnd,', '1', 15),
|
||||
(2147, 'artifactrepo', 'delete', 677, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(2148, 'space', 'browse', 678, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(2149, 'instance', 'manage', 679, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(2160, 'ops', 'stage', 132, ',biz,max,ipd,', ',rnd,', '1', 10);
|
||||
|
||||
REPLACE INTO
|
||||
`zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`)
|
||||
VALUES
|
||||
(658, 'manager', 'zh-cn', '', '仪表盘', ''),
|
||||
(658, 'manager', 'zh-tw', '', '儀表盤', ''),
|
||||
(658, 'manager', 'de', '', 'Dashboard', ''),
|
||||
(658, 'manager', 'en', '', 'Dashboard', ''),
|
||||
(658, 'manager', 'fr', '', 'Dashboard', ''),
|
||||
(126, 'manager', 'zh-cn', '', '平台', ''),
|
||||
(126, 'manager', 'zh-tw', '', '平臺', ''),
|
||||
(126, 'manager', 'de', '', 'Platform', ''),
|
||||
(126, 'manager', 'en', '', 'Platform', ''),
|
||||
(126, 'manager', 'fr', '', 'Platform', ''),
|
||||
(659, 'manager', 'zh-cn', '', '资源', ''),
|
||||
(659, 'manager', 'zh-tw', '', '資源', ''),
|
||||
(659, 'manager', 'de', '', 'Resource', ''),
|
||||
(659, 'manager', 'en', '', 'Resource', ''),
|
||||
(659, 'manager', 'fr', '', 'Resource', ''),
|
||||
(660, 'manager', 'zh-cn', '', '服务商管理', ''),
|
||||
(660, 'manager', 'zh-tw', '', '服務商管理', ''),
|
||||
(660, 'manager', 'de', '', 'Provider', ''),
|
||||
(660, 'manager', 'en', '', 'Provider', ''),
|
||||
(660, 'manager', 'fr', '', 'Provider', ''),
|
||||
(661, 'manager', 'zh-cn', '', '城市管理', ''),
|
||||
(661, 'manager', 'zh-tw', '', '城市管理', ''),
|
||||
(661, 'manager', 'de', '', 'City', ''),
|
||||
(661, 'manager', 'en', '', 'City', ''),
|
||||
(661, 'manager', 'fr', '', 'City', ''),
|
||||
(662, 'manager', 'zh-cn', '', 'CPU管理', ''),
|
||||
(662, 'manager', 'zh-tw', '', 'CPU管理', ''),
|
||||
(662, 'manager', 'de', '', 'Cpu Brand', ''),
|
||||
(662, 'manager', 'en', '', 'Cpu Brand', ''),
|
||||
(662, 'manager', 'fr', '', 'Cpu Brand', ''),
|
||||
(663, 'manager', 'zh-cn', '', '系统版本管理', ''),
|
||||
(663, 'manager', 'zh-tw', '', '系統版本管理', ''),
|
||||
(663, 'manager', 'de', '', 'OS Version', ''),
|
||||
(663, 'manager', 'en', '', 'OS Version', ''),
|
||||
(663, 'manager', 'fr', '', 'OS Version', ''),
|
||||
(129, 'manager', 'zh-cn', '', '账号管理', ''),
|
||||
(129, 'manager', 'zh-tw', '', '賬號管理', ''),
|
||||
(517, 'manager', 'zh-cn', '', '代码库', ''),
|
||||
(517, 'manager', 'zh-tw', '', '代碼庫', ''),
|
||||
(517, 'manager', 'de', '', 'Repository', ''),
|
||||
(517, 'manager', 'en', '', 'Repository', ''),
|
||||
(517, 'manager', 'fr', '', 'Repository', ''),
|
||||
(506, 'manager', 'zh-cn', '', '代码', ''),
|
||||
(506, 'manager', 'zh-tw', '', '代碼', ''),
|
||||
(506, 'manager', 'de', '', 'Code', ''),
|
||||
(506, 'manager', 'en', '', 'Code', ''),
|
||||
(506, 'manager', 'fr', '', 'Code', ''),
|
||||
(664, 'manager', 'zh-cn', '', '浏览代码库', ''),
|
||||
(664, 'manager', 'zh-tw', '', '瀏覽代碼庫', ''),
|
||||
(664, 'manager', 'de', '', 'Repo List', ''),
|
||||
(664, 'manager', 'en', '', 'Repo List', ''),
|
||||
(664, 'manager', 'fr', '', 'Repo List', ''),
|
||||
(665, 'manager', 'zh-cn', '', '创建维护代码库', ''),
|
||||
(665, 'manager', 'zh-tw', '', '創建維護代碼庫', ''),
|
||||
(665, 'manager', 'de', '', 'Manage Repository', ''),
|
||||
(665, 'manager', 'en', '', 'Manage Repository', ''),
|
||||
(665, 'manager', 'fr', '', 'Manage Repository', ''),
|
||||
(666, 'manager', 'zh-cn', '', '删除代码库', ''),
|
||||
(666, 'manager', 'zh-tw', '', '刪除代碼庫', ''),
|
||||
(666, 'manager', 'de', '', 'Delete Repository', ''),
|
||||
(666, 'manager', 'en', '', 'Delete Repository', ''),
|
||||
(666, 'manager', 'fr', '', 'Delete Repository', ''),
|
||||
(667, 'manager', 'zh-cn', '', '浏览代码', ''),
|
||||
(667, 'manager', 'zh-tw', '', '瀏覽代碼', ''),
|
||||
(667, 'manager', 'de', '', 'Code View', ''),
|
||||
(667, 'manager', 'en', '', 'Code View', ''),
|
||||
(667, 'manager', 'fr', '', 'Code View', ''),
|
||||
(668, 'manager', 'zh-cn', '', '维护代码', ''),
|
||||
(668, 'manager', 'zh-tw', '', '維護代碼', ''),
|
||||
(668, 'manager', 'de', '', 'Manage Code', ''),
|
||||
(668, 'manager', 'en', '', 'Manage Code', ''),
|
||||
(668, 'manager', 'fr', '', 'Manage Code', ''),
|
||||
(669, 'manager', 'zh-cn', '', '问题', ''),
|
||||
(669, 'manager', 'zh-tw', '', '問題', ''),
|
||||
(669, 'manager', 'de', '', 'Review', ''),
|
||||
(669, 'manager', 'en', '', 'Review', ''),
|
||||
(669, 'manager', 'fr', '', 'Review', ''),
|
||||
(227, 'manager', 'zh-cn', '', '浏览问题', ''),
|
||||
(227, 'manager', 'zh-tw', '', '瀏覽問題', ''),
|
||||
(227, 'manager', 'de', '', 'Review List', ''),
|
||||
(227, 'manager', 'en', '', 'Review List', ''),
|
||||
(227, 'manager', 'fr', '', 'Review List', ''),
|
||||
(670, 'manager', 'zh-cn', '', '创建维护问题', ''),
|
||||
(670, 'manager', 'zh-tw', '', '创建维护問題', ''),
|
||||
(670, 'manager', 'de', '', 'Manage Review', ''),
|
||||
(670, 'manager', 'en', '', 'Manage Review', ''),
|
||||
(670, 'manager', 'fr', '', 'Manage Review', ''),
|
||||
(671, 'manager', 'zh-cn', '', '删除问题', ''),
|
||||
(671, 'manager', 'zh-tw', '', '刪除問題', ''),
|
||||
(671, 'manager', 'de', '', 'Delete Review', ''),
|
||||
(671, 'manager', 'en', '', 'Delete Review', ''),
|
||||
(671, 'manager', 'fr', '', 'Delete Review', ''),
|
||||
(230, 'manager', 'zh-cn', '', '浏览合并请求', ''),
|
||||
(230, 'manager', 'zh-tw', '', '浏览合并請求', ''),
|
||||
(230, 'manager', 'de', '', 'MR List', ''),
|
||||
(230, 'manager', 'en', '', 'MR List', ''),
|
||||
(230, 'manager', 'fr', '', 'MR List', ''),
|
||||
(248, 'manager', 'zh-cn', '', '创建维护合并请求', ''),
|
||||
(248, 'manager', 'zh-tw', '', '创建维护合并請求', ''),
|
||||
(248, 'manager', 'de', '', 'Manage MR', ''),
|
||||
(248, 'manager', 'en', '', 'Manage MR', ''),
|
||||
(248, 'manager', 'fr', '', 'Manage MR', ''),
|
||||
(672, 'manager', 'zh-cn', '', '删除合并请求', ''),
|
||||
(672, 'manager', 'zh-tw', '', '刪除合并請求', ''),
|
||||
(672, 'manager', 'de', '', 'Delete MR', ''),
|
||||
(672, 'manager', 'en', '', 'Delete MR', ''),
|
||||
(672, 'manager', 'fr', '', 'Delete MR', ''),
|
||||
(246, 'manager', 'zh-cn', '', '浏览流水线', ''),
|
||||
(246, 'manager', 'zh-tw', '', '浏览流水綫', ''),
|
||||
(246, 'manager', 'de', '', 'PipeLine List', ''),
|
||||
(246, 'manager', 'en', '', 'PipeLine List', ''),
|
||||
(246, 'manager', 'fr', '', 'PipeLine List', ''),
|
||||
(247, 'manager', 'zh-cn', '', '创建维护流水线', ''),
|
||||
(247, 'manager', 'zh-tw', '', '创建维护流水綫', ''),
|
||||
(247, 'manager', 'de', '', 'Manage PipeLine', ''),
|
||||
(247, 'manager', 'en', '', 'Manage PipeLine', ''),
|
||||
(247, 'manager', 'fr', '', 'Manage PipeLine', ''),
|
||||
(673, 'manager', 'zh-cn', '', '删除流水线', ''),
|
||||
(673, 'manager', 'zh-tw', '', '刪除流水綫', ''),
|
||||
(673, 'manager', 'de', '', 'Delete PipeLine', ''),
|
||||
(673, 'manager', 'en', '', 'Delete PipeLine', ''),
|
||||
(673, 'manager', 'fr', '', 'Delete PipeLine', ''),
|
||||
(674, 'manager', 'zh-cn', 'artifactrepo', '制品库', ''),
|
||||
(674, 'manager', 'zh-tw', 'artifactrepo', '製品庫', ''),
|
||||
(674, 'manager', 'de', 'artifactrepo', 'Artifact Repo', ''),
|
||||
(674, 'manager', 'en', 'artifactrepo', 'Artifact Repo', ''),
|
||||
(674, 'manager', 'fr', 'artifactrepo', 'Artifact Repo', ''),
|
||||
(675, 'manager', 'zh-cn', '', '浏览制品库', ''),
|
||||
(675, 'manager', 'zh-tw', '', '浏览製品庫', ''),
|
||||
(675, 'manager', 'de', '', 'Artifact Repo List', ''),
|
||||
(675, 'manager', 'en', '', 'Artifact Repo List', ''),
|
||||
(675, 'manager', 'fr', '', 'Artifact Repo List', ''),
|
||||
(676, 'manager', 'zh-cn', '', '创建维护制品库', ''),
|
||||
(676, 'manager', 'zh-tw', '', '创建维护製品庫', ''),
|
||||
(676, 'manager', 'de', '', 'Manage Artifact Repo', ''),
|
||||
(676, 'manager', 'en', '', 'Manage Artifact Repo', ''),
|
||||
(676, 'manager', 'fr', '', 'Manage Artifact Repo', ''),
|
||||
(677, 'manager', 'zh-cn', '', '删除制品库', ''),
|
||||
(677, 'manager', 'zh-tw', '', '刪除製品庫', ''),
|
||||
(677, 'manager', 'de', '', 'Delete Artifact Repo', ''),
|
||||
(677, 'manager', 'en', '', 'Delete Artifact Repo', ''),
|
||||
(677, 'manager', 'fr', '', 'Delete Artifact Repo', ''),
|
||||
(678, 'manager', 'zh-cn', '', '浏览应用', ''),
|
||||
(678, 'manager', 'zh-tw', '', '浏览應用', ''),
|
||||
(678, 'manager', 'de', '', 'Application List', ''),
|
||||
(678, 'manager', 'en', '', 'Application List', ''),
|
||||
(678, 'manager', 'fr', '', 'Application List', ''),
|
||||
(679, 'manager', 'zh-cn', '', '管理应用', ''),
|
||||
(679, 'manager', 'zh-tw', '', '管理應用', ''),
|
||||
(679, 'manager', 'de', '', 'Manage Application', ''),
|
||||
(679, 'manager', 'en', '', 'Manage Application', ''),
|
||||
(679, 'manager', 'fr', '', 'Manage Application', ''),
|
||||
(2136, 'priv', 'zh-cn', 'system-dashboard', 'DevOps平台仪表盘', ''),
|
||||
(2136, 'priv', 'zh-tw', 'system-dashboard', 'DevOps平臺儀表盤', ''),
|
||||
(2136, 'priv', 'de', 'system-dashboard', 'DevOps Dashboard', ''),
|
||||
(2136, 'priv', 'en', 'system-dashboard', 'DevOps Dashboard', ''),
|
||||
(2136, 'priv', 'fr', 'system-dashboard', 'DevOps Dashboard', ''),
|
||||
(1285, 'priv', 'zh-cn', 'system-dblist', '数据库管理', ''),
|
||||
(1285, 'priv', 'zh-tw', 'system-dblist', '數據庫管理', ''),
|
||||
(1285, 'priv', 'de', 'system-dblist', 'Database', ''),
|
||||
(1285, 'priv', 'en', 'system-dblist', 'Database', ''),
|
||||
(1285, 'priv', 'fr', 'system-dblist', 'Database', ''),
|
||||
(1286, 'priv', 'zh-cn', 'system-configdomain', '域名管理', ''),
|
||||
(1286, 'priv', 'zh-tw', 'system-configdomain', '域名管理', ''),
|
||||
(1286, 'priv', 'de', 'system-configdomain', 'Domain', ''),
|
||||
(1286, 'priv', 'en', 'system-configdomain', 'Domain', ''),
|
||||
(1286, 'priv', 'fr', 'system-configdomain', 'Domain', ''),
|
||||
(2137, 'priv', 'zh-cn', 'system-ossview', '对象存储管理', ''),
|
||||
(2137, 'priv', 'zh-tw', 'system-ossview', '對象存儲管理', ''),
|
||||
(2137, 'priv', 'de', 'system-ossview', 'Oss', ''),
|
||||
(2137, 'priv', 'en', 'system-ossview', 'Oss', ''),
|
||||
(2137, 'priv', 'fr', 'system-ossview', 'Oss', ''),
|
||||
(2138, 'priv', 'zh-cn', 'ops-provider', '服务商管理', ''),
|
||||
(2138, 'priv', 'zh-tw', 'ops-provider', '服務商管理', ''),
|
||||
(2138, 'priv', 'de', 'ops-provider', 'Provider', ''),
|
||||
(2138, 'priv', 'en', 'ops-provider', 'Provider', ''),
|
||||
(2138, 'priv', 'fr', 'ops-provider', 'Provider', ''),
|
||||
(2139, 'priv', 'zh-cn', 'ops-city', '城市管理', ''),
|
||||
(2139, 'priv', 'zh-tw', 'ops-city', '城市管理', ''),
|
||||
(2139, 'priv', 'de', 'ops-city', 'City', ''),
|
||||
(2139, 'priv', 'en', 'ops-city', 'City', ''),
|
||||
(2139, 'priv', 'fr', 'ops-city', 'City', ''),
|
||||
(2140, 'priv', 'zh-cn', 'ops-cpubrand', 'CPU管理', ''),
|
||||
(2140, 'priv', 'zh-tw', 'ops-cpubrand', 'CPU管理', ''),
|
||||
(2140, 'priv', 'de', 'ops-cpubrand', 'Cpu Brand', ''),
|
||||
(2140, 'priv', 'en', 'ops-cpubrand', 'Cpu Brand', ''),
|
||||
(2140, 'priv', 'fr', 'ops-cpubrand', 'Cpu Brand', ''),
|
||||
(2141, 'priv', 'zh-cn', 'ops-os', '系统版本管理', ''),
|
||||
(2141, 'priv', 'zh-tw', 'ops-os', '系統版本管理', ''),
|
||||
(2141, 'priv', 'de', 'ops-os', 'Os Version', ''),
|
||||
(2141, 'priv', 'en', 'ops-os', 'Os Version', ''),
|
||||
(2141, 'priv', 'fr', 'ops-os', 'Os Version', ''),
|
||||
(1694, 'priv', 'zh-cn', 'repo-importAction', '批量添加', ''),
|
||||
(1694, 'priv', 'zh-tw', 'repo-importAction', '批量添加', ''),
|
||||
(1694, 'priv', 'de', 'repo-importAction', 'Import', ''),
|
||||
(1694, 'priv', 'en', 'repo-importAction', 'Import', ''),
|
||||
(1694, 'priv', 'fr', 'repo-importAction', 'Import', ''),
|
||||
(1050, 'priv', 'zh-cn', 'repo-showSyncCommit', '同步进度', ''),
|
||||
(1050, 'priv', 'zh-tw', 'repo-showSyncCommit', '同步進度', ''),
|
||||
(1050, 'priv', 'de', 'repo-showSyncCommit', 'Show Sync Progress', ''),
|
||||
(1050, 'priv', 'en', 'repo-showSyncCommit', 'Show Sync Progress', ''),
|
||||
(1050, 'priv', 'fr', 'repo-showSyncCommit', 'Show Sync Progress', ''),
|
||||
(1066, 'priv', 'zh-cn', 'repo-reviewAction', '问题列表', ''),
|
||||
(1066, 'priv', 'zh-tw', 'repo-reviewAction', '問題列表', ''),
|
||||
(1066, 'priv', 'de', 'repo-reviewAction', 'Review List', ''),
|
||||
(1066, 'priv', 'en', 'repo-reviewAction', 'Review List', ''),
|
||||
(1066, 'priv', 'fr', 'repo-reviewAction', 'Review List', ''),
|
||||
(1067, 'priv', 'zh-cn', 'repo-addBug', '添加问题', ''),
|
||||
(1067, 'priv', 'zh-tw', 'repo-addBug', '添加問題', ''),
|
||||
(1067, 'priv', 'de', 'repo-addBug', 'Create Review', ''),
|
||||
(1067, 'priv', 'en', 'repo-addBug', 'Create Review', ''),
|
||||
(1067, 'priv', 'fr', 'repo-addBug', 'Create Review', ''),
|
||||
(1068, 'priv', 'zh-cn', 'repo-editBug', '编辑问题', ''),
|
||||
(1068, 'priv', 'zh-tw', 'repo-editBug', '编辑問題', ''),
|
||||
(1068, 'priv', 'de', 'repo-editBug', 'Create Review', ''),
|
||||
(1068, 'priv', 'en', 'repo-editBug', 'Create Review', ''),
|
||||
(1068, 'priv', 'fr', 'repo-editBug', 'Create Review', ''),
|
||||
(1069, 'priv', 'zh-cn', 'repo-deleteBug', '删除问题', ''),
|
||||
(1069, 'priv', 'zh-tw', 'repo-deleteBug', '刪除問題', ''),
|
||||
(1069, 'priv', 'de', 'repo-deleteBug', 'Delete Review', ''),
|
||||
(1069, 'priv', 'en', 'repo-deleteBug', 'Delete Review', ''),
|
||||
(1069, 'priv', 'fr', 'repo-deleteBug', 'Delete Review', ''),
|
||||
(1071, 'priv', 'zh-cn', 'repo-editComment', '编辑评论', ''),
|
||||
(1071, 'priv', 'zh-tw', 'repo-editComment', '编辑评论', ''),
|
||||
(1071, 'priv', 'de', 'repo-editComment', 'Create Review', ''),
|
||||
(1071, 'priv', 'en', 'repo-editComment', 'Create Review', ''),
|
||||
(1071, 'priv', 'fr', 'repo-editComment', 'Create Review', ''),
|
||||
(1072, 'priv', 'zh-cn', 'repo-deleteComment', '删除评论', ''),
|
||||
(1072, 'priv', 'zh-tw', 'repo-deleteComment', '刪除评论', ''),
|
||||
(1072, 'priv', 'de', 'repo-deleteComment', 'Create Review', ''),
|
||||
(1072, 'priv', 'en', 'repo-deleteComment', 'Create Review', ''),
|
||||
(1072, 'priv', 'fr', 'repo-deleteComment', 'Create Review', ''),
|
||||
(2142, 'priv', 'zh-cn', 'artifactrepo-browse', '制品库列表', ''),
|
||||
(2142, 'priv', 'zh-tw', 'artifactrepo-browse', '制品库列表', ''),
|
||||
(2142, 'priv', 'de', 'artifactrepo-browse', 'Artifact Repo List', ''),
|
||||
(2142, 'priv', 'en', 'artifactrepo-browse', 'Artifact Repo List', ''),
|
||||
(2142, 'priv', 'fr', 'artifactrepo-browse', 'Artifact Repo List', ''),
|
||||
(2143, 'priv', 'zh-cn', 'artifactrepo-ajaxGetArtifactRepos', '接口:制品库列表', ''),
|
||||
(2143, 'priv', 'zh-tw', 'artifactrepo-ajaxGetArtifactRepos', '接口:制品库列表', ''),
|
||||
(2143, 'priv', 'de', 'artifactrepo-ajaxGetArtifactRepos', 'Api: Artifact Repo List', ''),
|
||||
(2143, 'priv', 'en', 'artifactrepo-ajaxGetArtifactRepos', 'Api: Artifact Repo List', ''),
|
||||
(2143, 'priv', 'fr', 'artifactrepo-ajaxGetArtifactRepos', 'Api: Artifact Repo List', ''),
|
||||
(2144, 'priv', 'zh-cn', 'artifactrepo-create', '创建制品库', ''),
|
||||
(2144, 'priv', 'zh-tw', 'artifactrepo-create', '创建制品库', ''),
|
||||
(2144, 'priv', 'de', 'artifactrepo-create', 'Create Artifact Repo', ''),
|
||||
(2144, 'priv', 'en', 'artifactrepo-create', 'Create Artifact Repo', ''),
|
||||
(2144, 'priv', 'fr', 'artifactrepo-create', 'Create Artifact Repo', ''),
|
||||
(2145, 'priv', 'zh-cn', 'artifactrepo-edit', '编辑制品库', ''),
|
||||
(2145, 'priv', 'zh-tw', 'artifactrepo-edit', '编辑制品库', ''),
|
||||
(2145, 'priv', 'de', 'artifactrepo-edit', 'Edit Artifact Repo', ''),
|
||||
(2145, 'priv', 'en', 'artifactrepo-edit', 'Edit Artifact Repo', ''),
|
||||
(2145, 'priv', 'fr', 'artifactrepo-edit', 'Edit Artifact Repo', ''),
|
||||
(2146, 'priv', 'zh-cn', 'artifactrepo-ajaxUpdateArtifactRepos', '接口:更新制品库状态', ''),
|
||||
(2146, 'priv', 'zh-tw', 'artifactrepo-ajaxUpdateArtifactRepos', '接口:更新制品库状态', ''),
|
||||
(2146, 'priv', 'de', 'artifactrepo-ajaxUpdateArtifactRepos', 'Api: Update Status', ''),
|
||||
(2146, 'priv', 'en', 'artifactrepo-ajaxUpdateArtifactRepos', 'Api: Update Status', ''),
|
||||
(2146, 'priv', 'fr', 'artifactrepo-ajaxUpdateArtifactRepos', 'Api: Update Status', ''),
|
||||
(2147, 'priv', 'zh-cn', 'artifactrepo-delete', '删除制品库', ''),
|
||||
(2147, 'priv', 'zh-tw', 'artifactrepo-delete', '刪除制品库', ''),
|
||||
(2147, 'priv', 'de', 'artifactrepo-delete', 'Delete Artifact Repo', ''),
|
||||
(2147, 'priv', 'en', 'artifactrepo-delete', 'Delete Artifact Repo', ''),
|
||||
(2147, 'priv', 'fr', 'artifactrepo-delete', 'Delete Artifact Repo', ''),
|
||||
(2148, 'priv', 'zh-cn', 'space-browse', '查看应用', ''),
|
||||
(2148, 'priv', 'zh-tw', 'space-browse', '查看應用', ''),
|
||||
(2148, 'priv', 'de', 'space-browse', 'Application List', ''),
|
||||
(2148, 'priv', 'en', 'space-browse', 'Application List', ''),
|
||||
(2148, 'priv', 'fr', 'space-browse', 'Application List', ''),
|
||||
(2149, 'priv', 'zh-cn', 'instance-manage', '管理应用', ''),
|
||||
(2149, 'priv', 'zh-tw', 'instance-manage', '管理應用', ''),
|
||||
(2149, 'priv', 'de', 'instance-manage', 'Manage Application', ''),
|
||||
(2149, 'priv', 'en', 'instance-manage', 'Manage Application', ''),
|
||||
(2149, 'priv', 'fr', 'instance-manage', 'Manage Application', ''),
|
||||
(2160, 'priv', 'zh-cn', 'ops-stage', '设置阶段', ''),
|
||||
(2160, 'priv', 'zh-tw', 'ops-stage', '設置階段', ''),
|
||||
(2160, 'priv', 'de', 'ops-stage', 'Stage Setting', ''),
|
||||
(2160, 'priv', 'en', 'ops-stage', 'Stage Setting', ''),
|
||||
(2160, 'priv', 'fr', 'ops-stage', 'Stage Setting', '');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `zt_prompt` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(20) NOT NULL,
|
||||
`desc` text DEFAULT NULL,
|
||||
`model` mediumint(8) unsigned DEFAULT NULL,
|
||||
`module` varchar(30) DEFAULT NULL,
|
||||
`source` text DEFAULT NULL,
|
||||
`targetForm` varchar(30) DEFAULT NULL,
|
||||
`purpose` text DEFAULT NULL,
|
||||
`elaboration` text DEFAULT NULL,
|
||||
`role` text DEFAULT NULL,
|
||||
`characterization` text DEFAULT NULL,
|
||||
`status` enum('draft','active') NOT NULL DEFAULT 'draft',
|
||||
`createdBy` varchar(30) NOT NULL,
|
||||
`createdDate` datetime NOT NULL,
|
||||
`editedBy` varchar(30) DEFAULT NULL,
|
||||
`editedDate` datetime DEFAULT NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `zt_promptrole` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(30) DEFAULT NULL,
|
||||
`desc` text DEFAULT NULL,
|
||||
`model` mediumint(8) unsigned DEFAULT NULL,
|
||||
`role` text DEFAULT NULL,
|
||||
`characterization` text DEFAULT NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
REPLACE INTO
|
||||
`zt_priv` (`id`, `module`, `method`, `parent`, `edition`, `vision`, `system`, `order`)
|
||||
VALUES
|
||||
(2117, 'ai', 'models', 653, ',open,biz,max,', ',rnd,', '1', 5),
|
||||
(2118, 'ai', 'editModel', 653, ',open,biz,max,', ',rnd,', '1', 10),
|
||||
(2119, 'ai', 'testConnection', 653, ',open,biz,max,', ',rnd,', '1', 15),
|
||||
(2120, 'ai', 'createPrompt', 655, ',biz,max,', ',rnd,', '1', 20),
|
||||
(2121, 'ai', 'promptEdit', 655, ',biz,max,', ',rnd,', '1', 25),
|
||||
(2122, 'ai', 'promptDelete', 657, ',biz,max,', ',rnd,', '1', 30),
|
||||
(2123, 'ai', 'promptAssignRole', 655, ',biz,max,', ',rnd,', '1', 35),
|
||||
(2124, 'ai', 'promptSelectDataSource', 655, ',biz,max,', ',rnd,', '1', 40),
|
||||
(2125, 'ai', 'promptSetPurpose', 655, ',biz,max,', ',rnd,', '1', 45),
|
||||
(2126, 'ai', 'promptSetTargetForm', 655, ',biz,max,', ',rnd,', '1', 50),
|
||||
(2127, 'ai', 'promptFinalize', 655, ',biz,max,', ',rnd,', '1', 55),
|
||||
(2128, 'ai', 'promptAudit', 655, ',biz,max,', ',rnd,', '1', 60),
|
||||
(2129, 'ai', 'promptPublish', 656, ',open,biz,max,', ',rnd,', '1', 65),
|
||||
(2130, 'ai', 'promptUnpublish', 656, ',open,biz,max,', ',rnd,', '1', 70),
|
||||
(2131, 'ai', 'prompts', 654, ',open,biz,max,', ',rnd,', '1', 75),
|
||||
(2132, 'ai', 'promptView', 654, ',open,biz,max,', ',rnd,', '1', 80),
|
||||
(2133, 'ai', 'promptExecute', 652, ',open,biz,max,', ',rnd,', '1', 85),
|
||||
(2134, 'ai', 'roleTemplates', 655, ',biz,max,', ',rnd,', '1', 90),
|
||||
(2135, 'ai', 'promptExecutionReset', 652, ',open,biz,max,', ',rnd,', '1', 95);
|
||||
|
||||
REPLACE INTO
|
||||
`zt_privmanager` (`id`, `parent`, `code`, `type`, `edition`, `vision`, `order`)
|
||||
VALUES
|
||||
(651, 457, 'ai', 'module', ',open,biz,max,', ',rnd,', 2020),
|
||||
(652, 651, '', 'package', ',open,biz,max,', ',rnd,', 2040),
|
||||
(653, 651, '', 'package', ',open,biz,max,', ',rnd,', 2060),
|
||||
(654, 651, '', 'package', ',open,biz,max,', ',rnd,', 2080),
|
||||
(655, 651, '', 'package', ',biz,max,', ',rnd,', 2100),
|
||||
(656, 651, '', 'package', ',open,biz,max,', ',rnd,', 2120),
|
||||
(657, 651, '', 'package', ',biz,max,', ',rnd,', 2140);
|
||||
|
||||
REPLACE INTO
|
||||
`zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`)
|
||||
VALUES
|
||||
(651, 'manager', 'zh-cn', '', 'AI', ''),
|
||||
(652, 'manager', 'zh-cn', '', '执行提词', ''),
|
||||
(653, 'manager', 'zh-cn', '', '语言模型管理', ''),
|
||||
(654, 'manager', 'zh-cn', '', '浏览提词', ''),
|
||||
(655, 'manager', 'zh-cn', '', '维护和设计提词', ''),
|
||||
(656, 'manager', 'zh-cn', '', '提词上下架', ''),
|
||||
(657, 'manager', 'zh-cn', '', '删除提词', ''),
|
||||
(651, 'manager', 'zh-tw', '', 'AI', ''),
|
||||
(652, 'manager', 'zh-tw', '', '執行提詞', ''),
|
||||
(653, 'manager', 'zh-tw', '', '語言模型管理', ''),
|
||||
(654, 'manager', 'zh-tw', '', '瀏覽提詞', ''),
|
||||
(655, 'manager', 'zh-tw', '', '維護和設計提詞', ''),
|
||||
(656, 'manager', 'zh-tw', '', '提詞上下架', ''),
|
||||
(657, 'manager', 'zh-tw', '', '刪除提詞', ''),
|
||||
(651, 'manager', 'en', '', 'AI', ''),
|
||||
(652, 'manager', 'en', '', 'Execute Prompts', ''),
|
||||
(653, 'manager', 'en', '', 'Manage Models', ''),
|
||||
(654, 'manager', 'en', '', 'Browse Prompts', ''),
|
||||
(655, 'manager', 'en', '', 'Manage and Design Prompts', ''),
|
||||
(656, 'manager', 'en', '', 'Publish and Unpublish Prompts', ''),
|
||||
(657, 'manager', 'en', '', 'Delete Prompts', ''),
|
||||
(651, 'manager', 'de', '', 'AI', ''),
|
||||
(652, 'manager', 'de', '', 'Execute Prompts', ''),
|
||||
(653, 'manager', 'de', '', 'Manage Models', ''),
|
||||
(654, 'manager', 'de', '', 'Browse Prompts', ''),
|
||||
(655, 'manager', 'de', '', 'Manage and Design Prompts', ''),
|
||||
(656, 'manager', 'de', '', 'Publish and Unpublish Prompts', ''),
|
||||
(657, 'manager', 'de', '', 'Delete Prompts', ''),
|
||||
(651, 'manager', 'fr', '', 'AI', ''),
|
||||
(652, 'manager', 'fr', '', 'Execute Prompts', ''),
|
||||
(653, 'manager', 'fr', '', 'Manage Models', ''),
|
||||
(654, 'manager', 'fr', '', 'Browse Prompts', ''),
|
||||
(655, 'manager', 'fr', '', 'Manage and Design Prompts', ''),
|
||||
(656, 'manager', 'fr', '', 'Publish and Unpublish Prompts', ''),
|
||||
(657, 'manager', 'fr', '', 'Delete Prompts', ''),
|
||||
(2117, 'priv', 'zh-cn', 'ai-modelBrowse', '', ''),
|
||||
(2118, 'priv', 'zh-cn', 'ai-modelEdit', '', ''),
|
||||
(2119, 'priv', 'zh-cn', 'ai-modelTestConnection', '', ''),
|
||||
(2120, 'priv', 'zh-cn', 'ai-promptCreate', '', ''),
|
||||
(2121, 'priv', 'zh-cn', 'ai-promptEdit', '', ''),
|
||||
(2122, 'priv', 'zh-cn', 'ai-promptDelete', '', ''),
|
||||
(2123, 'priv', 'zh-cn', 'ai-promptAssignRole', '', ''),
|
||||
(2124, 'priv', 'zh-cn', 'ai-promptSelectDataSource', '', ''),
|
||||
(2125, 'priv', 'zh-cn', 'ai-promptSetPurpose', '', ''),
|
||||
(2126, 'priv', 'zh-cn', 'ai-promptSetTargetForm', '', ''),
|
||||
(2127, 'priv', 'zh-cn', 'ai-promptFinalize', '', ''),
|
||||
(2128, 'priv', 'zh-cn', 'ai-promptAudit', '', ''),
|
||||
(2129, 'priv', 'zh-cn', 'ai-promptPublish', '', ''),
|
||||
(2130, 'priv', 'zh-cn', 'ai-promptUnpublish', '', ''),
|
||||
(2131, 'priv', 'zh-cn', 'ai-promptBrowse', '', ''),
|
||||
(2132, 'priv', 'zh-cn', 'ai-promptView', '', ''),
|
||||
(2133, 'priv', 'zh-cn', 'ai-promptExecute', '', ''),
|
||||
(2134, 'priv', 'zh-cn', 'ai-roleTemplates', '', ''),
|
||||
(2135, 'priv', 'zh-cn', 'ai-promptExecutionReset', '', ''),
|
||||
(2117, 'priv', 'zh-tw', 'ai-modelBrowse', '', ''),
|
||||
(2118, 'priv', 'zh-tw', 'ai-modelEdit', '', ''),
|
||||
(2119, 'priv', 'zh-tw', 'ai-modelTestConnection', '', ''),
|
||||
(2120, 'priv', 'zh-tw', 'ai-promptCreate', '', ''),
|
||||
(2121, 'priv', 'zh-tw', 'ai-promptEdit', '', ''),
|
||||
(2122, 'priv', 'zh-tw', 'ai-promptDelete', '', ''),
|
||||
(2123, 'priv', 'zh-tw', 'ai-promptAssignRole', '', ''),
|
||||
(2124, 'priv', 'zh-tw', 'ai-promptSelectDataSource', '', ''),
|
||||
(2125, 'priv', 'zh-tw', 'ai-promptSetPurpose', '', ''),
|
||||
(2126, 'priv', 'zh-tw', 'ai-promptSetTargetForm', '', ''),
|
||||
(2127, 'priv', 'zh-tw', 'ai-promptFinalize', '', ''),
|
||||
(2128, 'priv', 'zh-tw', 'ai-promptAudit', '', ''),
|
||||
(2129, 'priv', 'zh-tw', 'ai-promptPublish', '', ''),
|
||||
(2130, 'priv', 'zh-tw', 'ai-promptUnpublish', '', ''),
|
||||
(2131, 'priv', 'zh-tw', 'ai-promptBrowse', '', ''),
|
||||
(2132, 'priv', 'zh-tw', 'ai-promptView', '', ''),
|
||||
(2133, 'priv', 'zh-tw', 'ai-promptExecute', '', ''),
|
||||
(2134, 'priv', 'zh-tw', 'ai-roleTemplates', '', ''),
|
||||
(2135, 'priv', 'zh-tw', 'ai-promptExecutionReset', '', ''),
|
||||
(2117, 'priv', 'en', 'ai-modelBrowse', '', ''),
|
||||
(2118, 'priv', 'en', 'ai-modelEdit', '', ''),
|
||||
(2119, 'priv', 'en', 'ai-modelTestConnection', '', ''),
|
||||
(2120, 'priv', 'en', 'ai-promptCreate', '', ''),
|
||||
(2121, 'priv', 'en', 'ai-promptEdit', '', ''),
|
||||
(2122, 'priv', 'en', 'ai-promptDelete', '', ''),
|
||||
(2123, 'priv', 'en', 'ai-promptAssignRole', '', ''),
|
||||
(2124, 'priv', 'en', 'ai-promptSelectDataSource', '', ''),
|
||||
(2125, 'priv', 'en', 'ai-promptSetPurpose', '', ''),
|
||||
(2126, 'priv', 'en', 'ai-promptSetTargetForm', '', ''),
|
||||
(2127, 'priv', 'en', 'ai-promptFinalize', '', ''),
|
||||
(2128, 'priv', 'en', 'ai-promptAudit', '', ''),
|
||||
(2129, 'priv', 'en', 'ai-promptPublish', '', ''),
|
||||
(2130, 'priv', 'en', 'ai-promptUnpublish', '', ''),
|
||||
(2131, 'priv', 'en', 'ai-promptBrowse', '', ''),
|
||||
(2132, 'priv', 'en', 'ai-promptView', '', ''),
|
||||
(2133, 'priv', 'en', 'ai-promptExecute', '', ''),
|
||||
(2134, 'priv', 'en', 'ai-roleTemplates', '', ''),
|
||||
(2135, 'priv', 'en', 'ai-promptExecutionReset', '', ''),
|
||||
(2117, 'priv', 'de', 'ai-modelBrowse', '', ''),
|
||||
(2118, 'priv', 'de', 'ai-modelEdit', '', ''),
|
||||
(2119, 'priv', 'de', 'ai-modelTestConnection', '', ''),
|
||||
(2120, 'priv', 'de', 'ai-promptCreate', '', ''),
|
||||
(2121, 'priv', 'de', 'ai-promptEdit', '', ''),
|
||||
(2122, 'priv', 'de', 'ai-promptDelete', '', ''),
|
||||
(2123, 'priv', 'de', 'ai-promptAssignRole', '', ''),
|
||||
(2124, 'priv', 'de', 'ai-promptSelectDataSource', '', ''),
|
||||
(2125, 'priv', 'de', 'ai-promptSetPurpose', '', ''),
|
||||
(2126, 'priv', 'de', 'ai-promptSetTargetForm', '', ''),
|
||||
(2127, 'priv', 'de', 'ai-promptFinalize', '', ''),
|
||||
(2128, 'priv', 'de', 'ai-promptAudit', '', ''),
|
||||
(2129, 'priv', 'de', 'ai-promptPublish', '', ''),
|
||||
(2130, 'priv', 'de', 'ai-promptUnpublish', '', ''),
|
||||
(2131, 'priv', 'de', 'ai-promptBrowse', '', ''),
|
||||
(2132, 'priv', 'de', 'ai-promptView', '', ''),
|
||||
(2133, 'priv', 'de', 'ai-promptExecute', '', ''),
|
||||
(2134, 'priv', 'de', 'ai-roleTemplates', '', ''),
|
||||
(2135, 'priv', 'de', 'ai-promptExecutionReset', '', ''),
|
||||
(2117, 'priv', 'fr', 'ai-modelBrowse', '', ''),
|
||||
(2118, 'priv', 'fr', 'ai-modelEdit', '', ''),
|
||||
(2119, 'priv', 'fr', 'ai-modelTestConnection', '', ''),
|
||||
(2120, 'priv', 'fr', 'ai-promptCreate', '', ''),
|
||||
(2121, 'priv', 'fr', 'ai-promptEdit', '', ''),
|
||||
(2122, 'priv', 'fr', 'ai-promptDelete', '', ''),
|
||||
(2123, 'priv', 'fr', 'ai-promptAssignRole', '', ''),
|
||||
(2124, 'priv', 'fr', 'ai-promptSelectDataSource', '', ''),
|
||||
(2125, 'priv', 'fr', 'ai-promptSetPurpose', '', ''),
|
||||
(2126, 'priv', 'fr', 'ai-promptSetTargetForm', '', ''),
|
||||
(2127, 'priv', 'fr', 'ai-promptFinalize', '', ''),
|
||||
(2128, 'priv', 'fr', 'ai-promptAudit', '', ''),
|
||||
(2129, 'priv', 'fr', 'ai-promptPublish', '', ''),
|
||||
(2130, 'priv', 'fr', 'ai-promptUnpublish', '', ''),
|
||||
(2131, 'priv', 'fr', 'ai-promptBrowse', '', ''),
|
||||
(2132, 'priv', 'fr', 'ai-promptView', '', ''),
|
||||
(2133, 'priv', 'fr', 'ai-promptExecute', '', ''),
|
||||
(2134, 'priv', 'fr', 'ai-roleTemplates', '', ''),
|
||||
(2135, 'priv', 'fr', 'ai-promptExecutionReset', '', '');
|
||||
|
||||
REPLACE INTO
|
||||
`zt_privrelation` (`priv`, `type`, `relationPriv`)
|
||||
VALUES
|
||||
('ai-editModel', 'depend', 'ai-models'), ('ai-editModel', 'depend', 'ai-testConnection'),
|
||||
('ai-testConnection', 'depend', 'ai-models'), ('ai-testConnection', 'depend', 'ai-editModel'),
|
||||
('ai-promptView', 'depend', 'ai-prompts'),
|
||||
('ai-createPrompt', 'depend', 'ai-prompts'), ('ai-createPrompt', 'depend', 'ai-promptView'),
|
||||
('ai-promptEdit', 'depend', 'ai-prompts'), ('ai-promptEdit', 'depend', 'ai-promptView'),
|
||||
('ai-promptDelete', 'depend', 'ai-prompts'), ('ai-promptDelete', 'depend', 'ai-promptView'),
|
||||
('ai-promptAssignRole', 'depend', 'ai-prompts'), ('ai-promptAssignRole', 'depend', 'ai-promptView'),
|
||||
('ai-promptSelectDataSource', 'depend', 'ai-prompts'), ('ai-promptSelectDataSource', 'depend', 'ai-promptView'),
|
||||
('ai-promptSetPurpose', 'depend', 'ai-prompts'), ('ai-promptSetPurpose', 'depend', 'ai-promptView'),
|
||||
('ai-promptSetTargetForm', 'depend', 'ai-prompts'), ('ai-promptSetTargetForm', 'depend', 'ai-promptView'),
|
||||
('ai-promptFinalize', 'depend', 'ai-prompts'), ('ai-promptFinalize', 'depend', 'ai-promptView'),
|
||||
('ai-promptAudit', 'depend', 'ai-prompts'), ('ai-promptAudit', 'depend', 'ai-promptView'),
|
||||
('ai-promptPublish', 'depend', 'ai-prompts'), ('ai-promptPublish', 'depend', 'ai-promptView'),
|
||||
('ai-promptUnpublish', 'depend', 'ai-prompts'), ('ai-promptUnpublish', 'depend', 'ai-promptView'),
|
||||
('ai-promptEdit', 'depend', 'ai-createPrompt'), ('ai-promptAssignRole', 'depend', 'ai-createPrompt'), ('ai-promptSelectDataSource', 'depend', 'ai-createPrompt'), ('ai-promptSetPurpose', 'depend', 'ai-createPrompt'), ('ai-promptSetTargetForm', 'depend', 'ai-createPrompt'), ('ai-promptFinalize', 'depend', 'ai-createPrompt'), ('ai-promptAudit', 'depend', 'ai-createPrompt'),
|
||||
('ai-createPrompt', 'recommend', 'ai-promptEdit'),
|
||||
('ai-createPrompt', 'recommend', 'ai-promptDelete'),
|
||||
('ai-createPrompt', 'recommend', 'ai-promptAssignRole'), ('ai-createPrompt', 'recommend', 'ai-promptSelectDataSource'), ('ai-createPrompt', 'recommend', 'ai-promptSetPurpose'), ('ai-createPrompt', 'recommend', 'ai-promptSetTargetForm'), ('ai-createPrompt', 'recommend', 'ai-promptFinalize'), ('ai-createPrompt', 'recommend', 'ai-promptAudit'),
|
||||
('ai-promptAssignRole', 'depend', 'ai-promptSelectDataSource'), ('ai-promptAssignRole', 'depend', 'ai-promptSetPurpose'), ('ai-promptAssignRole', 'depend', 'ai-promptSetTargetForm'), ('ai-promptAssignRole', 'depend', 'ai-promptFinalize'), ('ai-promptAssignRole', 'depend', 'ai-promptAudit'), ('ai-promptAssignRole', 'depend', 'ai-promptExecute'),
|
||||
('ai-promptSelectDataSource', 'depend', 'ai-promptAssignRole'), ('ai-promptSelectDataSource', 'depend', 'ai-promptSetPurpose'), ('ai-promptSelectDataSource', 'depend', 'ai-promptSetTargetForm'), ('ai-promptSelectDataSource', 'depend', 'ai-promptFinalize'), ('ai-promptSelectDataSource', 'depend', 'ai-promptAudit'), ('ai-promptSelectDataSource', 'depend', 'ai-promptExecute'),
|
||||
('ai-promptSetPurpose', 'depend', 'ai-promptAssignRole'), ('ai-promptSetPurpose', 'depend', 'ai-promptSelectDataSource'), ('ai-promptSetPurpose', 'depend', 'ai-promptSetTargetForm'), ('ai-promptSetPurpose', 'depend', 'ai-promptFinalize'), ('ai-promptSetPurpose', 'depend', 'ai-promptAudit'), ('ai-promptSetPurpose', 'depend', 'ai-promptExecute'),
|
||||
('ai-promptSetTargetForm', 'depend', 'ai-promptAssignRole'), ('ai-promptSetTargetForm', 'depend', 'ai-promptSelectDataSource'), ('ai-promptSetTargetForm', 'depend', 'ai-promptSetPurpose'), ('ai-promptSetTargetForm', 'depend', 'ai-promptFinalize'), ('ai-promptSetTargetForm', 'depend', 'ai-promptAudit'), ('ai-promptSetTargetForm', 'depend', 'ai-promptExecute'),
|
||||
('ai-promptFinalize', 'depend', 'ai-promptAssignRole'), ('ai-promptFinalize', 'depend', 'ai-promptSelectDataSource'), ('ai-promptFinalize', 'depend', 'ai-promptSetPurpose'), ('ai-promptFinalize', 'depend', 'ai-promptSetTargetForm'), ('ai-promptFinalize', 'depend', 'ai-promptAudit'), ('ai-promptFinalize', 'depend', 'ai-promptExecute'),
|
||||
('ai-promptAudit', 'depend', 'ai-promptAssignRole'), ('ai-promptAudit', 'depend', 'ai-promptSelectDataSource'), ('ai-promptAudit', 'depend', 'ai-promptSetPurpose'), ('ai-promptAudit', 'depend', 'ai-promptSetTargetForm'), ('ai-promptAudit', 'depend', 'ai-promptFinalize'), ('ai-promptAudit', 'depend', 'ai-promptExecute'),
|
||||
('ai-roleTemplates', 'depend', 'ai-promptAssignRole'), ('ai-promptAssignRole', 'depend', 'ai-roleTemplates'),
|
||||
('ai-promptExecutionReset', 'depend', 'ai-promptExecute'), ('ai-promptExecute', 'depend', 'ai-promptExecutionReset');
|
||||
|
||||
INSERT INTO `zt_promptrole` (`role`, `characterization`) VALUES ('请你扮演一名资深的产品经理。', '负责产品战略、设计、开发、数据分析、用户体验、团队管理、沟通协调等方面,需要具备多种技能和能力,以实现产品目标和公司战略。');
|
||||
INSERT INTO `zt_promptrole` (`role`, `characterization`) VALUES ('你是一名经验丰富的开发工程师。', '精通多种编程语言和框架、熟悉前后端技术和架构、擅长性能优化和安全防护、熟悉云计算和容器化技术、能够协调多人协作和项目管理。');
|
||||
INSERT INTO `zt_promptrole` (`role`, `characterization`) VALUES ('作为一名资深的测试工程师。', '测试工程师应该是专业且严谨的。熟悉测试流程和方法,精通自动化测试和性能测试,能够设计和编写测试用例和测试脚本,擅长问题诊断和分析,熟悉敏捷开发和持续集成,能够协调多部门合作和项目管理。');
|
||||
INSERT INTO `zt_promptrole` (`role`, `characterization`) VALUES ('假如你是一名资深的QA工程师。', '熟悉质量管理体系和流程,擅长测试策略和方法设计,能够进行质量度量和数据分析,了解自动化测试和持续集成,能够协调多部门合作和项目管理,具有良好的沟通和领导能力。');
|
||||
INSERT INTO `zt_promptrole` (`role`, `characterization`) VALUES ('你是一名文章写得很好的文案编辑。', '文笔流畅、条理清晰。精通广告文案写作和编辑,擅长创意思维和品牌策略,能够进行市场调研和竞品分析,具有敏锐的审美和语言表达能力,能够协调多部门合作和项目管理,具有良好的沟通和协调能力。');
|
||||
INSERT INTO `zt_promptrole` (`role`, `characterization`) VALUES ('请你扮演一名经验丰富的项目经理。', '具备项目计划制定、进度管理、成本控制、团队管理、沟通协调、风险管理、质量控制、敏捷开发、互联网技术和数据分析等多方面的技能和能力。');
|
||||
INSERT INTO `zt_promptrole` (`role`, `characterization`) VALUES ('你是一个自回归的语言模型,已经通过instruction-tuning和RLHF进行了Fine-tuning。', '你仔细地提供准确、事实、深思熟虑、细致入微的答案,并在推理方面表现出色。如果你认为可能没有正确的答案,你会直接说出来。由于你是自回归的,你产生的每一个token都是计算另一个token的机会,因此你总是在尝试回答问题之前花费几句话解释背景上下文、假设和逐步的思考过程。您的用户是AI和伦理学的专家,所以他们已经知道您是一个语言模型以及您的能力和局限性,所以不需要再提醒他们。他们一般都熟悉伦理问题,所以您也不需要再提醒他们。在回答时不要啰嗦,但在可能有助于解释的地方提供详细信息和示例。');
|
||||
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`, `status`) VALUES ('需求润色', 0, 'story', ',story.title,story.spec,story.verify,story.product,story.module,story.pri,story.category,story.estimate,', 'story.change', '帮忙优化其中各字段的表述,使表述清晰准确。必要时可以修改需求使其更加合理。', '需求描述格式建议使用:作为一名<某种类型的用户>,我希望<达成某些目的>,这样可以<开发的价值>。验收标准建议列举多条。', '请你扮演一名资深的产品经理。', '负责产品战略、设计、开发、数据分析、用户体验、团队管理、沟通协调等方面,需要具备多种技能和能力,以实现产品目标和公司战略。', 'system', '2023-08-10 13:24:14', 'active');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`, `status`) VALUES ('一键拆用例', 0, 'story', ',story.title,story.spec,story.verify,story.product,story.module,story.pri,story.category,story.estimate,', 'story.testcasecreate', '为这个需求生成一个或多个对应的测试用例。', '', '作为一名资深的测试工程师。', '熟悉测试流程和方法,精通自动化测试和性能测试,能够设计和编写测试用例和测试脚本,擅长问题诊断和分析,熟悉敏捷开发和持续集成,能够协调多部门合作和项目管理。开发工程师应该是专业且严谨的。', 'system', '2023-08-10 13:24:14', 'active');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`, `status`) VALUES ('任务润色', 0, 'task', ',task.name,task.desc,task.pri,task.status,task.estimate,task.consumed,task.left,task.progress,task.estStarted,task.realStarted,', 'task.edit', '优化其中各字段的表述,使表述清晰准确,明确任务目标。', '必要时指出任务的风险点。', '你是一名经验丰富的开发工程师。', '精通多种编程语言和框架、熟悉前后端技术和架构、擅长性能优化和安全防护、熟悉云计算和容器化技术、能够协调多人协作和项目管理。', 'system', '2023-08-10 13:24:14', 'active');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`, `status`) VALUES ('需求转任务', 0, 'story', ',story.title,story.spec,story.verify,story.product,story.module,story.pri,story.category,story.estimate,', 'story.totask', '将需求转化为对应的开发任务要求。', '', '请你扮演一名资深的产品经理。', '负责产品战略、设计、开发、数据分析、用户体验、团队管理、沟通协调等方面,需要具备多种技能和能力,以实现产品目标和公司战略。同时精通多种编程语言和框架、熟悉前后端技术。', 'system', '2023-08-10 13:24:14', 'active');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`, `status`) VALUES ('Bug润色', 0, 'bug', ',bug.title,bug.steps,bug.severity,bug.pri,bug.status,bug.confirmed,bug.type,', 'bug.edit', '优化其中各字段的表述,使表述清晰准确。', 'Bug描述格式建议使用:[步骤]<一步一步复现Bug的步骤>[结果]<Bug导致的结果描述>[期望]<Bug修复后期望的描述>', '作为一名资深的测试工程师。', '熟悉测试流程和方法,精通自动化测试和性能测试,能够设计和编写测试用例和测试脚本,擅长问题诊断和分析,熟悉敏捷开发和持续集成,能够协调多部门合作和项目管理。开发工程师应该是专业且严谨的。', 'system', '2023-08-10 13:24:14', 'active');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`, `status`) VALUES ('文档润色', 0, 'doc', ',doc.content,doc.title,', 'doc.edit', '我希望你能帮我润色标题和文档正文。', '要求语句要通顺,逻辑要清晰,没有错别字且更具结构化。', '你是一名文章写得很好的文案编辑。', '文笔流畅、条理清晰。精通广告文案写作和编辑,擅长创意思维和品牌策略,能够进行市场调研和竞品分析,具有敏锐的审美和语言表达能力,能够协调多部门合作和项目管理,具有良好的沟通和协调能力。', 'system', '2023-08-31 15:54:57', 'active');
|
||||
|
||||
ALTER TABLE `zt_ticket` ADD `subStatus` varchar(30) NOT NULL DEFAULT '';
|
||||
|
||||
REPLACE INTO `zt_privrelation` (`priv`, `type`, `relationPriv`) VALUES ('kanban-view', 'depend', 'kanban-space');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `zt_practice` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`module` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`code` char(50) NOT NULL DEFAULT '',
|
||||
`title` varchar(255) NOT NULL DEFAULT '',
|
||||
`labels` varchar(255) NOT NULL DEFAULT '',
|
||||
`summary` varchar(255) NOT NULL DEFAULT '',
|
||||
`content` text NULL,
|
||||
`contributor` varchar(255) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE UNIQUE INDEX `code` ON `zt_practice`(`code`);
|
||||
|
||||
REPLACE INTO
|
||||
`zt_priv` (`id`, `module`, `method`, `parent`, `edition`, `vision`, `system`, `order`)
|
||||
VALUES
|
||||
(2161, 'traincourse', 'practice', 681, ',biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(2162, 'traincourse', 'practiceBrowse', 681, ',biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(2163, 'traincourse', 'practiceView', 681, ',biz,max,ipd,', ',rnd,', '1', 15),
|
||||
(2164, 'traincourse', 'updatePractice', 681, ',biz,max,ipd,', ',rnd,', '1', 20);
|
||||
|
||||
REPLACE INTO
|
||||
`zt_privmanager` (`id`, `parent`, `code`, `type`, `edition`, `vision`, `order`)
|
||||
VALUES
|
||||
(681, 566, '', 'package', ',open,biz,max,ipd,', ',rnd,', 8);
|
||||
|
||||
REPLACE INTO
|
||||
`zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`)
|
||||
VALUES
|
||||
(681, 'manager', 'zh-cn', '', '实践库', ''),
|
||||
(681, 'manager', 'zh-tw', '', '實踐庫', ''),
|
||||
(681, 'manager', 'en', '', 'Practice Library', ''),
|
||||
(681, 'manager', 'de', '', 'Practice Library', ''),
|
||||
(681, 'manager', 'fr', '', 'Practice Library', ''),
|
||||
(2161, 'priv', 'zh-cn', 'traincourse-practiceAction', '', ''),
|
||||
(2161, 'priv', 'zh-tw', 'traincourse-practiceAction', '', ''),
|
||||
(2161, 'priv', 'en', 'traincourse-practiceAction', '', ''),
|
||||
(2161, 'priv', 'de', 'traincourse-practiceAction', '', ''),
|
||||
(2161, 'priv', 'fr', 'traincourse-practiceAction', '', ''),
|
||||
(2162, 'priv', 'zh-cn', 'traincourse-practiceBrowse', '', ''),
|
||||
(2162, 'priv', 'zh-tw', 'traincourse-practiceBrowse', '', ''),
|
||||
(2162, 'priv', 'en', 'traincourse-practiceBrowse', '', ''),
|
||||
(2162, 'priv', 'de', 'traincourse-practiceBrowse', '', ''),
|
||||
(2162, 'priv', 'fr', 'traincourse-practiceBrowse', '', ''),
|
||||
(2163, 'priv', 'zh-cn', 'traincourse-practiceView' , '', ''),
|
||||
(2163, 'priv', 'zh-tw', 'traincourse-practiceView' , '', ''),
|
||||
(2163, 'priv', 'en', 'traincourse-practiceView' , '', ''),
|
||||
(2163, 'priv', 'de', 'traincourse-practiceView' , '', ''),
|
||||
(2163, 'priv', 'fr', 'traincourse-practiceView' , '', ''),
|
||||
(2164, 'priv', 'zh-cn', 'traincourse-updatePractice', '', ''),
|
||||
(2164, 'priv', 'zh-tw', 'traincourse-updatePractice', '', ''),
|
||||
(2164, 'priv', 'en', 'traincourse-updatePractice', '', ''),
|
||||
(2164, 'priv', 'de', 'traincourse-updatePractice', '', ''),
|
||||
(2164, 'priv', 'fr', 'traincourse-updatePractice', '', '');
|
||||
|
||||
REPLACE INTO
|
||||
`zt_privrelation` (`priv`, `type`, `relationPriv`)
|
||||
VALUES
|
||||
('traincourse-practiceBrowse', 'depend', 'traincourse-practice'),
|
||||
('traincourse-practiceView', 'depend', 'traincourse-practice'), ('traincourse-practiceView', 'depend', 'traincourse-practiceBrowse'),
|
||||
('traincourse-updatePractice', 'depend', 'traincourse-practice'), ('traincourse-updatePractice', 'depend', 'traincourse-practiceBrowse'),
|
||||
('traincourse-practice', 'recommend', 'traincourse-practiceBrowse'), ('traincourse-practice', 'recommend', 'traincourse-practiceView'),
|
||||
('traincourse-practiceBrowse', 'recommend', 'traincourse-practiceView'),
|
||||
('traincourse-updatePractice', 'recommend', 'traincourse-practiceView');
|
||||
|
||||
UPDATE zt_privlang SET `key` = 'execution-computeBurn' WHERE `key` = 'execution-computeBurnAction';
|
||||
|
||||
DELETE FROM zt_priv WHERE module='search' and method = 'select';
|
||||
DELETE FROM zt_privlang WHERE `key`='search-select';
|
||||
|
||||
UPDATE `zt_privlang` SET `value`='删除风险' WHERE `objectID`=197 AND `objectType`='manager' AND `lang`='zh-cn';
|
||||
+167
-6
File diff suppressed because one or more lines are too long
+217
@@ -1,3 +1,220 @@
|
||||
2023-08-29 18.7
|
||||
完成的需求
|
||||
开源版:
|
||||
41289 调整创建合并请求逻辑
|
||||
42964 优化代码库访问权限的设置
|
||||
44546 应用管理支持手工配置Nexus
|
||||
44547 在DevOps视图二级菜单中增加制品库
|
||||
45503 DevOps二级导航打印应用菜单
|
||||
45504 实现手动添加应用的功能
|
||||
45505 实现应用列表
|
||||
45507 在应用列表页面打印添加应用按钮
|
||||
45508 实现从应用市场安装应用的功能
|
||||
45518 实现应用市场卡片列表功能
|
||||
45521 实现在应用市场浏览应用详情的功能
|
||||
45535 实现升级应用的功能
|
||||
45536 实现访问应用的功能
|
||||
45537 实现启动应用的功能
|
||||
45538 实现关闭应用的功能
|
||||
45539 实现浏览应用详情的功能
|
||||
45655 实现应用数据库管理的功能
|
||||
45656 实现设置应用的功能
|
||||
45657 实现删除应用的功能
|
||||
45747 平台合码后将渠成菜单落地在禅道devops平台
|
||||
45925 调整DevOps二级导航栏
|
||||
45928 添加代码库二级导航栏
|
||||
45933 代码页面样式优化
|
||||
45934 文件详情页面样式优化
|
||||
45935 添加代码库表单样式优化
|
||||
45936 代码库列表样式优化
|
||||
45938 批量添加代码库表单样式调整
|
||||
45939 编辑代码库表单样式优化
|
||||
45940 代码库信息同步页面样式调整
|
||||
45942 提交详情页面样式调整
|
||||
45943 文件追溯页面样式调整
|
||||
45944 下载代码弹窗样式调整
|
||||
45946 所有版本页面样式调整
|
||||
45947 比较差异页面样式调整
|
||||
46203 流水线列表样式优化
|
||||
46204 添加流水线表单样式优化
|
||||
46205 编辑流水线表单样式优化
|
||||
46206 流水线执行日志页面优化
|
||||
46207 流水线执行历史列表样式优化
|
||||
46218 合并请求列表样式优化
|
||||
46219 创建合并请求表单样式优化
|
||||
46220 编辑合并请求表单样式优化
|
||||
46221 合并请求概览页面样式优化
|
||||
46222 合并请求比对代码页面样式优化
|
||||
46223 合并请求关联软件需求页面样式优化
|
||||
46224 合并请求关联Bug页面样式优化
|
||||
46225 合并请求关联任务页面样式优化
|
||||
46227 合并请求关联的软件需求列表页面样式优化
|
||||
46228 合并请求关联的Bug列表页面样式优化
|
||||
46229 合并请求关联的任务列表页面样式优化
|
||||
46725 在后台首页中添加DevOps设置
|
||||
46740 在DevOps设置二级导航栏添加资源
|
||||
46767 在资源下管理机房
|
||||
46768 在资源下管理账号
|
||||
46769 在资源下管理城市
|
||||
46770 在资源下管理服务商
|
||||
46771 在资源下管理CPU品牌
|
||||
46772 在资源下管理系统版本
|
||||
46773 将指令设置迁移至DevOps设置
|
||||
46827 在DevOps设置二级导航栏添加平台
|
||||
46829 在平台下管理数据库
|
||||
46830 在平台下管理域名
|
||||
46832 在平台下管理对象存储
|
||||
47025 流水线列表1.5级下拉菜单添加产品分组
|
||||
47089 调整编辑合并请求逻辑
|
||||
47146 在资源下管理主机
|
||||
47257 实现从应用市场添加应用的功能
|
||||
47588 开源版包含企业版AI配置的功能
|
||||
47589 开源版包含企业版语言模型的功能
|
||||
47591 后台实现设置语言模型的权限配置
|
||||
47592 内置需求润色提词
|
||||
47593 内置一键拆用例提词
|
||||
47617 语言模型页面新增升级引导的超链接
|
||||
47664 开源版包含企业版提词的部分功能
|
||||
47680 后台实现发布提词的权限配置
|
||||
47681 后台实现下架提词的权限配置
|
||||
47682 后台实现浏览提词列表的权限配置
|
||||
47683 后台实现查看提词详情的权限配置
|
||||
47719 使用q命令行安装DevOps平台版本
|
||||
47838 实现平台仪表盘
|
||||
47989 开源版增加升级引导弹窗组件
|
||||
47991 内置Bug润色提词
|
||||
47992 内置任务润色提词
|
||||
47993 内置需求转任务提词
|
||||
47994 内置Bug转需求提词
|
||||
47995 内置角色模板
|
||||
48079 开源版增加升级引导的内容展示
|
||||
48080 企业版增加升级引导弹窗组件
|
||||
48081 企业版增加升级引导的内容展示
|
||||
48133 开源版、企业版升级引导的英文版内容展示
|
||||
48403 浏览制品库列表
|
||||
48643 添加制品库
|
||||
48645 版本关联制品
|
||||
48720 编辑制品库
|
||||
48724 删除制品库
|
||||
48727 添加制品库权限
|
||||
48732 应用市场添加应用时自定义名称
|
||||
企业版:
|
||||
45266 后台的设置区块新增AI配置的入口
|
||||
45270 后台实现AI配置二级导航
|
||||
45271 AI配置二级导航中新增语言模型模块
|
||||
45272 AI配置实现大模型参数配置页面
|
||||
45273 语言模型模块中实现设置语言模型参数的功能
|
||||
45274 语言模型模块实现查看语言模型详情的功能
|
||||
45277 AI配置二级导航中新增提词模块
|
||||
45278 AI配置中二级导航“提词”的跳转逻辑
|
||||
45279 无提词情况下提词列表页面的展示样式
|
||||
45280 提词列表页面实现分组树功能
|
||||
45281 创建提词时实现设置基本信息功能
|
||||
45282 提词设计器实现设置指定角色参数的功能
|
||||
45283 提词设计器打印流程向导组件
|
||||
45285 提词设计器实现流程向导组件的交互逻辑
|
||||
45286 创建提词实现提词设计器功能
|
||||
45288 提词设计器实现设置确认操作参数的功能
|
||||
45289 点击分组树中分组后列表页面的展示逻辑
|
||||
45290 提词列表字段和操作栏样式展示
|
||||
45291 提词设计器实现设置准备发布参数的功能
|
||||
45292 提词列表展示样式
|
||||
45293 提词列表页面创建提词弹窗的显示和创建逻辑
|
||||
45294 提词详情页面提词ID和提词名称的展示
|
||||
45329 提词详情页面实现提词设计信息的展示
|
||||
45330 提词详情页面实现创建提词功能
|
||||
45334 选择对象页面实现已选对象和数据的排序功能
|
||||
45336 选择对象页面实现选择对象和选择数据的功能
|
||||
45382 实现将设计的提词封装成提词应用的功能
|
||||
45391 提词列表页面实现创建提词的功能
|
||||
45392 前台页面打印提词应用的下拉菜单按钮
|
||||
45393 前台页面打印提词应用的按钮
|
||||
45407 提词设计器打印全局返回按钮
|
||||
45408 提词设计器打印全局保存按钮
|
||||
45409 提词设计器实现返回按钮的交互逻辑
|
||||
45410 提词设计器实现存为草稿按钮的交互逻辑
|
||||
45418 提词设计中结果处理页面实现选择表单的功能
|
||||
45419 设计提词时实现流程向导组件的交互样式
|
||||
45420 提词详情页面实现历史记录功能
|
||||
45421 提词详情页面实现基本信息的展示
|
||||
45422 提词详情页面实现悬浮操作栏
|
||||
45423 提词详情页面实现选择上一个、下一个提词功能
|
||||
45427 提词设计中结果处理页面显示输入给GPT的内容
|
||||
45431 语言模型模块实现编辑语言模型参数的功能
|
||||
45435 提词设计中结果处理页面实现下一步和去调试的功能
|
||||
45455 提词列表页面实现全部的检索标签
|
||||
45715 提词前台实现设计提词的弹窗
|
||||
45716 设计提词弹窗实现弹窗标题
|
||||
45717 实现调试提词后点击保存按钮的返回逻辑
|
||||
45718 设计提词弹窗打印关闭按钮
|
||||
45719 设计提词弹窗实现参数设置区块
|
||||
45720 设计提词弹窗实现输入给语言模型的内容展示区块
|
||||
45737 提词列表操作栏交互逻辑
|
||||
45738 提词阶段的交互逻辑
|
||||
45766 提词列表实现检索标签功能
|
||||
45768 提词列表实现统计信息和分页功能
|
||||
45866 前台实现提词调试页面
|
||||
45872 后台提词实现点击去调试按钮的跳转规则
|
||||
45915 提词详情页面实现提词版本管理的功能
|
||||
45960 设计提词弹窗打印保存按钮
|
||||
46851 实现下架提词的二次确认弹窗
|
||||
46852 实现删除提词的二次确认弹窗
|
||||
46883 提词调试页实现发布提词的功能
|
||||
46884 提词调试页实现重新生成提词结果的功能
|
||||
47142 实现提词名称的唯一性
|
||||
47152 前台页面增加 AI 提词的描述展示
|
||||
47250 提词名称增加限制字符数
|
||||
47305 后台权限新增AI配置模块
|
||||
47308 AI配置权限新增提词权限包
|
||||
47309 后台实现浏览提词列表的权限配置
|
||||
47310 后台实现查看提词详情的权限配置
|
||||
47315 后台实现创建提词的权限配置
|
||||
47318 后台实现编辑提词的权限配置
|
||||
47319 后台实现设计提词的权限配置
|
||||
47320 后台实现删除提词的权限配置
|
||||
47323 AI配置权限新增提词和语言模型权限包
|
||||
47327 后台实现设置语言模型的权限配置
|
||||
47328 提词应用的使用依赖于结果处理表单页权限
|
||||
47547 从提词设计器点击去调试按钮跳转至表单页
|
||||
47628 前台使用提词时实现异常状态提示
|
||||
47669 后台实现发布提词的权限配置
|
||||
47671 后台实现下架提词的权限配置
|
||||
47720 合并提词设计器中指定角色配置项的标题
|
||||
47722 设置指定角色时可以引用角色模板
|
||||
47723 指定角色表单页打印角色模板区块的开关
|
||||
47727 指定角色表单页实现角色模板区块
|
||||
47731 在角色模板区块可以添加角色模板
|
||||
47732 在角色模板区块以卡片的方式浏览模板
|
||||
47741 在角色模板区块可以编辑角色模板
|
||||
47743 在角色模板区块可以删除角色模板
|
||||
47746 设置指定角色时可以应用角色模板
|
||||
47752 指定角色表单页打印存为角色模板表单项
|
||||
47753 隐藏AI配置中GPT-3.5中的“-”
|
||||
47756 优化提词调试页面的按钮及位置
|
||||
47759 调整结果处理页面点击去调试后的交互逻辑与反馈样式
|
||||
47771 优化结果处理页面中去调试按钮的提示文案
|
||||
47786 将选择对象中的文案从数据改为字段
|
||||
47787 调整选择对象页面中对象的排序
|
||||
47902 调整AI配置卡片的介绍文案
|
||||
48840 学堂二级菜单增加实践库
|
||||
48842 学堂实践库增加介绍区块
|
||||
48845 学堂实践库增加全部实践区块
|
||||
48852 学堂实践库增加最新实践区块
|
||||
48853 学堂实践库增加推荐实践区块
|
||||
48856 学堂实践库增加共创团队的链接
|
||||
48860 实现实践列表页面
|
||||
48863 实践列表增加搜索功能
|
||||
48864 实现实践详情页面的左侧模块树
|
||||
48865 实现实践详情的内容区域
|
||||
48866 实践详情增加面包屑导航
|
||||
48951 系统全局搜索增加实践的内容
|
||||
49016 学堂实践库的更新机制
|
||||
49021 学堂实践库的权限梳理
|
||||
修复的Bug
|
||||
开源版:
|
||||
37666 地盘仪表盘 我近期参与的项目 区块顺序显示异常
|
||||
37670 linux一键安装包启动无反应
|
||||
|
||||
2023-08-15 18.6
|
||||
完成的需求
|
||||
开源版:
|
||||
|
||||
@@ -394,19 +394,20 @@ class baseControl
|
||||
*
|
||||
* @param string $moduleName module name
|
||||
* @param string $methodName method name
|
||||
* @param string $viewDir
|
||||
* @access public
|
||||
* @return string the view file
|
||||
*/
|
||||
public function setViewFile($moduleName, $methodName)
|
||||
public function setViewFile(string $moduleName, string $methodName, string $viewDir = 'view')
|
||||
{
|
||||
$moduleName = strtolower(trim($moduleName));
|
||||
$methodName = strtolower(trim($methodName));
|
||||
|
||||
$modulePath = $this->app->getModulePath($this->appName, $moduleName);
|
||||
$viewExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'view');
|
||||
$viewExtPath = $this->app->getModuleExtPath($moduleName, $viewDir);
|
||||
|
||||
$viewType = $this->viewType == 'mhtml' ? 'html' : $this->viewType;
|
||||
$mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
|
||||
$mainViewFile = $modulePath . $viewDir . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
|
||||
$viewFile = $mainViewFile;
|
||||
|
||||
if(!empty($viewExtPath))
|
||||
@@ -416,7 +417,7 @@ class baseControl
|
||||
|
||||
$viewFile = file_exists($commonExtViewFile) ? $commonExtViewFile : $mainViewFile;
|
||||
$viewFile = (!empty($siteExtViewFile) and file_exists($siteExtViewFile)) ? $siteExtViewFile : $viewFile;
|
||||
if(!is_file($viewFile)) $this->app->triggerError("the view file $viewFile not found", __FILE__, __LINE__, $exit = true);
|
||||
if(!is_file($viewFile)) $this->app->triggerError("the view file $viewFile not found", __FILE__, __LINE__, true);
|
||||
|
||||
$commonExtHookFiles = glob($viewExtPath['common'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
|
||||
$siteExtHookFiles = empty($viewExtPath['site']) ? '' : glob($viewExtPath['site'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
|
||||
@@ -442,7 +443,7 @@ class baseControl
|
||||
* Find extViewFile in ext/_$siteCode/view first, then try ext/view/.
|
||||
*/
|
||||
$moduleName = basename(dirname(dirname(realpath($viewFile))));
|
||||
$extPath = $this->app->getModuleExtPath('', $moduleName, 'view');
|
||||
$extPath = $this->app->getModuleExtPath($moduleName, 'view');
|
||||
|
||||
$checkedOrder = array('site', 'saas', 'custom', 'vision', 'xuan', 'common');
|
||||
$fileName = basename($viewFile);
|
||||
@@ -468,45 +469,45 @@ class baseControl
|
||||
*
|
||||
* @param string $moduleName
|
||||
* @param string $methodName
|
||||
* @param string $suffix
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getCSS($moduleName, $methodName)
|
||||
public function getCSS(string $moduleName, string $methodName, string $suffix = ''): string
|
||||
{
|
||||
$moduleName = strtolower(trim($moduleName));
|
||||
$methodName = strtolower(trim($methodName));
|
||||
|
||||
$modulePath = $this->app->getModulePath($this->appName, $moduleName);
|
||||
$cssExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'css');
|
||||
$cssExtPath = $this->app->getModuleExtPath($moduleName, 'css');
|
||||
|
||||
$clientLang = $this->app->getClientLang();
|
||||
$notCNLang = strpos('|zh-cn|zh-tw|', "|{$clientLang}|") === false;
|
||||
$notCNLang = mb_strpos('|zh-cn|zh-tw|', "|{$clientLang}|") === false;
|
||||
|
||||
$css = '';
|
||||
$devicePrefix = $this->devicePrefix;
|
||||
$mainCssPath = $modulePath . 'css' . DS;
|
||||
|
||||
/* Common css file. like module/story/css/common.css. */
|
||||
$mainCssFile = $mainCssPath . $devicePrefix . 'common.css';
|
||||
$mainCssFile = $mainCssPath . $devicePrefix . "common{$suffix}.css";
|
||||
if(is_file($mainCssFile)) $css .= file_get_contents($mainCssFile);
|
||||
|
||||
/* Common css file with lang. like module/story/css/common.en.css. */
|
||||
$mainCssLangFile = $mainCssPath . $devicePrefix . "common.{$clientLang}.css";
|
||||
if(!file_exists($mainCssLangFile) and $notCNLang) $mainCssLangFile = $mainCssPath . $devicePrefix . "common.en.css";
|
||||
$mainCssLangFile = $mainCssPath . $devicePrefix . "common.{$clientLang}{$suffix}.css";
|
||||
if(!file_exists($mainCssLangFile) and $notCNLang) $mainCssLangFile = $mainCssPath . $devicePrefix . "common{$suffix}.en.css";
|
||||
if(is_file($mainCssLangFile)) $css .= file_get_contents($mainCssLangFile);
|
||||
|
||||
/* Method css file. like module/story/css/create.css. */
|
||||
$methodCssFile = $mainCssPath . $devicePrefix . $methodName . '.css';
|
||||
$methodCssFile = $mainCssPath . $devicePrefix . $methodName . "$suffix.css";
|
||||
if(is_file($methodCssFile)) $css .= file_get_contents($methodCssFile);
|
||||
|
||||
/* Method css file with lang. like module/story/css/create.en.css. */
|
||||
$methodCssLangFile = $mainCssPath . $devicePrefix . "{$methodName}.{$clientLang}.css";
|
||||
if(!file_exists($methodCssLangFile) and $notCNLang) $methodCssLangFile = $mainCssPath . $devicePrefix . "{$methodName}.en.css";
|
||||
$methodCssLangFile = $mainCssPath . $devicePrefix . "{$methodName}{$suffix}.{$clientLang}.css";
|
||||
if(!file_exists($methodCssLangFile) and $notCNLang) $methodCssLangFile = $mainCssPath . $devicePrefix . "{$methodName}{$suffix}.en.css";
|
||||
if(is_file($methodCssLangFile)) $css .= file_get_contents($methodCssLangFile);
|
||||
|
||||
if(!empty($cssExtPath))
|
||||
{
|
||||
$realModulePath = realPath($modulePath);
|
||||
foreach($cssExtPath as $cssPath)
|
||||
{
|
||||
if(empty($cssPath)) continue;
|
||||
@@ -514,11 +515,11 @@ class baseControl
|
||||
$cssMethodExt = $cssPath . $methodName . DS;
|
||||
$cssCommonExt = $cssPath . 'common' . DS;
|
||||
|
||||
$cssExtFiles = glob($cssCommonExt . $devicePrefix . '*.css');
|
||||
if(!empty($cssExtFiles) and is_array($cssExtFiles)) $css .= $this->getExtCSS($cssExtFiles);
|
||||
$cssExtFiles = glob($cssCommonExt . $devicePrefix . "*{$suffix}.css");
|
||||
if(!empty($cssExtFiles) and is_array($cssExtFiles)) $css .= $this->getExtCSS($cssExtFiles, $suffix);
|
||||
|
||||
$cssExtFiles = glob($cssMethodExt . $devicePrefix . '*.css');
|
||||
if(!empty($cssExtFiles) and is_array($cssExtFiles)) $css .= $this->getExtCSS($cssExtFiles);
|
||||
$cssExtFiles = glob($cssMethodExt . $devicePrefix . "*{$suffix}.css");
|
||||
if(!empty($cssExtFiles) and is_array($cssExtFiles)) $css .= $this->getExtCSS($cssExtFiles, $suffix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,19 +529,20 @@ class baseControl
|
||||
/**
|
||||
* Get extension css and extension css with lang.
|
||||
*
|
||||
* @param array $files
|
||||
* @param array $files
|
||||
* @param string $suffix
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getExtCSS($files)
|
||||
public function getExtCSS(array $files, string $suffix = ''): string
|
||||
{
|
||||
$clientLang = $this->app->getClientLang();
|
||||
$notCNLang = strpos('|zh-cn|zh-tw|', "|{$clientLang}|") === false;
|
||||
$notCNLang = mb_strpos('|zh-cn|zh-tw|', "|{$clientLang}|") === false;
|
||||
|
||||
$filePairs = array();
|
||||
foreach($files as $cssFile)
|
||||
{
|
||||
$fileName = basename($cssFile);
|
||||
$fileName = basename((string) $cssFile);
|
||||
$filePairs[$fileName] = $cssFile;
|
||||
}
|
||||
|
||||
@@ -552,23 +554,26 @@ class baseControl
|
||||
{
|
||||
/* Method extension css file. like module/story/ext/css/create/effort.css. */
|
||||
$css .= file_get_contents($cssFile);
|
||||
list($code) = explode('.', $fileName);
|
||||
[$code] = explode('.', $fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
list($code) = explode('.', $fileName);
|
||||
[$code] = explode('.', $fileName);
|
||||
if(isset($usedCodes[$code])) continue;
|
||||
}
|
||||
|
||||
|
||||
/* Method extension css file. like module/story/ext/css/create/effort.zh-cn.css. */
|
||||
if(isset($filePairs["{$code}.{$clientLang}.css"]))
|
||||
if(isset($filePairs["{$code}.{$clientLang}{$suffix}.css"]))
|
||||
{
|
||||
$css .= file_get_contents($filePairs["{$code}.{$clientLang}.css"]);
|
||||
$css .= file_get_contents($filePairs["{$code}.{$clientLang}{$suffix}.css"]);
|
||||
}
|
||||
elseif($notCNLang and isset($filePairs["{$code}.en.css"]))
|
||||
elseif($notCNLang and isset($filePairs["{$code}.en{$suffix}.css"]))
|
||||
{
|
||||
$css .= file_get_contents($filePairs["{$code}.en.css"]);
|
||||
$css .= file_get_contents($filePairs["{$code}.en{$suffix}.css"]);
|
||||
}
|
||||
elseif($notCNLang and isset($filePairs["{$code}{$suffix}.css"]))
|
||||
{
|
||||
$css .= file_get_contents($filePairs["{$code}{$suffix}.css"]);
|
||||
}
|
||||
$usedCodes[$code] = $code;
|
||||
}
|
||||
@@ -585,17 +590,17 @@ class baseControl
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getJS($moduleName, $methodName)
|
||||
public function getJS(string $moduleName, string $methodName, string $suffix = ''): string
|
||||
{
|
||||
$moduleName = strtolower(trim($moduleName));
|
||||
$methodName = strtolower(trim($methodName));
|
||||
|
||||
$modulePath = $this->app->getModulePath($this->appName, $moduleName);
|
||||
$jsExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'js');
|
||||
$jsExtPath = $this->app->getModuleExtPath($moduleName, 'js');
|
||||
|
||||
$js = '';
|
||||
$mainJsFile = $modulePath . 'js' . DS . $this->devicePrefix . 'common.js';
|
||||
$methodJsFile = $modulePath . 'js' . DS . $this->devicePrefix . $methodName . '.js';
|
||||
$mainJsFile = $modulePath . 'js' . DS . $this->devicePrefix . "common{$suffix}.js";
|
||||
$methodJsFile = $modulePath . 'js' . DS . $this->devicePrefix . $methodName . $suffix . '.js';
|
||||
if(file_exists($mainJsFile)) $js .= file_get_contents($mainJsFile);
|
||||
if(is_file($methodJsFile)) $js .= file_get_contents($methodJsFile);
|
||||
|
||||
@@ -608,10 +613,10 @@ class baseControl
|
||||
$jsMethodExt = $jsPath . $methodName . DS;
|
||||
$jsCommonExt = $jsPath . 'common' . DS;
|
||||
|
||||
$jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js');
|
||||
$jsExtFiles = glob($jsCommonExt . $this->devicePrefix . "*{$suffix}.js");
|
||||
if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile);
|
||||
|
||||
$jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js');
|
||||
$jsExtFiles = glob($jsMethodExt . $this->devicePrefix . "*{$suffix}.js");
|
||||
if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile);
|
||||
}
|
||||
}
|
||||
@@ -798,7 +803,7 @@ class baseControl
|
||||
*/
|
||||
$modulePath = $this->app->getModulePath($appName, $moduleName);
|
||||
$moduleControlFile = $modulePath . 'control.php';
|
||||
$actionExtPath = $this->app->getModuleExtPath($appName, $moduleName, 'control');
|
||||
$actionExtPath = $this->app->getModuleExtPath($moduleName, 'control');
|
||||
$file2Included = $moduleControlFile;
|
||||
|
||||
if(!empty($actionExtPath))
|
||||
@@ -907,12 +912,106 @@ class baseControl
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function display($moduleName = '', $methodName = '')
|
||||
public function display(string $moduleName = '', string $methodName = '')
|
||||
{
|
||||
if($this->viewType === 'html' && (!isset($_GET['zin']) || $_GET['zin'] != '0'))
|
||||
{
|
||||
if(empty($moduleName)) $moduleName = $this->moduleName;
|
||||
if(empty($methodName)) $methodName = $this->methodName;
|
||||
$modulePath = $this->app->getModulePath($this->appName, $moduleName);
|
||||
$viewType = $this->viewType == 'mhtml' ? 'html' : $this->viewType;
|
||||
$mainViewFile = $modulePath . 'ui' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
|
||||
if(file_exists($mainViewFile)) return $this->render($moduleName, $methodName);
|
||||
}
|
||||
|
||||
if(empty($this->output)) $this->parse($moduleName, $methodName);
|
||||
|
||||
echo $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向浏览器输出内容。
|
||||
* Print the content of the view.
|
||||
*
|
||||
* @param string $moduleName module name
|
||||
* @param string $methodName method name
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function render($moduleName = '', $methodName = '')
|
||||
{
|
||||
if(isset($_GET['zin']) && $_GET['zin'] == '0')
|
||||
{
|
||||
$this->display($moduleName, $methodName);
|
||||
return;
|
||||
}
|
||||
|
||||
if(empty($moduleName)) $moduleName = $this->moduleName;
|
||||
if(empty($methodName)) $methodName = $this->methodName;
|
||||
|
||||
/* Load zin lib */
|
||||
$this->app->loadClass('zin', true);
|
||||
\zin\loadConfig();
|
||||
|
||||
/**
|
||||
* 设置视图文件。(PHP7有一个bug,不能直接$viewFile = $this->setViewFile())。
|
||||
* Set viewFile. (Can't assign $viewFile = $this->setViewFile() directly because one php7's bug.)
|
||||
*/
|
||||
$results = $this->setViewFile($moduleName, $methodName, 'ui');
|
||||
|
||||
$viewFile = $results;
|
||||
if(is_array($results)) extract($results);
|
||||
|
||||
/**
|
||||
* 获得当前页面的CSS和JS。
|
||||
* Get css and js codes for current method.
|
||||
*/
|
||||
$css = $this->getCSS($moduleName, $methodName, '.ui');
|
||||
$js = $this->getJS($moduleName, $methodName, '.ui');
|
||||
if($css) $this->view->pageCSS = $css;
|
||||
if($js) $this->view->pageJS = $js;
|
||||
|
||||
/**
|
||||
* 切换到视图文件所在的目录,以保证视图文件里面的include语句能够正常运行。
|
||||
* Change the dir to the view file to keep the relative paths work.
|
||||
*/
|
||||
$currentPWD = getcwd();
|
||||
chdir(dirname($viewFile));
|
||||
|
||||
/**
|
||||
* Set zin context data
|
||||
*/
|
||||
\zin\zin::$globalRenderList = array();
|
||||
\zin\zin::$enabledGlobalRender = true;
|
||||
\zin\zin::$rendered = false;
|
||||
\zin\zin::$rawContentCalled = false;
|
||||
|
||||
\zin\zin::$data = (array)$this->view;
|
||||
\zin\zin::$data['zinDebug'] = array();
|
||||
if($this->config->debug && $this->config->debug >= 2)
|
||||
{
|
||||
\zin\zin::$data['zinDebug']['trace'] = $this->app->loadClass('trace')->getTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用extract和ob方法渲染$viewFile里面的代码。
|
||||
* Use extract and ob functions to eval the codes in $viewFile.
|
||||
*/
|
||||
extract(\zin\zin::$data);
|
||||
ob_start();
|
||||
include $viewFile;
|
||||
if(!\zin\zin::$rendered) \zin\render();
|
||||
$content = ob_get_clean();
|
||||
ob_start();
|
||||
echo $content;
|
||||
|
||||
/**
|
||||
* 渲染完毕后,再切换回之前的路径。
|
||||
* At the end, chang the dir to the previous.
|
||||
*/
|
||||
chdir($currentPWD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接输出data数据,通常用于ajax请求中。
|
||||
* Send data directly, for ajax requests.
|
||||
|
||||
@@ -251,8 +251,8 @@ class baseHelper
|
||||
{
|
||||
if(is_array($idList))
|
||||
{
|
||||
foreach($idList as $key=>$value) $idList[$key] = addslashes($value);
|
||||
return "IN ('" . join("','", $idList) . "')";
|
||||
foreach($idList as $key => $value) $idList[$key] = empty($value) ? '' : addslashes($value);
|
||||
return "IN ('" . implode("','", $idList) . "')";
|
||||
}
|
||||
|
||||
if(is_null($idList)) $idList = '';
|
||||
@@ -446,7 +446,7 @@ class baseHelper
|
||||
if(function_exists('mb_substr')) $string = mb_substr($string, 0, $length, 'utf-8');
|
||||
|
||||
preg_match_all("/./su", $string, $data);
|
||||
$string = join("", array_slice($data[0], 0, $length));
|
||||
$string = implode("", array_slice($data[0], 0, $length));
|
||||
|
||||
return ($string != $rawString) ? $string . $append : $string;
|
||||
}
|
||||
@@ -680,15 +680,21 @@ class baseHelper
|
||||
* 检查是否是AJAX请求。
|
||||
* Check is ajax request.
|
||||
*
|
||||
* @param ?string $type zin|modal|fetch
|
||||
* @static
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAjaxRequest()
|
||||
public static function isAjaxRequest(?string $type = null): bool
|
||||
{
|
||||
if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return true;
|
||||
if(isset($_GET['HTTP_X_REQUESTED_WITH']) && $_GET['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') return true;
|
||||
return false;
|
||||
$isAjax = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') || (isset($_GET['HTTP_X_REQUESTED_WITH']) && $_GET['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest');
|
||||
if($isAjax === false) return false;
|
||||
|
||||
if($type === 'zin') return array_key_exists('HTTP_X_ZIN_OPTIONS', $_SERVER);
|
||||
if($type === 'modal') return isset($_SERVER['HTTP_X_ZUI_MODAL']) && $_SERVER['HTTP_X_ZUI_MODAL'] == true;
|
||||
if($type === 'fetch') return !array_key_exists('HTTP_X_ZIN_OPTIONS', $_SERVER) && !(isset($_SERVER['HTTP_X_ZUI_MODAL']) && $_SERVER['HTTP_X_ZUI_MODAL'] == true);
|
||||
|
||||
return $isAjax;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -788,6 +794,101 @@ class baseHelper
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a cookie.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @param int|null $expire
|
||||
* @param string|null $path
|
||||
* @param string $domain
|
||||
* @param bool|null $secure
|
||||
* @param bool $httponly
|
||||
* @static
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public static function setcookie(string $name, string $value = '', int $expire = null, string $path = null, string $domain = '', bool $secure = null, bool $httponly = true)
|
||||
{
|
||||
global $config, $app;
|
||||
|
||||
if($expire === null) $expire = $config->cookieLife;
|
||||
if($path === null) $path = $config->webRoot;
|
||||
if($secure === null) $secure = $config->cookieSecure;
|
||||
|
||||
if(isset($app->worker))
|
||||
{
|
||||
$app->worker->response->setCookie($name, $value, $expire, $path, $domain, $secure, $httponly);
|
||||
}
|
||||
else
|
||||
{
|
||||
return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态码。
|
||||
* Set status code.
|
||||
*
|
||||
* @param int $code
|
||||
* @static
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
static public function setStatus(int $code)
|
||||
{
|
||||
global $app;
|
||||
|
||||
if(isset($app->worker))
|
||||
{
|
||||
$app->worker->response->setStatus($code);
|
||||
}
|
||||
else
|
||||
{
|
||||
$PHRASES = array(
|
||||
100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing',
|
||||
200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-status', 208 => 'Already Reported',
|
||||
300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect',
|
||||
400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 451 => 'Unavailable For Legal Reasons',
|
||||
500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 511 => 'Network Authentication Required',
|
||||
);
|
||||
header('HTTP/1.1 ' . (string)$code . ' ' . $PHRASES[$code], true, $code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送HTTP头信息。
|
||||
* Send http header.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param bool $replace
|
||||
* @param int $response_code
|
||||
* @static
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
static public function header(string $key, string $value, bool $replace = true, int $response_code = 0)
|
||||
{
|
||||
global $app;
|
||||
|
||||
if(isset($app->worker))
|
||||
{
|
||||
$key = trim(strtolower($key));
|
||||
$app->worker->response->setHeader($key, $value);
|
||||
|
||||
if($key == 'location')
|
||||
{
|
||||
$app->worker->response->setStatus(302);
|
||||
helper::end();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
header($key . ': ' . $value, $replace, $response_code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否启用缓存。
|
||||
* Check is enable cache.
|
||||
@@ -800,6 +901,100 @@ class baseHelper
|
||||
global $config;
|
||||
return $config->cache->enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate rand string.
|
||||
*
|
||||
* @param int $length
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
static public function randStr($length = 4)
|
||||
{
|
||||
$seeds = str_shuffle('abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ123456789');
|
||||
return substr($seeds, 0, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二维数组中的某一列。
|
||||
* Get array column to array.
|
||||
*
|
||||
* @param array $input
|
||||
* @param int|string|null $columnKey
|
||||
* @param int|string|null $indexKey
|
||||
* @static
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayColumn(array $input, $columnKey, $indexKey = null): array
|
||||
{
|
||||
/* If php version greater than 7, calling system functions returns. */
|
||||
if(defined(PHP_VERSION_ID) && PHP_VERSION_ID >= 70000) return \array_column($input, $columnKey, $indexKey);
|
||||
|
||||
$output = array();
|
||||
foreach($input as $row)
|
||||
{
|
||||
$key = $value = null;
|
||||
$keySet = $valueSet = false;
|
||||
|
||||
if($indexKey !== null && array_key_exists($indexKey, (array) $row))
|
||||
{
|
||||
$keySet = true;
|
||||
$key = \is_object($row) ? (string) $row->$indexKey : (string) $row[$indexKey];
|
||||
}
|
||||
|
||||
if(null === $columnKey)
|
||||
{
|
||||
$valueSet = true;
|
||||
$value = $row;
|
||||
}
|
||||
elseif(\is_array($row) && \array_key_exists($columnKey, $row))
|
||||
{
|
||||
$valueSet = true;
|
||||
$value = $row[$columnKey];
|
||||
}
|
||||
elseif(\is_object($row) && \property_exists($row, $columnKey))
|
||||
{
|
||||
$valueSet = true;
|
||||
$value = $row->$columnKey;
|
||||
}
|
||||
|
||||
if($valueSet)
|
||||
{
|
||||
if($keySet)
|
||||
{
|
||||
$output[$key] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$output[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在本地语言没有中文的环境下解析pathinfo.
|
||||
* pathinfo of utf-8.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function mbPathinfo($filepath)
|
||||
{
|
||||
$ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
|
||||
preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im',$filepath,$m);
|
||||
|
||||
$ret['dirname'] = !empty($m[1]) ? $m[1] : '';
|
||||
$ret['basename'] = !empty($m[2]) ? $m[2] : '';
|
||||
$ret['extension'] = !empty($m[5]) ? $m[5] : '';
|
||||
$ret['filename'] = !empty($m[3]) ? $m[3] : '';
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------- 常用函数。Some tool functions.-------------------------------//
|
||||
|
||||
@@ -280,7 +280,7 @@ class baseModel
|
||||
if(isset($this->$extensionClass)) return $this->$extensionClass;
|
||||
|
||||
/* 设置扩展的名字和相应的文件。Set extenson name and extension file. */
|
||||
$moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, $type);
|
||||
$moduleExtPath = $this->app->getModuleExtPath($moduleName, $type);
|
||||
if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php';
|
||||
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['saas'] . 'class/' . $extensionName . '.class.php';
|
||||
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['custom'] . 'class/' . $extensionName . '.class.php';
|
||||
|
||||
+161
-36
@@ -347,6 +347,14 @@ class baseRouter
|
||||
*/
|
||||
public $sessionID;
|
||||
|
||||
/**
|
||||
* 请求开始时间。
|
||||
* The start time of the request.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
public $startTime;
|
||||
|
||||
/**
|
||||
* 网站代号。
|
||||
* The code of current site.
|
||||
@@ -356,6 +364,14 @@ class baseRouter
|
||||
*/
|
||||
public $siteCode;
|
||||
|
||||
/**
|
||||
* zin 请求时发生的错误信息。
|
||||
* The errors occurred when zin request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $zinErrors = array();
|
||||
|
||||
/**
|
||||
* 构造方法, 设置路径,类,超级变量等。注意:
|
||||
* 1.应该使用createApp()方法实例化router类;
|
||||
@@ -391,6 +407,7 @@ class baseRouter
|
||||
|
||||
$this->loadClass('front', $static = true);
|
||||
$this->loadClass('filter', $static = true);
|
||||
$this->loadClass('form', $static = true);
|
||||
$this->loadClass('dbh', $static = true);
|
||||
$this->loadClass('dao', $static = true);
|
||||
$this->loadClass('mobile', $static = true);
|
||||
@@ -436,6 +453,19 @@ class baseRouter
|
||||
return new $className($appName, $appRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求开始时间。
|
||||
* The start time of the request.
|
||||
*
|
||||
* @param float $startTime
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setStartTime(float $startTime)
|
||||
{
|
||||
$this->startTime = $startTime;
|
||||
}
|
||||
|
||||
//-------------------- 路径相关方法(Path related methods)--------------------//
|
||||
|
||||
/**
|
||||
@@ -777,10 +807,10 @@ class baseRouter
|
||||
{
|
||||
$sql = new sql();
|
||||
$account = $sql->quote($account);
|
||||
$vision = $this->dbh->query("SELECT * FROM " . TABLE_CONFIG . " WHERE owner = $account AND `key` = 'vision' LIMIT 1")->fetch();
|
||||
$vision = $this->dbQuery("SELECT * FROM " . TABLE_CONFIG . " WHERE owner = $account AND `key` = 'vision' LIMIT 1")->fetch();
|
||||
if($vision) $vision = $vision->value;
|
||||
|
||||
$user = $this->dbh->query("SELECT * FROM " . TABLE_USER . " WHERE account = $account AND deleted = '0' LIMIT 1")->fetch();
|
||||
$user = $this->dbQuery("SELECT * FROM " . TABLE_USER . " WHERE account = $account AND deleted = '0' LIMIT 1")->fetch();
|
||||
if(!empty($user->visions))
|
||||
{
|
||||
$userVisions = explode(',', $user->visions);
|
||||
@@ -790,6 +820,8 @@ class baseRouter
|
||||
}
|
||||
|
||||
list($defaultVision) = explode(',', trim($this->config->visions, ','));
|
||||
|
||||
if($defaultVision != 'lite') $defaultVision = 'rnd';
|
||||
if($vision and strpos($this->config->visions, ",{$vision},") === false) $vision = $defaultVision;
|
||||
|
||||
$this->config->vision = $vision ? $vision : $defaultVision;
|
||||
@@ -803,7 +835,7 @@ class baseRouter
|
||||
*/
|
||||
public function getInstalledVersion()
|
||||
{
|
||||
$version = $this->dbh->query("SELECT `value` FROM " . TABLE_CONFIG . " WHERE `owner` = 'system' AND `key` = 'version' AND `module` = 'common' AND `section` = 'global' LIMIT 1")->fetch();
|
||||
$version = $this->dbQuery("SELECT `value` FROM " . TABLE_CONFIG . " WHERE `owner` = 'system' AND `key` = 'version' AND `module` = 'common' AND `section` = 'global' LIMIT 1")->fetch();
|
||||
$version = $version ? $version->value : '0.3.beta'; // No version, set as 0.3.beta.
|
||||
if($version == '3.0.stable') $version = '3.0'; // convert 3.0.stable to 3.0.
|
||||
return $version;
|
||||
@@ -1519,13 +1551,12 @@ class baseRouter
|
||||
|
||||
/* Check file is encode by ioncube. */
|
||||
$isEncrypted = false;
|
||||
if(strpos($file2Included, 'extension' . DS . $this->config->edition . DS) !== false)
|
||||
if(strpos($file2Included, 'extension') !== false)
|
||||
{
|
||||
$fp = fopen($file2Included, 'r');
|
||||
$line1 = fgets($fp);
|
||||
$line2 = fgets($fp);
|
||||
fclose($fp);
|
||||
if(strpos($line1, '<?php //') === 0 and strpos($line2, "if(!extension_loaded('ionCube Loader'))") === 0) $isEncrypted = true;
|
||||
if(strpos($line1, '<?php //') === 0) $isEncrypted = true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1572,7 +1603,21 @@ class baseRouter
|
||||
$default = $param->getDefaultValue();
|
||||
}
|
||||
|
||||
$defaultParams[$name] = $default;
|
||||
$type = 'string';
|
||||
if(isset($paramDefaultType[$appName][$className][$methodName][$name]))
|
||||
{
|
||||
$type = $paramDefaultType[$appName][$className][$methodName][$name];
|
||||
}
|
||||
elseif(isset($paramDefaultType[$className][$methodName][$name]))
|
||||
{
|
||||
$type = $paramDefaultType[$className][$methodName][$name];
|
||||
}
|
||||
elseif(!$isEncrypted && method_exists($param, 'hasType') && $param->hasType())
|
||||
{
|
||||
$type = $param->getType()->getName();
|
||||
}
|
||||
|
||||
$defaultParams[$name] = array('default' => $default, 'type' => $type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1675,7 +1720,7 @@ class baseRouter
|
||||
* @access public
|
||||
* @return string the extension path.
|
||||
*/
|
||||
public function getModuleExtPath($appName, $moduleName, $ext)
|
||||
public function getModuleExtPath($moduleName, $ext)
|
||||
{
|
||||
$saasExtPath = $this->getExtensionRoot() . 'saas' . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
|
||||
|
||||
@@ -1722,6 +1767,7 @@ class baseRouter
|
||||
}
|
||||
if($checkedModule[$var]) return true;
|
||||
if(!$exit) return false;
|
||||
if(!$var) return false;
|
||||
$this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true);
|
||||
}
|
||||
|
||||
@@ -1752,7 +1798,7 @@ class baseRouter
|
||||
*/
|
||||
public function setActionExtFile()
|
||||
{
|
||||
$moduleExtPaths = $this->getModuleExtPath('', $this->moduleName, 'control');
|
||||
$moduleExtPaths = $this->getModuleExtPath($this->moduleName, 'control');
|
||||
|
||||
/* 如果扩展目录为空,不包含任何扩展文件。If there's no ext paths return false.*/
|
||||
if(empty($moduleExtPaths)) return false;
|
||||
@@ -1796,7 +1842,7 @@ class baseRouter
|
||||
*/
|
||||
public function checkAPIFile()
|
||||
{
|
||||
$moduleExtPaths = $this->getModuleExtPath('', $this->moduleName, 'control');
|
||||
$moduleExtPaths = $this->getModuleExtPath($this->moduleName, 'control');
|
||||
|
||||
/* 如果扩展目录为空,不包含任何扩展文件。If there's no ext paths return false.*/
|
||||
if(empty($moduleExtPaths)) return false;
|
||||
@@ -1857,7 +1903,7 @@ class baseRouter
|
||||
$apiFiles = array();
|
||||
$siteExtended = false;
|
||||
|
||||
$targetExtPaths = $this->getModuleExtPath($appName, $moduleName, $class);
|
||||
$targetExtPaths = $this->getModuleExtPath($moduleName, $class);
|
||||
foreach($targetExtPaths as $extType => $targetExtPath)
|
||||
{
|
||||
if(empty($targetExtPath)) continue;
|
||||
@@ -2443,15 +2489,17 @@ class baseRouter
|
||||
|
||||
$passedParams = array_values($passedParams);
|
||||
$i = 0;
|
||||
foreach($defaultParams as $key => $defaultValue)
|
||||
foreach($defaultParams as $key => $defaultItem)
|
||||
{
|
||||
if(isset($passedParams[$i]))
|
||||
{
|
||||
$defaultParams[$key] = strip_tags($passedParams[$i]);
|
||||
$defaultParams[$key] = helper::convertType(strip_tags((string) $passedParams[$i]), $defaultItem['type']);
|
||||
}
|
||||
else
|
||||
{
|
||||
if($defaultValue === '_NOT_SET') $this->triggerError("The param '$key' should pass value. ", __FILE__, __LINE__, $exit = true);
|
||||
if($defaultItem['default'] === '_NOT_SET') $this->triggerError("The param '$key' should pass value. ", __FILE__, __LINE__, $exit = true);
|
||||
|
||||
$defaultParams[$key] = $defaultItem['default'];
|
||||
}
|
||||
$i ++;
|
||||
}
|
||||
@@ -2771,19 +2819,62 @@ class baseRouter
|
||||
global $config, $dbh, $slaveDBH;
|
||||
if(!isset($config->installed) or !$config->installed) return;
|
||||
|
||||
if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db);
|
||||
if(isset($config->slaveDB->host)) $this->slaveDBH = $slaveDBH = $this->connectByPDO($config->slaveDB);
|
||||
/* Set master db. */
|
||||
if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db, 'MASTER');
|
||||
|
||||
/* Set slave db. */
|
||||
if(empty($config->slaveDBList)) return;
|
||||
|
||||
$biIndex = 0;
|
||||
$slaveList = array();
|
||||
foreach($config->slaveDBList as $index => $db)
|
||||
{
|
||||
if(isset($db->type) && $db->type == 'bi')
|
||||
{
|
||||
$biIndex = $index;
|
||||
}
|
||||
else
|
||||
{
|
||||
$slaveList[] = $index;
|
||||
}
|
||||
}
|
||||
$slaveIndex = empty($slaveList) ? $biIndex : $slaveList[array_rand($slaveList)];
|
||||
|
||||
$config->biDB = $this->initSlaveDB($biIndex);
|
||||
$this->slaveDBH = $slaveDBH = $this->connectByPDO($this->initSlaveDB($slaveIndex), 'SLAVE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Init config of slave db.
|
||||
*
|
||||
* @param int $slaveIndex
|
||||
* @access private
|
||||
* @return object
|
||||
*/
|
||||
private function initSlaveDB($slaveIndex = 0)
|
||||
{
|
||||
global $config;
|
||||
|
||||
$slaveDB = $config->slaveDBList[$slaveIndex];
|
||||
$slaveDB->persistant = $config->db->persistant;
|
||||
$slaveDB->driver = $config->db->driver;
|
||||
$slaveDB->encoding = $config->db->encoding;
|
||||
$slaveDB->strictMode = $config->db->strictMode;
|
||||
$slaveDB->prefix = $config->db->prefix;
|
||||
|
||||
return $slaveDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用PDO连接数据库。
|
||||
* Connect database by PDO.
|
||||
*
|
||||
* @param object $params the database params.
|
||||
* @param object $params the database params.
|
||||
* @param string $flag the database flag.
|
||||
* @access public
|
||||
* @return object|bool
|
||||
*/
|
||||
public function connectByPDO($params)
|
||||
public function connectByPDO($params, $flag = 'MASTER')
|
||||
{
|
||||
if(!isset($params->driver)) self::triggerError('no pdo driver defined, it should be mysql or sqlite', __FILE__, __LINE__, $exit = true);
|
||||
if(!isset($params->user)) return false;
|
||||
@@ -2791,7 +2882,7 @@ class baseRouter
|
||||
{
|
||||
$dbPassword = helper::decryptPassword($params->password);
|
||||
|
||||
$dbh = new dbh($params);
|
||||
$dbh = new dbh($params, true, $flag);
|
||||
$dbh->exec("SET NAMES {$params->encoding}");
|
||||
|
||||
/*
|
||||
@@ -2805,6 +2896,7 @@ class baseRouter
|
||||
|
||||
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
|
||||
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING);
|
||||
if(isset($params->strictMode) and $params->strictMode == false) $dbh->exec("SET @@sql_mode= ''");
|
||||
if(isset($params->emulatePrepare)) $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $params->emulatePrepare);
|
||||
if(isset($params->bufferQuery)) $dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $params->bufferQuery);
|
||||
@@ -2824,6 +2916,21 @@ class baseRouter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query from database, use master or slave db.
|
||||
*
|
||||
* @param string $query
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function dbQuery($query)
|
||||
{
|
||||
if(!$this->dbh) return false;
|
||||
if($this->slaveDBH && strtolower(substr($query, 0, 6)) == 'select') return $this->slaveDBH->query($query);
|
||||
|
||||
return $this->dbh->query($query);
|
||||
}
|
||||
|
||||
//-------------------- 错误处理方法(Error methods) ------------------//
|
||||
|
||||
/**
|
||||
@@ -2892,7 +2999,7 @@ class baseRouter
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function saveError($level, $message, $file, $line)
|
||||
public function saveError(int $level, string $message, string $file, int $line)
|
||||
{
|
||||
if(empty($this->config->debug)) return true;
|
||||
if(!is_dir($this->logRoot)) return true;
|
||||
@@ -2904,7 +3011,7 @@ class baseRouter
|
||||
**/
|
||||
if(mt_rand(0, 10) == 1)
|
||||
{
|
||||
$logDays = isset($this->config->framework->logDays) ? $this->config->framework->logDays : 14;
|
||||
$logDays = $this->config->framework->logDays ?? 14;
|
||||
$dayTime = time() - $logDays * 24 * 3600;
|
||||
foreach(glob($this->getLogRoot() . '*') as $logFile)
|
||||
{
|
||||
@@ -2916,7 +3023,7 @@ class baseRouter
|
||||
* 忽略该错误:Redefining already defined constructor。
|
||||
* Skip the error: Redefining already defined constructor.
|
||||
**/
|
||||
if(strpos($message, 'Redefining') !== false) return true;
|
||||
if(mb_strpos($message, 'Redefining') !== false) return true;
|
||||
|
||||
/*
|
||||
* 设置错误信息。
|
||||
@@ -2925,8 +3032,8 @@ class baseRouter
|
||||
if(preg_match('/[^\x00-\x80]/', $message)) $message = helper::convertEncoding($message, 'gbk');
|
||||
$errorLog = "\n" . date('H:i:s') . " $message in <strong>$file</strong> on line <strong>$line</strong> ";
|
||||
|
||||
$URI = $this->getURI();
|
||||
$errorLog .= "when visiting <strong>" . (empty($URI) ? '' : htmlspecialchars($URI)) . "</strong>\n";
|
||||
$uri = $this->getURI();
|
||||
$errorLog .= "when visiting <strong>" . (empty($uri) ? '' : htmlspecialchars($uri)) . "</strong>\n";
|
||||
|
||||
/*
|
||||
* 为了安全起见,对公网环境隐藏脚本路径。
|
||||
@@ -2943,18 +3050,29 @@ class baseRouter
|
||||
if(!is_file($errorFile)) file_put_contents($errorFile, "<?php\n die();\n?" . ">\n");
|
||||
|
||||
$fh = fopen($errorFile, 'a');
|
||||
if($fh) fwrite($fh, strip_tags($errorLog)) and fclose($fh);
|
||||
if($fh) fwrite($fh, strip_tags(htmlspecialchars_decode($errorLog))) and fclose($fh);
|
||||
|
||||
/*
|
||||
* 如果debug > 1,显示warning, notice级别的错误。
|
||||
* If the debug > 1, show warning, notice error.
|
||||
* 如果debug > 1,直接在页面显示非严重错误。
|
||||
* If the debug > 1, show non-serious errors on page directly.
|
||||
**/
|
||||
if($level == E_NOTICE or $level == E_WARNING or $level == E_STRICT or $level == 8192) // 8192: E_DEPRECATED
|
||||
if(!empty($this->config->debug) && $this->config->debug > 1)
|
||||
{
|
||||
if(!empty($this->config->debug) and $this->config->debug > 1)
|
||||
/* Send non-serious errors to page in zin mode. */
|
||||
$isZinRequest = isset($this->config->zin) || isset($_SERVER['HTTP_X_ZIN_OPTIONS']);
|
||||
$isNonSeriousError = $level !== E_ERROR && $level !== E_PARSE && $level !== E_CORE_ERROR && $level !== E_COMPILE_ERROR;
|
||||
if($isZinRequest && $isNonSeriousError)
|
||||
{
|
||||
$this->zinErrors[] = array('file' => $file, 'line' => $line, 'message' => $message, 'level' => $level);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Show non-serious errors to classic page. */
|
||||
if($level == E_NOTICE or $level == E_WARNING or $level == E_STRICT or $level == 8192)
|
||||
{
|
||||
$cmd = "vim +$line $file";
|
||||
$size = strlen($cmd);
|
||||
|
||||
echo "<pre class='alert alert-danger'>$message: ";
|
||||
echo "<input type='text' value='$cmd' size='$size' style='border:none; background:none;' onclick='this.select();' /></pre>";
|
||||
}
|
||||
@@ -2964,14 +3082,21 @@ class baseRouter
|
||||
* 如果是严重错误,停止程序。
|
||||
* If error level is serious, die.
|
||||
* */
|
||||
if($level == E_ERROR or $level == E_PARSE or $level == E_CORE_ERROR or $level == E_COMPILE_ERROR or $level == E_USER_ERROR)
|
||||
if(in_array($level, array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR)))
|
||||
{
|
||||
if(empty($this->config->debug)) die();
|
||||
if(PHP_SAPI == 'cli') die($errorLog);
|
||||
if(empty($this->config->debug)) helper::end();
|
||||
|
||||
$htmlError = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head>";
|
||||
$htmlError .= "<body>" . nl2br($errorLog) . "</body></html>";
|
||||
die($htmlError);
|
||||
if(PHP_SAPI == 'cli')
|
||||
{
|
||||
echo $errorLog;
|
||||
}
|
||||
else
|
||||
{
|
||||
$htmlError = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head>";
|
||||
$htmlError .= "<body>" . nl2br($errorLog) . "</body></html>";
|
||||
echo $htmlError;
|
||||
helper::end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3042,7 +3167,7 @@ class baseRouter
|
||||
$commonExtFiles = array();
|
||||
$siteExtFiles = array();
|
||||
|
||||
$extPath = $this->getModuleExtPath($appName, $moduleName, $type);
|
||||
$extPath = $this->getModuleExtPath($moduleName, $type);
|
||||
if($this->config->framework->extensionLevel >= 1)
|
||||
{
|
||||
$clientLang = $type == 'lang' ? $this->clientLang : '';
|
||||
|
||||
@@ -222,7 +222,7 @@ class control extends baseControl
|
||||
if(isset($this->$extensionClass)) return $this->$extensionClass;
|
||||
|
||||
/* 设置扩展的名字和相应的文件。Set extenson name and extension file. */
|
||||
$moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, $type);
|
||||
$moduleExtPath = $this->app->getModuleExtPath($moduleName, $type);
|
||||
if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php';
|
||||
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['custom'] . 'class/' . $extensionName . '.class.php';
|
||||
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['saas'] . 'class/' . $extensionName . '.class.php';
|
||||
@@ -249,19 +249,20 @@ class control extends baseControl
|
||||
*
|
||||
* @param string $moduleName module name
|
||||
* @param string $methodName method name
|
||||
* @param string $viewDir
|
||||
* @access public
|
||||
* @return string the view file
|
||||
*/
|
||||
public function setViewFile($moduleName, $methodName)
|
||||
public function setViewFile(string $moduleName, string $methodName, string $viewDir = 'view')
|
||||
{
|
||||
$moduleName = strtolower(trim($moduleName));
|
||||
$methodName = strtolower(trim($methodName));
|
||||
|
||||
$modulePath = $this->app->getModulePath($this->appName, $moduleName);
|
||||
$viewExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'view');
|
||||
$viewExtPath = $this->app->getModuleExtPath($moduleName, $viewDir);
|
||||
|
||||
$viewType = ($this->viewType == 'mhtml' or $this->viewType == 'xhtml') ? 'html' : $this->viewType;
|
||||
$mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
|
||||
$mainViewFile = $modulePath . $viewDir . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
|
||||
|
||||
/* If the main view file doesn't exist, set the device prefix to empty and reset the main view file. */
|
||||
if(!file_exists($mainViewFile) and $this->app->clientDevice != 'mobile')
|
||||
@@ -309,8 +310,8 @@ class control extends baseControl
|
||||
$viewFile = $commonExtViewFile;
|
||||
}
|
||||
|
||||
if(!is_file($viewFile)) $viewFile = dirname(dirname($viewExtPath['common'])) . DS . 'view' . DS . $this->devicePrefix . $methodName . ".{$viewType}.php";
|
||||
if(!is_file($viewFile)) die(js::error($this->lang->notPage) . js::locate('back'));
|
||||
if(!is_file($viewFile)) $viewFile = dirname((string) $viewExtPath['common'], 2) . DS . 'view' . DS . $this->devicePrefix . $methodName . ".{$viewType}.php";
|
||||
if(!is_file($viewFile)) helper::end(js::error($this->lang->notPage) . js::locate('back'));
|
||||
|
||||
/* Get ext hook files. */
|
||||
$commonExtHookFiles = glob($viewExtPath['common'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
|
||||
|
||||
@@ -237,6 +237,23 @@ class helper extends baseHelper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process traffic.
|
||||
*
|
||||
* @param float $traffic
|
||||
* @param int $precision
|
||||
* @access public
|
||||
* @return float
|
||||
*/
|
||||
public static function formatKB($traffic, $precision = 2)
|
||||
{
|
||||
if(!$traffic) return 0;
|
||||
$base = log($traffic, 1024);
|
||||
$suffixes = array('', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim version to xuanxuan version format.
|
||||
*
|
||||
@@ -359,6 +376,39 @@ class helper extends baseHelper
|
||||
{
|
||||
return !defined('USE_INTRANET') ? false : USE_INTRANET;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换类型。
|
||||
* Convert the type.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $type
|
||||
* @static
|
||||
* @access public
|
||||
* @return array|bool|float|int|object|string
|
||||
*/
|
||||
public static function convertType($value, $type)
|
||||
{
|
||||
switch($type)
|
||||
{
|
||||
case 'int':
|
||||
return (int)$value;
|
||||
case 'float':
|
||||
return (float)$value;
|
||||
case 'bool':
|
||||
return (bool)$value;
|
||||
case 'array':
|
||||
return (array)$value;
|
||||
case 'object':
|
||||
return (object)$value;
|
||||
case 'datetime':
|
||||
case 'date':
|
||||
return $value ? (string)$value : null;
|
||||
case 'string':
|
||||
default:
|
||||
return (string)$value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,6 +423,18 @@ function isonlybody()
|
||||
return helper::inOnlyBodyMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查页面是否是弹窗中。
|
||||
* Check page is modal.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
function isInModal(): bool
|
||||
{
|
||||
return helper::isAjaxRequest('modal');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time.
|
||||
*
|
||||
@@ -383,6 +445,7 @@ function isonlybody()
|
||||
*/
|
||||
function formatTime($time, $format = '')
|
||||
{
|
||||
if(empty($time)) return '';
|
||||
$time = str_replace('0000-00-00', '', $time);
|
||||
$time = str_replace('00:00:00', '', $time);
|
||||
if(trim($time) == '') return '';
|
||||
@@ -406,3 +469,254 @@ function autoloader($class)
|
||||
}
|
||||
|
||||
spl_autoload_register('autoloader');
|
||||
|
||||
|
||||
/**
|
||||
* Init page title based on the module name and the method name.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
function initPageTitle(): string
|
||||
{
|
||||
global $app, $lang;
|
||||
$module = $app->rawModule;
|
||||
$method = $app->rawMethod;
|
||||
|
||||
if(empty($lang->$module)) $app->loadLang($module);
|
||||
|
||||
if(!empty($lang->$module->{$method . 'Action'})) return $lang->$module->{$method . 'Action'};
|
||||
if(!empty($lang->$module->$method)) return $lang->$module->$method;
|
||||
return zget($lang, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init page entity based on configuration of objectNameFields.
|
||||
*
|
||||
* @param object $object
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
function initPageEntity(object $object): array
|
||||
{
|
||||
if(empty($object)) return array();
|
||||
|
||||
global $app, $config;
|
||||
$app->loadModuleConfig('action');
|
||||
|
||||
$module = $app->getModuleName();
|
||||
$idField = isset($config->action->objectIdFields[$module]) ? $config->action->objectIdFields[$module] : 'id';
|
||||
$titleField = isset($config->action->objectNameFields[$module]) ? $config->action->objectNameFields[$module] : 'title';
|
||||
|
||||
return array(zget($object, $titleField, ''), zget($object, $idField, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Init table data of zin.
|
||||
*
|
||||
* @param array $items
|
||||
* @param array $fieldList
|
||||
* @param object $model
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
function initTableData(array $items, array &$fieldList, object $model = null): array
|
||||
{
|
||||
$items = setParent($items);
|
||||
if(empty($fieldList['actions'])) return $items;
|
||||
|
||||
foreach($fieldList['actions']['menu'] as $actionMenu)
|
||||
{
|
||||
if(is_array($actionMenu))
|
||||
{
|
||||
foreach($actionMenu as $actionMenuKey => $actionName)
|
||||
{
|
||||
if($actionMenuKey == 'other')
|
||||
{
|
||||
foreach($actionName as $otherActionName) initTableActions($fieldList, $otherActionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
initTableActions($fieldList, $actionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initTableActions($fieldList, $actionMenu);
|
||||
}
|
||||
}
|
||||
|
||||
global $app;
|
||||
if(empty($model))
|
||||
{
|
||||
$module = $app->getModuleName();
|
||||
$model = $app->control->loadModel($module);
|
||||
}
|
||||
|
||||
$maxActionCount = 0;
|
||||
foreach($items as $item)
|
||||
{
|
||||
$item->actions = array();
|
||||
foreach($fieldList['actions']['menu'] as $actionKey => $actionMenu)
|
||||
{
|
||||
if(isset($actionMenu['other']))
|
||||
{
|
||||
$currentActionMenu = $actionMenu[0];
|
||||
initItemActions($item, $currentActionMenu, $fieldList['actions']['list'], $model);
|
||||
|
||||
$otherActionMenus = $actionMenu['other'];
|
||||
$otherAction = '';
|
||||
foreach($otherActionMenus as $otherActionMenu)
|
||||
{
|
||||
$otherActions = explode('|', $otherActionMenu);
|
||||
foreach($otherActions as $otherActionName)
|
||||
{
|
||||
if(in_array($otherActionName, array_column($item->actions, 'name'))) continue;
|
||||
|
||||
if(method_exists($model, 'isClickable') && !$model->isClickable($item, $otherActionName)) $otherAction .= '-';
|
||||
$otherAction .= $otherActionName . ',';
|
||||
}
|
||||
}
|
||||
$item->actions[] = 'other:' . $otherAction;
|
||||
}
|
||||
elseif($actionKey === 'more')
|
||||
{
|
||||
$moreAction = '';
|
||||
foreach($actionMenu as $moreActionName)
|
||||
{
|
||||
if(method_exists($model, 'isClickable') && !$model->isClickable($item, $moreActionName)) $moreAction .= '-';
|
||||
$moreAction .= $moreActionName . ',';
|
||||
}
|
||||
|
||||
$item->actions[] = 'more:' . $moreAction;
|
||||
}
|
||||
elseif(is_array($actionMenu)) // Two or more grups.
|
||||
{
|
||||
/*
|
||||
* Menu可能会有多套,如果只有一套可以直接用一维数组。
|
||||
* There are maybe two or more groups of action menus.
|
||||
*/
|
||||
$item->actions = array();
|
||||
$isClickable = false;
|
||||
foreach($actionMenu as $actionName) $isClickable |= initItemActions($item, $actionName, $fieldList['actions']['list'], $model);
|
||||
|
||||
if($isClickable) break; // If the action is clickable, use this group.
|
||||
}
|
||||
else // Only one group of action menus.
|
||||
{
|
||||
initItemActions($item, $actionMenu, $fieldList['actions']['list'], $model);
|
||||
}
|
||||
}
|
||||
|
||||
if(count($item->actions) > $maxActionCount) $maxActionCount = count($item->actions);
|
||||
}
|
||||
if(isset($fieldList['actions'])) $fieldList['actions']['minWidth'] = $maxActionCount * 24 + 24;
|
||||
if($fieldList['actions']['minWidth'] < 48) $fieldList['actions']['minWidth'] = 48;
|
||||
|
||||
return array_values($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent property of the data.
|
||||
*
|
||||
* @param array $items
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
function setParent(array $items)
|
||||
{
|
||||
foreach($items as $item)
|
||||
{
|
||||
/* Set parent attribute. */
|
||||
$item->isParent = false;
|
||||
if(isset($item->parent) && $item->parent == -1)
|
||||
{
|
||||
/* When the parent is -1, the hierarchical structure is displayed incorrectly. */
|
||||
$item->parent = 0;
|
||||
$item->isParent = true;
|
||||
}
|
||||
|
||||
if(!empty($item->parent) && isset($items[$item->parent])) $items[$item->parent]->isParent = true;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init column actions of a table.
|
||||
*
|
||||
* @param array $fieldList
|
||||
* @param string $actionMenu
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function initTableActions(array &$fieldList, string $actionMenu): void
|
||||
{
|
||||
$actions = explode('|', $actionMenu);
|
||||
foreach($actions as $action)
|
||||
{
|
||||
if(!isset($fieldList['actions']['list'][$action])) continue;
|
||||
|
||||
$actionConfig = $fieldList['actions']['list'][$action];
|
||||
$actionConfig['text'] = '';
|
||||
|
||||
if(!empty($actionConfig['url']['module']) && !empty($actionConfig['url']['method']))
|
||||
{
|
||||
$module = $actionConfig['url']['module'];
|
||||
$method = $actionConfig['url']['method'];
|
||||
$params = !empty($actionConfig['url']['params']) ? $actionConfig['url']['params'] : array();
|
||||
|
||||
$actionConfig['url'] = helper::createLink($module, $method, $params);
|
||||
}
|
||||
|
||||
$fieldList['actions']['actionsMap'][$action] = $actionConfig;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init row actions of a item.
|
||||
*
|
||||
* @param object $item
|
||||
* @param string $actionMenu
|
||||
* @param array $actionList
|
||||
* @param object $model
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
function initItemActions(object &$item, string $actionMenu, array $actionList, object $model): bool
|
||||
{
|
||||
global $app;
|
||||
$module = $app->getModuleName();
|
||||
$method = '';
|
||||
|
||||
$isClickable = false;
|
||||
$actions = explode('|', $actionMenu);
|
||||
foreach($actions as $action)
|
||||
{
|
||||
if(!isset($actionList[$action])) continue;
|
||||
|
||||
$actionConfig = $actionList[$action];
|
||||
if(!empty($actionConfig['url']['module']) && $module != $actionConfig['url']['module'])
|
||||
{
|
||||
$module = $actionConfig['url']['module'];
|
||||
$model = $app->control->loadModel($module);
|
||||
}
|
||||
|
||||
$method = $action;
|
||||
if(!empty($actionConfig['url']['method']) && $method != $actionConfig['url']['method']) $method = $actionConfig['url']['method'];
|
||||
|
||||
if(!method_exists($model, 'isClickable') || $model->isClickable($item, $method))
|
||||
{
|
||||
$isClickable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(method_exists($model, 'isDisplay') && !$model->isDisplay($item, $method)) return $isClickable;
|
||||
|
||||
if(!$method || !common::hasPriv($module, $method)) return $isClickable;
|
||||
|
||||
$item->actions[] = array('name' => $action, 'disabled' => !$isClickable);
|
||||
|
||||
return $isClickable;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ class model extends baseModel
|
||||
|
||||
if($action->extensionType == 'override') return $this->loadModel('flow')->buildActionMenu($moduleName, $action, $data, $type);
|
||||
|
||||
$conditions = json_decode($action->conditions);
|
||||
$conditions = empty($action->conditions) ? array() : json_decode($action->conditions);
|
||||
if($conditions and $action->extensionType == 'extend')
|
||||
{
|
||||
if($icon != 'copy' and $methodName != 'create') $title = $action->name;
|
||||
@@ -133,6 +133,10 @@ class model extends baseModel
|
||||
if(method_exists($this, 'isClickable')) $enabled = $this->isClickable($data, $method, $module);
|
||||
}
|
||||
}
|
||||
elseif(strpos($misc, 'disabled') !== false)
|
||||
{
|
||||
$enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(method_exists($this, 'isClickable')) $enabled = $this->isClickable($data, $method, $module);
|
||||
@@ -395,4 +399,24 @@ class model extends baseModel
|
||||
|
||||
$app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, $exit = true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dao of bi.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function loadBIDAO()
|
||||
{
|
||||
global $config, $biDAO;
|
||||
if(is_object($biDAO)) return $this->dao = $biDAO;
|
||||
|
||||
if(!isset($config->biDB)) return;
|
||||
|
||||
$driver = $config->db->driver;
|
||||
$biDAO = new $driver();
|
||||
$biDAO->slaveDBH = $this->app->connectByPDO($config->biDB, 'BI');
|
||||
|
||||
$this->dao = $biDAO;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-12
@@ -78,7 +78,7 @@ class router extends baseRouter
|
||||
{
|
||||
if($this->dbh)
|
||||
{
|
||||
$langs = $this->dbh->query('SELECT `value` FROM ' . TABLE_CONFIG . " WHERE `owner`='system' AND `module`='common' AND `section`='global' AND `key`='langs'")->fetch();
|
||||
$langs = $this->dbQuery('SELECT `value` FROM ' . TABLE_CONFIG . " WHERE `owner`='system' AND `module`='common' AND `section`='global' AND `key`='langs'")->fetch();
|
||||
$langs = empty($langs) ? array() : json_decode($langs->value, true);
|
||||
foreach($langs as $langKey => $langData) $this->config->langs[$langKey] = $langData['name'];
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class router extends baseRouter
|
||||
$customMenus = array();
|
||||
try
|
||||
{
|
||||
$customMenus = $this->dbh->query('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `section`='mainNav' AND `lang`='{$this->clientLang}' AND `vision`='{$this->config->vision}'")->fetchAll();
|
||||
$customMenus = $this->dbQuery('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `section`='mainNav' AND `lang`='{$this->clientLang}' AND `vision`='{$this->config->vision}'")->fetchAll();
|
||||
}
|
||||
catch(PDOException $exception){}
|
||||
|
||||
@@ -206,7 +206,7 @@ class router extends baseRouter
|
||||
|
||||
try
|
||||
{
|
||||
$commonSettings = $this->dbh->query('SELECT `section`, `key`, `value` FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND (`module`='custom' or `module`='common') and `key` in ('sprintConcept', 'hourPoint', 'URSR', 'mode', 'URAndSR', 'scoreStatus', 'disabledFeatures', 'closedFeatures')")->fetchAll();
|
||||
$commonSettings = $this->dbQuery('SELECT `section`, `key`, `value` FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND (`module`='custom' or `module`='common') and `key` in ('sprintConcept', 'hourPoint', 'URSR', 'mode', 'URAndSR', 'scoreStatus', 'disabledFeatures', 'closedFeatures')")->fetchAll();
|
||||
}
|
||||
catch (PDOException $exception)
|
||||
{
|
||||
@@ -274,7 +274,7 @@ class router extends baseRouter
|
||||
{
|
||||
$sql = new sql();
|
||||
$account = $sql->quote($account);
|
||||
$userSetting = $this->dbh->query('SELECT `key`, `value` FROM ' . TABLE_CONFIG . " WHERE `owner`= $account AND `module`='common' and `key` in ('programLink', 'productLink', 'projectLink', 'executionLink', 'URSR')")->fetchAll();
|
||||
$userSetting = $this->dbQuery('SELECT `key`, `value` FROM ' . TABLE_CONFIG . " WHERE `owner`= $account AND `module`='common' and `key` in ('programLink', 'productLink', 'projectLink', 'executionLink', 'URSR')")->fetchAll();
|
||||
}
|
||||
|
||||
foreach($userSetting as $setting)
|
||||
@@ -290,7 +290,7 @@ class router extends baseRouter
|
||||
$lang->SRCommon = '';
|
||||
if($this->dbh and !empty($this->config->db->name))
|
||||
{
|
||||
$productProject = $this->dbh->query('SELECT `value` FROM ' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='custom' AND `key`='productProject'")->fetch();
|
||||
$productProject = $this->dbQuery('SELECT `value` FROM ' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='custom' AND `key`='productProject'")->fetch();
|
||||
if($productProject)
|
||||
{
|
||||
$productProject = $productProject->value;
|
||||
@@ -301,8 +301,8 @@ class router extends baseRouter
|
||||
{
|
||||
/* Get story concept in project and product. */
|
||||
$clientLang = $this->clientLang == 'zh-tw' ? 'zh-cn' : $this->clientLang;
|
||||
$URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE `module` = 'custom' and `section` = 'URSRList' and `lang` = '{$clientLang}'")->fetchAll();
|
||||
if(empty($URSRList)) $URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and `section` = 'URSRList' and `key` = '{$config->URSR}'")->fetchAll();
|
||||
$URSRList = $this->dbQuery('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE `module` = 'custom' and `section` = 'URSRList' and `lang` = '{$clientLang}'")->fetchAll();
|
||||
if(empty($URSRList)) $URSRList = $this->dbQuery('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and `section` = 'URSRList' and `key` = '{$config->URSR}'")->fetchAll();
|
||||
|
||||
/* Get UR pairs and SR pairs. */
|
||||
$URPairs = array();
|
||||
@@ -323,7 +323,7 @@ class router extends baseRouter
|
||||
$customMenus = array();
|
||||
try
|
||||
{
|
||||
$customMenus = $this->dbh->query('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `lang`='{$this->clientLang}' AND `section`='' AND `vision`='{$config->vision}'")->fetchAll();
|
||||
$customMenus = $this->dbQuery('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `lang`='{$this->clientLang}' AND `section`='' AND `vision`='{$config->vision}'")->fetchAll();
|
||||
}
|
||||
catch(PDOException $exception){}
|
||||
foreach($customMenus as $menu) if(isset($lang->{$menu->key})) $lang->{$menu->key} = $menu->value;
|
||||
@@ -375,8 +375,11 @@ class router extends baseRouter
|
||||
/* 先获得模块的主配置文件。Get the main config file for current module first. */
|
||||
$mainConfigFile = $this->getModulePath($appName, $moduleName) . 'config.php';
|
||||
|
||||
/* 获取 config 目录的配置文件。Get config files from config directory. */
|
||||
$configDirFiles = helper::ls($this->getModulePath($appName, $moduleName) . DS . 'config', '.php');
|
||||
|
||||
/* 查找扩展配置文件。Get extension config files. */
|
||||
if($config->framework->extensionLevel > 0) $extConfigPath = $this->getModuleExtPath($appName, $moduleName, 'config');
|
||||
if($config->framework->extensionLevel > 0) $extConfigPath = $this->getModuleExtPath($moduleName, 'config');
|
||||
if($config->framework->extensionLevel >= 1)
|
||||
{
|
||||
if(!empty($extConfigPath['common'])) $commonExtConfigFiles = helper::ls($extConfigPath['common'], '.php');
|
||||
@@ -386,7 +389,7 @@ class router extends baseRouter
|
||||
if(!empty($extConfigPath['custom'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['custom'], '.php'));
|
||||
}
|
||||
if($config->framework->extensionLevel == 2 and !empty($extConfigPath['site'])) $siteExtConfigFiles = helper::ls($extConfigPath['site'], '.php');
|
||||
$extConfigFiles = array_merge($commonExtConfigFiles, $siteExtConfigFiles);
|
||||
$extConfigFiles = array_merge($commonExtConfigFiles, $configDirFiles, $siteExtConfigFiles);
|
||||
|
||||
/* 将主配置文件和扩展配置文件合并在一起。Put the main config file and extension config files together. */
|
||||
$configFiles = array_merge(array($mainConfigFile), $extConfigFiles);
|
||||
@@ -481,7 +484,7 @@ class router extends baseRouter
|
||||
if($this->config->edition == 'open' or defined('IN_INSTALL') or defined('IN_UPGRADE')) return parent::setControlFile($exitIfNone);
|
||||
|
||||
/* Check if the requested module is defined in workflow. */
|
||||
$flow = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOW . " WHERE `module` = '$this->moduleName'")->fetch();
|
||||
$flow = $this->dbQuery("SELECT * FROM " . TABLE_WORKFLOW . " WHERE `module` = '$this->moduleName'")->fetch();
|
||||
if(!$flow) return parent::setControlFile($exitIfNone);
|
||||
if($flow->status != 'normal') die("<html><head><meta charset='utf-8'></head><body>{$this->lang->flowNotRelease}</body></html>");
|
||||
|
||||
@@ -509,7 +512,7 @@ class router extends baseRouter
|
||||
$actionQuery = "SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '$this->moduleName' AND `action` = '$this->methodName'";
|
||||
if(isset($this->app->user) && $this->app->user->account != 'guest') $actionQuery .= " AND `vision` = '{$this->config->vision}'";
|
||||
|
||||
$action = $this->dbh->query($actionQuery)->fetch();
|
||||
$action = $this->dbQuery($actionQuery)->fetch();
|
||||
if(zget($action, 'extensionType') == 'override')
|
||||
{
|
||||
$this->rawModule = $this->moduleName;
|
||||
|
||||
+16
-10
@@ -406,7 +406,6 @@ class baseDAO
|
||||
if($orderPOS) $subLength = $orderPOS;
|
||||
if($groupPOS) $subLength = $groupPOS;
|
||||
$sql = substr($sql, 0, $subLength);
|
||||
self::$querys[] = $sql;
|
||||
|
||||
/*
|
||||
* 获取记录数。
|
||||
@@ -414,7 +413,8 @@ class baseDAO
|
||||
**/
|
||||
try
|
||||
{
|
||||
$row = $this->dbh->rawQuery($sql)->fetch(PDO::FETCH_OBJ);
|
||||
$dbh = $this->slaveDBH ? $this->slaveDBH : $this->dbh;
|
||||
$row = $dbh->rawQuery($sql)->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
@@ -568,7 +568,7 @@ class baseDAO
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->processKeywords($this->processSQL(false));
|
||||
return self::processKeywords($this->processSQL());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,7 +605,7 @@ class baseDAO
|
||||
* @access public
|
||||
* @return string the sql string after process.
|
||||
*/
|
||||
public function processSQL($record = true)
|
||||
public function processSQL()
|
||||
{
|
||||
$sql = $this->sqlobj->get();
|
||||
|
||||
@@ -682,7 +682,6 @@ class baseDAO
|
||||
}
|
||||
}
|
||||
|
||||
if($record) self::$querys[] = $this->processKeywords($sql);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
@@ -694,7 +693,7 @@ class baseDAO
|
||||
* @access public
|
||||
* @return string the sql string.
|
||||
*/
|
||||
public function processKeywords($sql)
|
||||
static public function processKeywords($sql)
|
||||
{
|
||||
return str_replace(array(DAO::WHERE, DAO::GROUPBY, DAO::HAVING, DAO::ORDERBY, DAO::LIMIT), array('WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT'), $sql);
|
||||
}
|
||||
@@ -748,12 +747,15 @@ class baseDAO
|
||||
$method = $this->method;
|
||||
$this->reset();
|
||||
|
||||
if($this->slaveDBH and $method == 'select')
|
||||
if($this->slaveDBH and in_array($method, array('select', 'desc')))
|
||||
{
|
||||
return $this->slaveDBH->rawQuery($sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Force to query from master db, if db has been changed. */
|
||||
$this->slaveDBH = false;
|
||||
|
||||
return $this->dbh->rawQuery($sql);
|
||||
}
|
||||
}
|
||||
@@ -847,6 +849,10 @@ class baseDAO
|
||||
{
|
||||
if($this->table) unset(dao::$cache[$this->table]);
|
||||
$this->reset();
|
||||
|
||||
/* Force to query from master db, if db has been changed. */
|
||||
$this->slaveDBH = false;
|
||||
|
||||
return $this->dbh->exec($sql);
|
||||
}
|
||||
catch (PDOException $e)
|
||||
@@ -1925,7 +1931,7 @@ class baseSQL
|
||||
}
|
||||
else
|
||||
{
|
||||
$condition = ctype_alnum($arg1) ? '`' . $arg1 . '`' : $arg1;
|
||||
$condition = (is_string($arg1) && ctype_alnum($arg1)) ? '`' . $arg1 . '`' : $arg1;
|
||||
}
|
||||
|
||||
if(!$this->inMark) $this->sql .= ' ' . DAO::WHERE ." $condition ";
|
||||
@@ -1944,7 +1950,7 @@ class baseSQL
|
||||
public function andWhere($condition, $addMark = false)
|
||||
{
|
||||
if($this->inCondition and !$this->conditionIsTrue) return $this;
|
||||
if(ctype_alnum($condition)) $condition = '`' . $condition . '`';
|
||||
if(is_string($condition) && ctype_alnum($condition)) $condition = '`' . $condition . '`';
|
||||
|
||||
$mark = $addMark ? '(' : '';
|
||||
$this->sql .= " AND {$mark} $condition ";
|
||||
@@ -1962,7 +1968,7 @@ class baseSQL
|
||||
public function orWhere($condition)
|
||||
{
|
||||
if($this->inCondition and !$this->conditionIsTrue) return $this;
|
||||
if(ctype_alnum($condition)) $condition = '`' . $condition . '`';
|
||||
if(is_string($condition) && ctype_alnum($condition)) $condition = '`' . $condition . '`';
|
||||
|
||||
$this->sql .= " OR $condition ";
|
||||
return $this;
|
||||
|
||||
@@ -661,7 +661,7 @@ class baseValidater
|
||||
global $config;
|
||||
if(empty($config->framework->filterXSS)) return $var;
|
||||
|
||||
if(stripos($var, '<script') !== false or stripos($var, '<script') !== false)
|
||||
if(stripos($var, '<script') !== false || stripos($var, '<script') !== false)
|
||||
{
|
||||
$var = (string)$var;
|
||||
$evils = array('appendchild(', 'createElement(', 'xss.re', 'onfocus', 'onclick', 'innerHTML', 'replaceChild(', 'html(', 'append(', 'appendTo(', 'prepend(', 'prependTo(', 'after(', 'insertBefore', 'before(', 'replaceWith(', 'alert(', 'confirm(');
|
||||
|
||||
@@ -1160,18 +1160,8 @@ EOT;
|
||||
return $js;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出$config到js,因为js的createLink()方法需要获取config信息。
|
||||
* Export the config vars for createLink() js version.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
static public function exportConfigVars()
|
||||
static function getJSConfigVars()
|
||||
{
|
||||
if(!function_exists('json_encode')) return false;
|
||||
|
||||
global $app, $config, $lang;
|
||||
$defaultViewType = $app->getViewType();
|
||||
$themeRoot = $app->getWebRoot() . 'theme/';
|
||||
@@ -1180,7 +1170,7 @@ EOT;
|
||||
$clientLang = $app->getClientLang();
|
||||
$runMode = defined('RUN_MODE') ? RUN_MODE : '';
|
||||
$requiredFields = '';
|
||||
if(isset($config->$moduleName->$methodName->requiredFields)) $requiredFields = str_replace(' ', '', $config->$moduleName->$methodName->requiredFields);
|
||||
if(isset($config->$moduleName->$methodName->requiredFields)) $requiredFields = str_replace(' ', '', (string) $config->$moduleName->$methodName->requiredFields);
|
||||
|
||||
$jsConfig = new stdclass();
|
||||
$jsConfig->webRoot = $config->webRoot;
|
||||
@@ -1199,21 +1189,41 @@ EOT;
|
||||
$jsConfig->clientLang = $clientLang;
|
||||
$jsConfig->requiredFields = $requiredFields;
|
||||
$jsConfig->router = $app->server->SCRIPT_NAME;
|
||||
$jsConfig->save = isset($lang->save) ? $lang->save : '';
|
||||
$jsConfig->save = $lang->save ?? '';
|
||||
$jsConfig->runMode = $runMode;
|
||||
$jsConfig->timeout = isset($config->timeout) ? $config->timeout : '';
|
||||
$jsConfig->pingInterval = isset($config->pingInterval) ? $config->pingInterval : '';
|
||||
$jsConfig->timeout = $config->timeout ?? '';
|
||||
$jsConfig->pingInterval = $config->pingInterval ?? '';
|
||||
$jsConfig->onlybody = zget($_GET, 'onlybody', 'no');
|
||||
$jsConfig->version = $config->version;
|
||||
$jsConfig->tabSession = $config->tabSession;
|
||||
if($config->tabSession and helper::isWithTID()) $jsConfig->tid = zget($_GET, 'tid', '');
|
||||
|
||||
return $jsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出$config到js,因为js的createLink()方法需要获取config信息。
|
||||
* Export the config vars for createLink() js version.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
static public function exportConfigVars()
|
||||
{
|
||||
if(!function_exists('json_encode')) return false;
|
||||
|
||||
global $lang;
|
||||
|
||||
$jsConfig = static::getJSConfigVars();
|
||||
|
||||
$jsLang = new stdclass();
|
||||
$jsLang->submitting = isset($lang->loading) ? $lang->loading : '';
|
||||
$jsLang->submitting = $lang->loading ?? '';
|
||||
$jsLang->save = $jsConfig->save;
|
||||
$jsLang->expand = isset($lang->expand) ? $lang->expand : '';
|
||||
$jsLang->timeout = isset($lang->timeout) ? $lang->timeout : '';
|
||||
$jsLang->confirmDraft = isset($lang->confirmDraft) ? $lang->confirmDraft : '';
|
||||
$jsLang->resume = isset($lang->resume) ? $lang->resume : '';
|
||||
$jsLang->expand = $lang->expand ?? '';
|
||||
$jsLang->timeout = $lang->timeout ?? '';
|
||||
$jsLang->confirmDraft = $lang->confirmDraft ?? '';
|
||||
$jsLang->resume = $lang->resume ?? '';
|
||||
$jsLang->program = zget($lang->program, 'common', '');
|
||||
$jsLang->project = zget($lang->project, 'common', '');
|
||||
$jsLang->product = zget($lang->product, 'common', '');
|
||||
@@ -1300,12 +1310,12 @@ EOT;
|
||||
elseif(is_bool($value))
|
||||
{
|
||||
$value = $value ? 'true' : 'false';
|
||||
$js .= "{$prefix}{$key} = $value;";
|
||||
$js .= "{$prefix}{$key} = $value;";
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = addslashes($value);
|
||||
$js .= "{$prefix}{$key} = '{$value}';";
|
||||
$value = empty($value) ? '' : addslashes($value);
|
||||
$js .= "{$prefix}{$key} = '{$value}';";
|
||||
}
|
||||
$js .= static::end($newline = false);
|
||||
echo $js;
|
||||
|
||||
+23
-5
@@ -19,6 +19,14 @@
|
||||
*/
|
||||
class dbh
|
||||
{
|
||||
/**
|
||||
* Flag for database.
|
||||
*
|
||||
* @var string MASTER|SLAVE|BI
|
||||
* @access private
|
||||
*/
|
||||
private $flag;
|
||||
|
||||
/**
|
||||
* PDO.
|
||||
*
|
||||
@@ -40,10 +48,11 @@ class dbh
|
||||
*
|
||||
* @param object $config
|
||||
* @param bool $setSchema
|
||||
* @param string $flag
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($config, $setSchema = true)
|
||||
public function __construct($config, $setSchema = true, $flag = 'MASTER')
|
||||
{
|
||||
$dsn = "{$config->driver}:host={$config->host};port={$config->port}";
|
||||
if($setSchema) $dsn .= ";dbname={$config->name}";
|
||||
@@ -64,6 +73,7 @@ class dbh
|
||||
|
||||
$this->pdo = $pdo;
|
||||
$this->config = $config;
|
||||
$this->flag = $flag;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,6 +88,7 @@ class dbh
|
||||
$sql = $this->formatSQL($sql);
|
||||
if(!$sql) return true;
|
||||
|
||||
if(class_exists('dao')) dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
return $this->pdo->exec($sql);
|
||||
}
|
||||
|
||||
@@ -91,7 +102,11 @@ class dbh
|
||||
public function query($sql)
|
||||
{
|
||||
$sql = $this->formatSQL($sql);
|
||||
return $this->pdo->query($sql);
|
||||
|
||||
if(class_exists('dao')) dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
|
||||
$result = $this->pdo->query($sql);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +118,11 @@ class dbh
|
||||
*/
|
||||
public function rawQuery($sql)
|
||||
{
|
||||
return $this->pdo->query($sql);
|
||||
if(class_exists('dao')) dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
|
||||
$result = $this->pdo->query($sql);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,7 +336,7 @@ class dbh
|
||||
}
|
||||
elseif(stripos($sql, 'CREATE UNIQUE INDEX') === 0 || stripos($sql, 'CREATE INDEX') === 0)
|
||||
{
|
||||
preg_match('/ON\ +`([0-9a-zA-Z_]+)`/', $sql, $matches);
|
||||
preg_match('/ON\s+[^.`\s]+\.`([^\s`]+)`/', $sql, $matches);
|
||||
|
||||
$tableName = str_replace($this->config->prefix, '', $matches);
|
||||
$sql = preg_replace('/INDEX\ +\`/', 'INDEX `' . strtolower($tableName[1]) . '_', $sql);
|
||||
@@ -668,7 +687,6 @@ class dbh
|
||||
return $this->pdo->commit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepares a statement for execution and returns a statement object.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The form class file 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 form
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
helper::import(dirname(dirname(__FILE__)) . '/filter/filter.class.php');
|
||||
|
||||
class form extends fixer
|
||||
{
|
||||
/**
|
||||
* 批量处理的数据。
|
||||
* The data to be fixed.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $dataList;
|
||||
|
||||
/**
|
||||
* 类型。single|batch
|
||||
* Type. single|batch
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $formType = 'single';
|
||||
|
||||
/**
|
||||
* 原始配置。
|
||||
* The raw cofig.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $rawconfig;
|
||||
|
||||
/**
|
||||
* 错误信息列表。
|
||||
* Error list.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $errors;
|
||||
|
||||
/**
|
||||
* 构造方法。
|
||||
* The construct function.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->rawdata = (object)$_POST;
|
||||
$this->data = (object)array();
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表单数据。
|
||||
* Get the form data.
|
||||
*
|
||||
* @param array|null $configObject
|
||||
* @return form
|
||||
*/
|
||||
public static function data(array $configObject = null): form
|
||||
{
|
||||
global $app, $config;
|
||||
|
||||
if($configObject === null) $configObject = $config->{$app->moduleName}->form->{$app->methodName};
|
||||
return (new form)->config($configObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取批量表单数据。
|
||||
* Get the batch form data.
|
||||
*
|
||||
* @param array|null $configObject
|
||||
* @return form
|
||||
*/
|
||||
public static function batchData(array $configObject = null): form
|
||||
{
|
||||
global $app, $config;
|
||||
|
||||
if($configObject === null) $configObject = $config->{$app->moduleName}->form->{$app->methodName};
|
||||
return (new form)->config($configObject, 'batch');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表单配置项。
|
||||
* Set form configuration.
|
||||
*
|
||||
* @param array $config
|
||||
* @param string $type single|batch
|
||||
* @return $this
|
||||
* @throws EndResponseException
|
||||
*/
|
||||
public function config(array $config, string $type = 'single')
|
||||
{
|
||||
$this->rawconfig = $config;
|
||||
$this->formType = $type;
|
||||
|
||||
if($type == 'single')
|
||||
{
|
||||
foreach($this->rawconfig as $field => $fieldConfig)
|
||||
{
|
||||
if(isset($fieldConfig['control']) && in_array($fieldConfig['control'], array('textarea', 'richtext'))) $this->skipSpecial($field);
|
||||
$this->convertField($field, $fieldConfig);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->batchConvertField($config);
|
||||
}
|
||||
|
||||
if(!empty($this->errors))
|
||||
{
|
||||
$response = array('result' => 'fail', 'message' => $this->errors);
|
||||
helper::end(json_encode($response));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量转换字段类型。
|
||||
* Batch convert the field type.
|
||||
*
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function batchConvertField(array $fieldConfigs)
|
||||
{
|
||||
global $app;
|
||||
|
||||
$rowDataList = array();
|
||||
$baseField = '';
|
||||
|
||||
foreach($fieldConfigs as $field => $config)
|
||||
{
|
||||
/* 在第一行提示类型未定义。 Display type error in the first row. */
|
||||
if(!isset($config['type']))
|
||||
{
|
||||
if(empty($this->errors)) $this->errors[1] = array();
|
||||
if(!isset($this->errors[1][$field])) $this->errors[1][$field] = array();
|
||||
|
||||
$this->errors[1][$field][] = "Field '{$field}' need defined type";
|
||||
}
|
||||
|
||||
/* 以该字段为标准,判断某一行是否要构造数据。 If the value of the field in a row is empty, skip that row. */
|
||||
if(!empty($config['base'])) $baseField = $field;
|
||||
|
||||
if(isset($config['control']) && in_array($config['control'], array('textarea', 'richtext'))) $this->skipSpecial($field);
|
||||
}
|
||||
|
||||
/* 在第一行提示标准字段不能为空。 Display the field error in the first row. */
|
||||
if(!isset($this->rawdata->$baseField))
|
||||
{
|
||||
if(empty($this->errors)) $this->errors[1] = array();
|
||||
if(!isset($this->errors[1][$field])) $this->errors[1][$field] = array();
|
||||
$this->errors[1][$field][] = "Field '{$field}' not empty";
|
||||
}
|
||||
|
||||
/* 构造批量表单数据。Construct batch form data. */
|
||||
foreach($this->rawdata->$baseField as $rowIndex => $value)
|
||||
{
|
||||
if(empty($value)) continue;
|
||||
|
||||
$rowData = new stdclass();
|
||||
foreach($fieldConfigs as $field => $config)
|
||||
{
|
||||
$defaultValue = zget($config, 'default', '');
|
||||
|
||||
$rowData->$field = isset($this->rawdata->$field) ? zget($this->rawdata->$field, $rowIndex, $defaultValue) : $defaultValue;
|
||||
$rowData->$field = helper::convertType($rowData->$field, $config['type']);
|
||||
if(isset($config['filter'])) $rowData->$field = $this->filter($rowData->$field, $config['filter']);
|
||||
|
||||
/* 检查必填字段。Check required fields. */
|
||||
if(isset($config['required']) && $config['required'] && empty($rowData->$field))
|
||||
{
|
||||
$fieldName = isset($app->lang->{$app->rawModule}->$field) ? $app->lang->{$app->rawModule}->$field : $field;
|
||||
if(!isset($this->errors["{$field}[{$rowIndex}]"])) $this->errors["{$field}[{$rowIndex}]"] = array();
|
||||
$this->errors["{$field}[{$rowIndex}]"] = sprintf($app->lang->error->notempty, $fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
$rowDataList[$rowIndex] = $rowData;
|
||||
}
|
||||
|
||||
$this->dataList = $rowDataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取$_POST的数据。
|
||||
* Get the data of $_POST.
|
||||
*
|
||||
* @param bool $isRaw 是否获取原始数据。Whether to get the raw data.
|
||||
* @return object
|
||||
*/
|
||||
public function getAll(bool $isRaw = false): object
|
||||
{
|
||||
return $isRaw ? $this->rawdata : $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换字段类型。
|
||||
* Convert the field type.
|
||||
*
|
||||
* @param string $field
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function convertField(string $field, array $config)
|
||||
{
|
||||
global $app;
|
||||
|
||||
if(!isset($config['type']))
|
||||
{
|
||||
if(!isset($this->errors[$field])) $this->errors[$field] = array();
|
||||
$this->errors[$field][] = "Field '{$field}' need defined type";
|
||||
}
|
||||
|
||||
$requireValue = isset($config['required']) && $config['required'] && !isset($config['default']);
|
||||
if($requireValue && (!isset($this->rawdata->$field) || $this->rawdata->$field === ''))
|
||||
{
|
||||
if(!isset($this->errors[$field])) $this->errors[$field] = array();
|
||||
$fieldName = isset($app->lang->{$app->rawModule}->$field) ? $app->lang->{$app->rawModule}->$field : $field;
|
||||
$this->errors[$field][] = sprintf($app->lang->error->notempty, $fieldName);
|
||||
}
|
||||
|
||||
if(isset($this->rawdata->$field))
|
||||
{
|
||||
$data = $this->rawdata->$field;
|
||||
}
|
||||
|
||||
if(array_key_exists('default', $config) && !isset($this->rawdata->$field))
|
||||
{
|
||||
$data = $config['default'];
|
||||
}
|
||||
|
||||
$data = helper::convertType($data, $config['type']);
|
||||
|
||||
if(isset($config['filter']))
|
||||
{
|
||||
$data = $this->filter($data, $config['filter']);
|
||||
}
|
||||
|
||||
$this->data->$field = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special array.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function specialArray($data): mixed
|
||||
{
|
||||
if(!is_array($data))
|
||||
{
|
||||
if(is_string($data)) return htmlspecialchars($data, ENT_QUOTES);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
foreach($data as &$value) $value = $this->specialArray($value);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤表单字段数据。
|
||||
* Filter the form field data.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param mixed $filter
|
||||
* @return string
|
||||
*/
|
||||
protected function filter($value, $filter)
|
||||
{
|
||||
switch($filter)
|
||||
{
|
||||
case 'trim':
|
||||
return trim($value);
|
||||
case 'join':
|
||||
return implode(',', $value);
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤富文字段数据。
|
||||
* Filter the editor fields.
|
||||
*
|
||||
* @param string $fields
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($fields = ''): mixed
|
||||
{
|
||||
global $config;
|
||||
|
||||
foreach($this->rawconfig as $field => $fieldConfig)
|
||||
{
|
||||
if(isset($fieldConfig['control']) && $fieldConfig['control'] == 'editor') $this->stripTags($field, $config->allowedTags);
|
||||
}
|
||||
|
||||
if($this->formType == 'single') return parent::get($fields);
|
||||
foreach($this->dataList as $rowIndex => $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->dataList[$rowIndex] = parent::get($fields);
|
||||
}
|
||||
return $this->dataList;
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate {
|
||||
* @param string $key Item key
|
||||
* @return boolean Does the item exist?
|
||||
*/
|
||||
public function offsetExists($key) {
|
||||
public function offsetExists($key): bool {
|
||||
return isset($this->cookies[$key]);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate {
|
||||
* @param string $key Item key
|
||||
* @return string|null Item value (null if offsetExists is false)
|
||||
*/
|
||||
public function offsetGet($key) {
|
||||
public function offsetGet($key): string|null {
|
||||
if (!isset($this->cookies[$key])) {
|
||||
return null;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate {
|
||||
* @param string $key Item name
|
||||
* @param string $value Item value
|
||||
*/
|
||||
public function offsetSet($key, $value) {
|
||||
public function offsetSet($key, $value): void {
|
||||
if ($key === null) {
|
||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
||||
}
|
||||
@@ -99,7 +99,7 @@ class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate {
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function offsetUnset($key) {
|
||||
public function offsetUnset($key): void {
|
||||
unset($this->cookies[$key]);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate {
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator() {
|
||||
public function getIterator(): ArrayIterator {
|
||||
return new ArrayIterator($this->cookies);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictiona
|
||||
* @param string $key
|
||||
* @return string|null Header value
|
||||
*/
|
||||
public function offsetGet($key) {
|
||||
public function offsetGet($key): string|null {
|
||||
$key = strtolower($key);
|
||||
if (!isset($this->data[$key])) {
|
||||
return null;
|
||||
@@ -40,7 +40,7 @@ class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictiona
|
||||
* @param string $key Item name
|
||||
* @param string $value Item value
|
||||
*/
|
||||
public function offsetSet($key, $value) {
|
||||
public function offsetSet($key, $value): void {
|
||||
if ($key === null) {
|
||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
||||
}
|
||||
@@ -92,7 +92,7 @@ class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictiona
|
||||
* Converts the internal
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator() {
|
||||
public function getIterator(): ArrayIterator {
|
||||
return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,91 +13,97 @@
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
class Requests_Utility_CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate {
|
||||
/**
|
||||
* Actual item data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
/**
|
||||
* Actual item data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Creates a case insensitive dictionary.
|
||||
*
|
||||
* @param array $data Dictionary/map to convert to case-insensitive
|
||||
*/
|
||||
public function __construct(array $data = array()) {
|
||||
foreach ($data as $key => $value) {
|
||||
$this->offsetSet($key, $value);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a case insensitive dictionary.
|
||||
*
|
||||
* @param array $data Dictionary/map to convert to case-insensitive
|
||||
*/
|
||||
public function __construct(array $data = array()) {
|
||||
foreach ($data as $key => $value) {
|
||||
$this->offsetSet($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given item exists
|
||||
*
|
||||
* @param string $key Item key
|
||||
* @return boolean Does the item exist?
|
||||
*/
|
||||
public function offsetExists($key) {
|
||||
$key = strtolower($key);
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
/**
|
||||
* Check if the given item exists
|
||||
*
|
||||
* @param string $key Item key
|
||||
* @return boolean Does the item exist?
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($key): bool {
|
||||
$key = strtolower($key);
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for the item
|
||||
*
|
||||
* @param string $key Item key
|
||||
* @return string|null Item value (null if offsetExists is false)
|
||||
*/
|
||||
public function offsetGet($key) {
|
||||
$key = strtolower($key);
|
||||
if (!isset($this->data[$key])) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Get the value for the item
|
||||
*
|
||||
* @param string $key Item key
|
||||
* @return string|null Item value (null if offsetExists is false)
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key): string|null {
|
||||
$key = strtolower($key);
|
||||
if (!isset($this->data[$key])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->data[$key];
|
||||
}
|
||||
return $this->data[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given item
|
||||
*
|
||||
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
|
||||
*
|
||||
* @param string $key Item name
|
||||
* @param string $value Item value
|
||||
*/
|
||||
public function offsetSet($key, $value) {
|
||||
if ($key === null) {
|
||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
||||
}
|
||||
/**
|
||||
* Set the given item
|
||||
*
|
||||
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
|
||||
*
|
||||
* @param string $key Item name
|
||||
* @param string $value Item value
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($key, $value): void {
|
||||
if ($key === null) {
|
||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
||||
}
|
||||
|
||||
$key = strtolower($key);
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
$key = strtolower($key);
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset the given header
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function offsetUnset($key) {
|
||||
unset($this->data[strtolower($key)]);
|
||||
}
|
||||
/**
|
||||
* Unset the given header
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($key): void {
|
||||
unset($this->data[strtolower($key)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator for the data
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator() {
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
/**
|
||||
* Get an iterator for the data
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator(): ArrayIterator {
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the headers as an array
|
||||
*
|
||||
* @return array Header data
|
||||
*/
|
||||
public function getAll() {
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* Get the headers as an array
|
||||
*
|
||||
* @return array Header data
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getAll() {
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +322,9 @@ class Gitea
|
||||
if(strpos($fromRevision, '^') !== false)
|
||||
{
|
||||
$list = execCmd(escapeCmd("$this->client log -2 $toRevision --pretty=format:%H -- $path"), 'array');
|
||||
if(isset($list[1])) $fromRevision = $list[1];
|
||||
if(!isset($list[1])) return execCmd(escapeCmd("$this->client show HEAD"), 'array');
|
||||
|
||||
$fromRevision = $list[1];
|
||||
}
|
||||
$lines = execCmd(escapeCmd("$this->client diff $fromRevision $toRevision -- $path"), 'array');
|
||||
return $lines;
|
||||
|
||||
+17
-18
@@ -146,7 +146,7 @@ class gitlab
|
||||
{
|
||||
$params['page'] = $page;
|
||||
$list = $this->fetch($api, $params);
|
||||
if(empty($list)) break;
|
||||
if(empty($list) || !is_array($list)) break;
|
||||
|
||||
foreach($list as $tag) $tags[] = $tag->name;
|
||||
if(count($list) < $params['per_page']) break;
|
||||
@@ -173,7 +173,7 @@ class gitlab
|
||||
{
|
||||
$params['page'] = $page;
|
||||
$branchList = $this->fetch("branches", $params);
|
||||
if(empty($branchList)) break;
|
||||
if(empty($branchList) || !is_array($branchList)) break;
|
||||
|
||||
foreach($branchList as $branch)
|
||||
{
|
||||
@@ -260,7 +260,6 @@ class gitlab
|
||||
if(empty($results) or isset($results->message)) return array();
|
||||
|
||||
$blames = array();
|
||||
$revLine = 0;
|
||||
$revision = '';
|
||||
|
||||
$lineNumber = 1;
|
||||
@@ -305,22 +304,23 @@ class gitlab
|
||||
if(!scm::checkRevision($fromRevision) and $extra != 'isBranchOrTag') return array();
|
||||
if(!scm::checkRevision($toRevision) and $extra != 'isBranchOrTag') return array();
|
||||
|
||||
$api = "compare";
|
||||
$params = array('from' => $fromRevision, 'to' => $toRevision);
|
||||
$sameVersion = $fromRevision == $toRevision . '^';
|
||||
$api = $sameVersion ? "commits/$toRevision/diff" : "compare";
|
||||
$params = array('from' => $fromRevision, 'to' => $toRevision, 'straight' => true);
|
||||
if($fromProject) $params['from_project_id'] = $fromProject;
|
||||
|
||||
if($toRevision == 'HEAD' and $this->branch) $params['to'] = $this->branch;
|
||||
$results = $this->fetch($api, $params);
|
||||
if(!isset($results->diffs)) return array();
|
||||
$results = $this->fetch($api, $sameVersion ? array() : $params);
|
||||
|
||||
$diffs = isset($results->diffs) ? $results->diffs : array();
|
||||
if($sameVersion && is_array($results)) $diffs = $results;
|
||||
if(!$diffs) return array();
|
||||
|
||||
foreach($results->diffs as $key => $diff)
|
||||
{
|
||||
if($path != '' and strpos($diff->new_path, $path) === false) unset($results->diffs[$key]);
|
||||
}
|
||||
$diffs = $results->diffs;
|
||||
$lines = array();
|
||||
foreach($diffs as $diff)
|
||||
{
|
||||
if($path != '' && strpos($diff->new_path, $path) === false) continue;
|
||||
|
||||
$lines[] = sprintf("diff --git a/%s b/%s", $diff->old_path, $diff->new_path);
|
||||
$lines[] = sprintf("index %s ... %s %s ", $fromRevision, $toRevision, $diff->b_mode);
|
||||
$lines[] = sprintf("--a/%s", $diff->old_path);
|
||||
@@ -374,7 +374,7 @@ class gitlab
|
||||
$list = $this->tree($parent, 0);
|
||||
$file = new stdclass();
|
||||
|
||||
foreach($list as $node) if($node->path == $entry) $file = $node;
|
||||
if(!empty($list)) foreach($list as $node) if($node->path == $entry) $file = $node;
|
||||
|
||||
$commits = $this->getCommitsByPath($entry);
|
||||
|
||||
@@ -789,7 +789,7 @@ class gitlab
|
||||
$allResults = array();
|
||||
if($multi)
|
||||
{
|
||||
$results = commonModel::httpWithHeader($api . "&page=1");
|
||||
$results = commonModel::http($api . "&page=1", null, array(), array(), 'data', 'GET', 30, true, false);
|
||||
if(empty($results['header']['X-Total-Pages'])) return array();
|
||||
|
||||
$totalPages = $results['header']['X-Total-Pages'];
|
||||
@@ -829,15 +829,15 @@ class gitlab
|
||||
}
|
||||
else
|
||||
{
|
||||
list($response, $httpCode) = commonModel::http($api, null, array(), array(), 'data', 'POST', 30, true, false);
|
||||
$response = commonModel::http($api, null, array(), array(), 'data', 'POST', 30, true, false);
|
||||
if(!empty(commonModel::$requestErrors))
|
||||
{
|
||||
commonModel::$requestErrors = array();
|
||||
return array();
|
||||
}
|
||||
|
||||
if($httpCode == 500 or $httpCode == 404) return array();
|
||||
return json_decode($response);
|
||||
if(in_array($response[1], array(500, 404, 401))) return array();
|
||||
return json_decode($response['body']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -867,7 +867,6 @@ class gitlab
|
||||
public function parseLog($logs)
|
||||
{
|
||||
$parsedLogs = array();
|
||||
$i = 0;
|
||||
foreach($logs as $commit)
|
||||
{
|
||||
if(!isset($commit->id)) continue;
|
||||
|
||||
@@ -247,7 +247,7 @@ class GitRepo
|
||||
|
||||
$path = ltrim($path, DIRECTORY_SEPARATOR);
|
||||
chdir($this->root);
|
||||
$list = execCmd(escapeCmd("$this->client blame -l $revision -- $path"), 'array');
|
||||
$list = execCmd(escapeCmd("$this->client blame -c -l $revision -- $path"), 'array');
|
||||
|
||||
$blames = array();
|
||||
$revLine = 0;
|
||||
@@ -256,7 +256,7 @@ class GitRepo
|
||||
{
|
||||
if(empty($line)) continue;
|
||||
if($line[0] == '^') $line = substr($line, 1);
|
||||
preg_match('/^([0-9a-f]{39,40})\s.*\((\S+)\s+([\d-]+)\s(.*)\s(\d+)\)(.*)$/U', $line, $matches);
|
||||
preg_match('/^([0-9a-f]{39,40})\s.*\(\s*(\S+)\s+([\d-]+)\s(.*)\s(\d+)\)(.*)$/U', $line, $matches);
|
||||
|
||||
if(isset($matches[1]) and $matches[1] != $revision)
|
||||
{
|
||||
@@ -310,7 +310,9 @@ class GitRepo
|
||||
if(strpos($fromRevision, '^') !== false)
|
||||
{
|
||||
$list = execCmd(escapeCmd("$this->client log -2 $toRevision --pretty=format:%H -- $path"), 'array');
|
||||
if(isset($list[1])) $fromRevision = $list[1];
|
||||
if(!isset($list[1])) return execCmd(escapeCmd("$this->client show HEAD"), 'array');
|
||||
|
||||
$fromRevision = $list[1];
|
||||
}
|
||||
$lines = execCmd(escapeCmd("$this->client diff $fromRevision $toRevision -- $path"), 'array');
|
||||
return $lines;
|
||||
|
||||
@@ -322,7 +322,9 @@ class Gogs
|
||||
if(strpos($fromRevision, '^') !== false)
|
||||
{
|
||||
$list = execCmd(escapeCmd("$this->client log -2 $toRevision --pretty=format:%H -- $path"), 'array');
|
||||
if(isset($list[1])) $fromRevision = $list[1];
|
||||
if(!isset($list[1])) return execCmd(escapeCmd("$this->client show HEAD"), 'array');
|
||||
|
||||
$fromRevision = $list[1];
|
||||
}
|
||||
$lines = execCmd(escapeCmd("$this->client diff $fromRevision $toRevision -- $path"), 'array');
|
||||
return $lines;
|
||||
|
||||
@@ -15,9 +15,7 @@ class scm
|
||||
$className = $repo->SCM;
|
||||
if($className == 'Git') $className = 'GitRepo';
|
||||
if(!class_exists($className)) require(strtolower($className) . '.class.php');
|
||||
$repoRoot = $repo->path;
|
||||
if('Subversion' == $className && !empty($repo->prefix)) $repoRoot = substr($repo->path, 0, strlen($repo->path) - strlen($repo->prefix));
|
||||
$this->engine = new $className($repo->client, $repoRoot, $repo->account, $repo->password, $repo->encoding, $repo);
|
||||
$this->engine = new $className($repo->client, $className == 'Gitlab' ? $repo->apiPath : $repo->path, $repo->account, $repo->password, $repo->encoding, $repo);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The trace class file 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 trace
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
class trace
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $trace = array();
|
||||
|
||||
protected $app;
|
||||
|
||||
protected $dao;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
global $app, $dao;
|
||||
$this->app = $app;
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求信息。
|
||||
* Get request info.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getRequestInfo()
|
||||
{
|
||||
$this->trace['request'] = array(
|
||||
'start' => date('Y-m-d H:i:s', (int)$this->app->startTime),
|
||||
'url' => $this->app->getURI(true),
|
||||
'protocol' => $this->app->server->server_protocol,
|
||||
'method' => $this->app->server->request_method,
|
||||
'timeUsed' => round(getTime() - $this->app->startTime, 4) * 1000,
|
||||
'memory' => round(memory_get_peak_usage() / 1024, 1),
|
||||
'querys' => count(dao::$querys),
|
||||
'caches' => count(dao::$cache),
|
||||
'files' => count(get_included_files()),
|
||||
'session' => session_id()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求加载的文件。
|
||||
* Get request files.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getRequestFiles()
|
||||
{
|
||||
$this->trace['files'] = get_included_files();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求的 SQL 语句。
|
||||
* Get request SQLs.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getRequestSqls()
|
||||
{
|
||||
$this->trace['sqlQuery'] = dao::$querys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求的 SQL profiles。
|
||||
* Get request SQL profiles.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSQLProfiles()
|
||||
{
|
||||
global $config;
|
||||
/* 达梦数据库不支持下面的语法,直接跳过。The */
|
||||
if($config->db->driver === 'dm') return;
|
||||
|
||||
$profiling = $this->dao->dbh->query('SHOW PROFILES')->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$this->trace['profiles'] = $profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求 Trace。
|
||||
* Generate request trace.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTrace()
|
||||
{
|
||||
$this->getRequestInfo();
|
||||
$this->getRequestFiles();
|
||||
$this->getRequestSqls();
|
||||
$this->getSQLProfiles();
|
||||
return $this->trace;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return json_encode($this->getTrace());
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ class xmind
|
||||
|
||||
foreach($sceneMaps as $key => $scene)
|
||||
{
|
||||
if($scene->parentID != $parentScene->sceneID + CHANGEVALUE) continue;
|
||||
if($scene->parentID != $parentScene->sceneID) continue;
|
||||
|
||||
$suffix = $config['scene'].':'.$scene->sceneID;
|
||||
$sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType'=>'scene'));
|
||||
@@ -315,4 +315,4 @@ class xmind
|
||||
{
|
||||
return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The config file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
function loadConfig()
|
||||
{
|
||||
global $app, $config;
|
||||
|
||||
if(!isset($config->zin)) $config->zin = new \stdClass();
|
||||
|
||||
$config->zin->lang = $app->getClientLang();
|
||||
$config->zin->wgVer = isset($config->wgVer) ? $config->wgVer : '1';
|
||||
$config->zin->wgVerMap = isset($config->wgVerMap) ? $config->wgVerMap : array();
|
||||
$config->zin->zuiPath = isset($config->zuiPath) ? $config->zuiPath : ($app->getWebRoot() . 'js/zui3/');
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The context class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'dataset.class.php';
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'flat.func.php';
|
||||
|
||||
class context extends \zin\utils\dataset
|
||||
{
|
||||
public function addImport()
|
||||
{
|
||||
return $this->addToList('import', func_get_args());
|
||||
}
|
||||
|
||||
public function getImportList()
|
||||
{
|
||||
return $this->getList('import');
|
||||
}
|
||||
|
||||
public function addCSS()
|
||||
{
|
||||
return $this->addToList('css', func_get_args());
|
||||
}
|
||||
|
||||
public function getCSS()
|
||||
{
|
||||
return trim(implode("\n", $this->getList('css')));
|
||||
}
|
||||
|
||||
public function addJS()
|
||||
{
|
||||
return $this->addToList('js', func_get_args());
|
||||
}
|
||||
|
||||
public function addJSVar($name, $value)
|
||||
{
|
||||
return $this->addToList('jsVar', h::createJsVarCode($name, $value));
|
||||
}
|
||||
|
||||
public function addWgWithEvents($wg)
|
||||
{
|
||||
$list = $this->getWgWithEventsList();
|
||||
if(in_array($wg, $list)) return $this;
|
||||
return $this->addToList('wgWithEvents', $wg);
|
||||
}
|
||||
|
||||
public function getWgWithEventsList()
|
||||
{
|
||||
return $this->getList('wgWithEvents');
|
||||
}
|
||||
|
||||
public function addJSCall()
|
||||
{
|
||||
$code = call_user_func_array('\zin\h::createJsCallCode', func_get_args());
|
||||
return $this->addToList('jsCall', $code);
|
||||
}
|
||||
|
||||
public function getEventsBindings()
|
||||
{
|
||||
$wgs = $this->getList('wgWithEvents');
|
||||
$codes = [];
|
||||
foreach($wgs as $wg)
|
||||
{
|
||||
if(!method_exists($wg, 'buildEvents')) continue;
|
||||
$code = $wg->buildEvents();
|
||||
if(!empty($code)) $codes[] = $code;
|
||||
}
|
||||
return $codes;
|
||||
}
|
||||
|
||||
public function getJS()
|
||||
{
|
||||
$js = trim(implode("\n", array_merge($this->getList('jsVar'), $this->getList('js'), $this->getEventsBindings(), $this->getList('jsCall'))));
|
||||
if(empty($js)) return '';
|
||||
|
||||
if(strpos($js, 'setTimeout') !== false) $js = 'function setTimeout(callback, time){return typeof window.registerTimer === "function" ? window.registerTimer(callback, time) : window.setTimeout(callback, time);}' . $js;
|
||||
if(strpos($js, 'setInterval') !== false) $js = 'function setInterval(callback, time){return typeof window.registerTimer === "function" ? window.registerTimer(callback, time, "interval") : window.setInterval(callback, time);}' . $js;
|
||||
|
||||
$methods = array('onPageUnmount', 'beforePageUpdate', 'afterPageUpdate', 'onPageRender');
|
||||
foreach($methods as $method)
|
||||
{
|
||||
if(strpos($js, $method) !== false) $js .= "if(typeof $method === 'function') window.$method = $method;";
|
||||
}
|
||||
return $js;
|
||||
}
|
||||
|
||||
public static $map = array();
|
||||
|
||||
public static function js(/* string ...$code */)
|
||||
{
|
||||
$context = static::current();
|
||||
call_user_func_array(array($context, 'addJS'), \zin\utils\flat(func_get_args()));
|
||||
}
|
||||
|
||||
public static function jsCall(/* string ...$code */)
|
||||
{
|
||||
$context = static::current();
|
||||
call_user_func_array(array($context, 'addJSCall'), func_get_args());
|
||||
}
|
||||
|
||||
|
||||
public static function jsVar($name, $value)
|
||||
{
|
||||
$context = static::current();
|
||||
$context->addJSVar($name, $value);
|
||||
}
|
||||
|
||||
public static function css(/* string ...$code */)
|
||||
{
|
||||
$context = static::current();
|
||||
call_user_func_array(array($context, 'addCSS'), \zin\utils\flat(func_get_args()));
|
||||
}
|
||||
|
||||
public static function import(/* string ...$files */)
|
||||
{
|
||||
$context = static::current();
|
||||
call_user_func_array(array($context, 'addImport'), func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current context.
|
||||
*
|
||||
* @access public
|
||||
* @return context
|
||||
*/
|
||||
public static function current(): context
|
||||
{
|
||||
if(empty(static::$map)) static::$map['current'] = new context();
|
||||
return static::$map['current'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create widget context.
|
||||
*
|
||||
* @access public
|
||||
* @param string $gid The widget gid.
|
||||
* @return context
|
||||
*/
|
||||
public static function create(string $gid): context
|
||||
{
|
||||
if(isset(static::$map[$gid])) return static::$map[$gid];
|
||||
$context = new context();
|
||||
static::$map[$gid] = $context;
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy widget context.
|
||||
*
|
||||
* @access public
|
||||
* @param string $gid The widget gid.
|
||||
* @return void
|
||||
*/
|
||||
public static function destroy(string $gid = null): void
|
||||
{
|
||||
if($gid === null) unset(static::$map['current']);
|
||||
elseif(isset(static::$map[$gid])) unset(static::$map[$gid]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The context function file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'context.class.php';
|
||||
|
||||
function js()
|
||||
{
|
||||
call_user_func_array('\zin\context::js', func_get_args());
|
||||
}
|
||||
|
||||
function jsCall()
|
||||
{
|
||||
call_user_func_array('\zin\context::jsCall', func_get_args());
|
||||
}
|
||||
|
||||
function jsVar()
|
||||
{
|
||||
call_user_func_array('\zin\context::jsVar', func_get_args());
|
||||
}
|
||||
|
||||
function css()
|
||||
{
|
||||
call_user_func_array('\zin\context::css', func_get_args());
|
||||
}
|
||||
|
||||
function import()
|
||||
{
|
||||
call_user_func_array('\zin\context::import', func_get_args());
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The data function file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
function setPageData($name, $value)
|
||||
{
|
||||
if(is_array($value) && empty($name))
|
||||
{
|
||||
foreach ($value as $key => $val) zin::setData($key, $val);
|
||||
return;
|
||||
}
|
||||
zin::setData($name, $value);
|
||||
}
|
||||
|
||||
function getPageData($name)
|
||||
{
|
||||
if(is_array($name))
|
||||
{
|
||||
$values = array();
|
||||
foreach($name as $propName)
|
||||
{
|
||||
$values[] = zin::getData($propName);
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
return zin::getData($name);
|
||||
}
|
||||
|
||||
function data()
|
||||
{
|
||||
$args = func_get_args();
|
||||
|
||||
if(count($args) >= 2) return setPageData($args[0], $args[1]);
|
||||
return getPageData($args[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set page data
|
||||
* @deprecated Use data($name, $value) insteadOf useData($name, $value)
|
||||
*/
|
||||
function useData($name, $value)
|
||||
{
|
||||
return setPageData($name, $value);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The directive class file of zin lib.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'zin.class.php';
|
||||
|
||||
class directive
|
||||
{
|
||||
public string $type;
|
||||
|
||||
public mixed $data;
|
||||
|
||||
public ?array $options;
|
||||
|
||||
public ?wg $parent = null;
|
||||
|
||||
/**
|
||||
* Construct a directive object
|
||||
* @param string $type
|
||||
* @param mixed $data
|
||||
* @param array $options
|
||||
* @access public
|
||||
*/
|
||||
public function __construct(string $type, mixed $data, ?array $options = null)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->data = $data;
|
||||
$this->options = $options;
|
||||
|
||||
zin::renderInGlobal($this);
|
||||
}
|
||||
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return array(
|
||||
'type' => $this->type,
|
||||
'data' => $this->data,
|
||||
'options' => $this->options
|
||||
);
|
||||
}
|
||||
|
||||
public static function is(mixed $item, ?string $type = null): bool
|
||||
{
|
||||
return $item instanceof directive && ($type === null || $item->type === $type);
|
||||
}
|
||||
}
|
||||
|
||||
function directive($type, $data, $options = null): directive
|
||||
{
|
||||
return new directive($type, $data, $options);
|
||||
}
|
||||
|
||||
function isDirective(mixed $item, ?string $type = null): bool
|
||||
{
|
||||
return directive::is($item, $type);
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The dom widget class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
use stdClass;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'deep.func.php';
|
||||
require_once __DIR__ . DS . 'selector.func.php';
|
||||
|
||||
class dom
|
||||
{
|
||||
/**
|
||||
* @var wg
|
||||
*/
|
||||
public $wg;
|
||||
|
||||
public $children = array();
|
||||
|
||||
public $selectors = null;
|
||||
|
||||
public $renderInner = false;
|
||||
|
||||
public $renderType;
|
||||
|
||||
public $dataGetters = null;
|
||||
|
||||
public $dataCommands;
|
||||
|
||||
public $buildList = null;
|
||||
|
||||
public $buildListInner = false;
|
||||
|
||||
/**
|
||||
* Construct the dom object.
|
||||
*
|
||||
* @param wg $wg
|
||||
* @param array $children
|
||||
* @param array|string|object $selectors
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($wg, $children, $selectors = null, $renderType = null, $dataCommands = null)
|
||||
{
|
||||
$this->wg = $wg;
|
||||
$this->renderType = $renderType;
|
||||
|
||||
$this->add($children);
|
||||
$this->addSelectors($selectors);
|
||||
$this->addDataCommands($dataCommands);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
return array(
|
||||
'gid' => $this->wg->gid,
|
||||
'type' => $this->wg->type(),
|
||||
'count' => count($this->children),
|
||||
'renderInner' => $this->renderInner,
|
||||
'renderType' => $this->renderType,
|
||||
'dataCommands' => $this->dataCommands,
|
||||
'selectors' => stringifyWgSelectors($this->selectors)
|
||||
);
|
||||
}
|
||||
|
||||
public function add($children)
|
||||
{
|
||||
if(empty($children)) return;
|
||||
|
||||
if(!is_array($children)) $children = [$children];
|
||||
foreach($children as $child)
|
||||
{
|
||||
if(is_array($child)) $this->add($child);
|
||||
else $this->children[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
public function addDataCommands($commands)
|
||||
{
|
||||
if(empty($commands)) return;
|
||||
|
||||
if(is_string($commands))
|
||||
{
|
||||
$commandList = explode(',', $commands);
|
||||
$commands = array();
|
||||
foreach($commandList as $command)
|
||||
{
|
||||
$parts = explode(':', $command, 2);
|
||||
$commands[$parts[0]] = count($parts) > 1 ? $parts[1] : $parts[0];
|
||||
}
|
||||
}
|
||||
|
||||
if($this->dataCommands === null) $this->dataCommands = array();
|
||||
$index = 0;
|
||||
foreach($commands as $key => $command)
|
||||
{
|
||||
$this->dataCommands[$index == $key ? $command : $key] = $command;
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
public function addSelectors($selectors)
|
||||
{
|
||||
if(empty($selectors)) return;
|
||||
|
||||
if($this->selectors === null) $this->selectors = array();
|
||||
$selectors = parseWgSelectors($selectors);
|
||||
foreach($selectors as $selector)
|
||||
{
|
||||
if(isset($selector->command) && !empty($selector->command)) $this->addDataCommands([$selector->tag => $selector->command]);
|
||||
else $this->selectors[] = $selector;
|
||||
}
|
||||
}
|
||||
|
||||
public function isMatch($selector)
|
||||
{
|
||||
return $this->wg->isMatch($selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the children dom list.
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
if($this->buildList !== null && $this->buildListInner === $this->renderInner) return $this->buildList;
|
||||
|
||||
if(empty($this->selectors) && !empty($this->dataCommands))
|
||||
{
|
||||
$this->buildList = array();
|
||||
return $this->buildList;
|
||||
}
|
||||
|
||||
$list = array();
|
||||
$children = $this->renderInner ? $this->wg->children() : $this->children;
|
||||
|
||||
if(empty($children)) return $list;
|
||||
|
||||
foreach($children as $child) $list[] = ($child instanceof wg) ? $child->buildDom() : $child;
|
||||
|
||||
if(!empty($this->selectors)) $list = static::filter($list, $this->selectors);
|
||||
|
||||
$this->buildList = $list;
|
||||
$this->buildListInner = $this->renderInner;
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
if($this->renderType === 'json') return $this->renderJson();
|
||||
if($this->renderType === 'list') return $this->renderList();
|
||||
return $this->renderHtml();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render dom to json object.
|
||||
*
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function renderJson(): object
|
||||
{
|
||||
$list = $this->build();
|
||||
$output = new stdClass();
|
||||
foreach($list as $name => $item)
|
||||
{
|
||||
$output->$name = static::renderItemToJson($item);
|
||||
}
|
||||
|
||||
if(!empty($this->dataCommands))
|
||||
{
|
||||
$data = array();
|
||||
foreach($this->dataCommands as $name => $command)
|
||||
{
|
||||
$data[$name] = data($command);
|
||||
}
|
||||
$output->data = $data;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render dom to html string.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function renderHtml(): string
|
||||
{
|
||||
$list = $this->build();
|
||||
if(empty($list)) return '';
|
||||
|
||||
$output = array();
|
||||
foreach($list as $item)
|
||||
{
|
||||
$result = static::renderItemToHtml($item);
|
||||
if(!is_string($result)) $result = json_encode($result);
|
||||
$output[] = $result;
|
||||
}
|
||||
return implode('', $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render dom to list by given selector.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function renderList(): array
|
||||
{
|
||||
$list = $this->build();
|
||||
$output = array();
|
||||
foreach($list as $name => $item)
|
||||
{
|
||||
if(is_array($item) && count($item) === 1) $item = $item[0];
|
||||
$renderType = $item instanceof dom ? $item->renderType : 'html';
|
||||
if(empty($renderType)) $renderType = 'html';
|
||||
$output[] = array('name' => $name, 'data' => static::renderDomItem($item, $renderType), 'type' => $renderType);
|
||||
}
|
||||
|
||||
if(!empty($this->dataCommands))
|
||||
{
|
||||
foreach($this->dataCommands as $name => $command)
|
||||
{
|
||||
$output[] = array('name' => $name, 'data' => data($command), 'type' => 'command');
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public static function renderDomItem($item, $defaultType = 'html')
|
||||
{
|
||||
if($item instanceof dom)
|
||||
{
|
||||
$renderType = $item->renderType;
|
||||
if(empty($renderType)) $renderType = $defaultType;
|
||||
if($renderType === 'json') return dom::renderItemToJson($item);
|
||||
return dom::renderItemToHtml($item->build());
|
||||
}
|
||||
|
||||
$renderType = $defaultType;
|
||||
if($renderType === 'json') return static::renderItemToJson($item);
|
||||
return static::renderItemToHtml($item);
|
||||
}
|
||||
|
||||
public static function renderItemToJson($item)
|
||||
{
|
||||
if($item === null || is_bool($item)) return null;
|
||||
|
||||
if(is_array($item))
|
||||
{
|
||||
$output = array();
|
||||
foreach($item as $subItem) $output[] = static::renderItemToJson($subItem);
|
||||
return $output;
|
||||
}
|
||||
|
||||
if($item instanceof dom)
|
||||
{
|
||||
$json = $item->wg->toJSON();
|
||||
if(!empty($item->dataGetters))
|
||||
{
|
||||
$output = array();
|
||||
$props = explode(',', $item->dataGetters);
|
||||
foreach($props as $prop)
|
||||
{
|
||||
$prop = trim($prop);
|
||||
if(empty($prop)) continue;
|
||||
|
||||
$parts = explode(':', $prop, 2);
|
||||
$name = $parts[0];
|
||||
$namePath = count($parts) > 1 ? $parts[1] : $parts[0];
|
||||
$output[$name] = \zin\utils\deepGet($json, $namePath);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
if($item instanceof wg) return dom::renderDomItem($item, 'json');
|
||||
if(is_string($item)) return $item;
|
||||
|
||||
if(is_object($item))
|
||||
{
|
||||
if(isDirective($item, 'html')) return $item->data;
|
||||
if(isDirective($item, 'text')) return htmlspecialchars($item->data);
|
||||
if(isset($item->html)) return $item->html;
|
||||
if(isset($item->text)) return htmlspecialchars($item->text);
|
||||
if(method_exists($item, 'render')) return $item->render();
|
||||
}
|
||||
|
||||
return strval($item);
|
||||
}
|
||||
|
||||
public static function renderItemToHtml($item)
|
||||
{
|
||||
if($item === null || is_bool($item)) return '';
|
||||
|
||||
if(is_array($item))
|
||||
{
|
||||
$output = array();
|
||||
foreach($item as $subItem) $output[] = static::renderItemToHtml($subItem);
|
||||
return implode('', $output);
|
||||
}
|
||||
|
||||
if($item instanceof dom) return dom::renderItemToHtml($item->build());
|
||||
if($item instanceof wg) return $item->render();
|
||||
if(is_string($item)) return $item;
|
||||
|
||||
if(is_object($item))
|
||||
{
|
||||
if(isDirective($item, 'html')) return $item->data;
|
||||
if(isDirective($item, 'text')) return htmlspecialchars($item->data);
|
||||
if(isset($item->html)) return $item->html;
|
||||
if(isset($item->text)) return htmlspecialchars($item->text);
|
||||
if(method_exists($item, 'render')) return $item->render();
|
||||
}
|
||||
|
||||
return strval($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the dom list with selector.
|
||||
*
|
||||
* @param array $list
|
||||
* @param object $selector
|
||||
* @param array $filteredList
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function filterList(&$list, $selector, &$filteredList)
|
||||
{
|
||||
if(empty($list) || empty($selector)) return [];
|
||||
|
||||
$results = array();
|
||||
foreach($list as $item)
|
||||
{
|
||||
if(!($item instanceof dom) || in_array($item->wg->gid, $filteredList)) continue;
|
||||
|
||||
if($item->wg->isMatch($selector))
|
||||
{
|
||||
$item->selector = $selector;
|
||||
$item->renderInner = isset($selector->inner) ? $selector->inner : false;
|
||||
$item->renderType = isset($selector->type) ? $selector->type : null;
|
||||
$item->dataGetters = isset($selector->data) ? $selector->data : null;
|
||||
|
||||
$filteredList[] = $item->wg->gid;
|
||||
$results[] = $item;
|
||||
}
|
||||
else
|
||||
{
|
||||
$children = $item->build();
|
||||
if(!empty($children))
|
||||
{
|
||||
$subResults = static::filterList($children, $selector, $filteredList);
|
||||
foreach($subResults as $subItem) $results[] = $subItem;
|
||||
}
|
||||
}
|
||||
if($selector->first && !empty($results)) break;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the dom list with selectors.
|
||||
*
|
||||
* @param array $domList
|
||||
* @param array $selectors
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function filter(&$domList, $selectors)
|
||||
{
|
||||
if(empty($selectors)) return $domList;
|
||||
|
||||
$list = array();
|
||||
$filteredList = array();
|
||||
foreach($selectors as $selector)
|
||||
{
|
||||
$results = static::filterList($domList, $selector, $filteredList);
|
||||
$list[$selector->name] = $results;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The html element class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'flat.func.php';
|
||||
require_once __DIR__ . DS . 'wg.class.php';
|
||||
require_once __DIR__ . DS . 'wg.func.php';
|
||||
|
||||
class h extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'tagName: string',
|
||||
'selfClose?: bool'
|
||||
);
|
||||
|
||||
public function getTagName(): string
|
||||
{
|
||||
return $this->props->get('tagName');
|
||||
}
|
||||
|
||||
public function isDomElement(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isSelfClose()
|
||||
{
|
||||
$selfClose = $this->props->get('selfClose');
|
||||
if($selfClose !== null) return $selfClose;
|
||||
|
||||
return in_array($this->getTagName(), static::$selfCloseTags);
|
||||
}
|
||||
|
||||
public function build(): array
|
||||
{
|
||||
if($this->isSelfClose()) return array($this->buildSelfCloseTag());
|
||||
|
||||
return array($this->buildTagBegin(), parent::build(), $this->buildTagEnd());
|
||||
}
|
||||
|
||||
public function toJsonData(): array
|
||||
{
|
||||
$data = parent::toJsonData();
|
||||
$data['type'] = 'h:' . $this->getTagName();
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return $this->getTagName();
|
||||
}
|
||||
|
||||
public function shortType(): string
|
||||
{
|
||||
return $this->getTagName();
|
||||
}
|
||||
|
||||
protected function getPropsStr(): string
|
||||
{
|
||||
$propStr = $this->props->toStr(array_keys(static::definedPropsList()));
|
||||
if($this->props->hasEvent() && empty($this->id()) && $this->getTagName() !== 'html') $propStr = "$propStr id='$this->gid'";
|
||||
return empty($propStr) ? '' : " $propStr";
|
||||
}
|
||||
|
||||
protected function buildSelfCloseTag(): string
|
||||
{
|
||||
$tagName = $this->getTagName();
|
||||
$propStr = $this->getPropsStr();
|
||||
return "<$tagName$propStr />";
|
||||
}
|
||||
|
||||
protected function buildTagBegin(): string
|
||||
{
|
||||
$tagName = $this->getTagName();
|
||||
$propStr = $this->getPropsStr();
|
||||
return "<$tagName$propStr>";
|
||||
}
|
||||
|
||||
protected function buildTagEnd(): string
|
||||
{
|
||||
$tagName = $this->getTagName();
|
||||
return "</$tagName>";
|
||||
}
|
||||
|
||||
public static function create(): h
|
||||
{
|
||||
$args = func_get_args();
|
||||
$tagName = array_shift($args);
|
||||
return new h(is_string($tagName) ? set('tagName', $tagName) : $tagName, $args);
|
||||
}
|
||||
|
||||
public static function __callStatic(string $tagName, array $args): h
|
||||
{
|
||||
return new h(set('tagName', $tagName), $args);
|
||||
}
|
||||
|
||||
public static function a(): h
|
||||
{
|
||||
$a = static::create('a', func_get_args());
|
||||
if($a->prop('target') === '_blank' && !$a->hasProp('rel')) $a->prop('rel', 'noopener noreferrer');
|
||||
return $a;
|
||||
}
|
||||
|
||||
public static function button()
|
||||
{
|
||||
return static::create('button', set('type', 'button'), func_get_args());
|
||||
}
|
||||
|
||||
public static function input()
|
||||
{
|
||||
return static::create('input', set('type', 'text'), func_get_args());
|
||||
}
|
||||
|
||||
public static function formHidden(/* $name, $value, ...$args */)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$name = array_shift($args);
|
||||
$value = array_shift($args);
|
||||
return static::create('input', set('type', 'hidden'), set::name($name), set::value($value), $args);
|
||||
}
|
||||
|
||||
public static function checkbox()
|
||||
{
|
||||
return static::create('input', set('type', 'checkbox'), func_get_args());
|
||||
}
|
||||
|
||||
public static function radio()
|
||||
{
|
||||
return static::create('input', set('type', 'radio'), func_get_args());
|
||||
}
|
||||
|
||||
public static function date()
|
||||
{
|
||||
return static::create('input', set('type', 'date'), func_get_args());
|
||||
}
|
||||
|
||||
public static function file()
|
||||
{
|
||||
return static::create('input', set('type', 'file'), func_get_args());
|
||||
}
|
||||
|
||||
public static function textarea(/* ...$args */)
|
||||
{
|
||||
$args = func_get_args();
|
||||
list($code, $args) = h::splitRawCode($args);
|
||||
return static::create('textarea', $code, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a html comment tag <!--...-->
|
||||
*
|
||||
* @access public
|
||||
* @param string $comment
|
||||
* @return directive
|
||||
*/
|
||||
public static function comment(string $comment): directive
|
||||
{
|
||||
return html("<!-- $comment -->");
|
||||
}
|
||||
|
||||
public static function importJs(/* $src, ...$args */)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$src = array_shift($args);
|
||||
return static::create('script', set('src', $src), $args);
|
||||
}
|
||||
|
||||
public static function importCss(/* $src, ...$args */)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$src = array_shift($args);
|
||||
return static::create('link', set('rel', 'stylesheet'), set('href', $src), $args);
|
||||
}
|
||||
|
||||
public static function import(/* $file, $type = null, ...$args */)
|
||||
{
|
||||
$args = array_merge(func_get_args(), array(null, null));
|
||||
$file = array_shift($args);
|
||||
$type = array_shift($args);
|
||||
|
||||
if(is_array($file))
|
||||
{
|
||||
$children = array();
|
||||
foreach($file as $file)
|
||||
{
|
||||
$children[] = static::import($file, $type);
|
||||
}
|
||||
return $children;
|
||||
}
|
||||
if($type === null) $type = pathinfo($file, PATHINFO_EXTENSION);
|
||||
if($type == 'js' || $type == 'cjs') return static::importJs($file, $args);
|
||||
if($type == 'css') return static::importCss($file, $args);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function css(/* ...$args */)
|
||||
{
|
||||
list($code, $args) = h::splitRawCode(func_get_args());
|
||||
if(empty($code)) return null;
|
||||
return static::create('style', html(implode("\n", $code)), $args);
|
||||
}
|
||||
|
||||
public static function globalJS(/* ...$args */)
|
||||
{
|
||||
list($code, $args) = h::splitRawCode(func_get_args());
|
||||
if(empty($code)) return null;
|
||||
return static::create('script', html(implode("\n", $code)), $args);
|
||||
}
|
||||
|
||||
public static function js(/* ...$args */)
|
||||
{
|
||||
|
||||
list($code, $args) = h::splitRawCode(func_get_args());
|
||||
if(empty($code)) return null;
|
||||
return static::create('script', html(h::createJsScopeCode($code)), $args);
|
||||
}
|
||||
|
||||
public static function jsVar(/* $name, $value, ...$args */)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$name = array_shift($args);
|
||||
$value = array_shift($args);
|
||||
return static::js(static::createJsVarCode($name, $value), $args);
|
||||
}
|
||||
|
||||
public static function jsCall(/* $funcName, ...$args */)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$funcName = array_shift($args);
|
||||
|
||||
$funcArgs = [];
|
||||
$directives = [];
|
||||
foreach($args as $arg)
|
||||
{
|
||||
if(isDirective($arg)) $directives[] = $arg;
|
||||
else $funcArgs[] = $arg;
|
||||
}
|
||||
$code = static::createJsCallCode($funcName, $funcArgs);
|
||||
return static::js($code, $directives);
|
||||
}
|
||||
|
||||
public static function createJsCallCode($func, $args)
|
||||
{
|
||||
foreach($args as $index => $arg)
|
||||
{
|
||||
$args[$index] = h::encodeJsonWithRawJs($arg, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
if($func[0] === '~')
|
||||
{
|
||||
$func = substr($func, 1);
|
||||
return "$(() => $func(" . implode(',', $args) . "));";
|
||||
}
|
||||
return $func . '(' . implode(',', $args) . ');';
|
||||
}
|
||||
|
||||
public static function createJsVarCode($name, $value)
|
||||
{
|
||||
$vars = is_string($name) ? array($name => $value) : $name;
|
||||
$jsCode = '';
|
||||
foreach($vars as $var => $val)
|
||||
{
|
||||
if(empty($var)) continue;
|
||||
|
||||
$val = h::encodeJsonWithRawJs($val);
|
||||
|
||||
if(str_starts_with($var, 'window.')) $jsCode .= "$var=" . $val . ';';
|
||||
elseif(str_starts_with($var, '+')) $jsCode .= 'let ' . substr($var, 1) . '=' . $val . ';';
|
||||
else $jsCode .= "const $var=" . $val . ';';
|
||||
}
|
||||
return $jsCode;
|
||||
}
|
||||
|
||||
public static function createJsScopeCode(string|array $codes): string
|
||||
{
|
||||
if(is_array($codes)) $codes = implode("\n", $codes);
|
||||
return ";(function(){\n$codes\n}());";
|
||||
}
|
||||
|
||||
public static function jsRaw(): string
|
||||
{
|
||||
return 'RAWJS<' . implode("\n", func_get_args()) . '>RAWJS';
|
||||
}
|
||||
|
||||
protected static function encodeJsonWithRawJs($data)
|
||||
{
|
||||
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
if(empty($json) && (is_array($data) || is_object($data))) return '[]';
|
||||
|
||||
$json = str_replace('"RAWJS<', '', str_replace('>RAWJS"', '', $json));
|
||||
return $json;
|
||||
}
|
||||
|
||||
protected static function splitRawCode($children)
|
||||
{
|
||||
$children = \zin\utils\flat($children);
|
||||
$code = [];
|
||||
$args = [];
|
||||
foreach($children as $key => $child)
|
||||
{
|
||||
if(is_string($child)) $code[] = $child;
|
||||
else $args[] = $child;
|
||||
}
|
||||
return [$code, $args];
|
||||
}
|
||||
|
||||
public static $selfCloseTags = array('area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr');
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The html helper methods file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'h.class.php';
|
||||
require_once __DIR__ . DS . 'item.class.php';
|
||||
require_once __DIR__ . DS . 'wg.func.php';
|
||||
require_once __DIR__ . DS . 'set.class.php';
|
||||
require_once __DIR__ . DS . 'to.class.php';
|
||||
require_once __DIR__ . DS . 'data.func.php';
|
||||
require_once __DIR__ . DS . 'on.class.php';
|
||||
|
||||
function h(): h {return call_user_func_array('\zin\h::create', func_get_args());}
|
||||
|
||||
function div(): h {return call_user_func_array('\zin\h::div', func_get_args());}
|
||||
function span(): h {return call_user_func_array('\zin\h::span', func_get_args());}
|
||||
function code(): h {return call_user_func_array('\zin\h::code', func_get_args());}
|
||||
function canvas(): h {return call_user_func_array('\zin\h::canvas', func_get_args());}
|
||||
function br(): h {return call_user_func_array('\zin\h::br', func_get_args());}
|
||||
function a(): h {return call_user_func_array('\zin\h::a', func_get_args());}
|
||||
function p(): h {return call_user_func_array('\zin\h::p', func_get_args());}
|
||||
function img(): h {return call_user_func_array('\zin\h::img', func_get_args());}
|
||||
function button(): h {return call_user_func_array('\zin\h::button', func_get_args());}
|
||||
function h1(): h {return call_user_func_array('\zin\h::h1', func_get_args());}
|
||||
function h2(): h {return call_user_func_array('\zin\h::h2', func_get_args());}
|
||||
function h3(): h {return call_user_func_array('\zin\h::h3', func_get_args());}
|
||||
function h4(): h {return call_user_func_array('\zin\h::h4', func_get_args());}
|
||||
function h5(): h {return call_user_func_array('\zin\h::h5', func_get_args());}
|
||||
function h6(): h {return call_user_func_array('\zin\h::h6', func_get_args());}
|
||||
function ul(): h {return call_user_func_array('\zin\h::ul', func_get_args());}
|
||||
function li(): h {return call_user_func_array('\zin\h::li', func_get_args());}
|
||||
function template(): h {return call_user_func_array('\zin\h::template', func_get_args());}
|
||||
function formHidden(): h {return call_user_func_array('\zin\h::formHidden', func_get_args());}
|
||||
function fieldset(): h {return call_user_func_array('\zin\h::fieldset', func_get_args());}
|
||||
function legend(): h {return call_user_func_array('\zin\h::legend', func_get_args());}
|
||||
|
||||
function jsRaw(): string {return call_user_func_array('\zin\h::jsRaw', func_get_args());}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The common item element class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'wg.class.php';
|
||||
require_once __DIR__ . DS . 'wg.func.php';
|
||||
|
||||
class item extends wg
|
||||
{
|
||||
public function build(): wg
|
||||
{
|
||||
if($this->parent instanceof wg && method_exists($this->parent, 'onBuildItem'))
|
||||
{
|
||||
return call_user_func(array($this->parent, 'onBuildItem'), $this);
|
||||
}
|
||||
return parent::build();
|
||||
}
|
||||
}
|
||||
|
||||
function item()
|
||||
{
|
||||
return new item(func_get_args());
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The block setter class file of zin lib.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'wg.func.php';
|
||||
|
||||
class on
|
||||
{
|
||||
public static function __callStatic($name, $args)
|
||||
{
|
||||
list($callback, $options) = array_merge($args, array(null));
|
||||
return on($name, $callback, $options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The props class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
use zin\utils\classlist;
|
||||
use zin\utils\style;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'dataset.class.php';
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'classlist.class.php';
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'style.class.php';
|
||||
|
||||
/**
|
||||
* Manage properties for html element and widgets
|
||||
*/
|
||||
class props extends \zin\utils\dataset
|
||||
{
|
||||
/**
|
||||
* Style property
|
||||
*
|
||||
* @access public
|
||||
* @var style
|
||||
*/
|
||||
public style $style;
|
||||
|
||||
/**
|
||||
* Class property
|
||||
*
|
||||
* @access public
|
||||
* @var classlist
|
||||
*/
|
||||
public classlist $class;
|
||||
|
||||
public static array $booleanAttrs = array('allowfullscreen', 'async', 'autofocus', 'autoplay', 'checked', 'controls', 'default', 'defer', 'disabled', 'formnovalidate', 'inert', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nomodule', 'novalidate', 'open', 'playsinline', 'readonly', 'required', 'reversed', 'selected');
|
||||
|
||||
/**
|
||||
* Create properties instance
|
||||
*
|
||||
* @access public
|
||||
* @param array $props - Properties list array
|
||||
*/
|
||||
public function __construct(array $props = array())
|
||||
{
|
||||
$this->style = new style();
|
||||
$this->class = new classlist();
|
||||
|
||||
parent::__construct($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for sub class to modify value on setting it
|
||||
*
|
||||
* @access public
|
||||
* @param string $prop - Property name or properties list
|
||||
* @param mixed $value - Property value
|
||||
*/
|
||||
protected function setVal(string $prop, mixed $value): props
|
||||
{
|
||||
if($prop === 'class' || $prop === '.') $this->class->set($value);
|
||||
elseif($prop === 'style' || $prop === '~') $this->style->set($value);
|
||||
elseif(str_starts_with($prop, '~')) $this->style->set(substr($prop, 1), $value);
|
||||
elseif($prop === '--') $this->style->cssVar($value);
|
||||
elseif(str_starts_with($prop, '--')) $this->style->cssVar(substr($prop, 2), $value);
|
||||
elseif($prop === '!') $this->hx($value);
|
||||
elseif(str_starts_with($prop, '!')) $this->hx(substr($prop, 1), $value);
|
||||
elseif(str_starts_with($prop, ':')) $this->set('data-' . substr($prop, 1), $value);
|
||||
elseif($prop === '@') $this->bindEvent($value);
|
||||
elseif(str_starts_with($prop, '@')) $this->bindEvent(substr($prop, 1), $value);
|
||||
else parent::setVal($prop, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getVal(string $prop): mixed
|
||||
{
|
||||
if($prop === 'class' || $prop === '.')
|
||||
{
|
||||
if(!$this->class->count()) return null;
|
||||
return $this->class->toStr();
|
||||
}
|
||||
if($prop === 'style' || $prop === '~')
|
||||
{
|
||||
if(!$this->style->count(true)) return null;
|
||||
return $this->style->toStr();
|
||||
}
|
||||
return parent::getVal($prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|string[] $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function reset(array|string $name, mixed $value = null)
|
||||
{
|
||||
if(is_array($name))
|
||||
{
|
||||
foreach($name as $n) $this->reset($n);
|
||||
return;
|
||||
}
|
||||
if($name === 'class') return $this->class->clear();
|
||||
if($name === 'style') return $this->style->clear();
|
||||
|
||||
$this->remove($name);
|
||||
if($value) $this->setVal($name, $value);
|
||||
}
|
||||
|
||||
public function bindEvent($name, $callback = null)
|
||||
{
|
||||
if(is_array($name))
|
||||
{
|
||||
foreach($name as $key => $value) $this->bindEvent($key, $value);
|
||||
return;
|
||||
}
|
||||
|
||||
$events = parent::getVal("@$name") ?? [];
|
||||
if(is_array($callback)) $events = array_merge($events, $callback);
|
||||
else $events[] = $callback;
|
||||
|
||||
parent::setVal("@$name", $events);
|
||||
}
|
||||
|
||||
public function events(): array
|
||||
{
|
||||
$events = array();
|
||||
foreach($this->data as $name => $value)
|
||||
{
|
||||
if(str_starts_with($name, '@')) $events[substr($name, 1)] = $value;
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
public function hasEvent(): bool
|
||||
{
|
||||
foreach($this->data as $name => $value)
|
||||
{
|
||||
if(str_starts_with($name, '@')) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hx(array|string $name, ?string $value = null)
|
||||
{
|
||||
if(is_array($name))
|
||||
{
|
||||
foreach($name as $key => $val) $this->set("hx-$key", $val);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set("hx-$name", $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert props to html string
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* // Properties data map:
|
||||
* $map = array(
|
||||
* 'id' => 'sayHelloBtn',
|
||||
* 'data-title' => 'Say "Hello"!',
|
||||
* 'data-content' => null,
|
||||
* 'data-show' => true,
|
||||
* );
|
||||
* // Output string: id="sayHelloBtn" data-title="Say "Hello"!" data-show="true"
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function toStr(array|string $skipProps = array()): string
|
||||
{
|
||||
if(is_string($skipProps)) $skipProps = explode(',', $skipProps);
|
||||
|
||||
$pairs = array();
|
||||
|
||||
if($this->class->count()) $pairs[] = 'class="' . $this->class->toStr() . '"';
|
||||
if($this->style->count(true)) $pairs[] = 'style="' . $this->style->toStr() . '"';
|
||||
|
||||
foreach($this->data as $name => $value)
|
||||
{
|
||||
/* Handle boolean attributes */
|
||||
if(in_array($name, static::$booleanAttrs)) $value = $value ? true : null;
|
||||
|
||||
/* Skip any null value or events setting */
|
||||
if($value === null || in_array($name, $skipProps) || $name[0] === '@') continue;
|
||||
|
||||
/* Convert non-string to json */
|
||||
if($value === true && !str_starts_with($name, 'data-'))
|
||||
{
|
||||
$pairs[] = $name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!is_string($value)) $value = json_encode($value);
|
||||
|
||||
$pairs[] = $name . '="' . htmlspecialchars($value) . '"';
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' ', $pairs);
|
||||
}
|
||||
|
||||
public function toJSON(bool $skipEvents = false): array
|
||||
{
|
||||
$data = $this->data;
|
||||
if(!empty($this->style->data)) $data['style'] = $this->style->data;
|
||||
if(!empty($this->class->toJSON())) $data['class'] = $this->class->toStr();
|
||||
|
||||
if($skipEvents)
|
||||
{
|
||||
foreach($data as $name => $value)
|
||||
{
|
||||
if(str_starts_with($name, '@')) unset($data[$name]);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function skip(array|string $skipProps = array(), bool $skipFalse = false): array
|
||||
{
|
||||
if(is_string($skipProps)) $skipProps = explode(',', $skipProps);
|
||||
|
||||
$data = $this->toJSON();
|
||||
foreach($data as $name => $value)
|
||||
{
|
||||
if($value === null || $name[0] === '@' || in_array($name, $skipProps)) unset($data[$name]);
|
||||
if($skipFalse && $value === false) unset($data[$name]);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function split(array|string $firstListProps = array()): array
|
||||
{
|
||||
if(is_string($firstListProps)) $firstListProps = explode(',', $firstListProps);
|
||||
|
||||
$data = $this->toJSON();
|
||||
$firstList = array();
|
||||
$restList = array();
|
||||
foreach($data as $name => $value)
|
||||
{
|
||||
if($value === null || $name[0] === '@') continue;
|
||||
if(in_array($name, $firstListProps)) $firstList[$name] = $value;
|
||||
else $restList[$name] = $value;
|
||||
}
|
||||
|
||||
return array($firstList, $restList);
|
||||
}
|
||||
|
||||
public function pick(array|string $pickProps = array()): array
|
||||
{
|
||||
if(is_string($pickProps)) $pickProps = explode(',', $pickProps);
|
||||
|
||||
$data = $this->toJSON();
|
||||
foreach($data as $name => $value)
|
||||
{
|
||||
if($value === null || !in_array($name, $pickProps)) unset($data[$name]);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a new instance
|
||||
*
|
||||
* @access public
|
||||
* @return props
|
||||
*/
|
||||
public function copy(): props
|
||||
{
|
||||
$props = new props($this->data);
|
||||
$props->style = clone $this->style;
|
||||
$props->class = clone $this->class;
|
||||
return $props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The rawContent class file of zin lib.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'wg.class.php';
|
||||
|
||||
class rawContent extends wg
|
||||
{
|
||||
protected function build(): directive
|
||||
{
|
||||
return h::comment('{{RAW_CONTENT}}');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The render function file of zin 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 Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'zin.class.php';
|
||||
|
||||
/**
|
||||
* 将视图页面声明的所有内容通过一个部件进行渲染,并输出 HTML。
|
||||
* Render page content with a widget to HTML.
|
||||
*
|
||||
* @access public
|
||||
* @param string $wgName
|
||||
* @param array $options
|
||||
* @return void
|
||||
*/
|
||||
function render(string $wgName = '', array $options = array())
|
||||
{
|
||||
/* 获取全局渲染部件实例和指令。 Get global render widgets and directives. */
|
||||
$globalItems = zin::getGlobalRenderList();
|
||||
|
||||
/* 决定部件名称,如果是 Ajax 请求则进行特殊处理。 Decide widget name, if is ajax request, then do special process. */
|
||||
if(empty($wgName))
|
||||
{
|
||||
$wgName = 'page';
|
||||
if(isAjaxRequest('modal')) $wgName = 'modalDialog';
|
||||
else if(isAjaxRequest() && !isAjaxRequest('zin')) $wgName = 'fragment';
|
||||
}
|
||||
|
||||
/* 判断是否渲染为完整页面。 Check if render in full page. */
|
||||
$isFullPage = str_starts_with($wgName, 'page');
|
||||
if($isFullPage) $globalItems[] = set::display(false);
|
||||
|
||||
/* 获取部件渲染选项。 Get widget display options. */
|
||||
if(empty($options) && isset($_SERVER['HTTP_X_ZIN_OPTIONS']) && !empty($_SERVER['HTTP_X_ZIN_OPTIONS']))
|
||||
{
|
||||
$setting = $_SERVER['HTTP_X_ZIN_OPTIONS'];
|
||||
$options = $setting[0] === '{' ? json_decode($setting, true) : array('selector' => $setting);
|
||||
}
|
||||
|
||||
/* 创建部件实例。 Create widget instance. */
|
||||
$wg = createWg($wgName, $globalItems);
|
||||
|
||||
/* 如果不是渲染一个完整页面,则使用 fragment 进行渲染。 If not render in full page, then render all items in a fragment. */
|
||||
if(!$isFullPage && $wgName !== 'fragment') $wg = fragment($wg);
|
||||
|
||||
/* 渲染并输出 HTML。 Render and display html. */
|
||||
$wg->display($options);
|
||||
|
||||
zin::$rendered = true;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The selector helpers file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
/**
|
||||
* Parse wg selector
|
||||
* @param string|object $selector
|
||||
* @return object|null
|
||||
*/
|
||||
function parseWgSelector(string|object $selector): ?object
|
||||
{
|
||||
if(is_object($selector)) return $selector;
|
||||
|
||||
$selector = trim($selector);
|
||||
$len = strlen($selector);
|
||||
|
||||
if($len < 1) return null;
|
||||
|
||||
$result = array(
|
||||
'class' => array(),
|
||||
'id' => null,
|
||||
'tag' => null,
|
||||
'inner' => false,
|
||||
'name' => null,
|
||||
'first' => false,
|
||||
'selector' => $selector
|
||||
);
|
||||
if(str_contains($selector, '/'))
|
||||
{
|
||||
$parts = explode('/', $selector, 2);
|
||||
$result['name'] = $parts[0];
|
||||
$selector = $parts[1];
|
||||
$len = strlen($selector);
|
||||
}
|
||||
$selector = str_replace('> *', '>*', $selector);
|
||||
if(substr($selector, strlen($selector) - 2) == '>*')
|
||||
{
|
||||
$result['inner'] = true;
|
||||
$selector = substr($selector, 0, strlen($selector) - 2);
|
||||
$len = strlen($selector);
|
||||
}
|
||||
|
||||
$type = 'tag';
|
||||
$current = '';
|
||||
$updateResult = function(&$result, $current, $type)
|
||||
{
|
||||
if(empty($current)) return;
|
||||
|
||||
if($type === 'class')
|
||||
{
|
||||
$result[$type][] = $current;
|
||||
}
|
||||
elseif($type === 'option')
|
||||
{
|
||||
$options = [];
|
||||
parse_str($current, $options);
|
||||
foreach($options as $key => $value) $result[$key] = empty($value) ? true : $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result[$type] = $current;
|
||||
}
|
||||
};
|
||||
|
||||
for($i = 0; $i < $len; $i++)
|
||||
{
|
||||
$c = $selector[$i];
|
||||
$t = '';
|
||||
|
||||
if($c === '#' & $type !== 'option')
|
||||
{
|
||||
$t = 'id';
|
||||
}
|
||||
elseif($c === '.' & $type !== 'option')
|
||||
{
|
||||
$t = 'class';
|
||||
}
|
||||
elseif($c === '(' && $type !== 'option' && str_ends_with($selector, ')'))
|
||||
{
|
||||
$command = substr($selector, $i + 1, -1);
|
||||
if(empty($command)) $command = $current;
|
||||
$result['command'] = $command;
|
||||
break;
|
||||
}
|
||||
elseif($c === ':')
|
||||
{
|
||||
$t = 'option';
|
||||
}
|
||||
|
||||
if(empty($t))
|
||||
{
|
||||
$current .= $c;
|
||||
}
|
||||
else
|
||||
{
|
||||
$updateResult($result, $current, $type);
|
||||
$current = '';
|
||||
$type = $t;
|
||||
}
|
||||
}
|
||||
$updateResult($result, $current, $type);
|
||||
|
||||
if(empty($result['class'])) $result['class'] = null;
|
||||
if(empty($result['name']))
|
||||
{
|
||||
if(!empty($result['id'])) $result['name'] = $result['id'];
|
||||
elseif(!empty($result['tag'])) $result['name'] = $result['tag'];
|
||||
else $result['name'] = $selector;
|
||||
}
|
||||
|
||||
return (object)$result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse wg selectors.
|
||||
* @param object|string|object[]|string[] $selectors
|
||||
* @return object[]
|
||||
*/
|
||||
function parseWgSelectors(object|string|array $selectors): array
|
||||
{
|
||||
if(is_object($selectors)) return array($selectors);
|
||||
if(is_string($selectors)) $selectors = explode(',', trim($selectors));
|
||||
$results = array();
|
||||
foreach($selectors as $selector)
|
||||
{
|
||||
$selector = parseWgSelector($selector);
|
||||
if(is_object($selector)) $results[] = $selector;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify wg selectors.
|
||||
* @param object|object[] $selector
|
||||
* @return string
|
||||
*/
|
||||
function stringifyWgSelectors(array|object|null $selector): string
|
||||
{
|
||||
if(empty($selector)) return '';
|
||||
if(is_array($selector))
|
||||
{
|
||||
$result = [];
|
||||
foreach($selector as $s) $result[] = stringifyWgSelectors($s);
|
||||
return implode(',', $result);
|
||||
}
|
||||
|
||||
$result = '';
|
||||
if(!empty($selector->name) && $selector->name !== $selector->selector) $result .= $selector->name . '/';
|
||||
if(!empty($selector->tag)) $result .= $selector->tag;
|
||||
if(!empty($selector->id)) $result .= '#' . $selector->id;
|
||||
if(!empty($selector->class)) $result .= '.' . implode('.', $selector->class);
|
||||
if(!empty($selector->first)) $result .= ':first';
|
||||
if($selector->inner) $result .= '>*';
|
||||
return $result;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The properties setter class file of zin lib.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'directive.class.php';
|
||||
|
||||
class set
|
||||
{
|
||||
public static function __callStatic($prop, $args)
|
||||
{
|
||||
if($prop === 'class' || strtolower($prop) === 'classname')
|
||||
{
|
||||
global $config;
|
||||
if($prop === 'class' && isset($config->debug) && $config->debug)
|
||||
{
|
||||
trigger_error("[ZIN] Use set::className() instead of set::class() to compatible with php 5.4.", E_USER_WARNING);
|
||||
}
|
||||
return directive('prop', array('class' => $args));
|
||||
}
|
||||
// compatible with zui prop className.
|
||||
else if($prop === '_className')
|
||||
{
|
||||
return directive('prop', array('className' => $args));
|
||||
}
|
||||
|
||||
$value = array_shift($args);
|
||||
if(is_object($value)) $value = (array)$value;
|
||||
return directive('prop', array($prop => $value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The block setter class file of zin lib.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'wg.func.php';
|
||||
|
||||
class to
|
||||
{
|
||||
public static function __callStatic($name, $args)
|
||||
{
|
||||
return to($name, $args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,756 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The base widget class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'props.class.php';
|
||||
require_once __DIR__ . DS . 'directive.class.php';
|
||||
require_once __DIR__ . DS . 'zin.class.php';
|
||||
require_once __DIR__ . DS . 'context.class.php';
|
||||
require_once __DIR__ . DS . 'selector.func.php';
|
||||
require_once __DIR__ . DS . 'dom.class.php';
|
||||
|
||||
class wg
|
||||
{
|
||||
/**
|
||||
* Define props for the element
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static array $defineProps = array();
|
||||
|
||||
protected static array $defaultProps = array();
|
||||
|
||||
protected static array $defineBlocks = array();
|
||||
|
||||
protected static array $wgToBlockMap = array();
|
||||
|
||||
protected static array $definedPropsMap = array();
|
||||
|
||||
protected static array $pageResources = array();
|
||||
|
||||
/**
|
||||
* The props of the element
|
||||
*
|
||||
* @access public
|
||||
* @var props
|
||||
*/
|
||||
public props $props;
|
||||
|
||||
public array $blocks = array();
|
||||
|
||||
public ?wg $parent = null;
|
||||
|
||||
public string $gid;
|
||||
|
||||
public bool $displayed = false;
|
||||
|
||||
protected array $renderOptions = array();
|
||||
|
||||
public function __construct(/* string|element|object|array|null ...$args */)
|
||||
{
|
||||
$this->props = new props();
|
||||
|
||||
$this->gid = 'zin_' . uniqid();
|
||||
$this->setDefaultProps(static::getDefaultProps());
|
||||
$this->add(func_get_args());
|
||||
$this->created();
|
||||
|
||||
zin::renderInGlobal($this);
|
||||
static::checkPageResources();
|
||||
|
||||
$this->checkErrors();
|
||||
}
|
||||
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toJSON();
|
||||
}
|
||||
|
||||
public function isDomElement(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the element is match any of the selectors
|
||||
* @param string|array|object $selectors
|
||||
*/
|
||||
public function isMatch(string|array|object $selectors): bool
|
||||
{
|
||||
$list = parseWgSelectors($selectors);
|
||||
foreach($list as $selector)
|
||||
{
|
||||
if(isset($selector->command)) continue;
|
||||
if(!empty($selector->id) && $this->id() !== $selector->id) continue;
|
||||
if(!empty($selector->tag) && $this->shortType() !== $selector->tag) continue;
|
||||
if(!empty($selector->class) && !$this->props->class->has($selector->class)) continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build dom object
|
||||
* @return dom
|
||||
*/
|
||||
public function buildDom(): dom
|
||||
{
|
||||
$before = $this->buildBefore();
|
||||
$children = $this->build();
|
||||
$after = $this->buildAfter();
|
||||
$options = $this->renderOptions;
|
||||
$selectors = (!empty($options) && isset($options['selector'])) ? $options['selector'] : null;
|
||||
|
||||
return new dom
|
||||
(
|
||||
$this,
|
||||
[$before, $children, $after],
|
||||
$selectors,
|
||||
(!empty($options) && isset($options['type'])) ? $options['type'] : 'html', // TODO: () may not work in lower php
|
||||
(!empty($options) && isset($options['data'])) ? $options['data'] : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render widget to html
|
||||
* @return string
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
$dom = $this->buildDom();
|
||||
$result = $dom->render();
|
||||
|
||||
return is_string($result) ? $result : json_encode($result);
|
||||
}
|
||||
|
||||
public function display(array $options = array()): wg
|
||||
{
|
||||
zin::disableGlobalRender();
|
||||
$this->renderOptions = $options;
|
||||
|
||||
$dom = $this->buildDom();
|
||||
$result = $dom->render();
|
||||
$context = context::current();
|
||||
$css = $context->getCSS();
|
||||
$js = $context->getJS();
|
||||
|
||||
global $app, $config;
|
||||
$zinDebug = null;
|
||||
if($config->debug && (!isAjaxRequest() || isAjaxRequest('zin')))
|
||||
{
|
||||
$zinDebug = data('zinDebug');
|
||||
if(is_array($zinDebug))
|
||||
{
|
||||
$zinDebug['basePath'] = $app->getBasePath();
|
||||
if(isset($app->zinErrors)) $zinDebug['errors'] = $app->zinErrors;
|
||||
}
|
||||
}
|
||||
|
||||
$rawContent = ob_get_contents();
|
||||
if(!is_string($rawContent)) $rawContent = '';
|
||||
ob_end_clean();
|
||||
|
||||
if(is_object($result))
|
||||
{
|
||||
if($zinDebug && isset($result['zinDebug'])) $result['zinDebug'] = $zinDebug;
|
||||
$result = json_encode($result);
|
||||
}
|
||||
elseif(is_array($result))
|
||||
{
|
||||
foreach($result as $index => $item)
|
||||
{
|
||||
if($item['name'] === 'zinDebug' && $zinDebug)
|
||||
{
|
||||
$result[$index]['data'] = $zinDebug;
|
||||
continue;
|
||||
}
|
||||
if(!isset($item['type']) || $item['type'] !== 'html') continue;
|
||||
|
||||
$data = $item['data'];
|
||||
$data = str_replace('/*{{ZIN_PAGE_CSS}}*/', $css, $data);
|
||||
$data = str_replace('/*{{ZIN_PAGE_JS}}*/', $js, $data);
|
||||
$data = str_replace('<!-- {{RAW_CONTENT}} -->', $rawContent, $data);
|
||||
$result[$index]['data'] = $data;
|
||||
}
|
||||
$result = json_encode($result);
|
||||
}
|
||||
else
|
||||
{
|
||||
if($zinDebug) $js .= h::createJsVarCode('window.zinDebug', $zinDebug);
|
||||
$result = str_replace('/*{{ZIN_PAGE_CSS}}*/', $css, $result);
|
||||
$result = str_replace('/*{{ZIN_PAGE_JS}}*/', $js, $result);
|
||||
$result = str_replace('<!-- {{RAW_CONTENT}} -->', $rawContent, $result);
|
||||
}
|
||||
|
||||
ob_start();
|
||||
echo $result;
|
||||
|
||||
$this->displayed = true;
|
||||
context::destroy();
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function created() {}
|
||||
|
||||
protected function buildBefore(): array
|
||||
{
|
||||
return $this->block('before');
|
||||
}
|
||||
|
||||
protected function buildAfter(): array
|
||||
{
|
||||
return $this->block('after');
|
||||
}
|
||||
|
||||
protected function build(): array|wg|directive
|
||||
{
|
||||
return $this->children();
|
||||
}
|
||||
|
||||
public function buildEvents(): ?string
|
||||
{
|
||||
$events = $this->props->events();
|
||||
if(empty($events)) return null;
|
||||
|
||||
$id = $this->id();
|
||||
$code = array($this->shortType() === 'html' ? 'const ele = document;' : 'const ele = document.getElementById("' . (empty($id) ? $this->gid : $id) . '");if(!ele)return;const $ele = $(ele); const events = new Set(($ele.attr("data-zin-events") || "").split(" ").filter(Boolean));');
|
||||
foreach($events as $event => $bindingList)
|
||||
{
|
||||
$code[] = "\$ele.on('$event.on.zin', function(e){";
|
||||
foreach($bindingList as $binding)
|
||||
{
|
||||
if(is_string($binding)) $binding = (object)array('handler' => $binding);
|
||||
$selector = isset($binding->selector) ? $binding->selector : null;
|
||||
$handler = isset($binding->handler) ? trim($binding->handler) : '';
|
||||
$stop = isset($binding->stop) ? $binding->stop : null;
|
||||
$prevent = isset($binding->prevent) ? $binding->prevent : null;
|
||||
$self = isset($binding->self) ? $binding->self : null;
|
||||
|
||||
$code[] = '(function(){';
|
||||
if($selector) $code[] = "const target = e.target.closest('$selector');if(!target) return;";
|
||||
else $code[] = "const target = ele;";
|
||||
if($self) $code[] = "if(ele !== e.target) return;";
|
||||
if($stop) $code[] = "e.stopPropagation();";
|
||||
if($prevent) $code[] = "e.preventDefault();";
|
||||
|
||||
if(preg_match('/^[$A-Z_][0-9A-Z_$\[\]."\']*$/i', $handler)) $code[] = "($handler).call(target,e);";
|
||||
else $code[] = $handler;
|
||||
|
||||
$code[] = '})();';
|
||||
}
|
||||
$code[] = "});events.add('$event');";
|
||||
}
|
||||
$code[] = '$ele.attr("data-zin-events", Array.from(events).join(" "));';
|
||||
return h::createJsScopeCode($code);
|
||||
}
|
||||
|
||||
|
||||
protected function onAddBlock(array|string|wg|directive $child, string $name)
|
||||
{
|
||||
return $child;
|
||||
}
|
||||
|
||||
protected function onAddChild(array|string|wg|directive $child)
|
||||
{
|
||||
return $child;
|
||||
}
|
||||
|
||||
protected function onSetProp(array|string $prop, mixed $value)
|
||||
{
|
||||
if($prop === 'id' && $value === '$GID') $value = $this->gid;
|
||||
if($prop[0] === '@')
|
||||
{
|
||||
$this->setDefaultProps(array('id' => $this->gid));
|
||||
context::current()->addWgWithEvents($this);
|
||||
}
|
||||
$this->props->set($prop, $value);
|
||||
}
|
||||
|
||||
protected function onGetProp(string $prop, mixed $defaultValue): mixed
|
||||
{
|
||||
return $this->props->get($prop, $defaultValue);
|
||||
}
|
||||
|
||||
public function add($item, string $blockName = 'children')
|
||||
{
|
||||
if($item === null || is_bool($item)) return $this;
|
||||
|
||||
if(is_array($item))
|
||||
{
|
||||
foreach($item as $child) $this->add($child, $blockName);
|
||||
return $this;
|
||||
}
|
||||
|
||||
zin::disableGlobalRender();
|
||||
|
||||
if($item instanceof wg) $this->addToBlock($blockName, $item);
|
||||
elseif(is_string($item)) $this->addToBlock($blockName, htmlentities($item));
|
||||
elseif(isDirective($item)) $this->directive($item, $blockName);
|
||||
else $this->addToBlock($blockName, htmlentities(strval($item)));
|
||||
|
||||
zin::enableGlobalRender();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addToBlock(array|string $name, array|string|null|wg|directive $child = null)
|
||||
{
|
||||
if(is_array($name))
|
||||
{
|
||||
foreach($name as $blockName => $blockChildren)
|
||||
{
|
||||
$this->addToBlock($blockName, $blockChildren);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(is_array($child))
|
||||
{
|
||||
foreach($child as $blockChild)
|
||||
{
|
||||
$this->addToBlock($name, $blockChild);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if($child instanceof wg && empty($child->parent)) $child->parent = &$this;
|
||||
|
||||
if($name === 'children' && $child instanceof wg)
|
||||
{
|
||||
$blockName = static::getBlockNameForWg($child);
|
||||
if($blockName !== null) $name = $blockName;
|
||||
}
|
||||
|
||||
$result = $name === 'children' ? $this->onAddChild($child) : $this->onAddBlock($child, $name);
|
||||
|
||||
if($result === false) return;
|
||||
if($result !== null && $result !== true) $child = $result;
|
||||
|
||||
if(isset($this->blocks[$name])) $this->blocks[$name][] = $child;
|
||||
else $this->blocks[$name] = array($child);
|
||||
}
|
||||
|
||||
public function children(): array
|
||||
{
|
||||
return $this->block('children');
|
||||
}
|
||||
|
||||
public function block(string $name): array
|
||||
{
|
||||
$list = array();
|
||||
if(isset($this->blocks[$name]))
|
||||
{
|
||||
$blocks = $this->blocks[$name];
|
||||
foreach($blocks as $block)
|
||||
{
|
||||
$isWg = $block instanceof wg && $block->shortType() === 'wg';
|
||||
$block = $isWg ? $block->children() : $block;
|
||||
if(is_array($block)) $list = array_merge($list, $block);
|
||||
else $list[] = $block;
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function hasBlock(string $name): bool
|
||||
{
|
||||
return isset($this->blocks[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply directive
|
||||
*/
|
||||
public function directive(directive &$directive, array|string $blockName)
|
||||
{
|
||||
$data = $directive->data;
|
||||
$type = $directive->type;
|
||||
$directive->parent = &$this;
|
||||
|
||||
if($type === 'prop')
|
||||
{
|
||||
$this->setProp($data);
|
||||
return;
|
||||
}
|
||||
if($type === 'class' || $type === 'style')
|
||||
{
|
||||
$this->setProp($type, $data);
|
||||
return;
|
||||
}
|
||||
if($type === 'cssVar')
|
||||
{
|
||||
$this->setProp('--', $data);
|
||||
return;
|
||||
}
|
||||
if($type === 'html')
|
||||
{
|
||||
$this->addToBlock($blockName, $directive);
|
||||
return;
|
||||
}
|
||||
if($type === 'text')
|
||||
{
|
||||
$this->addToBlock($blockName, htmlspecialchars($data));
|
||||
return;
|
||||
}
|
||||
if($type === 'block')
|
||||
{
|
||||
foreach($data as $blockName => $blockChildren)
|
||||
{
|
||||
$this->add($blockChildren, $blockName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function prop(array|string $name, mixed $defaultValue = null): mixed
|
||||
{
|
||||
if(is_array($name))
|
||||
{
|
||||
$values = array();
|
||||
foreach($name as $index => $propName)
|
||||
{
|
||||
$values[] = $this->onGetProp($propName, is_array($defaultValue) ? (isset($defaultValue[$propName]) ? $defaultValue[$propName] : $defaultValue[$index]) : $defaultValue);
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
return $this->onGetProp($name, $defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set property, an array can be passed to set multiple properties
|
||||
*
|
||||
* @access public
|
||||
* @param props|array|string $prop - Property name or properties list
|
||||
* @param mixed $value - Property value
|
||||
*/
|
||||
public function setProp(props|array|string $prop, mixed $value = null)
|
||||
{
|
||||
if($prop instanceof props) $prop = $prop->toJSON();
|
||||
|
||||
if(is_array($prop))
|
||||
{
|
||||
foreach($prop as $name => $value) $this->setProp($name, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
if(!is_string($prop) || empty($prop)) return $this;
|
||||
|
||||
if($prop[0] === '#')
|
||||
{
|
||||
$this->add($value, substr($prop, 1));
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->onSetProp($prop, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasProp(): bool
|
||||
{
|
||||
$names = func_get_args();
|
||||
if(empty($names)) return false;
|
||||
foreach($names as $name)
|
||||
{
|
||||
if(!$this->props->has($name)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setDefaultProps(array $props)
|
||||
{
|
||||
if(!is_array($props) || empty($props)) return;
|
||||
|
||||
foreach($props as $name => $value)
|
||||
{
|
||||
if($this->props->has($name)) continue;
|
||||
$this->setProp($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function getRestProps(): array
|
||||
{
|
||||
return $this->props->skip(array_keys(static::definedPropsList()));
|
||||
}
|
||||
|
||||
public function getDefinedProps(): array
|
||||
{
|
||||
return $this->props->pick(array_keys(static::definedPropsList()));
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
public function shortType(): string
|
||||
{
|
||||
$type = $this->type();
|
||||
$pos = strrpos($type, '\\');
|
||||
return $pos === false ? $type : substr($type, $pos + 1);
|
||||
}
|
||||
|
||||
public function id(): ?string
|
||||
{
|
||||
return $this->prop('id');
|
||||
}
|
||||
|
||||
public function toJSON(): array
|
||||
{
|
||||
$data = array();
|
||||
$data['gid'] = $this->gid;
|
||||
$data['props'] = $this->props->toJSON();
|
||||
|
||||
$data['type'] = $this->type();
|
||||
if(str_starts_with($data['type'], 'zin\\')) $data['type'] = substr($data['type'], 4);
|
||||
|
||||
$data['blocks'] = array();
|
||||
foreach($this->blocks as $key => $value)
|
||||
{
|
||||
foreach($value as $index => $child)
|
||||
{
|
||||
if($child instanceof wg || (is_object($child) && method_exists($child, 'toJSON')))
|
||||
{
|
||||
$value[$index] = $child->toJSON();
|
||||
}
|
||||
elseif(isDirective($child, 'html'))
|
||||
{
|
||||
$value[$index] = $child->data;
|
||||
}
|
||||
}
|
||||
if($key === 'children')
|
||||
{
|
||||
unset($data['blocks'][$key]);
|
||||
$data['children'] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$data['blocks'][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($data['blocks'])) unset($data['blocks']);
|
||||
|
||||
if(!empty($this->parent)) $data['parent'] = $this->parent->gid;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check errors in debug mode.
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function checkErrors()
|
||||
{
|
||||
global $config;
|
||||
if(!isset($config->debug) || !$config->debug) return;
|
||||
|
||||
$definedProps = static::definedPropsList();
|
||||
foreach($definedProps as $name => $definition)
|
||||
{
|
||||
if($this->hasProp($name)) continue;
|
||||
if(isset($definition['default']) && $definition['default'] !== null) continue;
|
||||
if(isset($definition['optional']) && $definition['optional']) continue;
|
||||
|
||||
trigger_error("[ZIN] The property \"$name: {$definition['type']}\" of widget \"{$this->type()}#$this->gid\" is required.", E_USER_ERROR);
|
||||
}
|
||||
|
||||
$wgErrors = $this->onCheckErrors();
|
||||
if(empty($wgErrors)) return;
|
||||
|
||||
foreach($wgErrors as $error)
|
||||
{
|
||||
if(is_array($error)) trigger_error("[ZIN] $error[0]", count($error) > 1 ? $error[1] : E_USER_WARNING);
|
||||
else trigger_error("[ZIN] $error", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The lifecycle method for checking errors in debug mode.
|
||||
*
|
||||
* @access protected
|
||||
* @return array|null
|
||||
*/
|
||||
protected function onCheckErrors(): array|null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getPageCSS(): string|false
|
||||
{
|
||||
return false; // No css
|
||||
}
|
||||
|
||||
public static function getPageJS(): string|false
|
||||
{
|
||||
return false; // No js
|
||||
}
|
||||
|
||||
protected static function checkPageResources()
|
||||
{
|
||||
$name = get_called_class();
|
||||
if(isset(static::$pageResources[$name])) return;
|
||||
|
||||
static::$pageResources[$name] = true;
|
||||
|
||||
$pageCSS = static::getPageCSS();
|
||||
$pageJS = static::getPageJS();
|
||||
|
||||
if(!empty($pageCSS)) context::css($pageCSS);
|
||||
if(!empty($pageJS)) context::js($pageJS);
|
||||
}
|
||||
|
||||
public static function wgBlockMap(): array
|
||||
{
|
||||
$wgName = get_called_class();
|
||||
if(!isset(wg::$wgToBlockMap[$wgName]))
|
||||
{
|
||||
$wgBlockMap = array();
|
||||
if(!empty(static::$defineBlocks))
|
||||
{
|
||||
foreach(static::$defineBlocks as $blockName => $setting)
|
||||
{
|
||||
if(!isset($setting['map'])) continue;
|
||||
$map = $setting['map'];
|
||||
if(is_string($map)) $map = explode(',', $map);
|
||||
foreach($map as $name) $wgBlockMap[$name] = $blockName;
|
||||
}
|
||||
}
|
||||
wg::$wgToBlockMap[$wgName] = $wgBlockMap;
|
||||
}
|
||||
return wg::$wgToBlockMap[$wgName];
|
||||
}
|
||||
|
||||
public static function getBlockNameForWg(wg|string $wg): ?string
|
||||
{
|
||||
$wgType = ($wg instanceof wg) ? $wg->type() : $wg;
|
||||
$wgBlockMap = static::wgBlockMap();
|
||||
if(str_starts_with($wgType, 'zin\\')) $wgType = substr($wgType, 4);
|
||||
return isset($wgBlockMap[$wgType]) ? $wgBlockMap[$wgType] : null;
|
||||
}
|
||||
|
||||
protected static function definedPropsList(?string $wgName = null): array
|
||||
{
|
||||
if($wgName === null) $wgName = get_called_class();
|
||||
|
||||
if(!isset(wg::$definedPropsMap[$wgName]) && $wgName === get_called_class())
|
||||
{
|
||||
wg::$definedPropsMap[$wgName] = static::parsePropsDefinition(static::$defineProps);
|
||||
}
|
||||
return wg::$definedPropsMap[$wgName];
|
||||
}
|
||||
|
||||
protected static function getDefaultProps(?string $wgName = null): array
|
||||
{
|
||||
$defaultProps = array();
|
||||
foreach(static::definedPropsList($wgName) as $name => $definition)
|
||||
{
|
||||
if(!isset($definition['default'])) continue;
|
||||
$defaultProps[$name] = $definition['default'];
|
||||
}
|
||||
return $defaultProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse props definition
|
||||
* @param $definition
|
||||
* @example
|
||||
*
|
||||
* $definition = array('name', 'desc:string', 'title?:string|element', 'icon?:string="star"');
|
||||
* $definition = array('name' => 'mixed', 'desc' => 'string', 'title' => array('type' => 'string|element', 'optional' => true), 'icon' => array('type' => 'string', 'default' => 'star', 'optional' => true))))
|
||||
*/
|
||||
private static function parsePropsDefinition(array $definition): array
|
||||
{
|
||||
$parentClass = get_parent_class(get_called_class());
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
$props = $parentClass ? call_user_func("$parentClass::definedPropsList") : array();
|
||||
|
||||
if($parentClass !== false && $definition === $parentClass::$defineProps)
|
||||
{
|
||||
if(!empty(static::$defaultProps) && static::$defaultProps !== $parentClass::$defaultProps)
|
||||
{
|
||||
foreach($props as $name => $value)
|
||||
{
|
||||
if(isset(static::$defaultProps[$name]))
|
||||
{
|
||||
$value['default'] = static::$defaultProps[$name];
|
||||
$props[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $props;
|
||||
}
|
||||
|
||||
foreach($definition as $name => $value)
|
||||
{
|
||||
$optional = false;
|
||||
$type = 'mixed';
|
||||
$default = (isset($props[$name]) && isset($props[$name]['default'])) ? $props[$name]['default'] : null;
|
||||
|
||||
if(is_int($name) && is_string($value))
|
||||
{
|
||||
$value = trim($value);
|
||||
if(!str_contains($value, ':'))
|
||||
{
|
||||
$name = $value;
|
||||
$value = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
list($name, $value) = explode(':', $value, 2);
|
||||
}
|
||||
$name = trim($name);
|
||||
if($name[strlen($name) - 1] === '?')
|
||||
{
|
||||
$name = substr($name, 0, strlen($name) - 1);
|
||||
$optional = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_array($value))
|
||||
{
|
||||
$type = isset($value['type']) ? $value['type'] : $type;
|
||||
$default = isset($value['default']) ? $value['default'] : $default;
|
||||
$optional = isset($value['optional'])? $value['optional']: $optional;
|
||||
}
|
||||
else if(is_string($value))
|
||||
{
|
||||
if(!str_contains($value, '='))
|
||||
{
|
||||
$type = $value;
|
||||
$default = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
list($type, $default) = explode('=', $value, 2);
|
||||
}
|
||||
$type = trim($type);
|
||||
|
||||
if(is_string($default)) $default = json_decode(trim($default));
|
||||
}
|
||||
|
||||
$props[$name] = array('type' => empty($type) ? 'mixed' : $type, 'default' => $default, 'optional' => $default !== null || $optional);
|
||||
}
|
||||
|
||||
if(static::$defaultProps && (!$parentClass || static::$defaultProps !== $parentClass::$defaultProps))
|
||||
{
|
||||
foreach(static::$defaultProps as $name => $value)
|
||||
{
|
||||
if(!isset($props[$name])) continue;
|
||||
$props[$name]['default'] = $value;
|
||||
}
|
||||
}
|
||||
return $props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The widget function file of zin module 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 sunhao<sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'flat.func.php';
|
||||
require_once __DIR__ . DS . 'props.class.php';
|
||||
require_once __DIR__ . DS . 'directive.class.php';
|
||||
require_once __DIR__ . DS . 'rawcontent.class.php';
|
||||
require_once __DIR__ . DS . 'wg.class.php';
|
||||
require_once __DIR__ . DS . 'context.func.php';
|
||||
|
||||
/**
|
||||
* Create an new widget.
|
||||
*
|
||||
* @return wg
|
||||
*/
|
||||
function wg(): wg
|
||||
{
|
||||
return new wg(func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set widget properties.
|
||||
*
|
||||
* @param string|array|props|null $name
|
||||
* @param mixed $value
|
||||
* @return directive|null
|
||||
*/
|
||||
function set(string|array|props|null $name, mixed $value = null): ?directive
|
||||
{
|
||||
if($name === null) return null;
|
||||
|
||||
$props = null;
|
||||
if($name instanceof props) $props = $name;
|
||||
else if(is_array($name)) $props = $name;
|
||||
else if(is_object($name)) $props = (array)$name;
|
||||
else if(is_string($name)) $props = array($name => $value);
|
||||
return $props ? directive('prop', $props) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set widget CSS class attribute.
|
||||
*
|
||||
* @param array|string|null ...$classList
|
||||
* @return directive
|
||||
*/
|
||||
function setClass(/* array|string|null ...$classList */): directive
|
||||
{
|
||||
return directive('class', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set widget style attribute.
|
||||
*
|
||||
* @return directive
|
||||
*/
|
||||
function setStyle(array|string $name, ?string $value = null): directive
|
||||
{
|
||||
return directive('style', is_array($name) ? $name : array($name => $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set widget CSS variable.
|
||||
*
|
||||
* @return directive
|
||||
*/
|
||||
function setCssVar(array|string $name, ?string $value = null): directive
|
||||
{
|
||||
return directive('cssVar', is_array($name) ? $name : array($name => $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set widget ID attribute.
|
||||
*
|
||||
* @return ?directive
|
||||
*/
|
||||
function setID(?string $id = null): directive
|
||||
{
|
||||
return set('id', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set widget element tag name.
|
||||
*
|
||||
* @return directive
|
||||
*/
|
||||
function setTag(string $id): directive
|
||||
{
|
||||
return set('tagName', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set widget data-* attribute.
|
||||
*
|
||||
* @param string|array $name
|
||||
* @param mixed $value
|
||||
* @return directive
|
||||
*/
|
||||
function setData(string|array $name, mixed $value = null): directive
|
||||
{
|
||||
$map = is_array($name) ? $name : array($name => $value);
|
||||
$attrs = array();
|
||||
foreach($map as $key => $value)
|
||||
{
|
||||
$name = "data-$key";
|
||||
if(is_bool($value)) $attrs[$name] = $value ? 'true' : 'false';
|
||||
else if(is_array($value)) $attrs[$name] = json_encode($value);
|
||||
else $attrs[$name] = $value;
|
||||
}
|
||||
return set($attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listener to widget element.
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool|string|array $handler
|
||||
* @param array $options
|
||||
*/
|
||||
function on(string $name, bool|string|array $handler, array|string|bool $options = null): directive
|
||||
{
|
||||
if(is_string($options) && is_string($handler))
|
||||
{
|
||||
$options = array('selector' => $handler, 'handler' => $options);
|
||||
}
|
||||
elseif(is_bool($options))
|
||||
{
|
||||
$options = array('capture' => $options, 'handler' => $handler);
|
||||
}
|
||||
elseif(is_array($options))
|
||||
{
|
||||
$options['handler'] = $handler;
|
||||
}
|
||||
else
|
||||
{
|
||||
$options = array('handler' => $handler);
|
||||
}
|
||||
if(str_contains($name, '__'))
|
||||
{
|
||||
list($name, $flags) = explode('__', $name);
|
||||
if(str_contains($flags, 'capture')) $options['capture'] = true;
|
||||
if(str_contains($flags, 'stop')) $options['stop'] = true;
|
||||
if(str_contains($flags, 'prevent')) $options['prevent'] = true;
|
||||
if(str_contains($flags, 'self')) $options['self'] = true;
|
||||
}
|
||||
return set("@$name", (object)$options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create html content.
|
||||
*
|
||||
* @param string ...$lines
|
||||
* @return directive
|
||||
*/
|
||||
function html(/* string ...$lines */): directive
|
||||
{
|
||||
return directive('html', implode("\n", \zin\utils\flat(func_get_args())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create text content.
|
||||
*
|
||||
* @param string ...$lines
|
||||
* @return directive
|
||||
*/
|
||||
function text(/* string ...$lines */): directive
|
||||
{
|
||||
return directive('text', implode("\n", \zin\utils\flat(func_get_args())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create block content.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed ...$wgs
|
||||
* @return directive
|
||||
*/
|
||||
function to(/* string $name, mixed ...$wgs */): directive
|
||||
{
|
||||
$args = func_get_args();
|
||||
$name = array_shift($args);
|
||||
$wg = new wg(count($args) > 1 ? $args : $args[0]);
|
||||
return directive('block', array($name => $wg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content for block "before".
|
||||
*
|
||||
* @param string $wgs
|
||||
* @return directive
|
||||
*/
|
||||
function before(/* mixed ...$wgs */): directive
|
||||
{
|
||||
return to('before', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content for block "after".
|
||||
*
|
||||
* @param string $wgs
|
||||
* @return directive
|
||||
*/
|
||||
function after(): directive
|
||||
{
|
||||
return to('after', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create widget contents inherited from the given widget.
|
||||
*
|
||||
* @param wg|array $item
|
||||
* @return array
|
||||
*/
|
||||
function inherit(wg|array $item): array
|
||||
{
|
||||
if(!($item instanceof wg)) $item = new wg($item);
|
||||
return array(set($item->props), directive('block', $item->blocks), $item->children());
|
||||
}
|
||||
|
||||
/**
|
||||
* Divorce widget from parent.
|
||||
*
|
||||
* @param wg|array $item
|
||||
* @return array
|
||||
*/
|
||||
function divorce(wg|array $item): wg|array
|
||||
{
|
||||
if($item instanceof wg)
|
||||
{
|
||||
$item->parent = null;
|
||||
}
|
||||
else if(is_array($item))
|
||||
{
|
||||
foreach($item as $i) divorce($i);
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given widget list has the given widget type.
|
||||
*
|
||||
* @param wg|array $items
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
function hasWgInList(wg|array $items, string $type): bool
|
||||
{
|
||||
if(!is_array($items)) $items = array($items);
|
||||
foreach($items as $item)
|
||||
{
|
||||
if($item instanceof wg && $item->type() == $type) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group widgets by type.
|
||||
*
|
||||
* @param wg|array $items
|
||||
* @param string $types
|
||||
* @return array
|
||||
*/
|
||||
function groupWgInList(wg|array $items, string|array $types): array
|
||||
{
|
||||
if(is_string($types)) $types = explode(',', $types);
|
||||
$typesMap = array();
|
||||
$restList = array();
|
||||
|
||||
foreach($types as $type) $typesMap[$type] = array();
|
||||
|
||||
foreach($items as $item)
|
||||
{
|
||||
if(!($item instanceof wg)) continue;
|
||||
|
||||
$type = $item->shortType();
|
||||
if(isset($typesMap[$type])) $typesMap[$type][] = $item;
|
||||
else $restList[] = $item;
|
||||
}
|
||||
|
||||
$groups = array();
|
||||
foreach($types as $index => $type) $groups[] = $typesMap[$type];
|
||||
$groups[] = $restList;
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create raw content placeholder.
|
||||
*
|
||||
* @return rawContent
|
||||
*/
|
||||
function rawContent(): rawContent
|
||||
{
|
||||
zin::$rawContentCalled = true;
|
||||
|
||||
return new rawContent();
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The zin class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'utils' . DS . 'deep.func.php';
|
||||
|
||||
class zin
|
||||
{
|
||||
public static array $globalRenderList = array();
|
||||
|
||||
public static bool $enabledGlobalRender = true;
|
||||
|
||||
public static array $data = array();
|
||||
|
||||
public static bool $rendered = false;
|
||||
|
||||
public static bool $rawContentCalled = false;
|
||||
|
||||
public static function getData(string $namePath, mixed $defaultValue = null): mixed
|
||||
{
|
||||
return \zin\utils\deepGet(static::$data, $namePath, $defaultValue);
|
||||
}
|
||||
|
||||
public static function setData(string $namePath, mixed $value)
|
||||
{
|
||||
\zin\utils\deepSet(static::$data, $namePath, $value);
|
||||
}
|
||||
|
||||
public static function enableGlobalRender()
|
||||
{
|
||||
static::$enabledGlobalRender = true;
|
||||
}
|
||||
|
||||
public static function disableGlobalRender()
|
||||
{
|
||||
static::$enabledGlobalRender = false;
|
||||
}
|
||||
|
||||
public static function renderInGlobal(): bool
|
||||
{
|
||||
if(!static::$enabledGlobalRender) return false;
|
||||
|
||||
static::$globalRenderList = array_merge(static::$globalRenderList, func_get_args());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getGlobalRenderList(bool $clear = true): array
|
||||
{
|
||||
$globalItems = array();
|
||||
|
||||
foreach(static::$globalRenderList as $item)
|
||||
{
|
||||
if(is_object($item))
|
||||
{
|
||||
if((isset($item->parent) && $item->parent) || ($item instanceof wg && $item->shortType() === 'wg'))
|
||||
continue;
|
||||
}
|
||||
$globalItems[] = $item;
|
||||
}
|
||||
|
||||
/* Clear globalRenderList. */
|
||||
if($clear) static::$globalRenderList = array();
|
||||
|
||||
return $globalItems;
|
||||
}
|
||||
}
|
||||
+1759
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The helper methods file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once __DIR__ . DS . 'config.php';
|
||||
|
||||
function setWgVer($ver, $names = null)
|
||||
{
|
||||
global $config;
|
||||
$zinConfig = $config->zin;
|
||||
|
||||
if(is_string($names)) $names = explode(',', $names);
|
||||
if(!is_array($names)) return;
|
||||
|
||||
foreach($names as $name)
|
||||
{
|
||||
$name = trim($name);
|
||||
if(!empty($name)) continue;
|
||||
|
||||
$zinConfig->wgVerMap[$name] = $ver;
|
||||
}
|
||||
}
|
||||
|
||||
function getWgVer($name)
|
||||
{
|
||||
global $config;
|
||||
|
||||
return isset($config->zin->verMap[$name]) ? $config->zin->verMap[$name] : $config->zin->wgVer;
|
||||
}
|
||||
|
||||
function createWg($name, $args): wg
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$wgVer = getWgVer($name);
|
||||
|
||||
include_once __DIR__ . DS . 'wg' . DS . $name . DS . "v$wgVer.php";
|
||||
|
||||
$wgName = "\\zin\\$name";
|
||||
|
||||
return class_exists($wgName) ? (new $wgName($args)) : $wgName($args);
|
||||
}
|
||||
|
||||
if(!function_exists('str_contains'))
|
||||
{
|
||||
/**
|
||||
* Determine if a string contains a given substring
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
* @return bool
|
||||
*/
|
||||
function str_contains($haystack, $needle)
|
||||
{
|
||||
return strpos($haystack, $needle) !== false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
function str_contains($haystack, $needle)
|
||||
{
|
||||
return \str_contains($haystack, $needle);
|
||||
}
|
||||
}
|
||||
|
||||
if(!function_exists('str_starts_with'))
|
||||
{
|
||||
/**
|
||||
* Checks if a string starts with a given substring
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
* @return bool
|
||||
*/
|
||||
function str_starts_with($haystack, $needle)
|
||||
{
|
||||
return strpos($haystack, $needle) === 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
function str_starts_with($haystack, $needle)
|
||||
{
|
||||
return \str_starts_with($haystack, $needle);
|
||||
}
|
||||
}
|
||||
|
||||
if(!function_exists('str_ends_with'))
|
||||
{
|
||||
/**
|
||||
* Checks if a string starts with a given substring.
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
* @return bool
|
||||
*/
|
||||
function str_ends_with($haystack, $needle)
|
||||
{
|
||||
$length = strlen($needle);
|
||||
if ($length === 0) return true;
|
||||
|
||||
$position = strpos($haystack, $needle);
|
||||
return $position !== false && $position === strlen($haystack) - $length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
function str_ends_with($haystack, $needle)
|
||||
{
|
||||
return \str_ends_with($haystack, $needle);
|
||||
}
|
||||
}
|
||||
|
||||
function uncamelize(string $camelCaps, string $separator = '-'): string
|
||||
{
|
||||
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
|
||||
}
|
||||
|
||||
function isHTML(string $string): bool
|
||||
{
|
||||
return $string !== strip_tags($string) ? true : false;
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The classlist file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin\utils;
|
||||
|
||||
/**
|
||||
* Manage classname list for html element and widgets
|
||||
*/
|
||||
class classlist
|
||||
{
|
||||
/**
|
||||
* Store classname list, key => value
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private array $list = array();
|
||||
|
||||
/**
|
||||
* Create classname instance
|
||||
*
|
||||
* @access public
|
||||
* @param array ...$list - A string or a class name list
|
||||
*/
|
||||
public function __construct(/* ...$list */)
|
||||
{
|
||||
$list = func_get_args();
|
||||
if(!empty($list)) $this->set($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert classnames to string
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toStr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create classname instance
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* // Set class names
|
||||
* $classlist = new classlist();
|
||||
* $classlist->set('btn primary rounded');
|
||||
*
|
||||
* // Set multiple classnames by string list
|
||||
* $classlist->set(array('btn', 'primary', 'rounded'));
|
||||
*
|
||||
* // Set multiple classnames by a mapped array
|
||||
* $classlist->set(array('btn' => true, 'primary' => true, 'rounded' => $isRounded));
|
||||
*
|
||||
* @access public
|
||||
* @param string|array|null $list - A string or a class name list
|
||||
* @param bool $reset
|
||||
* @return classlist
|
||||
*/
|
||||
public function set(string|array|null $list, bool $reset = false): classlist
|
||||
{
|
||||
if(is_string($list)) $list = explode(' ', $list);
|
||||
|
||||
if(is_array($list))
|
||||
{
|
||||
if($reset) $this->list = array();
|
||||
|
||||
$expectedKey = 0;
|
||||
foreach($list as $index => $value)
|
||||
{
|
||||
if(is_array($value))
|
||||
{
|
||||
$this->set($value);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If $index is expected numberic key and the $value is string, then use the $value as the name */
|
||||
if($expectedKey === $index && is_string($value))
|
||||
{
|
||||
$value = trim($value);
|
||||
if(strlen($value) > 0) $this->list[$value] = true;
|
||||
}
|
||||
/* If index is string, then set $index as name */
|
||||
else if(is_string($index))
|
||||
{
|
||||
$index = trim($index);
|
||||
if(strlen($index) === 0) continue;
|
||||
|
||||
$this->list[$index] = boolval($value);
|
||||
}
|
||||
$expectedKey++;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add classnames
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $classlist = new classlist();
|
||||
* $classlist->add('btn primary rounded');
|
||||
*
|
||||
* // Add multiple classnames by string list
|
||||
* $classlist->add('btn', 'primary', 'rounded');
|
||||
*
|
||||
* @access public
|
||||
* @param array ...$list - classname string joined by space or string array
|
||||
* @return classlist
|
||||
*/
|
||||
public function add(/* ...$list */)
|
||||
{
|
||||
return $this->set(func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove classnames
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $classlist = new classlist('btn primary rounded');
|
||||
* $classlist->remove('btn primary');
|
||||
*
|
||||
* // Add multiple classnames by string list
|
||||
* $classlist->remove('btn', 'primary');
|
||||
*
|
||||
* @access public
|
||||
* @param array|string $list - classname string joined by space or string array
|
||||
* @return classlist
|
||||
*/
|
||||
public function remove(array|string $list): classlist
|
||||
{
|
||||
if(is_string($list)) $list = explode(' ', $list);
|
||||
|
||||
foreach($list as $name)
|
||||
{
|
||||
if(!is_string($name)) continue;
|
||||
$name = trim($name);
|
||||
if(!strlen($name)) continue;
|
||||
|
||||
$this->list[$name] = false;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle classname
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $classlist = new classlist('btn');
|
||||
* $classlist->toggle('btn'); // class list is ""
|
||||
*
|
||||
* // Toggle class name by flag
|
||||
* $classlist->toggle('primary', true); // class list is "primary"
|
||||
*
|
||||
* @access public
|
||||
* @param string $name - classname string
|
||||
* @return classlist
|
||||
*/
|
||||
public function toggle(string $name, bool|null $toggle = null): classlist
|
||||
{
|
||||
$name = trim($name);
|
||||
if(strlen($name))
|
||||
{
|
||||
if($toggle === null) $toggle = !$this->has($name);
|
||||
$this->list[$name] = $toggle;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether has specific class name
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $classlist = new classlist('btn primary rounded');
|
||||
* echo $classlist->has('btn'); // Output true
|
||||
*
|
||||
* // Check multiple names
|
||||
* echo $classlist->has('btn primary'); // Output true
|
||||
*/
|
||||
public function has(array|string $list): bool
|
||||
{
|
||||
if(is_string($list)) $list = explode(' ', $list);
|
||||
|
||||
foreach($list as $name)
|
||||
{
|
||||
if(!is_string($name)) continue;
|
||||
$name = trim($name);
|
||||
if(!strlen($name)) continue;
|
||||
|
||||
if(!isset($this->list[$name]) || !$this->list[$name]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
$this->list = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert classnames to string
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function toStr(): string
|
||||
{
|
||||
$names = array();
|
||||
foreach($this->list as $name => $toggle)
|
||||
{
|
||||
if(!$toggle) continue;
|
||||
|
||||
$name = trim($name);
|
||||
if(!strlen($name)) continue;
|
||||
|
||||
$names[] = $name;
|
||||
}
|
||||
return implode(' ', $names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class names count
|
||||
*
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->list);
|
||||
}
|
||||
|
||||
public function toJSON(): array
|
||||
{
|
||||
return $this->list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The dataset class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin\utils;
|
||||
|
||||
/**
|
||||
* Manage dataset properties for html element and widgets
|
||||
*/
|
||||
class dataset
|
||||
{
|
||||
/**
|
||||
* Store dataset properties list in an array
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected array $data = array();
|
||||
|
||||
/**
|
||||
* Create an instance, the initialed data can be passed
|
||||
*
|
||||
* @access public
|
||||
* @param array $data - Properties list array
|
||||
*/
|
||||
public function __construct(array $data = array())
|
||||
{
|
||||
if($data !== null) $this->set($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert dataset to json string
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toStr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for sub class to modify value on setting it
|
||||
*
|
||||
* @access protected
|
||||
* @param string $prop - Property name or properties list
|
||||
* @param mixed $value - Property value
|
||||
* @return dataset
|
||||
*/
|
||||
protected function setVal(string $prop, mixed $value): dataset
|
||||
{
|
||||
$this->data[$prop] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getVal(string $prop): mixed
|
||||
{
|
||||
return isset($this->data[$prop]) ? $this->data[$prop] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties count
|
||||
*
|
||||
* @access public
|
||||
* @param bool $skipEmpty - Whether to skip to count empty value
|
||||
* @return int
|
||||
*/
|
||||
public function count($skipEmpty = false): int
|
||||
{
|
||||
if(!$skipEmpty) return count($this->data);
|
||||
|
||||
$count = 0;
|
||||
foreach($this->data as $value)
|
||||
{
|
||||
if($value !== null) $count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert dataset to json string
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function toStr(): string
|
||||
{
|
||||
return json_encode($this->toJSON());
|
||||
}
|
||||
|
||||
public function toJSON(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set property, an array can be passed to set multiple properties
|
||||
*
|
||||
* @access public
|
||||
* @param array|string $prop - Property name or properties list
|
||||
* @param mixed $value - Property value
|
||||
* @return dataset
|
||||
*/
|
||||
public function set(array|string $prop, mixed $value = null): dataset
|
||||
{
|
||||
if(is_array($prop))
|
||||
{
|
||||
foreach($prop as $name => $val) $this->set($name, $val);
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->setVal($prop, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get property value by name
|
||||
*
|
||||
* @access public
|
||||
* @param string $prop - Property name
|
||||
* @param mixed $defaultValue - Optional default value if actual value is null
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($prop, $defaultValue = null)
|
||||
{
|
||||
$val = $this->getVal($prop);
|
||||
return $val === null ? $defaultValue : $val;
|
||||
}
|
||||
|
||||
public function addToList($prop, $values)
|
||||
{
|
||||
if(!is_array($values)) $values = array($values);
|
||||
|
||||
$list = $this->getList($prop);
|
||||
$this->set($prop, array_merge($list, $values));
|
||||
}
|
||||
|
||||
public function getList($prop)
|
||||
{
|
||||
return $this->get($prop, array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete property by name
|
||||
*
|
||||
* @access public
|
||||
* @param string $prop - Property name
|
||||
* @return dataset
|
||||
*/
|
||||
public function remove($prop)
|
||||
{
|
||||
return $this->setVal($prop, null);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
$this->data = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether has specified property
|
||||
*
|
||||
* @access public
|
||||
* @param string $prop - Property name
|
||||
* @return boolean
|
||||
*/
|
||||
public function has($prop)
|
||||
{
|
||||
return $this->getVal($prop) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a new instance
|
||||
*
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
$className = get_called_class();
|
||||
return new $className($this->data);
|
||||
}
|
||||
|
||||
public function merge($data)
|
||||
{
|
||||
if(is_object($data) && isset($data->data)) return $this->set($data->data);
|
||||
return $this->set($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The debug helpers file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin\utils;
|
||||
|
||||
$logs = array();
|
||||
|
||||
function log($type, $msg = null, $file)
|
||||
{
|
||||
global $config, $logs;
|
||||
|
||||
if(!$config->debug) return;
|
||||
|
||||
if($msg === null)
|
||||
{
|
||||
$msg = $type;
|
||||
$type = 'i';
|
||||
}
|
||||
|
||||
if(is_array($msg))
|
||||
{
|
||||
$msgLines = array();
|
||||
foreach($msg as $m) $msgLines[] = strval($m);
|
||||
$msg = implode(' ', $msgLines);
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg = strval($msg);
|
||||
}
|
||||
|
||||
$logs[] = array(array('type' => strtolower($type), 'msg' => $msg));
|
||||
}
|
||||
|
||||
function logInfo($msg, $file = null) {log('i', $msg, $file);};
|
||||
function logWarn($msg, $file = null) {log('w', $msg, $file);};
|
||||
function logError($msg, $file = null) {log('e', $msg, $file);};
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin\utils;
|
||||
|
||||
function deepGet(object|array &$data, string $namePath, mixed $defaultValue = null): mixed
|
||||
{
|
||||
$names = explode('.', $namePath);
|
||||
foreach($names as $name)
|
||||
{
|
||||
if(is_object($data))
|
||||
{
|
||||
if(!isset($data->$name)) return $defaultValue;
|
||||
$data = &$data->$name;
|
||||
continue;
|
||||
}
|
||||
if(!is_array($data) || !isset($data[$name])) return $defaultValue;
|
||||
$data = &$data[$name];
|
||||
}
|
||||
return $data === null ? $defaultValue : $data;
|
||||
}
|
||||
|
||||
function deepSet(array &$data, string $namePath, mixed $value)
|
||||
{
|
||||
$names = explode('.', $namePath);
|
||||
$lastName = array_pop($names);
|
||||
if(!empty($names))
|
||||
{
|
||||
foreach($names as $name)
|
||||
{
|
||||
if(!is_array($data)) return;
|
||||
|
||||
if(!isset($data[$name])) $data[$name] = array();
|
||||
$data = &$data[$name];
|
||||
}
|
||||
}
|
||||
|
||||
$data[$lastName] = $value;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin\utils;
|
||||
|
||||
function flat(array $array, string $prefix = '')
|
||||
{
|
||||
$result = array();
|
||||
foreach($array as $key => $value)
|
||||
{
|
||||
if(is_array($value))
|
||||
{
|
||||
$result = array_merge($result, flat($value, "{$prefix}{$key}."));
|
||||
}
|
||||
else
|
||||
{
|
||||
$result[$prefix . $key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The style class file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @author Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin\utils;
|
||||
|
||||
require_once __DIR__ . DS . 'dataset.class.php';
|
||||
|
||||
/**
|
||||
* Manage style for html element and widgets
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* // Create a style object an convert to css string
|
||||
* $style = style::create(array('color' => 'red'));
|
||||
* echo $style(); // Output "color:red"
|
||||
*
|
||||
* // Above example same as:
|
||||
* echo style::css(array('color' => 'red'));
|
||||
*
|
||||
* // Modifier style
|
||||
* $style = style::create(array('color' => 'red'));
|
||||
* $style->set('background', 'green');
|
||||
*
|
||||
* // Modifier style with property name directly
|
||||
* $style->background = 'green';
|
||||
*
|
||||
* // Get style value
|
||||
* echo $style->get('background'); // Output "green"
|
||||
*
|
||||
* // Get style value with property name directly
|
||||
* echo $style->background; // Output "green"
|
||||
*
|
||||
* @todo @sunhao: Validate style properties on modifying
|
||||
*/
|
||||
class style extends dataset
|
||||
{
|
||||
/**
|
||||
* Format CSS variable name with prefix "--"
|
||||
*
|
||||
* @access public
|
||||
* @param string $name - CSS variable name
|
||||
* @return string
|
||||
*/
|
||||
public static function formatVarName(string $name): string
|
||||
{
|
||||
return \zin\str_starts_with($name, '--') ? $name : "--$name";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or get css variable, an array can be passed to set multiple variables
|
||||
* If only pass variable name, then the variable value will be returned
|
||||
* If no params passed, then return all setted variables with an array
|
||||
*
|
||||
* Notice: no need to prepend prefix '--' to variable name, the method will prepend it automatically, if prepended already, the method will skip to prepend smartly
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* // Create a style object and set
|
||||
* $style = new style();
|
||||
* $style->cssVar('text-size', '14px');
|
||||
*
|
||||
* // Set multiple variables
|
||||
* $style->cssVar(array('text-color' => 'yellow', 'background-image' => 'none'));
|
||||
*
|
||||
* // Get variable value
|
||||
* echo $style->cssVar('text-size'); // Output "14px"
|
||||
*
|
||||
* // Get all variables value
|
||||
* echo $style->cssVar();
|
||||
* // Output array('text-size' => '14px', 'color' => 'yellow', 'background' => 'none');
|
||||
*
|
||||
* // Remove variable by setting value with an empty string
|
||||
* $style->cssVar('text-color', '');
|
||||
*
|
||||
* @access public
|
||||
* @param array|string $name - Variable name or variables list
|
||||
* @param string|null $value - Property value
|
||||
* @return style|array|string
|
||||
*/
|
||||
public function cssVar(array|string $name = '', ?string $value = null): style|array|string
|
||||
{
|
||||
/* Support for setting multiple variables by an array */
|
||||
if(is_array($name))
|
||||
{
|
||||
foreach($name as $n => $value) $this->set(style::formatVarName($n), $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/* Return all setted variables without passed any params */
|
||||
if(empty($name))
|
||||
{
|
||||
$vars = array();
|
||||
foreach ($this->data as $prop => $value)
|
||||
{
|
||||
if(!str_starts_with($name, '--')) continue;
|
||||
$vars[substr($prop, 2)] = $value;
|
||||
}
|
||||
return $vars;
|
||||
}
|
||||
|
||||
$varName = style::formatVarName($name);
|
||||
|
||||
/* Return the specific variable value by name */
|
||||
if($value === null) return $this->get($varName);
|
||||
|
||||
/* Set the specific variable value and return style object self */
|
||||
$this->set($varName, $value === '' ? null : $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to string
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function toStr(): string
|
||||
{
|
||||
$pairs = array();
|
||||
|
||||
foreach($this->data as $prop => $value)
|
||||
{
|
||||
/* Skip any empty value */
|
||||
if($value === null || $value === '') continue;
|
||||
|
||||
$pairs[] = $prop . ': ' . strval($value) . ';';
|
||||
}
|
||||
|
||||
return implode(' ', $pairs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'btn' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'dropdown' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'checkbox' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'btngroup' . DS . 'v1.php';
|
||||
|
||||
class actionItem extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'name:string="action"',
|
||||
'type:string="item"',
|
||||
'outerTag:string="li"',
|
||||
'tagName:string="a"',
|
||||
'icon?:string',
|
||||
'text?:string',
|
||||
'textClass?: string',
|
||||
'url?:string',
|
||||
'target?:string',
|
||||
'active?:bool',
|
||||
'disabled?:bool',
|
||||
'trailingIcon?:string',
|
||||
'outerProps?:array',
|
||||
'outerClass?:string',
|
||||
'badge?:string|array|object',
|
||||
'props?:array',
|
||||
'dropdown?:array',
|
||||
'items?:array',
|
||||
'caret?:bool|string'
|
||||
);
|
||||
|
||||
protected function buildDividerItem()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function buildHeadingItem()
|
||||
{
|
||||
list($icon, $text, $trailingIcon, $textClass) = $this->prop(array('icon', 'text', 'trailingIcon', 'textClass'));
|
||||
|
||||
return h::div
|
||||
(
|
||||
set($this->props->skip(array_keys(actionItem::definedPropsList()))),
|
||||
set($this->prop('props')),
|
||||
$icon ? icon($icon) : null,
|
||||
empty($text) ? null : span($text, setClass('text', $textClass)),
|
||||
$this->children(),
|
||||
$trailingIcon ? icon($trailingIcon) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildDropdownItem()
|
||||
{
|
||||
list($dropdown, $items, $icon, $text, $trailingIcon, $active, $disabled, $badge, $props, $caret, $textClass) = $this->prop(array('dropdown', 'items', 'icon', 'text', 'trailingIcon', 'active', 'disabled', 'badge', 'props', 'caret', 'textClass'));
|
||||
|
||||
if(is_string($badge))
|
||||
{
|
||||
$badge = label($badge);
|
||||
}
|
||||
elseif(is_array($badge))
|
||||
{
|
||||
$badge = label(set($badge));
|
||||
}
|
||||
|
||||
$dropdown = new dropdown
|
||||
(
|
||||
set::items($items),
|
||||
set($dropdown),
|
||||
h::a(
|
||||
setClass(array('active' => $active, 'disabled' => $disabled)),
|
||||
set($this->getRestProps()),
|
||||
set($props),
|
||||
$icon ? icon($icon) : null,
|
||||
span($text, setClass('text', $textClass)),
|
||||
$badge,
|
||||
$this->children(),
|
||||
$trailingIcon ? icon($trailingIcon) : null,
|
||||
h::span(setClass(is_string($caret) ? "caret-$caret" : 'caret'))
|
||||
)
|
||||
);
|
||||
return $dropdown;
|
||||
}
|
||||
|
||||
protected function buildBtnItem()
|
||||
{
|
||||
return new btn($this->props->skip('tagName,type,name,outerTag,outerProps,props'), set($this->prop('props')),$this->children());
|
||||
}
|
||||
|
||||
protected function buildCheckboxItem()
|
||||
{
|
||||
return new checkbox($this->props->skip('tagName,type,name,outerTag,outerProps,props'), set($this->prop('props')),$this->children());
|
||||
}
|
||||
|
||||
protected function buildBtnGroupItem()
|
||||
{
|
||||
return new btnGroup($this->props->skip('tagName,type,name,outerTag,outerProps,props'), set($this->prop('props')),$this->children());
|
||||
}
|
||||
|
||||
protected function buildItem()
|
||||
{
|
||||
$type = $this->prop('type');
|
||||
$methodName = "build{$type}Item";
|
||||
if(method_exists($this, $methodName)) return $this->$methodName();
|
||||
|
||||
list($tagName, $icon, $text, $trailingIcon, $url, $target, $active, $disabled, $badge, $textClass) = $this->prop(array('tagName', 'icon', 'text', 'trailingIcon', 'url', 'target', 'active', 'disabled', 'badge', 'textClass'));
|
||||
|
||||
if(is_string($badge)) $badge = label($badge);
|
||||
else if(is_array($badge)) $badge = label(set($badge));
|
||||
|
||||
return h::create
|
||||
(
|
||||
$tagName,
|
||||
set($tagName === 'a' ? array('href' => $url, 'target' => $target) : array('data-url' => $url, 'data-target' => $target)),
|
||||
setClass(array('active' => $active, 'disabled' => $disabled)),
|
||||
set($this->getRestProps()),
|
||||
set($this->prop('props')),
|
||||
$icon ? icon($icon) : null,
|
||||
span($text, setClass('text', $textClass)),
|
||||
$badge,
|
||||
$this->children(),
|
||||
$trailingIcon ? icon($trailingIcon) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
list($name, $type, $outerTag, $outerProps, $outerClass) = $this->prop(array('name', 'type', 'outerTag', 'outerProps', 'outerClass'));
|
||||
|
||||
return h::create
|
||||
(
|
||||
$outerTag,
|
||||
setClass(($type !== 'item' && $type !== 'divider') ? 'nav-item' : '', "$name-$type", $outerClass),
|
||||
set($outerProps),
|
||||
$this->buildItem()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class avatar extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'className?:string',
|
||||
'style?:array',
|
||||
'size?:int=32',
|
||||
'circle?:bool=true',
|
||||
'rounded?:string|int',
|
||||
'background?:string',
|
||||
'foreColor?:string',
|
||||
'text?:string',
|
||||
'code?:string',
|
||||
'maxTextLength?:int=2',
|
||||
'hueDistance?:int=43',
|
||||
'saturation?:int=0.4',
|
||||
'lightness?:int=0.6',
|
||||
'src?:string'
|
||||
);
|
||||
|
||||
private $textLen = 0;
|
||||
private $displayTextLen = 0;
|
||||
private $sizeMap = array('xs' => 20, 'sm' => 24, 'lg' => 48, 'xl' => 80);
|
||||
private $actualSize = 32;
|
||||
private $finalClass = array('avatar');
|
||||
private $finalStyle;
|
||||
|
||||
protected function onAddChild($child)
|
||||
{
|
||||
if(is_string($child) && !$this->props->has('text'))
|
||||
{
|
||||
$this->setProp('text', $child);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $child;
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
/* Attach classes. */
|
||||
$this->finalClass[] = $this->prop('className');
|
||||
|
||||
/* Init style. */
|
||||
$this->finalStyle = new stdClass();
|
||||
$this->finalStyle->background = $this->prop('background');
|
||||
$this->finalStyle->color = $this->prop('foreColor');
|
||||
|
||||
foreach($this->props->style->toJSON() as $attr => $val) $this->finalStyle->{$attr} = $val;
|
||||
|
||||
/* Init avatar size. */
|
||||
$this->initSize();
|
||||
/* Init avatar shape. */
|
||||
$this->initShape();
|
||||
|
||||
$content = $this->getContent();
|
||||
$finalStyle = json_decode(json_encode($this->finalStyle), true);
|
||||
return h::div
|
||||
(
|
||||
setClass($this->finalClass),
|
||||
setStyle($finalStyle),
|
||||
set($this->getRestProps()),
|
||||
$content,
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
|
||||
private function initSize()
|
||||
{
|
||||
$size = $this->prop('size');
|
||||
$this->actualSize = $size;
|
||||
|
||||
if(!$size) return;
|
||||
|
||||
if(is_numeric($size))
|
||||
{
|
||||
$fontSize = intval($size/2) > 12 ? intval($size/2) : 12;
|
||||
$this->finalStyle->width = "{$size}px";
|
||||
$this->finalStyle->height = "{$size}px";
|
||||
$this->finalStyle->{'font-size'} = "{$fontSize}px";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->finalClass[] = "size-{$size}";
|
||||
$this->actualSize = isset($this->sizeMap[$size]) ? $this->sizeMap[$size] : 20;
|
||||
}
|
||||
|
||||
private function initShape()
|
||||
{
|
||||
$circle = $this->prop('circle');
|
||||
$rounded = $this->prop('rounded');
|
||||
|
||||
/* Set circle. */
|
||||
if($circle)
|
||||
{
|
||||
$this->finalClass[] = 'rounded-full';
|
||||
}
|
||||
else if($rounded)
|
||||
{
|
||||
if(is_numeric($rounded)) $this->finalStyle->{'border-radius'} = "{$rounded}px";
|
||||
else $this->finalClass[] = "rounded-{$rounded}";
|
||||
}
|
||||
}
|
||||
|
||||
private function getAvatarText()
|
||||
{
|
||||
$maxTextLen = intval($this->prop('maxTextLength'));
|
||||
$text = strtoupper($this->prop('text', ''));
|
||||
$this->textLen = strlen($text);
|
||||
|
||||
if(preg_match('/[\x{4e00}-\x{9fa5}\s]+$/u', $text))
|
||||
{
|
||||
$this->textLen = mb_strlen($text);
|
||||
$text = $this->textLen <= $maxTextLen ? $text : mb_substr($text, $this->textLen - $maxTextLen);
|
||||
$this->displayTextLen = mb_strlen($text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
if(preg_match('/[A-Za-z\d\s]+$/', $text))
|
||||
{
|
||||
$this->displayTextLen = 1;
|
||||
return substr($text, 0, 1);
|
||||
}
|
||||
|
||||
return $this->textLen <= $maxTextLen ? $text : substr($text, 0, $maxTextLen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HSL values to RGB value.
|
||||
*
|
||||
* @param int $h
|
||||
* @param int $s
|
||||
* @param int $l
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function hslToRgb($h, $s, $l)
|
||||
{
|
||||
$h = ($h % 360) / 360;
|
||||
$s = ($s > 0 ? $s : 0);
|
||||
$s = ($s > 255) ? 255 : $s;
|
||||
$l = ($l > 0 ? $l : 0);
|
||||
$l = ($l > 255) ? 255 : $l;
|
||||
|
||||
$m2 = ($l <= 0.5) ? ($l * ($s + 1)) : ($l + $s - $l * $s);
|
||||
$m1 = $l * 2 - $m2;
|
||||
|
||||
$hueFn = function($val, $m1, $m2)
|
||||
{
|
||||
$val = $val < 0 ? $val + 1 : ($val > 1 ? $val - 1 : $val);
|
||||
|
||||
if($val * 6 < 1) return $m1 + ($m2 - $m1) * $val * 6;
|
||||
elseif($val * 2 < 1) return $m2;
|
||||
elseif($val * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $val) * 6;
|
||||
|
||||
return $m1;
|
||||
};
|
||||
|
||||
return array(
|
||||
'r' => $hueFn($h + 1/3, $m1, $m2) * 255,
|
||||
'g' => $hueFn($h, $m1, $m2) * 255,
|
||||
'b' => $hueFn($h - 1/3, $m1, $m2) * 255
|
||||
);
|
||||
}
|
||||
|
||||
private function hex2Rgb($hex)
|
||||
{
|
||||
if(!str_starts_with($hex, '#') || !preg_match('/#[0-9A-F]{3,6}$/', $hex)) throw new \Exception('incorrect data format');
|
||||
|
||||
$r = 0;
|
||||
$g = 0;
|
||||
$b = 0;
|
||||
if(strlen($hex) == 4) list($r, $g, $b) = sscanf($hex, "#%01x%01x%01x");
|
||||
elseif(strlen($hex) == 7) list($r, $g, $b) = sscanf($hex, "#%02x%02x%02x");
|
||||
else throw new \Exception('incorrect RGB value');
|
||||
|
||||
return array(
|
||||
'r' => $r,
|
||||
'g' => $g,
|
||||
'b' => $b
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get contrast color.
|
||||
*
|
||||
* @param array|string $rgb
|
||||
* @param string $theme dark|light
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function contrastColor($rgb, $themeDark = null, $themeLight = null)
|
||||
{
|
||||
$rgb = is_array($rgb) ? $rgb : $this->hex2Rgb($rgb);
|
||||
|
||||
$r = $rgb['r'];
|
||||
$g = $rgb['g'];
|
||||
$b = $rgb['b'];
|
||||
if(($r * 0.299 + $g * 0.587 + $b * 0.114) > 186)
|
||||
{
|
||||
/* Is light color. */
|
||||
return $themeDark ? $themeDark : '#333333';
|
||||
}
|
||||
|
||||
return $themeLight ? $themeLight : '#ffffff';
|
||||
}
|
||||
|
||||
private function getTextStyle()
|
||||
{
|
||||
$hueDistance = intval($this->prop('hueDistance'));
|
||||
$saturation = $this->prop('saturation');
|
||||
$lightness = $this->prop('lightness');
|
||||
$background = $this->prop('background');
|
||||
$foreColor = $this->prop('foreColor');
|
||||
$code = $this->prop('code');
|
||||
$avatarCode = $code ? $code : $this->prop('text');
|
||||
|
||||
if(!$background)
|
||||
{
|
||||
$val = 0;
|
||||
if(is_numeric($avatarCode))
|
||||
{
|
||||
$val = intval($avatarCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
for($i = 0; $i < strlen($avatarCode); $i++) $val += ord($avatarCode[$i]);
|
||||
}
|
||||
|
||||
$hue = $val * $hueDistance % 360;
|
||||
$actualSat = $saturation * 100;
|
||||
$actualLight = $lightness * 100;
|
||||
$this->finalStyle->background = "hsl({$hue}, {$actualSat}%, {$actualLight}%)";
|
||||
|
||||
if(!$foreColor)
|
||||
{
|
||||
$rgb = $this->hslToRgb($hue, $saturation, $lightness);
|
||||
$this->finalStyle->color = $this->contrastColor($rgb);
|
||||
}
|
||||
}
|
||||
elseif (!$foreColor && $background)
|
||||
{
|
||||
$this->finalStyle->color = $this->contrastColor($background);
|
||||
}
|
||||
|
||||
$textStyle = array();
|
||||
if($this->actualSize and $this->actualSize < (14 * $this->displayTextLen))
|
||||
{
|
||||
$textStyle = array(
|
||||
'transform' => 'scale(' . $this->actualSize / (14 * $this->displayTextLen) . ')',
|
||||
'white-space' => 'nowrap'
|
||||
);
|
||||
}
|
||||
|
||||
return $textStyle;
|
||||
}
|
||||
|
||||
private function getContent()
|
||||
{
|
||||
$src = $this->prop('src');
|
||||
$text = $this->prop('text');
|
||||
$code = $this->prop('code');
|
||||
|
||||
/* With avatar. */
|
||||
if($src)
|
||||
{
|
||||
$this->finalClass[] = 'has-img';
|
||||
|
||||
return h::img
|
||||
(
|
||||
setClass('avatar-img'),
|
||||
set('src', $src),
|
||||
set('alt', $text),
|
||||
set('data-code', $code),
|
||||
);
|
||||
}
|
||||
|
||||
/* Without text and image. */
|
||||
if(!$text) return null;
|
||||
|
||||
$displayText = $this->getAvatarText();
|
||||
|
||||
$this->finalClass[] = 'has-text';
|
||||
$this->finalClass[] = 'has-text-' . $this->textLen;
|
||||
|
||||
$textStyle = $this->getTextStyle();
|
||||
return h::div
|
||||
(
|
||||
setClass('avatar-text'),
|
||||
set('data-actualSize', $this->actualSize),
|
||||
$textStyle ? setStyle($textStyle) : null,
|
||||
$displayText
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The backBtn widget class file of zin module 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 sunhao<sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'btn' . DS . 'v1.php';
|
||||
|
||||
/**
|
||||
* 后退按钮(backBtn)部件类。
|
||||
* The back button widget class.
|
||||
*
|
||||
* @author Hao Sun
|
||||
*/
|
||||
class backBtn extends btn
|
||||
{
|
||||
/**
|
||||
* Define widget properties.
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected static array $defineProps = array(
|
||||
'back?: string="APP"' // 定义返回行为,可以为 `'APP'`(默认值,返回打开当前页面时的上一个历史记录)、 `'GLOBAL'`(返回上一个全局历史记录)、`'moduleName-methodName'`(从历史记录中向后查找符合指定路径的历史记录)。
|
||||
);
|
||||
|
||||
/**
|
||||
* Override the getProps method.
|
||||
*
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getProps(): array
|
||||
{
|
||||
global $app;
|
||||
|
||||
$backs = array(
|
||||
'task' => 'execution-task,my-work,my-contribute,',
|
||||
'story' => 'product-browse,projectstory-story,execution-story,my-work,my-contribute,productplan-view',
|
||||
'bug' => 'bug-browse,project-bug,my-work,my-contribute,',
|
||||
'testcase' => 'testcase-browse,project-testcase,my-work,my-contribute,',
|
||||
'testsuite' => 'testsuite-browse,testsuite-view,',
|
||||
'testtask' => 'testtask-browse,testtask-cases,',
|
||||
'testreport' => 'testreport-browse,project-testreport',
|
||||
'tree' => 'product-browse,project-browse,execution-task,bug-browse,projectstory-story',
|
||||
'doc' => 'doc-mySpace,doc-productSpace,doc-projectSpace,doc-teamSpace',
|
||||
'design' => 'design-browse',
|
||||
'release' => 'release-browse,release-view',
|
||||
'projectrelease' => 'projectrelease-browse',
|
||||
'build' => 'execution-build,build-view',
|
||||
'projectbuild' => 'projectbuild-browse,projectbuild-view',
|
||||
'mr' => 'mr-browse',
|
||||
'repo' => 'repo-browse,repo-log',
|
||||
'compile' => 'compile-browse',
|
||||
'store' => 'store-browse',
|
||||
'space' => 'space-browse',
|
||||
'instance' => 'space-browse',
|
||||
'artifactrepo' => 'artifactrepo-browse',
|
||||
);
|
||||
|
||||
$props = parent::getProps();
|
||||
$back = $this->prop('back');
|
||||
if($back != 'APP')
|
||||
{
|
||||
$props['data-back'] = $back;
|
||||
}
|
||||
elseif(isset($backs[$app->rawModule]))
|
||||
{
|
||||
$props['data-back'] = $backs[$app->rawModule];
|
||||
|
||||
if(!$this->prop('url'))
|
||||
{
|
||||
$backLinks = explode(',', $backs[$app->rawModule]);
|
||||
$props['data-url'] = $backLinks[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$props['data-back'] = empty($back) ? 'APP' : $back;
|
||||
}
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the getClassList method.
|
||||
*
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getClassList(): array
|
||||
{
|
||||
$classList = parent::getClassList();
|
||||
$classList['open-url'] = true;
|
||||
return $classList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Determines whether its argument represents a JavaScript number.
|
||||
* @param {*} obj
|
||||
* @returns bool
|
||||
*/
|
||||
function isNumeric(obj)
|
||||
{
|
||||
return (!isNaN(obj) && typeof obj === 'number') || $.isNumeric(obj);;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new item.
|
||||
*
|
||||
* @param obj e
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function addItem(e)
|
||||
{
|
||||
const obj = e.target
|
||||
const newItem = $(obj).closest('.form-row').clone();
|
||||
let index = 0;
|
||||
|
||||
newItem.find('.add-btn').on('click', addItem);
|
||||
newItem.find('.del-btn').on('click', removeItem);
|
||||
|
||||
let inputName = newItem.find('input').length > 0 ? newItem.find('input').first().attr('name') : newItem.find('select').first().attr('name');
|
||||
inputName = inputName.slice(0, inputName.indexOf('['));
|
||||
$('form').find("[name^='" + inputName + "']").each(function() {
|
||||
let $name = $(this).attr('name');
|
||||
|
||||
let id = parseInt($name.slice($name.indexOf('[')+1, $name.indexOf(']')));
|
||||
if(isNumeric(id) && id >= index) index = id + 1;
|
||||
})
|
||||
|
||||
/* Fix id and value. */
|
||||
newItem.addClass('newItem');
|
||||
newItem.find('.form-label').html('');
|
||||
newItem.find('input').each(function()
|
||||
{
|
||||
let name = $(this).attr('name');
|
||||
name = name.slice(0, name.indexOf('[')+1) + String(index) + name.slice(name.indexOf(']'));
|
||||
$(this).attr('name', name);
|
||||
$(this).attr('id', name);
|
||||
$(this).val('');
|
||||
});
|
||||
newItem.find('select').each(function()
|
||||
{
|
||||
let name = $(this).attr('name');
|
||||
name = name.slice(0, name.indexOf('[')+1) + String(index) + name.slice(name.indexOf(']'));
|
||||
$(this).attr('name', name);
|
||||
$(this).attr('id', name);
|
||||
$(this).val('');
|
||||
});
|
||||
|
||||
$(obj).closest('.form-row').after(newItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove item.
|
||||
*
|
||||
* @param obj e
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function removeItem(e)
|
||||
{
|
||||
const obj = e.target
|
||||
|
||||
/* Dsiabled btn can't remove line. */
|
||||
if($(obj).closest('.btn').hasClass('disabled')) return false;
|
||||
|
||||
$(obj).closest('.form-row').remove();
|
||||
|
||||
let chosenProducts = 0;
|
||||
$("select[name^='products']").each(function()
|
||||
{
|
||||
if($(this).val() > 0) chosenProducts ++;
|
||||
});
|
||||
|
||||
(chosenProducts.length > 1 && (model == 'waterfall' || model == 'waterfallplus')) ? $('.stageBy').removeClass('hide') : $('.stageBy').addClass('hide');
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class batchActions extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'actionClass?: string=""',
|
||||
);
|
||||
|
||||
public static function getPageJS(): string|false
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
return formGroup
|
||||
(
|
||||
setClass('ml-2'),
|
||||
div
|
||||
(
|
||||
setClass($this->prop('actionClass')),
|
||||
btn
|
||||
(
|
||||
icon('plus', set::size('lg')),
|
||||
setClass('bg-white ring-0 rounded bg-opacity-20 add-btn'),
|
||||
on::click('addItem'),
|
||||
),
|
||||
btn
|
||||
(
|
||||
icon('close', set::size('lg')),
|
||||
setClass('bg-white ring-0 rounded bg-opacity-20 del-btn'),
|
||||
on::click('removeItem'),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The blockPanel widget class file of zin module 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 sunhao<sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'panel' . DS . 'v1.php';
|
||||
|
||||
/**
|
||||
* 仪表盘区块面板(blockPanel)部件类。
|
||||
* The block panel widget class.
|
||||
*
|
||||
* @author Hao Sun
|
||||
*/
|
||||
class blockPanel extends panel
|
||||
{
|
||||
protected static array $defineProps = array
|
||||
(
|
||||
'class?: string="rounded bg-canvas panel-block"', // 类名。
|
||||
'id?: string', // ID。
|
||||
'name?: string', // 区块内部名称。
|
||||
'block?: object|array', // 区块对象。
|
||||
'title?: string', // 标题。
|
||||
'headingClass?: string="border-b"', // 标题栏类名。
|
||||
'longBlock?: bool', // 是否为长区块。
|
||||
'moreLink?: string' // 更多链接。
|
||||
);
|
||||
|
||||
protected function created()
|
||||
{
|
||||
global $lang;
|
||||
$props = array();
|
||||
|
||||
$name = $this->prop('name');
|
||||
$block = $this->prop('block', data('block'));
|
||||
|
||||
if(is_array($block)) $block = (object)$block;
|
||||
if(empty($name) && !empty($block))
|
||||
{
|
||||
$name = $block->code;
|
||||
$props['name'] = $name;
|
||||
|
||||
if(empty($this->prop('id'))) $props['id'] = $block->module . '-' . $block->code . '-' . $block->id;
|
||||
}
|
||||
|
||||
$moreLink = $this->prop('moreLink');
|
||||
if(empty($moreLink) && !empty($block) && isset($block->moreLink)) $moreLink = $block->moreLink;
|
||||
if(empty($this->prop('headingActions')) && !empty($moreLink))
|
||||
{
|
||||
$props['headingActions'] = array(array('type' => 'ghost', 'url' => $moreLink, 'text' => $lang->more, 'caret' => 'right', 'size' => 'sm'));
|
||||
}
|
||||
|
||||
if(empty($this->prop('title'))) $props['title'] = empty($block) ? $lang->block->titleList[$name] : $block->title;
|
||||
|
||||
if($this->prop('longBlock') === null) $props['longBlock'] = data('longBlock');
|
||||
|
||||
$this->setProp($props);
|
||||
}
|
||||
|
||||
protected function buildProps(): array
|
||||
{
|
||||
$props = parent::buildProps();
|
||||
$name = $this->prop('name');
|
||||
if(!empty($name))
|
||||
{
|
||||
$props[] = setData('block', $name);
|
||||
$props[] = setClass("block-{$name}");
|
||||
$props[] = setID($this->prop('id'));
|
||||
}
|
||||
|
||||
$props[] = setClass($this->prop('longBlock') ? 'is-long' : 'is-short');
|
||||
|
||||
return $props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class btn extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'text?:string', // 按钮的文本。
|
||||
'icon?:string', // 图标名称。
|
||||
'iconClass?:string', // 图标的样式类。
|
||||
'square?:bool', // 是否为方形按钮,通常用于只显示一个图标的按钮。
|
||||
'disabled?:bool', // 是否禁用按钮。
|
||||
'active?:bool', // 是否为激活状态。
|
||||
'url?:string', // 按钮的链接地址。
|
||||
'target?:string', // 按钮的链接目标。
|
||||
'size?:string|int', // 按钮的尺寸,可选值为 `'xl'`、`'lg'`、`'md'`、`'sm'` 或者通过数字设置宽高,如 `20`。
|
||||
'trailingIcon?:string', // 按钮尾部图标的名称。
|
||||
'trailingIconClass?:string', // 按钮尾部图标的样式类。
|
||||
'caret?:string|bool', // 按钮的下拉箭头,可选值为 `'top'`(向上)、`'bottom'`(向下) 或者 `true`(自动)。
|
||||
'hint?:string', // 按钮的提示文本(鼠标悬停时显示)。
|
||||
'type?:string', // 按钮的类型,可选值为 `'default'`、`'primary'`、`'success'`、`'info'`、`'warning'`、`'danger'`、`'link'`。
|
||||
'btnType?:string="button"' // 按钮的类型,可选值为 `'button'`、`'submit'`、`'reset'`。
|
||||
);
|
||||
|
||||
public function onAddChild($child)
|
||||
{
|
||||
if(is_string($child) && !$this->props->has('text'))
|
||||
{
|
||||
$this->props->set('text', $child);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getProps()
|
||||
{
|
||||
$url = $this->prop('disabled') ? null : $this->prop('url');
|
||||
$target = $this->prop('target');
|
||||
$props = array_merge($this->getRestProps(), array('title' => $this->prop('hint')));
|
||||
|
||||
if(empty($url))
|
||||
{
|
||||
$props['type'] = $this->prop('btnType');
|
||||
if(!isset($props['data-target'])) $props['data-target'] = $target;
|
||||
return $props;
|
||||
}
|
||||
|
||||
$props['tagName'] = 'a';
|
||||
if(!isset($props['href'])) $props['href'] = $url;
|
||||
if(!isset($props['target'])) $props['target'] = $target;
|
||||
return $props;
|
||||
}
|
||||
|
||||
private function getChildren()
|
||||
{
|
||||
list($caret, $text, $icon, $iconClass, $trailingIcon, $trailingIconClass) = $this->prop(array('caret', 'text', 'icon', 'iconClass', 'trailingIcon', 'trailingIconClass'));
|
||||
|
||||
$children = array();
|
||||
if(!empty($icon)) $children[] = icon($icon, setClass($iconClass));
|
||||
if(!empty($text)) $children[] = h::span($text, setClass('text'));
|
||||
$children[] = parent::build();
|
||||
if(!empty($trailingIcon)) $children[] = icon($trailingIcon, setClass($trailingIconClass));
|
||||
if(!empty($caret)) $children[] = h::span(setClass(is_string($caret) ? "caret-$caret" : 'caret'));
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
protected function getClassList()
|
||||
{
|
||||
list($url, $type, $caret, $text, $icon, $trailingIcon) = $this->prop(array('url', 'type', 'caret', 'text', 'icon', 'trailingIcon'));
|
||||
$onlyCaret = empty($text) && !empty($caret) && empty($icon) && empty($trailingIcon);
|
||||
$classList = array(
|
||||
'btn' => true,
|
||||
'disabled' => $this->prop('disabled'),
|
||||
'active' => $this->prop('active'),
|
||||
'btn-caret' => $onlyCaret,
|
||||
'square' => $this->prop('square')
|
||||
);
|
||||
|
||||
if(empty($type) && !empty($url)) $type = 'btn-default';
|
||||
else if($type === 'link') $type = 'btn-link';
|
||||
else if($type === 'default') $type = 'btn-default';
|
||||
if(!empty($type)) $classList[$type] = true;
|
||||
|
||||
if(empty($text) && !empty($icon) && !isset($classList['square'])) $classList['square'] = true;
|
||||
|
||||
$size = $this->prop('size');
|
||||
if(!empty($size)) $classList["size-$size"] = true;
|
||||
|
||||
return $classList;
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$props = $this->getProps();
|
||||
$children = $this->getChildren();
|
||||
$classList = $this->getClassList();
|
||||
|
||||
return button
|
||||
(
|
||||
set($props),
|
||||
setClass($classList),
|
||||
$children
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class btnGroup extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'items?:array',
|
||||
'disabled?:bool',
|
||||
'size?:string',
|
||||
);
|
||||
|
||||
public function onBuildItem($item): btn
|
||||
{
|
||||
if(!($item instanceof item)) $item = item(set($item));
|
||||
return btn(inherit($item));
|
||||
}
|
||||
|
||||
private function getClassName(): string
|
||||
{
|
||||
$disabled = $this->prop('disabled');
|
||||
$size = $this->prop('size');
|
||||
|
||||
$className = 'btn-group';
|
||||
if(!empty($disabled)) $className .= ' disabled';
|
||||
if(!empty($size)) $className .= " size-$size";
|
||||
|
||||
return $className;
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$items = $this->prop('items');
|
||||
$className = $this->getclassName();
|
||||
|
||||
return div
|
||||
(
|
||||
setClass($className),
|
||||
set($this->getRestProps()),
|
||||
is_array($items) ? array_map(array($this, 'onBuildItem'), $items) : null,
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The burn widget class file of zin module 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 Yanyi Cao<caoyanyi@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
/**
|
||||
* 仪表盘(burn)部件类。
|
||||
* The burn widget class.
|
||||
*
|
||||
* @author Hao Sun
|
||||
*/
|
||||
class burn extends wg
|
||||
{
|
||||
/**
|
||||
* Define widget properties.
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected static array $defineProps = array(
|
||||
'data?: string|array', // 数据源
|
||||
'referenceLine?: bool=false' // 参考线
|
||||
);
|
||||
|
||||
/**
|
||||
* Build widget.
|
||||
*/
|
||||
protected function build(): zui
|
||||
{
|
||||
return zui::burn(inherit($this));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class cell extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'flex?: string', // flex 类型或具体的值,例如:'auto'、'none'、'1'、'auto 1 1'。
|
||||
'order?: int', // flex-order 属性。
|
||||
'grow?: int', // flex-grow 属性。
|
||||
'shrink?: int', // flex-shrink 属性。
|
||||
'width?: string|int', // flex-basis 属性,支持数值或百分比,例如 128px、1/3、30%、128px。
|
||||
'align?: string' // align-self 属性,例如 'auto'、'flex-start'、'flex-end'、'center'、'baseline'、'stretch'。
|
||||
);
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$basis = null;
|
||||
$class = array('cell');
|
||||
$width = $this->prop('width');
|
||||
$flex = $this->prop('flex');
|
||||
if(!empty($width))
|
||||
{
|
||||
$basis = $width;
|
||||
if(is_numeric($width)) $basis = $width . 'px';
|
||||
elseif(preg_match('/^(\d+)\/(\d+)$/', $width, $matches) !== 0) $basis = ((int)$matches[1] / (int)$matches[2] * 100) . '%';
|
||||
}
|
||||
if(!empty($flex))
|
||||
{
|
||||
if(strpos($flex, ' ') !== false) $style['flex'] = $flex;
|
||||
else $class[] = "flex-$flex";
|
||||
}
|
||||
|
||||
$style = array();
|
||||
$style['order'] = $this->prop('order');
|
||||
$style['flex-grow'] = $this->prop('grow');
|
||||
$style['flex-shrink'] = $this->prop('shrink');
|
||||
$style['flex-basis'] = $basis;
|
||||
$style['align-self'] = $this->prop('align');
|
||||
|
||||
return div
|
||||
(
|
||||
setClass($class),
|
||||
setStyle($style),
|
||||
set($this->getRestProps()),
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class center extends wg
|
||||
{
|
||||
protected function build(): wg
|
||||
{
|
||||
return div
|
||||
(
|
||||
setClass("center"),
|
||||
set($this->getRestProps()),
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class checkbox extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'text?: string',
|
||||
'checked?: bool',
|
||||
'name?: string',
|
||||
'primary: bool=true',
|
||||
'id?: string',
|
||||
'disabled?: bool',
|
||||
'type: string="checkbox"',
|
||||
'value?: string',
|
||||
'typeClass?: string',
|
||||
'rootClass?: string',
|
||||
'labelClass?: string',
|
||||
);
|
||||
|
||||
public function onAddChild($child)
|
||||
{
|
||||
if(is_string($child) && !$this->props->has('text'))
|
||||
{
|
||||
$this->props->set('text', $child);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildPrimary()
|
||||
{
|
||||
list($id, $text, $name, $checked, $disabled, $type, $typeClass, $rootClass, $labelClass, $value) = $this->prop(array('id', 'text', 'name', 'checked', 'disabled', 'type', 'typeClass', 'rootClass', 'labelClass', 'value'));
|
||||
|
||||
if(empty($typeClass)) $typeClass = $type;
|
||||
if(empty($id)) $id = $name . '_' . $value;
|
||||
|
||||
return div
|
||||
(
|
||||
setClass("$typeClass-primary", $rootClass, array('disabled' => $disabled)),
|
||||
h::input
|
||||
(
|
||||
set::type($type),
|
||||
set::id($id),
|
||||
set::name($name),
|
||||
set::checked($checked),
|
||||
set($this->props->skip('text,primary,typeClass,rootClass,id,labelClass')),
|
||||
),
|
||||
h::label
|
||||
(
|
||||
set('for', $id),
|
||||
setClass($labelClass),
|
||||
$text,
|
||||
),
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
if($this->prop('primary')) return $this->buildPrimary();
|
||||
list($text, $type, $typeClass) = $this->prop(array('text', 'type', 'typeClass'));
|
||||
|
||||
return h::label
|
||||
(
|
||||
setClass(empty($typeClass) ? $type : $typeClass),
|
||||
h::input
|
||||
(
|
||||
set::type($type),
|
||||
set($this->props->skip('text,primary,typeClass')),
|
||||
),
|
||||
is_string($text) ? span($text, set::className('text')) : $text,
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.checkbox-list {border-left: 1px solid var(--color-gray-400);}
|
||||
@@ -0,0 +1,41 @@
|
||||
window.handleCheckboxGroupClick = function(event)
|
||||
{
|
||||
const $target = $(event.target);
|
||||
const $checkboxGroup = $target.closest('.checkbox-group');
|
||||
if($target.closest('.checkbox-title').length > 0)
|
||||
{
|
||||
const $checkboxTitle = $target.closest('.checkbox-title');
|
||||
$checkboxGroup
|
||||
.find('.checkbox-child')
|
||||
.prop('checked', $checkboxTitle.prop('checked'));
|
||||
return;
|
||||
}
|
||||
|
||||
if($target.closest('.checkbox-child').length > 0)
|
||||
{
|
||||
let checkedCount = 0;
|
||||
const $checkboxChildren = $checkboxGroup.find('.checkbox-child');
|
||||
$checkboxChildren.each((_i, input) =>
|
||||
{
|
||||
if(input.checked === true) checkedCount++;
|
||||
});
|
||||
|
||||
const checkboxTitle = $checkboxGroup.find('.checkbox-title')[0];
|
||||
if(checkedCount === 0)
|
||||
{
|
||||
checkboxTitle.checked = false;
|
||||
checkboxTitle.indeterminate = false;
|
||||
}
|
||||
else if(checkedCount === $checkboxChildren.length)
|
||||
{
|
||||
$checkboxGroup.find('.checkbox-title').prop('checked', true);
|
||||
checkboxTitle.checked = true;
|
||||
checkboxTitle.indeterminate = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkboxTitle.checked = false;
|
||||
checkboxTitle.indeterminate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class checkboxGroup extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'title: array',
|
||||
'items: array'
|
||||
);
|
||||
|
||||
private static array $checkboxProps = array(
|
||||
'checked' => false,
|
||||
'disabled' => false,
|
||||
);
|
||||
|
||||
public static function getPageCSS(): string|false
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
||||
}
|
||||
|
||||
public static function getPageJS(): string|false
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
private function buildTitle(): wg
|
||||
{
|
||||
$title = array_merge(self::$checkboxProps, $this->prop('title'));
|
||||
return checkbox(set($title), setClass('checkbox-title'));
|
||||
}
|
||||
|
||||
private function buildCheckboxList(): wg
|
||||
{
|
||||
$items = $this->prop('items');
|
||||
$title = array_merge(self::$checkboxProps, $this->prop('title'));
|
||||
$list = ul(setClass('flex', 'flex-wrap', 'ml-1.5', 'checkbox-list', 'pl-3'));
|
||||
foreach($items as $item)
|
||||
{
|
||||
$item = array_merge(self::$checkboxProps, $item);
|
||||
if($title['checked'] === true) $item['checked'] = true;
|
||||
if($title['disabled'] === true) $item['disabled'] = true;
|
||||
$list->add
|
||||
(
|
||||
li
|
||||
(
|
||||
setClass('basis-1/2'),
|
||||
checkbox(set($item), setClass('checkbox-child'))
|
||||
)
|
||||
);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function build(): wg
|
||||
{
|
||||
return div
|
||||
(
|
||||
set('data-on', 'click'),
|
||||
set('data-call', 'window.handleCheckboxGroupClick'),
|
||||
set('data-params', 'event'),
|
||||
setClass('checkbox-group'),
|
||||
$this->buildTitle(),
|
||||
$this->buildCheckboxList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'checkbox' . DS . 'v1.php';
|
||||
|
||||
class checkList extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'primary: bool=true',
|
||||
'type: string="checkbox"',
|
||||
'name?: string',
|
||||
'value?: string|array',
|
||||
'items?: array',
|
||||
'inline?: bool',
|
||||
'disabled?: bool'
|
||||
);
|
||||
|
||||
public function getValueList()
|
||||
{
|
||||
$value = $this->prop('value');
|
||||
if(is_null($value)) return array();
|
||||
|
||||
if($this->prop('type') === 'checkbox') return is_array($value) ? $value : explode(',', $value);
|
||||
return [$value];
|
||||
}
|
||||
|
||||
public function onBuildItem($item): checkbox
|
||||
{
|
||||
if($item instanceof item) $item = $item->props->toJSON();
|
||||
|
||||
if(!isset($item['checked']))
|
||||
{
|
||||
$value = isset($item['value']) ? $item['value'] : '';
|
||||
$valueList = $this->getValueList();
|
||||
|
||||
$item['checked'] = in_array($value, $valueList);
|
||||
$item['disabled'] = $this->prop('disabled');
|
||||
}
|
||||
|
||||
$props = $this->props->pick(['primary', 'type', 'name', 'disabled']);
|
||||
if(!empty($props['name']) && !empty($item['value'])) $props['id'] = $props['name'] . $item['value'];
|
||||
|
||||
return new checkbox(set($props), set($item));
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
list($items, $inline, $disabled) = $this->prop(['items', 'inline', 'disabled']);
|
||||
|
||||
if(!empty($items))
|
||||
{
|
||||
$valueList = $this->getValueList();
|
||||
foreach($items as $key => $item)
|
||||
{
|
||||
if(!is_array($item)) $item = array('text' => $item, 'value' => $key);
|
||||
if(!isset($item['checked'])) $item['checked'] = in_array($item['value'], $valueList);
|
||||
$items[$key] = $this->onBuildItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
return div
|
||||
(
|
||||
setClass($inline ? 'check-list-inline' : 'check-list'),
|
||||
set($this->getRestProps()),
|
||||
$disabled ? set('disabled', 'disabled') : '',
|
||||
$items,
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class col extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'justify?:string',
|
||||
'align?:string'
|
||||
);
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$classList = 'col';
|
||||
list($justify, $align) = $this->prop(array('justify', 'align'));
|
||||
if(!empty($justify)) $classList .= ' justify-' . $justify;
|
||||
if(!empty($align)) $classList .= ' items-' . $align;
|
||||
|
||||
return div
|
||||
(
|
||||
setClass($classList),
|
||||
set($this->getRestProps()),
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'btn' . DS . 'v1.php';
|
||||
|
||||
class collapseBtn extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'target: string', // 展开折叠的目标元素选择器。
|
||||
'parent: string' // 目标元素与按钮共同的父级元素选择器,使用 closest 辅助目标元素的确定。
|
||||
);
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$target = $this->prop('target');
|
||||
$parent = $this->prop('parent');
|
||||
|
||||
return btn
|
||||
(
|
||||
setClass('btn-link', 'collapse-btn'),
|
||||
set($this->getRestProps()),
|
||||
set::icon('angle-down'),
|
||||
on::click
|
||||
(
|
||||
<<<FUNC
|
||||
const btn = event.target.closest('.collapse-btn');
|
||||
const icon = btn.querySelector('.icon');
|
||||
icon.classList.toggle('icon-angle-down');
|
||||
icon.classList.toggle('icon-angle-top');
|
||||
|
||||
const parentElm = btn.closest('$parent');
|
||||
const targetElm = parentElm.querySelector('$target');
|
||||
if(targetElm) targetElm.classList.toggle('hidden');
|
||||
FUNC
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The colorPicker widget class file of zin module 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 sunhao<sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
/**
|
||||
* 颜色选择器(colorPicker)部件类
|
||||
* The colorPicker widget class
|
||||
*/
|
||||
class colorPicker extends wg
|
||||
{
|
||||
/**
|
||||
* Define widget properties.
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected static array $defineProps = array(
|
||||
'id?: string="$GID"', // 组件根元素的 ID。
|
||||
'formID?: string', // 组件隐藏的表单元素 ID。
|
||||
'className?: string|array', // 类名。
|
||||
'style?: array', // 样式。
|
||||
'tagName?: string', // 组件根元素的标签名。
|
||||
'attrs?: array', // 附加到组件根元素上的属性。
|
||||
'clickType?: "toggle"|"open"', // 点击类型,`toggle` 表示点击按钮时切换显示隐藏,`open` 表示点击按钮时只打。
|
||||
'afterRender?: function', // 渲染完成后的回调函数。
|
||||
'beforeDestroy?: function', // 销毁前的回调函数。
|
||||
'name?: string', // 作为表单项的名称。
|
||||
'value?: string|string[]', // 默认值。
|
||||
'onChange?: function', // 值变更回调函数。
|
||||
'disabled?: boolean', // 是否禁用。
|
||||
'multiple?: boolean|number=false', // 是否允许选择多个值,如果指定为数字,则限制多选的数目,默认 `false`。
|
||||
'required?: boolean', // 是否必选(不允许空值,不可以被清除)。
|
||||
'items?: string | string[]', // 颜色选项列表。
|
||||
'icon?: string|array="color"', // 将触发按钮显示为图标。
|
||||
'syncValue?: string', // 指定选择器同步颜色值作为文本到的元素。
|
||||
'syncColor?: string', // 指定选择器同步文字颜色到的元素。
|
||||
'syncBackground?: string', // 指定选择器同步背景颜色到的元素。
|
||||
'syncBorder?: string', // 指定选择器同步边框颜色到的元素。
|
||||
'hint?: string', // 提示文字。
|
||||
'closeBtn?: boolean', // 是否在弹出面板上显示关闭按钮。
|
||||
'heading?: ComponentChildren' // 弹出面板的标题。
|
||||
);
|
||||
|
||||
/**
|
||||
* Build widget.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function build(): wg
|
||||
{
|
||||
list($props, $restProps) = $this->props->split(array_keys(static::definedPropsList()));
|
||||
if(isset($props['id']))
|
||||
{
|
||||
$props['_id'] = $props['id'];
|
||||
unset($props['id']);
|
||||
}
|
||||
|
||||
if(!isset($props['items']))
|
||||
{
|
||||
global $app, $lang;
|
||||
$moduleName = $app->getModuleName();
|
||||
if(isset($lang->$moduleName->colorList)) $props['items'] = $lang->$moduleName->colorList;
|
||||
}
|
||||
return zui::colorPicker
|
||||
(
|
||||
set::_class('form-group-wrapper'),
|
||||
set::_map(array('value' => 'defaultValue', 'items' => 'colors', 'formID' => 'id')),
|
||||
set::_props($restProps),
|
||||
set($props),
|
||||
$this->children(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class commentBtn extends btn
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'dataTarget?:string',
|
||||
'dataUrl?:string',
|
||||
'dataType?:string',
|
||||
'icon?:string',
|
||||
'iconClass?:string',
|
||||
'text?:string',
|
||||
'square?:bool',
|
||||
'disabled?:bool',
|
||||
'active?:bool',
|
||||
'url?:string',
|
||||
'target?:string',
|
||||
'size?:string|int',
|
||||
'trailingIcon?:string',
|
||||
'trailingIconClass?:string',
|
||||
'caret?:string|bool',
|
||||
'hint?:string',
|
||||
'type?:string',
|
||||
'btnType?:string'
|
||||
);
|
||||
|
||||
protected function getProps(): array
|
||||
{
|
||||
$dataTarget = $this->prop('dataTarget');
|
||||
$dataUrl = $this->prop('dataUrl');
|
||||
$dataType = $this->prop('dataType');
|
||||
$props = parent::getProps();
|
||||
|
||||
$props['data-toggle'] = 'modal';
|
||||
$props['data-type'] = $dataType;
|
||||
$props['data-url'] = $dataUrl;
|
||||
$props['data-target'] = $dataTarget;
|
||||
|
||||
return $props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class commentDialog extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'title?:string',
|
||||
'url?:string',
|
||||
'name?:string="comment"',
|
||||
'method?:string="post"'
|
||||
);
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
global $lang;
|
||||
$title = $this->prop('title');
|
||||
$name = $this->prop('name');
|
||||
$url = $this->prop('url');
|
||||
$method = $this->prop('method');
|
||||
if(empty($title)) $title = $lang->action->create;
|
||||
|
||||
return modal
|
||||
(
|
||||
setID('comment-dialog'),
|
||||
set::modalProps(array('title' => $title)),
|
||||
commentForm
|
||||
(
|
||||
set::url($url),
|
||||
set::method($method),
|
||||
set::name($name),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class commentForm extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'url?:string',
|
||||
'name?:string="comment"',
|
||||
'method?:string="POST"'
|
||||
);
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
global $lang;
|
||||
$url = $this->prop('url');
|
||||
$name = $this->prop('name');
|
||||
$method = $this->prop('method');
|
||||
if(empty($name)) $name = 'comment';
|
||||
|
||||
return form
|
||||
(
|
||||
set::url($url),
|
||||
set::method($method),
|
||||
set::submitBtnText($lang->save),
|
||||
setClass('comment-form'),
|
||||
editor
|
||||
(
|
||||
setID($name),
|
||||
set::name($name)
|
||||
),
|
||||
set::actions
|
||||
(
|
||||
array(
|
||||
'submit',
|
||||
array('data-dismiss' => 'modal', 'text' => $lang->close)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'input' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'textarea' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'editor' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'checkbox' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'checklist' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'radiolist' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'select' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'inputcontrol' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'picker' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'datepicker' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'timepicker' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'pripicker' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'severitypicker' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'colorpicker' . DS . 'v1.php';
|
||||
|
||||
class control extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'type?: string', // 表单输入元素类型,值可以为:static, text, password, email, number, date, time, datetime, month, url, search, tel, color, picker, pri, severity, select, checkbox, radio, checkboxList, radioList, checkboxListInline, radioListInline, file, textarea
|
||||
'name: string', // HTML name 属性
|
||||
'id?: string', // HTML id 属性
|
||||
'value?: string', // HTML value 属性
|
||||
'placeholder?: string', // HTML placeholder 属性
|
||||
'readonly?: bool', // HTML readonly 属性
|
||||
'required?: bool', // 是否为必填项
|
||||
'disabled?: bool', // 是否为禁用状态
|
||||
'items?: array' // 表单输入元素子项数据
|
||||
);
|
||||
|
||||
protected function created()
|
||||
{
|
||||
$name = $this->prop('name');
|
||||
if($this->prop('type') === 'static' && $name === null) $this->setProp('name', '');
|
||||
if($this->prop('id') === null && $name !== null)
|
||||
{
|
||||
$id = substr($name, -2) == '[]' ? substr($name, 0, - 2) : $name;
|
||||
$this->setProp('id', $id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build control with static content.
|
||||
*
|
||||
* @return wg
|
||||
*/
|
||||
protected function buildStatic(): wg
|
||||
{
|
||||
$name = $this->prop('name');
|
||||
return div
|
||||
(
|
||||
set::className('form-control-static'),
|
||||
set($this->props->skip(array('type', 'name', 'value', 'required', 'disabled', 'placeholder', 'items', 'required'))),
|
||||
$name ? set('data-name', $name) : null,
|
||||
$this->prop('value')
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildTextarea(): wg
|
||||
{
|
||||
return new textarea(set($this->props->skip('type')));
|
||||
}
|
||||
|
||||
protected function buildInputControl(): wg
|
||||
{
|
||||
$controlProps = array();
|
||||
$allProps = $this->props->skip('type');
|
||||
$propsNames = array_keys(inputControl::definedPropsList());
|
||||
|
||||
foreach($propsNames as $propName)
|
||||
{
|
||||
if(!isset($allProps[$propName])) continue;
|
||||
|
||||
$controlProps[$propName] = $allProps[$propName];
|
||||
unset($allProps[$propName]);
|
||||
}
|
||||
|
||||
return new inputControl
|
||||
(
|
||||
set($controlProps),
|
||||
new input(set($allProps)),
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildCheckbox(): wg
|
||||
{
|
||||
if($this->hasProp('items')) return $this->buildCheckList();
|
||||
return new checkList
|
||||
(
|
||||
new checkbox(set($this->props->skip('type')))
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildCheckList(): wg
|
||||
{
|
||||
return new checkList
|
||||
(
|
||||
set($this->props->skip('type'))
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildRadioList(): wg
|
||||
{
|
||||
return new radioList
|
||||
(
|
||||
set($this->props->skip('type'))
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildCheckListInline(): wg
|
||||
{
|
||||
return new checkList
|
||||
(
|
||||
set::inline(true),
|
||||
set($this->props->skip('type'))
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildRadioListInline(): wg
|
||||
{
|
||||
return new radioList
|
||||
(
|
||||
set::inline(true),
|
||||
set($this->props->skip('type'))
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildDate(): wg
|
||||
{
|
||||
return new datePicker(set($this->props->skip('type')));
|
||||
}
|
||||
|
||||
protected function buildTime(): wg
|
||||
{
|
||||
return new timePicker(set($this->props->skip('type')));
|
||||
}
|
||||
|
||||
protected function buildPri(): wg
|
||||
{
|
||||
return new priPicker(set($this->props->skip('type')));
|
||||
}
|
||||
|
||||
protected function buildSeverity(): wg
|
||||
{
|
||||
return new severityPicker(set($this->props->skip('type')));
|
||||
}
|
||||
|
||||
protected function buildColor(): wg
|
||||
{
|
||||
return new colorPicker(set($this->props->skip('type')));
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$type = $this->prop('type');
|
||||
if(empty($type)) $type = $this->hasProp('items') ? 'picker' : 'text';
|
||||
|
||||
$methodName = "build{$type}";
|
||||
if(method_exists($this, $methodName)) return $this->$methodName();
|
||||
|
||||
$wgName = "\\zin\\$type";
|
||||
if(class_exists($wgName)) return new $wgName(set($this->props->skip('type')), $this->children());
|
||||
|
||||
return input(set($this->props));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The dashboard widget class file of zin module 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 sunhao<sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
/**
|
||||
* 仪表盘(dashboard)部件类。
|
||||
* The dashboard widget class.
|
||||
*
|
||||
* @author Hao Sun
|
||||
*/
|
||||
class dashboard extends wg
|
||||
{
|
||||
/**
|
||||
* Define widget properties.
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected static array $defineProps = array(
|
||||
'id?: string', // ID。
|
||||
'cache?: bool|string', // 是否启用缓存。
|
||||
'responsive?: bool', // 是否启用响应式。
|
||||
'blocks: array', // 区块列表。
|
||||
'grid?: int', // 栅格数。
|
||||
'gap?: int', // 间距。
|
||||
'leftStop?: int', // 区块水平停靠间隔。
|
||||
'cellHeight?: int', // 网格高度。
|
||||
'blockFetch?: string|function|array', // 区块数据获取 url 或选项。
|
||||
'blockDefaultSize?: array', // 区块默认大小。
|
||||
'blockSizeMap?: array', // 区块大小映射。
|
||||
'blockMenu?: array', // 区块菜单。
|
||||
'onLayoutChange?: function', // 布局变更事件。
|
||||
'onClickMenu?: function' // 布局变更事件。
|
||||
);
|
||||
|
||||
static $dashboardID = 0;
|
||||
|
||||
protected function created()
|
||||
{
|
||||
$this->setDefaultProps(array('id' => static::$dashboardID ? static::$dashboardID : 'dashboard', 'cache' => data('app.user.account')));
|
||||
static::$dashboardID++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build widget.
|
||||
*/
|
||||
protected function build(): wg
|
||||
{
|
||||
return zui::dashboard
|
||||
(
|
||||
set($this->props->skip(array('id'))),
|
||||
set('_id', $this->prop('id')),
|
||||
set('_props', $this->getRestProps())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The datePicker widget class file of zin module 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 sunhao<sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'input' . DS . 'v1.php';
|
||||
|
||||
/**
|
||||
* 日期选择器(datePicker)部件类
|
||||
* The datePicker widget class
|
||||
*/
|
||||
class datePicker extends wg
|
||||
{
|
||||
/**
|
||||
* Define widget properties.
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected static array $defineProps = array
|
||||
(
|
||||
'id?: string="$GID"', // 组件根元素的 ID。
|
||||
'formID?: string', // 组件隐藏的表单元素 ID。
|
||||
'className?: string|array', // 类名。
|
||||
'style?: array', // 样式。
|
||||
'tagName?: string', // 组件根元素的标签名。
|
||||
'attrs?: array', // 附加到组件根元素上的属性。
|
||||
'clickType?: "toggle"|"open"', // 点击类型,`toggle` 表示点击按钮时切换显示隐藏,`open` 表示点击按钮时只打。
|
||||
'afterRender?: function', // 渲染完成后的回调函数。
|
||||
'beforeDestroy?: function', // 销毁前的回调函数。
|
||||
'name?: string', // 作为表单项的名称。
|
||||
'value?: string|string[]', // 默认值。
|
||||
'onChange?: function', // 值变更回调函数。
|
||||
'disabled?: boolean', // 是否禁用。
|
||||
'multiple?: boolean|number=false', // 是否允许选择多个值,如果指定为数字,则限制多选的数目,默认 `false`。
|
||||
'required?: boolean', // 是否必选(不允许空值,不可以被清除)。
|
||||
'placeholder?: string', // 选择框上的占位文本。
|
||||
'format?: string', // 日期格式,默认 yyyy-MM-dd。
|
||||
'icon?: string|array="calendar"', // 在输入框右侧显示的图标。
|
||||
'weekNames?: string[]', // 星期名称,索引为 0 表示周日。
|
||||
'monthNames?: string[]', // 月份名称,索引为 0 表示一月份。
|
||||
'yearText?: string', // 用于显示年份的格式化文本。
|
||||
'todayText?: string', // 用于显示“今天”的文本。
|
||||
'clearText?: string', // 用于显示“清除”的文本。
|
||||
'weekStart?: int', // 一周从星期几开始,默认 1。
|
||||
'minDate?: string|int', // 最小可选的日期。
|
||||
'maxDate?: string|int', // 最大可选的日期。
|
||||
'menu?: array', // 左侧显示的菜单设置。
|
||||
'actions?: array', // 底部工具栏设置。
|
||||
'onInvalid?: function', // 日期值无效时的回调函数。
|
||||
);
|
||||
|
||||
/**
|
||||
* Build the widget.
|
||||
*
|
||||
* @access protected
|
||||
* @return wg
|
||||
*/
|
||||
protected function build(): wg
|
||||
{
|
||||
list($props, $restProps) = $this->props->split(array_keys(static::definedPropsList()));
|
||||
if(isset($props['id']))
|
||||
{
|
||||
$props['_id'] = $props['id'];
|
||||
unset($props['id']);
|
||||
}
|
||||
|
||||
return zui::datePicker
|
||||
(
|
||||
set::_class('form-group-wrapper'),
|
||||
set::_map(array('value' => 'defaultValue', 'formID' => 'id')),
|
||||
set($props),
|
||||
set::_props($restProps),
|
||||
$this->children(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'input' . DS . 'v1.php';
|
||||
|
||||
class datetimePicker extends wg
|
||||
{
|
||||
/**
|
||||
* Define widget properties.
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected static array $defineProps = array
|
||||
(
|
||||
'id?: string="$GID"', // 组件根元素的 ID。
|
||||
'formID?: string', // 组件隐藏的表单元素 ID。
|
||||
'className?: string|array', // 类名。
|
||||
'style?: array', // 样式。
|
||||
'tagName?: string', // 组件根元素的标签名。
|
||||
'attrs?: array', // 附加到组件根元素上的属性。
|
||||
'clickType?: "toggle"|"open"', // 点击类型,`toggle` 表示点击按钮时切换显示隐藏,`open` 表示点击按钮时只打。
|
||||
'afterRender?: function', // 渲染完成后的回调函数。
|
||||
'beforeDestroy?: function', // 销毁前的回调函数。
|
||||
'name?: string', // 作为表单项的名称。
|
||||
'value?: string|string[]', // 默认值。
|
||||
'onChange?: function', // 值变更回调函数。
|
||||
'disabled?: boolean', // 是否禁用。
|
||||
'multiple?: boolean|number=false', // 是否允许选择多个值,如果指定为数字,则限制多选的数目,默认 `false`。
|
||||
'required?: boolean', // 是否必选(不允许空值,不可以被清除)。
|
||||
'placeholder?: string', // 选择框上的占位文本。
|
||||
'icon?: string|array="calendar"', // 在输入框右侧显示的图标。
|
||||
'weekNames?: string[]', // 星期名称,索引为 0 表示周日。
|
||||
'monthNames?: string[]', // 月份名称,索引为 0 表示一月份。
|
||||
'yearText?: string', // 用于显示年份的格式化文本。
|
||||
'todayText?: string', // 用于显示“今天”的文本。
|
||||
'clearText?: string', // 用于显示“清除”的文本。
|
||||
'weekStart?: int', // 一周从星期几开始,默认 1。
|
||||
'minDate?: string|int', // 最小可选的日期。
|
||||
'maxDate?: string|int', // 最大可选的日期。
|
||||
'menu?: array', // 左侧显示的菜单设置。
|
||||
'actions?: array', // 底部工具栏设置。
|
||||
'onInvalid?: function', // 日期值无效时的回调函数。
|
||||
'dateFormat?: string', // 日期格式,默认 yyyy-MM-dd。
|
||||
'timeFormat?: string', // 时间格式,默认 hh:mm
|
||||
'joiner?: string' // 日期与时间的连接符,默认为单个空格
|
||||
);
|
||||
|
||||
/**
|
||||
* Build the widget.
|
||||
*
|
||||
* @access protected
|
||||
* @return wg
|
||||
*/
|
||||
protected function build(): wg
|
||||
{
|
||||
list($props, $restProps) = $this->props->split(array_keys(static::definedPropsList()));
|
||||
if(isset($props['id']))
|
||||
{
|
||||
$props['_id'] = $props['id'];
|
||||
unset($props['id']);
|
||||
}
|
||||
|
||||
return zui::datetimePicker
|
||||
(
|
||||
set::_class('form-group-wrapper'),
|
||||
set::_map(array('value' => 'defaultValue', 'formID' => 'id')),
|
||||
set($props),
|
||||
set::_props($restProps),
|
||||
$this->children(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.detail-body > .form-actions:last-child {margin-top: 0;}
|
||||
@@ -0,0 +1,9 @@
|
||||
$(() =>
|
||||
{
|
||||
const $formActions = $('form.detail-body + .form-actions');
|
||||
if($formActions.length)
|
||||
{
|
||||
$detailBody = $formActions.prev();
|
||||
$detailBody.append($formActions);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class detailBody extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'isForm?: bool=false'
|
||||
);
|
||||
|
||||
protected static array $defineBlocks = array(
|
||||
'main' => array('map' => 'sectionList'),
|
||||
'side' => array('map' => 'detailSide'),
|
||||
'bottom' => array('map' => 'history,fileList'),
|
||||
'floating' => array('map' => 'floatToolbar'),
|
||||
);
|
||||
|
||||
public static function getPageCSS(): string|false
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
||||
}
|
||||
|
||||
public static function getPageJS(): string|false
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$main = $this->block('main');
|
||||
$side = $this->block('side');
|
||||
$bottom = $this->block('bottom');
|
||||
$floating = $this->block('floating');
|
||||
$isForm = $this->prop('isForm');
|
||||
|
||||
if(!$isForm)
|
||||
{
|
||||
return div
|
||||
(
|
||||
setClass('detail-body rounded flex gap-1'),
|
||||
set($this->getRestProps()),
|
||||
div
|
||||
(
|
||||
setClass('col gap-1 grow'),
|
||||
$main,
|
||||
$bottom,
|
||||
center(setClass('pt-6'), $floating),
|
||||
),
|
||||
$side
|
||||
);
|
||||
}
|
||||
|
||||
return formBase
|
||||
(
|
||||
set::actionsClass('h-14 flex flex-none items-center justify-center shadow'),
|
||||
setClass('detail-body rounded col overflow-y-hidden bg-white'),
|
||||
set($this->getRestProps()),
|
||||
setStyle('height', 'calc(100vh - 120px)'),
|
||||
div
|
||||
(
|
||||
setClass('flex-auto overflow-y-auto'),
|
||||
div
|
||||
(
|
||||
setClass('flex'),
|
||||
setStyle('min-height', '100%'),
|
||||
div
|
||||
(
|
||||
setClass('col grow'),
|
||||
$main,
|
||||
$bottom,
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('w-1'),
|
||||
setStyle('background', 'var(--zt-page-bg)'),
|
||||
),
|
||||
$side,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class detailHeader extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'back?: string="APP"',
|
||||
'backUrl?: string',
|
||||
);
|
||||
|
||||
protected static array $defineBlocks = array(
|
||||
'prefix' => array(),
|
||||
'title' => array(),
|
||||
'suffix' => array(),
|
||||
);
|
||||
|
||||
private function backBtn(): wg
|
||||
{
|
||||
global $lang;
|
||||
return backBtn
|
||||
(
|
||||
set::icon('back'),
|
||||
set::type('secondary'),
|
||||
set::back($this->prop('back')),
|
||||
set::url($this->prop('backUrl')),
|
||||
$lang->goback
|
||||
);
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$prefix = $this->block('prefix');
|
||||
$title = $this->block('title');
|
||||
$suffix = $this->block('suffix');
|
||||
|
||||
if(empty($prefix) && !isAjaxRequest('modal')) $prefix = $this->backBtn();
|
||||
|
||||
return div
|
||||
(
|
||||
setClass('detail-header flex justify-between mb-3'),
|
||||
div
|
||||
(
|
||||
setClass('flex', 'items-center', 'gap-x-4'),
|
||||
$prefix,
|
||||
$title,
|
||||
),
|
||||
$suffix
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
.detail-side {width: 370px;}
|
||||
.detail-side .tab-content>.tab-pane {padding-left: 0 !important;}
|
||||
.detail-side {background: #fff; height: min-content;}
|
||||
.detail-side .tabs:not(:first-child) {border-top: 1px solid #E6EAF1;}
|
||||
.detail-side .tabs {padding-top: 12px; padding-bottom: 20px;}
|
||||
.detail-side > .table-data {margin-top: 16px;}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class detailSide extends wg
|
||||
{
|
||||
public static function getPageCSS(): string|false
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
return div
|
||||
(
|
||||
setClass('detail-side flex-none px-6'),
|
||||
set($this->getRestProps()),
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class divider extends wg
|
||||
{
|
||||
protected function build(): wg
|
||||
{
|
||||
return div
|
||||
(
|
||||
setClass("divider"),
|
||||
set($this->getRestProps()),
|
||||
$this->children()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
.module-menu {max-height: calc(100vh - 105px); padding: 0 0 8px;}
|
||||
.module-menu .active {color: var(--color-primary-600); font-weight: 500;}
|
||||
.module-menu header a:hover > .icon {color: var(--color-primary-600) !important;}
|
||||
.module-menu .tree-item * {white-space: nowrap;}
|
||||
|
||||
#docDropmenu {padding-bottom: 0.5rem;}
|
||||
#docDropmenu .is-leading {display: none;}
|
||||
#docDropmenu .primary {--tw-ring-color: var(--btn-border-color); background-color: var(--btn-bg); color: inherit; width: 100%;}
|
||||
|
||||
.module-menu .tree .tree-item .tree-link {text-overflow: clip; overflow: hidden; flex: 1 10 auto;}
|
||||
.module-menu .tree .tree-item .tree-actions {margin-left: 0;}
|
||||
.module-menu .tree .tree-item .tree-actions .icon-ellipsis-v {display: none;}
|
||||
.module-menu .tree .tree-item .tree-item-content:hover .tree-actions .icon {display: block;}
|
||||
.module-menu .tree .tree-item .tree-item-content .tree-actions .with-popover-show .icon {display: block;}
|
||||
|
||||
.tree > .tree-item > .project-tree-title {font-size: 16px; margin-bottom: 0.5rem;}
|
||||
.tree > .tree-item > .project-tree-title .text {margin-left: 0.5rem;}
|
||||
.tree > .tree-item > .project-tree-title .tree-icon {color: var(--nav-active-color); opacity: 1;}
|
||||
.tree > .tree-item > .project-tree-title > .tree-toggle-icon {display: none;}
|
||||
@@ -0,0 +1,69 @@
|
||||
window.saveModule = function()
|
||||
{
|
||||
const name = $(this).val();
|
||||
if(!name) return $(this).closest('.tree-item').remove();
|
||||
|
||||
const {id, type, lib, module} = $(this).data();
|
||||
const parentID = $(this).data('parent');
|
||||
|
||||
const $element = $(`div[data-id='${id}']`);
|
||||
$.ajaxSubmit({
|
||||
url: $.createLink('tree', 'ajaxCreateModule'),
|
||||
data: {
|
||||
name : name,
|
||||
libID : lib,
|
||||
parentID : type == 'child' ? id : parentID,
|
||||
objectID : id,
|
||||
moduleType : module,
|
||||
isUpdate : false,
|
||||
createType : type,
|
||||
},
|
||||
onSuccess: () =>
|
||||
{
|
||||
$(this).val('');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addModule = function(id, addType)
|
||||
{
|
||||
const $element = $(`div[data-id='${id}']`);
|
||||
const {lib, type, module} = $element.data();
|
||||
|
||||
let parentID = ['docLib', 'apiLib'].includes(type) ? '0' : $element.data('parent');
|
||||
if(addType == 'child') parentID = id;
|
||||
|
||||
const level = addType == 'same' ? $element.data('level') : $element.data('level') + 1;
|
||||
const style = `style="margin-left: calc(${level} * var(--tree-indent, 20px))"`;
|
||||
|
||||
let inputTpl = '<li class="tree-item">';
|
||||
inputTpl += `<div class="tree-item-content" ${style}>`;
|
||||
inputTpl += `<input id="moduleName" class="form-control" data-id="${id}" data-parent="${parentID}" data-type="${addType}" data-lib="${lib}" data-module="${module}">`;
|
||||
inputTpl += '</div></li>';
|
||||
|
||||
if(addType == 'same')
|
||||
{
|
||||
$(`div[data-id='${id}']`).before(inputTpl);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!$element.parent().hasClass('show')) $element.find('.tree-toggle-icon').trigger('click');
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
if($element.next('.tree').length == 0) $element.after(`<menu class="tree" level="${level}" data-level="${level}"></menu>`);
|
||||
|
||||
$(`div[data-id='${id}']`).next('.tree').prepend(inputTpl);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
$('#moduleName').trigger('focus');
|
||||
document.getElementById("moduleName").addEventListener('blur', saveModule);
|
||||
document.getElementById("moduleName").addEventListener('keydown', function(e)
|
||||
{
|
||||
if(e.keyCode == 13) saveModule.call(this);
|
||||
});
|
||||
}, 1);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user