Merge branch '18.x' into zentaoipd
This commit is contained in:
+372
-136
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Executable
+168
@@ -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
|
||||
@@ -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), )
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"pkg": {
|
||||
"xuanxuan": {
|
||||
"gitVersion": "4786a9a7555ef5cdc6f8b84049d44e2add643a23",
|
||||
"version": "7.1"
|
||||
},
|
||||
"zentaoext": {
|
||||
"gitVersion": "18.x"
|
||||
},
|
||||
"zdoo": {
|
||||
"gitVersion": "master"
|
||||
},
|
||||
"zdooext": {
|
||||
"gitVersion": "master"
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
-34
@@ -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 启用多分支功能后执行任务列表增加是否显示分支名
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
Executable
+38
@@ -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
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 = '设置了默认分支';
|
||||
|
||||
@@ -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}")));
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,13 @@ $(function()
|
||||
|
||||
if(isWaterfall)
|
||||
{
|
||||
hidePlanBox(executionAttr);
|
||||
$('#attribute').change(function()
|
||||
{
|
||||
var attribute = $(this).val();
|
||||
hidePlanBox(attribute);
|
||||
})
|
||||
|
||||
$('#attribute').change();
|
||||
}
|
||||
})
|
||||
var lastProjectID = $("#project").val();
|
||||
|
||||
@@ -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]))
|
||||
|
||||
@@ -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=¶m=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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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' => '');
|
||||
|
||||
@@ -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' => '');
|
||||
|
||||
@@ -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' => '');
|
||||
|
||||
@@ -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' => '');
|
||||
|
||||
@@ -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`";
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')))
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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(',') : '';
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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')
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@
|
||||
4786a9a7555ef5cdc6f8b84049d44e2add643a23
|
||||
v7.2.1-rc.4
|
||||
|
||||
+1
-1
@@ -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'));
|
||||
|
||||
Reference in New Issue
Block a user