Merge remote-tracking branch 'origin/18.x' into sprint/4352
This commit is contained in:
Vendored
+5
-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,7 +118,7 @@ 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"
|
||||
@@ -700,18 +703,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. */
|
||||
|
||||
@@ -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": {
|
||||
|
||||
+32
-4
@@ -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 样式文件。
|
||||
|
||||
+74
-1
@@ -240,7 +240,61 @@ $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[] = 'search.deletezinquery';
|
||||
$config->openMethods[] = 'space.edit';
|
||||
$config->openMethods[] = 'space.binduser';
|
||||
|
||||
$config->openModules = array();
|
||||
$config->openModules[] = 'install';
|
||||
@@ -343,6 +397,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`');
|
||||
@@ -377,10 +434,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;
|
||||
@@ -439,6 +499,13 @@ $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('aiPrompts', 'promptDesign', 'promptExec');
|
||||
@@ -476,3 +543,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 it is too large
Load Diff
+753
-193
@@ -1,3 +1,561 @@
|
||||
-- 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),
|
||||
(680, 515, 'application', 'package', ',open,biz,max,ipd,', ',rnd,', 2880),
|
||||
(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, ',biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(1286, 'system', 'configdomain', 126, ',biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(2137, 'system', 'ossview', 126, ',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', 'view', 678, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(2158, 'store', 'browse', 678, ',open,biz,max,ipd,', ',rnd,', '1', 15),
|
||||
(2159, 'store', 'appView', 678, ',open,biz,max,ipd,', ',rnd,', '1', 20),
|
||||
(2150, 'space', 'getStoreAppInfo', 678, ',open,biz,max,ipd,', ',rnd,', '1', 25),
|
||||
(2151, 'instance', 'install', 679, ',open,biz,max,ipd,', ',rnd,', '1', 10),
|
||||
(2152, 'instance', 'visit', 679, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(2153, 'instance', 'ajaxStatus', 679, ',open,biz,max,ipd,', ',rnd,', '1', 15),
|
||||
(2154, 'instance', 'ajaxStart', 679, ',open,biz,max,ipd,', ',rnd,', '1', 20),
|
||||
(2155, 'instance', 'ajaxStop', 679, ',open,biz,max,ipd,', ',rnd,', '1', 25),
|
||||
(2157, 'instance', 'upgrade', 679, ',open,biz,max,ipd,', ',rnd,', '1', 30),
|
||||
(2156, 'instance', 'ajaxUninstall', 680, ',open,biz,max,ipd,', ',rnd,', '1', 5),
|
||||
(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', ''),
|
||||
(680, 'manager', 'zh-cn', '', '删除应用', ''),
|
||||
(680, 'manager', 'zh-tw', '', '刪除應用', ''),
|
||||
(680, 'manager', 'de', '', 'Delete Application', ''),
|
||||
(680, 'manager', 'en', '', 'Delete Application', ''),
|
||||
(680, 'manager', 'fr', '', 'Delete 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', '', ''),
|
||||
(2148, 'priv', 'en', 'space-browse', '', ''),
|
||||
(2148, 'priv', 'fr', 'space-browse', '', ''),
|
||||
(2149, 'priv', 'zh-cn', 'instance-view', '', ''),
|
||||
(2149, 'priv', 'zh-tw', 'instance-view', '', ''),
|
||||
(2149, 'priv', 'de', 'instance-view', '', ''),
|
||||
(2149, 'priv', 'en', 'instance-view', '', ''),
|
||||
(2149, 'priv', 'fr', 'instance-view', '', ''),
|
||||
(2158, 'priv', 'zh-cn', 'store-browse', '', ''),
|
||||
(2158, 'priv', 'zh-tw', 'store-browse', '', ''),
|
||||
(2158, 'priv', 'de', 'store-browse', '', ''),
|
||||
(2158, 'priv', 'en', 'store-browse', '', ''),
|
||||
(2158, 'priv', 'fr', 'store-browse', '', ''),
|
||||
(2159, 'priv', 'zh-cn', 'store-appView', '上架应用详情', ''),
|
||||
(2159, 'priv', 'zh-tw', 'store-appView', '上架應用詳情', ''),
|
||||
(2159, 'priv', 'de', 'store-appView', 'APP Store Detail', ''),
|
||||
(2159, 'priv', 'en', 'store-appView', 'APP Store Detail', ''),
|
||||
(2159, 'priv', 'fr', 'store-appView', 'APP Store Detail', ''),
|
||||
(2150, 'priv', 'zh-cn', 'space-getStoreAppInfo', '接口:获取商店应用信息', ''),
|
||||
(2150, 'priv', 'zh-tw', 'space-getStoreAppInfo', '接口:獲取商店應用信息', ''),
|
||||
(2150, 'priv', 'de', 'space-getStoreAppInfo', 'Api: Get Store APP Info', ''),
|
||||
(2150, 'priv', 'en', 'space-getStoreAppInfo', 'Api: Get Store APP Info', ''),
|
||||
(2150, 'priv', 'fr', 'space-getStoreAppInfo', 'Api: Get Store APP Info', ''),
|
||||
(2151, 'priv', 'zh-cn', 'instance-install', '安装应用', ''),
|
||||
(2151, 'priv', 'zh-tw', 'instance-install', '安裝應用', ''),
|
||||
(2151, 'priv', 'de', 'instance-install', 'Install', ''),
|
||||
(2151, 'priv', 'en', 'instance-install', 'Install', ''),
|
||||
(2151, 'priv', 'fr', 'instance-install', 'Install', ''),
|
||||
(2152, 'priv', 'zh-cn', 'instance-visit', '访问应用', ''),
|
||||
(2152, 'priv', 'zh-tw', 'instance-visit', '訪問應用', ''),
|
||||
(2152, 'priv', 'de', 'instance-visit', 'Visit', ''),
|
||||
(2152, 'priv', 'en', 'instance-visit', 'Visit', ''),
|
||||
(2152, 'priv', 'fr', 'instance-visit', 'Visit', ''),
|
||||
(2153, 'priv', 'zh-cn', 'instance-ajaxStatus', '接口:应用状态', ''),
|
||||
(2153, 'priv', 'zh-tw', 'instance-ajaxStatus', '接口:應用狀態', ''),
|
||||
(2153, 'priv', 'de', 'instance-ajaxStatus', 'Api: App status', ''),
|
||||
(2153, 'priv', 'en', 'instance-ajaxStatus', 'Api: App status', ''),
|
||||
(2153, 'priv', 'fr', 'instance-ajaxStatus', 'Api: App status', ''),
|
||||
(2154, 'priv', 'zh-cn', 'instance-ajaxStart', '开启应用', ''),
|
||||
(2154, 'priv', 'zh-tw', 'instance-ajaxStart', '開啓應用', ''),
|
||||
(2154, 'priv', 'de', 'instance-ajaxStart', 'Start', ''),
|
||||
(2154, 'priv', 'en', 'instance-ajaxStart', 'Start', ''),
|
||||
(2154, 'priv', 'fr', 'instance-ajaxStart', 'Start', ''),
|
||||
(2155, 'priv', 'zh-cn', 'instance-ajaxStop', '关闭应用', ''),
|
||||
(2155, 'priv', 'zh-tw', 'instance-ajaxStop', '關閉應用', ''),
|
||||
(2155, 'priv', 'de', 'instance-ajaxStop', 'Stop', ''),
|
||||
(2155, 'priv', 'en', 'instance-ajaxStop', 'Stop', ''),
|
||||
(2155, 'priv', 'fr', 'instance-ajaxStop', 'Stop', ''),
|
||||
(2157, 'priv', 'zh-cn', 'instance-upgrade', '升级应用', ''),
|
||||
(2157, 'priv', 'zh-tw', 'instance-upgrade', '升級應用', ''),
|
||||
(2157, 'priv', 'de', 'instance-upgrade', 'Upgrade', ''),
|
||||
(2157, 'priv', 'en', 'instance-upgrade', 'Upgrade', ''),
|
||||
(2157, 'priv', 'fr', 'instance-upgrade', 'Upgrade', ''),
|
||||
(2156, 'priv', 'zh-cn', 'instance-ajaxUninstall', '删除应用', ''),
|
||||
(2156, 'priv', 'zh-tw', 'instance-ajaxUninstall', '刪除應用', ''),
|
||||
(2156, 'priv', 'de', 'instance-ajaxUninstall', 'Delete', ''),
|
||||
(2156, 'priv', 'en', 'instance-ajaxUninstall', 'Delete', ''),
|
||||
(2156, 'priv', 'fr', 'instance-ajaxUninstall', 'Delete', ''),
|
||||
(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,
|
||||
@@ -10,7 +568,7 @@ CREATE TABLE IF NOT EXISTS `zt_prompt` (
|
||||
`elaboration` text DEFAULT NULL,
|
||||
`role` text DEFAULT NULL,
|
||||
`characterization` text DEFAULT NULL,
|
||||
`status` enum('draft','active','replaced') NOT NULL DEFAULT 'draft',
|
||||
`status` enum('draft','active') NOT NULL DEFAULT 'draft',
|
||||
`createdBy` varchar(30) NOT NULL,
|
||||
`createdDate` datetime NOT NULL,
|
||||
`editedBy` varchar(30) DEFAULT NULL,
|
||||
@@ -31,202 +589,202 @@ CREATE TABLE IF NOT EXISTS `zt_promptrole` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
REPLACE INTO
|
||||
`zt_priv` (`id`, `module`, `method`, `parent`, `edition`, `vision`, `system`, `order`)
|
||||
`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);
|
||||
(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`)
|
||||
`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);
|
||||
(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`)
|
||||
`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', 'zh-cn', 'ai-promptExecutionReset', '', '');
|
||||
(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`)
|
||||
`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');
|
||||
('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 ('你是一名经验丰富的开发工程师。', '精通多种编程语言和框架、熟悉前后端技术和架构、擅长性能优化和安全防护、熟悉云计算和容器化技术、能够协调多人协作和项目管理。');
|
||||
@@ -236,11 +794,13 @@ 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`) 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');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`) 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');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`) 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');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`) 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');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`) VALUES ('Bug转需求', 0, 'bug', ',bug.title,bug.steps,bug.severity,bug.pri,bug.status,bug.confirmed,bug.type,', 'bug.story/create', '将bug转换为产品需求,表述清晰准确。', '需求描述格式建议使用:作为一名<某种类型的用户>,我希望<达成某些目的>,这样可以<开发的价值>。验收标准建议列举多条。', '请你扮演一名资深的产品经理。', '负责产品战略、设计、开发、数据分析、用户体验、团队管理、沟通协调等方面,需要具备多种技能和能力,以实现产品目标和公司战略。', 'system', '2023-08-10 13:24:14');
|
||||
INSERT INTO `zt_prompt` (`name`, `model`, `module`, `source`, `targetForm`, `purpose`, `elaboration`, `role`, `characterization`, `createdBy`, `createdDate`) 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');
|
||||
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');
|
||||
|
||||
+108
-14
File diff suppressed because one or more lines are too long
+169
@@ -1,3 +1,172 @@
|
||||
2023-08-29 18.7
|
||||
完成的需求
|
||||
开源版:
|
||||
47588 开源版包含企业版AI配置的功能
|
||||
47589 开源版包含企业版语言模型的功能
|
||||
47591 后台实现设置语言模型的权限配置
|
||||
47592 内置需求润色提词
|
||||
47593 内置一键拆用例提词
|
||||
47617 语言模型页面新增升级引导的超链接
|
||||
47664 开源版包含企业版提词的部分功能
|
||||
47680 后台实现发布提词的权限配置
|
||||
47681 后台实现下架提词的权限配置
|
||||
47682 后台实现浏览提词列表的权限配置
|
||||
47683 后台实现查看提词详情的权限配置
|
||||
47989 开源版增加升级引导弹窗组件
|
||||
47991 内置Bug润色提词
|
||||
47992 内置任务润色提词
|
||||
47993 内置需求转任务提词
|
||||
47994 内置Bug转需求提词
|
||||
47995 内置角色模板
|
||||
48079 开源版增加升级引导的内容展示
|
||||
48080 企业版增加升级引导弹窗组件
|
||||
48081 企业版增加升级引导的内容展示
|
||||
48133 开源版、企业版升级引导的英文版内容展示
|
||||
45747 平台合码后将渠成菜单落地在禅道devops平台
|
||||
45925 调整DevOps二级导航栏
|
||||
45928 添加代码库二级导航栏
|
||||
45933 代码页面样式优化
|
||||
45935 添加代码库表单样式优化
|
||||
45936 代码库列表样式优化
|
||||
45939 编辑代码库表单样式优化
|
||||
45944 下载代码弹窗样式调整
|
||||
45934 文件详情页面样式优化
|
||||
45938 批量添加代码库表单样式调整
|
||||
45940 代码库信息同步页面样式调整
|
||||
45942 提交详情页面样式调整
|
||||
45943 文件追溯页面样式调整
|
||||
45946 所有版本页面样式调整
|
||||
45947 比较差异页面样式调整
|
||||
46218 合并请求列表样式优化
|
||||
46219 创建合并请求表单样式优化
|
||||
46220 编辑合并请求表单样式优化
|
||||
46221 合并请求概览页面样式优化
|
||||
46222 合并请求比对代码页面样式优化
|
||||
46223 合并请求关联软件需求页面样式优化
|
||||
46224 合并请求关联Bug页面样式优化
|
||||
46225 合并请求关联任务页面样式优化
|
||||
46227 合并请求关联的软件需求列表页面样式优化
|
||||
46228 合并请求关联的Bug列表页面样式优化
|
||||
46229 合并请求关联的任务列表页面样式优化
|
||||
41289 调整创建合并请求逻辑
|
||||
46203 流水线列表样式优化
|
||||
46204 添加流水线表单样式优化
|
||||
46205 编辑流水线表单样式优化
|
||||
46206 流水线执行日志页面优化
|
||||
46207 流水线执行历史列表样式优化
|
||||
47025 流水线列表1.5级下拉菜单添加产品分组
|
||||
47089 调整编辑合并请求逻辑
|
||||
46725 在后台首页中添加DevOps设置
|
||||
46740 在DevOps设置二级导航栏添加资源
|
||||
46767 在资源下管理机房
|
||||
46768 在资源下管理账号
|
||||
46769 在资源下管理城市
|
||||
46770 在资源下管理服务商
|
||||
46771 在资源下管理CPU品牌
|
||||
46772 在资源下管理系统版本
|
||||
46773 将指令设置迁移至DevOps设置
|
||||
46827 在DevOps设置二级导航栏添加平台
|
||||
46829 在平台下管理数据库
|
||||
46830 在平台下管理域名
|
||||
46832 在平台下管理对象存储
|
||||
47146 在资源下管理主机
|
||||
45503 DevOps二级导航打印应用菜单
|
||||
45504 实现手动添加应用的功能
|
||||
45505 实现应用列表
|
||||
45507 在应用列表页面打印添加应用按钮
|
||||
45535 实现升级应用的功能
|
||||
45536 实现访问应用的功能
|
||||
45537 实现启动应用的功能
|
||||
45538 实现关闭应用的功能
|
||||
45539 实现浏览应用详情的功能
|
||||
45655 实现应用数据库管理的功能
|
||||
45656 实现设置应用的功能
|
||||
45657 实现删除应用的功能
|
||||
47257 实现从应用市场添加应用的功能
|
||||
45508 实现从应用市场安装应用的功能
|
||||
45518 实现应用市场卡片列表功能
|
||||
45521 实现在应用市场浏览应用详情的功能
|
||||
47719 使用q命令行安装DevOps平台版本
|
||||
47838 实现平台仪表盘
|
||||
42964 优化代码库访问权限的设置
|
||||
44546 应用管理支持手工配置Nexus
|
||||
44547 在DevOps视图二级菜单中增加制品库
|
||||
48403 浏览制品库列表
|
||||
48643 添加制品库
|
||||
48645 版本关联制品
|
||||
48720 编辑制品库
|
||||
48724 删除制品库
|
||||
48727 添加制品库权限
|
||||
48732 应用市场添加应用时自定义名称
|
||||
企业版:
|
||||
47671 后台实现下架提词的权限配置
|
||||
47669 后台实现发布提词的权限配置
|
||||
47628 前台使用提词时实现异常状态提示
|
||||
47547 从提词设计器点击去调试按钮跳转至表单页
|
||||
47305 后台权限新增AI配置模块
|
||||
47309 后台实现浏览提词列表的权限配置
|
||||
47310 后台实现查看提词详情的权限配置
|
||||
47315 后台实现创建提词的权限配置
|
||||
47318 后台实现编辑提词的权限配置
|
||||
47319 后台实现设计提词的权限配置
|
||||
47320 后台实现删除提词的权限配置
|
||||
47323 AI配置权限新增提词和语言模型权限包
|
||||
47327 后台实现设置语言模型的权限配置
|
||||
47328 提词应用的使用依赖于结果处理表单页权限
|
||||
45294 提词详情页面提词ID和提词名称的展示
|
||||
47152 前台页面增加 AI 提词的描述展示
|
||||
45329 提词详情页面实现提词设计信息的展示
|
||||
47142 实现提词名称的唯一性
|
||||
45330 提词详情页面实现创建提词功能
|
||||
46852 实现删除提词的二次确认弹窗
|
||||
45420 提词详情页面实现历史记录功能
|
||||
46851 实现下架提词的二次确认弹窗
|
||||
45421 提词详情页面实现基本信息的展示
|
||||
45423 提词详情页面实现选择上一个、下一个提词功能
|
||||
45422 提词详情页面实现悬浮操作栏
|
||||
45292 提词列表展示样式
|
||||
45382 实现将设计的提词封装成提词应用的功能
|
||||
45392 前台页面打印提词应用的下拉菜单按钮
|
||||
45393 前台页面打印提词应用的按钮
|
||||
45435 提词设计中结果处理页面实现下一步和去调试的功能
|
||||
45715 提词前台实现设计提词的弹窗
|
||||
45716 设计提词弹窗实现弹窗标题
|
||||
45718 设计提词弹窗打印关闭按钮
|
||||
45717 实现调试提词后点击保存按钮的返回逻辑
|
||||
45737 提词列表操作栏交互逻辑
|
||||
45738 提词阶段的交互逻辑
|
||||
45719 设计提词弹窗实现参数设置区块
|
||||
45768 提词列表实现统计信息和分页功能
|
||||
45720 设计提词弹窗实现输入给语言模型的内容展示区块
|
||||
45872 后台提词实现点击去调试按钮的跳转规则
|
||||
45409 提词设计器实现返回按钮的交互逻辑
|
||||
45407 提词设计器打印全局返回按钮
|
||||
45766 提词列表实现检索标签功能
|
||||
45285 提词设计器实现流程向导组件的交互逻辑
|
||||
45866 前台实现提词调试页面
|
||||
45960 设计提词弹窗打印保存按钮
|
||||
46883 提词调试页实现发布提词的功能
|
||||
46884 提词调试页实现重新生成提词结果的功能
|
||||
47720 合并提词设计器中指定角色配置项的标题
|
||||
47723 指定角色表单页打印角色模板区块的开关
|
||||
47727 指定角色表单页实现角色模板区块
|
||||
47731 在角色模板区块可以添加角色模板
|
||||
47732 在角色模板区块以卡片的方式浏览模板
|
||||
47741 在角色模板区块可以编辑角色模板
|
||||
47743 在角色模板区块可以删除角色模板
|
||||
47746 设置指定角色时可以应用角色模板
|
||||
47752 指定角色表单页打印存为角色模板表单项
|
||||
47753 隐藏AI配置中GPT-3.5中的“-”
|
||||
47756 优化提词调试页面的按钮及位置
|
||||
47759 调整结果处理页面点击去调试后的交互逻辑与反馈样式
|
||||
47786 将选择对象中的文案从数据改为字段
|
||||
47787 调整选择对象页面中对象的排序
|
||||
47902 调整AI配置卡片的介绍文案
|
||||
修复的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.
|
||||
|
||||
@@ -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,79 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------- 常用函数。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';
|
||||
|
||||
+160
-34
@@ -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,7 +1551,7 @@ 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);
|
||||
@@ -1571,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1674,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;
|
||||
|
||||
@@ -1721,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);
|
||||
}
|
||||
|
||||
@@ -1751,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;
|
||||
@@ -1795,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;
|
||||
@@ -1856,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;
|
||||
@@ -2442,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 ++;
|
||||
}
|
||||
@@ -2770,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;
|
||||
@@ -2790,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}");
|
||||
|
||||
/*
|
||||
@@ -2804,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);
|
||||
@@ -2823,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) return $this->slaveDBH->query($query);
|
||||
|
||||
return $this->dbh->query($query);
|
||||
}
|
||||
|
||||
//-------------------- 错误处理方法(Error methods) ------------------//
|
||||
|
||||
/**
|
||||
@@ -2891,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;
|
||||
@@ -2903,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)
|
||||
{
|
||||
@@ -2915,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;
|
||||
|
||||
/*
|
||||
* 设置错误信息。
|
||||
@@ -2924,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";
|
||||
|
||||
/*
|
||||
* 为了安全起见,对公网环境隐藏脚本路径。
|
||||
@@ -2942,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>";
|
||||
}
|
||||
@@ -2963,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3041,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.
|
||||
*
|
||||
@@ -407,3 +469,252 @@ 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 || !common::hasPriv($module, $method)) return $isClickable;
|
||||
|
||||
$item->actions[] = array('name' => $action, 'disabled' => !$isClickable);
|
||||
|
||||
return $isClickable;
|
||||
}
|
||||
|
||||
@@ -395,4 +395,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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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', '');
|
||||
|
||||
+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;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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,104 @@
|
||||
<?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',
|
||||
'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);
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class docMenu extends wg
|
||||
{
|
||||
private array $modules = array();
|
||||
|
||||
private array $mineTypes = array('mine', 'view', 'collect', 'createdby', 'editedby');
|
||||
|
||||
protected static array $defineProps = array(
|
||||
'modules: array',
|
||||
'activeKey?: int',
|
||||
'settingLink?: string',
|
||||
'menuLink: string',
|
||||
'title?: string',
|
||||
'linkParams?: string="%s"',
|
||||
'libID?: int=0',
|
||||
'moduleID?: int=0',
|
||||
'spaceType?: string',
|
||||
'objectType?: string',
|
||||
'objectID?: int=0',
|
||||
'hover?: bool=true',
|
||||
);
|
||||
|
||||
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 buildLink($item, $releaseID = 0): string
|
||||
{
|
||||
$url = $item->url;
|
||||
if(!empty($url)) return $url;
|
||||
if(in_array($item->type, array('apiLib', 'docLib')))
|
||||
{
|
||||
$this->libID = $item->id;
|
||||
$this->moduleID = 0;
|
||||
}
|
||||
if($item->type == 'module') $this->moduleID = $item->id;
|
||||
|
||||
$linkParams = sprintf($this->linkParams, "libID={$this->libID}&moduleID={$this->moduleID}");
|
||||
if(in_array($this->spaceType, array('product', 'project', 'custom'))) $linkParams = "objectID={$this->objectID}&{$linkParams}";
|
||||
|
||||
$objectType = $this->objectType;
|
||||
|
||||
$moduleName = $this->spaceType == 'api' ? 'api' : 'doc';
|
||||
$methodName = '';
|
||||
if($this->spaceType == 'api')
|
||||
{
|
||||
$methodName = 'index';
|
||||
$linkParams = substr($linkParams, 1);
|
||||
}
|
||||
else if($item->type == 'annex')
|
||||
{
|
||||
$methodName = 'showFiles';
|
||||
$linkParams = "type={$objectType}&objectID={$item->objectID}";
|
||||
}
|
||||
else if(in_array($item->type, array('text', 'word', 'ppt', 'excel')))
|
||||
{
|
||||
$methodName = 'view';
|
||||
$linkParams = "docID={$this->moduleID}";
|
||||
}
|
||||
else if($objectType == 'execution')
|
||||
{
|
||||
$moduleName = 'execution';
|
||||
$methodName = 'doc';
|
||||
}
|
||||
else
|
||||
{
|
||||
$methodName = $this->spaceMethod[$objectType] ? $this->spaceMethod[$objectType] : 'teamSpace';
|
||||
if(in_array($objectType, $this->mineTypes))
|
||||
{
|
||||
$moduleID = $item->id;
|
||||
if(in_array($item->type, array('docLib', 'annex', 'api', 'execution'))) $moduleID = 0;
|
||||
|
||||
$type = in_array(strtolower($item->type), $this->mineTypes) ? strtolower($item->type) : 'mine';
|
||||
$linkParams = "type={$type}&libID={$this->libID}&moduleID={$moduleID}";
|
||||
}
|
||||
if($item->type == 'module' && $item->object == 'api')
|
||||
{
|
||||
$linkParams = str_replace(array('browseType=&', 'param=0'), array('browseType=byrelease&', "param={$this->release}"), $linkParams);
|
||||
}
|
||||
}
|
||||
|
||||
if($releaseID)
|
||||
{
|
||||
if($this->currentModule == 'doc')
|
||||
{
|
||||
$linkParams = str_replace(array('browseType=&', 'param=0'), array('browseType=byrelease&', "param={$releaseID}"), $linkParams);
|
||||
if($this->rawMethod == 'view') $linkParams = "libID={$this->libID}&moduleID=0&browseType=byrelease&orderBy=&status,id_desc¶m={$releaseID}";
|
||||
}
|
||||
else
|
||||
{
|
||||
$linkParams = "libID={$this->libID}&moduleID=0&apiID=0&version=0&release={$releaseID}";
|
||||
}
|
||||
}
|
||||
return helper::createLink($moduleName, $methodName, $linkParams);
|
||||
}
|
||||
|
||||
private function buildMenuTree(array $items, int $parentID = 0): array
|
||||
{
|
||||
if(empty($items)) $items = $this->modules;
|
||||
if(empty($items)) return array();
|
||||
|
||||
$activeKey = $this->prop('activeKey');
|
||||
$parentItems = array();
|
||||
foreach($items as $setting)
|
||||
{
|
||||
if(!is_object($setting)) continue;
|
||||
|
||||
$setting->parentID = $parentID;
|
||||
|
||||
$itemID = 0;
|
||||
if(!in_array(strtolower($setting->type), $this->mineTypes)) $itemID = $setting->id ? $setting->id : $parentID;
|
||||
|
||||
$item = array(
|
||||
'key' => $itemID,
|
||||
'text' => $setting->name,
|
||||
'icon' => $this->getIcon($setting),
|
||||
'url' => $this->buildLink($setting),
|
||||
'attrs' => array('data-app' => $this->tab),
|
||||
'data-id' => $itemID,
|
||||
'data-lib' => in_array($setting->type, array('docLib', 'apiLib')) ? $itemID : $setting->libID,
|
||||
'data-type' => $setting->type,
|
||||
'data-parent' => $setting->parentID,
|
||||
'data-module' => $this->currentModule,
|
||||
'active' => zget($setting, 'active', $itemID == $activeKey),
|
||||
'actions' => $this->getActions($setting)
|
||||
);
|
||||
|
||||
$children = zget($setting, 'children', array());
|
||||
if(!empty($children))
|
||||
{
|
||||
$children = $this->buildMenuTree($children, $itemID);
|
||||
$item['items'] = $children;
|
||||
}
|
||||
|
||||
$parentItems[] = $item;
|
||||
}
|
||||
return $parentItems;
|
||||
}
|
||||
|
||||
private function setMenuTreeProps(): void
|
||||
{
|
||||
global $app, $lang;
|
||||
$this->lang = $lang;
|
||||
$this->tab = $app->tab;
|
||||
$this->rawModule = $app->rawModule;
|
||||
$this->rawMethod = $app->rawMethod;
|
||||
$this->currentModule = $app->moduleName;
|
||||
|
||||
$this->release = $this->prop('release', 0);
|
||||
$this->libID = $this->prop('libID');
|
||||
$this->moduleID = $this->prop('moduleID');
|
||||
$this->modules = $this->prop('modules');
|
||||
$this->linkParams = $this->prop('linkParams', '%s');
|
||||
$this->spaceType = $this->prop('spaceType', '');
|
||||
$this->objectType = $this->prop('objectType', '');
|
||||
$this->objectID = $this->prop('objectID', 0);
|
||||
$this->spaceMethod = $this->prop('spaceMethod');
|
||||
|
||||
if($this->rawModule == 'api' && $this->rawMethod == 'view') $this->spaceType = 'api';
|
||||
if(empty($this->modules['project']))
|
||||
{
|
||||
$this->setProp('items', $this->buildMenuTree(array(), $this->libID));
|
||||
}
|
||||
else
|
||||
{
|
||||
$items = array();
|
||||
$index = 0;
|
||||
foreach($this->modules as $treeType => $modules)
|
||||
{
|
||||
if($treeType == 'project')
|
||||
{
|
||||
$treeTitle = $lang->projectCommon;
|
||||
$treeIcon = 'project';
|
||||
}
|
||||
elseif($treeType == 'execution')
|
||||
{
|
||||
$treeTitle = $lang->execution->common;
|
||||
$treeIcon = 'run';
|
||||
}
|
||||
else
|
||||
{
|
||||
$treeTitle = $lang->files;
|
||||
$treeIcon = 'paper-clip';
|
||||
}
|
||||
$items[] = array(
|
||||
'text' => $treeTitle,
|
||||
'icon' => $treeIcon,
|
||||
'class' => 'project-tree-title ' . ($index > 0 ? 'border-t mt-2 pt-2' : ''),
|
||||
);
|
||||
|
||||
$items = array_merge($items, $this->buildMenuTree($modules, $this->libID));
|
||||
$index ++;
|
||||
}
|
||||
$this->setProp('items', $items);
|
||||
}
|
||||
}
|
||||
|
||||
private function getActions($item): array|null
|
||||
{
|
||||
$versionBtn = array();
|
||||
if(isset($item->versions) && $item->versions)
|
||||
{
|
||||
global $lang;
|
||||
$versionTitle = $lang->build->common;
|
||||
$versionBtn = array(
|
||||
'key' => 'version',
|
||||
'text' => $versionTitle,
|
||||
'type' => 'dropdown',
|
||||
'dropdown' => array(
|
||||
'placement' => 'bottom-end',
|
||||
'items' => array(),
|
||||
)
|
||||
);
|
||||
|
||||
foreach($item->versions as $version)
|
||||
{
|
||||
if($version->id == $this->release) $versionBtn['text'] = $version->version;
|
||||
|
||||
$versionBtn['dropdown']['items'][] = array(
|
||||
'text' => $version->version,
|
||||
'href' => $this->buildLink($item, $version->id),
|
||||
'active' => $version->id == $this->release,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$moreBtn = array();
|
||||
if(!isset($item->hasAction) || $item->hasAction || in_array($item->type, array('mine', 'view', 'collect', 'createdBy', 'editedBy')))
|
||||
{
|
||||
$actions = $this->getOperateItems($item);
|
||||
if($actions)
|
||||
{
|
||||
$moreBtn = array(
|
||||
'key' => 'more',
|
||||
'icon' => 'ellipsis-v',
|
||||
'type' => 'dropdown',
|
||||
'caret' => false,
|
||||
'dropdown' => array(
|
||||
'placement' => 'bottom-end',
|
||||
'items' => $actions,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$actions = array();
|
||||
if($versionBtn) $actions[] = $versionBtn;
|
||||
if($moreBtn) $actions[] = $moreBtn;
|
||||
return $actions ? $actions : null;
|
||||
}
|
||||
|
||||
private function getOperateItems($item): array
|
||||
{
|
||||
$menus = array();
|
||||
if(in_array($item->type, array('docLib', 'apiLib')))
|
||||
{
|
||||
$itemID = $item->id ? $item->id : $item->parentID;
|
||||
if(hasPriv($this->currentModule, 'addCatalog'))
|
||||
{
|
||||
$menus[] = array(
|
||||
'key' => 'adddirectory',
|
||||
'icon' => 'add-directory',
|
||||
'text' => $this->lang->doc->libDropdown['addModule'],
|
||||
'onClick' => jsRaw("() => addModule({$itemID}, 'child')")
|
||||
);
|
||||
}
|
||||
|
||||
if(hasPriv($this->currentModule, 'editCatalog'))
|
||||
{
|
||||
$menus[] = array(
|
||||
'key' => 'editlib',
|
||||
'icon' => 'edit',
|
||||
'text' => $this->lang->doc->libDropdown['editLib'],
|
||||
'data-toggle' => 'modal',
|
||||
'data-url' => createlink($this->currentModule, 'editlib', "libID={$itemID}"),
|
||||
);
|
||||
}
|
||||
|
||||
if(hasPriv($this->currentModule, 'deleteCatalog'))
|
||||
{
|
||||
$menus[] = array(
|
||||
'key' => 'dellib',
|
||||
'icon' => 'trash',
|
||||
'text' => $this->lang->doc->libDropdown['deleteLib'],
|
||||
'class' => 'ajax-submit',
|
||||
'data-url' => createLink($this->currentModule, 'deleteLib', "libID={$itemID}"),
|
||||
'data-confirm' => $this->lang->doc->confirmDeleteLib,
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif($item->type == 'module')
|
||||
{
|
||||
if(hasPriv($this->currentModule, 'addCatalog'))
|
||||
{
|
||||
$menus[] = array(
|
||||
'key' => 'adddirectory',
|
||||
'icon' => 'add-directory',
|
||||
'text' => $this->lang->doc->libDropdown['addSameModule'],
|
||||
'onClick' => jsRaw("() => addModule({$item->id}, 'same')")
|
||||
);
|
||||
$menus[] = array(
|
||||
'key' => 'addsubdirectory',
|
||||
'icon' => 'add-directory',
|
||||
'text' => $this->lang->doc->libDropdown['addSubModule'],
|
||||
'onClick' => jsRaw("() => addModule({$item->id}, 'child')")
|
||||
);
|
||||
}
|
||||
|
||||
if(hasPriv($this->currentModule, 'editCatalog'))
|
||||
{
|
||||
$menus[] = array(
|
||||
'key' => 'editmodule',
|
||||
'icon' => 'edit',
|
||||
'text' => $this->lang->doc->libDropdown['editModule'],
|
||||
'link' => '',
|
||||
'data-toggle' => 'modal',
|
||||
'data-url' => createlink($this->currentModule, 'editCatalog', "moduleID={$item->id}&type=" . ($this->rawModule == 'api' ? 'api' : 'doc')),
|
||||
);
|
||||
}
|
||||
|
||||
if(hasPriv($this->currentModule, 'deleteCatalog'))
|
||||
{
|
||||
$menus[] = array(
|
||||
'key' => 'delmodule',
|
||||
'icon' => 'trash',
|
||||
'text' => $this->lang->doc->libDropdown['delModule'],
|
||||
'class' => 'ajax-submit',
|
||||
'data-url' => createLink($this->currentModule, 'deleteCatalog', "rootID={$item->parentID}&moduleID={$item->id}"),
|
||||
'data-confirm' => $this->lang->api->confirmDeleteLib,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $menus;
|
||||
}
|
||||
|
||||
private function getIcon($item): string
|
||||
{
|
||||
$type = $item->type;
|
||||
if($type == 'apiLib') return 'interface-lib';
|
||||
if($type == 'docLib') return 'wiki-lib';
|
||||
if($type == 'annex') return 'annex-lib';
|
||||
if($type == 'execution') return 'execution';
|
||||
if($type == 'text') return 'file-text';
|
||||
if($type == 'word') return 'file-word';
|
||||
if($type == 'ppt') return 'file-powerpoint';
|
||||
if($type == 'excel') return 'file-excel';
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getTitle(): string
|
||||
{
|
||||
global $lang;
|
||||
$activeKey = $this->prop('activeKey');
|
||||
|
||||
if(empty($activeKey)) return $this->prop('title');
|
||||
|
||||
foreach($this->modules as $module)
|
||||
{
|
||||
if($module->id == $activeKey) return $module->name;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function buildBtns(): wg|null
|
||||
{
|
||||
$settingLink = $this->prop('settingLink');
|
||||
$settingText = $this->prop('settingText');
|
||||
if(!$settingLink) return null;
|
||||
|
||||
global $app;
|
||||
$lang = $app->loadLang('datatable')->datatable;
|
||||
$currentModule = $app->rawModule;
|
||||
$currentMethod = $app->rawMethod;
|
||||
|
||||
if(!$settingText) $settingText = $lang->moduleSetting;
|
||||
|
||||
$datatableId = $app->moduleName . ucfirst($app->methodName);
|
||||
|
||||
return div
|
||||
(
|
||||
setClass('col gap-2 py-3 px-7'),
|
||||
$settingLink
|
||||
? a
|
||||
(
|
||||
setClass('btn'),
|
||||
setStyle('background', '#EEF5FF'),
|
||||
setStyle('box-shadow', 'none'),
|
||||
set('data-app', $app->tab),
|
||||
set('data-size', 'sm'),
|
||||
set('data-toggle', 'modal'),
|
||||
set::href($settingLink),
|
||||
$settingText
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$this->setMenuTreeProps();
|
||||
$title = $this->getTitle();
|
||||
$menuLink = $this->prop('menuLink', '');
|
||||
|
||||
return div
|
||||
(
|
||||
$menuLink ? dropmenu
|
||||
(
|
||||
set::id('docDropmenu'),
|
||||
set::menuID('docDropmenuMenu'),
|
||||
set::text($title),
|
||||
set::url($menuLink),
|
||||
) : null,
|
||||
div
|
||||
(
|
||||
setClass('module-menu rounded shadow-sm bg-white col rounded-sm'),
|
||||
$title && empty($menuLink) ? h::header
|
||||
(
|
||||
setClass('h-10 flex items-center pl-4 flex-none gap-3'),
|
||||
span
|
||||
(
|
||||
setClass('module-title text-lg font-semibold'),
|
||||
html($title)
|
||||
),
|
||||
) : null,
|
||||
h::main
|
||||
(
|
||||
setClass($menuLink ? 'pt-3' : ''),
|
||||
setClass('col flex-auto overflow-y-auto overflow-x-hidden pl-4 pr-1'),
|
||||
zui::tree(set($this->props->pick(array('items', 'activeClass', 'activeIcon', 'activeKey', 'onClickItem', 'defaultNestedShow', 'changeActiveKey', 'isDropdownMenu', 'hover'))))
|
||||
),
|
||||
$this->buildBtns()
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.hold {border: 1px dashed #ccc; box-sizing: border-box;}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class dragUl extends wg
|
||||
{
|
||||
private $ul;
|
||||
|
||||
public static function getPageCSS(): string|false
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
||||
}
|
||||
|
||||
protected function onAddChild($child)
|
||||
{
|
||||
if(!($child instanceof wg)) return false;
|
||||
|
||||
if($child->prop('tagName') !== 'li') return false;
|
||||
|
||||
$child->setProp('draggable', 'true');
|
||||
return $child;
|
||||
}
|
||||
|
||||
private function bindDragstartEvent()
|
||||
{
|
||||
$func = <<<DRAGSTART
|
||||
const dragLi = e.target;
|
||||
dragLi.style.opacity = .5;
|
||||
|
||||
const ul = document.querySelector('[data-zin-gid="{$this->ul->gid}"]');
|
||||
const liList = Array.from(ul.children);
|
||||
ul.dataset.dragIndex = liList.indexOf(dragLi);
|
||||
DRAGSTART;
|
||||
|
||||
$this->ul->add(on::dragstart($func));
|
||||
}
|
||||
|
||||
private function bindDragendEvent()
|
||||
{
|
||||
$func = <<<DRAGEND
|
||||
e.target.style.opacity = '';
|
||||
console.log('dragend');
|
||||
DRAGEND;
|
||||
$this->ul->add(on::dragend($func));
|
||||
}
|
||||
|
||||
private function bindDragoverEvent()
|
||||
{
|
||||
$func = <<<DRAGOVER
|
||||
e.preventDefault();
|
||||
DRAGOVER;
|
||||
$this->ul->add(on::dragover($func));
|
||||
}
|
||||
|
||||
private function bindDragexitEvent()
|
||||
{
|
||||
$func = <<<DRAGEXIT
|
||||
e.preventDefault();
|
||||
DRAGEXIT;
|
||||
$this->ul->add(on::dragexit($func));
|
||||
}
|
||||
|
||||
private function bindDragenterEvent()
|
||||
{
|
||||
$func = <<<DRAGENTER
|
||||
const enterLi = e.target.closest('li');
|
||||
enterLi.classList.add('hold');
|
||||
|
||||
const ul = document.querySelector('[data-zin-gid="{$this->ul->gid}"]');
|
||||
const liList = Array.from(ul.children);
|
||||
ul.dataset.enterIndex = liList.indexOf(enterLi);
|
||||
DRAGENTER;
|
||||
$this->ul->add(on::dragenter($func));
|
||||
}
|
||||
|
||||
private function bindDragleaveEvent()
|
||||
{
|
||||
$func = <<<DRAGLEAVE
|
||||
e.target.classList.remove('hold');
|
||||
DRAGLEAVE;
|
||||
$this->ul->add(on::dragleave($func));
|
||||
}
|
||||
|
||||
private function bindDropEvent()
|
||||
{
|
||||
$func = <<<DROP
|
||||
e.preventDefault();
|
||||
const ul = document.querySelector('[data-zin-gid="{$this->ul->gid}"]');
|
||||
const dragIndex = ul.dataset.dragIndex;
|
||||
const enterIndex = ul.dataset.enterIndex;
|
||||
const dragLi = Array.from(ul.children)[ul.dataset.dragIndex];
|
||||
const enterLi = Array.from(ul.children)[ul.dataset.enterIndex];
|
||||
enterLi.classList.remove('hold');
|
||||
if(dragIndex < enterIndex) {
|
||||
enterLi.after(dragLi);
|
||||
} else if(dragIndex > enterIndex) {
|
||||
enterLi.before(dragLi);
|
||||
}
|
||||
DROP;
|
||||
$this->ul->add(on::drop($func));
|
||||
}
|
||||
|
||||
protected function build(): wg
|
||||
{
|
||||
$ul = ul
|
||||
(
|
||||
setClass('drag-ul'),
|
||||
set($this->getRestProps()),
|
||||
$this->children(),
|
||||
);
|
||||
|
||||
$ul->setProp('data-zin-gid', $ul->gid);
|
||||
$this->ul = $ul;
|
||||
$this->bindDragstartEvent();
|
||||
$this->bindDragendEvent();
|
||||
$this->bindDragoverEvent();
|
||||
$this->bindDragexitEvent();
|
||||
$this->bindDragenterEvent();
|
||||
$this->bindDragleaveEvent();
|
||||
$this->bindDropEvent();
|
||||
return $ul;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
require_once dirname(__DIR__) . DS . 'menu' . DS . 'v1.php';
|
||||
require_once dirname(__DIR__) . DS . 'btn' . DS . 'v1.php';
|
||||
|
||||
class dropdown extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'items?:array',
|
||||
'placement?:string',
|
||||
'strategy?:string',
|
||||
'offset?: int',
|
||||
'flip?: bool',
|
||||
'arrow?: string',
|
||||
'trigger?: string',
|
||||
'menu?: array',
|
||||
'target?: string',
|
||||
'id?: string',
|
||||
'menuClass?: string',
|
||||
'hasIcons?: bool',
|
||||
'staticMenu?: bool'
|
||||
);
|
||||
|
||||
protected static array $defineBlocks = array
|
||||
(
|
||||
'trigger' => array('map' => 'btn,a'),
|
||||
'menu' => array('map' => 'menu'),
|
||||
'items' => array('map' => 'item')
|
||||
);
|
||||
|
||||
protected function build(): array
|
||||
{
|
||||
list($items, $placement, $strategy, $offset, $flip, $arrow, $trigger, $menuProps, $target, $id, $menuClass, $hasIcons, $staticMenu) = $this->prop(array('items', 'placement', 'strategy', 'offset', 'flip', 'arrow', 'trigger', 'menu', 'target', 'id', 'menuClass', 'hasIcons', 'staticMenu'));
|
||||
|
||||
$triggerBlock = $this->block('trigger');
|
||||
$menu = $this->block('menu');
|
||||
$itemsList = $this->block('items');
|
||||
|
||||
if(empty($id)) $id = $this->gid;
|
||||
if(empty($target) && empty($items)) $target = "#$id";
|
||||
if(empty($menuProps)) $menuProps = array();
|
||||
|
||||
if(empty($triggerBlock)) $triggerBlock = h::a($this->children());
|
||||
elseif(is_array($triggerBlock)) $triggerBlock = $triggerBlock[0];
|
||||
$triggerID = '';
|
||||
if($triggerBlock instanceof wg)
|
||||
{
|
||||
if($triggerBlock instanceof btn) $triggerBlock->setDefaultProps(array('caret' => true));
|
||||
$triggerBlock->setProp($this->getRestProps());
|
||||
|
||||
$triggerProps = array
|
||||
(
|
||||
'data-target' => $triggerBlock->hasProp('target', 'href') ? null : $target,
|
||||
'data-toggle' => 'dropdown',
|
||||
'data-placement' => $placement,
|
||||
'data-strategy' => $strategy,
|
||||
'data-offset' => $offset,
|
||||
'data-flip' => $flip,
|
||||
'data-arrow' => $arrow,
|
||||
'data-trigger' => $trigger
|
||||
);
|
||||
$triggerBlock->setProp($triggerProps);
|
||||
|
||||
$triggerID = $triggerBlock->id();
|
||||
if(empty($triggerID))
|
||||
{
|
||||
$triggerID = "$id-toggle";
|
||||
$triggerBlock->setProp('id', $triggerID);
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($menu))
|
||||
{
|
||||
if($staticMenu)
|
||||
{
|
||||
$menu = new menu
|
||||
(
|
||||
setClass('dropdown-menu'),
|
||||
set::items($items),
|
||||
divorce($itemsList),
|
||||
);
|
||||
|
||||
if($hasIcons === null)
|
||||
{
|
||||
if(is_array($items))
|
||||
{
|
||||
foreach($items as $item)
|
||||
{
|
||||
if((is_array($item) and isset($item['icon'])) || (($item instanceof wg) && $item->hasProp('icon')))
|
||||
{
|
||||
$hasIcons = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$hasIcons)
|
||||
{
|
||||
foreach($itemsList as $item)
|
||||
{
|
||||
if(($item instanceof wg) && $item->hasProp('icon'))
|
||||
{
|
||||
$hasIcons = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(empty($items)) $items = array();
|
||||
if(!empty($itemsList))
|
||||
{
|
||||
foreach($itemsList as $item)
|
||||
{
|
||||
if(!($item instanceof item)) continue;
|
||||
$items[] = $item->props->toJSON();
|
||||
}
|
||||
}
|
||||
foreach($items as $index => $item)
|
||||
{
|
||||
if(!isset($item['icon']) || empty($item['icon']) || str_starts_with($item['icon'], 'icon-')) continue;
|
||||
$items[$index]['icon'] = 'icon-' . $item['icon'];
|
||||
}
|
||||
|
||||
if(!is_array($menuProps)) $menuProps = array();
|
||||
$menuProps['items'] = $items;
|
||||
|
||||
$menu = zui::dropdown
|
||||
(
|
||||
set(array
|
||||
(
|
||||
'_to' => "#$triggerID",
|
||||
'trigger' => $trigger,
|
||||
'placement' => $placement,
|
||||
'strategy' => $strategy,
|
||||
'arrow' => $arrow,
|
||||
'flip' => $flip,
|
||||
'offset' => $offset,
|
||||
'target' => $target,
|
||||
'className' => $menuClass,
|
||||
'hasIcons' => $hasIcons,
|
||||
'menu' => $menuProps
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif(is_array($menu))
|
||||
{
|
||||
$menu = $menu[0];
|
||||
}
|
||||
|
||||
if($menu instanceof menu)
|
||||
{
|
||||
$menu->setProp($menuProps);
|
||||
$menu->setProp('class', $menuClass);
|
||||
$menu->setProp('id', $id);
|
||||
if($hasIcons) $menu->setProp('class', 'has-icons');
|
||||
}
|
||||
|
||||
return array($triggerBlock, $menu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pick-pop-admin-menu {width: 130px !important;}
|
||||
#pick-pop-admin-menu .dropmenu-list {padding: 0;}
|
||||
#pick-pop-admin-menu .admin-menu-item > .dropmenu-item {padding-left: 10px !important;}
|
||||
#pick-pop-admin-menu .admin-menu-item > .dropmenu-item.active {background-color: unset;}
|
||||
#pick-pop-admin-menu .admin-menu-item > .dropmenu-item:hover {color: rgba(var(--color-primary-500-rgb),var(--tw-text-opacity)); background-color: rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user