Merge branch '18.x' into zentaoipd

This commit is contained in:
tanghucheng
2023-07-13 17:26:49 +08:00
92 changed files with 5166 additions and 399 deletions
Vendored Executable → Regular
+372 -136
View File
@@ -1,168 +1,404 @@
pipeline {
agent {
kubernetes {
inheritFrom "build-docker code-scan xuanim mysql-server"
inheritFrom "xuanim"
}
}
options {
skipDefaultCheckout()
}
environment {
ZENTAO_VERSION = """${sh(
returnStdout: true,
script: 'cat VERSION'
).trim()}"""
MYSQL_SERVER_HOST = """${sh(
returnStdout: true,
script: 'hostname -I'
).trim()}"""
MYSQL_ROOT_PASSWORD = 'pass4ci'
ZENTAO_RELEASE_PATH = "${WORKSPACE}/release"
MIDDLE_IMAGE_REPO = "hub.qc.oop.cc/zentao-ztf"
MIDDLE_IMAGE_TAG = """${sh(
returnStdout: true,
script: 'echo $BUILD_ID-${GIT_COMMIT}'
).trim()}"""
XUANXUAN_SRC_PATH = "${WORKSPACE}/xuansrc"
SRC_ZDOO_PATH = "${WORKSPACE}/zdoo"
SRC_ZDOOEXT_PATH = "${WORKSPACE}/zdooext"
SRC_ZENTAOEXT_PATH = "${WORKSPACE}/zentaoext"
}
stages {
stage("checkout code") {
steps {
echo "checkout code success"
}
}
stage('Sonar Scanner') {
parallel {
stage('SonarQube') {
steps {
container('sonar') {
withSonarQubeEnv('sonarqube') {
sh 'git config --global --add safe.directory $(pwd)'
sh 'sonar-scanner -Dsonar.inclusions=$(git diff --name-only HEAD~1|tr "\\n" ",") -Dsonar.analysis.user=$(git show -s --format=%an)'
}
}
}
post {
success {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "sonar scanner" --url "https://sonar.qc.oop.cc/dashboard?id=zentaopms&branch=${GIT_BRANCH}" --content "sonar scanner success" --debug --custom'
}
}
failure {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "sonar scanner" --url "https://sonar.qc.oop.cc/dashboard?id=zentaopms&branch=${GIT_BRANCH}" --content "sonar scanner failure" --debug --custom'
}
stage("Package") {
when {
allOf {
buildingTag()
}
beforeAgent true
}
agent {
kubernetes {
inheritFrom "zentao-package build-docker xuanim"
}
}
environment {
MIDDLE_IMAGE_REPO = "hub.qc.oop.cc/zentao-package-ext"
MIDDLE_IMAGE_TAG = """${sh(
returnStdout: true,
script: 'date +%y%m%d%H%M-${BUILD_ID}'
).trim()}"""
}
stages {
stage("Pull") {
steps {
checkout scm
}
}
stage("PullExt") {
environment {
XUANVERSION = """${sh(
returnStdout: true,
script: 'jq -r .pkg.xuanxuan.gitVersion < dependency.json'
).trim()}"""
ZENTAOEXT_VERSION = """${sh(
returnStdout: true,
script: 'jq -r .pkg.zentaoext.gitVersion < dependency.json'
).trim()}"""
ZDOO_VERSION = """${sh(
returnStdout: true,
script: 'jq -r .pkg.zdoo.gitVersion < dependency.json'
).trim()}"""
ZDOOEXT_VERSION = """${sh(
returnStdout: true,
script: 'jq -r .pkg.zdooext.gitVersion < dependency.json'
).trim()}"""
}
steps {
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: 'https://git.zcorp.cc/easycorp/zentaoext.git']]
)
}
}
}
stage('Build Image') {
steps {
container('docker') {
sh 'docker build --pull . -f Dockerfile.test --build-arg VERSION=${ZENTAO_VERSION} --build-arg MIRROR=true --build-arg MYSQL_HOST=${MYSQL_SERVER_HOST} --build-arg MYSQL_PASSWORD=${MYSQL_ROOT_PASSWORD} -t ${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}'
sh 'docker push ${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}'
}
}
post {
success {
echo 'build image success'
}
failure {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "build image" --url "${RUN_DISPLAY_URL}" --content "Build unit test image failure" --debug --custom'
}
}
}
}
}
}
stage("Build") {
environment {
GIT_TAG_BUILD_TYPE = """${sh(
returnStdout: true,
script: 'misc/parse_tag.sh $TAG_NAME type'
).trim()}"""
stage('Unit Test'){
stages{
stage('Init') {
agent {
kubernetes {
inheritFrom "xuanim"
containerTemplate {
name "zentao"
image "${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}"
command "sleep"
args "99d"
}
}
}
options { skipDefaultCheckout() }
steps {
container('zentao') {
sh 'initdb.php ; /apps/zentao/test/ztest init'
}
}
post {
success {
sh 'echo "stage unit init success"'
}
failure {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "unittest init" --url "${RUN_DISPLAY_URL}" --content "Unit test database initialization failed" --debug --custom'
}
}
}
GIT_TAG_BUILD_GROUP = """${sh(
returnStdout: true,
script: 'misc/parse_tag.sh $TAG_NAME group'
).trim()}"""
}
stage('Run') {
matrix {
agent {
kubernetes {
inheritFrom "xuanim"
containerTemplate {
name "zentao"
image "${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}"
command "sleep"
args "99d"
}
}
}
stages {
stage("make ciCommon") {
steps {
sh "echo $GIT_TAG_BUILD_TYPE/abc"
sh "echo $GIT_TAG_BUILD_GROUP/def"
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("encrypt ext code") {
steps {
container('package') {
sh 'cd $SRC_ZENTAOEXT_PATH && make'
sh 'cp ${ZENTAO_BUILD_PATH}/docker/Dockerfile.release.ext ./Dockerfile.release.ext'
}
container('docker') {
sh 'docker build --pull -t ${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG} -f Dockerfile.release.ext ${ZENTAO_RELEASE_PATH}'
sh 'docker push ${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}'
}
}
}
}
}
stage("Publish") {
environment {
PMS_VERSION = """${sh(
returnStdout: true,
script: 'cat ${SRC_ZENTAOEXT_PATH}/VERSION'
).trim()}"""
BIZ_VERSION = """${sh(
returnStdout: true,
script: 'cat ${SRC_ZENTAOEXT_PATH}/BIZVERSION'
).trim()}"""
MAX_VERSION = """${sh(
returnStdout: true,
script: 'cat ${SRC_ZENTAOEXT_PATH}/MAXVERSION'
).trim()}"""
GIT_TAG_BUILD_TYPE = """${sh(
returnStdout: true,
script: 'misc/parse_tag.sh $TAG_NAME type'
).trim()}"""
GIT_TAG_BUILD_GROUP = """${sh(
returnStdout: true,
script: 'misc/parse_tag.sh $TAG_NAME group'
).trim()}"""
OUTPUT_PKG_PATH = "${ZENTAO_RELEASE_PATH}/output"
ARTIFACT_REPOSITORY = """${sh(
returnStdout: true,
script: 'echo easycorp-snapshot'
).trim()}"""
ARTIFACT_HOST = "nexus.qc.oop.cc"
ARTIFACT_PROTOCOL = "https"
ARTIFACT_CRED_ID = "nexus-jenkins"
}
stages {
stage("Merge and Upload") {
matrix {
agent {
kubernetes {
containerTemplate {
name "package"
image "${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}"
command "sleep"
args "99d"
}
inheritFrom "publish-qiniu"
}
}
options { skipDefaultCheckout() }
axes {
axis {
name "ZLANG"
values "cn", "en"
}
axis {
name "PHPVERSION"
values "php5.4_5.6", "php7.0", "php7.1", "php7.2_7.4", "php8.1", "k8s.php7.2_7.4", "k8s.php8.1"
}
}
excludes {
exclude {
axis {
name "SEQUENCE"
values "P1", "P2", "P3", "P4", "P5", "P6", "P7"
name 'ZLANG'
values 'en'
}
axis {
name "PHPVERSION"
values "k8s.php7.2_7.4", "k8s.php8.1"
}
}
}
stages {
stage("Run") {
steps {
container('zentao') {
sh 'initdb.php config'
sh '/apps/zentao/test/ztest extract ; /apps/zentao/test/ztest ${SEQUENCE} | tee /apps/zentao/test/${SEQUENCE}.log'
sh 'pipeline-unittest.sh /apps/zentao/test/${SEQUENCE}.log'
stage("frame") {
environment {
ARTIFACT_NAME = """${sh(
returnStdout: true,
script: 'test ${ZLANG} = cn && echo -n zentaopms || echo -n zentaoalm'
).trim()}"""
}
stages {
stage("package zip") {
steps{
echo "${env.ZLANG} <=> ${env.PHPVERSION}"
container('package') {
sh 'mkdir $ZENTAO_RELEASE_PATH'
sh '${ZENTAO_BUILD_PATH}/package.sh zip'
sh 'mkdir $OUTPUT_PKG_PATH'
sh 'env | grep GIT_TAG'
}
}
}
}
}
post{
success{
container('xuanimbot') {
sh 'git config --global --add safe.directory /home/jenkins/agent/workspace/pangu_pangu_xuanimbot_master'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "unittest" --url "${RUN_DISPLAY_URL}" --content "Unit test passed" --debug --custom'
}
}
failure{
container('xuanimbot') {
}
stage("upload zip") {
steps {
nexusArtifactUploader(
nexusVersion: 'nexus3',
protocol: env.ARTIFACT_PROTOCOL,
nexusUrl: env.ARTIFACT_HOST,
groupId: 'zentao.base' + '.' + env.GIT_TAG_BUILD_GROUP,
version: env.PMS_VERSION,
repository: env.ARTIFACT_REPOSITORY,
credentialsId: env.ARTIFACT_CRED_ID,
artifacts: [
[artifactId: env.ARTIFACT_NAME,
classifier: env.PHPVERSION,
file: env.ZENTAO_RELEASE_PATH + '/base.zip',
type: 'zip']
]
)
nexusArtifactUploader(
nexusVersion: 'nexus3',
protocol: env.ARTIFACT_PROTOCOL,
nexusUrl: env.ARTIFACT_HOST,
groupId: 'zentao.biz' + '.' + env.GIT_TAG_BUILD_GROUP,
version: env.BIZ_VERSION,
repository: env.ARTIFACT_REPOSITORY,
credentialsId: env.ARTIFACT_CRED_ID,
artifacts: [
[artifactId: env.ARTIFACT_NAME,
classifier: env.PHPVERSION,
file: env.ZENTAO_RELEASE_PATH + '/biz.zip',
type: 'zip'],
[artifactId: env.ARTIFACT_NAME + '.update',
classifier: env.PHPVERSION,
file: env.ZENTAO_RELEASE_PATH + '/biz.update.zip',
type: 'zip']
]
)
nexusArtifactUploader(
nexusVersion: 'nexus3',
protocol: env.ARTIFACT_PROTOCOL,
nexusUrl: env.ARTIFACT_HOST,
groupId: 'zentao.max' + '.' + env.GIT_TAG_BUILD_GROUP,
version: env.MAX_VERSION,
repository: env.ARTIFACT_REPOSITORY,
credentialsId: env.ARTIFACT_CRED_ID,
artifacts: [
[artifactId: env.ARTIFACT_NAME,
classifier: env.PHPVERSION,
file: env.ZENTAO_RELEASE_PATH + '/max.zip',
type: 'zip'],
[artifactId: env.ARTIFACT_NAME + '.update',
classifier: env.PHPVERSION,
file: env.ZENTAO_RELEASE_PATH + '/max.update.zip',
type: 'zip']
]
)
sh 'mkdir ${OUTPUT_PKG_PATH}/${PMS_VERSION} ${OUTPUT_PKG_PATH}/${BIZ_VERSION} ${OUTPUT_PKG_PATH}/${MAX_VERSION}'
sh 'cp ${ZENTAO_RELEASE_PATH}/base.zip ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}-${PMS_VERSION}-${PHPVERSION}.zip'
sh 'mv ${ZENTAO_RELEASE_PATH}/biz.zip ${OUTPUT_PKG_PATH}/${BIZ_VERSION}/${ARTIFACT_NAME}-${BIZ_VERSION}-${PHPVERSION}.zip'
sh 'mv ${ZENTAO_RELEASE_PATH}/biz.update.zip ${OUTPUT_PKG_PATH}/${BIZ_VERSION}/${ARTIFACT_NAME}.update-${BIZ_VERSION}-${PHPVERSION}.zip'
sh 'mv ${ZENTAO_RELEASE_PATH}/max.zip ${OUTPUT_PKG_PATH}/${MAX_VERSION}/${ARTIFACT_NAME}-${MAX_VERSION}-${PHPVERSION}.zip'
sh 'mv ${ZENTAO_RELEASE_PATH}/max.update.zip ${OUTPUT_PKG_PATH}/${MAX_VERSION}/${ARTIFACT_NAME}.update-${MAX_VERSION}-${PHPVERSION}.zip'
}
} // End upload zip
stage("syspack") {
when {
buildingTag()
// environment name: 'GIT_TAG_BUILD_TYPE', value: 'release'
}
steps{
sh 'env | grep GIT_TAG'
container('package') {
sh '${ZENTAO_BUILD_PATH}/package.sh deb'
sh '${ZENTAO_BUILD_PATH}/package.sh rpm'
}
}
}
stage("upload syspack") {
when {
environment name: 'GIT_TAG_BUILD_TYPE', value: 'release'
}
steps {
nexusArtifactUploader(
nexusVersion: 'nexus3',
protocol: env.ARTIFACT_PROTOCOL,
nexusUrl: env.ARTIFACT_HOST,
groupId: 'zentao.pms' + '.' + env.GIT_TAG_BUILD_GROUP,
version: env.PMS_VERSION,
repository: env.ARTIFACT_REPOSITORY,
credentialsId: env.ARTIFACT_CRED_ID,
artifacts: [
[artifactId: env.ARTIFACT_NAME,
classifier: env.PHPVERSION + '-1.noarch',
file: env.ZENTAO_RELEASE_PATH + '/zentao.rpm',
type: 'rpm'],
[artifactId: env.ARTIFACT_NAME,
classifier: env.PHPVERSION + '.1.all',
file: env.ZENTAO_RELEASE_PATH + '/zentao.deb',
type: 'deb']
]
)
sh 'mv ${ZENTAO_RELEASE_PATH}/zentao.rpm ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}-${PMS_VERSION}-${PHPVERSION}-1.noarch.rpm'
sh 'mv ${ZENTAO_RELEASE_PATH}/zentao.deb ${OUTPUT_PKG_PATH}/${PMS_VERSION}/${ARTIFACT_NAME}-${PMS_VERSION}-${PHPVERSION}.1.all.deb'
}
} // End upload syspack cn
stage("Upload Qiniu") {
when {
environment name: 'GIT_TAG_BUILD_TYPE', value: 'release'
}
environment {
QINIU_BUCKET = "qisytest"
OBJECT_KEY_PREFIX = "zentao/"
QINIU_ACCESS_KEY = credentials('qiniu-upload-ak')
QINIU_SECRET_KEY = credentials('qiniu-upload-sk')
}
steps {
sh 'ls -l ${OUTPUT_PKG_PATH}'
container('qiniu') {
sh "qshell account $QINIU_ACCESS_KEY $QINIU_SECRET_KEY uploader"
sh "qshell qupload2 --bucket ${QINIU_BUCKET} --overwrite --src-dir ${OUTPUT_PKG_PATH} --key-prefix ${OBJECT_KEY_PREFIX}"
}
}
}
} // end stages
} // end stage frame
} // End matrix stages
} // End matrix
} // End Merge and Upload Max
stage("Notice") {
steps {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "unittest" --url "${RUN_DISPLAY_URL}" --content "Unit test failed" --debug --custom'
//sh '/usr/local/bin/xuanimbot --users "qishiyao" --title "zentao build with tag ${TAG_NAME} success" --url ""${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/#browse/browse:${ARTIFACT_REPOSITORY}:zentao"" --content "zentaopms build success, click buttom below for browse artifacts" --debug --custom'
sh '/usr/local/bin/xuanimbot --users "qishiyao" --groups "fced7fb3-0d48-449f-b408-ecae52a50f89" --title "zentao build with tag ${TAG_NAME} success" --url ""${ARTIFACT_PROTOCOL}://${ARTIFACT_HOST}/#browse/browse:${ARTIFACT_REPOSITORY}:zentao"" --content "zentaopms build success, click buttom below for browse artifacts" --debug --custom'
}
}
}
}
}
}//End unittest
}
}
} // End Root Stages
} // End pipeline
} // end publish
}
} // end package
}
}
+168
View File
@@ -0,0 +1,168 @@
pipeline {
agent {
kubernetes {
inheritFrom "build-docker code-scan xuanim mysql-server"
}
}
environment {
ZENTAO_VERSION = """${sh(
returnStdout: true,
script: 'cat VERSION'
).trim()}"""
MYSQL_SERVER_HOST = """${sh(
returnStdout: true,
script: 'hostname -I'
).trim()}"""
MYSQL_ROOT_PASSWORD = 'pass4ci'
MIDDLE_IMAGE_REPO = "hub.qc.oop.cc/zentao-ztf"
MIDDLE_IMAGE_TAG = """${sh(
returnStdout: true,
script: 'echo $BUILD_ID-${GIT_COMMIT}'
).trim()}"""
}
stages {
stage("checkout code") {
steps {
echo "checkout code success"
}
}
stage('Sonar Scanner') {
parallel {
stage('SonarQube') {
steps {
container('sonar') {
withSonarQubeEnv('sonarqube') {
sh 'git config --global --add safe.directory $(pwd)'
sh 'sonar-scanner -Dsonar.inclusions=$(git diff --name-only HEAD~1|tr "\\n" ",") -Dsonar.analysis.user=$(git show -s --format=%an)'
}
}
}
post {
success {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "sonar scanner" --url "https://sonar.qc.oop.cc/dashboard?id=zentaopms&branch=${GIT_BRANCH}" --content "sonar scanner success" --debug --custom'
}
}
failure {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "sonar scanner" --url "https://sonar.qc.oop.cc/dashboard?id=zentaopms&branch=${GIT_BRANCH}" --content "sonar scanner failure" --debug --custom'
}
}
}
}
stage('Build Image') {
steps {
container('docker') {
sh 'docker build --pull . -f Dockerfile.test --build-arg VERSION=${ZENTAO_VERSION} --build-arg MIRROR=true --build-arg MYSQL_HOST=${MYSQL_SERVER_HOST} --build-arg MYSQL_PASSWORD=${MYSQL_ROOT_PASSWORD} -t ${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}'
sh 'docker push ${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}'
}
}
post {
success {
echo 'build image success'
}
failure {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "build image" --url "${RUN_DISPLAY_URL}" --content "Build unit test image failure" --debug --custom'
}
}
}
}
}
}
stage('Unit Test'){
stages{
stage('Init') {
agent {
kubernetes {
inheritFrom "xuanim"
containerTemplate {
name "zentao"
image "${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}"
command "sleep"
args "99d"
}
}
}
options { skipDefaultCheckout() }
steps {
container('zentao') {
sh 'initdb.php ; /apps/zentao/test/ztest init'
}
}
post {
success {
sh 'echo "stage unit init success"'
}
failure {
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "unittest init" --url "${RUN_DISPLAY_URL}" --content "Unit test database initialization failed" --debug --custom'
}
}
}
}
stage('Run') {
matrix {
agent {
kubernetes {
inheritFrom "xuanim"
containerTemplate {
name "zentao"
image "${MIDDLE_IMAGE_REPO}:${MIDDLE_IMAGE_TAG}"
command "sleep"
args "99d"
}
}
}
options { skipDefaultCheckout() }
axes {
axis {
name "SEQUENCE"
values "P1", "P2", "P3", "P4", "P5", "P6", "P7"
}
}
stages {
stage("Run") {
steps {
container('zentao') {
sh 'initdb.php config'
sh '/apps/zentao/test/ztest extract ; /apps/zentao/test/ztest ${SEQUENCE} | tee /apps/zentao/test/${SEQUENCE}.log'
sh 'pipeline-unittest.sh /apps/zentao/test/${SEQUENCE}.log'
}
}
}
}
}
post{
success{
container('xuanimbot') {
sh 'git config --global --add safe.directory /home/jenkins/agent/workspace/pangu_pangu_xuanimbot_master'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "unittest" --url "${RUN_DISPLAY_URL}" --content "Unit test passed" --debug --custom'
}
}
failure{
container('xuanimbot') {
sh 'git config --global --add safe.directory $(pwd)'
sh '/usr/local/bin/xuanimbot --users "$(git show -s --format=%an)" --title "unittest" --url "${RUN_DISPLAY_URL}" --content "Unit test failed" --debug --custom'
}
}
}
}//End unittest
}
}
} // End Root Stages
} // End pipeline
+8 -8
View File
@@ -1,6 +1,6 @@
VERSION = $(shell head -n 1 VERSION)
XUANVERSION = $(shell head -n 1 xuanxuan/XUANVERSION)
XVERSION = $(shell head -n 1 xuanxuan/XVERSION)
XUANVERSION = $(shell jq -r .pkg.xuanxuan.gitVersion < dependency.json)
XVERSION = $(shell jq -r .pkg.xuanxuan.ersion < dependency.json)
XHPROF_VERSION = 2.3.9
XUANPATH := $(XUANXUAN_SRC_PATH)
@@ -68,9 +68,9 @@ zentaoxx:
mkdir -p zentaoxx/db
mkdir -p zentaoxx/www
mkdir -p zentaoxx/extension/xuan/common/ext/model/
cd $(XUANPATH); git pull; git archive --format=zip --prefix=xuan/ $(XUANVERSION) > xuan.zip
cd $(XUANPATH); git archive --format=zip --prefix=xuan/ $(XUANVERSION) > xuan.zip
mv $(XUANPATH)/xuan.zip .
unzip xuan.zip
unzip -q xuan.zip
cp xuan/xxb/config/ext/_0_xuanxuan.php zentaoxx/config/ext/
cp -r xuan/xxb/lib/phpaes zentaoxx/lib/
cp -r xuan/xxb/framework/xuanxuan.class.php zentaoxx/framework/
@@ -98,6 +98,8 @@ zentaoxx:
cp -r $(XUAN_WEB_PATH) zentaoxx/www/data/xuanxuan/
mv zentaoxx/db/ zentaoxx/db_bak
mkdir zentaoxx/db/
sed -i "s/datetime NOT NULL DEFAULT '0000-00-00 00:00:00'/datetime NULL/" zentaoxx/db_bak/*.sql
sed -i "/`xxb_user` ADD `clientStatus`/d; /`xxb_user` ADD `clientLang`/d; /`xxb_file` CHANGE `pathname`/d" zentaoxx/db_bak/xuanxuan.sql
cp zentaoxx/db_bak/upgradexuanxuan*.sql zentaoxx/db_bak/xuanxuan.sql zentaoxx/db/
rm -rf zentaoxx/db_bak/
sed -i "s/\$$accountAdmin = \$$this->dao->select('account, admin')->from(TABLE_USER)->where('id')->eq(\$$userID)->fetch();/\$$accountAdmin = \$$this->dao->select('account')->from(TABLE_USER)->where('id')->eq(\$$userID)->fetch();\n\$$sysAdmins = \$$this->dao->select('admins')->from(TABLE_COMPANY)->where('id')->eq(\$$this->app->company->id)->fetch('admins');\n\$$sysAdminArray = explode(',', \$$sysAdmins);\n\$$accountAdmin->admin = in_array(\$$accountAdmin->account, \$$sysAdminArray) ? 'super' : '';\n/" zentaoxx/extension/xuan/im/model/chat.php
@@ -132,6 +134,7 @@ zentaoxx:
sed -i '/xxb_entry/d' zentaoxx/db/*.sql
sed -i '/deviceToken/d' zentaoxx/db/*.sql
sed -i '/deviceType/d' zentaoxx/db/*.sql
sed -i 's/xxb_/zt_/g' zentaoxx/db/*.sql
sed -i "/fetch('push', 'pushMessage');/d" zentaoxx/extension/xuan/im/control.php
#sed -i "s/marked\.html\.php';?>/marked\.html\.php';?>\n<div id='mainMenu' class='clearfix'><div class='btn-toolbar pull-left'><?php common::printAdminSubMenu('xuanxuan');?><\/div><\/div>/g" zentaoxx/extension/xuan/client/view/checkupgrade.html.php
sed -i '/var serverVersions/d' zentaoxx/extension/xuan/client/js/checkupgrade.js
@@ -146,7 +149,7 @@ zentaoxx:
sed -i 's/v\.//g' zentaoxx/extension/xuan/client/js/checkupgrade.js
sed -i 's/commonModel::getLicensePropertyValue/extCommonModel::getLicensePropertyValue/g' zentaoxx/extension/xuan/im/control.php
sed -i 's/commonModel::getLicensePropertyValue/extCommonModel::getLicensePropertyValue/g' zentaoxx/extension/xuan/im/model/conference.php
sed -i 's/xxb_/zt_/g' zentaoxx/db/*.sql
sed -i 's/commonModel::isLicensedMethod([^\)]*)/false/g' zentaoxx/extension/xuan/conference/model.php
sed -i "s#\$this->app->getModuleRoot() . 'im/apischeme.json'#\$this->app->getExtensionRoot() . 'xuan/im/apischeme.json'#g" zentaoxx/extension/xuan/im/model.php
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
@@ -154,7 +157,6 @@ zentaoxx:
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
echo "ALTER TABLE \`zt_user\` ADD \`pinyin\` varchar(255) NOT NULL DEFAULT '' AFTER \`realname\`;" >> zentaoxx/db/xuanxuan.sql
find zentaoxx/extension/xuan/ -name '*.php' -exec sed -i -r 's|->ne(["'\'']0000-00-00 00:00:00["'\''])|->notZeroDatetime()|g; s|["'\'']\)->eq\(["'\'']0000-00-00( 00:00:00)?| is null|g; s|([=!]=) ?["'\'']0000-00-00( 00:00:00)?["'\'']|\1 null|g; s|([^!=]=) ?["'\'']0000-00-00( 00:00:00)?["'\'']|\1 null|g; s|(["'\''])(,.*)\)->eq\(["'\'']0000-00-00( 00:00:00)?["'\'']| is null\1\2|g; s|["'\'']0000-00-00 00:00:00["'\'']|null|g' {} +
mkdir zentaoxx/tools; cp tools/cn2tw.php zentaoxx/tools; cd zentaoxx/tools; php cn2tw.php
cp tools/en2other.php zentaoxx/tools; cd zentaoxx/tools; php en2other.php ../
@@ -258,8 +260,6 @@ enrpm:
cleanAssets:
find zentaopms/ -type f \( -name "*.js.map" -o -name "*.gz" \) -delete
ciCommon:
make clean
git pull
make common
ifneq ($(XUANPATH), )
+1 -1
View File
@@ -1 +1 @@
18.4
18.5
+1 -1
View File
@@ -92,6 +92,6 @@ class bugsEntry extends entry
$bug = $this->loadModel('bug')->getByID($data->id);
return $this->send(200, $this->format($bug, 'activatedDate:time,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,mailto:userList,resolvedBy:user,resolvedDate:time,closedBy:user,closedDate:time,lastEditedBy:user,lastEditedDate:time,deadline:date,deleted:bool'));
return $this->send(201, $this->format($bug, 'activatedDate:time,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,mailto:userList,resolvedBy:user,resolvedDate:time,closedBy:user,closedDate:time,lastEditedBy:user,lastEditedDate:time,deadline:date,deleted:bool'));
}
}
+3 -1
View File
@@ -51,7 +51,9 @@ class buildsEntry extends entry
if(!$projectID) $projectID = $this->param('project', 0);
$project = $this->loadModel('project')->getByID($projectID);
if(!$project) return $this->send404();
if(!$project) return $this->sendError(404, 'Error project id');
$this->setPost('project', $projectID);
$fields = 'execution,product,name,builder,date,scmPath,filePath,desc,branch';
$this->batchSetPost($fields);
+3 -1
View File
@@ -119,8 +119,10 @@ class executionEntry extends entry
{
$oldExecution = $this->loadModel('execution')->getByID($executionID);
$useCode = $this->checkCodeUsed();
/* Set $_POST variables. */
$fields = 'project,code,name,begin,end,lifetime,desc,days,acl,status,PO,PM,QD,RD';
$fields = 'project,name,begin,end,lifetime,desc,days,acl,status,PO,PM,QD,RD';
if($useCode) $fields .= 'code';
$this->batchSetPost($fields, $oldExecution);
$this->setPost('whitelist', $this->request('whitelist', explode(',', $oldExecution->whitelist)));
+11 -5
View File
@@ -55,8 +55,6 @@ class executionsEntry extends entry
$result = array();
foreach($data->data->executionStats as $execution)
{
foreach($execution->hours as $field => $value) $execution->$field = $value;
$execution = $this->filterFields($execution, 'id,name,project,code,type,parent,begin,end,status,openedBy,openedDate,delay,progress,children,' . $appendFields);
$result[] = $this->format($execution, 'openedBy:user,openedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,PM:user,PO:user,RD:user,QD:user,whitelist:userList,begin:date,end:date,realBegan:date,realEnd:date,deleted:bool');
}
@@ -80,7 +78,10 @@ class executionsEntry extends entry
*/
public function post($projectID = 0)
{
$fields = 'project,code,name,begin,end,lifetime,desc,days,percent,parent';
$useCode = $this->checkCodeUsed();
$fields = 'project,name,begin,end,lifetime,desc,days,percent,parent';
if($useCode) $fields .= 'code';
$this->batchSetPost($fields);
$projectID = $this->param('project', $projectID);
@@ -94,7 +95,12 @@ class executionsEntry extends entry
$this->setPost('products', $this->request('products', array()));
$this->setPost('plans', $this->request('plans', array()));
$control = $this->loadController('execution', 'create'); $this->requireFields('name,code,begin,end,days');
$control = $this->loadController('execution', 'create');
$requireFields = 'name,begin,end';
if($useCode) $requireFields .= ',code';
$this->requireFields($requireFields);
$control->create($projectID);
$data = $this->getData();
@@ -122,7 +128,7 @@ class executionsEntry extends entry
$account = $this->app->user->account;
$projects = $data->data->projects;
$dropMenu = array('involved' => array(), 'other' => array(), 'closed' => array());
foreach($data->data->executions as $projectID => $projectExecutions)
foreach($data->data->projectExecutions as $projectID => $projectExecutions)
{
foreach($projectExecutions as $execution)
{
+1 -3
View File
@@ -28,10 +28,8 @@ class feedbackEntry extends entry
$feedback = $data->data->feedback;
$feedback->publicStatus = $feedback->public;
$feedback->productName = $data->data->product;
$feedback->productName = is_object($data->data->product) ? $data->data->product->name : $data->data->product;
$feedback->moduleName = isset($data->data->modulePath[0]->name) ? $data->data->modulePath[0]->name : '/';
$feedback->resultType = $data->data->type;
if(isset($feedback->resultInfo) and $feedback->resultInfo->deleted == 0) $feedback->resultStatus = $this->loadModel('feedback')->processStatus($feedback->resultType, $feedback->resultInfo);
if(!$data or !isset($data->status)) return $this->send400('error');
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
+1
View File
@@ -21,6 +21,7 @@ class feedbacksEntry extends entry
{
if(strpos(strtolower($this->param('fields')), 'moduleandproduct') !== false) return $this->getModuleAndProduct();
$this->app->session->set('feedbackProduct', 'all');
$control = $this->loadController('feedback', 'admin');
$control->admin($this->param('status', 'unclosed'), 0, $this->param('orderBy', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
$data = $this->getData();
+2
View File
@@ -107,10 +107,12 @@ class productEntry extends entry
*/
public function put($productID)
{
$useCode = $this->checkCodeUsed();
$oldProduct = $this->loadModel('product')->getByID($productID);
/* Set $_POST variables. */
$fields = 'program,line,name,PO,QD,RD,type,desc,whitelist,status,acl';
if($useCode) $fields .= ',code';
$this->batchSetPost($fields, $oldProduct);
$control = $this->loadController('product', 'edit');
+1 -1
View File
@@ -85,7 +85,7 @@ class productplansEntry extends entry
$plan = $this->loadModel('productplan')->getByID($data->id);
$plan->stories = array();
$plan->bugs = array();
return $this->send(200, $this->format($plan, 'begin:date,end:date,deleted:bool,project:int'));
return $this->send(201, $this->format($plan, 'begin:date,end:date,deleted:bool,project:int'));
}
$this->sendError(400, array('message' => isset($data->message) ? $data->message : 'error'));
+9 -3
View File
@@ -112,14 +112,20 @@ class productsEntry extends entry
*/
public function post()
{
$fields = 'program,code,line,name,PO,QD,RD,type,desc,whitelist';
$useCode = $this->checkCodeUsed();
$fields = 'program,line,name,PO,QD,RD,type,desc,whitelist';
if($useCode) $fields .= ',code';
$this->batchSetPost($fields);
$this->setPost('acl', $this->request('acl', 'private'));
$this->setPost('whitelist', $this->request('whitelist', array()));
$control = $this->loadController('product', 'create');
$this->requireFields('name,code');
$requireFields = 'name';
if($useCode) $requireFields .= ',code';
$this->requireFields($requireFields);
$control->create($this->request('program', 0));
@@ -130,7 +136,7 @@ class productsEntry extends entry
$product = $this->loadModel('product')->getByID($data->id);
$product = $this->format($product, 'createdDate:time,whitelist:userList,createdBy:user,PO:user,RD:user,QD:user');
return $this->send(200, $product);
return $this->send(201, $product);
}
/**
+3 -1
View File
@@ -45,7 +45,9 @@ class programEntry extends entry
$control = $this->loadController('program', 'edit');
$control->edit($programID);
$this->getData();
$data = $this->getData();
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
$program = $this->program->getByID($programID);
return $this->send(200, $this->format($program, 'begin:date,end:date,PO:user,PM:user,QD:user,RD:user,realBegan:date,realEnd:date,openedBy:user,openedDate:time,lastEditedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,deleted:bool,whitelist:userList'));
}
+1 -4
View File
@@ -28,19 +28,16 @@ class programsEntry extends entry
if(stripos(strtolower(",{$fields},"), ",dropmenu,") !== false) return $this->getDropMenu();
$program = $this->loadController('program', 'browse');
$program->browse($this->param('status', 'all'), $this->param('order', 'order_asc'));
$program->browse($this->param('status', 'all'), $this->param('order', 'order_asc'), 0, 10000, 1);
$data = $this->getData();
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
$programs = $data->data->programs;
$progressList = $data->data->progressList;
$users = $data->data->users;
$result = array();
foreach($programs as $program)
{
if(isset($progressList->{$program->id})) $program->progress = $progressList->{$program->id};
$program = $this->format($program, 'begin:date,end:date,PO:user,PM:user,QD:user,RD:user,realBegan:date,realEnd:date,openedBy:user,openedDate:time,lastEditedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,deleted:bool,whitelist:userList');
if($mergeChildren)
+4 -2
View File
@@ -101,8 +101,10 @@ class projectEntry extends entry
$oldProject = $this->loadModel('project')->getByID($projectID);
$linkedProducts = $this->loadModel('product')->getProducts($projectID);
$useCode = $this->checkCodeUsed();
/* Set $_POST variables. */
$fields = 'name,code,begin,end,acl,parent,desc,PM,whitelist,model';
$fields = 'name,begin,end,acl,parent,desc,PM,whitelist,model';
if($useCode) $fields .= ',code';
$this->batchSetPost($fields, $oldProject);
$products = array();
@@ -119,7 +121,7 @@ class projectEntry extends entry
$control->edit($projectID);
$data = $this->getData();
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
if(!isset($data->result)) return $this->sendError(400, 'error');
$project = $this->project->getByID($projectID);
+8 -4
View File
@@ -48,8 +48,6 @@ class projectsEntry extends entry
$result = array();
foreach($data->data->projectStats as $project)
{
foreach($project->hours as $field => $value) $project->$field = $value;
$result[] = $this->format($project, 'openedBy:user,openedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,realBegan:date,realEnd:date,PM:user,whitelist:userList,deleted:bool');
}
@@ -79,8 +77,11 @@ class projectsEntry extends entry
{
$fields = 'name,begin,end,products';
$this->batchSetPost($fields);
if(isset($_POST['products'])) $_POST['hasProduct'] = true;
$this->setPost('code', $this->request('code', ''));
$useCode = $this->checkCodeUsed();
if($useCode) $this->setPost('code', $this->request('code', ''));
$this->setPost('acl', $this->request('acl', 'private'));
$this->setPost('parent', $this->request('program', 0));
$this->setPost('whitelist', $this->request('whitelist', array()));
@@ -89,7 +90,10 @@ class projectsEntry extends entry
$this->setPost('parent', $this->request('parent', 0));
$control = $this->loadController('project', 'create');
$this->requireFields('name,code,begin,end,products');
$requireFields = 'name,begin,end,products';
if($useCode) $requireFields .= ',code';
$this->requireFields($requireFields);
$control->create($this->request('model', 'scrum'));
+2 -2
View File
@@ -162,7 +162,7 @@ class reportsEntry extends entry
$newProject->id = $project->id;
$newProject->name = $project->name;
$newProject->status = $project->status;
$newProject->progress = round($project->hours->progress, 1);
$newProject->progress = isset($project->progress) ? $project->progress : round($project->hours->progress, 1);
$newProject->totalConsumed = round($project->hours->totalConsumed, 1);
$newProject->totalLeft = round($project->hours->totalLeft, 1);
if(isset($project->delay)) $newProject->delay = $project->delay;
@@ -206,7 +206,7 @@ class reportsEntry extends entry
$newExecution->id = $execution->id;
$newExecution->name = $execution->name;
$newExecution->status = $execution->status;
$newExecution->progress = round($execution->hours->progress, 1);
$newExecution->progress = isset($execution->progress) ? $execution->progress : round($execution->hours->progress, 1);
$newExecution->totalConsumed = round($execution->hours->totalConsumed, 1);
$newExecution->totalLeft = round($execution->hours->totalLeft, 1);
if(isset($execution->delay)) $newExecution->delay = $execution->delay;
+1 -1
View File
@@ -92,6 +92,6 @@ class storiesEntry extends entry
if(isset($data->result) and !isset($data->id)) return $this->sendError(400, $data->message);
$story = $this->loadModel('story')->getByID($data->id);
return $this->send(200, $this->format($story, 'openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,reviewedBy:user,reviewedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,deleted:bool,mailto:userList'));
return $this->send(201, $this->format($story, 'openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,reviewedBy:user,reviewedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,deleted:bool,mailto:userList'));
}
}
+2 -2
View File
@@ -104,8 +104,8 @@ class storyEntry extends entry
$data = $this->getData();
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
if(!isset($data->status)) return $this->sendError(400, 'error');
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
if(!isset($data->result)) return $this->sendError(400, 'error');
$story = $this->story->getByID($storyID);
return $this->send(200, $this->format($story, 'openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,reviewedBy:user,reviewedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,deleted:bool,mailto:userList'));
+4 -3
View File
@@ -28,17 +28,18 @@ class taskFinishEntry extends entry
$fields = 'finishedDate,comment';
$this->batchSetPost($fields);
$realStarted = $this->request('realStarted', (isset($task->realStarted) and !helper::isZeroDate($task->realStarted)) ? $task->realStarted : '');
if($realStarted) $this->setPost('realStarted', $realStarted);
$this->setPost('currentConsumed', $this->request('currentConsumed', 0));
$this->setPost('consumed', $this->request('currentConsumed', 0) + $task->consumed);
$control = $this->loadController('task', 'finish');
$this->requireFields('assignedTo,currentConsumed,realStarted,finishedDate');
$this->requireFields('currentConsumed,realStarted,finishedDate');
$control->finish($taskID);
$data = $this->getData();
if(!$data) return $this->send400('error');
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
$task = $this->loadModel('task')->getByID($taskID);
+1 -1
View File
@@ -30,7 +30,7 @@ class taskPauseEntry extends entry
$data = $this->getData();
if(!$data) return $this->send400('error');
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
$task = $this->loadModel('task')->getByID($taskID);
+1 -1
View File
@@ -52,7 +52,7 @@ class taskRecordEstimateEntry extends entry
{
if($this->loadModel('effort'))
{
$fields = 'id,dates,consumed,left,objectType,objectID,work';
$fields = 'id,dates,consumed,left,objectType,objectID,work,date';
$this->batchSetPost($fields);
$control = $this->loadController('effort', 'createForObject');
$control->createForObject('task', $taskID);
+1 -1
View File
@@ -34,7 +34,7 @@ class taskRestartEntry extends entry
$data = $this->getData();
if(!$data) return $this->send400('error');
if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
$task = $this->loadModel('task')->getByID($taskID);
+3 -2
View File
@@ -104,8 +104,9 @@ class tasksEntry extends entry
$fields = 'name,type,assignedTo,estimate,story,execution,project,module,pri,desc,estStarted,deadline,mailto,team,teamEstimate,multiple,uid';
$this->batchSetPost($fields);
$assignedTo = $this->request('assignedTo');
if($assignedTo and !is_array($assignedTo)) $this->setPost('assignedTo', array($assignedTo));
$assignedTo = $this->request('assignedTo', array(0 => ''));
if($assignedTo and !is_array($assignedTo)) $assignedTo = array($assignedTo);
$this->setPost('assignedTo', $assignedTo);
if($this->request('multiple'))
{
if(count($this->request('team')) != count($this->request('teamEstimate'))) return $this->sendError(400, 'Arrays team and teamEstimate should be the same length');
+4 -4
View File
@@ -22,17 +22,17 @@ class taskStartEntry extends entry
{
$task = $this->loadModel('task')->getByID($taskID);
$fields = 'assignedTo,realStarted,consumed,left,comment';
$fields = 'assignedTo,consumed,left,comment';
$this->batchSetPost($fields);
$this->setPost('realStarted', $this->request('realStarted', helper::now()));
$control = $this->loadController('task', 'start');
$this->requireFields('left');
$control->start($taskID);
$data = $this->getData();
if(!$data) return $this->send400('error');
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
$task = $this->loadModel('task')->getByID($taskID);
+1 -1
View File
@@ -166,7 +166,7 @@ class userEntry extends entry
{
$myExecutions['owner'] = array();
$myExecutions['other'] = array();
foreach($data->data->executions as $projectID => $projectExecutions)
foreach($data->data->projectExecutions as $projectID => $projectExecutions)
{
foreach($projectExecutions as $execution)
{
+1 -1
View File
@@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}}
if(!function_exists('getWebRoot')){function getWebRoot(){}}
/* 基本设置。Basic settings. */
$config->version = '18.4'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->version = '18.5'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->liteVersion = '1.2'; // 迅捷版版本。 The version of Lite.
$config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP.
$config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time.
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -214,9 +214,10 @@ CREATE TABLE IF NOT EXISTS `zt_privrelation` (
UNIQUE KEY `privrelation`(`priv`, `type`, `relationPriv`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 'CREATE TABLE `zt_chart_back` SELECT * FROM `zt_chart`;' will raise MySQL GTID consistency violation error.
CREATE TABLE `zt_chart_back` LIKE `zt_chart`;
INSERT INTO `zt_chart_back` SELECT * FROM `zt_chart`;
-- 'CREATE TABLE `zt_chart_back` SELECT * FROM `zt_chart`;' maybe raise MySQL GTID consistency violation error.
-- 'CREATE TABLE `zt_chart_back` LIKE `zt_chart`;INSERT INTO `zt_chart_back` SELECT * FROM `zt_chart`' maybe raise MYSQL Insert value list does not match column list ;
CREATE TABLE `zt_chart_back` SELECT * FROM `zt_chart`;
ALTER TABLE `zt_chart` MODIFY `fields` mediumtext NULL;
ALTER TABLE `zt_chart` MODIFY `group` varchar(255) NOT NULL;
+25 -21
View File
@@ -1,27 +1,26 @@
INSERT INTO `zt_priv` (`module`, `method`, `parent`, `edition`, `vision`, `system`, `order`) VALUES ('traincourse', 'cloudImport', '125', ',biz,max,ipd,', ',rnd,', '1', '10');
INSERT INTO `zt_priv` (`module`, `method`, `parent`, `edition`, `vision`, `system`, `order`) VALUES ('dataview', 'export', '650', ',biz,max,ipd,', ',rnd,', '1', '30');
REPLACE INTO `zt_priv` (`module`, `method`, `parent`, `edition`, `vision`, `system`, `order`) VALUES ('traincourse', 'cloudImport', '125', ',biz,max,ipd,', ',rnd,', '1', '10');
REPLACE INTO `zt_priv` (`module`, `method`, `parent`, `edition`, `vision`, `system`, `order`) VALUES ('dataview', 'export', '650', ',biz,max,ipd,', ',rnd,', '1', '30');
INSERT INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES
(2107, 'priv', 'de', 'traincourse-cloudImport', '', ''),
(2107, 'priv', 'en', 'traincourse-cloudImport', '', ''),
(2107, 'priv', 'fr', 'traincourse-cloudImport', '', ''),
(2107, 'priv', 'zh-cn', 'traincourse-cloudImport', '', ''),
(2107, 'priv', 'zh-tw', 'traincourse-cloudImport', '', ''),
(2108, 'priv', 'de', 'dataview-export', '', ''),
(2108, 'priv', 'en', 'dataview-export', '', ''),
(2108, 'priv', 'fr', 'dataview-export', '', ''),
(2108, 'priv', 'zh-cn', 'dataview-export', '', ''),
(2108, 'priv', 'zh-tw', 'dataview-export', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2107, 'priv', 'de', 'traincourse-cloudImport', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2107, 'priv', 'en', 'traincourse-cloudImport', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2107, 'priv', 'fr', 'traincourse-cloudImport', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2107, 'priv', 'zh-cn', 'traincourse-cloudImport', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2107, 'priv', 'zh-tw', 'traincourse-cloudImport', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2108, 'priv', 'de', 'dataview-export', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2108, 'priv', 'en', 'dataview-export', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2108, 'priv', 'fr', 'dataview-export', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2108, 'priv', 'zh-cn', 'dataview-export', '', '');
REPLACE INTO `zt_privlang` (`objectID`, `objectType`, `lang`, `key`, `value`, `desc`) VALUES (2108, 'priv', 'zh-tw', 'dataview-export', '', '');
INSERT INTO zt_privmanager (id, parent, code, `type`, edition, vision, `order`) VALUES (650, 445, '', 'package', ',biz,max,ipd,', ',rnd,', 20);
INSERT INTO zt_privlang (objectID, objectType, lang, `key`, value, `desc`) VALUES(650, 'manager', 'zh-cn', '', '导出数据表', '');
REPLACE INTO zt_privmanager (id, parent, code, `type`, edition, vision, `order`) VALUES (650, 445, '', 'package', ',biz,max,ipd,', ',rnd,', 20);
REPLACE INTO zt_privlang (objectID, objectType, lang, `key`, value, `desc`) VALUES(650, 'manager', 'zh-cn', '', '导出数据表', '');
INSERT INTO zt_privrelation (priv, `type`, relationPriv) VALUES(2108, 'depend', 1648);
INSERT INTO zt_privrelation (priv, `type`, relationPriv) VALUES(2108, 'depend', 1651);
REPLACE INTO zt_privrelation (priv, `type`, relationPriv) VALUES(2108, 'depend', 1648);
REPLACE INTO zt_privrelation (priv, `type`, relationPriv) VALUES(2108, 'depend', 1651);
ALTER TABLE `zt_traincourse` ADD `importedStatus` enum('wait','doing','done') NOT NULL DEFAULT 'wait' AFTER `desc`;
ALTER TABLE `zt_traincourse` ADD `lastUpdatedTime` int UNSIGNED NOT NULL DEFAULT 0 AFTER `importedStatus`;
ALTER TABLE `zt_traincourse` MODIFY COLUMN `code` varchar(255) DEFAULT '' NOT NULL;
ALTER TABLE `zt_traincourse` ADD `importedStatus` enum('','wait','doing','done') NOT NULL DEFAULT '';
ALTER TABLE `zt_traincourse` ADD `lastUpdatedTime` int UNSIGNED NOT NULL DEFAULT 0;
ALTER TABLE `zt_traincourse` MODIFY `code` varchar(255) NOT NULL DEFAULT '';
UPDATE `zt_im_chat` SET `createdDate` = NULL WHERE `createdDate` = '0000-00-00 00:00:00';
UPDATE `zt_im_chat` SET `editedDate` = NULL WHERE `editedDate` = '0000-00-00 00:00:00';
@@ -47,4 +46,9 @@ UPDATE `zt_im_userdevice` SET `validUntil` = NULL WHERE `validUntil`
UPDATE `zt_im_userdevice` SET `lastLogin` = NULL WHERE `lastLogin` = '0000-00-00 00:00:00';
UPDATE `zt_im_userdevice` SET `lastLogout` = NULL WHERE `lastLogout` = '0000-00-00 00:00:00';
CREATE INDEX `project` ON `zt_project` (`project`);
CREATE INDEX `project` ON `zt_project` (`project`);
UPDATE `zt_privlang` SET `value`='删除风险' WHERE `objectID`=197 AND `objectType`='manager';
UPDATE `zt_auditcl` SET `deleted` = '0' WHERE `deleted` = '';
UPDATE `zt_workflowdatasource` SET `vision` = 'rnd' WHERE `code` IN ('products','projects','productLines','stories','tasks','bugs','groups','users','branches','builds','modules','plans','productType','productStatus','productAcl','projectType','projectStatus','projectAcl','releaseStatus','storySource','storyPri','storyStatus','storyStage','bugSeverity','bugPri','bugType','bugOs','bugBrowser','bugStatus','taskType','taskPri','taskStatus','testcasePri','testcaseType','testcaseStage','testcaseStatus','testtaskPri','testtaskStatus','feedbackStatus','bugResolution','cases','feedbackModules','storyType','executions','projectModel','feedbackType','feedbackSolution','feedbackclosedReason','taskReason','testsuiteAuth','programs','storyClosedReason');
+85 -61
View File
File diff suppressed because one or more lines are too long
+17
View File
@@ -0,0 +1,17 @@
{
"pkg": {
"xuanxuan": {
"gitVersion": "4786a9a7555ef5cdc6f8b84049d44e2add643a23",
"version": "7.1"
},
"zentaoext": {
"gitVersion": "18.x"
},
"zdoo": {
"gitVersion": "master"
},
"zdooext": {
"gitVersion": "master"
}
}
}
+109 -34
View File
@@ -1,3 +1,78 @@
2023-07-05 18.5
完成的需求
开源版:
45633 优化产品模块中用户需求列表性能
45632 优化产品模块中研发需求列表性能
45630 优化项目列表性能
45256 版本数据过多影响bug编辑页面加载速度
企业版:
46105 从云端导入课程完成后给用户邮件通知
45927 后台BI权限实现数据表的导出权限
45924 数据表浏览页面实现导出功能
45498 学堂导入课程默认为上线状态
45497 学堂后台课程列表中增加默认引导语
45496 私有部署禅道实现从云端导入课程功能
45268 项目型项目支持反馈功能
44991 yaml课程打包工具支持打包PDF文件
44918 调整授权到期规则,授权到期前30天在登录页面给用户提示
43764 学堂后台课程列表中打印从云端导入按钮
40829 学堂播放器支持PDF文件播放功能
修复的Bug
开源版:
36161 运营界面点击任务默认跳转到看板视图
36142 产品列表中需求完成率计算不对
企业版:
36057 透视表前台筛选条件能不正常查询
36218 scrum项目型项目反馈转需求,点击反馈详情页历史记录的超链接报错
禅道客户端:
13775 成员被踢出讨论组后,还停留在讨论组会话页面
15925 内网喧喧有新消息时不提醒
16398 iOS端音视频会议没有声音
16720 喧喧聊天窗口中的聊天记录的滚动条体验需要增强
16806 跳转到某个比较老的消息后,会话列表消息中间会缺失一段范围
17240 移动端登录后未读消息的情况与桌面端未读情况不一致
17617 喧喧后台运行时再次打开喧喧有用户正在会议中实际没有在进行会议
20444 右击复制聊天窗口收到的图片执行失败
21380 喧喧文件查看卡白
21533 设置白名单弹窗宽度过大且文字不居中
21587 移动端收到的新消息少了一条
21602 消息记录中存在搜索不到消息的情况
21610 偶发新的消息只在左侧消息栏中部分显示
21782 部门结构和按钮样式不统一
21864 应用标签的前后按钮和窗口操作项重叠
22053 从聊天列表中移除自己多次才可以生效
22176 群合并人员退出群组白名单展示不对
23356 消息记录搜索后无法收起下拉框
26134 群管理员不能移除用户
26215 6.2版本群设置白名单成员不成功
27093 群组重命名后名字没有刷新
27329 讨论组消息记录窗口白屏
28232 打开固定到左侧边栏的应用时“一口X”会遮挡应用
28907 群组归档完取消归档转发之前的消息界面崩溃(偶发)
29117 开始会议挂断后一分钟后频繁提示连接超时
29145 @成员的时候错误地把在群里的成员标红色
29257 分组出现空分组
29262 群管理--搜索的时候下方出现横线
29391 低版本的xxc小喧喧提示小喧喧头像展示需优化
29392 打开小喧喧对话框,发送指令,小喧喧的头像和名称展示有误
29393 小喧喧头像展示不对
29394 进入授权信息bot输入指令提示指令不存在
29395 讨论组右上角下拉菜单出现横线
29480 禅道客户端--小喧喧会话进入内置应用退出不了
30293 两个用户同时退出讨论组点击两次才可以完全退出
30738 小喧喧提醒左侧消息栏不展示该条信息
31173 查看小喧喧消息记录提示获取消息记录失败
31185 非admin账号私人会话协作文档入口没有了
31340 会议共享屏幕非常模糊
31590 freemind预览,没有展示图标
31593 freemind预览中,文字颜色与原文件不一致
32612 7.1版本喧喧用户点击查看图片放大、缩小按钮置灰不能点击
32661 喧喧客户端登录内网,会话界面报错
32808 讨论组成员列表群主右键踢人弹窗不展示有报错。
32855 云端同步的数据量大的时候,喧喧客户端会卡顿,点击取消同步无响应
33105 客户端7.1发着送信息过程中掉线了点重新发送提示需优化
36060 文件应用,只显示了一个image.png
2023-06-14 18.4
修复的Bug
开源版:
@@ -3617,29 +3692,29 @@
2021-12-06 16.0.beat1
完成的需求
14005 能够按照平台分支浏览用户需求
14005 能够按照平台分支浏览用户需求
14429 父需求支持变更
14761 选择某一个分支之后点击其他菜单希望能够保持分支下拉菜单状态
14771 启用多分支功能后增加分支字样显示的控制
14772 需求修改所属分支时如果有冲突给出提示
14778 分支设置默认分支功能
14877 根据分支提示上次版本计划名称
14761 选择某一个分支之后点击其他菜单希望能够保持分支下拉菜单状态
14771 启用多分支功能后增加分支字样显示的控制
14772 需求修改所属分支时如果有冲突给出提示
14778 分支设置默认分支功能
14877 根据分支提示上次版本计划名称
15134 员工负载表增加备注说明
15470 看板可以设置一个单元格里面卡片的排列方式
15478 我的地盘bug列表增加确认字段
25665 梳理项目和执行关联产品分支的逻辑
25666 可以将某一个分支关闭
25667 将所有分支概念改为主干并处理历史数据
25670 多分支产品浏览需求的时候将所有和主干区分开
25673 分支增加排序功能
15478 我的地盘bug列表增加确认字段
25665 梳理项目和执行关联产品分支的逻辑
25666 可以将某一个分支关闭
25667 将所有分支概念改为主干并处理历史数据
25670 多分支产品浏览需求的时候将所有和主干区分开
25673 分支增加排序功能
27484 文档库右侧目录增加文件的浮动操作菜单
27485 文档库右侧目录增加目录的浮动操作菜单
27486 wiki目录中,当鼠标悬浮到文章名称时展示文章快捷操作
27487 wiki目录中,鼠标悬浮到具体章节时展示章节的快捷操作
27927 执行看板默认只显示一屏滚动时再往下加载
28098 需求批量转任务时对已经转换过任务的需求进行提示
28131 待处理任务列表中子任务名称前显示父任务
28138 员工负载表中增加项目名称信息
28098 需求批量转任务时对已经转换过任务的需求进行提示
28131 待处理任务列表中子任务名称前显示父任务
28138 员工负载表中增加项目名称信息
28147 统计列表中具备横向滚动条的列表可以直接展示出来
28153 Bug创建表中优化执行查询条件
28235 关闭项目时增加实际完成日期的记录
@@ -3648,9 +3723,9 @@
28238 关闭执行时增加实际完成日期的记录
28239 执行工时统计中增加实际开始和实际完成的信息
28310 未开始的项目
28466 测试Bug列表中增加按所属项目的搜索条件
28509 项目概况中增加项目代号的显示
28510 项目列表中增加项目代号字段显示
28466 测试Bug列表中增加按所属项目的搜索条件
28509 项目概况中增加项目代号的显示
28510 项目列表中增加项目代号字段显示
28719 项目看板中看板列显示数量汇总
28727 计划创建迭代时选择项目点击下一步后自动关闭选择弹窗
28809 执行看板中进行中项目排序规则调整
@@ -3665,29 +3740,29 @@
28816 产品看板中调整项目集和产品的排序规则
28817 项目集看板中调整项目集和产品的排序规则
28818 项目集看板中看板列显示数量汇总
28819 调整项目集/产品/项目/执行重名规则
28833 创建执行页面可以直接维护相关负责人信息
28819 调整项目集/产品/项目/执行重名规则
28833 创建执行页面可以直接维护相关负责人信息
28834 项目的分组权限中移除无用的模块和方法
28835 关联多个产品的项目也可以选择归并方式
28904 优化项目列表“我参与的”筛选结果 结
28928 可以设置哪些人不需要提醒
28929 编辑bug修改状态时如果未修改成功希望有错误提示
28931 调整分支管理功能
28932 实现分支的批量操作
28941 重新登陆系统后产品下拉菜单应当保持上次选中的产品名称
28931 调整分支管理功能
28932 实现分支的批量操作
28941 重新登陆系统后产品下拉菜单应当保持上次选中的产品名称
28944 在开源版中增加瀑布模型的相关功能
28959 老版本升级新版本数据归并的时候,可以修改项目、执行名称。
28960 调整产品列表页面的宽度展示比例
28965 实现分支的新增/编辑操作
28965 实现分支的新增/编辑操作
28970 需求列表批量选中希望可以通过shift快捷键操作
28972 产品列表左侧的模块设置调整为显示设置
28973 提交Bug时分支与影响版本要跟着联动
28974 产品列表批量调整计划时在计划名称前显示分支信息
28972 产品列表左侧的模块设置调整为显示设置
28973 提交Bug时分支与影响版本要跟着联动
28974 产品列表批量调整计划时在计划名称前显示分支信息
28993 创建或者编辑用例时调整所属需求的查询逻辑
29000 产品列表中调整字段显示宽度
29002 动态支持按正序/倒序切换排序
29030 已挂起的项目/执行下面的任务不在待办/待处理的任务中展示
29059 多分支产品批量创建需求时分支字段设为默认项
29059 多分支产品批量创建需求时分支字段设为默认项
29069 组织团队成员任务列表增加优先级排序功能
29074 用户下拉菜单中增加人员的头像/部门/职位信息
29108 提交/编辑软件需求页面中增加反馈者和通知邮箱信息
@@ -3696,12 +3771,12 @@
29127 计划的Bug列表中增加批量编辑和调整计划功能
29131 产品概况中增加评审人员的设置
29134 看板页面的刷新不要抖动用更加平滑的方式呈现
29141 启用多分支功能后要用例增加分支字样显示的控制
29142 启用多分支功能后要Bug增加分支字样显示的控制
29143 批量创建需求/BUG/用例时所属模块选择同上,需要检测上一行数据的所属分支和当前行的所属分支是否一致
29144 所有分支下批量编辑需求/BUG/用例,所属模块数据默认为所有分支的模块时,需要处理分支与模块不一致的问题
29145 项目和执行关联了产品的多个分支及计划时调整概况页面显示方式
29147 产品下软件需求列表需求名称前显示主干的分支名
29141 启用多分支功能后要用例增加分支字样显示的控制
29142 启用多分支功能后要Bug增加分支字样显示的控制
29143 批量创建需求/BUG/用例时所属模块选择同上,需要检测上一行数据的所属分支和当前行的所属分支是否一致
29144 所有分支下批量编辑需求/BUG/用例,所属模块数据默认为所有分支的模块时,需要处理分支与模块不一致的问题
29145 项目和执行关联了产品的多个分支及计划时调整概况页面显示方式
29147 产品下软件需求列表需求名称前显示主干的分支名
29156 启用多分支功能后项目需求列表增加是否显示分支名
29157 启用多分支功能后执行需求列表增加是否显示分支名
29158 启用多分支功能后执行任务列表增加是否显示分支名
+13 -6
View File
@@ -450,12 +450,6 @@ class baseEntry
$this->setPost($field, $value);
}
/* Use htmlspecialcharts for rich text fields. */
foreach($fields as $field)
{
if(in_array($field, array('desc', 'spec', 'verify', 'steps')) and isset($_POST[$field])) $_POST[$field] = htmlspecialchars($_POST[$field]);
}
}
/**
@@ -481,6 +475,19 @@ class baseEntry
}
}
/**
* 检查是否在后台启用了代号.
* Check whether config->setCode are used in product,project,execution.
*
* @access public
* @return bool
*/
public function checkCodeUsed()
{
return isset($this->config->setCode) ? $this->config->setCode : 0;
}
/**
* 格式化数据的字段类型.
* Format fields of response data.
+34
View File
@@ -279,4 +279,38 @@ class api extends router
return $output;
}
/**
* 设置vision。
* set Debug.
*
* @access public
* @return void
*/
public function setVision()
{
$account = isset($_SESSION['user']) ? $_SESSION['user']->account : '';
if(empty($account) and isset($_POST['account'])) $account = $_POST['account'];
if(empty($account) and isset($_GET['account'])) $account = $_GET['account'];
$vision = 'rnd';
if($this->config->installed and validater::checkAccount($account))
{
$sql = new sql();
$account = $sql->quote($account);
$user = $this->dbh->query("SELECT * FROM " . TABLE_USER . " WHERE account = $account AND deleted = '0' LIMIT 1")->fetch();
if(!empty($user->visions))
{
$userVisions = explode(',', $user->visions);
if(!in_array($vision, $userVisions)) $vision = '';
if(empty($vision)) list($vision) = $userVisions;
}
}
list($defaultVision) = explode(',', trim($this->config->visions, ','));
if($vision and strpos($this->config->visions, ",{$vision},") === false) $vision = $defaultVision;
$this->config->vision = $vision ? $vision : $defaultVision;
}
}
+65 -1
View File
@@ -268,7 +268,10 @@ class dbh
case 'SELECT':
return $this->formatField($sql);
case 'REPLACE':
$sql = str_replace('REPLACE', 'INSERT', $sql);
$result = $this->processReplace($sql);
if($result != $sql) return $result;
$sql = str_replace('REPLACE', 'INSERT', $sql);
$action = 'INSERT';
case 'INSERT':
case 'UPDATE':
@@ -472,6 +475,67 @@ class dbh
return $sql;
}
/**
* Process replace into sql.
*
* @param mixed $sql
* @access public
* @return void
*/
public function processReplace($sql)
{
// 解析REPLACE INTO语句,提取出表名、字段和值
$matches = [];
preg_match('/^REPLACE\s+INTO\s+`?([\w_]+)`?\s*\((.*)\)\s+VALUES\s*\((.*?)\)\s*$/i', $sql, $matches);
$table_name = $matches[1];
$columns = array_map('trim', explode(',', $matches[2]));
$values = array_map('trim', explode(', ', $matches[3]));
if($table_name == '' or $columns == '' or $values == '') return $sql;
// 构造SELECT语句,查询数据是否存在
$where = [];
foreach ($columns as $index => $column) {
$value = trim($values[$index], "'");
$column = trim($column, '`');
$values[$index] = $value;
$columns[$index] = $column;
$where[] = "`$column` = '$value'";
}
$select_sql = "SELECT * FROM `$table_name` WHERE " . implode(' AND ', $where);
$result = $this->query($select_sql);
$result = $result->fetchAll();
$sql = in_array('id', $columns) ? "SET IDENTITY_INSERT `$table_name` ON;" : '';
if($result)
{
// 数据已存在,构造UPDATE语句并执行
$set = [];
$where = [];
foreach ($columns as $index => $column) {
$value = $values[$index];
$set[] = "`$column` = '$value'";
$where[] = "`$column` = '$value'";
}
$sql .= "UPDATE `$table_name` SET " . implode(', ', $set) . " WHERE " . implode(' AND ', $where);
}
else
{
// 数据不存在,构造INSERT INTO语句并执行
$selectColumn = array();
$selectValue = array();
foreach ($columns as $index => $column) {
$selectColumn[] .= "`$column`";
$selectValue[] .= "'{$values[$index]}'";
}
$sql .= "INSERT INTO `$table_name` (" . implode(', ', $selectColumn) . ") VALUES (" . implode(', ', $selectValue) . ")";
}
return $sql;
}
/**
* Convert alter table sql.
*
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
TAG=$1
FIELD=$2
frameCount=`echo $TAG| grep -o '_' | wc -l`
build_date=${TAG##*_}
build_id=''
if [ $frameCount -eq 3 ];then
build_id=`echo $TAG | cut -d _ -f 3`
fi
case $FIELD in
date)
# must 8 numbers
echo $build_date | egrep -E '^[0-9]{8}$'
;;
id)
echo $build_id
;;
group)
if [ -n "$build_id" ];then
echo "$build_date.$build_id"
else
echo "$build_date.release"
fi
;;
type)
if [ -n "$build_id" ];then
echo snapshot
else
echo release
fi
;;
esac
+1
View File
@@ -383,6 +383,7 @@ $lang->action->label->estimated = 'estimated';
$lang->action->label->reviewpassed = 'Pass';
$lang->action->label->reviewrejected = 'Reject';
$lang->action->label->reviewclarified = 'Clarify';
$lang->action->label->reviewreverted = 'Revert';
$lang->action->label->commitsummary = 'Commit Summary';
$lang->action->label->updatetrainee = 'Update Trainee';
$lang->action->label->setdefaultbranch = 'Set default branch';
+1
View File
@@ -383,6 +383,7 @@ $lang->action->label->estimated = 'estimated';
$lang->action->label->reviewpassed = 'Pass';
$lang->action->label->reviewrejected = 'Reject';
$lang->action->label->reviewclarified = 'Clarify';
$lang->action->label->reviewreverted = 'Revert';
$lang->action->label->commitsummary = 'Commit Summary';
$lang->action->label->updatetrainee = 'Update Trainee';
$lang->action->label->setdefaultbranch = 'Set default branch';
+1
View File
@@ -383,6 +383,7 @@ $lang->action->label->estimated = 'estimated';
$lang->action->label->reviewpassed = 'Pass';
$lang->action->label->reviewrejected = 'Reject';
$lang->action->label->reviewclarified = 'Clarify';
$lang->action->label->reviewreverted = 'Revert';
$lang->action->label->commitsummary = 'Commit Summary';
$lang->action->label->updatetrainee = 'Update Trainee';
$lang->action->label->setdefaultbranch = 'Set default branch';
+1
View File
@@ -383,6 +383,7 @@ $lang->action->label->estimated = '估算了';
$lang->action->label->reviewpassed = '确认通过';
$lang->action->label->reviewrejected = '拒绝了';
$lang->action->label->reviewclarified = '有待明确';
$lang->action->label->reviewreverted = '撤销变更';
$lang->action->label->commitsummary = '提交了培训总结';
$lang->action->label->updatetrainee = '更新了培训人员';
$lang->action->label->setdefaultbranch = '设置了默认分支';
+2 -2
View File
@@ -587,7 +587,7 @@ class api extends control
if($changes)
{
$actionID = $this->action->create('api', $apiID, 'edited');
$actionID = $this->action->create('api', $apiID, 'edited', '', '', '', false);
$this->action->logHistory($actionID, $changes);
}
@@ -630,7 +630,7 @@ class api extends control
$api = $this->api->create();
if($api === false) return $this->sendError(dao::getError());
$this->action->create('api', $api->id, 'Created');
$this->action->create('api', $api->id, 'Created', '', '', '', false);
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => 'true', 'callback' => "parentLocate({$api->id}, {$api->lib}, {$api->module})"));
return $this->sendSuccess(array('locate' => helper::createLink('api', 'index', "libID={$api->lib}&moduleID={$api->module}&apiID={$api->id}")));
+1 -1
View File
@@ -314,7 +314,7 @@ if($this->app->tab == 'project') js::set('objectID', $bug->project);
</tr>
<tr>
<th><?php echo $lang->bug->testtask;?></th>
<td id='testtaskBox'><?php echo html::select('testtask', array(), $bug->testtask, 'class="form-control picker-select"');?></td>
<td id='testtaskBox'><?php echo html::select('testtask', array('' => ''), $bug->testtask, 'class="form-control picker-select"');?></td>
</tr>
<tr>
<th><?php echo $lang->bug->fromCase;?></th>
+1 -1
View File
@@ -205,7 +205,7 @@ $lang->scrum->menu = new stdclass();
$lang->scrum->menu->index = array('link' => "{$lang->dashboard}|project|index|project=%s");
$lang->scrum->menu->execution = array('link' => "$lang->executionCommon|project|execution|status=all&projectID=%s", 'exclude' => 'execution-testreport', 'subModule' => 'task');
$lang->scrum->menu->storyGroup = array('link' => "{$lang->common->story}|projectstory|story|projectID=%s&product=%s",'class' => 'dropdown dropdown-hover', 'exclude' => 'tree-browse');
$lang->scrum->menu->story = array('link' => "$lang->SRCommon|projectstory|story|projectID=%s", 'subModule' => 'projectstory,tree', 'alias' => 'story,track');
$lang->scrum->menu->story = array('link' => "$lang->SRCommon|projectstory|story|projectID=%s", 'subModule' => 'projectstory,tree', 'alias' => 'story,track', 'exclude' => 'tree-browse');
$lang->scrum->menu->projectplan = array('link' => "{$lang->productplan->shortCommon}|projectplan|browse|productID=%s", 'subModule' => 'productplan');
$lang->scrum->menu->doc = array('link' => "{$lang->doc->common}|doc|projectSpace|objectID=%s", 'subModule' => 'doc,api');
$lang->scrum->menu->qa = array('link' => "{$lang->qa->common}|project|bug|projectID=%s", 'subModule' => 'testcase,testtask,bug,testreport', 'alias' => 'bug,testtask,testcase,testreport', 'exclude' => 'execution-create,execution-batchedit');
+3
View File
@@ -2328,6 +2328,7 @@ EOF;
->andWhere($titleField)->in($titles)
->andWhere($dateField)->ge($date)->fi()
->beginIF($condition)->andWhere($condition)->fi()
->beginIF($type == 'story')->andWhere('type')->eq($data->type)
->fetchPairs();
if($duplicate and is_string($titles)) return array('stop' => true, 'duplicate' => key($duplicate));
@@ -3040,6 +3041,8 @@ EOF;
*/
public static function canBeChanged($module, $object = null)
{
if(defined('RUN_MODE') && RUN_MODE == 'api') return true;
global $app, $config;
static $productsStatus = array();
static $executionsStatus = array();
+1 -1
View File
@@ -171,7 +171,7 @@ class cronModel extends model
*/
public function checkChange()
{
$updatedCron = $this->dao->select('*')->from(TABLE_CRON)->where('lastTime')->eq('0000-00-00 00:00:00')->andWhere('status')->ne('stop')->fetch();
$updatedCron = $this->dao->select('*')->from(TABLE_CRON)->where('lastTime')->notZeroDatetime()->andWhere('status')->ne('stop')->fetch();
return $updatedCron ? true : false;
}
+3 -1
View File
@@ -380,11 +380,13 @@ class doc extends control
$doclib = $this->loadModel('doc')->getLibById($libID);
$canVisit = true;
if(!empty($doclib->groups)) $groupAccounts = $this->loadModel('group')->getGroupAccounts(explode(',', $doclib->groups));
switch($objectType)
{
case 'custom':
$account = (string)$this->app->user->account;
if(($doclib->acl == 'custom' or $doclib->acl == 'private') and strpos($doclib->users, $account) === false and $doclib->addedBy !== $account) $canVisit = false;
if(($doclib->acl == 'custom' or $doclib->acl == 'private') and strpos($doclib->users, $account) === false and $doclib->addedBy !== $account and !(isset($groupAccounts) and in_array($account, $groupAccounts, true))) $canVisit = false;
break;
case 'product':
$canVisit = $this->loadModel('product')->checkPriv($doclib->product);
+8 -1
View File
@@ -1865,7 +1865,7 @@ class docModel extends model
if(!$this->checkPrivDoc($doc)) unset($docs[$id]);
}
$bugIdList = $testReportIdList = $caseIdList = $storyIdList = $planIdList = $releaseIdList = $executionIdList = $taskIdList = $buildIdList = $issueIdList = $meetingIdList = $designIdList = $reviewIdList = 0;
$bugIdList = $testReportIdList = $caseIdList = $storyIdList = $planIdList = $releaseIdList = $executionIdList = $taskIdList = $buildIdList = $testtaskIdList = $issueIdList = $meetingIdList = $designIdList = $reviewIdList = 0;
$userView = $this->app->user->view->products;
if($type == 'project') $userView = $this->app->user->view->projects;
@@ -1917,6 +1917,9 @@ class docModel extends model
$buildPairs = $this->dao->select('id')->from(TABLE_BUILD)->where('execution')->in($executionIdList)->andWhere('deleted')->eq('0')->andWhere('execution')->in($this->app->user->view->sprints)->fetchPairs('id');
if(!empty($buildPairs)) $buildIdList = implode(',', $buildPairs);
$testtaskPairs = $this->dao->select('id')->from(TABLE_TESTTASK)->where('execution')->in($executionIdList)->andWhere('deleted')->eq('0')->andWhere('execution')->in($this->app->user->view->sprints)->fetchPairs('id');
if(!empty($testtaskPairs)) $testtaskIdList = implode(',', $testtaskPairs);
$executionIdList = $executionIdList ? join(',', $executionIdList) : 0;
$storyIDList = $storyIDList ? join(',', $storyIDList) : 0;
}
@@ -1932,6 +1935,9 @@ class docModel extends model
$buildPairs = $this->dao->select('id')->from(TABLE_BUILD)->where('execution')->eq($objectID)->andWhere('deleted')->eq('0')->andWhere('execution')->in($userView)->fetchPairs('id');
if(!empty($buildPairs)) $buildIdList = implode(',', $buildPairs);
$testtaskPairs = $this->dao->select('id')->from(TABLE_TESTTASK)->where('execution')->eq($objectID)->andWhere('deleted')->eq('0')->andWhere('execution')->in($userView)->fetchPairs('id');
if(!empty($testtaskPairs)) $testtaskIdList = implode(',', $testtaskPairs);
}
$files = $this->dao->select('*')->from(TABLE_FILE)->alias('t1')
@@ -1956,6 +1962,7 @@ class docModel extends model
->beginIF($type == 'project' or $type == 'execution')
->orWhere("(objectType = 'task' and objectID in ($taskIdList))")
->orWhere("(objectType = 'build' and objectID in ($buildIdList))")
->orWhere("(objectType = 'testtask' and objectID in ($testtaskIdList))")
->beginIF($storyIDList)->orWhere("((objectType = 'story' OR objectType = 'requirement') and objectID in ($storyIDList))")->fi()
->fi()
->markRight(1)
+1 -1
View File
@@ -162,7 +162,7 @@ class editor extends control
if(strpos(strtolower($filePath), strtolower($this->app->getBasePath())) !== 0) return print($this->lang->editor->editFileError);
$fileName = empty($_POST['fileName']) ? '' : trim($this->post->fileName);
if($action != 'newPage' and empty($fileName)) return print(js::error($this->lang->editor->emptyFileName));
if($action != 'edit' and empty($fileName)) return print(js::error($this->lang->editor->emptyFileName));
if($action != 'edit' and $action != 'newPage') $filePath = $this->editor->getSavePath($filePath, $action);
if($action != 'edit' and $action != 'newPage' and file_exists($filePath) and !$this->post->override) return print(js::error($this->lang->editor->repeatFile));
+3 -2
View File
@@ -246,6 +246,7 @@ function addNewLine(obj)
newLine.find("select[name^='products']").attr('name', 'products[' + index + ']').attr('id', 'products' + index).val('').chosen();
newLine.find("select[name^='plans']").attr('name', 'plans[' + index + '][' + 0 + '][]').chosen();
newLine.find("div[id^='plan']").attr('id', 'plan' + index);
newLine.find('[name*=products]').removeAttr('data-last');
$(obj).closest('tr').after(newLine);
var product = newLine.find("select[name^='products']");
@@ -307,10 +308,10 @@ function hidePlanBox(attribute)
else
{
$('.productsBox .planBox').removeClass('hide');
$('.productsBox .planBox select').attr('disabled', '');
$('.productsBox .planBox select').removeAttr('disabled');
$('#productTitle').text(manageProductPlanLang);
$('#plansBox').closest('tr').removeClass('hide');
$('#plansBox').attr('disabled', '');
$('#plansBox').removeAttr('disabled');
}
}
+7 -1
View File
@@ -117,7 +117,13 @@ $(function()
if(isWaterfall)
{
hidePlanBox(executionAttr);
$('#attribute').change(function()
{
var attribute = $(this).val();
hidePlanBox(attribute);
})
$('#attribute').change();
}
})
var lastProjectID = $("#project").val();
+1 -1
View File
@@ -583,7 +583,7 @@ class executionModel extends model
{
$this->app->loadLang('project');
$multipleProducts = $this->loadModel('product')->getMultiBranchPairs();
if(is_string($_POST['branch']) !== false) $_POST['branch'] = json_decode($_POST['branch'], true);
if(isset($_POST['branch']) and is_string($_POST['branch']) !== false) $_POST['branch'] = json_decode($_POST['branch'], true);
foreach($_POST['products'] as $index => $productID)
{
if(isset($multipleProducts[$productID]) and !isset($_POST['branch'][$index]))
+1 -1
View File
@@ -92,7 +92,7 @@
?>
</td>
<?php if($project->hasProduct):?>
<td class='text-left' title='<?php echo $products[$story->product]->name?>'><?php echo html::a($this->createLink('product', 'browse', "productID=$story->product&branch=$story->branch"), $products[$story->product]->name);?></td>
<td class='text-left' title='<?php echo $products[$story->product]->name?>'><?php echo html::a($this->createLink('product', 'browse', "productID=$story->product&branch=$story->branch&browseType=&param=0&storyType=story&orderBy=&recTotal=0&recPerPage=20&pageID=1&projectID={$project->id}"), $products[$story->product]->name);?></td>
<?php endif;?>
<td class='c-module text-left' title='<?php echo zget($modules, $story->module, '')?>'><?php echo zget($modules, $story->module, '')?></td>
<td class='text-ellipsis <?php if((empty($project->hasProduct) && $project->model != 'scrum') || $storyType != 'story') echo 'hide';?>' title='<?php echo $story->planTitle;?>'><?php echo $story->planTitle;?></td>
+7 -7
View File
@@ -174,14 +174,14 @@ class mail extends control
if(dao::isError()) return print(js::error(dao::getError()));
$this->session->set('mailConfig', '');
$this->view->title = $this->lang->mail->common . $this->lang->colon . $this->lang->mail->save;
$this->view->position[] = html::a(inlink('index'), $this->lang->mail->common);
$this->view->position[] = $this->lang->mail->save;
$this->view->mailExist = $this->mail->mailExist();
$this->display();
}
$this->view->title = $this->lang->mail->common . $this->lang->colon . $this->lang->mail->save;
$this->view->position[] = html::a(inlink('index'), $this->lang->mail->common);
$this->view->position[] = $this->lang->mail->save;
$this->view->mailExist = $this->mail->mailExist();
$this->display();
}
/**
+2
View File
@@ -104,6 +104,7 @@ $lang->misc->feature->visionsDesc = "<p>The concept of interface has been add
$lang->misc->feature->visionsImage = 'theme/default/images/main/visions_en.png';
/* Release Date. */
$lang->misc->releaseDate['18.5'] = '2023-07-05';
$lang->misc->releaseDate['18.4'] = '2023-06-14';
$lang->misc->releaseDate['18.4.beta1'] = '2023-05-31';
$lang->misc->releaseDate['18.4.alpha1'] = '2023-04-21';
@@ -204,6 +205,7 @@ $lang->misc->releaseDate['7.1.stable'] = '2015-03-07';
$lang->misc->releaseDate['6.3.stable'] = '2014-11-07';
/* Release Detail. */
$lang->misc->feature->all['18.5'][] = array('title' => "Our academy's curriculum now supports cloud-based importing and the previewing of PDF files within courses. Additionally, we've optimized the loading speed of commonly used lists and addressed multiple bugs.", 'desc' => '');
$lang->misc->feature->all['18.4'][] = array('title' => 'In this release, we have improved the performance of the core list, added compatibility with Dameng databases, and resolved multiple bugs.', 'desc' => '');
$lang->misc->feature->all['18.4.beta1'][] = array('title' => 'Fix bugs.', 'desc' => '');
$lang->misc->feature->all['18.4.alpha1'][] = array('title' => 'We have enhanced the interaction experience of permissions and documents, while introducing the concept of test scenarios. Moreover, we now provide support for importing use cases with XMIND. Additionally, we have fully revamped the BI module by incorporating large screens, pivot tables, charts, and data capabilities.', 'desc' => '');
+2
View File
@@ -104,6 +104,7 @@ $lang->misc->feature->visionsDesc = "<p>The concept of interface has been add
$lang->misc->feature->visionsImage = 'theme/default/images/main/visions_en.png';
/* Release Date. */
$lang->misc->releaseDate['18.5'] = '2023-07-05';
$lang->misc->releaseDate['18.4'] = '2023-06-14';
$lang->misc->releaseDate['18.4.beta1'] = '2023-05-31';
$lang->misc->releaseDate['18.4.alpha1'] = '2023-04-21';
@@ -204,6 +205,7 @@ $lang->misc->releaseDate['7.1.stable'] = '2015-03-07';
$lang->misc->releaseDate['6.3.stable'] = '2014-11-07';
/* Release Detail. */
$lang->misc->feature->all['18.5'][] = array('title' => "Our academy's curriculum now supports cloud-based importing and the previewing of PDF files within courses. Additionally, we've optimized the loading speed of commonly used lists and addressed multiple bugs.", 'desc' => '');
$lang->misc->feature->all['18.4'][] = array('title' => 'In this release, we have improved the performance of the core list, added compatibility with Dameng databases, and resolved multiple bugs.', 'desc' => '');
$lang->misc->feature->all['18.4.beta1'][] = array('title' => 'Fix bugs.', 'desc' => '');
$lang->misc->feature->all['18.4.alpha1'][] = array('title' => 'We have enhanced the interaction experience of permissions and documents, while introducing the concept of test scenarios. Moreover, we now provide support for importing use cases with XMIND. Additionally, we have fully revamped the BI module by incorporating large screens, pivot tables, charts, and data capabilities.', 'desc' => '');
+2
View File
@@ -104,6 +104,7 @@ $lang->misc->feature->visionsDesc = "<p>The concept of interface has been add
$lang->misc->feature->visionsImage = 'theme/default/images/main/visions_en.png';
/* Release Date. */
$lang->misc->releaseDate['18.5'] = '2023-07-05';
$lang->misc->releaseDate['18.4'] = '2023-06-14';
$lang->misc->releaseDate['18.4.beta1'] = '2023-05-31';
$lang->misc->releaseDate['18.4.alpha1'] = '2023-04-21';
@@ -204,6 +205,7 @@ $lang->misc->releaseDate['7.1.stable'] = '2015-03-07';
$lang->misc->releaseDate['6.3.stable'] = '2014-11-07';
/* Release Detail. */
$lang->misc->feature->all['18.5'][] = array('title' => "Our academy's curriculum now supports cloud-based importing and the previewing of PDF files within courses. Additionally, we've optimized the loading speed of commonly used lists and addressed multiple bugs.", 'desc' => '');
$lang->misc->feature->all['18.4'][] = array('title' => 'In this release, we have improved the performance of the core list, added compatibility with Dameng databases, and resolved multiple bugs.', 'desc' => '');
$lang->misc->feature->all['18.4.beta1'][] = array('title' => 'Fix bugs.', 'desc' => '');
$lang->misc->feature->all['18.4.alpha1'][] = array('title' => 'We have enhanced the interaction experience of permissions and documents, while introducing the concept of test scenarios. Moreover, we now provide support for importing use cases with XMIND. Additionally, we have fully revamped the BI module by incorporating large screens, pivot tables, charts, and data capabilities.', 'desc' => '');
+2
View File
@@ -104,6 +104,7 @@ $lang->misc->feature->visionsDesc = "<p>从16.5开始增加了界面概念,
$lang->misc->feature->visionsImage = 'theme/default/images/main/visions.png';
/* Release Date. */
$lang->misc->releaseDate['18.5'] = '2023-07-05';
$lang->misc->releaseDate['18.4'] = '2023-06-14';
$lang->misc->releaseDate['18.4.beta1'] = '2023-05-31';
$lang->misc->releaseDate['18.4.alpha1'] = '2023-04-21';
@@ -204,6 +205,7 @@ $lang->misc->releaseDate['7.1.stable'] = '2015-03-07';
$lang->misc->releaseDate['6.3.stable'] = '2014-11-07';
/* Release Detail. */
$lang->misc->feature->all['18.5'][] = array('title' => '学堂课程支持从云端导入,支持课程中PDF文件的预览,同时还优化了常用列表的加载速度,修复了多处Bug。', 'desc' => '');
$lang->misc->feature->all['18.4'][] = array('title' => '本次发布优化了核心列表的性能,兼容达梦数据库,修复了多处Bug。', 'desc' => '');
$lang->misc->feature->all['18.4.beta1'][] = array('title' => '解Bug。', 'desc' => '');
$lang->misc->feature->all['18.4.alpha1'][] = array('title' => '优化权限、文档交互体验,测试新增场景概念,用例支持xmind导入,并对BI模块中的大屏、透视表、图表、数据表进行了全面升级。', 'desc' => '');
+8 -1
View File
@@ -1539,7 +1539,14 @@ class pivotModel extends model
}
else
{
$columnSQL = "$stat(tt.`$field`) as `$uuName`";
if($fields[$field]['type'] != 'number' and in_array($stat, array('avg', 'sum')))
{
$columnSQL = "$stat(0) as `$uuName`";
}
else
{
$columnSQL = "$stat(tt.`$field`) as `$uuName`";
}
}
if($slice != 'noSlice') $columnSQL = "select $groupList,`$slice`,$columnSQL from ($sql) tt" . $connectSQL . $groupSQL . ",tt.`$slice`" . $orderSQL . ",tt.`$slice`";
+2
View File
@@ -1964,7 +1964,9 @@ class productModel extends model
->where('deleted')->eq(0)
->andWhere('status')->eq('closed')
->andWhere('closedReason')->eq('done')
->groupBy('product')
->fetchPairs('product', 'finish');
$launchedStory = $this->dao->select('product, count(1) as launched')->from(TABLE_STORY)
->where('deleted')->eq(0)
->andWhere('status')->eq('launched')
+3 -3
View File
@@ -128,7 +128,7 @@
<td><?php echo $program['activeStories'];?></td>
<td><?php echo $program['changingStories'];?></td>
<td><?php echo $program['reviewingStories'];?></td>
<?php $totalStories = $program['activeStories'] + $program['finishClosedStories'];?>
<?php $totalStories = ($program['totalStories'] - $program['closedStories']) + $program['finishClosedStories'];?>
<td><?php echo $totalStories == 0 ? 0 : round($program['finishedStories'] / $totalStories, 3) * 100;?>%</td>
<td><?php echo $program['unresolvedBugs'];?></td>
<td><?php echo ($program['unresolvedBugs'] + $program['fixedBugs']) == 0 ? 0 : round($program['fixedBugs'] / ($program['unresolvedBugs'] + $program['fixedBugs']), 3) * 100;?>%</td>
@@ -169,7 +169,7 @@
<td><?php echo isset($line['activeStories']) ? $line['activeStories'] : 0;?></td>
<td><?php echo isset($line['changingStories']) ? $line['changingStories'] : 0;?></td>
<td><?php echo isset($line['reviewingStories']) ? $line['reviewingStories'] : 0;?></td>
<?php $totalStories = (isset($line['activeStories']) ? $line['activeStories'] : 0) + (isset($line['finishClosedStories']) ? $line['finishClosedStories'] : 0)?>
<?php $totalStories = (isset($line['totalStories']) ? $line['totalStories'] : 0) - (isset($line['closedStories']) ? $line['closedStories'] : 0) + (isset($line['finishClosedStories']) ? $line['finishClosedStories'] : 0)?>
<td><?php echo $totalStories == 0 ? 0 : round((isset($line['finishedStories']) ? $line['finishedStories'] : 0) / $totalStories, 3) * 100;?>%</td>
<td><?php echo isset($line['unresolvedBugs']) ? $line['unresolvedBugs'] : 0;?></td>
<td><?php echo (isset($line['fixedBugs']) and ($line['unresolvedBugs'] + $line['fixedBugs'] != 0)) ? round($line['fixedBugs'] / ($line['unresolvedBugs'] + $line['fixedBugs']), 3) * 100 : 0;?>%</td>
@@ -184,7 +184,7 @@
<?php if(isset($line['products']) and is_array($line['products'])):?>
<?php foreach($line['products'] as $productID => $product):?>
<?php
$totalStories = $product->activeStories + $product->finishClosedStories;
$totalStories = ($product->totalStories - $product->closedStories) + $product->finishClosedStories;
$trClass = '';
if($product->line and in_array($config->systemMode, array('ALM', 'PLM')))
+1 -1
View File
@@ -75,7 +75,7 @@ class program extends control
}
/* Get PM id list. */
$accounts = array_unique(array_column($programs, 'PM'));
$accounts = array_unique(array_column(json_decode(json_encode($programs), true), 'PM'));
$hasProject = false;
foreach($programs as $program)
{
+39 -1
View File
@@ -196,6 +196,7 @@ class programModel extends model
->beginIF($this->app->rawMethod == 'browse' and $type === 'top')->andWhere('parent')->eq(0)->fi()
->beginIF($this->app->rawMethod == 'browse' and ($type === 'child' or !$this->app->user->admin))->andWhere('id')->in($objectIdList)->fi()
->beginIF(!$this->app->user->admin and $this->app->rawMethod != 'browse')->andWhere('id')->in($userViewIdList)->fi()
->beginIF(defined('RUN_MODE') && RUN_MODE == 'api' && !$this->cookie->showClosed)->andWhere('status')->ne('closed')->fi()
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
@@ -1388,7 +1389,35 @@ class programModel extends model
->exec();
}
/* 5. Update projectStatsTime in config. */
/* 5. Update programStatsTime. */
$projectList = $this->dao->select('id,progress,path')->from(TABLE_PROJECT)
->where('type')->eq('project')
->andWhere('parent')->ne(0)
->fetchAll('id');
$programProgress = array();
foreach($projectList as $projectID => $project)
{
$path = explode(',', trim($project->path, ','));
foreach($path as $programID)
{
if($programID == $projectID) continue;
$programProgress[$programID][] = $project->progress;
}
}
foreach($programProgress as $programID => $progress)
{
$count = count($progress);
$sum = array_sum($progress);
$average = round($sum / $count, 2);
$this->dao->update(TABLE_PROJECT)
->set('progress')->eq($average)
->where('id')->eq($programID)
->exec();
}
/* 6. Update projectStatsTime in config. */
$this->loadModel('setting')->setItem('system.common.global.projectStatsTime', $now);
$this->app->config->global->projectStatsTime = $now;
}
@@ -1416,6 +1445,14 @@ class programModel extends model
$leftTasks = ($this->cookie->projectType and $this->cookie->projectType == 'bycard') ? $this->loadModel('project')->getProjectLeftTasks(array_keys($projects)) : array();
/* Get the members of project teams. */
$teamMembers = $this->dao->select('t1.root,t1.account')->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
->where('t1.root')->in(array_keys($projects))
->andWhere('t1.type')->eq('project')
->andWhere('t2.deleted')->eq(0)
->fetchGroup('root', 'account');
/* Process projects. */
foreach($projects as $projectID => $project)
{
@@ -1428,6 +1465,7 @@ class programModel extends model
if($delay > 0) $project->delay = $delay;
}
$project->teamMembers = isset($teamMembers[$projectID]) ? array_keys($teamMembers[$projectID]) : array();
$project->leftTasks = isset($leftTasks[$projectID]) ? $leftTasks[$projectID]->tasks : '—';
/* Convert predefined HTML entities to characters. */
+1 -1
View File
@@ -517,7 +517,7 @@ class project extends control
{
foreach($planStory as $id => $story)
{
if($story->status == 'draft' or $story->status == 'reviewing')
if($story->status != 'active')
{
unset($planStory[$id]);
continue;
+8 -8
View File
@@ -30,16 +30,16 @@ $config->repo->synced = '';
$config->repo->repoSyncLog = new stdclass();
$config->repo->repoSyncLog->one = 1;
$config->repo->repoSyncLog->done = 'done';
$config->repo->repoSyncLog->total = 'Total';
$config->repo->repoSyncLog->fatal = 'fatal';
$config->repo->repoSyncLog->error = 'error';
$config->repo->repoSyncLog->failed = 'failed';
$config->repo->repoSyncLog->done = array('done', '完成');
$config->repo->repoSyncLog->total = array('Total', '总数');
$config->repo->repoSyncLog->fatal = array('fatal', '致命');
$config->repo->repoSyncLog->error = array('error', '错误');
$config->repo->repoSyncLog->failed = array('failed', '失败');
$config->repo->repoSyncLog->finish = 'finish';
$config->repo->repoSyncLog->emptyRepo = 'empty repository';
$config->repo->repoSyncLog->finishCount = 'Counting objects: 100%';
$config->repo->repoSyncLog->emptyRepo = array('empty repository', '空仓库');
$config->repo->repoSyncLog->finishCount = array('Counting objects: 100%');
$config->repo->repoSyncLog->logFilePrefix = '/log/clone.progress.';
$config->repo->repoSyncLog->finishCompress = 'Compressing objects: 100%';
$config->repo->repoSyncLog->finishCompress = array('Compressing objects: 100%');
$config->repo->editor = new stdclass();
$config->repo->editor->create = array('id' => 'desc', 'tools' => 'simpleTools');
+5 -5
View File
@@ -1450,20 +1450,20 @@ class repo extends control
$content = file($logFile);
foreach($content as $line)
{
if(strpos($line, $this->config->repo->repoSyncLog->fatal) !== false or strpos($line, $this->config->repo->repoSyncLog->failed) !== false) return print($this->config->repo->repoSyncLog->error);
if($this->repo->strposAry($line, $this->config->repo->repoSyncLog->fatal) !== false or $this->repo->strposAry($line, $this->config->repo->repoSyncLog->failed) !== false) return print($this->config->repo->repoSyncLog->error);
}
$lastLine = $content[count($content) - 1];
if(strpos($lastLine, $this->config->repo->repoSyncLog->done) === false)
if($this->repo->strposAry($lastLine, $this->config->repo->repoSyncLog->done) === false)
{
if(strpos($lastLine, $this->config->repo->repoSyncLog->emptyRepo) !== false)
if($this->repo->strposAry($lastLine, $this->config->repo->repoSyncLog->emptyRepo) !== false)
{
@unlink($logFile);
}
elseif(strpos($lastLine, $this->config->repo->repoSyncLog->total) !== false)
elseif($this->repo->strposAry($lastLine, $this->config->repo->repoSyncLog->total) !== false)
{
$logContent = file_get_contents($logFile);
if(strpos($logContent, $this->config->repo->repoSyncLog->finishCount) !== false and strpos($logContent, $this->config->repo->repoSyncLog->finishCompress) !== false)
if($this->repo->strposAry($logContent, $this->config->repo->repoSyncLog->finishCount) !== false and $this->repo->strposAry($logContent, $this->config->repo->repoSyncLog->finishCompress) !== false)
{
@unlink($logFile);
}
+1 -1
View File
@@ -158,7 +158,7 @@ function replaceExample()
}
}
var resolveBug = $('[id*=bug][id*="resolve\]"]').val().split(';');
var resolveBug = $('[id*=bug][id*="resolve"]').val().split(';');
var bugModule = $('[id*=module][id*=bug]').val().split(';');
for(i in resolveBug)
{
+18
View File
@@ -3026,4 +3026,22 @@ class repoModel extends model
}
return true;
}
/**
* Check str in array.
*
* @param string $str
* @param array $checkAry
* @access public
* @return bool
*/
public function strposAry($str, $checkAry)
{
foreach($checkAry as $check)
{
if(mb_strpos($str, $check) !== false) return true;
}
return false;
}
}
+10 -4
View File
@@ -135,8 +135,8 @@ class story extends control
setcookie('lastStoryModule', (int)$this->post->module, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false);
$storyResult = $this->story->create($objectID, $bugID, $from = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $extra);
if(!$storyResult or dao::isError())
$this->story->create($objectID, $bugID, $from = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $extra);
if(dao::isError())
{
$response['result'] = 'fail';
$response['message'] = dao::getError();
@@ -855,7 +855,11 @@ class story extends control
if(!empty($_POST))
{
$this->story->update($storyID);
if(dao::isError()) return print(js::error(dao::getError()));
if(dao::isError())
{
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return print(js::error(dao::getError()));
}
$this->executeHooks($storyID);
@@ -1019,7 +1023,7 @@ class story extends control
$project = $this->dao->findByID($executionID)->from(TABLE_PROJECT)->fetch();
if($project->type == 'project')
{
if(!($project->model == 'scrum' and !$project->hasProduct and $project->multiple)) $this->view->hiddenPlan = true;
if((in_array($project->model, array('waterfallplus', 'waterfall')) and !$project->hasProduct) or !$project->multiple) $this->view->hiddenPlan = true;
$this->project->setMenu($executionID);
}
else
@@ -1154,6 +1158,7 @@ class story extends control
$showFields = str_replace('stage', '', $showFields);
}
if(!$branchProduct) unset($customFields['branch']);
if($this->view->hiddenPlan) unset($customFields['plan']);
$this->view->customFields = $customFields;
$this->view->showFields = $showFields;
@@ -1454,6 +1459,7 @@ class story extends control
elseif($from == 'project')
{
$projectID = $param ? $param : $this->session->project;
if(!$projectID) $projectID = $this->dao->select('project')->from(TABLE_PROJECTSTORY)->where('story')->eq($storyID)->fetch('project');
$this->loadModel('project')->setMenu($projectID);
}
elseif($from == 'qa')
+1 -1
View File
@@ -104,7 +104,7 @@ function getStatus(method, params)
function loadURS()
{
var productID = $('#product').val();
var branchID = $('#branch').val();
var branchID = $('#branch').val() ? $('#branch').val() : 0;
var requirementList = $('#URS').val();
requirementList = requirementList ? requirementList.join(',') : '';
+8 -2
View File
@@ -215,7 +215,7 @@ class storyModel extends model
* @param string $from
* @param string $extra
* @access public
* @return int|bool the id of the created story or false when error.
* @return bool|array
*/
public function create($executionID = 0, $bugID = 0, $from = '', $extra = '')
{
@@ -253,7 +253,11 @@ class storyModel extends model
/* Check repeat story. */
$result = $this->loadModel('common')->removeDuplicate('story', $story, "product={$story->product}");
if(isset($result['stop']) and $result['stop']) return array('status' => 'exists', 'id' => $result['duplicate']);
if(isset($result['stop']) and $result['stop'])
{
dao::$errors[] = $this->lang->story->title . $this->lang->story->reasonList['duplicate'] . ',' . $this->lang->story->id . $result['duplicate'];
return false;
}
if($story->status != 'draft' and $this->checkForceReview()) $story->status = 'reviewing';
$story = $this->loadModel('file')->processImgURL($story, $this->config->story->editor->create['id'], $this->post->uid);
@@ -330,6 +334,7 @@ class storyModel extends model
$this->file->updateObjectID($this->post->uid, $storyID, $story->type);
$files = $this->file->saveUpload($story->type, $storyID, 1);
if(empty($files) && defined('RUN_MODE') && RUN_MODE == 'api' && !empty($_SESSION['album']['used'][$this->post->uid])) $files = $_SESSION['album']['used'][$this->post->uid];
/* Multi branch sync files */
!empty($files) ? $storyFile = $files : $files = $storyFile;
@@ -970,6 +975,7 @@ class storyModel extends model
->setIF(!isset($_POST['title']), 'title', $oldStory->title)
->setIF(!isset($_POST['spec']), 'spec', $oldStory->spec)
->setIF(!isset($_POST['verify']), 'verify', $oldStory->verify)
->setIF(isset($_POST['status']) && $_POST['status'] == 'reviewing', 'reviewedBy', '')
->stripTags($this->config->story->editor->edit['id'], $this->config->allowedTags)
->join('mailto', ',')
->join('linkStories', ',')
+13 -2
View File
@@ -1432,7 +1432,11 @@ class task extends control
{
$this->loadModel('action');
$changes = $this->task->pause($taskID, $extra);
if(dao::isError()) return print(js::error(dao::getError()));
if(dao::isError())
{
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return print(js::error(dao::getError()));
}
if($this->post->comment != '' or !empty($changes))
{
@@ -1469,6 +1473,7 @@ class task extends control
}
return print(js::closeModal('parent.parent', 'this'));
}
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success'));
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
}
@@ -1498,7 +1503,11 @@ class task extends control
{
$this->loadModel('action');
$changes = $this->task->start($taskID);
if(dao::isError()) return print(js::error(dao::getError()));
if(dao::isError())
{
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return print(js::error(dao::getError()));
}
$act = $this->post->left == 0 ? 'Finished' : 'Restarted';
$actionID = $this->action->create('task', $taskID, $act, $this->post->comment);
@@ -1532,6 +1541,8 @@ class task extends control
}
return print(js::closeModal('parent.parent', 'this'));
}
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success'));
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
}
+11 -2
View File
@@ -3803,7 +3803,16 @@ class taskModel extends model
$storyChanged = (!empty($task->storyStatus) and $task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion and !in_array($task->status, array('cancel', 'closed')));
$canView = common::hasPriv('task', 'view');
$taskLink = helper::createLink('task', 'view', "taskID=$task->id");
if($this->config->vision == 'lite')
{
$taskLink = helper::createLink('task', 'view', "taskID=$task->id", '', true);
$linkClass = 'class="iframe"';
}
else
{
$taskLink = helper::createLink('task', 'view', "taskID=$task->id");
$linkClass = '';
}
$account = $this->app->user->account;
$id = $col->id;
if($col->show)
@@ -3862,7 +3871,7 @@ class taskModel extends model
if($task->module and isset($modulePairs[$task->module])) echo "<span class='label label-gray label-badge'>" . $modulePairs[$task->module] . '</span> ';
if($task->parent > 0) echo '<span class="label label-badge label-light" title="' . $this->lang->task->children . '">' . $this->lang->task->childrenAB . '</span> ';
if(!empty($task->team)) echo '<span class="label label-badge label-light" title="' . $this->lang->task->multiple . '">' . $this->lang->task->multipleAB . '</span> ';
echo $canView ? html::a($taskLink, $task->name, null, "style='color: $task->color' title='$task->name'") : "<span style='color: $task->color'>$task->name</span>";
echo $canView ? html::a($taskLink, $task->name, null, "$linkClass style='color: $task->color' title='$task->name'") : "<span style='color: $task->color'>$task->name</span>";
if(!empty($task->children)) echo '<a class="task-toggle" data-id="' . $task->id . '"><i class="icon icon-angle-double-right"></i></a>';
if($task->fromBug) echo html::a(helper::createLink('bug', 'view', "id=$task->fromBug"), "[BUG#$task->fromBug]", '', "class='bug'");
break;
-7
View File
@@ -972,13 +972,6 @@ class testcase extends control
}
}
if($case->auto == 'unit')
{
$this->lang->testcase->subMenu->testcase->feature['alias'] = '';
$this->lang->testcase->subMenu->testcase->unit['alias'] = 'view';
$this->lang->testcase->subMenu->testcase->unit['subModule'] = 'testcase';
}
if(empty($case->steps))
{
$step = new stdclass();
+4
View File
@@ -555,6 +555,9 @@ class testtask extends control
/* Append bugs and results. */
$runs = $this->testcase->appendData($runs, 'run');
$case2RunMap = array();
foreach($runs as $run) $case2RunMap[$run->case] = $run->id;
$this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->testtask->cases;
$this->view->position[] = html::a($this->createLink('testtask', 'browse', "productID=$productID"), $this->products[$productID]);
$this->view->position[] = $this->lang->testtask->common;
@@ -564,6 +567,7 @@ class testtask extends control
$this->view->productName = $this->products[$productID];
$this->view->task = $task;
$this->view->runs = $runs;
$this->view->case2RunMap = $case2RunMap;
$this->view->users = $this->loadModel('user')->getPairs('noclosed|qafirst|noletter');
$this->view->assignedToList = $assignedToList;
$this->view->moduleTree = $this->loadModel('tree')->getTreeMenu($productID, 'case', 0, array('treeModel', 'createTestTaskLink'), $taskID, $task->branch);
+4 -1
View File
@@ -17,6 +17,7 @@
<?php js::set('taskCaseBrowseType', ($browseType == 'bymodule' and $this->session->taskCaseBrowseType == 'bysearch') ? 'all' : $this->session->taskCaseBrowseType);?>
<?php js::set('browseType', $browseType);?>
<?php js::set('moduleID', $moduleID);?>
<?php js::set('case2RunMap', $case2RunMap);?>
<?php js::set('automation', !empty($automation) ? $automation->id : 0);?>
<?php $this->app->loadLang('zanode');?>
<?php js::set('runCaseConfirm', $lang->zanode->runCaseConfirm);?>
@@ -170,13 +171,15 @@ $("thead").find('.c-assignedTo').attr('class', '');
function runAutocase()
{
var caseIDList = [];
var runIDList = [];
$.each($('input[name^=caseIDList]:checked'),function(){
caseIDList.push($(this).val());
runIDList.push(case2RunMap[$(this).val()]);
});
var url = createLink('zanode', 'ajaxRunZTFScript', 'scriptID=' + automation)
var postData = {'caseIDList' : caseIDList.join(',')};
var postData = {'caseIDList' : caseIDList.join(','), 'runIDList' : runIDList.join(',')};
var response = true;
$.post(url, postData, function(result)
+11 -8
View File
@@ -78,15 +78,18 @@ class todoModel extends model
{
unset($todo->config['week']);
unset($todo->config['month']);
if(!$todo->config['day'])
if(empty($todo->config['specifiedDate']))
{
dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->todo->cycleDaysLabel);
return false;
}
if(!validater::checkInt($todo->config['day']))
{
dao::$errors[] = sprintf($this->lang->error->int[0], $this->lang->todo->cycleDaysLabel);
return false;
if(!$todo->config['day'])
{
dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->todo->cycleDaysLabel);
return false;
}
if(!validater::checkInt($todo->config['day']))
{
dao::$errors[] = sprintf($this->lang->error->int[0], $this->lang->todo->cycleDaysLabel);
return false;
}
}
}
if($todo->config['type'] == 'week')
+6 -2
View File
@@ -37,7 +37,8 @@ $config->upgrade->maxVersion['max4_2'] = '18_2';
$config->upgrade->maxVersion['max4_3'] = '18_3';
$config->upgrade->maxVersion['max4_4_alpha1'] = '18_4_alpha1';
$config->upgrade->maxVersion['max4_4_beta1'] = '18_4_beta1';
$config->upgrade->maxVersion['max4_4'] = '18_4'; // max insert position.
$config->upgrade->maxVersion['max4_4'] = '18_4';
$config->upgrade->maxVersion['max4_5'] = '18_5'; // max insert position.
$config->upgrade->bizVersion = array();
$config->upgrade->bizVersion['biz1_0'] = '9_5_1';
@@ -113,7 +114,8 @@ $config->upgrade->bizVersion['biz8_2'] = '18_2';
$config->upgrade->bizVersion['biz8_3'] = '18_3';
$config->upgrade->bizVersion['biz8_4_alpha1'] = '18_4_alpha1';
$config->upgrade->bizVersion['biz8_4_beta1'] = '18_4_beta1';
$config->upgrade->bizVersion['biz8_4'] = '18_4'; // biz insert position.
$config->upgrade->bizVersion['biz8_4'] = '18_4';
$config->upgrade->bizVersion['biz8_5'] = '18_5'; // biz insert position.
$config->upgrade->proVersion = array();
$config->upgrade->proVersion['pro1_0'] = '3_1';
@@ -757,3 +759,5 @@ $config->upgrade->recoveryActions->testcase->review['name'] = $lang->upgrade
$config->upgrade->recoveryActions->testcase->review['method'] = 'operate';
$config->upgrade->recoveryActions->testcase->review['open'] = 'normal';
$config->upgrade->recoveryActions->testcase->review['position'] = 'browseandview';
$config->upgrade->missedFlowFieldVersions = array('max4_4_alpha1', 'max4_4_beta1', 'max4_4', 'max4_5', 'biz8_4_alpha1', 'biz8_4_beta1', 'biz8_4', 'biz8_5');
+6 -3
View File
@@ -178,7 +178,8 @@ $lang->upgrade->fromVersions['18_2'] = '18.2';
$lang->upgrade->fromVersions['18_3'] = '18.3';
$lang->upgrade->fromVersions['18_4_alpha1'] = '18.4.alpha1';
$lang->upgrade->fromVersions['18_4_beta1'] = '18.4.beta1';
$lang->upgrade->fromVersions['18_4'] = '18.4'; // pms insert position.
$lang->upgrade->fromVersions['18_4'] = '18.4';
$lang->upgrade->fromVersions['18_5'] = '18.5'; // pms insert position.
global $config;
/* Lite. */
@@ -361,7 +362,8 @@ $lang->upgrade->fromVersions['biz8_2'] = 'Biz8.2';
$lang->upgrade->fromVersions['biz8_3'] = 'Biz8.3';
$lang->upgrade->fromVersions['biz8_4_alpha1'] = 'Biz8.4.alpha1';
$lang->upgrade->fromVersions['biz8_4_beta1'] = 'Biz8.4.beta1';
$lang->upgrade->fromVersions['biz8_4'] = 'Biz8.4'; // biz insert position.
$lang->upgrade->fromVersions['biz8_4'] = 'Biz8.4';
$lang->upgrade->fromVersions['biz8_5'] = 'Biz8.5'; // biz insert position.
/* Max. */
$lang->upgrade->fromVersions['max2_0_beta4'] = 'Max2.0.beta4';
@@ -401,4 +403,5 @@ $lang->upgrade->fromVersions['max4_2'] = 'Max4.2';
$lang->upgrade->fromVersions['max4_2'] = 'Max4.2';
$lang->upgrade->fromVersions['max4_3'] = 'Max4.3';
$lang->upgrade->fromVersions['max4_4_alpha1'] = 'Max4.4.alpha1';
$lang->upgrade->fromVersions['max4_4_beta1'] = 'Max4.4.beta1'; // max insert position.
$lang->upgrade->fromVersions['max4_4_beta1'] = 'Max4.4.beta1';
$lang->upgrade->fromVersions['max4_4'] = 'Max4.4'; // max insert position.
+137 -1
View File
@@ -642,7 +642,20 @@ class upgradeModel extends model
$this->checkPivotSQL();
}
break;
case '18_4':
if(!$executedXuanxuan)
{
$xuanxuanSql = $this->app->getAppRoot() . 'db' . DS . 'upgradexuanxuan7.1.sql';
$this->execSQL($xuanxuanSql);
$xuanxuanSql = $this->app->getAppRoot() . 'db' . DS . 'upgradexuanxuan7.2.beta.sql';
$this->execSQL($xuanxuanSql);
}
if($this->config->edition != 'open' and in_array($fromVersion, $this->config->upgrade->missedFlowFieldVersions))
{
$this->fixMissedFlowField();
}
break;
}
$this->deletePatch();
@@ -1117,7 +1130,9 @@ class upgradeModel extends model
case '18_4_alpha1':
$confirmContent .= file_get_contents($this->getUpgradeFile('18.4.alpha1'));
case '18_4_beta1':
$confirmContent .= file_get_contents($this->getUpgradeFile('18.4.beta1')); // confirm insert position.
$confirmContent .= file_get_contents($this->getUpgradeFile('18.4.beta1'));
case '18_4':
$confirmContent .= file_get_contents($this->getUpgradeFile('18.4')); // confirm insert position.
}
return $confirmContent;
@@ -9389,4 +9404,125 @@ class upgradeModel extends model
$this->dao->update(TABLE_PIVOT)->set('stage')->eq($stage)->where('id')->eq($pivotID)->exec();
}
}
/**
* Fix missed rnd workflow field.
*
* @access public
* @return void
*/
public function fixMissedFlowField()
{
$this->loadModel('workflow');
$this->loadModel('workflowaction');
$this->loadModel('workflowlayout');
$modules = $this->config->workflow->buildin->modules;
$actions = $this->config->workflowaction->buildin->actions;
$actionTypes = $this->config->workflowaction->buildin->types;
$actionMethods = $this->config->workflowaction->buildin->methods;
$actionOpens = $this->config->workflowaction->buildin->opens;
$actionLayouts = $this->config->workflowaction->buildin->layouts;
$actionPositions = $this->config->workflowaction->buildin->positions;
$actionShows = $this->config->workflowaction->buildin->shows;
$layouts = $this->config->workflowlayout->buildin->layouts;
$account = isset($this->app->user) ? $this->app->user->account : 'admin';
$now = helper::now();
/* Insert buildin modules to TABLE_WORKFLOW. */
$data = new stdclass();
$data->vision = 'rnd';
$data->buildin = 1;
$data->createdBy = $account;
$data->createdDate = $now;
$data->status = 'normal';
foreach($modules as $app => $appModules)
{
$data->app = $app;
foreach($appModules as $module => $options)
{
$this->app->loadLang($module);
$data->module = $module;
$data->name = isset($this->lang->$module->common) ? $this->lang->$module->common : $module;
$data->table = str_replace('`', '', zget($options, 'table', ''));
$data->navigator = zget($options, 'navigator', 'secondary');
if($module == 'story') $data->name = $this->lang->searchObjects['story'];
if($module == 'execution') $data->name = $this->lang->workflow->execution;
$modelField = $this->dao->select('id')->from(TABLE_WORKFLOW)->where('app')->eq($app)->andWhere('module')->eq($module)->andWhere('vision')->eq('rnd')->fetch('id');
if(!$modelField) $this->dao->insert(TABLE_WORKFLOW)->data($data)->exec();
}
}
/* Insert actions of buildin modules to TABLE_WORKFLOWACTION. */
$data = new stdclass();
$data->buildin = 1;
$data->role = 'buildin';
$data->extensionType = 'none';
$data->createdBy = $account;
$data->createdDate = $now;
foreach($actions as $module => $moduleActions)
{
$data->module = $module;
foreach($moduleActions as $action)
{
$data->action = $action;
/* Use default action name if not set flow action name. */
$name = '';
if(isset($this->lang->$module->$action)) $name = $this->lang->$module->$action;
if(empty($name) && in_array($action, array_keys($this->config->flowAction)))
{
$methodName = $this->config->flowAction[$action];
if(isset($this->lang->$module->$methodName)) $name = $this->lang->$module->$methodName;
}
if(empty($name) && isset($this->lang->workflowaction->default->actions[$action])) $name = $this->lang->workflowaction->default->actions[$action];
if(empty($name)) $name = $action;
$data->name = $name;
$data->method = $data->action;
$data->open = 'normal';
$data->layout = 'normal';
$data->type = 'single';
$data->position = 'browseandview';
$data->show = 'direct';
if(isset($actionMethods[$module][$action])) $data->method = $actionMethods[$module][$action];
if(isset($actionOpens[$module][$action])) $data->open = $actionOpens[$module][$action];
if(isset($actionLayouts[$module][$action])) $data->layout = $actionLayouts[$module][$action];
if(isset($actionTypes[$module][$action])) $data->type = $actionTypes[$module][$action];
if(isset($actionPositions[$module][$action])) $data->position = $actionPositions[$module][$action];
if(isset($actionShows[$module][$action])) $data->show = $actionShows[$module][$action];
$actionField = $this->dao->select('id')->from(TABLE_WORKFLOWACTION)->where('module')->eq($module)->andWhere('action')->eq($action)->andWhere('vision')->eq('rnd')->fetch('id');
if(!$actionField) $this->dao->insert(TABLE_WORKFLOWACTION)->data($data)->exec();
}
}
/* Insert layouts of buildin modules to TABLE_WORKFLOWLAYOUT. */
$data = new stdclass();
foreach($layouts as $module => $moduleLayouts)
{
$data->module = $module;
foreach($moduleLayouts as $action => $layoutFields)
{
$order = 1;
$data->action = $action;
foreach($layoutFields as $field => $options)
{
$data->field = $field;
$data->width = zget($options, 'width', 0);
$data->mobileShow = zget($options, 'mobileShow', 0);
$data->order = $order++;
if($data->width == 'auto') $data->width = 0;
$layoutField = $this->dao->select('id')->from(TABLE_WORKFLOWLAYOUT)->where('module')->eq($module)->andWhere('action')->eq($action)->andWhere('field')->eq($field)->andWhere('vision')->eq('rnd')->fetch('id');
if(!$layoutField) $this->dao->insert(TABLE_WORKFLOWLAYOUT)->data($data)->exec();
}
}
}
}
}
+1 -1
View File
@@ -83,7 +83,7 @@
<tr class='text-center'>
<td><?php echo $user->id;?></td>
<td class='text-left<?php echo zget($visibleFields, 'dept', ' hidden')?>' style='overflow:visible'><?php echo html::select("dept[$user->id]", $depts, $dept, "class='form-control chosen'");?></td>
<td><?php echo html::input("account[$user->id]", $user->account, "class='form-control'");?></td>
<td><?php echo html::input("account[$user->id]", $user->account, "class='form-control' readonly");?></td>
<td><?php echo html::input("realname[$user->id]", $user->realname, "class='form-control'");?></td>
<?php if($showVisionList):?>
<td class='text-left'><?php echo html::select("visions[$user->id][]", $visionList, $user->visions, "class='form-control chosen' multiple");?></td>
+8 -1
View File
@@ -617,13 +617,20 @@ class zanode extends control
if($_POST)
{
$caseIDList = $_POST['caseIDList'];
$runIDList = $_POST['runIDList'];
$script = $this->zanode->getAutomationByID($scriptID);
$cases = $this->loadModel('testcase')->getByList($caseIDList);
$caseIDListArray = explode(',', $caseIDList);
$runIDListArray = explode(',', $runIDList);
$case2RunMap = array();
foreach($caseIDListArray as $index => $caseID) $case2RunMap[$caseID] = $runIDListArray[$index];
foreach($cases as $id => $case)
{
if($case->auto != 'auto') continue;
$resultID = $this->loadModel('testtask')->initResult(0, $id, $case->version, $script->node);
$resultID = $this->loadModel('testtask')->initResult($case2RunMap[$id], $id, $case->version, $script->node);
if(!dao::isError()) $this->zanode->runZTFScript($script->id, $id, $resultID);
}
+4 -4
View File
@@ -267,8 +267,8 @@ class prepareUpdate
public function addLastVersion()
{
`sed -i "s/ \/\/ pms insert position\.$/\\n\\\$lang->upgrade->fromVersions['{$this->internalZT->pmsVersionAB}'] = '{$this->internalZT->pmsVersion}'; \/\/ pms insert position\./" ../module/upgrade/lang/version.php`;
`sed -i "s/ \/\/ biz insert position\.$/\\n\\\$lang->upgrade->fromVersions['biz{$this->internalZT->bizVersionAB}'] = 'Biz{$this->internalZT->bizVersion}'; \/\/ biz insert position\./" ../module/upgrade/lang/version.php`;
`sed -i "s/ \/\/ max insert position\.$/\\n\\\$lang->upgrade->fromVersions['max{$this->internalZT->lastMaxVersionAB}'] = 'Max{$this->internalZT->lastMaxVersion}'; \/\/ max insert position\./" ../module/upgrade/lang/version.php`;
`sed -i "s/ \/\/ biz insert position\.$/\\n\\\$lang->upgrade->fromVersions['biz{$this->internalZT->bizVersionAB}'] = 'Biz{$this->internalZT->bizVersion}'; \/\/ biz insert position\./" ../module/upgrade/lang/version.php`;
`sed -i "s/ \/\/ max insert position\.$/\\n\\\$lang->upgrade->fromVersions['max{$this->internalZT->lastMaxVersionAB}'] = 'Max{$this->internalZT->lastMaxVersion}'; \/\/ max insert position\./" ../module/upgrade/lang/version.php`;
}
/**
@@ -279,8 +279,8 @@ class prepareUpdate
*/
public function addVersionCorrespondence()
{
`sed -i "s/ \/\/ biz insert position\.$/\\n\\\$config->upgrade->bizVersion['biz{$this->internalZT->bizVersionAB}'] = '{$this->internalZT->pmsVersionAB}'; \/\/ biz insert position\./" ../module/upgrade/config.php`;
`sed -i "s/ \/\/ max insert position\.$/\\n\\\$config->upgrade->maxVersion['max{$this->internalZT->maxVersionAB}'] = '{$this->internalZT->pmsVersionAB}'; \/\/ max insert position\./" ../module/upgrade/config.php`;
`sed -i "s/ \/\/ biz insert position\.$/\\n\\\$config->upgrade->bizVersion['biz{$this->internalZT->bizVersionAB}'] = '{$this->internalZT->pmsVersionAB}'; \/\/ biz insert position\./" ../module/upgrade/config.php`;
`sed -i "s/ \/\/ max insert position\.$/\\n\\\$config->upgrade->maxVersion['max{$this->internalZT->maxVersionAB}'] = '{$this->internalZT->pmsVersionAB}'; \/\/ max insert position\./" ../module/upgrade/config.php`;
}
/**
+1 -1
View File
@@ -1 +1 @@
4786a9a7555ef5cdc6f8b84049d44e2add643a23
v7.2.1-rc.4
+1 -1
View File
@@ -1 +1 @@
7.1
7.2.1
@@ -0,0 +1,2 @@
<?php
$lang->conference->setupDescription = 'Audio & video conference requires a conference server. There are two types of conference servers: OWT and SRS. For now SRS is recommended.';
@@ -0,0 +1,2 @@
<?php
$lang->conference->setupDescription = 'Audio & video conference requires a conference server. There are two types of conference servers: OWT and SRS. For now SRS is recommended.';
@@ -0,0 +1,2 @@
<?php
$lang->conference->setupDescription = 'Audio & video conference requires a conference server. There are two types of conference servers: OWT and SRS. For now SRS is recommended.';
@@ -0,0 +1,2 @@
<?php
$lang->conference->setupDescription = '音视频会议功能需要部署额外的音视频服务端。音视频服务器端分为 OWT 和 SRS ,推荐使用 SRS 音视频服务端。';
@@ -0,0 +1,2 @@
<?php
$lang->conference->setupDescription = '音視頻會議功能需要部署額外的音視頻服務端。音視頻伺服器端分為 OWT 和 SRS ,推薦使用 SRS 音視頻服務端。';
@@ -154,7 +154,6 @@ class xuanxuanMessage extends messageModel
elseif($objectType == 'feedback')
{
$subcontent->headTitle = $object->productName;
$subcontent->headSubTitle = $object->execuName;
$subcontent->parentType = 'product';
$subcontent->parent = $object->product;
$subcontent->parentURL = "xxc:openInApp/zentao-integrated/" . urlencode($server . helper::createLink('feedback', 'browse', "id=$object->product", 'html'));