Merge branch feature/workflowgroup of zentao/zentaopms (#11478)

This commit is contained in:
刘刚
2025-09-15 13:20:36 +08:00
committed by Gitfox
860 changed files with 5073 additions and 73767 deletions
File diff suppressed because it is too large Load Diff
@@ -13,6 +13,8 @@
### 📝 统一占位符说明
**AI大模型必须严格按照以下占位符规范,保持命名一致性:**
- `{moduleName}` - 模块名(小写,如:user、task、project)
- `{layerName}` - 业务分层(小写,如:model、tao、zen、control、ui)
- `{className}` - 类名(驼峰命名,如:userModel、taskTao、projectZen)
- `{methodName}` - 方法名(驼峰命名,如:getById、createUser)
- `{tableName}` - 数据表名(小写,如:user、task、project)
- `{fieldName}` - 字段名(小写,如:id、name、status)
@@ -30,43 +32,18 @@
---
**🚨🚨🚨 AI大模型必读警告 🚨🚨🚨**
**第一步:必须先切换到 feature/unittest/ai 分支!**
**第二步:每个测试用例必须包含至少5个测试步骤(r()...e()语句)!**
**第三步:必须使用 test/runtime/ztf 运行测试验证!**
**第四步:必须按照指定格式提交代码!**
**第一步:每个测试用例必须包含至少5个测试步骤(r()...e()语句)!**
**第二步:必须按照指定格式提交代码!必须使用 test/runtime/ztf 运行测试验证!**
**第三步:必须先使用 php 命令验证脚本没有错误再使用 test/runtime/ztf 验证测试是否通过**
**⛔ 绝对禁止的行为:**
- 在开发分支上直接编写测试代码
- 测试步骤少于5个
- 使用php命令直接运行测试
- 不遵循提交信息格式
- 代码包含行尾空格(会导致提交失败)
- 使用 `test/spider.php` 运行测试(必须使用 `test/runtime/ztf`)
---
## ⚠️ 重要:分支开发流程(必须执行)
**任何单元测试脚本的开发都必须严格按照以下分支流程进行:**
### 第一步:切换到测试分支(必须)
1. **测试分支名**:`feature/unittest/ai`
2. **检查当前分支**:确认当前处于测试分支
3. **切换到测试分支**:如果当前没有处于测试分支,立即切换到测试分支进行开发
### 分支操作命令示例:
```bash
# 检查当前分支
git branch
# 切换到测试分支
git switch feature/unittest/ai
# 确认已切换到测试分支
git branch
```
**🚨 警告:如果不切换到测试分支,将违反ZenTao开发规范,必须立即停止操作并切换到正确的分支!**
## 🔍 核心概念说明
### 🔑 关键概念:测试用例 vs 测试步骤
@@ -86,7 +63,7 @@ git branch
**示例说明:**
```php
// 这是一个测试用例:getUserById.php
r($userTest->getByIdTest(1)) && p('account') && e('admin'); // 测试步骤1:正常用户查询
r($userTest->getByIdTest(1)) && p('account') && e('admin'); // 测试步骤1:正常用户查询
r($userTest->getByIdTest(999)) && p() && e(false); // 测试步骤2:不存在用户查询
r($userTest->getByIdTest(0)) && p() && e(false); // 测试步骤3:无效ID查询
r($userTest->getByIdTest(-1)) && p() && e(false); // 测试步骤4:负数ID查询
@@ -127,7 +104,7 @@ module/{moduleName}/test/
/**
title=测试 {moduleName}Model::{methodName}();
title=测试 {className}::{methodName}();
cid=0
- 测试步骤1描述 @期望结果1
@@ -146,7 +123,7 @@ cid=0
/**
title=测试 {moduleName}Model::{methodName}();
title=测试 {className}::{methodName}();
cid=0
- 测试步骤1:正常输入情况 >> 期望正常结果
@@ -399,42 +376,13 @@ fields:
## 单元测试开发完整流程(必须按顺序执行)
### 🔴 步骤1:切换到测试分支(AI大模型必须首先执行)
**⚠️ AI大模型执行指令:在生成任何代码之前,必须先执行以下分支命令**
#### 1.1 检查当前分支
```bash
# 执行此命令查看当前分支
git branch
git status
```
#### 1.2 切换到指定测试分支
```bash
# 必须切换到这个分支,分支名固定不变
git switch feature/unittest/ai
```
#### 1.3 验证切换成功
```bash
# 确认当前分支标记为 * feature/unittest/ai
git branch
```
**🚨 AI执行检查点:**
- [ ] 已执行 `git branch` 命令
- [ ] 已执行 `git switch feature/unittest/ai` 命令
- [ ] 已确认当前在 feature/unittest/ai 分支
- [ ] 如果分支不存在,需要先创建:`git checkout -b feature/unittest/ai`
### 步骤2:分析待测方法
1. 确定方法所属模块和层次(Model/TAO/ZEN)
### 步骤1:分析待测方法
1. 确定方法所属模块和业务分层(model/tao/zen/control/ui)
2. 分析方法参数类型和返回值
3. 理解业务逻辑和数据依赖关系
4. 识别可能的异常情况
### 步骤3:设计测试用例和测试步骤
### 步骤2:设计测试用例和测试步骤
1. **设计测试用例**:每个测试脚本对应一个测试用例,用于测试单个方法
2. **设计测试步骤**:在测试用例中设计多个测试步骤来验证不同场景
- 正常流程测试步骤
@@ -444,13 +392,13 @@ git branch
4. 确定每个步骤的断言点和期望结果
5. **🔴 强制要求:每个测试用例必须包含至少5个测试步骤**
### 步骤4:创建YAML数据文件(如需要)
### 步骤3:创建YAML数据文件(如需要)
1. 分析方法依赖的数据表
2. 设计符合业务逻辑的测试数据
3. 使用适当的数据生成规则
4. 考虑数据之间的关联关系
### 步骤5:编写测试脚本
### 步骤4:编写测试脚本
1. 创建测试执行脚本,包含完整的测试用例描述
2. 测试脚本使用小写的方法名称作为文件名,后缀统一为.php
3. **🔴 强制要求:每个测试用例必须包含至少5个测试步骤(r()...e()语句)**
@@ -458,20 +406,21 @@ git branch
5. 配置数据准备和环境设置
6. 为每个测试步骤编写准确的断言验证
### 步骤6:验证测试脚本(AI大模型必须执行验证)
### 步骤5:验证测试脚本(AI大模型必须执行验证)
**🔴 强制要求:必须使用 ztf 运行器,禁止使用 php 命令直接运行**
**🔴 强制要求:必须先使用 php 命令运行脚本没有错误再使用 ztf 运行器验证**
#### 6.1 执行测试命令
#### 5.1 执行测试命令
```bash
# 切换到测试运行目录
cd test/runtime
# 运行测试脚本(替换实际的模块名和方法名)
./ztf run ../../module/{moduleName}/test/model/{methodName}.php
# 使用 php 命令运行测试脚本,检查是否有错误
php module/{moduleName}/test/{layerName}/{methodName}.php
# 使用 ztf 运行测试脚本
test/runtime/ztf module/{moduleName}/test/{layerName}/{methodName}.php
```
#### 6.2 验证测试结果
#### 5.2 验证测试结果
**✅ 测试通过标准:**
- 通过数(PASS):1
- 失败数(FAIL):0
@@ -483,43 +432,36 @@ cd test/runtime
3. 验证断言是否匹配实际结果
4. 修改测试脚本后重新运行
#### 6.3 AI执行检查点
- [ ] 已切换到 test/runtime 目录
- [ ] 已使用 ztf 命令运行测试
#### 5.3 AI执行检查点
- [ ] 已使用 php 命令运行测试脚本,确认无错误
- [ ] 已使用 test/runtime/ztf 命令运行测试
- [ ] 测试结果显示 PASS=1, FAIL=0, SKIP=0
- [ ] 如有失败,已根据错误信息修正代码
### 🔴 步骤7:提交和推送代码(强制要求)
### 🔴 步骤6:提交代码(强制要求)
**测试验证通过后,必须按以下步骤提交代码:**
1. **添加测试文件到git(仅添加测试相关文件)**:
```bash
git add module/{moduleName}/test/lib/{moduleName}.unittest.class.php
git add module/{moduleName}/test/model/{methodName}.php
git add module/{moduleName}/test/model/yaml/{tableName}_{methodName}.yaml
# 注意:不要添加规范文档文件到测试分支!规范文档应保留在开发分支上
git add module/{moduleName}/test/{layerName}/{methodName}.php
git add module/{moduleName}/test/{layerName}/yaml/{tableName}_{methodName}.yaml
```
2. **提交代码**:
```bash
git commit -m "+ [misc] Add unit tests for {moduleName}::{methodName}() method
git commit -m "+ [misc] Add unit tests for {className}::{methodName}() method
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>"
```
3. **推送到远程仓库**:
```bash
git push
```
**⚠️ AI大模型提交信息模板(必须严格遵循):**
```bash
git commit -m "+ [misc] Add unit tests for {moduleName}::{methodName}() method
git commit -m "+ [misc] Add unit tests for {className}::{methodName}() method
🤖 Generated with [Claude Code](https://claude.ai/code)
@@ -529,7 +471,7 @@ Co-Authored-By: Claude <noreply@anthropic.com>"
**📋 提交信息格式说明:**
- **符号**:`+` (新增测试) / `*` (修改测试) / `-` (删除测试)
- **分类**:`[misc]` (测试相关固定使用misc)
- **描述**:`Add unit tests for {moduleName}::{methodName}() method`
- **描述**:`Add unit tests for {className}::{methodName}() method`
- **标识**:必须包含Claude Code和Co-Authored-By标记
## 常见测试模式
@@ -688,7 +630,7 @@ r($userTest->createTest($invalidUser)) && p('errors,account') && e('用户名不
- 理解被测方法的业务逻辑
- 分析被测方法涉及的数据库表
- 理解数据库表的结构
- 表结构定义参考db/zentao.sql
- 表结构定义参考 `db/zentao.sql`
## 注意事项
@@ -716,10 +658,6 @@ r($userTest->createTest($invalidUser)) && p('errors,account') && e('用户名不
### 🔴 AI大模型强制检查清单(每项必须100%完成)
**📋 分支操作检查**
- [ ] 已执行 `git switch feature/unittest/ai` 切换分支
- [ ] 已确认当前在 `feature/unittest/ai` 分支(git branch 显示*号)
**📋 代码生成检查**
- [ ] 测试文件名使用小写方法名:`{methodName}.php`
- [ ] 单元测试类名格式:`{moduleName}Test`
@@ -729,14 +667,14 @@ r($userTest->createTest($invalidUser)) && p('errors,account') && e('用户名不
- [ ] **🚨 所有代码行尾无空格**:检查每行末尾是否清洁
**📋 测试验证检查**
- [ ] 已使用 `cd test/runtime && ./ztf run` 命令运行测试
- [ ] 已使用 `php` 命令验证测试脚本无语法错误
- [ ] 已使用 `test/runtime/ztf` 命令运行测试
- [ ] 测试结果:PASS=1, FAIL=0, SKIP=0
- [ ] 所有断言都与实际输出匹配
**📋 代码提交检查**
- [ ] 已添加测试相关文件:`git add module/{moduleName}/test/...`
- [ ] 提交信息符合格式:`+ [misc] Add unit tests for...`
- [ ] 已推送到远程:`git push`
### 📝 代码质量检查项
- [ ] **测试步骤完整**:测试用例包含正常流程、边界值、异常情况等测试步骤
@@ -745,14 +683,9 @@ r($userTest->createTest($invalidUser)) && p('errors,account') && e('用户名不
- [ ] **注释清晰**:测试步骤描述明确,期望结果准确
### ⚠️ 常见错误避免
- [ ] **避免直接在开发分支开发**:必须在测试分支上进行开发
- [ ] **避免使用php直接运行测试**:必须使用ztf运行器验证
- [ ] **避免不提交代码**:测试通过后必须提交并推送代码
- [ ] **避免将规范文档提交到测试分支**:规范文档应该保留在开发分支供后续参照使用
- [ ] **避免不提交代码**:测试通过后必须提交代码
- [ ] **🚨 避免行尾空格**:所有生成的代码必须移除行尾空格,否则提交会被拒绝
---
**🎯 最终目标:通过遵循本指导文档,生成高质量、可维护的ZenTao单元测试脚本,确保代码质量和系统稳定性。**
**🚨 重要提醒:任何跳过分支管理步骤的行为都违反了ZenTao开发规范,必须严格按照流程执行!**
+264
View File
@@ -0,0 +1,264 @@
#!/bin/bash
# 自动生成单元测试脚本
# 作者: Auto-generated script
# 日期: $(date +%Y-%m-%d)
echo_usage() {
echo "Usage: $0 [-c] [-s START_LINE] <csv_file_name>"
echo "This script generates unit test scripts based on the provided CSV file."
echo " -c Continue processing on error (default is to break)."
echo " -s START_LINE Start processing from line START_LINE. Default is 2 (the first line is header)."
exit 1
}
# 检查参数数量
if [ "$#" -eq 0 ]; then
echo_usage
fi
# 检查是否提供了 CSV 文件名
if [ -z "$1" ]; then
echo_usage
fi
# 错误处理策略
ON_ERROR_STRATEGY="break" # 默认为 break
# 默认从第2行开始(跳过标题行)
START_LINE=2
# 解析命令行选项
while getopts ":cs:" opt; do # 注意 's:' 表示 s 选项需要一个参数
case ${opt} in
c )
ON_ERROR_STRATEGY="continue"
;;
s )
# 验证 START_LINE 是否为正整数
if ! [[ "$OPTARG" =~ ^[0-9]+$ ]] || [ "$OPTARG" -lt 1 ]; then
echo "Invalid start line: '$OPTARG'. Must be a positive integer."
exit 1
fi
START_LINE="$OPTARG"
;;
\? )
echo "Invalid Option: -$OPTARG" 1>&2
exit 1
;;
: )
echo "Invalid Option: -$OPTARG requires an argument" 1>&2
exit 1
;;
esac
done
# 移除已处理的选项和参数
shift $((OPTIND -1))
# 设置变量
BASE_PATH=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/../..")
GUIDE_FILE="$BASE_PATH/.claude/rules/zentao-unit-test-guide.md"
CSV_FILE="$BASE_PATH/.claude/data/$1"
LOG_FILE="$BASE_PATH/tmp/log/unit_test_generation_$(date +%Y%m%d_%H%M%S).log"
CURRENT_LINE=0
TOTAL_PROCESSED=0
TOTAL_SKIPPED=0
TOTAL_GENERATED=0
# 检查必要文件和工具
check_prerequisites() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 检查前置条件..." | tee -a "$LOG_FILE"
if [ ! -f "$CSV_FILE" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] CSV文件不存在: $CSV_FILE" | tee -a "$LOG_FILE"
exit 1
fi
if ! command -v claude &> /dev/null; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] claude命令未找到,请确保已安装Claude CLI" | tee -a "$LOG_FILE"
exit 1
fi
if [ ! -d "$BASE_PATH" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] 基础路径不存在: $BASE_PATH" | tee -a "$LOG_FILE"
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 前置条件检查完成" | tee -a "$LOG_FILE"
}
# 调用Claude生成测试脚本
claude_generate_test() {
local claude_command="$1"
local test_file_path="$2"
local retry_count="${3:-0}" # 添加重试计数参数,默认为0
# 设置最大重试次数
local max_retries=12
# 检查是否超过最大重试次数
if [ "$retry_count" -ge "$max_retries" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] 达到最大重试次数($max_retries),放弃处理第 $CURRENT_LINE 行" | tee -a "$LOG_FILE"
return 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 执行Claude命令: $claude_command" | tee -a "$LOG_FILE"
# 执行Claude命令并捕获输出
local claude_output
if claude_output=$($claude_command < /dev/null 2>&1); then
echo "$(date '+%Y-%m-%d %H:%M:%S') [SUCCESS] Claude命令执行成功" | tee -a "$LOG_FILE"
# 记录测试文件已生成到日志
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 测试文件已生成: $test_file_path" | tee -a "$LOG_FILE"
# 记录详细的Claude输出到日志
echo "$(date '+%Y-%m-%d %H:%M:%S') [DEBUG] Claude输出内容:" | tee -a "$LOG_FILE"
echo "--- 开始输出 ---" | tee -a "$LOG_FILE"
echo "$claude_output" | tee -a "$LOG_FILE"
echo "--- 结束输出 ---" | tee -a "$LOG_FILE"
((TOTAL_GENERATED++))
else
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] Claude命令执行失败" | tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] 错误信息: $claude_output" | tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 等待5分钟后重试..." | tee -a "$LOG_FILE"
# 等待5分钟
sleep 300
# 递归重试
claude_generate_test "$claude_command" "$test_file_path" "$((retry_count + 1))"
# 返回递归调用的结果
return $?
fi
return 0
}
# 处理单个方法的单元测试生成
process_method() {
local module="$1"
local class="$2"
local method="$3"
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 处理第 $CURRENT_LINE 行: 模块=$module, 类=$class, 方法=$method" | tee -a "$LOG_FILE"
# 构建测试文件路径(方法名转为小写)
local method_lower=$(echo "$method" | tr '[:upper:]' '[:lower:]')
local test_file_path="$BASE_PATH/module/$module/test/$class/$method_lower.php"
# 检查测试文件是否存在
if [ -f "$test_file_path" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 测试文件已存在,跳过: $test_file_path" | tee -a "$LOG_FILE"
((TOTAL_SKIPPED++))
return 0
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 测试文件不存在,开始生成: $test_file_path" | tee -a "$LOG_FILE"
# 创建测试目录(如果不存在)
local test_dir="$BASE_PATH/module/$module/test/$class"
if [ ! -d "$test_dir" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 创建测试目录: $test_dir" | tee -a "$LOG_FILE"
mkdir -p "$test_dir"
fi
# 构建Claude命令
local claude_prompt="根据$GUIDE_FILE文档的内容为$module模块的$class.php文件中的$method方法生成单元测试脚本。"
local claude_command="claude -p \"$claude_prompt\" --dangerously-skip-permissions"
# 调用Claude生成测试脚本
claude_generate_test "$claude_command" "$test_file_path"
# 返回Claude生成测试脚本的结果
return $?
}
# 主处理函数
main() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] ========== 开始执行单元测试生成脚本 ==========" | tee -a "$LOG_FILE"
# 检查前置条件
check_prerequisites
# 读取CSV文件(跳过第一行标题)
local line_count=$(wc -l < "$CSV_FILE")
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] CSV文件总行数: $line_count (包含标题行)" | tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 从第 $START_LINE 行开始处理 (跳过前面的数据行和标题行)." | tee -a "$LOG_FILE"
# 使用while循环读取CSV文件
while IFS=',' read -r module class method || [ -n "$module" ]; do
((CURRENT_LINE++))
# 跳过标题行
if [ $CURRENT_LINE -eq 1 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 跳过标题行" | tee -a "$LOG_FILE"
continue
fi
# 跳过指定行数之前的数据行和标题行
if [ $CURRENT_LINE -lt $START_LINE ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 跳过第 $CURRENT_LINE 行 (在起始行 $START_LINE 之前)." | tee -a "$LOG_FILE"
continue
fi
# 清理字段中的回车符和空白字符
module=$(echo "$module" | tr -d '\r\n' | xargs)
class=$(echo "$class" | tr -d '\r\n' | xargs)
method=$(echo "$method" | tr -d '\r\n' | xargs)
# 跳过空行
if [ -z "$module" ] || [ -z "$class" ] || [ -z "$method" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [WARN] 第 $CURRENT_LINE 行数据不完整,跳过: 模块=$module, 类=$class, 方法=$method" | tee -a "$LOG_FILE"
continue
fi
# 处理当前方法
if process_method "$module" "$class" "$method"; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [SUCCESS] 第 $CURRENT_LINE 行处理完成" | tee -a "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] 第 $CURRENT_LINE 行处理失败" | tee -a "$LOG_FILE"
# 根据错误处理策略决定是否继续
if [ "$ON_ERROR_STRATEGY" == "break" ]; then
break
else
continue
fi
fi
((TOTAL_PROCESSED++))
# 进度提示
if [ $((TOTAL_PROCESSED % 10)) -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [PROGRESS] 已处理 $TOTAL_PROCESSED 个方法" | tee -a "$LOG_FILE"
fi
done < "$CSV_FILE"
# 输出统计信息
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] ========== 执行完成 ==========" | tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [STATS] 总处理数量: $TOTAL_PROCESSED" | tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [STATS] 跳过数量: $TOTAL_SKIPPED" | tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [STATS] 生成数量: $TOTAL_GENERATED" | tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 日志文件: $LOG_FILE" | tee -a "$LOG_FILE"
}
# 信号处理函数
cleanup() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 接收到中断信号,正在清理..." | tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') [STATS] 中断前已处理: $TOTAL_PROCESSED 个方法" | tee -a "$LOG_FILE"
exit 130
}
# 注册信号处理
trap cleanup INT TERM
# 执行主函数
main
echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 脚本执行完成!" | tee -a "$LOG_FILE"
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
CURRENT_DIR="$(dirname "$(readlink -f "$0")")"
SCRIPT_NAME="unit_test_generator.sh"
SCRIPT_PATH="$CURRENT_DIR/$SCRIPT_NAME"
LOG_FILE="/tmp/unit_test_monitor.log"
# 检测进程是否存在
if ! pgrep -f "$SCRIPT_NAME" > /dev/null; then
# 重启脚本并记录日志
nohup bash "$SCRIPT_PATH" 2>&1 &
echo "$(date '+%Y-%m-%d %H:%M:%S') - 进程未运行,已重启" >> "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - 进程正常运行" >> "$LOG_FILE"
fi
+2
View File
@@ -0,0 +1,2 @@
# .gitattributes
.claude/** -diff
+6 -2
View File
@@ -86,8 +86,12 @@ spec:
${GITFOX_WORKSPACE_BASE}
- ls -l $ZENTAO_RELEASE_PATH
- ls -l $ZENTAO_BUILD_PATH
- cp -rf $ZENTAO_BUILD_PATH/zentaopms/* .
- mkdir ${GITFOX_WORKSPACE_BASE}/testcode
- cp -rf $ZENTAO_BUILD_PATH/zentaopms/* ${GITFOX_WORKSPACE_BASE}/testcode && cp -r ${GITFOX_WORKSPACE_BASE}/zentaopms/test ${GITFOX_WORKSPACE_BASE}/testcode/
- cd ${GITFOX_WORKSPACE_BASE}/testcode
- rm -rf tmp
- echo "<?php\n\$config->edition = 'ipd';"
- echo "<?php\n\$config->edition = 'ipd';" > config/ext/edition.php
- name: 镜像构建
type: run
spec:
@@ -104,7 +108,7 @@ spec:
--build-arg VERSION=${ZENTAO_VERSION} --build-arg MIRROR=true
-t
hub.qc.oop.cc/zentao-ztf:${GITFOX_BUILD_NUMBER}-${GITFOX_COMMIT}
-f misc/ci/Dockerfile.test .
-f misc/ci/Dockerfile.test ${GITFOX_WORKSPACE_BASE}/testcode
- name: unit test
type: run
spec:
+2 -3
View File
@@ -69,8 +69,7 @@ common:
# delete the unused files.
find zentaopms -name .gitkeep |xargs rm -fr
find zentaopms -name tests |xargs rm -fr
echo $(DELETE_TEST)
if [ "$(DELETE_TEST)" = "true" ]; then find zentaopms -name test |xargs rm -fr; fi
if [ "$(DELETE_TEST)" != "false" ]; then find zentaopms -name test |xargs rm -fr; fi
# notify.zip.
mkdir zentaopms/www/data/notify/
zentaoxx:
@@ -226,7 +225,7 @@ package:
#rm -r zentaopms/module/misc/ext
rm -rf zentaopms/misc
rm -rf zentaopms/extension/xuanxuan
if [ "$(DELETE_TEST)" = "true" ]; then find zentaopms/module -type d -name 'test' -maxdepth 2 -exec rm -rf {} +; fi
if [ "$(DELETE_TEST)" != "false" ]; then find zentaopms/module -type d -name 'test' -maxdepth 2 -exec rm -rf {} +; fi
pms:
make common
make zentaoxx
+183
View File
@@ -0,0 +1,183 @@
# 禅道PMS项目上下文
## 交互
始终用中文回复。
## 项目概述
禅道PMS(项目管理系统)是由禅道软件(青岛)有限公司开发的开源项目管理软件。它是中国第一个开源项目管理软件,旨在覆盖从产品和项目管理到质量管理、文档管理、组织管理和办公管理的主要项目管理流程。
### 主要特性
- 基于禅道PHP框架构建
- 支持多种项目管理方法论:Scrum、瀑布和看板
- 全面的功能包括产品管理、项目管理、质量管理、文档管理
- 多语言支持(中文、英文、德语、法语等)
- 丰富的模块包括数据可视化、度量、DevOps、文档资产管理、自动化测试
- 版本21.7.5(根据VERSION文件)
### 架构
禅道采用模块化MVC架构:
- **框架**:位于`/framework`的自定义PHP框架
- **模块**:位于`/module`的功能模块(如产品、项目、执行、缺陷、任务等)
- **数据库**:基于MySQL,模式在`/db/zentao.sql`中定义
- **Web界面**:位于`/www`的基于PHP的Web应用程序
- **配置**:位于`/config`的集中配置
## 项目结构
```
zentaopms/
├── api/ # API相关文件
├── bin/ # 可执行二进制文件/脚本
├── config/ # 配置文件
├── db/ # 数据库模式文件
├── doc/ # 文档
├── extension/ # 扩展模块
├── framework/ # 核心框架文件
├── lib/ # 库文件
├── misc/ # 杂项文件
├── module/ # 应用模块
├── sdk/ # 软件开发工具包
├── tmp/ # 临时文件
├── www/ # Web根目录
└── README.md # 项目文档
```
## 构建和运行
### 先决条件
- PHP 8.1或更高版本
- MySQL数据库
- Apache或Nginx Web服务器
### 构建过程
项目使用Makefile进行构建:
1. **清理构建环境**:`make clean`
2. **构建应用程序**:`make`或`make all`
3. **创建包**:`make package`
4. **创建分发版**:`make pms`
构建过程:
- 将必要文件复制到`zentaopms`目录
- 处理和压缩JavaScript/CSS资源
- 设置适当权限
- 创建分发包(ZIP、tar.xz)
### 安装
1. 将分发包解压到Web服务器目录
2. 在`config/my.php`中配置数据库设置(从`config/config.php`复制)
3. 访问Web界面并按照安装向导操作
4. 如果未安装,系统将自动重定向到`install.php`
### 开发环境运行
1. 将源代码放置在Web服务器的文档根目录中
2. 配置Web服务器以服务PHP文件
3. 确保以下目录可写:
- `tmp/`
- `www/data/`
- `config/`
- `extension/`
### 生产环境运行
1. 使用`make pms`构建分发包
2. 解压到生产服务器
3. 设置目录的适当权限
4. 在`config/my.php`中配置数据库连接
5. 通过Web浏览器访问以完成安装
## 配置
### 主配置
主配置在`config/config.php`中,重要设置包括:
- 数据库连接设置
- 请求路由配置
- 语言和主题设置
- 安全设置
- 文件上传限制
### 自定义配置
创建`config/my.php`来覆盖默认设置:
- 从`config/config.php`复制
- 修改数据库连接设置
- 根据需要调整其他配置
### 数据库配置
数据库表在`config/zentaopms.php`中定义为常量:
- 表前缀:默认为`zt_`
- 超过100个表用于不同功能
- 模式在`db/zentao.sql`中定义
## 开发规范
### 代码结构
- 遵循MVC模式,包含模型、视图和控制器
- 模块组织在`/module/{module}/`目录中
- 每个模块通常包含:
- `model.php`:业务逻辑
- `control.php`:控制器动作
- `view/`:模板文件
- `lang/`:语言文件
- `js/`:模块特定的JavaScript
- `css/`:模块特定的CSS
### 路由
- 默认路由:`index.php?m={module}&f={method}`
- PATH_INFO路由:`index.php/{module}-{method}`
- 可在`config/routes.php`中自定义API路由
### 数据库访问
- 使用自定义DAO(数据访问对象)模式
- 定义在`lib/base/dao/`中
- 示例:`$this->dao->select('*')->from(TABLE_USER)->where('id')->eq($id)->fetch()`
### 测试
- 测试位于模块目录下的`test/`中
## 主要模块
禅道具有广泛的模块结构:
- **产品管理**:product、story、productplan、release
- **项目管理**:project、execution、task、build
- **质量管理**:bug、testcase、testtask、testsuite
- **文档管理**:doc、doclib
- **组织管理**:company、dept、user、group
- **DevOps**:repo、job、compile、mr
- **报表**:report、pivot、chart
- **管理**:admin、custom、extension
## API和集成
- 通过`api.php`提供RESTful API
- Webhooks支持集成
- 支持GitLab、Gogs、Gitea、Jenkins、SonarQube
- Xuanxuan集成用于消息传递
## 安全
- 输入验证和过滤
- XSS防护
- CSRF防护
- 通过DAO防止SQL注入
- 文件上传限制
## 国际化
- 多语言支持,语言文件在`/module/{module}/lang/`中
- 支持的语言:简体中文、繁体中文、英文、德语、法语、越南语、日语、西班牙语、葡萄牙语
- 语言选择可在用户偏好中配置
+1 -1
View File
@@ -95,7 +95,7 @@ class bugEntry extends entry
$oldBug = $this->loadModel('bug')->getByID($bugID);
/* Set $_POST variables. */
$fields = 'uid,title,project,execution,openedBuild,assignedTo,pri,severity,type,story,resolvedBy,resolvedBuild,closedBy,resolution,product,plan,task,module,steps,mailto,keywords,deadline';
$fields = 'uid,title,project,execution,openedBuild,assignedTo,pri,severity,type,story,resolvedBy,resolvedBuild,resolvedDate,closedBy,resolution,product,plan,task,module,steps,mailto,keywords,deadline';
$this->batchSetPost($fields, $oldBug);
$this->setPost('notifyEmail', implode(',', $this->request('notifyEmail', array())));
+1 -1
View File
@@ -66,7 +66,7 @@ class bugsEntry extends entry
$control = $this->loadController('bug', 'create');
$fields = 'title,project,execution,openedBuild,assignedTo,pri,module,severity,type,story,task,mailto,keywords,steps,uid,deadline';
$fields = 'title,project,execution,openedBuild,browser,os,assignedTo,pri,module,severity,type,story,task,mailto,keywords,steps,uid,deadline';
$this->batchSetPost($fields);
$caseID = $this->request('case', 0);
+10 -9
View File
@@ -88,15 +88,16 @@ class executionsEntry extends entry
$this->batchSetPost($fields);
$projectID = $this->param('project', $projectID);
$this->setPost('project', $projectID);
$this->setPost('acl', $this->request('acl', 'private'));
$this->setPost('PO', $this->request('PO', ''));
$this->setPost('PM', $this->request('PM', ''));
$this->setPost('QD', $this->request('QD', ''));
$this->setPost('RD', $this->request('RD', ''));
$this->setPost('whitelist', $this->request('whitelist', array()));
$this->setPost('products', $this->request('products', array()));
$this->setPost('plans', $this->request('plans', array()));
$this->setPost('project', $projectID);
$this->setPost('acl', $this->request('acl', 'private'));
$this->setPost('PO', $this->request('PO', ''));
$this->setPost('PM', $this->request('PM', ''));
$this->setPost('QD', $this->request('QD', ''));
$this->setPost('RD', $this->request('RD', ''));
$this->setPost('whitelist', $this->request('whitelist', array()));
$this->setPost('products', $this->request('products', array()));
$this->setPost('plans', $this->request('plans', array()));
$this->setPost('teamMembers', $this->request('teamMembers', array()));
$requireFields = 'name,begin,end';
if($useCode) $requireFields .= ',code';
+2
View File
@@ -653,6 +653,8 @@ if(!defined('TABLE_SYSTEM')) define('TABLE_SYSTEM', '`' . $config->db->p
define('JIRA_TMPRELATION', '`jiratmprelation`');
define('CONFLUENCE_TMPRELATION', '`confluencetmprelation`');
if(!defined('FIRST_RELEASE_DATE')) define('FIRST_RELEASE_DATE', '2010-05-03');
$config->objectTables['dept'] = TABLE_DEPT;
$config->objectTables['product'] = TABLE_PRODUCT;
$config->objectTables['productplan'] = TABLE_PRODUCTPLAN;
+13 -13
View File
@@ -676,9 +676,9 @@ CREATE TABLE `zt_bug` (
KEY `assignedTo` (`assignedTo`),
KEY `deleted` (`deleted`),
KEY `project` (`project`),
KEY `feedback` (`feedback`),
KEY `product_status_deleted` (`product`,`status`,`deleted`),
KEY `idx_repo` (`repo`)
KEY `idx_repo` (`repo`),
KEY `feedback` (`feedback`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `zt_build` (
`id` mediumint unsigned NOT NULL AUTO_INCREMENT,
@@ -745,7 +745,7 @@ CREATE TABLE `zt_case` (
`subStatus` varchar(30) NOT NULL DEFAULT '',
`color` char(7) NOT NULL DEFAULT '',
`frequency` enum('1','2','3') NOT NULL DEFAULT '1',
`order` tinyint unsigned NOT NULL DEFAULT '0',
`order` mediumint unsigned NOT NULL DEFAULT '0',
`openedBy` char(30) NOT NULL DEFAULT '',
`openedDate` datetime DEFAULT NULL,
`reviewedBy` varchar(255) NOT NULL DEFAULT '',
@@ -1803,8 +1803,8 @@ CREATE TABLE `zt_kanbancell` (
`type` char(30) NOT NULL DEFAULT '',
`cards` mediumtext,
PRIMARY KEY (`id`),
KEY `lane` (`lane`),
UNIQUE KEY `card_group` (`kanban`,`type`,`lane`,`column`)
UNIQUE KEY `card_group` (`kanban`,`type`,`lane`,`column`),
KEY `lane` (`lane`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `zt_kanbancolumn` (
`id` int NOT NULL AUTO_INCREMENT,
@@ -3416,9 +3416,9 @@ CREATE TABLE `zt_story` (
KEY `parent` (`parent`),
KEY `product` (`product`),
KEY `root` (`root`),
KEY `feedback` (`feedback`),
KEY `status` (`status`),
KEY `assignedTo` (`assignedTo`)
KEY `assignedTo` (`assignedTo`),
KEY `feedback` (`feedback`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `zt_storyestimate` (
`story` mediumint NOT NULL DEFAULT '0',
@@ -3555,8 +3555,8 @@ CREATE TABLE `zt_task` (
KEY `parent` (`parent`),
KEY `path` (`path`),
KEY `assignedTo` (`assignedTo`),
KEY `feedback` (`feedback`),
KEY `order` (`order`)
KEY `order` (`order`),
KEY `feedback` (`feedback`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `zt_taskestimate` (
`id` mediumint unsigned NOT NULL AUTO_INCREMENT,
@@ -3752,8 +3752,8 @@ CREATE TABLE `zt_ticket` (
`deleted` enum('0','1') NOT NULL DEFAULT '0',
`subStatus` varchar(30) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `feedback` (`feedback`),
KEY `product` (`product`)
KEY `product` (`product`),
KEY `feedback` (`feedback`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `zt_ticketrelation` (
`id` mediumint unsigned NOT NULL AUTO_INCREMENT,
@@ -3800,10 +3800,10 @@ CREATE TABLE `zt_todo` (
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
PRIMARY KEY (`id`),
KEY `account` (`account`),
KEY `feedback` (`feedback`),
KEY `assignedTo` (`assignedTo`),
KEY `finishedBy` (`finishedBy`),
KEY `date` (`date`)
KEY `date` (`date`),
KEY `feedback` (`feedback`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `zt_traincategory` (
`id` mediumint unsigned NOT NULL AUTO_INCREMENT,
+5
View File
@@ -10,3 +10,8 @@ CREATE INDEX `feedback` ON `zt_story`(`feedback`);
CREATE INDEX `feedback` ON `zt_task`(`feedback`);
CREATE INDEX `feedback` ON `zt_ticket`(`feedback`);
CREATE INDEX `feedback` ON `zt_todo`(`feedback`);
CREATE OR REPLACE VIEW `ztv_executionsummary` AS SELECT `zt_task`.`execution` AS `execution`,SUM(IF((`zt_task`.`isParent` = '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` = '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` = '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` = '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` = '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`execution`;
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` = '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` = '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` = '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` = '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` = '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
REPLACE INTO `zt_grouppriv` (`group`, `module`, `method`) SELECT `group`, `module`, 'edit' FROM `zt_grouppriv` WHERE `module` = 'doc' AND `method` = 'create';
+2 -2
View File
@@ -2753,9 +2753,9 @@ CREATE TABLE IF NOT EXISTS `zt_report` (
CREATE UNIQUE INDEX `code` ON `zt_report`(`code`);
-- DROP VIEW IF EXISTS `ztv_executionsummary`;
CREATE OR REPLACE VIEW `ztv_executionsummary` AS SELECT `zt_task`.`execution` AS `execution`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`execution`;
CREATE OR REPLACE VIEW `ztv_executionsummary` AS SELECT `zt_task`.`execution` AS `execution`,SUM(IF((`zt_task`.`isParent` = '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` = '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` = '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` = '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` = '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`execution`;
-- DROP VIEW IF EXISTS `ztv_projectsummary`;
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` = '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` = '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` = '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` = '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` = '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
-- DROP VIEW IF EXISTS `ztv_projectstories`;
CREATE OR REPLACE VIEW `ztv_projectstories` AS SELECT `t1`.`project` AS `execution`,COUNT(1) AS `stories`,SUM(IF((`t2`.`status` = 'closed'),0,1)) AS `undone` FROM ((`zt_projectstory` `t1` LEFT JOIN `zt_story` `t2` ON((`t1`.`story` = `t2`.`id`))) LEFT JOIN `zt_project` `t3` ON((`t1`.`project` = `t3`.`id`))) WHERE ((`t2`.`deleted` = '0') AND (`t3`.`type` IN ('sprint','stage'))) GROUP BY `t1`.`project`;
-- DROP VIEW IF EXISTS `ztv_projectteams`;
+165 -2
View File
@@ -1,19 +1,49 @@
2025-08-22 21.7.5
2025-09-11 21.7.5
完成的需求
开源版:
77897 创建发布/应用/构建页面增加帮助说明
77640 重构搜索表单相关功能以提高性能
77398 源码包安装流程中增加埋点
75100 文档编辑器增加对齐功能
75100 文档编辑器增加文本对齐功能
企业版:
76672 重构文档历史版本对比功能
73517 工作流配置的流程中在详情点击返回可以返回上一页面
修复的Bug
开源版:
22932 Bug重要程度阶段分布图标中鼠标悬浮后信息显示不全
46933 【20版本】点击搜索按钮后搜索表单会消失
54900 测试下批量建用例超过50个,点击保存提示用例类型不能为空
55125 任务列表下进入需求返回页面不是任务列表 (Feedback#7052)
55259 测试用例列表页点击相关研发需求后,详情页点击返回时没有返回到用例列表
55577 模块名称中不能输入空格 (Feedback#7177)
58165 文档详情页中图片复制按钮无效
60076 提bug时,切换所属产品,页面会刷新,数据不保存
61043 草稿文档切换为新编辑器保存草稿页面显示代码
61176 安装第三步提示“还没有生成配置文件” (Feedback#8912)
61312 编辑文档无法粘贴图片 (Feedback#8877)
61316 旧编辑器文档不显示插入的图片
61737 [内禅]库卡片统计的文档数量不正确
61739 [内禅]库卡片的文档数量统计信息没有常显
61763 [内禅]文档历史版本中图片裂开
61766 [内禅]升级后文档对齐方式丢失
61777 markdown的列表项中和简单表格中的图片升级后不显示
61779 [内禅]文档升级后文字位置不正确
61790 [内禅]文档升级后表格中显示滚动条
61791 升级后文档对空白行的处理不一致
61792 [内禅]文档显示图片加载失败
61795 [内禅]升级后的文档中不是列表项的图片不显示在详情页和编辑页面
61798 升级后缩进(空格)的样式丢失
61805 【内禅】一块引用升级后被拆分成两块
61807 【内禅】文字和图片的位置升级后改变了
61813 [内禅]内禅升级后的内禅文档历史记录和历史版本中的@可以点击
61815 [内禅]文档升级后表格中的换行丢失
61816 【内禅】文档升级后表格的字体发生变化加粗丢失
61826 【内禅】列表升级后有多余的间距
62391 提Bug页面内容全部刷新
64109 批量编辑产品页面负责人字段无法回显也无法选择 (Feedback#9853)
64121 首页-待处理-点击待评审需求提示app not found。 (Feedback#9665)
64133 当gitlab服务器包含端口时链接代码库失败 (Feedback#9883)
64222 离职人员历史的文档无法打开
64389 删除项目后并没有删除该项目下相关的目标评审 (Feedback#9988)
64400 合并产品分支报错 (Feedback#9987)
64473 用例库导出用例报错 (Feedback#10004)
@@ -21,44 +51,126 @@
64516 项目概况中的项目描述过多时显示不全 (Feedback#10045)
64528 视野权限取消勾选devops后管理应用按钮也会消失 (Feedback#9906)
64529 多人并行任务指派给 在地盘待处理 不显示 (Feedback#9902)
64536 解决Bug选择重复Bug编辑时重复Bug的字段是空的
64539 任务列表父子任务显示不正确
64540 文档插入多层级的禅道数据时只能插入第一层级的任务
64549 用户没有需求提交评审权限,但是仍展示了按钮,点击弹出异常框 (Feedback#9803)
64581 编辑需求、用例、bug日志,工时显示错误
64599 文档编辑权限异常 (Feedback#10044)
64623 激活Bug上传的图片在历史记录中显示是裂开的
64637 编辑bug的模块,丢失指派给 (Feedback#10064)
64639 Scrum教程到“点击指派给”卡住不能往下继续了 (Feedback#10085)
64685 组织动态搜索排序选项丢失
64693 没有提交评审权限,需求列表显示了提交评审按钮
64717 项目使用重新定义权限分组时不能从项目执行列表访问项目下执行
64720 项目文档添加研发需求列表,弹窗中报错
64751 激活bug时上传截图保存后截图展示异常 (Feedback#10098)
64758 组织-团队-日程切换其他标签后显示页面不同 (Feedback#10105)
64759 文档库设置了排序后文档库列表不能按照设置的顺序显示文档顺序
64765 项目与产品解除关联后编辑项目下的产品有报错
64770 因为`oder`数据库字段长度限制导致导入用例报错 (Feedback#10118)
64776 Bug列表批量指派功能,无法通过拼音或者首字母搜索人员 (Feedback#10056)
64780 用例导入html格式的文件报错
64787 运营管理界面分配权限报错
64792 批量添加用户报错
64795 在Xmind 编辑页面,点击保存后出现的确认弹窗点击取消后仍然保存
64808 restful新建bug接口,无法传浏览器和操作系统参数 (Feedback#10084)
64809 关联到发布,验收,点发布,这种情况下需求的阶段不变 (Feedback#10135)
64815 用例覆盖率统计口径与用例需求关联范围不一致的问题 (Feedback#10137)
64821 批量编辑项目置空名称保存后再刷新页面弹出保存成功提示
64822 项目下搜索表单空白
64857 用户没有编辑文档权限,发布文档后是草稿状态
64868 多分支产品分支和白名单页面显示了1.5级分支下拉
64871 运营界面待处理区块报错
64872 运营界面待处理区块目标未显示所属项目
64875 开始、激活、继续任务预计剩余为负数报错
64880 项目迭代列表页搜索后点击列表表头排序不生效
64893 激活bug时,备注添加图片,保存后不显示 (Feedback#10165)
64903 项目干系人无法查看此项目下的迭代/执行 (Feedback#10163)
64904 批量创建时过滤了尖括号之间的内容 (Feedback#10066)
64920 任务搜索相关研发需求的条件下拉框没有显示相关研发需求
64923 贡献任务列表搜索框没加载显示出来
64925 项目--迭代/阶段列表页执行搜索后,总是列出所有任务
64927 项目--迭代/阶段列表执行搜索后重置搜索条件,没有列出所有任务
64938 新手教程任务管理页面有报错
64939 复制已完成的多人任务时团队未复制且没有历史记录
64941 编辑场景名称置空保存,必填提示不正确
64967 执行--任务,【最初预计】和【预计剩余】小数点后显示很多位
64968 数据库中工时保留两位小数
64990 已经关闭的bug,在列表中还能错误的指派给某个人 (Feedback#10186)
65004 集成应用创建发布时包含应用的+按钮没有常显
65007 批量创建需求,无法选择对应的模块 (Feedback#10024)
65021 部分发布数据在发布列表中不显示
65022 集成应用的发布包含的构建缺失
65023 用例执行时上传的附件无法正确保存、显示 (Feedback#10217)
65024 批量编辑页面已关闭的bug指派给字段应置灰
65030 demo账户登录时跳转修改密码页
65032 内禅历史记录中会显示额外的代码
65033 套件/测试报告的无创建权限仍然显示 (Feedback#10225)
65038 所属项目显示为ID
65039 任务检索表单空白
65048 多人串行任务团队第一个用户完成后下一个成员无法记录工时
企业版:
39077 用户分配工作流“流程详情”权限后流程页面未显示“预览”
55416 待处理下批量编辑反馈三级导航菜单未高亮
58762 工作流数据关联工作流数据名称错误
58863 组织动态页面搜索页面和前面的控件距离太近
61129 反馈列表页左侧区块中产品名称加粗显示了
62476 导出用户xls文件第10个用户没有背景色
62491 发布工作流有报错
62537 linux一键包中项目阶段下导出xls模板后弹窗未关闭
62654 产品下导入xls格式数据,弹窗没有关闭
62669 项目阶段导入任务时上传一个空的excel保存时会报错
64317 导入研发需求确认页面评审人建议增加[同上]按钮
64468 批量编辑反馈,设置显示多个自定义字段时,字段重叠 (Feedback#10010)
64470 项目需求导入导出权限配置错误 (Feedback#10005)
64481 用例批量编辑与单个编辑表单数据不一致 (Feedback#10038)
64538 自定义透视表查看原始数据报错
64675 配置了office后,文档详情页不显示预览按钮
64696 产品下导入产品需求报错
64714 预览导入的office文档在禅道中打开没有新开标签
64716 导出文档文档中甘特图和附件没有链接
64762 需求导出xls页面报错
64763 导出项目进度没有显示百分比
64764 导出项目的已投入列建议显示单位人天
64801 用例库里导入excel时页面有报错
64810 研发需求重复导入报错
64824 反馈状态更新为已处理后没有发送消息通知 (Feedback#10142)
64859 运营界面,反馈列表的状态字段不显示 (Feedback#10154)
64888 发布的工作流配置动作后在项目里不生效 (Feedback#1822)
64889 项目和迭代下的任务指派给分布饼图不一致
64909 任务进行搜索后查看报表有报错
64913 英文下迭代列表基本统计报表空白有报错
64928 企业版和旗舰版后台通知设置页面显示了“由需求池需求处理”
64934 导入excel表格报错
64985 升级biz12.3报错 (Feedback#10198)
65001 导出用户xls格式没有隐藏
65012 bug导出模板里面包含的模块,导入不展示这个模块 (Feedback#10209)
65031 开源版升级到企业版后访问禅道页面报错
65036 导入bug确认页面指派给下拉列出了closed
65054 项目下迭代报表任务统计等页面有报错
65063 执行需求报表页面报错
65069 研发需求导入时评审人必填提示文案不对
65077 导入需求确认页面评审人默认同上了
65081 导出用户后导出弹窗未关闭
65097 项目下阶段列表导入任务时有报错
65100 地盘日程软需详情页点击用例按钮后弹窗页置灰了
65149 项目型项目创建的反馈,转研发需求后,项目需求列表看不到 (Feedback#10251)
65177 快捷访问下的文档点击关联对象按钮无反应
65196 工时明细表按完成者和关闭者分组时报错
65206 无产品项目的日志导出后产品列显示了影子产品
65207 无产品项目计划的日志,导出后项目列显示空
65213 项目投入表查询不到任务消耗
65225 导出xls用户数据后再导入未成功导入数据
65252 导出xls用户数据时确认导入页有报错
65268 休息日加班,考勤显示为休息 (Feedback#10281)
65310 项目迭代报表下的资源分析页面有报错
65321 工单转研发需求不显示自定义字段 (Feedback#10276)
65329 PHP70加密代码,任务统计报表的基本统计中有些报错
65361 导入任务无法导入多人任务
旗舰版:
24150 添加自定义项不适用某个语言时,该语言下字段显示为空了
61007 组织动态搜索之后翻页异常 (Feedback#8703)
64338 项目评审问题列表中选择评审项时会返回已删除评审项 (Feedback#9942)
64384 审批人必填提示信息不正确
64480 项目甘特图下无法拖拽任务更新任务排序 (Feedback#9998)
@@ -69,18 +181,69 @@
64761 工单编辑界面编辑器还是老版本编辑器 (Feedback#10107)
64782 升级后过程列表和活动列表每页按照左侧区块顺序显示
64784 新建产品无法选择产品线 (Feedback#10101)
64796 瀑布项目设计评审页面无评审人员选项,导致提交失败 (Feedback#10129)
64797 项目模板无法新增子阶段 (Feedback#10126)
64832 批量创建设计,关联对象不自动绑定 (Feedback#10152)
64847 瀑布项目评审时填写的工时被自动取整 (Feedback#10149)
64850 上传有大小写字母的文件类型,下载文件有双后缀 (Feedback#10094)
64851 项目型项目工单日志搜索异常 (Feedback#10148)
64858 立项中上传其他附件时无法上传多个 (Feedback#10140)
64865 集成应用发布,列表包括应用和发布显示错乱且缺少集成icon (Feedback#10159)
64866 集成应用的发布,发布描述显示不正常 (Feedback#10158)
64873 项目模板下点击阶段的二级导航,页面报错
64874 文档插入甘特图有报错
64879 瀑布项目阶段列表点击甘特图按钮报错
64917 瀑布项目甘特图按指派给分组没有显示未指派的任务
64919 瀑布项目甘特图搜索条件不起作用
64924 甘特图搜索表单未列出所属模块
64936 从已有项目创建模板页面报错
64962 excel导入任务,导入的任务层级关系不正确 (Feedback#10146)
64981 【合并】工作流字段设置为隐藏后错误的要求其必填
64983 工作流富文本字段不能正常使用 (Feedback#9495)
64986 反馈列表所属模块字段错误的显示成了模块id (Feedback#10201)
64997 项目型瀑布项目模板,创建项目完善阶段页面空白,无法正常读取模板阶段 (Feedback#10212)
65018 文档中添加甘特图提示需优化
65041 基线评审列表页报错
65049 反馈全文检索问题 (Feedback#10181)
65052 php8.1安装报错
65099 QA不符合项列表搜索不成功
65101 维护项目/执行交付物,上传文件保存后报错
65113 自定义大屏,部门筛选器没有数据 (Feedback#10122)
65115 点击已关闭执行的设置后,会跳转到其他的执行概况表单 (Feedback#10110)
65117 补全后台-二次开发-数据字典数据表字段为空的描述 (Feedback#9974)
65123 项目-需求列表倒入需求库需求点击详情提示无权限 (Feedback#10240)
65134 需求库导入需求提示信息没有显示
65161 项目模板中拆分子阶段,无法保存 (Feedback#10259)
65164 项目维护交付物页面提交文件保存报错
65166 工作流扩展的审批流程没有给审批人进行通知 (Feedback#9597)
65212 无权限时在报告中点任务链接没有提示信息
65224 提交交付物后,页面有报错
65231 审计时检查清单建议解决时间为空保存报错
65235 升级后交后台付物列表报错
65239 项目下提交的附件交付物升级后文档显示在迭代主库下
65242 升级后项目交付物列表页其他交付物的交付物类型显示为空
65245 升级后交付物的提交人显示system
65246 升级后执行下交付物丢失
65317 评审时上传的图片,在评审报告中显示为裂图
65327 审批流为或签时,有一人评审不通过则评审失败
65339 编辑评审页面修改附件名称后保存时有报错
65381 任务取消异常 (Feedback#10206)
IPD版:
62782 IPD项目阶段添加子级后,子级行末尾显示了“否”
64285 研发界面点击消息通知链接打开反馈详情,没有展示工作流自定义的字段
64422 甘特图上的阶段和评审掉悬停时显示圆圈
64495 PHP7.4 项目-文档下,移动文档库的顺序无效 (Feedback#9865)
64533 执行甘特图,预计工时与消耗工时重复计算了父子任务工时 (Feedback#10031)
64585 复制IPD项目校验子阶段的开始时间应大于父阶段的结束时间
64713 任务附件名称异常 (Feedback#9638)
64916 OR界面进入地盘时有任务搜索表单的相关报错
64978 【性能】填写日志过多导致的性能问题
64979 调研任务完成页面点添加备注页面卡住,需要重新刷新 (Feedback#9840)
64980 反馈数据导出标题列不展示内容 (Feedback#10131)
65203 OR界面用户需求/业务需求保存搜索条件时勾选显示在菜单栏但保存后未显示在菜单栏
65266 立项详情页创建IPD项目时项目类型未置灰
65290 甘特图中提交评审后,提示无法发起评审
65295 立项详情页创建IPD项目保存时提示项目类型不能为空
2025-07-29 21.7.4
完成的需求
+1 -1
View File
@@ -6,5 +6,5 @@ public function getExecutionStoryPairs(int $executionID = 0, int $productID = 0,
$execution = $this->loadModel('execution')->getById($executionID);
if(!empty($execution->project)) $executionID = $execution->project;
}
return parent::getExecutionStoryPairs($executionID, $productID, $branch, $moduleIdList, $type, $status, $storyType);
return parent::getExecutionStoryPairs($executionID, $productID, $branch, $moduleIdList, $type, $status, $storyType, $hasParent);
}
+1 -1
View File
@@ -560,7 +560,7 @@ class baseEntry
}
else
{
$vs = implode(',', $object->$key);
$vs = explode(',', $object->$key);
foreach($vs as $v)
{
if($v === '') continue;
+4 -2
View File
@@ -59,11 +59,13 @@ class scm
* @param string $orderBy
* @param int $limit
* @param int $pageID
* @param string $label
* @param int $showArchived
* @return array
*/
public function branch(string $showDetail = '', string $orderBy = '', int $limit = 0, int $pageID = 1, string $label = '')
public function branch(string $showDetail = '', string $orderBy = '', int $limit = 0, int $pageID = 1, string $label = '', int $showArchived = 0)
{
return $this->engine->branch($showDetail, $orderBy, $limit, $pageID, $label);
return $this->engine->branch($showDetail, $orderBy, $limit, $pageID, $label, $showArchived);
}
/**
+348
View File
@@ -0,0 +1,348 @@
<?php
/**
* sqlfilter - 多语言SQL注入防护过滤器
*
* 设计目标:
* 1. 支持多语言字符(中文、阿拉伯文、俄文、韩文等)
* 2. 防止SQL注入攻击
* 3. 提供不同安全级别的过滤选项
*/
class sqlfilter
{
/**
* 方案1:基于危险字符和模式的过滤(推荐方案)
*
* 核心思路:
* - 保留所有正常的语言字符
* - 只移除明确的SQL注入威胁
* - 多层防护确保安全性
*
* @param string $input 用户输入
* @return string 过滤后的安全字符串
*/
public function filterForSQL($input)
{
// 步骤1:基础验证
if(!is_string($input) || empty(trim($input))) return '';
// 步骤2:移除控制字符(但保留正常的空白字符)
$filtered = $this->removeControlCharacters($input);
// 步骤3:移除SQL注入攻击模式
$filtered = $this->removeSQLInjectionPatterns($filtered);
// 步骤4:处理危险的SQL字符组合
$filtered = $this->removeDangerousSQLChars($filtered);
// 步骤5:去除首尾的空白字符
$filtered = trim($filtered);
return $filtered;
}
/**
* 方案2:Unicode属性过滤 + SQL危险字符过滤
*
* 适用场景:
* - 需要更宽松的字符支持
* - 用户可能输入各种标点符号
* - 对性能要求不是特别高
*
* @param string $input 用户输入
* @return string 过滤后的字符串
*/
public function filterUnicodeForSQL($input)
{
if(!is_string($input) || empty(trim($input))) return '';
// 步骤1:使用Unicode属性类保留语言字符和基本标点
// \p{L} = 所有语言的字母
// \p{N} = 所有数字
// \p{M} = 标记字符(如重音符号)
// \s = 空白字符
// \.\,\!\?\-\_\(\) = 常用标点符号
$filtered = preg_replace('/[^\p{L}\p{N}\p{M}\s\.\,\!\?\-\_\(\)]/u', ' ', $input);
// 步骤2:移除SQL注入模式
$filtered = $this->removeSQLInjectionPatterns($filtered);
// 步骤3:标准化空格
$filtered = preg_replace('/\s+/', ' ', $filtered);
// 步骤4:去除首尾的空白字符
$filtered = trim($filtered);
return $filtered;
}
/**
* 方案3:严格的SQL安全过滤
*
* 适用场景:
* - 高安全要求的环境
* - 可以接受某些特殊字符被过滤
* - 关键数据操作
*
* @param string $input 用户输入
* @return string 过滤后的字符串
*/
public function filterStrictSQL($input)
{
if(!is_string($input) || empty(trim($input))) return '';
// 步骤1:只保留字母、数字、最基本的标点
$filtered = preg_replace('/[^\p{L}\p{N}\s\.\,\!\?\-\_]/u', '', $input);
// 步骤2:移除SQL注入模式
$filtered = $this->removeSQLInjectionPatterns($filtered);
// 步骤3:去除首尾的空白字符
$filtered = trim($filtered);
return $filtered;
}
/**
* 移除控制字符(但保留正常的空白字符)
*
* 控制字符可能被用于绕过安全检查或造成解析错误
*
* @param string $input 输入字符串
* @return string 处理后的字符串
*/
private function removeControlCharacters($input)
{
// 移除C0和C1控制字符
// 但保留:制表符(\t)、换行符(\n)、回车符(\r)
// \x00-\x08: NULL到BACKSPACE
// \x0B: 垂直制表符
// \x0C: 换页符
// \x0E-\x1F: 其他C0控制字符
// \x7F-\x9F: DEL和C1控制字符
return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u', '', $input);
}
/**
* 移除明显的SQL注入攻击模式
*
* 这是防护的核心部分,识别并移除常见的SQL注入技术
*
* @param string $input 输入字符串
* @return string 处理后的字符串
*/
private function removeSQLInjectionPatterns($input)
{
$dangerousPatterns = [
// === SQL注释攻击 ===
'/--[\s\S]*$/i', // 单行注释:-- comment
'/\/\*[\s\S]*?\*\//i', // 块注释:/* comment */
'/#.*$/m', // MySQL特有的#注释
// === 联合查询注入 ===
'/\b(union\s+select)\b/i', // UNION SELECT
'/\b(union\s+all\s+select)\b/i', // UNION ALL SELECT
// === 数据操作注入 ===
'/\b(drop\s+table)\b/i', // DROP TABLE
'/\b(drop\s+database)\b/i', // DROP DATABASE
'/\b(delete\s+from)\b/i', // DELETE FROM
'/\b(insert\s+into)\b/i', // INSERT INTO
'/\b(update\s+.*\s+set)\b/i', // UPDATE ... SET
'/\b(alter\s+table)\b/i', // ALTER TABLE
'/\b(create\s+table)\b/i', // CREATE TABLE
'/\b(truncate\s+table)\b/i', // TRUNCATE TABLE
// === 存储过程和函数执行 ===
'/\b(exec\s*\()\b/i', // EXEC()
'/\b(execute\s*\()\b/i', // EXECUTE()
'/\b(sp_executesql)\b/i', // SQL Server存储过程
'/\b(xp_cmdshell)\b/i', // SQL Server命令执行
// === 文件操作注入 ===
'/\b(load_file)\b/i', // MySQL LOAD_FILE()
'/\b(into\s+outfile)\b/i', // INTO OUTFILE
'/\b(into\s+dumpfile)\b/i', // INTO DUMPFILE
// === 编码绕过尝试 ===
'/\\\\x[0-9a-f]{2}/i', // 十六进制编码 \x41
'/\\\\[0-7]{1,3}/i', // 八进制编码 \101
'/%[0-9a-f]{2}/i', // URL编码 %41
// === 其他危险模式 ===
'/\b(information_schema)\b/i', // 信息模式数据库
'/\b(mysql\.user)\b/i', // MySQL用户表
'/\b(pg_sleep)\b/i', // PostgreSQL延时函数
'/\b(waitfor\s+delay)\b/i', // SQL Server延时
];
// 逐个应用模式,将匹配内容替换为空格
foreach($dangerousPatterns as $pattern) $input = preg_replace($pattern, ' ', $input);
return $input;
}
/**
* 移除危险的SQL字符组合
*
* 处理单个字符可能无害,但组合起来就危险的情况
*
* @param string $input 输入字符串
* @return string 处理后的字符串
*/
private function removeDangerousSQLChars($input)
{
$replacements = [
// === 引号相关 ===
"/'+/i" => "", // 多个单引号:'''
'/"+/i' => "", // 多个双引号:"""
"/'+\s*or\s*'+/i" => " ", // 引号包围的OR:'or'
"/'+\s*and\s*'+/i" => " ", // 引号包围的AND:'and'
// === SQL语法字符 ===
'/;+/' => " ", // 分号(语句分隔符)
'/\|\|/' => " ", // SQL字符串连接符 ||
'/`+/' => "", // 反引号(MySQL标识符)
// === 注释字符组合 ===
'/--+/' => " ", // 多个连字符
'/\/\*|\*\//' => " ", // 注释开始/结束符
// === 危险操作符 ===
'/\s*=\s*[\'"]?\s*[\'"]?\s*/i' => " ", // 可能的等号注入
'/\s+(or|and)\s+[\'"]?\d+[\'"]?\s*=\s*[\'"]?\d+[\'"]?/i' => " ", // 1=1类型注入
];
foreach($replacements as $pattern => $replacement) $input = preg_replace($pattern, $replacement, $input);
return $input;
}
/**
* 验证过滤后的字符串是否仍包含危险特征
*
* 作为最后的安全检查
*
* @param string $input 已过滤的字符串
* @return bool true表示安全,false表示仍有风险
*/
public function validateSafety($input)
{
// 危险特征检查
$dangerousPatterns = [
'/[\'"`;]/i', // 引号和分号
'/\b(select|union|drop|delete|insert|update|alter|create|exec)\b/i', // SQL关键词
'/--|\*\/|\/\*/', // 注释符
'/\|\|/', // 字符串连接
'/\bor\s+\d+\s*=\s*\d+/i', // 1=1类型条件
];
foreach($dangerousPatterns as $pattern)
{
if(preg_match($pattern, $input)) return false; // 发现危险特征
}
return true; // 通过安全检查
}
/**
* 根据安全级别选择合适的过滤方法
*
* @param string $input 用户输入
* @param string $securityLevel 安全级别:'high', 'medium', 'low'
* @return string 过滤后的字符串
*/
public function getRecommendedFilter($input, $securityLevel = 'medium')
{
switch($securityLevel)
{
case 'high':
// 高安全级别:最严格的过滤
return $this->filterStrictSQL($input);
case 'low':
// 低安全级别:更宽松,保留更多字符
return $this->filterUnicodeForSQL($input);
case 'medium':
default:
// 中等安全级别:平衡安全性和可用性
return $this->filterForSQL($input);
}
}
/**
* 批量过滤多个输入
*
* @param array $inputs 输入数组
* @param string $securityLevel 安全级别
* @return array 过滤后的数组
*/
public function filterMultiple($inputs, $securityLevel = 'medium')
{
$filtered = [];
foreach($inputs as $key => $input) $filtered[$key] = $this->getRecommendedFilter($input, $securityLevel);
return $filtered;
}
/**
* 生成过滤报告(调试用)
*
* @param string $original 原始输入
* @param string $filtered 过滤后结果
* @return array 详细的过滤报告
*/
public function generateFilterReport($original, $filtered)
{
return [
'original' => $original,
'filtered' => $filtered,
'length_change' => strlen($original) - strlen($filtered),
'removed_chars' => $this->getRemovedChars($original, $filtered),
'safety_check' => $this->validateSafety($filtered),
'potential_threats' => $this->identifyThreats($original)
];
}
/**
* 识别输入中的潜在威胁(调试和日志记录用)
*
* @param string $input 输入字符串
* @return array 发现的威胁列表
*/
private function identifyThreats($input)
{
$threats = [];
$threatPatterns = [
'sql_injection' => '/\b(union|select|drop|delete|insert|update)\b/i',
'comment_attack' => '/--|\*\/|\/\*/',
'quote_manipulation' => '/[\'"]{2,}/',
'statement_separator' => '/;/',
'encoding_bypass' => '/\\\\x[0-9a-f]{2}|%[0-9a-f]{2}/i'
];
foreach($threatPatterns as $threat => $pattern)
{
if(preg_match_all($pattern, $input, $matches)) $threats[$threat] = $matches[0];
}
return $threats;
}
/**
* 计算被移除的字符
*
* @param string $original 原始字符串
* @param string $filtered 过滤后字符串
* @return string 被移除的字符
*/
private function getRemovedChars($original, $filtered)
{
$originalChars = mb_str_split($original, 1, 'UTF-8');
$filteredChars = mb_str_split($filtered, 1, 'UTF-8');
// 简化的差异检测
$diff = array_diff($originalChars, $filteredChars);
return implode('', array_unique($diff));
}
}
+35 -11
View File
@@ -11,9 +11,8 @@ let savingDocData = {};
let docBasicModal = {};
let currentDocApp = null;
window.setZentaoSlashMenu = function(menus, title, vision, position)
window.setZentaoSlashMenu = function(menus, title, position)
{
if(vision == 'or' || vision == 'lite') return;
if(!menus?.length) return;
const slashMenus = menus.map(menu =>
@@ -94,13 +93,17 @@ function getZentaoListProps(type, blockID, props)
if(props && props.isTemplate) blockID = '__TML_ZENTAOLIST__' + blockID;
const zentaoListLang = getLang('zentaoList');
const exportUrl = $.createLink('doc', 'ajaxExportZentaoList', `blockID=${blockID}`);
const exportUrl = `${location.origin}${$.createLink('doc', 'ajaxExportZentaoList', `blockID=${blockID}`)}`;
const title = [(zentaoListLang ? zentaoListLang[type] : type), getLang('list')].join(config.clientLang.includes('zh') ? '' : ' ');
return $.extend(
{
title: [(zentaoListLang ? zentaoListLang[type] : type), getLang('list')].join(config.clientLang.includes('zh') ? '' : ' '),
exportUrl: `${location.origin}${exportUrl}`,
title: title,
fetcher: $.createLink('doc', 'zentaoList', `type=${type}&blockID=${blockID}`),
clearBeforeLoad: false
}, type === 'gantt' ? {
export: [{type: 'link', props: {text: title, href: exportUrl}}]
} : {
exportUrl: exportUrl,
}, props);
}
@@ -1099,7 +1102,7 @@ const actionsMap =
const canCreateSpace = canModify && hasPriv('createSpace');
const canCreateOffice = canModify && hasPriv('createOffice');
const canUploadDoc = canModify && hasPriv('uploadFile');
const canExportDoc = hasPriv('exportDoc');
const canExportDoc = hasPriv('exportDoc') && docApp.spaceType !== 'quick';
/* 文档列表没有文档时的按钮。Actions for empty doc list. */
if(info.ui === 'doc-list-empty')
@@ -2239,11 +2242,32 @@ window.getDocViewSidebarTabs = function(doc, info)
].filter(Boolean);
}
if(info.isNewDoc) return [{key: 'outline', icon: 'list-box', title: getLang('docOutline')}];
return [
{key: 'outline', icon: 'list-box', title: getLang('docOutline')},
{key: 'history', icon: 'history', title: getLang('history')},
]
let tabList = [];
if(info.isNewDoc)
{
tabList.push({key: 'outline', icon: 'list-box', title: getLang('docOutline')});
}
else
{
tabList.push(
{key: 'outline', icon: 'list-box', title: getLang('docOutline')},
{key: 'history', icon: 'history', title: getLang('history')}
);
}
if(info.mode == 'view' && doc.status != 'draft' && !doc.api)
{
tabList.push(
{
key : 'relateObject',
icon : 'link',
title : getLang('relateObject'),
render: function(doc){return {fetcher: $.createLink('doc', 'ajaxGetRelatedObjects', `docID=${doc.id}`)}}
});
}
return tabList;
}
function getSpaceFetcher(spaceType, spaceID = 0)
+5 -2
View File
@@ -320,11 +320,14 @@ class docApp extends wg
$historyPanelProps = array('fileListProps' => $fileListProps);
$canPreviewOffice = $canDownload && isset($config->file->libreOfficeTurnon) and $config->file->libreOfficeTurnon == 1;
// 不可用场景:文档模板、API 文档、开源版
/* 对比不可用场景:文档模板、API 文档、开源版 */
$diffEnabled = ($config->edition != 'open')
&& !($rawModule == 'doc' && $rawMethod == 'view')
&& $rawModule != 'api';
/* 禅道数据菜单不可用界面:运营(lite)、需求与市场(or) */
if ($config->vision == 'lite' || $config->vision == 'or') $hasZentaoSlashMenu = false;
$zentaoListMenu = $hasZentaoSlashMenu ? $this->getZentaoListMenu() : array();
$moreMenus = $zentaoListMenu
@@ -382,7 +385,7 @@ class docApp extends wg
set::moreMenuAction(jsRaw('window.moreMenuAction')),
set::canPreviewOffice($canPreviewOffice),
set::fileInfoUrl($fileInfoUrl),
$hasZentaoSlashMenu ? jsCall('setZentaoSlashMenu', $zentaoListMenu, $lang->doc->zentaoData, $config->vision, $config->doc->zentaoListMenuPosition) : null
$hasZentaoSlashMenu ? jsCall('setZentaoSlashMenu', $zentaoListMenu, $lang->doc->zentaoData, $config->doc->zentaoListMenuPosition) : null
);
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ class fileList extends wg
foreach($files as $file)
{
if($file->extra != $extra) continue;
if($extra && $file->extra != $extra) continue;
$fileItemView = html($app->loadTarget('file')->printFile($file, $method, $showDelete, $showEdit, $object));
$fileListView->add($fileItemView);
+14
View File
@@ -134,6 +134,20 @@ class formPanel extends panel
}
}
if($moduleName == 'ticket')
{
if($methodName == 'createstory')
{
$moduleName = 'story';
$methodName = 'create';
}
elseif($methodName == 'createbug')
{
$moduleName = 'bug';
$methodName = 'create';
}
}
return array($moduleName, $methodName);
}
+6 -1
View File
@@ -340,6 +340,10 @@ function waitGantt(callback)
clearInterval(timer);
callback();
}
else
{
zui.Gantt.loadModule();
}
}, 300);
}
@@ -619,7 +623,7 @@ function initGantt()
if(editBtn.hasClass('icon-confirm'))
{
let pointAttr = reviewPoints;
let pointAttr = JSON.parse($('#gantt_here').attr('data-reviewpoints'));
let category = id.split("-")[1];
if(pointAttr[category]['disabled'])
@@ -795,6 +799,7 @@ function initGantt()
window.onPageUnmount = function()
{
$(window).off('.gannt');
$('.gantt_tooltip').remove();
document.removeEventListener('fullscreenchange', exitHandler);
document.removeEventListener('webkitfullscreenchange', delayHandleFullscreen, false);
document.removeEventListener('mozfullscreenchange', delayHandleFullscreen, false);
+1 -9
View File
@@ -29,10 +29,7 @@ class gantt extends wg
{
global $app;
$currentLang = $app->getClientLang();
$langJSFile = $app->getWwwRoot() . 'js/dhtmlxgantt/lang/' . $currentLang . '.js';
$js = file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
if($currentLang != 'en' && file_exists($langJSFile)) $js .= "\nwaitGantt(function(){\n" . file_get_contents($langJSFile) . "\n});\n";
return $js;
}
@@ -55,8 +52,6 @@ class gantt extends wg
protected function build()
{
global $app;
$cssFile = $app->getWebRoot() . 'js/dhtmlxgantt/min.css';
$jsFile = $app->getWebRoot() . 'js/dhtmlxgantt/min.js';
list($id, $zooming, $colsWidth, $showChart) = $this->prop(array('id', 'zooming', 'colsWidth', 'showChart'));
if(empty($id)) $id = 'ganttView';
@@ -73,15 +68,12 @@ class gantt extends wg
return div
(
h::import($cssFile),
h::import($jsFile),
jsVar('ganttID', $id),
jsVar('projectID', $project ? $project->id : 0),
jsVar('module', $app->rawModule),
jsVar('method', $app->rawMethod),
jsVar('jsRoot', $app->getWebRoot()),
jsVar('fileName', $fileName),
jsVar('reviewPoints', $reviewPoints),
jsVar('ganttType', $ganttType),
jsVar('showFields', $showFields),
jsVar('showChart', $showChart),
@@ -101,7 +93,7 @@ class gantt extends wg
setID('ganttContainer'),
on::click('.toggle-all-icon')->call('toggleAllTasks'),
div(setID($id), setClass('gantt is-collapsed')),
div(setID('myCover'), div(setID('gantt_here')))
div(setID('myCover'), div(setID('gantt_here'), setData('reviewpoints', json_encode($reviewPoints))))
);
}
}
+1 -1
View File
@@ -365,7 +365,7 @@ class header extends wg
{
$params = "model=kanban";
}
else if(!defined('TUTORIAL'))
elseif(!commonModel::isTutorialMode())
{
$params = "programID=0&from=global";
$createMethod = 'createGuide';
+3 -2
View File
@@ -29,7 +29,7 @@ class navbar extends wg
protected function getExecutionMoreItem($executionID)
{
if(defined('TUTORIAL')) return;
if(commonModel::isTutorialMode()) return;
global $lang, $app;
@@ -106,7 +106,8 @@ class navbar extends wg
protected function getAppBtnItem()
{
if(defined('TUTORIAL')) return;
if(commonModel::isTutorialMode()) return;
global $app, $config, $lang;
$condition = '';
+1 -1
View File
@@ -89,7 +89,7 @@ $config->action->majorList['doc'] = array('releaseddoc');
$config->action->needGetProjectType = 'build,task,bug,case,testcase,caselib,testtask,testsuite,testreport,doc,issue,release,risk,design,opportunity,trainplan,gapanalysis,researchplan,researchreport,';
$config->action->needGetRelateField = ',branch,story,epic,requirement,productplan,release,task,build,bug,testcase,case,testtask,testreport,design,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,researchplan,researchreport,meeting,kanbanlane,kanbancolumn,module,review,';
$config->action->noLinkModules = ',doclib,module,webhook,gitlab,instance,gitea,gogs,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,user,entry,repo,pivot,scene,boardspace,auditplan,auditresult,productline,chapter,doc,';
$config->action->noLinkModules = ',doclib,module,webhook,gitlab,instance,gitea,gogs,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,user,entry,repo,pivot,scene,boardspace,auditplan,auditresult,productline,chapter,doc,caselib,';
$config->action->ignoreObjectType4Dynamic = 'kanbanregion,kanbanlane,kanbancolumn';
$config->action->ignoreActions4Dynamic = 'disconnectxuanxuan,reconnectxuanxuan,loginxuanxuan,logoutxuanxuan,editmr,removemr,syncdoingbyticket,syncdoingbystory,syncdoingbyuserstory,syncdoingbyepic,syncdoingbytask,syncdoingbybug,syncdoingbytodo,syncdoingbydemand';
+25 -17
View File
@@ -358,6 +358,14 @@ class actionModel extends model
if(isset($fieldList[$history->old])) $history->oldValue = $fieldList[$history->old];
if(isset($fieldList[$history->new])) $history->newValue = $fieldList[$history->new];
}
/* 如果是升级, 检查oldValue和newValue字段是否存在。以防止从老版本升级的时候,而这两个字段不存在,导致升级失败。 */
if(!empty($this->app->upgrading) && (isset($history->oldValue) || isset($history->newValue)))
{
$existHistory = $this->dao->select('*')->from(TABLE_HISTORY)->limit(1)->fetch();
if(!isset($existHistory->oldValue)) unset($history->oldValue, $history->newValue);
}
return $history;
}
@@ -1860,29 +1868,29 @@ class actionModel extends model
/* 只保留允许的标签。 */
/* Keep only allowed tags. */
$action->comment = trim(strip_tags($newComment->lastComment, $this->config->allowedTags));
$action->comment = trim(strip_tags($newComment->lastComment, $this->config->allowedTags));
/* 处理评论内的图片。*/
/* Handle images in comment. */
$action = $this->loadModel('file')->processImgURL($action, 'comment', $newComment->uid);
$action = $this->loadModel('file')->processImgURL($action, 'comment', $newComment->uid);
$this->dao->update(TABLE_ACTION)
->set('date')->eq(helper::now())
->set('comment')->eq($newComment->lastComment)
->where('id')->eq($actionID)
->exec();
$this->dao->update(TABLE_ACTION)
->set('date')->eq(helper::now())
->set('comment')->eq($newComment->lastComment)
->where('id')->eq($actionID)
->exec();
$this->file->updateObjectID($newComment->uid, $action->objectID, $action->objectType);
$this->file->processFileDiffsForObject('comment', $action, $newComment);
if(!empty($newComment->files))
{
$action->files = $newComment->files;
$this->dao->update(TABLE_ACTION)->set('files')->eq($newComment->files)->where('id')->eq($actionID)->exec();
}
$changes = common::createChanges($action, $newComment);
if($changes) $this->logHistory($actionID, $changes);
$this->file->updateObjectID($newComment->uid, $action->objectID, $action->objectType);
$this->file->processFileDiffsForObject('comment', $action, $newComment);
if(!empty($newComment->files))
{
$action->files = $newComment->files;
$this->dao->update(TABLE_ACTION)->set('files')->eq($newComment->files)->where('id')->eq($actionID)->exec();
}
$changes = common::createChanges($action, $newComment);
if($changes) $this->logHistory($actionID, $changes);
return true;
return true;
}
/**
+3 -3
View File
@@ -17,8 +17,8 @@ cid=1
- 测试更新action 1的备注, 备注被成功更新为备注1属性comment @备注1
- 测试更新action 2的备注, 备注被成功更新为备注2属性comment @备注2
- 测试文件是否更新成功, 文件的objectType和objectID被更新为comment和2
- 属性objectType @comment
- 查看更新后的文件
- 属性objectType @story
- 属性objectID @2
- 属性name @文件标题1
@@ -33,4 +33,4 @@ $file = new fileTest();
r($action->updateCommentTest($actionIDList[0], $commentList[0], $uidList[0])) && p('comment') && e('备注1'); // 测试更新action 1的备注, 备注被成功更新为备注1
r($action->updateCommentTest($actionIDList[1], $commentList[1], $uidList[1])) && p('comment') && e('备注2'); // 测试更新action 2的备注, 备注被成功更新为备注2
r($file->getByIdTest(1)) && p('objectType;objectID;name') && e('comment;2;文件标题1'); //测试文件是否更新成功, 文件的objectType和objectID被更新为comment和2
r($file->getByIdTest(1)) && p('objectType;objectID;name') && e('story;2;文件标题1'); // 查看更新后的文件
-206
View File
@@ -1,206 +0,0 @@
<?php
/**
* The trash view file of action module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package action
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php $activeClass = $currentObjectType == 'all' ? 'btn-active-text' : '';?>
<?php echo html::a($this->createLink('action', 'trash', "objectType=all&type=$type"), "<span class='text'>" . $lang->all . "</span>", '', "class='btn btn-link $activeClass'");?>
<?php
/* Output the objectType order by preferredTypeConfig. */
foreach($preferredTypeConfig as $objectType)
{
if(in_array($objectType, $preferredType))
{
$activeClass = $objectType == $currentObjectType ? 'btn-active-text' : '';
echo html::a($this->createLink('action', 'trash', "objectType=$objectType&type=$type"), "<span class='text'>" . zget($lang->action->objectTypes, $objectType) . "</span>", '', "class='btn btn-link $activeClass'");
unset($preferredType[$objectType]);
}
}
/* Output the remaining types which transformed from more type. */
foreach($preferredType as $objectType)
{
$activeClass = $objectType == $currentObjectType ? 'btn-active-text' : '';
echo html::a($this->createLink('action', 'trash', "objectType=$objectType&type=$type"), "<span class='text'>" . zget($lang->action->objectTypes, $objectType) . "</span>", '', "class='btn btn-link $activeClass'");
}
/* Output the more types. */
if(!empty($moreType))
{
$moreLabel = $lang->more;
$moreLabelActive = '';
if(in_array($currentObjectType, $moreType))
{
$moreLabel = "<span class='text'>" . zget($lang->action->objectTypes, $currentObjectType) . "</span>";
$moreLabelActive = 'btn-active-text';
}
echo '<div class="btn-group" id="more">';
echo html::a('javascript:;', $moreLabel . " <span class='caret'></span>", '', "data-toggle='dropdown' class='btn btn-link $moreLabelActive'");
echo "<ul class='dropdown-menu'>";
foreach($moreType as $objectType)
{
$activeClass = $objectType == $currentObjectType ? 'btn-active-text' : '';
$objectName = zget($lang->action->objectTypes, $objectType, '');
if(empty($objectName)) continue;
echo '<li>' . html::a($this->createLink('action', 'trash', "objectType=$objectType&type=$type"), "<span class='text'>" . $objectName . "</span>", '', "class='btn btn-link $activeClass'") . '</li>';
}
echo '</ul></div>';
}
?>
<?php if($currentObjectType != 'all'):?>
<a class="btn btn-link querybox-toggle" id='bysearchTab'><i class="icon icon-search muted"></i> <?php echo $lang->action->byQuery;?></a>
<?php endif;?>
</div>
<div class='btn-toolbar pull-right'>
<?php if($type == 'hidden') echo html::a(inLink('trash', "browseType=all&type=all"), $lang->goback, '', "class='btn'");?>
<?php if($type == 'all') echo html::a(inLink('trash', "browseType=all&type=hidden"), "<i class='icon-eye-close'></i> " . $lang->action->dynamic->hidden, '', "class='btn btn-danger'");?>
</div>
</div>
<div class="cell<?php if($byQuery) echo ' show';?>" id="queryBox" data-module='trash'></div>
<div id='mainContent' class="main-row">
<?php if(empty($trashes)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->noData;?></span></p>
</div>
<?php else:?>
<div class='main-table' data-ride='table'>
<table class='table has-sort-head'>
<?php $vars = "browseType=$currentObjectType&type=$type&byQuery=$byQuery&queryID=$queryID&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; ?>
<thead>
<tr class='colhead'>
<th class='c-object-type'><?php common::printOrderLink('objectType', $orderBy, $vars, $lang->action->objectType);?></th>
<th class='c-id'><?php common::printOrderLink('objectID', $orderBy, $vars, $lang->idAB);?></th>
<th><?php echo $lang->action->objectName;?></th>
<?php if($currentObjectType == 'execution'):?>
<th class='w-250px'><?php echo $this->lang->project->project;?></th>
<?php endif;?>
<?php if(strpos(',story,requirement,', ",$currentObjectType,") !== false):?>
<th class='w-250px'><?php echo $this->lang->story->product;?></th>
<?php endif;?>
<?php if($currentObjectType == 'task'):?>
<th class='w-250px'><?php echo $this->lang->task->execution;?></th>
<?php endif;?>
<th class='c-user'><?php common::printOrderLink('actor', $orderBy, $vars, $lang->action->actor);?></th>
<th class='c-full-date'><?php common::printOrderLink('date', $orderBy, $vars, $lang->action->date);?></th>
<th class='c-actions'><?php echo $lang->actions;?></th>
</tr>
</thead>
<tbody>
<?php foreach($trashes as $action):?>
<?php $module = $action->objectType == 'case' ? 'testcase' : $action->objectType;?>
<tr>
<td><?php echo zget($lang->action->objectTypes, $action->objectType, '');?></td>
<td><?php echo $action->objectID;?></td>
<?php if($action->objectType == 'scene'): ?>
<td class='text-left'>
<?php echo $action->objectName;?>
</td>
<?php else: ?>
<td class='text-left'>
<?php
$params = $action->objectType == 'user' ? "account={$action->objectName}" : "id={$action->objectID}";
$methodName = 'view';
if($module == 'caselib')
{
$methodName = 'view';
$module = 'caselib';
}
if($module == 'basicmeas')
{
$module = 'measurement';
$methodName = 'setSQL';
$params = "id={$action->objectID}";
}
if($module == 'deploystep')
{
$module = 'deploy';
$methodName = 'viewStep';
}
if($action->objectType == 'api')
{
$params = "libID=0&moduelID=0&apiID={$action->objectID}";
$methodName = 'index';
}
if(strpos('traincourse,traincontents', $module) !== false)
{
$methodName = $module == 'traincourse' ? 'viewcourse' : 'viewchapter';
$module = 'traincourse';
}
if(isset($config->action->customFlows[$action->objectType]))
{
$flow = $config->action->customFlows[$action->objectType];
$module = $flow->module;
}
if(strpos($this->config->action->noLinkModules, ",{$module},") !== false)
{
echo $action->objectName;
}
else
{
$tab = '';
$canView = common::hasPriv($module, $methodName);
if($action->objectType == 'meeting') $tab = $action->project ? "data-app='project'" : "data-app='my'";
if($module == 'requirement') $module = 'story';
echo $canView ? html::a($this->createLink($module, $methodName, $params), $action->objectName, '_self', "title='{$action->objectName}' $tab") : "<span title='$action->objectName'>$action->objectName</span>";
}
?>
</td>
<?php endif ?>
<?php if($currentObjectType == 'execution'):?>
<td class="c-name flex" title="<?php echo $projectList[$action->project]->name;?>">
<span class="text-ellipsis"><?php echo $projectList[$action->project]->name;?></span>
<?php if($projectList[$action->project]->deleted):?>
<span class='label label-danger'><?php echo $this->lang->project->deleted;?></span>
<?php endif;?>
</td>
<?php endif;?>
<?php if(strpos(',story,requirement,', ",$currentObjectType,") !== false):?>
<td class="c-name flex" title="<?php echo $productList[$action->objectID]->productTitle;?>">
<span class="text-ellipsis"><?php echo $productList[$action->objectID]->productTitle;?></span>
<?php if($productList[$action->objectID]->productDeleted):?>
<span class='label label-danger'><?php echo $this->lang->story->deleted;?></span>
<?php endif;?>
</td>
<?php endif;?>
<?php if($currentObjectType == 'task'):?>
<td class="c-name flex" title="<?php echo $executionList[$action->execution]->name;?>">
<span class="text-ellipsis"><?php echo $executionList[$action->execution]->name;?></span>
<?php if($executionList[$action->execution]->deleted):?>
<span class='label label-danger'><?php echo $this->lang->execution->deleted;?></span>
<?php endif;?>
</td>
<?php endif;?>
<td><?php echo zget($users, $action->actor);?></td>
<td><?php echo $action->date;?></td>
<td>
<?php
common::printLink('action', 'undelete', "actionid=$action->id&browseType=$currentObjectType", $lang->action->undelete, 'hiddenwin');
if($type == 'all') common::printLink('action', 'hideOne', "actionid=$action->id&browseType=$currentObjectType", $lang->action->hideOne, 'hiddenwin');
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<div class="table-footer">
<div class="table-actions btn-toolbar" style=''>
<?php echo html::linkButton($lang->action->hideAll, inlink('hideAll'), 'hiddenwin');?>
<span class='table-statistic'><?php echo $lang->action->trashTips;?></span>
</div>
<?php $pager->show('right', 'pagerjs');?>
</div>
</div>
<?php endif;?>
</div>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -455,7 +455,7 @@ class adminModel extends model
public function genDateUsed(): object
{
$firstUseDate = $this->dao->select('min(`date`) as `date`')->from(TABLE_ACTION)
->where('`date`')->gt('1970-01-01')
->where('`date`')->ge(FIRST_RELEASE_DATE)
->andWhere('actor')->eq($this->app->user->account)
->fetch('date');
-55
View File
@@ -1,55 +0,0 @@
<?php
/**
* The safe view file of admin module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package admin
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div class="main-row">
<div class='side-col' id='sidebar'>
<div class='cell'>
<div class='list-group'>
<?php
if(common::hasPriv('admin', 'safe')) echo html::a($this->createLink('admin', 'safe'), $lang->admin->safe->set);
if(common::hasPriv('admin', 'checkWeak')) echo html::a($this->createLink('admin', 'checkWeak'), $lang->admin->safe->checkWeak, '', "class='active'");
if(common::hasPriv('admin', 'resetPWDSetting')) echo html::a($this->createLink('admin', 'resetPWDSetting'), $lang->admin->resetPWDSetting);
?>
</div>
</div>
</div>
<div id='mainContent' class="main-col main-content">
<div class='main-table pd-0'>
<table class='table table-condensed table-hover table-striped table-fixed'>
<thead>
<th class='c-id-sm text-center'><?php echo $lang->idAB;?></th>
<th class='c-user'><?php echo $lang->user->realname;?></th>
<th><?php echo $lang->user->account;?></th>
<th class='c-mobile'><?php echo $lang->user->phone;?></th>
<th class='c-mobile'><?php echo $lang->user->mobile;?></th>
<th class='c-text'><?php echo $lang->admin->safe->reason;?></th>
<th class='c-actions-2'><?php echo $lang->actions;?></th>
</thead>
<tbody>
<?php foreach($weakUsers as $user):?>
<tr>
<td class='text-center'><?php echo $user->id?></td>
<td class='text-left'><?php echo $user->realname?></td>
<td class='text-left'><?php echo $user->account?></td>
<td><?php echo $user->phone?></td>
<td><?php echo $user->mobile?></td>
<td><?php echo $lang->admin->safe->reasonList[$user->weakReason];?></td>
<td class='c-actions text-center'><?php common::printIcon('user', 'edit', "userID=$user->id", '', 'list');?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-172
View File
@@ -1,172 +0,0 @@
<?php
/**
* The view file of bug module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package admin
* @version $Id: view.html.php 2568 2012-02-09 06:56:35Z shiyangyangwork@yahoo.cn $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php js::set('hasInternet', $zentaoData->hasData);?>
<?php js::set('isIntranet', $isIntranet);?>
<div id='mainContent' class='main-content admin'>
<div class="main <?php if(!$zentaoData->hasData) echo 'without-internet';?>">
<div class="settings panel">
<div class="panel-title mt-6"><?php echo $lang->admin->setting?></div>
<div class="settings-list <?php if($config->vision == 'lite') echo 'lite-setting';?>">
<?php foreach($lang->admin->menuList as $menuKey => $menu):?>
<?php if($config->vision == 'lite' and !in_array($menuKey, $config->admin->liteMenuList)) continue;?>
<div class="setting-box" <?php if($menu['disabled']) echo "title={$lang->admin->noPriv}";?> data-id="<?php echo $menuKey;?>">
<button class="btn shadow-primary-hover" <?php if($menu['disabled']) echo 'disabled';?> data-link='<?php echo $menu['link'];?>'>
<h4 class="flex align-center w-full">
<div class="flex align-center">
<img src="static/svg/admin-<?php echo $menuKey;?>.svg"/>
<?php echo $menu['name'];?>
</div>
<?php echo html::a($config->admin->helpURL[$menuKey], "<i title='{$lang->help}' class='icon icon-help'></i> ", '_blank', 'class="text-muted setting-help" onclick="window.event.cancelBubble=true;"');?>
</h4>
<p class="text-muted setting-desc" title="<?php echo $menu['desc'];?>"><?php echo $menu['desc'];?></p>
</button>
</div>
<?php endforeach;?>
</div>
</div>
<?php if($zentaoData->plugins):?>
<div class="plug panel">
<div class="panel-title">
<?php echo $lang->admin->pluginRecommendation;?>
<?php echo html::a($config->admin->extensionURL, "{$lang->more} <i class='icon icon-caret-right'></i>", '_blank', 'class="more text-muted flex align-center"');?>
</div>
<div class="plugin-list" <?php if($langNotCN) echo 'style="flex-wrap: nowrap"';?>>
<?php foreach($zentaoData->plugins as $plugin):?>
<?php $pluginDesc = preg_replace('/[[:cntrl:]]/mu', '', strip_tags($plugin->abstract));?>
<div class="plugin-item shadow-primary-hover" onclick="window.open('<?php echo $plugin->viewLink;?>')">
<span class='ext-download'><i class='icon icon-download-alt text-primary bg-primary-100 pd-3'></i></span>
<h4 class="plug-title" title="<?php echo $plugin->name;?>"><?php echo $plugin->name;?></h4>
<p class='extension-desc text-muted' title="<?php echo $pluginDesc;?>"><?php echo $pluginDesc;?></p>
</div>
<?php endforeach;?>
</div>
</div>
<?php endif;?>
<?php if(!$langNotCN):?>
<div class="flex bottom">
<div class="panel official">
<div class="panel-title"><?php echo $lang->admin->officialAccount?></div>
<div class="flex main-panel">
<div class="official-img"></div>
<div class="official-content">
<div class="title">
<?php echo $lang->admin->followUs?>
<?php if(!$zentaoData->hasData):?>
<i class="icon follow-us icon-arrow-right text-primary"></i>
<?php endif;?>
</div>
<div class="content"> <?php echo $lang->admin->followUsContent?></div>
</div>
</div>
<?php if(!$bind and !$ignore and $zentaoData->hasData and common::hasPriv('admin', 'register')):?>
<div class="panel-link"> <?php echo sprintf($lang->admin->notice->register, html::a(inlink('register'), $lang->admin->registerNotice->submitHere, '', 'class="text-primary"'));?></div>
<?php endif;?>
</div>
<?php if($zentaoData->classes):?>
<div class="panel publicClass">
<div class="panel-title">
<?php echo $lang->admin->publicClass;?>
<?php echo html::a($config->admin->classURL, "{$lang->more} <i class='icon icon-caret-right'></i>", '_blank', 'class="more text-muted flex align-center"');?>
</div>
<div class="classList flex">
<?php foreach($zentaoData->classes as $class):?>
<a class="classItem shadow-primary-hover" href='<?php echo $class->viewLink;?>' target='_blank'>
<div class="imgBack">
<div class="classImg" style="background-image: url('<?php echo $class->image;?>');"></div>
</div>
<div class="classContent"><?php echo $class->name;?></div>
</a>
<?php endforeach;?>
</div>
</div>
<?php endif;?>
<?php endif;?>
</div>
</div>
<?php if($zentaoData->hasData and !$langNotCN):?>
<div class="side panel" style="background: #FCFDFE">
<div class="h-56 flex align-center justify-between">
<div class="panel-title"><?php echo $lang->admin->zentaoInfo?></div>
<div class="time-count">
<?php echo $lang->admin->zentaoUsed;?>
<?php if($dateUsed->year):?>
<span class="time-block"><?php echo $dateUsed->year;?></span><?php echo $lang->year;?>
<?php endif;?>
<?php if($dateUsed->month):?>
<span class="time-block"><?php echo $dateUsed->month;?></span><?php echo $lang->admin->mon;?>
<?php endif;?>
<span class="time-block"><?php echo $dateUsed->day;?></span><?php echo $lang->admin->day;?>
</div>
</div>
<div class="border-gray mb-16 radius-4">
<div class="h-40 pl-16 flex align-center justify-between">
<div class="panel-title"><?php echo $lang->admin->updateDynamics?></div>
<?php echo html::a($config->admin->downloadURL, "{$lang->more} <i class='icon icon-caret-right pb-3'></i>", '_blank', 'class="more text-muted flex align-center"');?>
</div>
<?php foreach($zentaoData->dynamics as $dynamic):?>
<div class="dynamic-block">
<div class="dynamic-content" title=<?php echo $dynamic->title ?>><i class="icon icon-horn text-primary pr-4 font-20"></i><?php echo html::a($dynamic->link, $dynamic->title, '_blank');?></div>
<div class="dynamic-time"><?php echo substr($dynamic->addedDate, 0, 10);?></div>
</div>
<?php endforeach;?>
</div>
<div class="border-gray mb-16 radius-4">
<div class="h-40 pl-16 flex align-center justify-between">
<div class="panel-title"><?php echo $lang->admin->updatePatch?></div>
<?php echo html::a($config->admin->patchURL, "{$lang->more} <i class='icon icon-caret-right pb-3'></i>", '_blank', 'class="more text-muted flex align-center"');?>
</div>
<?php foreach($zentaoData->patches as $patch):?>
<div class="patch-block">
<div class="title flex justify-between">
<div class="panel-title"><?php echo $patch->name?></div>
<a href="<?php echo $patch->viewLink;?>" class='ext-download flex align-center' target='_blank'><i class='icon icon-download-alt text-primary bg-primary-100 pd-3'></i></a>
</div>
<div class="patch-content"><?php echo $patch->desc?></div>
</div>
<?php endforeach;?>
</div>
<?php if(!in_array($config->edition, array('max', 'ipd'))):?>
<div class="border-gray mb-16 radius-4">
<div class="h-40 pl-16 flex align-center justify-between">
<div class="panel-title"><?php echo $lang->admin->upgradeRecommend?></div>
</div>
<?php if($config->edition != 'biz'):?>
<div class="upgrade-block">
<a class="title text-primary flex justify-between" href="<?php echo $config->admin->apiRoot;?>" target="_blank">
<div class="panel-title"><i class="icon icon-zentao text-primary pr-4 font-18"></i><?php echo $lang->admin->bizTag?></div>
<div class="flex align-center"><div><?php echo $lang->admin->productDetail?></div><i class="icon icon-caret-right text-primary pb-3"></i></div>
</a>
<?php foreach($lang->admin->productFeature['biz'] as $feature):?>
<div class="upgrade-content color-gray"><?php echo $feature;?></div>
<?php endforeach;?>
</div>
<?php endif;?>
<div class="upgrade-block">
<a class="title text-primary flex justify-between" href='<?php echo $config->admin->apiRoot;?>' target="_blank">
<div class="panel-title"><i class="icon icon-zentao text-primary pr-4 font-18"></i><?php echo $lang->admin->maxTag?></div>
<div class="flex align-center"><div><?php echo $lang->admin->productDetail?></div><i class="icon icon-caret-right text-primary pb-3"></i></div>
</a>
<?php foreach($lang->admin->productFeature['max'] as $feature):?>
<div class="upgrade-content color-gray"><?php echo $feature;?></div>
<?php endforeach;?>
</div>
</div>
<?php endif;?>
</div>
<?php endif;?>
</div>
<?php include '../../common/view/footer.html.php';?>
-37
View File
@@ -1,37 +0,0 @@
<?php
/**
* The log view file of admin module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Gang Liu <liugang@cnezsoft.com>
* @package admin
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/form.html.php';?>
<div id='mainContent' class='main-content'>
<div class='main-header'>
<h2><?php echo $lang->webhook->setting;?></h2>
</div>
<div class='center-block mw-700px'>
<form id='logForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th class='w-100px'><?php echo $lang->admin->days;?></th>
<td><?php echo html::input('days', $config->admin->log->saveDays, "class='form-control'");?></td>
</tr>
<tr>
<th></th>
<td>
<?php echo html::submitButton();?>
<?php echo $lang->admin->info->log;?>
</td>
</tr>
</table>
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
@@ -1,59 +0,0 @@
<?php
/**
* The resetpwdsetting view file of admin module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Shujie Tian <tianshujie@easycorp.ltd>
* @package admin
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<style>
<?php if(!common::checkNotCN()):?>
.mailBox {width: 45px !important;}
<?php else:?>
.mailBox {width: 109px !important;}
<?php endif;?>
.heading {padding-left: 15px;}
</style>
<div class="main-row">
<div class='side-col' id='sidebar'>
<div class='cell'>
<div class='list-group'>
<?php
if(common::hasPriv('admin', 'safe')) echo html::a($this->createLink('admin', 'safe'), $lang->admin->safe->set);
if(common::hasPriv('admin', 'checkWeak')) echo html::a($this->createLink('admin', 'checkWeak'), $lang->admin->safe->checkWeak);
if(common::hasPriv('admin', 'resetPWDSetting')) echo html::a($this->createLink('admin', 'resetPWDSetting'), $lang->admin->resetPWDSetting, '', "class='active'");
?>
</div>
</div>
</div>
<div id='mainContent' class='main-col main-content'>
<div class='main-header'>
<h2><?php echo $lang->admin->resetPWDSetting;?></h2>
</div>
<div class='center-block'>
<form class="load-indicator main-form form-ajax" method='post'>
<table class='table table-form'>
<tr>
<th class='mailBox'><?php echo $lang->admin->resetPWDByMail;?></th>
<td class='w-300px text-left'>
<?php $checkedKey = isset($config->resetPWDByMail) ? $config->resetPWDByMail : 0;?>
<?php foreach($lang->admin->safe->resetPWDList as $key => $value):?>
<label class="radio-inline"><input type="radio" name="resetPWDByMail" value="<?php echo $key?>"<?php echo $key == $checkedKey ? " checked='checked'" : ''?> id="resetPWDByMail<?php echo $key;?>"><?php echo $value;?></label>
<?php endforeach;?>
</td>
</tr>
<tr>
<th></th>
<td class='form-actions'><?php echo html::submitButton();?></td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-110
View File
@@ -1,110 +0,0 @@
<?php
/**
* The safe view file of admin module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package admin
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.table-form>tbody>tr>th {width:118px !important;}
.icon-exclamation-sign.icon {color:#2667E3; margin-right: 8px;}
.notice {color:#2667E3; margin-left: 12px;}
</style>
<?php js::set('adminLang', $lang->admin);?>
<?php js::set('loadedGD', extension_loaded('gd') && !empty($gdInfo['FreeType Support']));?>
<?php include '../../common/view/header.html.php';?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<div class='btn-toolbar pull-left'><?php // common::printAdminSubMenu('system');?></div>
</div>
</div>
<div class="main-row">
<div class='side-col' id='sidebar'>
<div class='cell'>
<div class='list-group'>
<?php
if(common::hasPriv('admin', 'safe')) echo html::a($this->createLink('admin', 'safe'), $lang->admin->safe->set, '', "class='active'");
if(common::hasPriv('admin', 'checkWeak')) echo html::a($this->createLink('admin', 'checkWeak'), $lang->admin->safe->checkWeak);
if(common::hasPriv('admin', 'resetPWDSetting')) echo html::a($this->createLink('admin', 'resetPWDSetting'), $lang->admin->resetPWDSetting);
?>
</div>
</div>
</div>
<div id='mainContent' class='main-col main-content'>
<div class='center-block'>
<form class="load-indicator main-form form-ajax" method='post'>
<table class='table table-form'>
<tr>
<th class='thWidth'><?php echo $lang->admin->safe->password?></th>
<td class='w-250px'><?php echo html::radio('mode', $lang->admin->safe->modeList, isset($config->safe->mode) ? $config->safe->mode : 0, "onclick=showModeRule(this.value)")?></td>
<td class='notice'><?php echo !empty($config->safe->changeWeak) ? $lang->admin->safe->noticeWeakMode : $lang->admin->safe->noticeMode;?></td>
</tr>
<tr id='mode1Rule' class='hidden'>
<th></th>
<td colspan='2'>
<i class="icon icon-exclamation-sign"></i>
<span style='color:#5E626D;font-weight:bold;font-size:13px;'><?php echo $lang->admin->safe->modeRuleList[1] . $lang->admin->safe->noticeStrong;?></span>
</td>
</tr>
<tr id='mode2Rule' class='hidden'>
<th></th>
<td colspan='2'>
<i class="icon icon-exclamation-sign"></i>
<span style='color:#5E626D;font-weight:bold;font-size:13px;'><?php echo $lang->admin->safe->modeRuleList[2] . $lang->admin->safe->noticeStrong;?></span>
</td>
</tr>
<tr>
<th><?php echo $lang->admin->safe->weak?></th>
<td colspan='2'><?php echo html::textarea('weak', $config->safe->weak, "class='form-control' style='height:100px'")?></td>
</tr>
<tr>
<th><?php echo $lang->admin->safe->changeWeak?></th>
<td colspan='2'><?php echo html::radio('changeWeak', $lang->admin->safe->modifyPasswordList, isset($config->safe->changeWeak) ? $config->safe->changeWeak : 0, "onchange='changeWeakChange(this.value)'")?></td>
</tr>
<tr>
<th><?php echo $lang->admin->safe->modifyPasswordFirstLogin?></th>
<td colspan='2'><?php echo html::radio('modifyPasswordFirstLogin', $lang->admin->safe->modifyPasswordList, isset($config->safe->modifyPasswordFirstLogin) ? $config->safe->modifyPasswordFirstLogin : 0)?></td>
</tr>
<tr>
<th><?php echo $lang->admin->safe->loginCaptcha?></th>
<td><?php echo html::radio('loginCaptcha', $lang->admin->safe->loginCaptchaList, isset($config->safe->loginCaptcha) ? $config->safe->loginCaptcha : 0)?></td>
<td class='notice'><?php if(!extension_loaded('gd') || empty($gdInfo['FreeType Support'])) echo $lang->admin->safe->noticeGd;?></td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton();?>
<?php echo html::backButton();?>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<script>
$(function()
{
var mode = $("input[name='mode']:checked").val();
showModeRule(mode);
if(!loadedGD) $('#loginCaptcha1').attr('disabled', true);
});
function showModeRule(mode)
{
if(mode == 0)
{
$('#mode1Rule').addClass('hidden');
$('#mode2Rule').addClass('hidden');
}
else
{
mode == 1 ? $('#mode1Rule').removeClass('hidden') : $('#mode1Rule').addClass('hidden');
mode == 2 ? $('#mode2Rule').removeClass('hidden') : $('#mode2Rule').addClass('hidden');
}
}
</script>
<?php include '../../common/view/footer.html.php';?>
-83
View File
@@ -1,83 +0,0 @@
<?php
/**
* The setmodule view file of admin module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yuting Wang <wangyuting@cnezsoft.com>
* @package admin
* @version $Id: setmodule.html.php 2568 2012-02-09 06:56:35Z shiyangyangwork@yahoo.cn $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-row'>
<div class='main-col main-content'>
<div class='main-header'>
<h2><?php echo $lang->admin->setModuleIndex;?></h2>
</div>
<form class="load-indicator main-form form-ajax" id="ajaxForm" method="post">
<table class='table table-condensed table-bordered active-disabled table-fixed'>
<thead class='text-center'>
<tr>
<th class='w-150px'><?php echo $lang->admin->setModule->module;?></th>
<th><?php echo $lang->admin->setModule->optional;?></th>
</tr>
</thead>
<tbody>
<?php foreach($config->featureGroup as $group => $features):?>
<?php if(strpos(",$disabledFeatures,", ",$group,") !== false) continue;?>
<?php $hasData = false;?>
<?php foreach($features as $feature):?>
<?php $code = $group . ucfirst($feature);?>
<?php if(strpos(",$disabledFeatures,", ",$code,") !== false) continue;?>
<?php $hasData = true;?>
<?php endforeach;?>
<?php if($hasData):?>
<tr>
<td class='text-middle text-right thWidth'>
<div class="checkbox-primary checkbox-inline checkbox-right check-all">
<input type='checkbox' id='allChecker<?php echo $group;?>' name='allChecker'>
<label class='text-right' for='allChecker<?php echo $group;?>'><?php echo $lang->admin->setModule->{$group};?></label>
</div>
</td>
<td>
<?php foreach($features as $feature):?>
<?php $code = $group. ucfirst($feature);?>
<?php if(strpos(",$disabledFeatures,", ",$code,") !== false) continue;?>
<?php
if($code == 'myScore')
{
$value = $useScore;
}
else
{
$value = strpos(",$closedFeatures,", ",$code,") === false ? '1' : '0';
}
?>
<div class='group-item'>
<?php echo html::checkbox("module[{$code}]", array('1' => $lang->admin->setModule->{$feature}), $value, "data-code='{$code}'", 'inline');?>
<?php echo html::hidden("module[{$code}][]", $value, $value ? 'disabled' : '');?>
</div>
<?php endforeach;?>
</td>
</tr>
<?php endif;?>
<?php endforeach;?>
<tr>
<td class='text-middle text-right thWidth'>
<div class="checkbox-primary checkbox-inline checkbox-right check-all">
<input type='checkbox' id='allChecker'>
<label class='text-right' for='allChecker'><?php echo $lang->selectAll;?></label>
</div>
</td>
<td><?php echo html::submitButton();?></td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-54
View File
@@ -1,54 +0,0 @@
<?php
/**
* The sso view file of admin module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package admin
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php'; ?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<div class='heading'>
<h4><?php echo $lang->admin->sso;?></h4>
</div>
</div>
<form method='post' id='dataform'>
<table class='table table-form'>
<tr>
<th class='rowhead w-150px'><?php echo $lang->sso->turnon; ?></th>
<td><?php echo html::radio('turnon', $lang->sso->turnonList, $turnon);?></td>
</tr>
<tr>
<th class='rowhead w-120px'><?php echo $lang->sso->redirect; ?></th>
<td><?php echo html::radio('redirect', $lang->sso->turnonList, $redirect);?></td>
</tr>
<tr>
<th><?php echo $lang->sso->addr; ?></th>
<td><?php echo html::input('addr', $addr, "class='form-control' placeholder='{$lang->sso->addrNotice}'");?></td>
</tr>
<tr>
<th><?php echo $lang->sso->code; ?></th>
<td><?php echo html::input('code', $code, "class='form-control'");?></td>
</tr>
<tr>
<th><?php echo $lang->sso->key; ?></th>
<td><?php echo html::input('key', $key, "class='form-control'");?></td>
</tr>
<tr>
<td colspan='2' class='text-center form-actions'>
<?php echo html::submitButton();?>
<?php echo html::backButton();?>
</td>
</tr>
</table>
</form>
<div class='alert alert-info mg-0'><?php echo $lang->sso->help?></div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-105
View File
@@ -1,105 +0,0 @@
<?php
/**
* The tableEngine view file of admin module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package admin
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-content'>
<div class='main-header'>
<?php
$MyISAMCount = 0;
$engineListHtml = '';
foreach($tableEngines as $tableName => $engine)
{
if($engine != 'InnoDB') $MyISAMCount ++;
$engineListHtml .= "<li data-table='$tableName'>" . sprintf($lang->admin->engineInfo, $tableName, $engine) . "</li>\n";
}
?>
<h2>
<?php if($MyISAMCount > 0):?>
<?php printf($lang->admin->engineSummary['hasMyISAM'], $MyISAMCount);?>
<?php echo html::a('###', $lang->admin->changeEngine, '', "class='btn btn-sm changeEngine btn-primary' onclick='changeAllEngines()'");?>
<?php else:?>
<?php print($lang->admin->engineSummary['allInnoDB']);?>
<?php endif;?>
</h2>
</div>
<div>
<ul id='engineBox'><?php echo $engineListHtml?></ul>
</div>
</div>
<?php js::set('changingTable', $lang->admin->changingTable);?>
<?php js::set('hasMyISAM', $lang->admin->engineSummary['hasMyISAM']);?>
<script>
var changeFails = 0;
/**
* Change all table engines.
*
* @access public
* @return void
*/
function changeAllEngines()
{
var $engineBox = $('#engineBox');
$('.btn.changeEngine').hide();
$engineBox.empty();
changeTableEngine();
}
/**
* Ajax change table engine.
*
* @access public
* @return void
*/
function changeTableEngine()
{
var $engineBox = $('#engineBox');
var link = createLink('admin', 'ajaxChangeTableEngine');
$.ajax(
{
type: "GET",
url: link,
success: function(response)
{
response = JSON.parse(response);
if(response == null || response.result == 'finished')
{
$engineBox.append("<div class='text-success'><?php echo $lang->admin->changeFinished?></div>");
$engineBox.append("<div class='btn btn-sm'><a href='javascript:location.reload()'><?php echo $lang->refresh;?></a></div>");
if(changeFails == 0) $('#mainContent .main-header h2').html("<?php echo $lang->admin->engineSummary['allInnoDB']?>");
if(changeFails != 0) $('#mainContent .main-header h2').html(hasMyISAM.replace('%s', changeFails));
}
else
{
table = response.thisTable;
if($engineBox.find('[data-table=' + table + ']').length == 0) $engineBox.append("<li data-table='" + table + "'>" + changingTable.replace('%s', table) + "</li>");
$engineBox.find('[data-table=' + table + ']').html(response.message);
if(response.result == 'success') $engineBox.find('[data-table=' + table + ']').addClass('text-success');
if(response.result == 'fail') $engineBox.find('[data-table=' + table + ']').addClass('text-warning');
nextTable = response.nextTable;
if(nextTable && $engineBox.find('[data-table=' + nextTable + ']').length == 0) $engineBox.append("<li data-table='" + nextTable + "'>" + changingTable.replace('%s', nextTable) + "</li>");
if(response.result == 'fail') changeFails += 1;
$(document).scrollTop($(document).height());
changeTableEngine();
}
},
error: function()
{
changeTableEngine();
}
})
}
</script>
<?php include '../../common/view/footer.html.php';?>
-228
View File
@@ -1,228 +0,0 @@
<?php
/**
* The create view of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Jia Fu <fujia@cnezsoft.com>
* @package doc
* @version $Id: create.html.php 975 2010-07-29 03:30:25Z jajacn@126.com $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::set('libID', $libID);?>
<?php
js::set('typeOptions', $typeOptions);
js::set('langField', $lang->struct->field);
js::set('langDesc', $lang->struct->desc);
js::set('structAdd', $lang->struct->add);
js::set('structDelete', $lang->delete);
js::set('addSubField', $lang->struct->addSubField);
js::set('struct_field', $lang->struct->field);
js::set('struct_desc', $lang->struct->desc);
js::set('struct_action', $lang->struct->action);
js::set('struct_required', $lang->struct->required);
js::set('struct_paramsType', $lang->struct->paramsType);
?>
<?php js::import($jsRoot . 'vue/vue.js');?>
<div class='modal fade' id='filterStruct'>
<div class='modal-dialog mw-500px'>
<div class='modal-content'>
<div class='modal-header'>
<button type='button' class='close' data-dismiss='modal'>
<i class='icon icon-close'></i>
</button>
<h4 class='modal-title'><?php echo $lang->api->struct;?></h4>
</div>
<div class='modal-body'>
<table class='table table-form'>
<tr>
<td><?php echo html::select('filter', $allStruct, '', "class='form-control chosen filterSelect'");?></td>
<td></td>
</tr>
</table>
</div>
<div class="modal-footer">
<?php echo html::submitButton($lang->confirm, '', 'btn btn-wide btn-primary submit-filter');?>
</div>
</div>
</div>
</div>
<div id="mainContent" class="main-content">
<div class='center-block' id="apiApp">
<div class='main-header'>
<h2><?php echo $lang->api->create;?></h2>
</div>
<form class="load-indicator main-form form-ajax" id="dataform" method='post' enctype='multipart/form-data'>
<table class='table table-form'>
<tbody>
<tr>
<th class='w-110px'><?php echo $lang->api->lib;?></th>
<td>
<?php echo $libName . html::hidden('lib', $libID);?>
</td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->api->module;?></th>
<td>
<span id='moduleBox'><?php echo html::select('module', $moduleOptionMenu, $moduleID, "class='form-control chosen'");?></span>
</td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->api->formTitle;?></th>
<td colspan='2'><?php echo html::input('title', '', "class='form-control' required");?></td>
</tr>
<tr>
<th><?php echo $lang->api->path;?></th>
<td colspan='2'>
<div class='table-row'>
<div class='table-col col-prefix'>
<?php echo html::select('protocol', $lang->api->protocalOptions, 'HTTP', "class='form-control chosen'");?>
</div>
<div class='table-col col-prefix'>
<?php echo html::select('method', $lang->api->methodOptions, 'GET', "class='form-control chosen'");?>
</div>
<div class='table-col'>
<?php echo html::input('path', '', "class='form-control'");?>
</div>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->api->requestType;?></th>
<td>
<span id='moduleBox'><?php echo html::select('requestType', $lang->api->requestTypeOptions, 'application/json', "class='form-control chosen'");?></span>
</td>
</tr>
<tr>
<th><?php echo $lang->api->status;?></th>
<td><?php echo html::radio('status', $lang->api->statusOptions, apiModel::STATUS_DONE);?></td>
</tr>
<tr>
<th><?php echo $lang->api->owner;?></th>
<td>
<div class='input-group'>
<?php echo html::select('owner', $allUsers, $user, "class='form-control chosen'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->api->header;?></th>
<td colspan="2">
<table class="table table-data">
<thead>
<tr>
<th class="w-300px"><?php echo $lang->struct->field;?></th>
<th class="w-50px"><?php echo $lang->struct->required;?></th>
<th class="w-500px"><?php echo $lang->struct->desc;?></th>
<th><?php echo $lang->struct->action;?></th>
</tr>
</thead>
<tbody>
<tr v-for="(item,key) in header">
<td class="w-300px">
<input type="text" placeholder="<?php echo $lang->struct->field;?>" autocomplete="off" class="form-control" v-model="item.field">
</td>
<td class="w-50px">
<div class="checkbox-primary">
<input type="checkbox" v-model="item.required">
<label></label>
</div>
</td>
<td class="w-500px">
<input type="text" placeholder="<?php echo $lang->struct->desc;?>" autocomplete="off" class="form-control" v-model="item.desc">
</td>
<td>
<button class="btn btn-link btn-icon btn-add" type="button" @click="add(header, key, 'header')"><i class="icon icon-plus"></i></button>
<button class="btn btn-link btn-icon btn-delete" type="button" @click="del(header, key)"><i class="icon icon-close"></i></button>
</td>
</tbody>
</table>
</td>
</tr>
<tr>
<th><?php echo $lang->api->query;?></th>
<td colspan="2">
<table class="table table-data">
<thead>
<tr>
<th class="w-300px"><?php echo $lang->struct->field;?></th>
<th class="w-50px"><?php echo $lang->struct->required;?></th>
<th class="w-500px"><?php echo $lang->struct->desc;?></th>
<th><?php echo $lang->struct->action;?></th>
</tr>
</thead>
<tbody>
<tr v-for="(item,key) in queryP">
<td class="w-300px">
<input type="text" placeholder="<?php echo $lang->struct->field;?>" autocomplete="off" class="form-control" v-model="item.field">
</td>
<td class="w-50px">
<div class="checkbox-primary">
<input type="checkbox" v-model="item.required">
<label></label>
</div>
</td>
<td class="w-500px">
<input type="text" placeholder="<?php echo $lang->struct->desc;?>" autocomplete="off" class="form-control" v-model="item.desc">
</td>
<td>
<button class="btn btn-link btn-icon btn-add" type="button" @click="add(queryP, key, 'query')"><i class="icon icon-plus"></i></button>
<button class="btn btn-link btn-icon btn-delete" type="button" @click="del(queryP, key)"><i class="icon icon-close"></i></button>
</td>
</tbody>
</table>
</td>
</tr>
<tr>
<th><?php echo $lang->api->params;?></th>
<td colspan='2'>
<body-field @change="changeAttr" @change-type="changeType"></body-field>
<input type="hidden" name="params" v-model="params">
</td>
</tr>
<tr>
<th><?php echo $lang->api->paramsExample;?></th>
<td>
<div class='input-group'>
<?php echo html::textarea('paramsExample', '', "style='width:100%;height:200px'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->api->response;?></th>
<td colspan='2' id='responseDiv'>
<body-field @change="changeRes" :struct-type="'json'" :show-type="false"></body-field>
<input type="hidden" name="response" v-model="response">
</td>
</tr>
<tr>
<th><?php echo $lang->api->responseExample;?></th>
<td>
<div class='input-group'>
<?php echo html::textarea('responseExample', '', "style='width:100%;height:200px'");?>
</div>
</td>
</tr>
<tr id='contentBox'>
<th><?php echo $lang->api->desc;?></th>
<td colspan='2'>
<div class='contenthtml'><?php echo html::textarea('desc', '', "style='width:100%;height:200px'");?></div>
</td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton();?>
<?php echo html::backButton($lang->goback, "data-app='{$app->tab}' class='btn btn-back btn-wide'");?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-83
View File
@@ -1,83 +0,0 @@
<?php
/**
* The createlib view of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Jia Fu <fujia@cnezsoft.com>
* @package doc
* @version $Id: createlib.html.php 975 2010-07-29 03:30:25Z jajacn@126.com $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php';?>
<?php include '../../common/view/chosen.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<div id="main">
<div class="container">
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->api->createLib;?></h2>
</div>
<form class='load-indicator main-form' id="apiForm" method='post' enctype='multipart/form-data'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->api->libType;?></th>
<td>
<span><?php echo html::radio('libType', $lang->api->libTypeList, $type, "onchange='toggleLibType(this.value)'")?></span>
</td>
</tr>
<tr id='productBox' class="<?php if($type != 'product') echo 'hidden';?>">
<th><?php echo $lang->api->product;?></th>
<td class='required'>
<span><?php echo html::select('product', $products, $objectID, "class='form-control chosen'")?></span>
</td>
</tr>
<tr id='projectBox' class="<?php if($type != 'project') echo 'hidden';?>">
<th><?php echo $lang->api->project;?></th>
<td class='required'>
<span><?php echo html::select('project', $projects, $objectID, "class='form-control chosen'")?></span>
</td>
</tr>
<tr>
<th><?php echo $lang->api->name?></th>
<td style="width: 80%"><?php echo html::input('name', '', "class='form-control'")?></td>
</tr>
<tr>
<th><?php echo $lang->api->baseUrl?></th>
<td style="width: 80%"><?php echo html::input('baseUrl', '', "class='form-control' placeholder='" . $lang->api->baseUrlDesc . "'");?></td>
</tr>
<tr id='aclBox'>
<th><?php echo $lang->api->control;?></th>
<td>
<span><?php echo html::radio('acl', $lang->api->aclList, 'open', "onchange='toggleAcl(this.value, \"lib\")'", 'block');?></span>
</td>
</tr>
<tr id='whiteListBox' class='hidden'>
<th><?php echo $lang->api->whiteList;?></th>
<td>
<div class='input-group'>
<span class='input-group-addon groups-addon'><?php echo $lang->api->group?></span>
<?php echo html::select('groups[]', $groups, '', "class='form-control chosen' multiple")?>
</div>
<div class='input-group'>
<span class='input-group-addon'><?php echo $lang->api->user?></span>
<?php echo html::select('users[]', $users, '', "class='form-control chosen' multiple")?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName=users&attr=data-drop_direction='up'");?>
</div>
</td>
</tr>
<tr>
<td class='text-center form-actions' colspan='2'><?php echo html::submitButton();?></td>
</tr>
</table>
</form>
</div>
</div>
</div>
</div>
<?php js::set('productLang', $lang->productCommon);?>
<?php js::set('projectLang', $lang->projectCommon);?>
<?php js::set('libType', $type);?>
<?php include '../../common/view/footer.lite.html.php';?>
-44
View File
@@ -1,44 +0,0 @@
<?php
/**
* The createlib view of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Jia Fu <fujia@cnezsoft.com>
* @package doc
* @version $Id: createlib.html.php 975 2010-07-29 03:30:25Z jajacn@126.com $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php';?>
<?php include '../../common/view/chosen.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<div id="main">
<div class="container">
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->api->createRelease;?></h2>
</div>
<form class='load-indicator main-form form-ajax' id="apiForm" method='post' enctype='multipart/form-data'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->api->version?></th>
<td style="width: 100%"><?php echo html::input('version', '', "class='form-control'")?></td>
</tr>
<tr>
<th><?php echo $lang->api->desc;?></th>
<td colspan='2'>
<?php echo html::textarea('desc', '', "rows='8' class='form-control kindeditor' hidefocus='true' tabindex=''");?>
</td>
</tr>
<tr>
<td class='text-center form-actions' colspan='2'><?php echo html::submitButton();?></td>
</tr>
</table>
</form>
</div>
</div>
</div>
</div>
<?php include '../../common/view/footer.lite.html.php';?>
-69
View File
@@ -1,69 +0,0 @@
<?php
/**
* The create struct view of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Thanatos <thanatos@915.com>
* @package doc
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/chosen.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::import($jsRoot . 'vue/vue.js');?>
<?php js::set('paramsTypeOption', $lang->api->paramsScopeOptions);?>
<?php
js::set('typeOptions', $typeOptions);
js::set('structAdd', $lang->struct->add);
js::set('addSubField', $lang->struct->addSubField);
js::set('structDelete', $lang->delete);
js::set('langField', $lang->struct->field);
js::set('langDesc', $lang->struct->desc);
js::set('struct_field', $lang->struct->field);
js::set('struct_desc', $lang->struct->desc);
js::set('struct_action', $lang->struct->action);
js::set('struct_required', $lang->struct->required);
js::set('struct_paramsType', $lang->struct->paramsType);
js::set('struct', '');
?>
<div id="app">
<div id="mainContent" class="main-content">
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->api->createStruct;?></h2>
</div>
<form class="load-indicator main-form form-ajax" id="dataform" method='post' enctype='multipart/form-data'>
<table class='table table-form'>
<tbody>
<tr>
<th class='w-110px'><?php echo $lang->api->structName;?></th>
<td><?php echo html::input('name', '', "class='form-control' required");?> </td>
<td></td>
</tr>
<tr>
<td class="w-110px"></td>
<td colspan="2">
<body-field @change="changeAttr"></body-field>
<input type="hidden" name="attribute" v-model="attr">
</td>
</tr>
<tr>
<th class='w-110px'><?php echo $lang->api->desc;?></th>
<td colspan="2"><?php echo html::textarea('desc', '', "class='form-control'");?></td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton();?>
<?php if(empty($gobackLink)) echo html::backButton($lang->goback, "data-app='{$app->tab}'");?>
<?php if(!empty($gobackLink)) echo html::a($gobackLink, $lang->goback, '', "class='btn btn-back btn-wide'");?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-231
View File
@@ -1,231 +0,0 @@
<?php
/**
* The create view of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Jia Fu <fujia@cnezsoft.com>
* @package doc
* @version $Id: create.html.php 975 2010-07-29 03:30:25Z jajacn@126.com $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::import($jsRoot . 'vue/vue.js');?>
<?php js::set('libID', $api->lib);?>
<?php
js::set('typeOptions', $typeOptions);
js::set('langField', $lang->struct->field);
js::set('langDesc', $lang->struct->desc);
js::set('structAdd', $lang->struct->add);
js::set('structDelete', $lang->delete);
js::set('addSubField', $lang->struct->addSubField);
js::set('struct_field', $lang->struct->field);
js::set('struct_desc', $lang->struct->desc);
js::set('struct_action', $lang->struct->action);
js::set('struct_required', $lang->struct->required);
js::set('struct_paramsType', $lang->struct->paramsType);
js::set('api', $api);
?>
<div class='modal fade' id='customType'>
<div class='modal-dialog mw-500px'>
<div class='modal-content'>
<div class='modal-header'>
<button type='button' class='close' data-dismiss='modal'>
<i class='icon icon-close'></i>
</button>
<h4 class='modal-title'><?php echo $lang->api->customType;?></h4>
</div>
<div class='modal-body'>
<textarea class="form-control customTypeTextarea" rows="10" placeholder=""></textarea>
</div>
<div class="modal-footer">
<button class="btn btn-wide btn-warning formatCustom" style="float: left"><?php echo $lang->api->format;?></button>
<?php echo html::submitButton($lang->confirm, '', 'btn btn-wide btn-primary submit-custom');?>
</div>
</div>
</div>
</div>
<div id="mainContent" class="main-content">
<div class='center-block' id="apiApp">
<div class='main-header'>
<?php if($edit):?>
<h2>
<span class='label label-id'><?php echo $api->id;?></span>
<?php echo html::a($this->createLink('api', 'index', "apiID=$api->id"), $api->title, '', "title='$api->title'");?>
<small> <?php echo $lang->arrow . ' ' . $lang->api->edit;?></small>
</h2>
<div
class='pull-right'><?php echo html::a('###', $lang->save, '', 'id="top-submit" class="btn btn-primary"');?></div>
<?php else:?>
<h2><?php echo $lang->api->create;?></h2>
<?php endif;?>
</div>
<form class="load-indicator main-form form-ajax" id="dataform" method='post' enctype='multipart/form-data'>
<table class='table table-form'>
<tbody>
<tr>
<th><?php echo $lang->api->module;?></th>
<td>
<span
id='moduleBox'><?php echo html::select('module', $moduleOptionMenu, $moduleID, "class='form-control chosen'");?></span>
</td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->api->formTitle;?></th>
<td colspan='2'><?php echo html::input('title', $api->title, "class='form-control' required");?></td>
</tr>
<tr>
<th><?php echo $lang->api->path;?></th>
<td colspan='2'><?php echo html::input('path', $api->path, "class='form-control'");?></td>
</tr>
<tr>
<th><?php echo $lang->api->protocol;?></th>
<td><?php echo html::radio('protocol', $lang->api->protocalOptions, $api->protocol ? $api->protocol : 'HTTP');?></td>
</tr>
<tr>
<th><?php echo $lang->api->method;?></th>
<td><span id='moduleBox'><?php echo html::select('method', $lang->api->methodOptions, $api->method ? $api->method : 'GET', "class='form-control chosen'");?></span></td>
</tr>
<tr>
<th><?php echo $lang->api->requestType;?></th>
<td><span id='moduleBox'><?php echo html::select('requestType', $lang->api->requestTypeOptions, $api->requestType ? $api->requestType : 'application/json', "class='form-control chosen'");?></span></td>
</tr>
<tr>
<th><?php echo $lang->api->status;?></th>
<td><?php echo html::radio('status', $lang->api->statusOptions, $api->status ? $api->status : apiModel::STATUS_DOING);?></td>
</tr>
<tr>
<th>
<nobr><?php echo $lang->api->owner;?></nobr>
</th>
<td>
<div class='input-group'>
<?php echo html::select('owner', $allUsers, $api->owner ? $api->owner : $user, "class='form-control chosen'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->api->header;?></th>
<td colspan="2">
<table class="table table-data">
<thead>
<tr>
<th class="w-300px"><?php echo $lang->struct->field;?></th>
<th class="w-50px"><?php echo $lang->struct->required;?></th>
<th class="w-500px"><?php echo $lang->struct->desc;?></th>
<th><?php echo $lang->struct->action;?></th>
</tr>
</thead>
<tbody>
<tr v-for="(item,key) in header">
<td class="w-300px">
<input type="text" placeholder="<?php echo $lang->struct->field;?>" autocomplete="off" class="form-control" v-model="item.field">
</td>
<td class="w-50px">
<div class="checkbox-primary">
<input type="checkbox" v-model="item.required">
<label></label>
</div>
</td>
<td class="w-500px">
<input type="text" placeholder="<?php echo $lang->struct->desc;?>" autocomplete="off" class="form-control" v-model="item.desc">
</td>
<td>
<button class="btn btn-link btn-icon btn-add" type="button" @click="add(header, key, 'header')"><i class="icon icon-plus"></i></button>
<button class="btn btn-link btn-icon btn-delete" type="button" @click="del(header, key)"><i class="icon icon-close"></i></button>
</td>
</tbody>
</table>
</td>
</tr>
<tr>
<th><?php echo $lang->api->query;?></th>
<td colspan="2">
<table class="table table-data">
<thead>
<tr>
<th class="w-300px"><?php echo $lang->struct->field;?></th>
<th class="w-50px"><?php echo $lang->struct->required;?></th>
<th class="w-500px"><?php echo $lang->struct->desc;?></th>
<th><?php echo $lang->struct->action;?></th>
</tr>
</thead>
<tbody>
<tr v-for="(item,key) in queryP">
<td class="w-300px">
<input type="text" placeholder="<?php echo $lang->struct->field;?>" autocomplete="off" class="form-control" v-model="queryP[key].field">
</td>
<td class="w-50px">
<div class="checkbox-primary">
<input type="checkbox" v-model="item.required">
<label></label>
</div>
</td>
<td class="w-500px">
<input type="text" placeholder="<?php echo $lang->struct->desc;?>" autocomplete="off" class="form-control" v-model="item.desc">
</td>
<td>
<button class="btn btn-link btn-icon btn-add" type="button" @click="add(queryP, key, 'query')"><i class="icon icon-plus"></i></button>
<button class="btn btn-link btn-icon btn-delete" type="button" @click="del(queryP, key)"><i class="icon icon-close"></i></button>
</td>
</tbody>
</table>
</td>
</tr>
<tr>
<th><?php echo $lang->api->params;?></th>
<td colspan='2'>
<body-field @change="changeAttr" :attr="attr" :struct-type="attrType" @change-type="changeType"></body-field>
<input type="hidden" name="params" v-model="params">
</td>
</tr>
<tr>
<th>
<nobr><?php echo $lang->api->paramsExample;?></nobr>
</th>
<td>
<div class='input-group'>
<?php echo html::textarea('paramsExample', $api->paramsExample, "style='width:100%;height:200px'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->api->response;?></th>
<td colspan='2' id='responseDiv'>
<body-field @change="changeRes" :struct-type="'json'" :show-type="false" :attr="api.response"></body-field>
<input type="hidden" name="response" v-model="response">
</td>
</tr>
<tr>
<th>
<nobr><?php echo $lang->api->responseExample;?></nobr>
</th>
<td>
<div class='input-group'>
<?php echo html::textarea('responseExample', $api->responseExample, "style='width:100%;height:200px'");?>
</div>
</td>
</tr>
<tr id='contentBox'>
<th><?php echo $lang->api->desc;?></th>
<td colspan='2'>
<div class='contenthtml'><?php echo html::textarea('desc', $api->desc, "style='width:100%;height:200px'");?></div>
</td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton();?>
<?php echo html::hidden('editedDate', $api->editedDate);?>
<?php if(empty($gobackLink)) echo html::backButton($lang->goback, "data-app='{$app->tab}'");?>
<?php if(!empty($gobackLink)) echo html::a($gobackLink, $lang->goback, '', "class='btn btn-back btn-wide'");?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-70
View File
@@ -1,70 +0,0 @@
<?php
/**
* The createlib view of api module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Jia Fu <fujia@cnezsoft.com>
* @package api
* @version $Id: editlib.html.php 975 2010-07-29 03:30:25Z jajacn@126.com $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php';?>
<?php include '../../common/view/chosen.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<div id="main">
<div class="container">
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->api->editLib;?></h2>
</div>
<form class='load-indicator main-form form-ajax' id="apiForm" method='post' enctype='multipart/form-data'>
<table class='table table-form'>
<?php if(in_array($type, array('product', 'project'))):?>
<tr>
<th class='w-130px'><?php echo $lang->api->{$type};?></th>
<td><?php echo $object->name?></td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->api->name?></th>
<td style="width: 80%"><?php echo html::input('name', $lib->name, "class='form-control'");?></td>
</tr>
<tr>
<th><?php echo $lang->api->baseUrl?></th>
<td style="width: 80%"><?php echo html::input('baseUrl', $lib->baseUrl, "class='form-control'");?></td>
</tr>
<tr id='aclBox'>
<th><?php echo $lang->api->control;?></th>
<td>
<span><?php echo html::radio('acl', $lang->api->aclList, $lib->acl, "onchange='toggleAcl(this.value, \"lib\")'", 'block')?></span>
</td>
</tr>
<tr id='whiteListBox' class='<?php echo $lib->acl == 'private' ? '' : 'hidden';?>'>
<th><?php echo $lang->api->whiteList;?></th>
<td>
<div class='input-group'>
<span class='input-group-addon groups-addon'><?php echo $lang->api->group?></span>
<?php echo html::select('groups[]', $groups, $lib->groups, "class='form-control chosen' multiple");?>
</div>
<div class='input-group'>
<span class='input-group-addon'><?php echo $lang->api->user?></span>
<?php echo html::select('users[]', $users, $lib->users, "class='form-control chosen' multiple");?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName=users&attr=data-drop_direction='up'");?>
</div>
</td>
</tr>
<tr>
<td class='text-center form-actions' colspan='2'><?php echo html::hidden('type', $type) . html::submitButton();?></td>
</tr>
</table>
</form>
</div>
</div>
</div>
</div>
<?php js::set('productLang', $lang->productCommon);?>
<?php js::set('projectLang', $lang->projectCommon);?>
<?php include '../../common/view/footer.lite.html.php';?>
-72
View File
@@ -1,72 +0,0 @@
<?php
/**
* The create struct view of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Thanatos <thanatos@915.com>
* @package doc
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/chosen.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::import($jsRoot . 'vue/vue.js');?>
<?php js::set('paramsTypeOption', $lang->api->paramsScopeOptions);?>
<?php
js::set('typeOptions', $typeOptions);
js::set('structAdd', $lang->struct->add);
js::set('addSubField', $lang->struct->addSubField);
js::set('structDelete', $lang->delete);
js::set('langField', $lang->struct->field);
js::set('langDesc', $lang->struct->desc);
js::set('struct_field', $lang->struct->field);
js::set('struct_desc', $lang->struct->desc);
js::set('struct_action', $lang->struct->action);
js::set('struct_required', $lang->struct->required);
js::set('struct_paramsType', $lang->struct->paramsType);
js::set('struct', $struct);
?>
<div id="app">
<div id="mainContent" class="main-content">
<div class='center-block'>
<div class='main-header'>
<h2>
<?php echo $struct->name;?>
<small><?php echo $lang->arrow . ' ' . $lang->api->editStruct;?></small>
</h2>
</div>
<form class="load-indicator main-form form-ajax" id="dataform" method='post' enctype='multipart/form-data'>
<table class='table table-form'>
<tbody>
<tr>
<th class='w-110px'><?php echo $lang->api->structName;?></th>
<td><?php echo html::input('name', $struct->name, "class='form-control' required");?></td>
<td></td>
</tr>
<tr>
<td class="w-110px"></td>
<td colspan="2">
<body-field @change="changeAttr" :attr="struct.attribute" :struct-type="struct.structType"></body-field>
<input type="hidden" name="attribute" v-model="attr">
</td>
</tr>
<tr>
<th class='w-110px'><?php echo $lang->api->desc;?></th>
<td colspan="2"><?php echo html::textarea('desc', $struct->desc, "class='form-control'");?></td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton();?>
<?php if(empty($gobackLink)) echo html::backButton($lang->goback, "data-app='{$app->tab}'");?>
<?php if(!empty($gobackLink)) echo html::a($gobackLink, $lang->goback, '', "class='btn btn-back btn-wide'");?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-91
View File
@@ -1,91 +0,0 @@
<?php
/**
* The index view file of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package doc
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php js::set('confirmDelete', $lang->api->confirmDelete);?>
<?php js::set('treeData', $libTree);?>
<div id="mainMenu" class="clearfix">
<div id="leftBar" class="btn-toolbar pull-left">
<?php echo $objectDropdown;?>
<?php if(!empty($libTree)):?>
<a class="btn btn-link querybox-toggle" id='bysearchTab'><i class="icon icon-search muted"></i> <?php echo $lang->doc->searchDoc;?></a>
<?php endif;?>
</div>
<div class="btn-toolbar pull-right">
<?php
if($libTree and common::hasPriv('api', 'struct')) echo html::a($this->createLink('api', 'struct', "libID=$libID"), "<i class='icon-treemap muted'> </i>" . $lang->api->struct, '', "class='btn btn-link'");
if($libTree and common::hasPriv('api', 'releases')) echo html::a($this->createLink('api', 'releases', "libID=$libID", 'html', true), "<i class='icon-version muted'> </i>" . $lang->api->releases, '', "class='btn btn-link iframe' data-width='800px'");
if($libTree and common::hasPriv('api', 'createRelease')) echo html::a($this->createLink('api', 'createRelease', "libID=$libID"), "<i class='icon-publish muted'> </i>" . $lang->api->createRelease, '', "class='btn btn-link iframe' data-width='800px'");
if($libTree and common::hasPriv('api', 'export') and $config->edition != 'open') echo html::a($this->createLink('api', 'export', "libID=$libID&version=$version&release=$release&moduleID=$moduleID", 'html', true), "<i class='icon-export muted'> </i>" . $lang->export, '', "class='btn btn-link export' data-width='480px' id='export'");
if(common::hasPriv('api', 'createLib')) echo html::a($this->createLink('api', 'createLib', "type=" . ($objectType ? $objectType : 'nolink') . "&objectID=$objectID"), '<i class="icon icon-plus"></i> ' . $lang->api->createLib, '', 'class="btn btn-secondary iframe" data-width="800px"');
if($libTree and common::hasPriv('api', 'create')) echo html::a($this->createLink('api', 'create', "libID=$libID&moduleID=$moduleID"), '<i class="icon icon-plus"></i> ' . $lang->api->createApi, '', 'class="btn btn-primary"');
?>
</div>
</div>
<div id='mainContent' class="fade <?php if(!empty($libTree)) echo 'flex';?>">
<?php if(empty($libTree)):?>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->doc->noLib;?></span>
<?php
if(common::hasPriv('api', 'createLib')) echo html::a(helper::createLink('api', 'createLib', "type=" . ($objectType ? $objectType : 'nolink')), '<i class="icon icon-plus"></i> ' . $lang->api->createLib, '', 'class="btn btn-info iframe" data-width="800px"');
?>
</p>
</div>
<?php else:?>
<div id='sideBar' class="panel side side-col col overflow-auto" data-min-width="150">
<?php include '../../doc/view/lefttree.html.php';?>
</div>
<div class="sidebar-toggle flex-center"><i class="icon icon-angle-left"></i></div>
<div class="main-col flex-full overflow-visible flex-auto overflow-visible" data-min-width="500">
<div class="cell<?php if($browseType == 'bySearch') echo ' show';?>" style="min-width: 400px" id="queryBox" data-module='api'></div>
<?php include 'apilist.html.php';?>
</div>
<?php endif;?>
</div>
<div class='hidden' id='dropDownData'>
<ul class='libDorpdown'>
<?php if(common::hasPriv('tree', 'browse')):?>
<li data-method="addCataLib" data-has-children='%hasChildren%' data-libid='%libID%' data-moduleid="%moduleID%" data-type="add"><a><i class="icon icon-add-directory"></i><?php echo $lang->doc->libDropdown['addModule'];?></a></li>
<?php endif;?>
<?php if(common::hasPriv('api', 'editLib')):?>
<li data-method="editLib"><a href='<?php echo inlink('editLib', 'libID=%libID%');?>' data-toggle='modal' data-type='iframe'><i class="icon icon-edit"></i><?php echo $lang->doc->libDropdown['editLib'];?></a></li>
<?php endif;?>
<?php if(common::hasPriv('api', 'deleteLib')):?>
<li data-method="deleteLib"><a href='<?php echo inlink('deleteLib', 'libID=%libID%');?>' target='hiddenwin'><i class="icon icon-trash"></i><?php echo $lang->doc->libDropdown['deleteLib'];?></a></li>
<?php endif;?>
</ul>
<ul class='moduleDorpdown'>
<?php if(common::hasPriv('tree', 'browse')):?>
<li data-method="addCataBro" data-type="add" data-id="%moduleID%"><a><i class="icon icon-add-directory"></i><?php echo $lang->doc->libDropdown['addSameModule'];?></a></li>
<li data-method="addCataChild" data-type="add" data-id="%moduleID%" data-has-children='%hasChildren%'><a><i class="icon icon-add-directory"></i><?php echo $lang->doc->libDropdown['addSubModule'];?></a></li>
<li data-method="editCata" class='edit-module'><a data-href='<?php echo helper::createLink('tree', 'edit', 'moduleID=%moduleID%&type=doc');?>'><i class="icon icon-edit"></i><?php echo $lang->doc->libDropdown['editModule'];?></a></li>
<li data-method="deleteCata"><a href='<?php echo helper::createLink('tree', 'delete', 'rootID=%libID%&moduleID=%moduleID%');?>' target='hiddenwin'><i class="icon icon-trash"></i><?php echo $lang->doc->libDropdown['delModule'];?></a></li>
<?php endif;?>
</ul>
</div>
<div class='hidden' data-id="ulTreeModal">
<ul data-id="liTreeModal" class="menu-active-primary menu-hover-primary has-input">
<li data-id="insert" class="has-input">
<input data-target="%target%" class="form-control input-tree"></input>
</li>
</ul>
</div>
<div class="hidden" data-id="aTreeModal">
<a href="###" data-has-children="false" title="%name%" data-id="%id%">
<div class="text h-full w-full flex-between overflow-hidden" style="position: relative;">
<div>%name%</div>
<i class="icon icon-drop icon-ellipsis-v hidden file-drop-icon" data-iscatalogue="true"></i>
</div>
</a>
</div>
<?php include '../../common/view/footer.html.php';?>
-76
View File
@@ -1,76 +0,0 @@
<?php
/**
* The index view file of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Thanatos <thanatos915@163.com>
* @package doc
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-row fade'>
<div class='main-col'>
<div id='mainMenu' class='clearfix'>
<div class='main-header'>
<h2><?php echo $lang->api->managePublish;?></h2>
</div>
</div>
<?php if(empty($releases)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->noData;?></span></p>
</div>
<?php else:?>
<form class='main-table' method='post' id='releaseListForm'>
<table class='table has-sort-head' id='releaseList'>
<thead>
<tr>
<?php $vars = "libID={$libID}&orderBy=%s";?>
<th class='c-id'><?php common::printOrderLink('id', $orderBy, $vars, $lang->idAB);?></th>
<th class='c-version'><?php common::printOrderLink('version', $orderBy, $vars, $lang->api->version);?></th>
<th class='c-name'><?php echo $lang->api->desc;?></th>
<th class='c-user'><?php common::printOrderLink('addedBy', $orderBy, $vars, $lang->api->structAddedBy);?></th>
<th class='c-date'><?php common::printOrderLink('addedDate', $orderBy, $vars, $lang->api->structAddedDate);?></th>
<th class='c-actions'><?php echo $lang->actions;?></th>
</tr>
</thead>
<tbody>
<?php foreach($releases as $release):?>
<tr data-id="<?php echo $release->id;?>">
<td><?php printf('%03d', $release->id);?></td>
<td title=<?php echo $release->version;?>><?php echo $release->version;?></td>
<td title=<?php echo $release->desc;?>><?php echo $release->desc;?></td>
<td><?php echo zget($users, $release->addedBy, '');?></td>
<td class="c-date"><?php echo $release->addedDate;?></td>
<td class='c-actions'>
<?php if(common::hasPriv('api', 'deleteRelease')) echo html::a($this->createLink('api', 'deleteRelease', "libID=$libID&id=$release->id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->api->deleteRelease}' class='btn'");?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</form>
<?php endif;?>
</div>
</div>
<script>
parent.$('#triggerModal .modal-content .modal-header .close').click(function()
{
parent.location.reload();
});
/**
* Remove release.
*
* @param int $releaseID
* @access public
* @return void
*/
function removeRelease(releaseID)
{
$('tr[data-id=' + releaseID + ']').remove()
}
</script>
<?php include '../../common/view/footer.html.php';?>
-75
View File
@@ -1,75 +0,0 @@
<?php
/**
* The index view file of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Thanatos <thanatos915@163.com>
* @package doc
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-row fade'>
<div class='main-col'>
<div class="cell" id="queryBox" data-module='user'></div>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left leftBar'>
<?php $gobackLink = $this->session->structList ? $this->session->structList : inlink('struct', "libID=$libID");?>
<?php echo html::a($gobackLink, "<i class='icon-back'></i> " . $lang->goback, '', "class='btn btn-link'");?>
<div class='divider'></div>
<div class='page-title'>
<span class='text'><?php echo $lang->struct->list;?></span>
</div>
</div>
<div class='btn-toolbar pull-right'>
<?php common::printLink('api', 'createStruct', "libID=$libID", "<i class='icon icon-plus'></i> " . $lang->api->createStruct, '', "class='btn btn-primary create-user-btn'");?>
</div>
</div>
<?php if(empty($structs)):?>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->api->noStruct;?></span>
</p>
</div>
<?php else:?>
<form class='main-table table-user' data-ride='table' method='post' data-checkable='false' id='userListForm'>
<table class='table has-sort-head' id='userList'>
<thead>
<tr>
<?php $vars = "libID={$libID}&releaseID=$releaseID&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}";?>
<th class='c-id'><?php common::printOrderLink('id', $orderBy, $vars, $lang->idAB);?></th>
<th class='c-type'><?php common::printOrderLink('type', $orderBy, $vars, $lang->api->structType);?></th>
<th class='c-name'><?php echo $lang->api->structName;?></th>
<th class='c-user'><?php common::printOrderLink('addedBy', $orderBy, $vars, $lang->api->structAddedBy);?></th>
<th class='c-date'><?php common::printOrderLink('addedDate', $orderBy, $vars, $lang->api->structAddedDate);?></th>
<th class='c-actions-2'><?php echo $lang->actions;?></th>
</tr>
</thead>
<tbody>
<?php foreach($structs as $struct):?>
<tr>
<td><?php printf('%03d', $struct->id);?></td>
<td><?php echo $struct->type;?></td>
<td class='c-name' title="<?php echo $struct->name;?>"><?php echo $struct->name;?></td>
<td><?php echo $struct->addedName;?></td>
<td><?php echo formatTime($struct->addedDate, 'Y-m-d');?></td>
<td class='c-actions'>
<?php
if(common::hasPriv('api', 'editStruct')) echo html::a($this->createLink('api', 'editStruct', "libID=$libID&structID=$struct->id"), '<i class="icon-edit"></i>', '', "title='{$lang->api->editStruct}' class='btn'");
if(common::hasPriv('api', 'deleteStruct')) echo html::a($this->createLink('api', 'deleteStruct', "libID=$libID&structID=$struct->id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->api->deleteStruct}' class='btn'");
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<div class='table-footer'>
<?php $pager->show('right', 'pagerjs');?>
</div>
</form>
<?php endif;?>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-46
View File
@@ -1,46 +0,0 @@
<?php
/**
* The index view file of doc module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package doc
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::set('confirmDelete', $lang->api->confirmDelete);?>
<?php js::set('treeData', $libTree);?>
<?php js::set('apiID', $apiID);?>
<style>.panel-body{min-height: 180px}</style>
<div id="mainMenu" class="clearfix">
<div id="leftBar" class="btn-toolbar pull-left">
<?php echo $objectDropdown;?>
<?php $gobackLink = $this->session->structList ? $this->session->structList : inlink('index', "libID=$libID&moduleID=$moduleID");?>
<?php echo html::a($gobackLink, "<i class='icon-back'></i> " . $lang->goback, '', "class='btn btn-link'");?>
</div>
<div class="btn-toolbar pull-right">
<?php
if($libTree and common::hasPriv('api', 'struct')) echo html::a($this->createLink('api', 'struct', "libID=$libID"), "<i class='icon-treemap muted'> </i>" . $lang->api->struct, '', "class='btn btn-link'");
if($libTree and common::hasPriv('api', 'releases')) echo html::a($this->createLink('api', 'releases', "libID=$libID", 'html', true), "<i class='icon-version muted'> </i>" . $lang->api->releases, '', "class='btn btn-link iframe' data-width='800px'");
if($libTree and common::hasPriv('api', 'createRelease')) echo html::a($this->createLink('api', 'createRelease', "libID=$libID"), "<i class='icon-publish muted'> </i>" . $lang->api->createRelease, '', "class='btn btn-link iframe' data-width='800px'");
if($objectType == 'api' and common::hasPriv('api', 'createLib')) echo html::a($this->createLink('api', 'createLib', "type=" . ($objectType ? $objectType : 'nolink') . "&objectID=$objectID"), '<i class="icon icon-plus"></i> ' . $lang->api->createLib, '', 'class="btn btn-secondary iframe" data-width="800px"');
if($objectType != 'api' and common::hasPriv('doc', 'createLib')) echo html::a($this->createLink('doc', 'createLib', "type=" . ($objectType ? $objectType : 'nolink') . "&objectID=$objectID"), '<i class="icon icon-plus"></i> ' . $lang->api->createLib, '', 'class="btn btn-secondary iframe" data-width="800px"');
if($libTree and common::hasPriv('api', 'create')) echo html::a($this->createLink('api', 'create', "libID=$libID&moduleID=$moduleID"), '<i class="icon icon-plus"></i> ' . $lang->api->createApi, '', 'class="btn btn-primary"');
?>
</div>
</div>
<div id='mainContent' class="fade flex">
<div id='sideBar' class="panel side side-col col overflow-auto" data-min-width="150">
<?php include '../../doc/view/lefttree.html.php';?>
</div>
<div class="sidebar-toggle flex-center"><i class="icon icon-angle-left"></i></div>
<div class="main-col flex-full overflow-visible flex-auto overflow-visible" data-min-width="500">
<div class="cell<?php if($browseType == 'bySearch') echo ' show';?>" style="min-width: 400px" id="queryBox" data-module='api'></div>
<?php include 'apilist.html.php';?>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-140
View File
@@ -1,140 +0,0 @@
<?php
/**
* The view file of backup module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package backup
* @version $Id: view.html.php 2568 2012-02-09 06:56:35Z shiyangyangwork@yahoo.cn $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php if(!empty($error)):?>
<div id="notice" class='alert alert-success' style="margin-bottom:35px;">
<div class="content"><i class='icon-exclamation-sign'></i> <?php echo $error;?></div>
</div>
<?php endif;?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'><?php // common::printAdminSubMenu('system');?></div>
</div>
<div id='mainContent' class="main-row">
<div class="main-col main-content">
<div class='main-header'>
<h2>
<?php echo $lang->backup->history?>
</h2>
<div class='pull-right'>
<?php common::printLink('backup', 'setting', '', "<i class='icon icon-cog'></i> " . $lang->backup->setting, '', "data-width='500' class='iframe btn btn-primary'");?>
<?php
if(common::hasPriv('backup', 'backup'))
{
$backupLink = helper::createLink('backup', 'backup', 'reload=yes');
echo html::commonButton("<i class='icon icon-copy'> </i>" . $lang->backup->backup, "data-link='{$backupLink}'", 'btn btn-primary backup load-indicator');
}
?>
</div>
</div>
<table class='table table-condensed table-bordered active-disabled table-fixed'>
<thead class="text-center">
<tr>
<th class='w-150px'><?php echo $lang->backup->time?></th>
<th><?php echo $lang->backup->files?></th>
<th class='w-100px'><?php echo $lang->backup->allCount?></th>
<th class='w-100px'><?php echo $lang->backup->count?></th>
<th class='w-100px'><?php echo $lang->backup->size?></th>
<th class='w-100px'><?php echo $lang->backup->status?></th>
<th class='actionWidth'><?php echo $lang->actions?></th>
</tr>
</thead>
<tbody class='text-center'>
<?php foreach($backups as $backupFile):?>
<?php $rowspan = count($backupFile->files);?>
<?php $i = 0;?>
<?php $isPHP = false;?>
<?php foreach($backupFile->files as $file => $summary):?>
<?php if(!$isPHP) $isPHP = strpos($file, '.php') !== false;?>
<tr>
<?php if($i == 0):?>
<td <?php if($rowspan > 1) echo "rowspan='$rowspan'"?>><?php echo date(DT_DATETIME1, $backupFile->time);?></td>
<?php endif;?>
<td title=<?php echo $file;?> class='text-left' style='padding-left:5px;'><?php echo $file;?></td>
<td><?php echo zget($summary, 'allCount', '');?></td>
<td><?php echo zget($summary, 'count', '');?></td>
<td>
<?php
$size = zget($summary, 'size', 0);
if(!empty($size)) echo $this->backup->processFileSize($size);
?>
</td>
<td style='overflow:visible'>
<?php
$status = zget($summary, 'count', 0) == zget($summary, 'allCount', 0) ? 'success' : 'fail';
if(!empty($summary['errorFiles'])) $status = 'fail';
if(!empty($summary['errorFiles'])):
?>
<div class="dropdown dropdown-hover"><?php echo $lang->backup->statusList[$status];?> <span class="caret"></span><div class="dropdown-menu pull-right errorFiles"><?php echo $lang->backup->copiedFail . '<br />' . join('<br />', $summary['errorFiles']);?></div></div>
<?php else:?>
<?php echo $lang->backup->statusList[$status];?>
<?php endif;?>
</td>
<?php if($i == 0):?>
<td <?php if($rowspan > 1) echo "rowspan='$rowspan'"?>>
<?php
if(common::hasPriv('backup', 'rmPHPHeader') and $isPHP)
{
echo html::a(inlink('rmPHPHeader', "file={$backupFile->name}"), $lang->backup->rmPHPHeader, 'hiddenwin', "class='rmPHPHeader'");
echo "<br />";
}
if(common::hasPriv('backup', 'restore')) echo html::a(inlink('restore', "file={$backupFile->name}&confirm=yes"), $lang->backup->restore, 'hiddenwin', "class='restore'");
if(common::hasPriv('backup', 'delete')) echo html::a(inlink('delete', "file=$backupFile->name"), $lang->delete, 'hiddenwin');
?>
</td>
<?php endif;?>
</tr>
<?php $i++;?>
<?php endforeach;?>
<?php endforeach;?>
</tbody>
</table>
<h2>
<span class='label label-info'>
<?php echo $lang->backup->restoreTip;?>
<?php printf($lang->backup->holdDays, $config->backup->holdDays)?>
</span>
</h2>
</div>
</div>
<div class="modal fade" id="waiting" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog w-400px">
<div class="modal-content">
<div class="modal-body">
<p><?php echo $lang->backup->waiting?></p>
<div id='message'><?php echo sprintf($lang->backup->progressSQL, 0);?></div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="spaceConfirm" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><?php echo $lang->backup->cancelBackup;?></button>
</div>
</div>
</div>
</div>
<?php js::set('backup', $lang->backup->common);?>
<?php js::set('startBackup', $lang->backup->backup);?>
<?php js::set('getSpaceLoading', $lang->backup->getSpaceLoading);?>
<?php js::set('rmPHPHeader', $lang->backup->rmPHPHeader);?>
<?php js::set('confirmRestore', $lang->backup->confirmRestore);?>
<?php js::set('restore', $lang->backup->restore);?>
<?php js::set('backupTimeout', $lang->backup->error->timeout);?>
<?php js::set('alertTips', $lang->backup->insufficientDisk);?>
<?php js::set('backupError', empty($backupError) ? '' : $backupError);?>
<?php include '../../common/view/footer.html.php';?>
-55
View File
@@ -1,55 +0,0 @@
<?php
/**
* The setting view file of backup module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package backup
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php';?>
<div id='mainContent' class='main-content'>
<div class='main-header'>
<h2><?php echo $lang->backup->setting;?></h2>
</div>
<?php if(!empty($error)):?>
<div id='error'><?php echo $error;?></div>
<?php else:?>
<form method='post' target='hiddenwin'>
<table class='w-p100'>
<tr>
<td style='height:80px;vertical-align:top'>
<div class='input-group'>
<?php echo html::checkbox('setting', $lang->backup->settingList, isset($config->backup->setting) ? $config->backup->setting : '');?>
</div>
</td>
</tr>
<tr>
<td>
<div class='input-group'>
<span class='input-group-addon'><?php echo $lang->backup->settingDir;?></span>
<?php echo html::input('settingDir', !empty($config->backup->settingDir) ? $config->backup->settingDir : $this->app->getTmpRoot() . 'backup/', "class='form-control'");?>
</div>
</td>
</tr>
<?php if(common::hasPriv('backup', 'change')):?>
<tr>
<td>
<div class='input-group'>
<span class='input-group-addon'><?php echo $lang->backup->change;?></span>
<?php echo html::input('holdDays', $config->backup->holdDays, "class='form-control'");?>
<span class='input-group-addon'><?php echo $lang->day;?></span>
</div>
</td>
</tr>
<?php endif;?>
<tr><td><?php echo html::submitButton('', '', 'btn btn-primary');?></td></tr>
</table>
</form>
<?php endif;?>
</div>
<?php include '../../common/view/footer.lite.html.php';?>
-77
View File
@@ -1,77 +0,0 @@
<?php
/**
* The bug block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(empty($bugs)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-bugs .c-id {width: 55px;}
.block-bugs .c-level {width: 60px;text-align: center;}
.block-bugs .c-pri {width: 35px;text-align: center;}
.block-bugs .c-date {width: 140px;}
.block-bugs .c-status {width: 80px;}
.block-bugs.block-sm .c-status {text-align: center;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-fixed table-fixed-head table-hover tablesorter block-bugs <?php if(!$longBlock) echo 'block-sm'?>'>
<thead>
<tr>
<th class='c-id'><?php echo $lang->idAB?></th>
<th class='c-name'><?php echo $lang->bug->title;?></th>
<?php if($longBlock):?>
<th class='c-level'><?php echo $lang->bug->abbr->severity?></th>
<th class='c-pri'><?php echo $lang->priAB?></th>
<th class='c-date'><?php echo $lang->bug->deadline;?></th>
<?php endif;?>
<th class='c-status'><?php echo $lang->bug->abbr->status;?></th>
</tr>
</thead>
<?php
$hasCustomSeverity = false;
foreach($lang->bug->severityList as $severityKey => $severityValue)
{
if(!empty($severityKey) && (string)$severityKey != (string)$severityValue)
{
$hasCustomSeverity = true;
break;
}
}
?>
<tbody>
<?php foreach($bugs as $bug):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
?>
<tr>
<td class='c-id-xs'><?php echo sprintf('%03d', $bug->id);?></td>
<td class='c-name' title='<?php echo $bug->title?>'><?php echo html::a($this->createLink('bug', 'view', "bugID=$bug->id"), $bug->title, '', "style='color: $bug->color'")?></td>
<?php if($longBlock):?>
<td class='c-severity'>
<?php if($hasCustomSeverity):?>
<span class='<?php echo 'label-severity-custom';?>' title='<?php echo zget($lang->bug->severityList, $bug->severity);?>' data-severity='<?php echo $bug->severity;?>'><?php echo zget($lang->bug->severityList, $bug->severity, $bug->severity);?></span>
<?php else:?>
<span class='label-severity' title='<?php echo zget($lang->bug->severityList, $bug->severity);?>' data-severity='<?php echo $bug->severity;?>'></span>
<?php endif;?>
</td>
<td class='c-pri'><span class='label-pri label-pri-<?php echo $bug->pri?>' title='<?php echo zget($lang->bug->priList, $bug->pri);?>'><?php echo zget($lang->bug->priList, $bug->pri)?></span></td>
<td class='c-date'><?php echo helper::isZeroDate($bug->deadline) ? '' : $bug->deadline;?></td>
<?php endif;?>
<?php $status = $this->processStatus('bug', $bug);?>
<td class='c-status' title='<?php echo $status;?>'>
<span class="status-bug status-<?php echo $bug->status?>"><?php echo $status;?></span>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-50
View File
@@ -1,50 +0,0 @@
<?php
/**
* The build block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(empty($builds)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-builds'>
<thead>
<tr>
<th class='c-id text-center'><?php echo $lang->idAB?></th>
<th><?php echo $lang->build->name;?></th>
<?php if($longBlock):?>
<th><?php echo $lang->build->product;?></th>
<th><?php echo $lang->build->project;?></th>
<?php endif;?>
<th class='c-date'><?php echo $lang->build->date;?></th>
</tr>
</thead>
<tbody>
<?php foreach($builds as $build):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
$buildViewLink = $this->createLink('build', 'view', "buildID={$build->id}");
$productViewLink = $this->createLink('product', 'browse', "productID={$build->product}");
$projectViewLink = $this->createLink('projectbuild', 'browse', "projectID={$build->project}");
?>
<tr <?php echo $appid?>>
<td class='text-center'><?php echo sprintf('%03d', $build->id);?></td>
<td title='<?php echo $build->name?>'><?php echo html::a($buildViewLink, $build->name);?></td>
<?php if($longBlock):?>
<td title='<?php echo $build->productName?>'><?php echo html::a($build->shadow ? $projectViewLink : $productViewLink, $build->productName);?></td>
<td title='<?php echo $build->projectName?>'><?php echo html::a($projectViewLink, $build->projectName);?></td>
<?php endif;?>
<td><?php echo $build->date?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-62
View File
@@ -1,62 +0,0 @@
<?php
/**
* The case block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(empty($cases)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<div class='panel-body has-table scrollbar-hover'>
<style>
.block-cases.block-sm .c-status{text-align:center}
.block-cases.block-sm .c-status .status-text{display:none;}
.case-status-normal {color: #a6aab8;}
.case-status-blocked { color: #a6aab8; }
.case-status-investigate {color: #a6aab8;}
</style>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-cases <?php if(!$longBlock) echo 'block-sm';?>'>
<thead>
<tr class='text-center'>
<th class='c-id-xs'><?php echo $lang->idAB?></th>
<th class='c-pri'><?php echo $lang->priAB?></th>
<th class='c-title text-left'><?php echo $lang->case->title;?></th>
<?php if($longBlock):?>
<th class='c-runtime'><?php echo $lang->testtask->lastRunTime;?></th>
<th class='c-result'><?php echo $lang->testtask->lastRunResult;?></th>
<th class='c-status'><?php echo $lang->statusAB;?></th>
<?php endif;?>
</tr>
</thead>
<tbody>
<?php foreach($cases as $case):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
$viewLink = $this->createLink('testcase', 'view', "caseID={$case->id}");
?>
<tr class='text-center' <?php echo $appid?>>
<td class='c-id-xs'><?php echo sprintf('%03d', $case->id);?></td>
<td class='c-pri'><span class='label-pri label-pri-<?php echo $case->pri?>' title='<?php echo zget($lang->case->priList, $case->pri, $case->pri);?>'><?php echo zget($lang->case->priList, $case->pri, $case->pri)?></span></td>
<td class='c-title text-left' style='color: <?php echo $case->color?>' title='<?php echo $case->title?>'><?php echo $case->title?></td>
<?php if($longBlock):?>
<td class='c-runtime'><?php if(!helper::isZeroDate($case->lastRunDate)) echo date(DT_MONTHTIME1, strtotime($case->lastRunDate));?></td>
<td class='c-result'><?php if($case->lastRunResult) echo $lang->testcase->resultList[$case->lastRunResult];?></td>
<?php $status = $this->processStatus('testcase', $case);?>
<td class='c-status' title='<?php echo $status;?>'>
<span class="case-status-<?php echo $case->status?>">
<span class='status-text'><?php echo $status;?></span>
</span>
</td>
<?php endif;?>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-46
View File
@@ -1,46 +0,0 @@
<?php
/**
* The admin view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php
$webRoot = $this->app->getWebRoot();
$jsRoot = $webRoot . "js/";
include '../../common/view/chosen.html.php';
?>
<?php if(isset($pageCSS)) css::internal($pageCSS); ?>
<form class="form-horizontal load-indicator" id="blockAdminForm" method='post' target='hiddenwin'>
<div class="form-group">
<label for="modules" class="col-sm-3"><?php echo $lang->block->lblModule;?></label>
<div class="col-sm-7">
<?php echo html::select('module', $modules, '', "class='form-control chosen'")?>
</div>
</div>
<?php if($blocks):?>
<div id="blocksList">
<div class="form-group">
<label for="moduleBlock" class="col-sm-3"><?php echo $this->lang->block->lblBlock;?></label>
<div class="col-sm-7">
<?php html::select('block', $blocks, '', "class='form-control chosen'");?>
</div>
</div>
</div>
<?php endif;?>
<div id="blockParams">
<?php include 'publicform.html.php';?>
</div>
<div class="form-group">
<div class="col-sm-7 col-sm-offset-3">
<button type="submit" class="btn btn-wide btn-primary"><?php echo $lang->save;?></button>
<button type="button" class="btn btn-wide" data-dismiss="modal"><?php echo $lang->cancel;?></button>
</div>
</div>
</form>
<?php if(isset($pageJS)) js::execute($pageJS);?>
-148
View File
@@ -1,148 +0,0 @@
<?php
/**
* The dashboard view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
if($extView = $this->getExtViewFile(__FILE__)){include $extView; return helper::cd();}
$webRoot = $config->webRoot;
$jsRoot = $webRoot . "js/";
$themeRoot = $webRoot . "theme/";
if(isset($pageCSS)) css::internal($pageCSS);
$useGuest = $this->app->user->account == 'guest';
?>
<?php include '../../common/view/tablesorter.html.php';?>
<div class='dashboard auto-fade-in fade' id='dashboard' data-confirm-remove-block='<?php echo $lang->block->confirmRemoveBlock;?>'>
<?php if(empty($longBlocks) && empty($shortBlocks)):?>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->block->noData;?></span>
<?php echo html::a($this->createLink("block", "admin", "id=0&module=$module"), "<i class='icon icon-plus'></i> {$lang->block->createBlock}", '', "data-toggle='modal' data-type='ajax' data-width='700' data-title='{$lang->block->createBlock}' class='btn btn-info'")?>
<?php echo html::a($this->createLink("block", "ajaxReset", "module=$module"), "<i class='icon icon-refresh'></i> {$lang->block->reset}", 'hiddenwin', 'class="btn btn-info"')?>
</p>
</div>
<?php endif;?>
<div class="row">
<div class='col-main'>
<?php foreach($longBlocks as $index => $block):?>
<?php if(isset($config->block->closed) && strpos(",{$config->block->closed},", ",{$block->source}|{$block->block},") !== false) continue;?>
<div class='panel block-<?php echo $block->block;?> <?php if(isset($block->params->color)) echo 'panel-' . $block->params->color;?>' id='block<?php echo $block->id?>' data-id='<?php echo $block->id?>' data-name='<?php echo $block->title?>' data-order='<?php echo $block->order?>' data-url='<?php echo $block->blockLink?>'>
<?php $hasHeading = ($block->block != 'welcome');?>
<?php if($hasHeading):?>
<div class='panel-heading'>
<div class='panel-title'><?php echo $block->title;?></div>
<?php endif;?>
<nav class='panel-actions nav nav-default'>
<?php if($this->config->vision == 'rnd' && $block->block == 'guide' && !commonModel::isTutorialMode()) echo '<li>' . html::a($this->createLink('tutorial', 'start'), $lang->block->tutorial, '', "title='{$lang->block->tutorial}' class='iframe tutorialBtn'") . '</li>'; ?>
<?php if(!empty($block->moreLink)) echo '<li>' . html::a($block->moreLink, strtoupper($lang->more), '', "title='{$lang->more}'") . '</li>'; ?>
<li class='dropdown'>
<a href='javascript:;' data-toggle='dropdown' class='panel-action'><i class='icon icon-ellipsis-v'></i></a>
<ul class='dropdown-menu pull-right'>
<li><a href='javascript:;' class='refresh-panel'><i class='icon-repeat'></i> <?php echo $lang->block->refresh;?></a></li>
<?php if(!$useGuest):?>
<li><a data-toggle='modal' href="<?php echo $this->createLink("block", "admin", "id=$block->id&module=$module");?>" class='edit-block' data-title='<?php echo $block->title;?>' ><?php echo $lang->edit;?></a></li>
<li><a href='javascript:deleteBlock(<?php echo $index;?>);' class='hidden-panel'><?php echo $lang->block->hidden;?></a></li>
<li><?php if($this->app->user->admin):?>
<?php echo html::a($this->createLink('block','close',"blockID={$block->id}"), $lang->block->closeForever, 'hiddenwin', "class='close-block' onclick=\"return confirm('{$lang->block->confirmClose}')\"")?>
<?php endif;?></li>
<?php endif;?>
<li class="divider"></li>
<li><?php echo html::a($this->createLink("block", "admin", "id=0&module=$module"), "{$lang->block->createBlock}", '', "data-toggle='modal' data-type='ajax' data-width='700' data-title='{$lang->block->createBlock}'")?></li>
<li><?php echo html::a($this->createLink("block", "ajaxReset", "module=$module"), "{$lang->block->reset}", 'hiddenwin')?></li>
</ul>
</li>
</nav>
<?php if($hasHeading):?>
</div>
<?php endif;?>
<div class='panel-body scrollbar-hover'></div>
</div>
<?php endforeach;?>
</div>
<div class='col-side'>
<?php foreach($shortBlocks as $index => $block):?>
<?php if(isset($config->block->closed) && strpos(",{$config->block->closed},", ",{$block->source}|{$block->block},") !== false) continue;?>
<div class='panel block-sm block-<?php echo $block->block;?> <?php if(isset($block->params->color)) echo 'panel-' . $block->params->color;?>' id='block<?php echo $block->id?>' data-id='<?php echo $block->id?>' data-name='<?php echo $block->title?>' data-order='<?php echo $block->order?>' data-url='<?php echo $block->blockLink?>'>
<?php $hasHeading = ($block->block != 'welcome');?>
<?php if($hasHeading):?>
<div class='panel-heading'>
<div class='panel-title'><?php echo $block->title;?></div>
<?php endif;?>
<nav class='panel-actions nav nav-default'>
<?php if(!empty($block->moreLink)) echo '<li>' . html::a($block->moreLink, strtoupper($lang->more), '', "title='{$lang->more}'") . '</li>';?>
<li class='dropdown'>
<a href='javascript:;' data-toggle='dropdown' class='panel-action'><i class='icon icon-ellipsis-v'></i></a>
<ul class='dropdown-menu pull-right'>
<li><a href='javascript:;' class='refresh-panel'><i class='icon-repeat'></i> <?php echo $lang->block->refresh?></a></li>
<?php if(!$useGuest):?>
<li><a data-toggle='modal' href="<?php echo $this->createLink("block", "admin", "id=$block->id&module=$module"); ?>" class='edit-block' data-title='<?php echo $block->title; ?>' ><?php echo $lang->edit; ?></a></li>
<li><a href='javascript:deleteBlock(<?php echo $index?>);' class='hidden-panel'><?php echo $lang->block->hidden; ?></a></li>
<?php if($this->app->user->admin):?>
<li><?php echo html::a($this->createLink('block', 'close', "blockID={$block->id}"), $lang->block->closeForever, 'hiddenwin', "class='close-block' onclick=\"return confirm('{$lang->block->confirmClose}')\"")?>
<?php endif;?>
<?php endif;?>
<li class="divider"></li>
<li><?php echo html::a($this->createLink("block", "admin", "id=0&module=$module"), "{$lang->block->createBlock}", '', "data-toggle='modal' data-type='ajax' data-width='700' data-title='{$lang->block->createBlock}'")?></li>
<li><?php echo html::a($this->createLink("block", "ajaxReset", "module=$module"), "{$lang->block->reset}", 'hiddenwin')?></li>
</ul>
</li>
</nav>
<?php if($hasHeading):?>
</div>
<?php endif;?>
<div class='panel-body scrollbar-hover'></div>
</div>
<?php endforeach;?>
</div>
</div>
</div>
<script>
config.ordersSaved = '<?php echo $lang->block->ordersSaved; ?>';
config.confirmRemoveBlock = '<?php echo $lang->block->confirmRemoveBlock; ?>';
config.cannotPlaceInLeft = '<?php echo $lang->block->cannotPlaceInLeft; ?>';
config.cannotPlaceInRight = '<?php echo $lang->block->cannotPlaceInRight; ?>';
var module = '<?php echo $module?>';
var useGuest = <?php echo $useGuest ? 'true' : 'false';?>;
<?php $remind = $this->loadModel('misc')->getPluginRemind();?>
<?php if(!empty($remind)):?>
var myModalTrigger = new $.zui.ModalTrigger({title:'<?php echo $lang->misc->expiredTipsTitle;?>', custom: function(){return <?php echo json_encode($remind);?>}, width:'600px'});
var result = myModalTrigger.show();
$('#pluginButton').click(function(){myModalTrigger.close()});
$('#cancelButton').click(function(){myModalTrigger.close()});
<?php endif;?>
<?php /* Check annual remind */ ?>
$(function()
{
function checkRemind()
{
$.getJSON(createLink('misc', 'getRemind'), function(response)
{
if(!response || !response.data || !response.data.content) return;
var myModalTrigger = new $.zui.ModalTrigger(
{
title: response.data.title,
custom: response.data.content,
width: 600
});
$('#showAnnual').click(function(){myModalTrigger.close()});
});
}
setTimeout(checkRemind, 1000);
$('#dashboard .row .panel').each(function()
{
refreshBlock($(this));
})
});
</script>
<?php if($extView = $this->getExtViewFile(__FILE__)){include $extView; return helper::cd();}?>
<?php if(isset($pageJS)) js::execute($pageJS);?>
@@ -1,56 +0,0 @@
<?php
/**
* The doccollectlistblock view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Shujie Tian <tianshujie@easycorp.ltd>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.block-doccollectlist .panel-body {padding-top: 0px;}
.block-doccollectlist .doc-list > .doc-title {display: flex; overflow: hidden; text-overflow: clip; white-space: nowrap; padding: 8px 0;}
.block-doccollectlist .doc-list > .doc-title > .label-rank {margin-right: 10px; padding-top: 1px; border: unset; color: #2E7FFF; background-color: #D5E5FF;}
.block-doccollectlist .doc-list > .doc-title > .label-rank-1 {color: #FC5959; background-color: #FFE2D9;}
.block-doccollectlist .doc-list > .doc-title > .label-rank-2 {color: #FF8058; background-color: #FFE2D9;}
.block-doccollectlist .doc-list > .doc-title > .label-rank-3 {color: #FF9F46; background-color: #FFECDB;}
.block-doccollectlist .doc-list > .doc-title > .doc-name {overflow: hidden; max-width: calc(100% - 130px);}
[lang^=zh] .block-doccollectlist .doc-list > .doc-title > .doc-name {max-width: calc(100% - 110px);}
.block-doccollectlist .doc-list > .doc-title > .label-collect-count {margin-left: 10px;}
.block-doccollectlist .doc-list > .doc-title > .label-collect-count > .icon-flame {margin-bottom: 3px;}
.block-doccollectlist .doc-list > .doc-title > .label-collect-count > .icon-flame.gray {filter: grayscale(100%);}
.block-doccollectlist .doc-list > .doc-title > .label-collect-count > .view-text {color: #838A9D;}
</style>
<div class="panel-body">
<?php if(empty($docList)):?>
<div class='table-empty-tip'><p><span class='text-muted'><?php echo $lang->doc->noDoc;?></p></span></div>
<?php else:?>
<div class="doc-list table-row">
<?php $rank = 1;?>
<?php foreach($docList as $doc):?>
<div class='doc-title'>
<span class="label-pri label-rank label-rank-<?php echo $rank;?>"><?php echo $rank;?></span>
<?php
if(common::hasPriv('doc', 'view'))
{
echo html::a($this->createLink('doc', 'view', "docID=$doc->id"), $doc->title, '', "title='{$doc->title}' class='doc-name' data-app='{$this->app->tab}'");
}
else
{
echo "<span class='doc-name' title='{$doc->title}'>{$doc->title}</span>";
}
?>
<div class='label-collect-count'>
<?php $flameClass = $rank < 4 ? '' : 'gray';?>
<?php echo html::image("static/svg/flame.svg", "class='icon-flame $flameClass'");?>
<span class='view-text'><?php echo sprintf($lang->doc->collectCount, $doc->collects);?></span>
</div>
</div>
<?php $rank ++;?>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
@@ -1,37 +0,0 @@
<?php if(empty($actions)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-docdynamic .timeline > li .timeline-text {max-width: 600px; display: block; white-space: nowrap; overflow: hidden; text-overflow: clip; max-height: 20px;}
.block-docdynamic .panel-body {padding-top: 0;}
.timeline > li:before {left: -26px;}
.timeline > li + li:after {left: -23px;}
.timeline-text {margin-left: -18px;}
.block-docdynamic .label-action {padding: 0 6px;}
.block-docdynamic .label-action + a {padding-left: 6px;}
.timeline > li.active:before {left: -30px; background-color: #FFF;}
.timeline > li.collected > div:after {background-color: #FFAF65;}
.timeline > li.releaseddoc > div:after {background-color: #66A2FF;}
.timeline > li > div:after {left: -27px;}
.timeline > li > div > .timeline-tag, .timeline > li > div > .timeline-text > .label-action {color: #838A9D;}
.timeline > li > div > .timeline-text > a {color: #313C52;}
</style>
<div class='panel-body scrollbar-hover'>
<ul class="timeline timeline-tag-left no-margin">
<?php
$i = 0;
foreach($actions as $action)
{
$user = zget($users, $action->actor);
$class = $action->major ? 'active' : '';
if(in_array($action->action, array('releaseddoc', 'collected'))) $class .= " {$action->action}";
echo "<li class='$class'><div>";
if($action->objectLink) printf($lang->block->dynamicInfo, $action->date, $user, $action->actionLabel, $action->objectLabel, $action->objectLink, $action->objectName, $action->objectName);
if(!$action->objectLink) printf($lang->block->noLinkDynamic, $action->date, $action->objectName, $user, $action->actionLabel, $action->objectLabel, ' ' . $action->objectName);
echo "</div></li>";
$i++;
}
?>
</ul>
</div>
<?php endif;?>
@@ -1,79 +0,0 @@
<?php
/**
* The docmycollectionblock view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yanyi Cao <caoyanyi@easycorp.ltd>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.block-docmycollection .panel-body {padding: 0px 10px 20px;}
.block-docmycollection .doc-list {display: flex; flex-wrap: wrap; padding: 0 4px 0 0;}
.block-docmycollection .doc-list > .doc-box {border: unset; flex: 0 1 50%; padding: 8px; width: 0;}
.block-docmycollection .doc-list > .doc-box > button.btn {padding: 5px 10px; height: 100%; width: 100%; cursor: pointer; white-space: nowrap; text-align: unset; border: 1px solid rgba(227, 228, 233, 0.6)}
.block-docmycollection .doc-list > .doc-box > .btn:hover {background: unset;}
.block-docmycollection .doc-list > .doc-box > .btn.no-priv {cursor: not-allowed; pointer-events: unset;}
.block-docmycollection .doc-list > .doc-box > .btn.no-priv p {pointer-events: none;}
.block-docmycollection .doc-list > .doc-box .date-interval {float: right; padding: 8px 0 8px 12px;}
.block-docmycollection .doc-list > .doc-box > .btn > h4 {padding-right: 5px;}
.block-docmycollection .doc-list > .doc-box .file-icon {margin-right: 2px; margin-bottom: 2px;}
.block-docmycollection .doc-list > .doc-box .plug-title {height: 16px; overflow: hidden;}
.block-docmycollection.block-sm .doc-list > .doc-box {flex: 0 1 100%;}
</style>
<?php $canView = common::hasPriv('doc', 'view');?>
<div class="panel-body">
<?php if(empty($docList)):?>
<div class='table-empty-tip'><p><span class='text-muted'><?php echo $lang->doc->noDoc;?></p></span></div>
<?php else:?>
<div class="doc-list">
<?php foreach($docList as $doc):?>
<div class="doc-box">
<button class="btn shadow-primary-hover <?php if(!$canView) echo 'no-priv';?>" data-link='<?php echo $this->createLink("doc", "view", "docID=$doc->id");?>'>
<span class='date-interval text-muted'>
<?php
$interval = $doc->editInterval;
$editTip = $lang->doc->todayUpdated;
if($interval->year)
{
$editTip = sprintf($lang->doc->yearsUpdated, $interval->year);
}
elseif($interval->month)
{
$editTip = sprintf($lang->doc->monthsUpdated, $interval->month);
}
elseif($interval->day)
{ $editTip = sprintf($lang->doc->daysUpdated, $interval->day);
}
echo $editTip;
?>
</span>
<h4 class="plug-title" title="<?php echo $doc->title;?>">
<?php
$docType = $doc->type == 'text' ? 'wiki-file' : $doc->type;
echo html::image("static/svg/{$docType}.svg", "class='file-icon'");
?>
<?php echo $doc->title;?>
</h4>
<p class='edit-date text-muted'><?php echo $lang->doc->editedDate . (common::checkNotCN() ? ': ' : ':') . $doc->editedDate;?></p>
</button>
</div>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
<script>
$(function()
{
$('.doc-box .btn').on('click', function()
{
if($(this).hasClass('no-priv')) return;
location.href = $(this).data('link');
});
});
</script>
@@ -1,80 +0,0 @@
<?php
/**
* The docrecentupdateblock view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yanyi Cao <caoyanyi@easycorp.ltd>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.block-docrecentupdate .panel-body {padding: 0px 10px 20px;}
.block-docrecentupdate .doc-list {display: flex; flex-wrap: wrap; padding: 0 4px 0 0;}
.block-docrecentupdate .doc-list > .doc-box {border: unset; flex: 0 1 33%; padding: 8px; width: 0;}
.block-docrecentupdate .doc-list > .doc-box > button.btn {padding: 5px 10px; height: 100%; width: 100%; cursor: pointer; white-space: nowrap; text-align: unset; border: 1px solid rgba(227, 228, 233, 0.6)}
.block-docrecentupdate .doc-list > .doc-box > .btn:hover {background: unset;}
.block-docrecentupdate .doc-list > .doc-box > .btn.no-priv {cursor: not-allowed; pointer-events: unset;}
.block-docrecentupdate .doc-list > .doc-box > .btn.no-priv p {pointer-events: none;}
.block-docrecentupdate .doc-list > .doc-box .date-interval {float: right; padding: 8px 0 8px 12px;}
.block-docrecentupdate .doc-list > .doc-box > .btn > h4 {padding-right: 5px;}
.block-docrecentupdate .doc-list > .doc-box .file-icon {margin-right: 2px; margin-bottom: 2px;}
.block-docrecentupdate .doc-list > .doc-box .plug-title {height: 16px; overflow: hidden;}
.block-docrecentupdate.block-sm .doc-list > .doc-box {flex: 0 1 100%;}
</style>
<?php $canView = common::hasPriv('doc', 'view');?>
<div class="panel-body">
<?php if(empty($docList)):?>
<div class='table-empty-tip'><p><span class='text-muted'><?php echo $lang->doc->noDoc;?></p></span></div>
<?php else:?>
<div class="doc-list">
<?php foreach($docList as $doc):?>
<div class="doc-box">
<button class="btn shadow-primary-hover <?php if(!$canView) echo 'no-priv';?>" data-link='<?php echo $this->createLink("doc", "view", "docID=$doc->id");?>'>
<span class='date-interval text-muted'>
<?php
$interval = $doc->editInterval;
$editTip = $lang->doc->todayUpdated;
if($interval->year)
{
$editTip = sprintf($lang->doc->yearsUpdated, $interval->year);
}
elseif($interval->month)
{
$editTip = sprintf($lang->doc->monthsUpdated, $interval->month);
}
elseif($interval->day)
{
$editTip = sprintf($lang->doc->daysUpdated, $interval->day);
}
echo $editTip;
?>
</span>
<h4 class="plug-title" title="<?php echo $doc->title;?>">
<?php
$docType = $doc->type == 'text' ? 'wiki-file' : $doc->type;
echo html::image("static/svg/{$docType}.svg", "class='file-icon'");
?>
<?php echo $doc->title;?>
</h4>
<p class='edit-date text-muted'><?php echo $lang->doc->editedDate . (common::checkNotCN() ? ': ' : ':') . $doc->editedDate;?></p>
</button>
</div>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
<script>
$(function()
{
$('.doc-box .btn').on('click', function()
{
if($(this).hasClass('no-priv')) return;
location.href = $(this).data('link');
});
});
</script>
@@ -1,79 +0,0 @@
<?php
/**
* The statistic view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yanyi Cao <caoyanyi@easycorp.ltd>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.block-docstatistic .flex {display: flex; flex-wrap: nowrap; flex-direction: row; justify-content: space-around; flex: auto;}
.block-docstatistic .flex-column {flex-direction: column; padding-left: 10px;}
.block-docstatistic .statistic {flex: 0 1 32%;}
.block-docstatistic .created {flex: 0 1 48%;}
.block-docstatistic .edited {flex: 0 1 16%; padding-left: 0px;}
.block-docstatistic .divider {border-right: 1px solid #eee; padding-right: 12%;}
.block-docstatistic .panel-body {padding-top: 0;}
.block-docstatistic.block-sm .flex {justify-content: start;}
.block-docstatistic.block-sm .panel-body.flex {flex-direction: column;}
.block-docstatistic.block-sm .divider {border-right: none;}
.block-docstatistic.block-sm .statistic .flex {margin-top: -28px;}
.block-docstatistic.block-sm .tile {padding: 10px 32px 20px;}
.block-docstatistic.block-sm .panel-body {margin-top: 0px; padding: 10px;}
.block-docstatistic.block-sm .flex-column .flex .tile:nth-child(1) {padding-left: 0;}
.block-docstatistic.block-sm .edited {padding-left: 10px;}
.block-docstatistic.block-sm .flex-column {border-bottom: 1px solid #eee;}
.block-docstatistic.block-sm .flex-column:last-child {border-bottom: none;}
</style>
<div class='panel-move-handler'></div>
<div class="panel-body flex">
<div class='flex flex-column statistic'>
<div class='flex'>
<div class="tile">
<div class="tile-amount"><?php echo (int)$statistic->totalDocs;?></div>
<div class="tile-title"><?php echo $lang->doc->allDoc;?></div>
</div>
<div class="tile divider">
<div class="tile-amount"><?php echo (int)$statistic->todayEditedDocs;?></div>
<div class="tile-title"><?php echo $lang->doc->todayEdited;?></div>
</div>
</div>
</div>
<div class='flex flex-column created'>
<div class='flex'>
<?php if(common::hasPriv('doc', 'mySpace')):?>
<a class="tile" href="<?php echo $this->createLink('doc', 'mySpace', 'type=createdBy');?>">
<?php else:?>
<div class="tile">
<?php endif;?>
<div class="tile-amount"><?php echo (int)$statistic->myDocs;?></div>
<div class="tile-title"><?php echo $lang->doc->docCreated;?></div>
<?php if(common::hasPriv('doc', 'mySpace')):?>
</a>
<?php else:?>
</div>
<?php endif;?>
<div class="tile">
<div class="tile-amount"><?php echo (int)$statistic->myDoc->docViews;?></div>
<div class="tile-title"><?php echo $lang->doc->docViews;?></div>
</div>
<div class="tile divider">
<div class="tile-amount"><?php echo (int)$statistic->myDoc->docCollects;?></div>
<div class="tile-title"><?php echo $lang->doc->docCollects;?></div>
</div>
</div>
</div>
<div class='flex flex-column edited'>
<div class='flex'>
<div class="tile">
<div class="tile-amount"><?php echo (int)$statistic->myEditedDocs;?></div>
<div class="tile-title"><?php echo $lang->doc->docEdited;?></div>
</div>
</div>
</div>
</div>
@@ -1,55 +0,0 @@
<?php
/**
* The docviewlistblock view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Shujie Tian <tianshujie@easycorp.ltd>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.block-docviewlist .panel-body {padding-top: 0px;}
.block-docviewlist .doc-list > .doc-title {display: flex; overflow: hidden; text-overflow: clip; white-space: nowrap; padding: 8px 0;}
.block-docviewlist .doc-list > .doc-title > .label-rank {margin-right: 10px; padding-top: 1px; border: unset; color: #2E7FFF; background-color: #D5E5FF;}
.block-docviewlist .doc-list > .doc-title > .label-rank-1 {color: #FC5959; background-color: #FFE2D9;}
.block-docviewlist .doc-list > .doc-title > .label-rank-2 {color: #FF8058; background-color: #FFE2D9;}
.block-docviewlist .doc-list > .doc-title > .label-rank-3 {color: #FF9F46; background-color: #FFECDB;}
.block-docviewlist .doc-list > .doc-title > .doc-name {overflow: hidden; max-width: calc(100% - 110px);}
.block-docviewlist .doc-list > .doc-title > .label-view-count {margin-left: 10px;}
.block-docviewlist .doc-list > .doc-title > .label-view-count > .icon-flame {margin-bottom: 3px;}
.block-docviewlist .doc-list > .doc-title > .label-view-count > .icon-flame.gray {filter: grayscale(100%);}
.block-docviewlist .doc-list > .doc-title > .label-view-count > .view-text {color: #838A9D;}
</style>
<div class="panel-body">
<?php if(empty($docList)):?>
<div class='table-empty-tip'><p><span class='text-muted'><?php echo $lang->doc->noDoc;?></p></span></div>
<?php else:?>
<div class="doc-list table-row">
<?php $rank = 1;?>
<?php foreach($docList as $doc):?>
<div class='doc-title'>
<span class="label-pri label-rank label-rank-<?php echo $rank;?>"><?php echo $rank;?></span>
<?php
if(common::hasPriv('doc', 'view'))
{
echo html::a($this->createLink('doc', 'view', "docID=$doc->id"), $doc->title, '', "title='{$doc->title}' class='doc-name' data-app='{$this->app->tab}'");
}
else
{
echo "<span class='doc-name' title='{$doc->title}'>{$doc->title}</span>";
}
?>
<div class='label-view-count'>
<?php $flameClass = $rank < 4 ? '' : 'gray';?>
<?php echo html::image("static/svg/flame.svg", "class='icon-flame $flameClass'");?>
<span class='view-text'><?php echo sprintf($lang->doc->viewCount, $doc->views);?></span>
</div>
</div>
<?php $rank ++;?>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
@@ -1,34 +0,0 @@
<?php
/**
* The execution overview block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Tingting Dai <daitingting@xirangit.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.block-overview .tile-amount {font-size: 48px; margin-bottom: 10px;}
</style>
<div class="panel-body table-row">
<div class="col-4 text-middle text-center">
<div class="tile">
<div class="tile-title"><?php echo $lang->execution->allExecutions;?></div>
<div class="tile-amount"><?php echo $total;?></div>
<a class="btn btn-primary btn-circle btn-icon-right btn-sm" href="<?php echo $this->createLink('execution', 'all', 'type=all');?>"><?php echo $lang->execution->viewAll;?> <span class="label label-badge label-icon"><i class="icon icon-arrow-right"></i></span></a>
</div>
</div>
<div class="col-8 text-middle">
<ul class="status-bars">
<?php foreach($lang->execution->statusList as $statusKey => $statusName):?>
<li>
<span class="bar" style="height: <?php echo $overviewPercent[$statusKey];?>"><span class="value"><?php echo empty($overview[$statusKey]) ? 0 :html::a($this->createLink('execution', 'all', "type=$statusKey"), $overview[$statusKey]);?></span></span>
<span class="title"><?php echo $statusName;?></span>
</li>
<?php endforeach;?>
</ul>
</div>
</div>
@@ -1,210 +0,0 @@
<style>
.types-line {display: table; padding: 0; margin: 0 auto;}
.types-line > li {display: table-cell;}
.types-line > li > div {position: relative; padding: 18px 5px 5px; text-align: center;}
.types-line > li > div:before {content: ''; display: block; background: #eee; width: 100%; height: 2px; position: absolute; left: 50%; top: 4px;}
.types-line > li:last-child > div:before {display: none;}
.types-line > li > div:after {content: ''; display: block; background: #FFFFFF; box-shadow: 0 2px 4px 0 rgba(170,170,170,0.50), 0 0 5px 0 rgba(0,0,0,0.1); width: 10px; height: 10px; position: absolute; border-radius: 50%; top: 0; left: 50%; margin-left: -2px;}
.types-line > li > div > small {display: block; color: #A6AAB8;}
.types-line > li > div > span {display: block; color: #CBD0DB; font-size: 16px;}
.execution-info {position: relative; height: 65px;}
.execution-info + .execution-info {margin-top: 10px;}
.execution-info .progress {position: absolute; left: 10px; top: 35px; right: 100px;}
.execution-info .progress-info {position: absolute; left: 8px; top: 10px; width: 180px; font-size: 12px;}
.execution-info .type-info {color: #A6AAB8; text-align: center; position: absolute; right: 0; top: 6px; width: 100px;}
html[lang="en"] .execution-info .type-info {color: #A6AAB8; text-align: center; position: absolute; right: 0; top: 6px; width: 110px;}
.execution-info .type-value,
.execution-info .type-label {font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.execution-info .type-value {font-size: 14px;}
.execution-info .type-value > strong {font-size: 20px; color: #3C4353;}
.execution-info .actions {position: absolute; left: 10px; top: 14px;}
.block-statistic .panel-body {padding-top: 0}
.block-statistic .tile {margin-bottom: 30px;}
.block-statistic .tile-title {font-size: 18px; color: #A6AAB8;}
.block-statistic .tile-amount {font-size: 48px; margin-bottom: 10px;}
.block-statistic .col-nav {border-right: 1px solid #EBF2FB; width: 260px; padding: 0;}
.block-statistic .nav-secondary > li {position: relative}
.block-statistic .nav-secondary > li > a {font-size: 14px; color: #838A9D; position: relative; box-shadow: none; padding-left: 20px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; transition: all .2s;}
.block-statistic .nav-secondary > li > a:first-child {padding-right: 36px;}
.block-statistic .nav-secondary > li.active > a:first-child {color: #3C4353; background: transparent; box-shadow: none;}
.block-statistic .nav-secondary > li.active > a:first-child:hover,
.block-statistic .nav-secondary > li.active > a:first-child:focus,
.block-statistic .nav-secondary > li > a:first-child:hover {box-shadow: none; border-radius: 4px 0 0 4px;}
.block-statistic .nav-secondary > li.active > a:first-child:before {content: ' '; display: block; left: -1px; top: 10px; bottom: 10px; width: 4px; background: #006af1; position: absolute;}
.block-statistic .nav-secondary > li > a.btn-view {position: absolute; top: 0; right: 0; bottom: 0; padding: 8px; width: 36px; text-align: center; opacity: 0;}
.block-statistic .nav-secondary > li:hover > a.btn-view {opacity: 1}
.block-statistic .nav-secondary > li.active > a.btn-view {box-shadow: none}
.block-statistic .nav-secondary > li.switch-icon {display: none;}
.block-statistic.block-sm .panel-body {padding-bottom: 10px; position: relative; padding-top: 45px; border-radius: 3px;}
.block-statistic.block-sm .panel-body > .table-row,
.block-statistic.block-sm .panel-body > .table-row > .col {display: block; width: auto;}
.block-statistic.block-sm .panel-body > .table-row > .tab-content {padding: 0; margin: 0 -5px;}
.block-statistic.block-sm .tab-pane > .table-row > .col-5 {width: 125px;}
.block-statistic.block-sm .tab-pane > .table-row > .col-5 > .table-row {padding: 5px 0;}
.block-statistic.block-sm .col-nav {border-left: none; position: absolute; top: 0; left: 15px; right: 15px; background: #f5f5f5;}
.block-statistic.block-sm .nav-secondary {display: table; width: 100%; padding: 0; table-layout: fixed;}
.block-statistic.block-sm .nav-secondary > li {display: none}
.block-statistic.block-sm .nav-secondary > li.switch-icon,
.block-statistic.block-sm .nav-secondary > li.active {display: table-cell; width: 100%; text-align: center;}
.block-statistic.block-sm .nav-secondary > li.active > a:hover {cursor: default; background: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon > a:hover {background: rgba(0,0,0,0.07);}
.block-statistic.block-sm .nav-secondary > li > a {padding: 5px 10px; border-radius: 4px;}
.block-statistic.block-sm .nav-secondary > li > a:before {display: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon {width: 40px;}
.block-statistic.block-sm .nav-secondary > li.active > a:first-child:before {display: none}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view {width: auto; left: 0; right: 0;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view > i {display: none;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view:hover {cursor: pointer; background: rgba(0,0,0,.1)}
.status-count{margin:auto}
.status-count tr:first-child td:last-child{color:#000;font-weight:bold}
.block-statistic .execution-progress {vertical-align: middle;}
</style>
<script>
<?php $blockNavId = 'nav-' . uniqid(); ?>
$(function()
{
var $nav = $('#<?php echo $blockNavId;?>');
$nav.on('click', '.switch-icon', function(e)
{
var isPrev = $(this).is('.prev');
var $activeItem = $nav.children('.active');
var $next = $activeItem[isPrev ? 'prev' : 'next']('li:not(.switch-icon)');
if ($next.length) $next.find('a[data-toggle="tab"]').trigger('click');
else $nav.children('li:not(.switch-icon)')[isPrev ? 'last' : 'first']().find('a[data-toggle="tab"]').trigger('click');
e.preventDefault();
});
var $executionLi = $('#activeExecution');
if($executionLi.length)
{
var executionLi = $executionLi[0];
$(".col ul.nav").animate({scrollTop: executionLi.offsetTop}, "slow");
}
});
</script>
<div class="panel-body">
<div class="table-row">
<?php if(empty($executions)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->block->noData;?></span></p>
</div>
<?php else:?>
<div class="col col-nav">
<ul class="nav nav-stacked nav-secondary scrollbar-hover" id='<?php echo $blockNavId;?>'>
<li class='switch-icon prev'><a><i class='icon icon-arrow-left'></i></a></li>
<?php $selected = key($executions);?>
<?php $selected = !isset($executions[$selected]) ? key($executions) : $selected;?>
<?php foreach($executions as $execution):?>
<li <?php if($execution->id == $selected) echo "class='active' id='activeExecution'";?> executionID='<?php echo $execution->id;?>'>
<a href="###" data-target="#tab3Content<?php echo $blockNavId . $execution->id;?>" data-toggle="tab" title='<?php echo $execution->name;?>'><?php echo $execution->name;?></a>
<?php echo html::a(helper::createLink('execution', 'task', "executionID=$execution->id"), "<i class='icon-arrow-right text-primary'></i>", '', "class='btn-view' title={$lang->execution->task}");?>
</li>
<?php endforeach;?>
<li class='switch-icon next'><a><i class='icon icon-arrow-right'></i></a></li>
</ul>
</div>
<div class="col tab-content">
<?php foreach($executions as $execution):?>
<div class="tab-pane fade<?php if($execution->id == $selected) echo ' active in';?>" id="tab3Content<?php echo $blockNavId . $execution->id;?>">
<div class="table-row">
<div class="col-5 text-middle text-center">
<div class="progress-pie inline-block space" data-value="<?php echo $execution->progress;?>" data-doughnut-size="84">
<canvas width="120" height="120"></canvas>
<div class="progress-info">
<small><?php echo $lang->task->statusList['done'];?></small>
<strong><?php echo $execution->progress;?><small><?php echo $lang->percent;?></small></strong>
</div>
</div>
<div class="table-row text-center small text-muted with-padding">
<div class="col-4 text-bottom">
<div><?php echo $lang->execution->totalEstimate;?></div>
<div><?php echo (float)$execution->totalEstimate;?> <span class="muted"><?php echo $lang->task->hour;?></span></div>
</div>
<div class="col-4">
<span class="label label-dot label-primary"></span>
<div><?php echo $lang->execution->totalConsumed;?></div>
<div><?php echo (float)$execution->totalConsumed;?> <span class="muted"><?php echo $lang->task->hour;?></span></div>
</div>
<div class="col-4">
<span class="label label-dot label-pale"></span>
<div><?php echo $lang->execution->totalLeft;?></div>
<div><?php echo (float)$execution->totalLeft;?> <span class="muted"><?php echo $lang->task->hour;?></span></div>
</div>
</div>
</div>
<div class="col-7 execution-progress">
<div class="execution-info">
<div class="progress-info"><i class="icon icon-check-circle text-success icon-sm"></i> <span class="text-muted"><?php echo $lang->task->yesterdayFinished;?></span> <strong><?php echo $execution->yesterdayFinished;?></strong></div>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="<?php echo $execution->taskProgress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $execution->taskProgress;?>%">
</div>
</div>
<div class="type-info">
<div class="type-label">
<table class='status-count'>
<tr>
<td class='text-right'><?php echo $lang->task->allTasks;?> :</td>
<td class='text-left'><?php echo empty($execution->totalTasks) ? 0 : html::a($this->createLink('execution', 'task', "executionID={$execution->id}&status=all"), $execution->totalTasks);?></td>
</tr>
<tr>
<td class='text-right'><?php echo $lang->task->noFinished;?> :</td>
<td class='text-left'><?php echo empty($execution->undoneTasks) ? 0 : html::a($this->createLink('execution', 'task', "executionID={$execution->id}&status=undone"), $execution->undoneTasks);?></td>
</tr>
</table>
</div>
</div>
</div>
<?php if($execution->lifetime != 'ops' and !in_array($execution->attribute, array('request', 'review'))):?>
<div class="execution-info">
<div class="progress-info"><i class="icon icon-check-circle text-success icon-sm"></i> <span class="text-muted"><?php echo $lang->story->released;?></span> <strong><?php echo $execution->releasedStories;?></strong></div>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="<?php echo $execution->storyProgress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $execution->storyProgress;?>%"></div>
</div>
<div class="type-info">
<div class="type-label">
<table class='status-count'>
<tr>
<td class='text-right'><?php echo $lang->story->total;?> :</td>
<td class='text-left'><?php echo empty($execution->totalStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&storyType=story&orderBy=order_desc&type=all"), $execution->totalStories);?></td>
</tr>
<tr>
<td class='text-right'><?php echo $lang->story->unclosed;?> :</td>
<td class='text-left'><?php echo empty($execution->unclosedStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&storyType=story&orderBy=order_desc&type=unclosed"), $execution->unclosedStories);?></td>
</tr>
</table>
</div>
</div>
</div>
<?php if($execution->attribute != 'design'):?>
<div class="execution-info">
<div class="progress-info"><i class="icon icon-check-circle text-success icon-sm"></i> <span class="text-muted"><?php echo $lang->bug->yesterdayResolved;?></span> <strong><?php echo $execution->yesterdayResolved;?></strong></div>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="<?php echo $execution->bugProgress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $execution->bugProgress;?>%">
</div>
</div>
<div class="type-info">
<div class="type-label">
<table class='status-count'>
<tr>
<td class='text-right'><?php echo $lang->bug->allBugs;?> :</td>
<td class='text-left'><?php echo empty($execution->totalBugs) ? 0 : html::a($this->createLink('execution', 'bug', "executionID={$execution->id}&productID=0&branch=0&orderBy=status,id_desc&build=0&type=all"), $execution->totalBugs);?></td>
</tr>
<tr>
<td class='text-right'><?php echo $lang->bug->unResolved;?> :</td>
<td class='text-left'><?php echo empty($execution->activeBugs) ? 0 : html::a($this->createLink('execution', 'bug', "executionID={$execution->id}&productID=0&branch=0&orderBy=status,id_desc&build=0&type=unresolved"), $execution->activeBugs);?></td>
</tr>
</table>
</div>
</div>
</div>
<?php endif;?>
<?php endif;?>
</div>
</div>
</div>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
</div>
-33
View File
@@ -1,33 +0,0 @@
<div class='flowchart'>
<?php foreach ($lang->block->flowchart as $flowName => $flow):?>
<?php $idx = 0; ?>
<div class='row flow-chart-row row-<?php echo $flowName?>'>
<?php foreach ($flow as $flowItem):?>
<div class='flow-item flow-item-<?php echo $idx ++ ?>'><div title='<?php echo $flowItem ?>'><div class='flow-item-display'><?php echo $flowItem ?></div></div></div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
<style>
.flow-chart-row {display: flex; gap: 5px; margin-bottom: 9px; width: 100%;}
.flow-item {background: url('static/svg/arrow-blue.svg') center no-repeat; background-size: cover; text-align: center; margin: 0; padding-right: 15px; flex:0 1 15%; position: relative; padding: 5px 0 5px 8px; white-space:nowrap; overflow: hidden; color: #3c4353; display: flex; align-items: center; text-align: center; padding-right: 10px;}
.ie-8 .flow-item > div:before {display: none}
.flow-item > div:before {border-left-color: #fff; z-index: 1}
.flow-item > div > div {display: flow-root; overflow: hidden;}
.ie-8 .flow-item > div {margin-right: 10px}
.flow-item-0 > div {color: #FFF; font-weight: 400;}
.flow-item-0 > div:before {display: none}
.flow-item-1,
.flow-item-2,
.flow-item-3,
.flow-item-4,
.flow-item-5 {background-image: url('static/svg/arrow-white.svg'); }
.block-sm .flow-item {padding-right: 5px}
.block-sm .flow-item > div:before, .block-sm .flow-item > div:after {border-width: 15px 0 15px 6px;}
.block-sm .row-3 .flow-item-1, .block-sm .row-3 .flow-item-3 {width: 25%}
.block-sm .flow-item > div:after {right: -6px;}
.flow-item-display {overflow: hidden; white-space:nowrap; text-overflow: clip; font-size: 12px;}
</style>
-49
View File
@@ -1,49 +0,0 @@
<?php if(empty($issues)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-issues .c-id {width: 50px;}
.block-issues .c-pri {width: 50px;text-align: center;}
.block-issues .c-category {width: 90px;}
.block-issues .c-identifiedDate {width: 100px;}
.block-issues .c-severity {width: 80px;}
.block-issues .c-status {width: 90px;}
.block-issues.block-sm .c-status {text-align: center;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-fixed table-fixed-head table-hover tablesorter block-issues <?php if(!$longBlock) echo 'block-sm'?>'>
<thead>
<tr>
<th class='c-id'><?php echo $lang->idAB?></th>
<th class='c-name'><?php echo $lang->issue->title?></th>
<?php if($longBlock):?>
<th class='c-pri'><?php echo $lang->priAB?></th>
<th class='c-category'><?php echo $lang->issue->severity;?></th>
<th class='c-identifiedDate'><?php echo $lang->issue->deadline;?></th>
<?php endif;?>
<th class='c-status'><?php echo $lang->issue->status;?></th>
</tr>
</thead>
<tbody>
<?php foreach($issues as $issue):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
?>
<tr>
<td class='c-id-xs'><?php echo sprintf('%03d', $issue->id);?></td>
<td class='c-name' title='<?php echo $issue->title?>'><?php echo html::a($this->createLink('issue', 'view', "issueID=$issue->id", '', '', $issue->project), $issue->title)?></td>
<?php if($longBlock):?>
<td class='c-pri'><span class="label-pri label-pri-<?php echo $issue->pri;?>"><?php echo zget($lang->issue->priList, $issue->pri)?></span></td>
<td class='c-severity severity-issue severity-<?php echo $issue->severity;?>'><?php echo zget($lang->issue->severityList, $issue->severity)?></td>
<td class='c-deadline'><?php echo $issue->deadline == '0000-00-00' ? '' : $issue->deadline;?></td>
<?php endif;?>
<?php $status = $this->processStatus('issue', $issue);?>
<td class='c-status' title='<?php echo $status;?>'>
<span class="status-issue status-<?php echo $issue->status?>"><?php echo $status;?></span>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-16
View File
@@ -1,16 +0,0 @@
<?php
/**
* The main view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link http://www.zentao.pms
*/
$viewDir = dirname(__FILE__);
$extFire = $app->getExtensionRoot() . $config->edition . "/block/ext/view/{$code}block.html.php";
$file2Include = file_exists($extFire) ? $extFire : "{$viewDir}/{$code}block.html.php";
include $file2Include;
?>
-49
View File
@@ -1,49 +0,0 @@
<?php
/**
* The plan block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(empty($plans)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-plan'>
<thead>
<tr>
<?php if($longBlock):?>
<th class='text-center w-50px'><?php echo $lang->idAB?></th>
<th><?php echo $lang->productplan->product;?></th>
<?php endif;?>
<th><?php echo $lang->productplan->title;?></th>
<th class='text-center w-100px'><?php echo $lang->productplan->begin;?></th>
<th class='text-center w-100px'><?php echo $lang->productplan->end;?></th>
</tr>
</thead>
<tbody>
<?php foreach($plans as $plan):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
$planViewLink = $this->createLink('productplan', 'view', "planID={$plan->id}");
$productViewLink = $this->createLink('product', 'view', "productID={$plan->product}");
?>
<tr <?php echo $appid?>>
<?php if($longBlock):?>
<td class='text-center'><?php echo $plan->id;?></td>
<td title='<?php echo $plan->productName?>'><?php echo html::a($productViewLink, $plan->productName);?></td>
<?php endif;?>
<td title='<?php echo $plan->title?>'><?php echo html::a($planViewLink, $plan->title);?></td>
<td class='text-center'><?php echo $plan->begin?></td>
<td class='text-center'><?php echo $plan->end?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-242
View File
@@ -1,242 +0,0 @@
<style>
.preference .table-form > tbody > tr > th {font-weight: 400; color: #0B0F18;}
.preference .chosen-container-single .chosen-single > span {color: #313C52;}
.tip {margin-top: 10px;}
.preference > .preference-border {
display: flex;
padding: 5px 16px;
border: 1px solid #EDEEF2;
}
.preference-border > .preference-img {
flex: 0 0 80px;
}
.preference.picker-option-active >.preference-border {
border: 2px solid #2E7FFF;
}
.preference.picker-option-active {
background: unset!important;
}
.preference.picker-option-active > .preference-border {
background: rgba(230,240,255, 0.4)!important;
}
.picker-option.picker-option-selected.preference {
background: unset!important;
}
.picker-option.picker-option-selected.preference > .preference-border {
background: rgba(230,240,255, 0.4)!important;
}
.preference-text {
display: flex;
flex-direction: column;
justify-content: space-around;
padding-left: 5px;
}
.preference-text > .title {
color: #0B0F18;
font-size: 13px;
}
.preference-text > .context {
color: #838A9D;
font-size: 12px;
overflow: hidden;
white-space: normal;
}
.picker-option.picker-option-active,
.picker-single .picker-option.picker-option-selected.picker-option-active.option-ursr,
.picker-option.picker-option-selected.option-ursr {
background: unset!important;
color: unset;
}
.picker-option.picker-option-active > .border,
.picker-single .picker-option.picker-option-selected.picker-option-active.option-ursr > .border,
.picker-single .picker-option.picker-option-selected.picker-option-active.option-ursr > .border {
background: rgba(230,240,255, 0.4)!important;
}
#pickerDropMenu-pk_URSR > .picker-option-list {
display: flex;
flex-wrap: wrap;
}
.option-ursr {
flex: 1 1 50%;
}
.option-ursr > .border {
height: 46px;
display: flex;
padding: 5px 16px;
border: 1px solid #EDEEF2;
align-items: center;
}
.option-ursr > .border > .value {
margin-right: 20px;
width: 20px;
height: 20px;
border-radius: 100%;
background: rgba(230,240,255, 0.4);
}
.option-ursr > .border > .value > p {
line-height: 20px;
text-align: center;
}
</style>
<div class='preference'>
<form method='post' target='hiddenwin' action='<?php echo $this->createLink('my', 'preference', "showTip=false")?>'>
<table align='center' class='table table-form w-320px'>
<tr>
<th class='w-120px'><?php echo $lang->my->storyConcept;?></th>
<td><?php echo html::select('URSR', $URSRList, $URSR, "class='form-control picker URSR'");?></td>
</tr>
<?php if(in_array($config->systemMode, array('ALM', 'PLM'))):?>
<tr>
<th><?php echo $lang->my->programLink;?></th>
<td><?php echo html::select('programLink', $lang->my->programLinkList, $programLink, "class='form-control picker programLink'");?></td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->my->productLink;?></th>
<td><?php echo html::select('productLink', $lang->my->productLinkList, $productLink, "class='form-control picker productLink'");?></td>
</tr>
<?php if($config->vision != 'or'):?>
<tr>
<th><?php echo $lang->my->projectLink;?></th>
<td><?php echo html::select('projectLink', $lang->my->projectLinkList, $projectLink, "class='form-control picker projectLink'");?></td>
</tr>
<tr>
<th><?php echo $lang->my->executionLink;?></th>
<td><?php echo html::select('executionLink', $lang->my->executionLinkList, $executionLink, "class='form-control picker executionLink'");?></td>
</tr>
<?php endif;?>
<tr>
<td colspan='2' class='text-center form-actions'>
<?php echo html::submitButton();?>
</td>
</tr>
</table>
</form>
</div>
<script>
/**
* Init preference.
*
* @access public
* @return void
*/
function initPreference() {
var objPngSrc = {
'program-browse': 'list',
'program-project': 'list-recent',
'program-kanban': 'kanban',
'product-index': 'panel-recent-browse',
'product-all': 'list',
'product-dashboard': 'panel',
'product-browse': 'list-recent',
'product-kanban': 'kanban',
'project-browse': 'list',
'project-execution': 'list-recent',
'project-index': 'panel-recent-browse',
'project-kanban': 'kanban',
'execution-all': 'list',
'execution-task': 'list-recent',
'execution-executionkanban': 'kanban',
}
/**
* Render program option.
*
* @param object $option
* @param object $b
* @access public
* @return object
*/
function optionRenderProgram($option, b)
{
/* transform , to , then split to fit lang */
var textArr = b.text.split('/');
if (!$option.hasClass('preference'))
{
$option.empty();
$option.addClass('preference');
$option.attr("title", textArr[0]);
$option.prepend('<div class="preference-border"><div class="preference-img"><img src="theme/default/images/guide/' + objPngSrc[b.value] + '.png"></div><div class="preference-text"><div class="title">' + textArr[0] + '</div><div class="context">' + textArr[1] + '</div></div></div>');
}
return $option;
}
/**
* Render program text.
*
* @param object $text
* @param object $b
* @access public
* @return object
*/
function textRenderProgram($text, b)
{
$text.empty();
$text.addClass('preference-selection');
$text.prepend('<span>' + b.split('/')[0] + '</span>')
return $text;
}
/**
* Render URSR option.
*
* @param object $option
* @param object $b
* @access public
* @return object
*/
function optionRenderURSR($option, b)
{
if (!$option.hasClass('option-ursr'))
{
$option.addClass('option-ursr');
$option.parent().addClass('list-ursr');
$option.empty();
$option.prepend('<div class="border shadow-primary-hover"><div class="value"><p>' + (b.$_index + 1) + '</p></div><div class="context">' + b.text + '</div></div>');
}
return $option;
}
$('.programLink').picker({
optionRender: optionRenderProgram,
selectionTextRender: textRenderProgram
});
$('.productLink').picker({
optionRender: optionRenderProgram,
selectionTextRender: textRenderProgram
});
$('.projectLink').picker({
optionRender: optionRenderProgram,
selectionTextRender: textRenderProgram
});
$('.executionLink').picker({
optionRender: optionRenderProgram,
selectionTextRender: textRenderProgram
});
$('.URSR').picker({
optionRender: optionRenderURSR
});
$(document).on('mousemove', 'a.picker-option', function(e)
{
$('.picker-option.text-primary').removeClass('text-primary');
$(this).addClass('text-primary');
});
}
$(function()
{
initPreference();
})
</script>
-208
View File
@@ -1,208 +0,0 @@
<style>
.block-productdoc .nav-stacked {overflow:auto; height:220px; max-height:220px; }
.block-productdoc .panel-heading {border-bottom:1px solid #ddd;}
.block-productdoc .panel-body {padding-top: 0; height:240px; padding-right:0px; padding-bottom:0px;}
.block-productdoc .tab-content {padding-right:0px;}
.block-productdoc .tab-pane {max-height:220px; overflow:auto;}
.block-productdoc table.tablesorter th{border-bottom:0px !important;}
.block-productdoc .tile {margin-bottom: 30px;}
.block-productdoc .tile-title {font-size: 18px; color: #A6AAB8;}
.block-productdoc .tile-amount {font-size: 48px; margin-bottom: 10px;}
.block-productdoc .col-nav {border-right: 1px solid #EBF2FB; width: 210px; padding: 5px 0 0 0; }
.block-productdoc .nav-secondary > li {position: relative;}
.block-productdoc .nav-secondary > li > a {font-size: 14px; color: #838A9D; position: relative; box-shadow: none; padding-left: 20px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; transition: all .2s;}
.block-productdoc .nav-secondary > li > a:first-child {padding-right: 36px;}
.block-productdoc .nav-secondary > li.active > a:first-child {color: #3C4353; box-shadow: none;}
.block-productdoc .nav-secondary > li.active > a:first-child:hover,
.block-productdoc .nav-secondary > li.active > a:first-child:focus,
.block-productdoc .nav-secondary > li > a:first-child:hover {box-shadow: none; border-radius: 4px 0 0 4px;}
.block-productdoc .nav-secondary > li.active > a:first-child:before {content: ' '; display: block; left: -1px; top: 10px; bottom: 10px; width: 4px; background: #006af1; position: absolute;}
.block-productdoc .nav-secondary > li > a.btn-view {position: absolute; top: 0; right: 0; bottom: 0; padding: 8px; width: 36px; text-align: center; opacity: 0;}
.block-productdoc .nav-secondary > li:hover > a.btn-view {opacity: 1;}
.block-productdoc .nav-secondary > li.active > a.btn-view {box-shadow: none;}
.block-productdoc .nav-secondary > li.switch-icon {display: none;}
.block-productdoc.block-sm .nav-stacked {height:auto;}
.block-productdoc.block-sm .panel-body {position: relative; border-radius: 3px; height:275px; padding: 45px 8px 10px 8px}
.block-productdoc.block-sm .panel-body > .table-row,
.block-productdoc.block-sm .panel-body > .table-row > .col {display: block; width: auto;}
.block-productdoc.block-sm .panel-body > .table-row > .tab-content {padding: 0; margin: 0 -5px;}
.block-productdoc.block-sm .tab-pane > .table-row > .col-5 {width: 125px;}
.block-productdoc.block-sm .tab-pane > .table-row > .col-5 > .table-row {padding: 5px 0;}
.block-productdoc.block-sm .col-nav {border-left: none; position: absolute; top: 0; left: 15px; right: 15px; background: #f5f5f5; padding: 0px; margin-top:5px;}
.block-productdoc.block-sm .nav-secondary {display: table; width: 100%; padding: 0; table-layout: fixed;}
.block-productdoc.block-sm .nav-secondary > li {display: none;}
.block-productdoc.block-sm .nav-secondary > li.switch-icon,
.block-productdoc.block-sm .nav-secondary > li.active {display: table-cell; width: 100%; text-align: center;}
.block-productdoc.block-sm .nav-secondary > li.active > a:hover {cursor: default; background: none;}
.block-productdoc.block-sm .nav-secondary > li.switch-icon > a:hover {background: rgba(0, 0, 0, 0.07);}
.block-productdoc.block-sm .nav-secondary > li > a {padding: 5px 10px; border-radius: 4px;}
.block-productdoc.block-sm .nav-secondary > li > a:before {display: none;}
.block-productdoc.block-sm .nav-secondary > li.switch-icon {width: 40px;}
.block-productdoc.block-sm .nav-secondary > li.active > a:first-child:before {display: none}
.block-productdoc.block-sm .nav-secondary > li.active > a.btn-view {width: auto; left: 0; right: 0;}
.block-productdoc.block-sm .nav-secondary > li.active > a.btn-view > i {display: none;}
.block-productdoc.block-sm .nav-secondary > li.active > a.btn-view:hover {cursor: pointer; background: rgba(0,0,0,.1);}
.block-productdoc .data {width: 40%; text-align: left; padding: 10px 0px; font-size: 14px; font-weight: 700;}
.block-productdoc .dataTitle {width: 60%; text-align: right; padding: 10px 0px; font-size: 14px;}
.block-productdoc .executionName {padding: 2px 10px; font-size: 14px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;}
.block-productdoc .lastIteration {padding-top: 6px;}
.forty-percent {width: 40%;}
html[lang="de"] .block-productdoc .c-user{width: 105px;}
.block-productdoc #productType {position: absolute;top: 8px;left: 120px;}
.block-productdoc #productType .btn {border:0px;}
.block-productdoc #productType .btn:hover {background-color:#f5f5f5;}
.block-productdoc .table .c-title > .doc-title {display: inline-block; overflow: hidden; background: transparent; padding-right:0px;}
.block-productdoc .table .c-title > span.doc-title {line-height: 0; vertical-align: inherit;}
.block-productdoc .table .c-title[data-status=draft] > .doc-title {max-width: calc(100% - 35px);}
.block-productdoc .table .c-title > .draft {background-color:rgba(129, 102, 238, 0.12); color:#8166EE;}
</style>
<?php $blockNavId = 'nav-' . uniqid(); ?>
<?php js::set('emptyProducts', empty($products));?>
<?php js::set('emptyInvolveds', empty($involveds));?>
<script>
$(function()
{
if($('.block-productdoc<?php echo "#block{$block->id}";?> #productType').length > 1);
{
count = $('.block-productdoc<?php echo "#block{$block->id}";?> #productType').length;
$('.block-productdoc<?php echo "#block{$block->id}";?> #productType').each(function()
{
if(count == 1) return;
$(this).remove();
count --;
})
}
});
function switch<?php echo "block{$block->id}";?>Product(obj)
{
var $this = $(obj);
var $nav = $this.closest('.nav');
var isPrev = $this.is('.prev');
var $activeItem = $nav.children('.active');
var $next = $activeItem[isPrev ? 'prev' : 'next']('li:not(.switch-icon)');
if($next.length > 0) $next.find('a[data-toggle="tab"]').trigger('click');
if($next.length == 0) $nav.children('li:not(.switch-icon)')[isPrev ? 'last' : 'first']().find('a[data-toggle="tab"]').trigger('click');
return false;
}
function change<?php echo "block{$block->id}";?>ProductType(type)
{
var hiddenData = type == 'all' ? emptyProducts : emptyInvolveds;
if(hiddenData)
{
$('.block-productdoc<?php echo "#block{$block->id}";?> .dataBlock').addClass('hidden');
$('.block-productdoc<?php echo "#block{$block->id}";?> .block-statistic > .table-empty-tip').removeClass('hidden');
}
else
{
$('.block-productdoc<?php echo "#block{$block->id}";?> .dataBlock').removeClass('hidden');
$('.block-productdoc<?php echo "#block{$block->id}";?> .block-statistic > .table-empty-tip').addClass('hidden');
}
$('.block-productdoc<?php echo "#block{$block->id}";?> .nav.products').toggleClass('hidden', type != 'all');
$('.block-productdoc<?php echo "#block{$block->id}";?> .nav.involveds').toggleClass('hidden', type != 'involved');
$('.block-productdoc<?php echo "#block{$block->id}";?> #productType .btn').html($('.block-productdoc<?php echo "#block{$block->id}";?> #productType [data-type=' + type + ']').html() + " <span class='caret'></span>");
var name = type == 'all' ? '.block-productdoc<?php echo "#block{$block->id}";?> .products' : '.block-productdoc<?php echo "#block{$block->id}";?> .involveds';
var $obj = $(name + ' li.active').length > 0 ? $(name + ' .active:first').find('a') : $(name + ' li:not(.switch-icon):first').find('a');
$(name + ' li').removeClass('active');
$obj.closest('li').addClass('active');
$('.block-productdoc<?php echo "#block{$block->id}";?> .tab-pane').removeClass('active').removeClass('in');
$('.block-productdoc<?php echo "#block{$block->id}";?> .tab-pane' + $obj.data('target')).addClass('active').addClass('in');
$('.block-productdoc<?php echo "#block{$block->id}";?> #productType .dropdown-menu li').removeClass('active');
$('.block-productdoc<?php echo "#block{$block->id}";?> #productType .dropdown-menu li a[data-type=' + type + ']').closest('li').addClass('active');
}
</script>
<div class="dropdown" id='productType'>
<button class="btn" type="button" data-toggle="dropdown"><?php echo $lang->product->involved;?> <span class="caret"></span></button>
<ul class="dropdown-menu">
<li class='active'><a href="javascript:change<?php echo "block{$block->id}";?>ProductType('involved')" data-type='involved'><?php echo $lang->product->involved;?></a></li>
<li><a href="javascript:change<?php echo "block{$block->id}";?>ProductType('all')" data-type='all'><?php echo $lang->product->all;?></a></li>
</ul>
</div>
<?php $hiddenBlock = empty($involveds) ? 'hidden' : '';?>
<?php $hiddenEmpty = empty($involveds) ? '' : 'hidden';?>
<div class="panel-body">
<div class="table-row block-statistic">
<div class="table-empty-tip <?php echo $hiddenEmpty;?>">
<p><span class="text-muted"><?php echo $lang->block->emptyTip;?></span></p>
</div>
<div class="col col-nav dataBlock <?php echo $hiddenBlock;?>">
<ul class="nav nav-stacked nav-secondary scrollbar-hover involveds">
<li class='switch-icon prev'><a href='###' onclick='switch<?php echo "block{$block->id}";?>Product(this)'><i class='icon icon-arrow-left'></i></a></li>
<?php $selected = key($involveds);?>
<?php foreach($involveds as $product):?>
<li <?php if($product->id == $selected) echo "class='active' id='activeProduct'";?> productid='<?php echo $product->id;?>'>
<a href="###" title="<?php echo $product->name?>" data-target='<?php echo "#tab3{$blockNavId}Content{$product->id}";?>' data-toggle="tab"><?php echo $product->name;?></a>
<?php echo html::a(helper::createLink('doc', 'productSpace', "productID=$product->id"), "<i class='icon-arrow-right text-primary'></i>", '', "class='btn-view'");?>
</li>
<?php endforeach;?>
<li class='switch-icon next'><a href='###' onclick='switch<?php echo "block{$block->id}";?>Product(this)'><i class='icon icon-arrow-right'></i></a></li>
</ul>
<ul class="nav nav-stacked nav-secondary scrollbar-hover products hidden">
<li class='switch-icon prev'><a href='###' onclick='switch<?php echo "block{$block->id}";?>Product(this)'><i class='icon icon-arrow-left'></i></a></li>
<?php foreach($products as $product):?>
<li productid='<?php echo $product->id;?>'>
<a href="###" title="<?php echo $product->name?>" data-target='<?php echo "#tab3{$blockNavId}Content{$product->id}";?>' data-toggle="tab"><?php echo $product->name;?></a>
<?php echo html::a(helper::createLink('doc', 'productSpace', "productID=$product->id"), "<i class='icon-arrow-right text-primary'></i>", '', "class='btn-view'");?>
</li>
<?php endforeach;?>
<li class='switch-icon next'><a href='###' onclick='switch<?php echo "block{$block->id}";?>Product(this)'><i class='icon icon-arrow-right'></i></a></li>
</ul>
</div>
<div class="col tab-content dataBlock <?php echo $hiddenBlock;?>">
<?php foreach($products as $product):?>
<div class="tab-pane fade<?php if($product->id == $selected) echo ' active in';?>" id='<?php echo "tab3{$blockNavId}Content{$product->id}";?>'>
<?php if(isset($docGroup[$product->id])):?>
<div class="table-row">
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter'>
<thead>
<tr>
<th class='c-name'><?php echo $lang->doc->title?></th>
<th class='c-user'><?php echo $lang->doc->addedBy?></th>
<th class='c-date'><?php echo $lang->doc->addedDate?></th>
<th class='c-date'><?php echo $lang->doc->editedDate?></th>
</tr>
</thead>
<tbody>
<?php foreach($docGroup[$product->id] as $doc):?>
<tr>
<td class='c-title' data-status='<?php echo $doc->status?>'>
<?php
$docType = zget($config->doc->iconList, $doc->type);
$icon = html::image("static/svg/{$docType}.svg", "class='file-icon'");
if(common::hasPriv('doc', 'view'))
{
echo html::a($this->createLink('doc', 'view', "docID=$doc->id"), $icon . $doc->title, '', "title='{$doc->title}' class='doc-title' data-app='{$this->app->tab}'");
}
else
{
echo "<span class='doc-title' title='{$doc->title}'>$icon {$doc->title}</span>";
}
if($doc->status == 'draft') echo "<span class='label label-badge draft'>{$lang->doc->draft}</span>";
?>
</td>
<td class='c-user'><?php echo zget($users, $doc->addedBy);?></td>
<td class='c-date'><?php echo substr($doc->addedDate, 0, 10);?></td>
<td class='c-date'><?php echo substr($doc->editedDate, 0, 10);?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php else:?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->block->emptyTip;?></span></p>
</div>
<?php endif;?>
</div>
<?php endforeach;?>
</div>
</div>
</div>
@@ -1,37 +0,0 @@
<?php
/**
* The overview block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Gang Liu <liugang@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.block-overview .status-list li + li {margin-top: 5px;}
.block-overview .status-list li > strong {font-size: 16px;}
.progress-pie {position: relative;}
.progress-pie .progress-info {position: absolute; width: 100%; height: 100%; left: 0; top: 0; text-align: center; padding-top: 25px;}
.progress-pie .progress-info > small {display: block; color: #A6AAB8; line-height: 14px;}
.progress-pie .progress-info > strong {display: block; font-size: 32px; line-height: 40px;}
</style>
<div class="panel-body table-row">
<div class="col-6 text-middle text-center">
<div class="progress-pie inline-block" data-value="<?php echo $normalPercent;?>" data-doughnut-size="80">
<canvas width="100" height="100" style="width: 100px; height: 100px;"></canvas>
<div class="progress-info">
<small><?php echo $lang->product->all;?></small>
<strong class='text-primary'><?php echo empty($total) ? 0 : html::a($this->createLink('product', 'all', "browseType=all"), $total, '', "class='text-primary'");?></strong>
</div>
</div>
</div>
<div class="col-6 text-middle">
<ul class="list-unstyled status-list">
<li><span class="status-product status-unclosed"><?php echo $lang->product->statusList['normal'];?></span> &nbsp; <strong class='text-primary'><?php echo empty($normal) ? 0 : html::a($this->createLink('product', 'all', "browseType=noclosed"), $normal, '', "class='text-primary'");?></strong></li>
<li><span class="status-product status-closed"><?php echo $lang->product->statusList['closed'];?></span> &nbsp; <strong class='text-primary'><?php echo empty($closed) ? 0 : html::a($this->createLink('product', 'all', "browseType=closed"), $closed, '', "class='text-primary'");?></strong></li>
</ul>
</div>
</div>
@@ -1,219 +0,0 @@
<?php
/**
* The report block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Gang Liu <liugang@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.types-line {display: table; padding: 0; margin: 0 auto;}
.types-line > li {display: table-cell;}
.types-line > li > div {position: relative; padding: 18px 5px 5px; text-align: center;}
.types-line > li > div:before {content: ''; display: block; background: #eee; width: 100%; height: 2px; position: absolute; left: 50%; top: 4px;}
.types-line > li:last-child > div:before {display: none;}
.types-line > li > div:after {content: ''; display: block; background: #FFFFFF; box-shadow: 0 2px 4px 0 rgba(170,170,170,0.50), 0 0 5px 0 rgba(0,0,0,0.1); width: 10px; height: 10px; position: absolute; border-radius: 50%; top: 0; left: 50%; margin-left: -2px;}
.types-line > li > div > small {display: block; color: #A6AAB8;}
.types-line > li > div > span {display: block; color: #CBD0DB; font-size: 16px;}
.product-info {position: relative; height: 65px;}
.product-info + .product-info {margin-top: 10px;}
.product-info .progress {position: absolute; left: 10px; top: 35px; right: 90px;}
.product-info .progress-info {position: absolute; left: 5px; top: 10px; width: 180px; font-size: 12px;}
.product-info .type-info {color: #A6AAB8; text-align: center; position: absolute; right: 0; top: 6px; width: 100px;}
html[lang="en"] .product-info .type-info {color: #A6AAB8; text-align: center; position: absolute; right: 0; top: 6px; width: 75px;}
.product-info .type-value,
.product-info .type-label {font-size: 12px; overflow: visible; text-overflow: ellipsis; white-space: nowrap;}
.product-info .type-value {font-size: 14px;}
.product-info .type-value > strong {font-size: 20px; color: #3C4353;}
.product-info .actions {position: absolute; right: 90px; top: 14px;}
.block-statistic .panel-body {padding-top: 0;}
.block-statistic .panel-body {padding-top: 0;}
.block-statistic .tile {margin-bottom: 30px;}
.block-statistic .tile-title {font-size: 18px; margin-bottom: 10px; color: #313C52;}
.block-statistic .tile-amount {font-size: 44px; margin-bottom: 10px;}
.block-statistic .col-nav {border-right: 1px solid #EBF2FB; width: 220px; padding: 0;}
.block-statistic .nav-secondary > li {position: relative}
.block-statistic .nav-secondary > li:hover {background: #f5f5f5;}
.block-statistic .nav-secondary > li > a {font-size: 14px; color: #838A9D; position: relative; box-shadow: none; padding-left: 20px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; transition: all .2s;}
.block-statistic .nav-secondary > li > a:first-child {padding-right: 36px;}
.block-statistic .nav-secondary > li.active > a:first-child {color: #3C4353; background: transparent; box-shadow: none;}
.block-statistic .nav-secondary > li.active > a:first-child:hover,
.block-statistic .nav-secondary > li.active > a:first-child:focus,
.block-statistic .nav-secondary > li > a:first-child:hover {box-shadow: none; border-radius: 4px 0 0 4px;}
.block-statistic .nav-secondary > li.active > a:first-child:before {content: ' '; display: block; left: -1px; top: 10px; bottom: 10px; width: 4px; background: #006af1; position: absolute;}
.block-statistic .nav-secondary > li > a.btn-view {position: absolute; top: 0; right: 0; bottom: 0; padding: 8px; width: 36px; text-align: center; opacity: 0; background: rgba(0,0,0,.05)}
.block-statistic .nav-secondary > li.active > a.btn-view {box-shadow: none;}
.block-statistic .nav-secondary > li:hover > a.btn-view {opacity: 1;}
.block-statistic .nav-secondary > li.switch-icon {display: none;}
.block-statistic.block-sm .panel-body {padding-bottom: 10px; position: relative; padding-top: 45px;}
.block-statistic.block-sm .panel-body > .table-row,
.block-statistic.block-sm .panel-body > .table-row > .col {display: block; width: auto;}
.block-statistic.block-sm .panel-body > .table-row > .tab-content {padding: 0; margin: 0 -5px;}
.block-statistic.block-sm .tab-pane > .table-row > .col-5 {width: 125px;}
.block-statistic.block-sm .tab-pane > .table-row > .col-5 > .table-row {padding: 5px 0;}
.block-statistic.block-sm .col-nav {border-left: none; position: absolute; top: 0; left: 15px; right: 15px; background: #f5f5f5; border-radius: 3px;}
.block-statistic.block-sm .nav-secondary {display: table; width: 100%; padding: 0; table-layout: fixed;}
.block-statistic.block-sm .nav-secondary > li {display: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon,
.block-statistic.block-sm .nav-secondary > li.active {display: table-cell; width: 100%; text-align: center;}
.block-statistic.block-sm .nav-secondary > li.active > a:hover {cursor: default; background: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon > a:hover {background: rgba(0,0,0,0.07);}
.block-statistic.block-sm .nav-secondary > li > a {padding: 5px 10px; border-radius: 4px;}
.block-statistic.block-sm .nav-secondary > li > a:before {display: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon {width: 40px;}
.block-statistic.block-sm .types-line > li > div {padding: 18px 2px 5px;}
.block-statistic.block-sm .nav-secondary > li.active > a:first-child:before {display: none;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view {width: auto; left: 0; right: 0;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view > i {display: none;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view:hover {cursor: pointer; background: rgba(0,0,0,.1);}
.status-count {margin:auto;}
.status-count tr:first-child td:last-child {color:#000; font-weight:bold;}
</style>
<script>
<?php $blockNavId = 'nav-' . uniqid(); ?>
$(function()
{
var $nav = $('#<?php echo $blockNavId;?>');
$nav.on('click', '.switch-icon', function(e)
{
var isPrev = $(this).is('.prev');
var $activeItem = $nav.children('.active');
var $next = $activeItem[isPrev ? 'prev' : 'next']('li:not(.switch-icon)');
if ($next.length) $next.find('a[data-toggle="tab"]').trigger('click');
else $nav.children('li:not(.switch-icon)')[isPrev ? 'last' : 'first']().find('a[data-toggle="tab"]').trigger('click');
e.preventDefault();
});
var $productLi = $('#activeProduct');
if($productLi.length)
{
var productLi = $productLi[0];
$(".col ul.nav").animate({scrollTop: productLi.offsetTop}, "slow");
}
});
</script>
<div class="panel-body">
<div class="table-row">
<?php if(empty($products)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->block->noData;?></span></p>
</div>
<?php else:?>
<div class="col col-nav">
<ul class="nav nav-stacked nav-secondary scrollbar-hover" id='<?php echo $blockNavId;?>'>
<li class='switch-icon prev'><a><i class='icon icon-arrow-left'></i></a></li>
<?php $selected = empty($_SESSION['product']) ? key($products) : $this->session->product;?>
<?php $selected = !isset($products[$selected]) ? key($products) : $selected;?>
<?php foreach($products as $product):?>
<li <?php if($product->id == $selected) echo "class='active' id='activeProduct'";?> productID='<?php echo $product->id;?>'>
<a href="javascript:;" data-target="#tabProduct<?php echo $product->id;?>" data-toggle="tab" title='<?php echo $product->name;?>'><?php echo $product->name;?></a>
<?php echo html::a(helper::createLink('product', 'browse', "productID=$product->id"), "<i class='icon-arrow-right text-primary'></i>", '', "class='btn-view' title={$lang->product->browse}");?></li>
<?php endforeach;?>
<li class='switch-icon next'><a><i class='icon icon-arrow-right'></i></a></li>
</ul>
</div>
<div class="col tab-content">
<?php foreach($products as $product):?>
<div class="tab-pane fade <?php if($product->id == $selected) echo 'active in';?>" id="tabProduct<?php echo $product->id;?>">
<div class="table-row">
<div class="col-6 text-middle">
<div class="tile">
<div class="tile-title"><?php echo $lang->SRCommon;?></div>
<?php if($product->stories):?>
<div class="tile-amount text-primary"><?php echo array_sum($product->stories);?></div>
<?php common::printLink('product', 'browse', "productID={$product->id}&branch=&type=allstory", $lang->story->viewAll . '<span class="label label-badge label-icon"><i class="icon icon-arrow-right"></i></span>', '', 'class="btn btn-primary btn-circle btn-icon-right btn-sm" data-app="product"');?>
<?php else:?>
<div class="tile-amount text-primary">0</div>
<?php common::printLink('story', 'create', "productID={$product->id}", '<span class="label label-badge label-icon"><i class="icon icon-plus"></i></span>' . $lang->story->create, '', 'class="btn btn-primary btn-circle btn-icon-left btn-sm" data-app="product"');?>
<?php endif;?>
</div>
<ul class="types-line">
<?php foreach($config->statistic->storyStages as $stage):?>
<li>
<div>
<small><?php echo zget($lang->story->stageList, $stage);?></small>
<span><?php echo $product->stories ? zget($product->stories, $stage, 0) : 0;?></span>
</div>
</li>
<?php endforeach;?>
</ul>
</div>
<?php if($product->stories):?>
<div class="col-5">
<div class="product-info">
<?php $totalPlan = $product->plans ? array_sum($product->plans) : 0;?>
<?php $unexpiredPlan = $product->plans ? zget($product->plans, 'unexpired', 0) : 0;?>
<?php $unexpiredRate = $totalPlan ? round($unexpiredPlan / $totalPlan * 100, 2) : 0;?>
<?php if($totalPlan):?>
<div class="progress-info"></div>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="<?php echo $unexpiredRate;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $unexpiredRate;?>%"></div>
</div>
<?php else:?>
<div class="actions">
<?php common::printLink('productplan', 'create', "productID={$product->id}", "<i class='icon icon-plus'></i> " . $lang->productplan->create, '', "class='btn btn-info' data-app='product'");?>
</div>
<?php endif;?>
<div class="type-info">
<div class="type-label">
<table class='status-count'>
<tr>
<td class='text-right'><?php echo $lang->productplan->all;?> :</td>
<td class='text-left'><?php echo empty($totalPlan) ? 0 : html::a($this->createLink('productplan', 'browse', "productID={$product->id}&branch=0&browseType=all"), $totalPlan);?></td>
</tr>
<tr>
<td class='text-right'><?php echo $lang->productplan->unexpired;?> :</td>
<td class='text-left'><?php echo empty($unexpiredPlan) ? 0 : html::a($this->createLink('productplan', 'browse', "productID={$product->id}&branch=0&browseType=unexpired"), $unexpiredPlan);?></td>
</tr>
</table>
</div>
</div>
</div>
<div class="product-info">
<?php $totalRelease = $product->releases ? array_sum($product->releases) : 0;?>
<?php $normalRelease = $product->releases ? zget($product->releases, 'normal', 0) : 0;?>
<?php $normalRate = $totalRelease ? round($normalRelease / $totalRelease * 100, 2) : 0;?>
<?php if($totalRelease):?>
<div class="progress-info">
<?php if($product->lastRelease):?>
<i class="icon icon-check-circle text-success icon-sm"></i> <span class="text-muted"><?php echo $lang->release->yesterday;?></span> <strong><?php echo $product->lastRelease;?></strong>
<?php endif;?>
</div>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="<?php echo $normalRate;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $normalRate;?>%"></div>
</div>
<?php else:?>
<div class="actions">
<?php common::printLink('release', 'create', "productID={$product->id}", "<i class='icon icon-plus'></i> " . $lang->release->create, '', "class='btn btn-info' data-app='product'");?>
</div>
<?php endif;?>
<div class="type-info">
<div class="type-label">
<table class='status-count'>
<tr>
<td class='text-right'><?php echo $lang->product->allRelease;?> :</td>
<td class='text-left'><?php echo empty($totalRelease) ? 0 : html::a($this->createLink('release', 'browse', "productID={$product->id}&branch=0&type=all"), $totalRelease);?></td>
</tr>
<tr>
<td class='text-right'><?php echo $lang->product->maintain;?> :</td>
<td class='text-left'><?php echo empty($normalRelease) ? 0 : html::a($this->createLink('release', 'browse', "productID={$product->id}&branch=0&type=normal"), $normalRelease);?></td>
</tr>
</table>
</div>
</div>
</div>
</div>
<?php endif;?>
</div>
</div>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
</div>
-48
View File
@@ -1,48 +0,0 @@
<?php if(empty($projects)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-programs .c-pri {width: 45px;text-align: center;}
.block-programs .c-status, .block-programs .c-user {width: 80px;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-programs <?php if(!$longBlock) echo 'block-sm';?>'>
<thead>
<tr>
<th class='c-name'><?php echo $lang->project->name;?></th>
<th class='c-user'><?php echo $lang->project->PM;?></th>
<th class='c-status'><?php echo $lang->project->status;?></th>
<?php if($longBlock):?>
<th class='c-number'><?php echo $lang->project->teamCount;?></th>
<th class='c-number text-right c-consumed'><?php echo $lang->task->consumed;?></th>
<th class='c-budget text-right'><?php echo $lang->project->budget;?></th>
<th class='c-number c-leftStories'><?php echo $lang->project->leftStories;?></th>
<th class='c-number c-leftTasks'><?php echo $lang->project->leftTasks;?></th>
<th class='c-number c-leftBugs'><?php echo $lang->project->leftBugs;?></th>
<?php endif;?>
</tr>
</thead>
<tbody>
<?php foreach($projects as $project):?>
<?php $viewLink = $this->createLink('project', 'index', "programID={$project->id}");?>
<tr>
<td title='<?php echo $project->name?>'><?php echo html::a($viewLink, $project->name);?></td>
<td title="<?php echo zget($users, $project->PM, $project->PM)?>"><?php echo zget($users, $project->PM, $project->PM)?></td>
<td class='c-status'>
<span class="status-program status-<?php echo $project->status?>"><?php echo zget($lang->project->statusList, $project->status);?></span>
</td>
<?php if($longBlock):?>
<td class='text-center'><?php echo $project->teamCount;?></td>
<td class='text-right' title="<?php echo $project->consumed . ' ' . $lang->execution->workHour;?>"><?php echo $project->consumed . $lang->execution->workHourUnit;?></td>
<?php $programBudget = in_array($this->app->getClientLang(), array('zh-cn','zh-tw')) ? round((float)$project->budget / 10000, 2) . $this->lang->project->tenThousand : round((float)$project->budget, 2);?>
<td class='text-right'><?php echo $project->budget != 0 ? zget($lang->project->currencySymbol, $project->budgetUnit) . ' ' . $programBudget : $lang->project->future;?></td>
<td class='text-center'><?php echo $project->leftStories;?></td>
<td class='text-center'><?php echo $project->leftTasks;?></td>
<td class='text-center'><?php echo $project->leftBugs;?></td>
<?php endif;?>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-208
View File
@@ -1,208 +0,0 @@
<style>
.block-projectdoc .nav-stacked {overflow:auto; height:220px; max-height:220px; }
.block-projectdoc .panel-heading {border-bottom:1px solid #ddd;}
.block-projectdoc .panel-body {padding-top: 0; height:240px; padding-right:0px; padding-bottom:0px;}
.block-projectdoc .tab-content {padding-right:0px;}
.block-projectdoc .tab-pane {max-height:220px; overflow:auto;}
.block-projectdoc table.tablesorter th{border-bottom:0px !important;}
.block-projectdoc .tile {margin-bottom: 30px;}
.block-projectdoc .tile-title {font-size: 18px; color: #A6AAB8;}
.block-projectdoc .tile-amount {font-size: 48px; margin-bottom: 10px;}
.block-projectdoc .col-nav {border-right: 1px solid #EBF2FB; width: 210px; padding: 8px 0 0 0;}
.block-projectdoc .nav-secondary > li {position: relative;}
.block-projectdoc .nav-secondary > li > a {font-size: 14px; color: #838A9D; position: relative; box-shadow: none; padding-left: 20px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; transition: all .2s;}
.block-projectdoc .nav-secondary > li > a:first-child {padding-right: 36px;}
.block-projectdoc .nav-secondary > li.active > a:first-child {color: #3C4353; box-shadow: none;}
.block-projectdoc .nav-secondary > li.active > a:first-child:hover,
.block-projectdoc .nav-secondary > li.active > a:first-child:focus,
.block-projectdoc .nav-secondary > li > a:first-child:hover {box-shadow: none; border-radius: 4px 0 0 4px;}
.block-projectdoc .nav-secondary > li.active > a:first-child:before {content: ' '; display: block; left: -1px; top: 10px; bottom: 10px; width: 4px; background: #006af1; position: absolute;}
.block-projectdoc .nav-secondary > li > a.btn-view {position: absolute; top: 0; right: 0; bottom: 0; padding: 8px; width: 36px; text-align: center; opacity: 0;}
.block-projectdoc .nav-secondary > li:hover > a.btn-view {opacity: 1;}
.block-projectdoc .nav-secondary > li.active > a.btn-view {box-shadow: none;}
.block-projectdoc .nav-secondary > li.switch-icon {display: none;}
.block-projectdoc.block-sm .nav-stacked {height:auto;}
.block-projectdoc.block-sm .panel-body {padding: 45px 8px 10px 8px; position: relative; border-radius: 3px; height:275px;}
.block-projectdoc.block-sm .panel-body > .table-row,
.block-projectdoc.block-sm .panel-body > .table-row > .col {display: block; width: auto;}
.block-projectdoc.block-sm .panel-body > .table-row > .tab-content {padding: 0; margin: 0 -5px;}
.block-projectdoc.block-sm .tab-pane > .table-row > .col-5 {width: 125px;}
.block-projectdoc.block-sm .tab-pane > .table-row > .col-5 > .table-row {padding: 5px 0;}
.block-projectdoc.block-sm .col-nav {border-left: none; position: absolute; top: 0; left: 15px; right: 15px; background: #f5f5f5; padding:0px; margin-top:5px;}
.block-projectdoc.block-sm .nav-secondary {display: table; width: 100%; padding: 0; table-layout: fixed;}
.block-projectdoc.block-sm .nav-secondary > li {display: none;}
.block-projectdoc.block-sm .nav-secondary > li.switch-icon,
.block-projectdoc.block-sm .nav-secondary > li.active {display: table-cell; width: 100%; text-align: center;}
.block-projectdoc.block-sm .nav-secondary > li.active > a:hover {cursor: default; background: none;}
.block-projectdoc.block-sm .nav-secondary > li.switch-icon > a:hover {background: rgba(0, 0, 0, 0.07);}
.block-projectdoc.block-sm .nav-secondary > li > a {padding: 5px 10px; border-radius: 4px;}
.block-projectdoc.block-sm .nav-secondary > li > a:before {display: none;}
.block-projectdoc.block-sm .nav-secondary > li.switch-icon {width: 40px;}
.block-projectdoc.block-sm .nav-secondary > li.active > a:first-child:before {display: none}
.block-projectdoc.block-sm .nav-secondary > li.active > a.btn-view {width: auto; left: 0; right: 0;}
.block-projectdoc.block-sm .nav-secondary > li.active > a.btn-view > i {display: none;}
.block-projectdoc.block-sm .nav-secondary > li.active > a.btn-view:hover {cursor: pointer; background: rgba(0,0,0,.1);}
.block-projectdoc .data {width: 40%; text-align: left; padding: 10px 0px; font-size: 14px; font-weight: 700;}
.block-projectdoc .dataTitle {width: 60%; text-align: right; padding: 10px 0px; font-size: 14px;}
.block-projectdoc .executionName {padding: 2px 10px; font-size: 14px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;}
.block-projectdoc .lastIteration {padding-top: 6px;}
.forty-percent {width: 40%;}
html[lang="de"] .block-projectdoc .c-user{width: 105px;}
.block-projectdoc #projectType {position: absolute;top: 8px;left: 120px;}
.block-projectdoc #projectType .btn {border:0px;}
.block-projectdoc #projectType .btn:hover {background-color:#f5f5f5;}
.block-projectdoc .table .c-title > .doc-title {display: inline-block; overflow: hidden; background: transparent; padding-right:0px;}
.block-projectdoc .table .c-title > span.doc-title {line-height: 0; vertical-align: inherit;}
.block-projectdoc .table .c-title[data-status=draft] > .doc-title {max-width: calc(100% - 35px);}
.block-projectdoc .table .c-title > .draft {background-color:rgba(129, 102, 238, 0.12); color:#8166EE;}
</style>
<?php $blockNavId = 'nav-' . uniqid();?>
<?php js::set('emptyProjects', empty($projects));?>
<?php js::set('emptyInvolveds', empty($involveds));?>
<script>
$(function()
{
if($('.block-projectdoc<?php echo "#block{$block->id}";?> #projectType').length > 1);
{
count = $('.block-projectdoc<?php echo "#block{$block->id}";?> #projectType').length;
$('.block-projectdoc<?php echo "#block{$block->id}";?> #projectType').each(function()
{
if(count == 1) return;
$(this).remove();
count --;
})
}
});
function switch<?php echo "block{$block->id}";?>Project(obj)
{
var $this = $(obj);
var $nav = $this.closest('.nav');
var isPrev = $this.is('.prev');
var $activeItem = $nav.children('.active');
var $next = $activeItem[isPrev ? 'prev' : 'next']('li:not(.switch-icon)');
if($next.length > 0) $next.find('a[data-toggle="tab"]').trigger('click');
if($next.length == 0) $nav.children('li:not(.switch-icon)')[isPrev ? 'last' : 'first']().find('a[data-toggle="tab"]').trigger('click');
return false;
}
function change<?php echo "block{$block->id}";?>ProjectType(type)
{
var hiddenData = type == 'all' ? emptyProjects : emptyInvolveds;
if(hiddenData)
{
$('.block-projectdoc<?php echo "#block{$block->id}";?> .dataBlock').addClass('hidden');
$('.block-projectdoc<?php echo "#block{$block->id}";?> .block-statistic > .table-empty-tip').removeClass('hidden');
}
else
{
$('.block-projectdoc<?php echo "#block{$block->id}";?> .dataBlock').removeClass('hidden');
$('.block-projectdoc<?php echo "#block{$block->id}";?> .block-statistic > .table-empty-tip').addClass('hidden');
}
$('.block-projectdoc<?php echo "#block{$block->id}";?> .nav.projects').toggleClass('hidden', type != 'all');
$('.block-projectdoc<?php echo "#block{$block->id}";?> .nav.involveds').toggleClass('hidden', type != 'involved');
$('.block-projectdoc<?php echo "#block{$block->id}";?> #projectType .btn').html($('.block-projectdoc<?php echo "#block{$block->id}";?> #projectType [data-type=' + type + ']').html() + " <span class='caret'></span>");
var name = type == 'all' ? '.block-projectdoc<?php echo "#block{$block->id}";?> .projects' : '.block-projectdoc<?php echo "#block{$block->id}";?> .involveds';
var $obj = $(name + ' li.active').length > 0 ? $(name + ' .active:first').find('a') : $(name + ' li:not(.switch-icon):first').find('a');
$(name + ' li').removeClass('active');
$obj.closest('li').addClass('active');
$('.block-projectdoc<?php echo "#block{$block->id}";?> .tab-pane').removeClass('active').removeClass('in');
$('.block-projectdoc<?php echo "#block{$block->id}";?> .tab-pane' + $obj.data('target')).addClass('active').addClass('in');
$('.block-projectdoc<?php echo "#block{$block->id}";?> #projectType .dropdown-menu li').removeClass('active');
$('.block-projectdoc<?php echo "#block{$block->id}";?> #projectType .dropdown-menu li a[data-type=' + type + ']').closest('li').addClass('active');
}
</script>
<div class="dropdown" id='projectType'>
<button class="btn" type="button" data-toggle="dropdown"><?php echo $lang->project->involved;?> <span class="caret"></span></button>
<ul class="dropdown-menu">
<li class='active'><a href="javascript:change<?php echo "block{$block->id}";?>ProjectType('involved')" data-type='involved'><?php echo $lang->project->involved;?></a></li>
<li><a href="javascript:change<?php echo "block{$block->id}";?>ProjectType('all')" data-type='all'><?php echo $lang->project->all;?></a></li>
</ul>
</div>
<?php $hiddenBlock = empty($involveds) ? 'hidden' : '';?>
<?php $hiddenEmpty = empty($involveds) ? '' : 'hidden';?>
<div class="panel-body">
<div class="table-row block-statistic">
<div class="table-empty-tip <?php echo $hiddenEmpty;?>">
<p><span class="text-muted"><?php echo $lang->block->emptyTip;?></span></p>
</div>
<div class="col col-nav dataBlock <?php echo $hiddenBlock;?>">
<ul class="nav nav-stacked nav-secondary scrollbar-hover involveds">
<li class='switch-icon prev'><a href='###' onclick='switch<?php echo "block{$block->id}";?>Project(this)'><i class='icon icon-arrow-left'></i></a></li>
<?php $selected = key($involveds);?>
<?php foreach($involveds as $project):?>
<li <?php if($project->id == $selected) echo "class='active' id='activeProject'";?> projectid='<?php echo $project->id;?>'>
<a href="###" title="<?php echo $project->name?>" data-target='<?php echo "#tab3{$blockNavId}Content{$project->id}";?>' data-toggle="tab"><?php echo $project->name;?></a>
<?php echo html::a(helper::createLink('doc', 'projectSpace', "projectID=$project->id"), "<i class='icon-arrow-right text-primary'></i>", '', "class='btn-view'");?>
</li>
<?php endforeach;?>
<li class='switch-icon next'><a href='###' onclick='switch<?php echo "block{$block->id}";?>Project(this)'><i class='icon icon-arrow-right'></i></a></li>
</ul>
<ul class="nav nav-stacked nav-secondary scrollbar-hover projects hidden">
<li class='switch-icon prev'><a href='###' onclick='switch<?php echo "block{$block->id}";?>Project(this)'><i class='icon icon-arrow-left'></i></a></li>
<?php foreach($projects as $project):?>
<li projectid='<?php echo $project->id;?>'>
<a href="###" title="<?php echo $project->name?>" data-target='<?php echo "#tab3{$blockNavId}Content{$project->id}";?>' data-toggle="tab"><?php echo $project->name;?></a>
<?php echo html::a(helper::createLink('doc', 'projectSpace', "projectID=$project->id"), "<i class='icon-arrow-right text-primary'></i>", '', "class='btn-view'");?>
</li>
<?php endforeach;?>
<li class='switch-icon next'><a href='###' onclick='switch<?php echo "block{$block->id}";?>Project(this)'><i class='icon icon-arrow-right'></i></a></li>
</ul>
</div>
<div class="col tab-content dataBlock <?php echo $hiddenBlock;?>">
<?php foreach($projects as $project):?>
<div class="tab-pane fade<?php if($project->id == $selected) echo ' active in';?>" id='<?php echo "tab3{$blockNavId}Content{$project->id}";?>'>
<?php if(isset($docGroup[$project->id])):?>
<div class="table-row">
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter'>
<thead>
<tr>
<th class='c-name'><?php echo $lang->doc->title?></th>
<th class='c-user'><?php echo $lang->doc->addedBy?></th>
<th class='c-date'><?php echo $lang->doc->addedDate?></th>
<th class='c-date'><?php echo $lang->doc->editedDate?></th>
</tr>
</thead>
<tbody>
<?php foreach($docGroup[$project->id] as $doc):?>
<tr>
<td class='c-title' data-status='<?php echo $doc->status?>'>
<?php
$docType = zget($config->doc->iconList, $doc->type);
$icon = html::image("static/svg/{$docType}.svg", "class='file-icon'");
if(common::hasPriv('doc', 'view'))
{
echo html::a($this->createLink('doc', 'view', "docID=$doc->id"), $icon . $doc->title, '', "title='{$doc->title}' class='doc-title' data-app='{$this->app->tab}'");
}
else
{
echo "<span class='doc-title' title='{$doc->title}'>$icon {$doc->title}</span>";
}
if($doc->status == 'draft') echo "<span class='label label-badge draft'>{$lang->doc->draft}</span>";
?>
</td>
<td class='c-user'><?php echo zget($users, $doc->addedBy);?></td>
<td class='c-date'><?php echo substr($doc->addedDate, 0, 10);?></td>
<td class='c-date'><?php echo substr($doc->editedDate, 0, 10);?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php else:?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->block->emptyTip;?></span></p>
</div>
<?php endif;?>
</div>
<?php endforeach;?>
</div>
</div>
</div>
@@ -1,36 +0,0 @@
<?php if(empty($actions)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.timeline > li .timeline-text {display: block; overflow: hidden; text-overflow: ellipsis; max-height: 20px; -webkit-line-clamp: 1; -webkit-box-orient: vertical; }
.panel-body {padding-top: 0;}
.timeline >li:before {left: -26px;}
.timeline >li + li:after {left: -23px;}
.timeline-text {margin-left: -18px;}
.block-projectdynamic .label-action {padding: 0 6px;}
.block-projectdynamic .label-action + a {padding-left: 6px;}
.timeline > li.active:before {left: -30px;}
.timeline > li > div:after {left: -27px;}
.timeline .timeline-text {display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.timeline > li > div > .timeline-tag, .timeline > li > div > .timeline-text > .label-action {color: #838A9D;}
.timeline > li > div > .timeline-text > a {color: #313C52;}
</style>
<div class='panel-body scrollbar-hover'>
<ul class="timeline timeline-tag-left no-margin">
<?php
$i = 0;
foreach($actions as $action)
{
$user = zget($users, $action->actor);
if($action->action == 'login' or $action->action == 'logout') $action->objectName = $action->objectLabel = '';
$class = $action->major ? "class='active'" : '';
echo "<li $class><div>";
printf($lang->block->dynamicInfo, $action->date, $user, $action->actionLabel, $action->objectLabel, $action->objectLink, $action->objectName, $action->objectName);
echo "</div></li>";
$i++;
}
?>
</ul>
</div>
<?php endif;?>
@@ -1,268 +0,0 @@
<style>
.types-line {display: table; padding: 0; margin: 0 auto;}
.types-line > li {display: table-cell;}
.types-line > li > div {position: relative; padding: 18px 5px 5px; text-align: center;}
.types-line > li > div:before {content: ''; display: block; background: #eee; width: 100%; height: 2px; position: absolute; left: 50%; top: 4px;}
.types-line > li:last-child > div:before {display: none;}
.types-line > li > div:after {content: ''; display: block; background: #FFFFFF; box-shadow: 0 2px 4px 0 rgba(170,170,170,0.50), 0 0 5px 0 rgba(0,0,0,0.1); width: 10px; height: 10px; position: absolute; border-radius: 50%; top: 0; left: 50%; margin-left: -2px;}
.types-line > li > div > small {display: block; color: #A6AAB8;}
.types-line > li > div > span {display: block; color: #CBD0DB; font-size: 16px;}
.product-info {position: relative; height: 65px;}
.product-info + .product-info {margin-top: 10px;}
.product-info .progress {position: absolute; left: 10px; top: 35px; right: 100px;}
.product-info .progress-info {position: absolute; left: 8px; top: 10px; width: 180px; font-size: 12px;}
.product-info .type-info {color: #A6AAB8; text-align: center; position: absolute; right: 0; top: 6px; width: 100px;}
html[lang="en"] .product-info .type-info {color: #A6AAB8; text-align: center; position: absolute; right: 0; top: 6px; width: 110px;}
.product-info .type-value,
.product-info .type-label {font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.product-info .type-value {font-size: 14px;}
.product-info .type-value > strong {font-size: 20px; color: #3C4353;}
.product-info .actions {position: absolute; left: 10px; top: 14px;}
.block-statistic .panel-body {padding-top: 0}
.block-statistic .tile {margin-bottom: 30px;}
.block-statistic .tile-title {font-size: 18px; color: #A6AAB8;}
.block-statistic .tile-amount {font-size: 48px; margin-bottom: 10px;}
.block-statistic .col-nav {border-right: 1px solid #EBF2FB; width: 210px; padding: 0;}
.block-statistic .nav-secondary > li {position: relative;}
.block-statistic .nav-secondary > li > a {font-size: 14px; color: #838A9D; position: relative; box-shadow: none; padding-left: 20px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; transition: all .2s;}
.block-statistic .nav-secondary > li > a:first-child {padding-right: 36px;}
.block-statistic .nav-secondary > li.active > a:first-child {color: #3C4353; background: transparent; box-shadow: none;}
.block-statistic .nav-secondary > li.active > a:first-child:hover,
.block-statistic .nav-secondary > li.active > a:first-child:focus,
.block-statistic .nav-secondary > li > a:first-child:hover {box-shadow: none; border-radius: 4px 0 0 4px;}
.block-statistic .nav-secondary > li.active > a:first-child:before {content: ' '; display: block; left: -1px; top: 10px; bottom: 10px; width: 4px; background: #006af1; position: absolute;}
.block-statistic .nav-secondary > li > a.btn-view {position: absolute; top: 0; right: 0; bottom: 0; padding: 8px; width: 36px; text-align: center; opacity: 0;}
.block-statistic .nav-secondary > li:hover > a.btn-view {opacity: 1;}
.block-statistic .nav-secondary > li.active > a.btn-view {box-shadow: none;}
.block-statistic .nav-secondary > li.switch-icon {display: none;}
.block-statistic.block-sm .panel-body {padding-bottom: 10px; position: relative; padding-top: 45px; border-radius: 3px;}
.block-statistic.block-sm .panel-body > .table-row,
.block-statistic.block-sm .panel-body > .table-row > .col {display: block; width: auto;}
.block-statistic.block-sm .panel-body > .table-row > .tab-content {padding: 0; margin: 0 -5px;}
.block-statistic.block-sm .tab-pane > .table-row > .col-5 {width: 125px;}
.block-statistic.block-sm .tab-pane > .table-row > .col-5 > .table-row {padding: 5px 0;}
.block-statistic.block-sm .col-nav {border-left: none; position: absolute; top: 0; left: 15px; right: 15px; background: #f5f5f5;}
.block-statistic.block-sm .nav-secondary {display: table; width: 100%; padding: 0; table-layout: fixed;}
.block-statistic.block-sm .nav-secondary > li {display: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon,
.block-statistic.block-sm .nav-secondary > li.active {display: table-cell; width: 100%; text-align: center;}
.block-statistic.block-sm .nav-secondary > li.active > a:hover {cursor: default; background: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon > a:hover {background: rgba(0, 0, 0, 0.07);}
.block-statistic.block-sm .nav-secondary > li > a {padding: 5px 10px; border-radius: 4px;}
.block-statistic.block-sm .nav-secondary > li > a:before {display: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon {width: 40px;}
.block-statistic.block-sm .nav-secondary > li.active > a:first-child:before {display: none}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view {width: auto; left: 0; right: 0;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view > i {display: none;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view:hover {cursor: pointer; background: rgba(0,0,0,.1);}
.block-statistic .program-info .info span+span {margin-left: 15px;}
.block-statistic .project-info {margin-top: 25px;}
.block-statistic .project-info .col-xs-5, .block-statistic .project-info .col-xs-7 {margin-top: 8px;}
.block-statistic .project-info .col-xs-5 {padding-left: 0;}
.block-statistic .data {width: 40%; text-align: left; padding: 10px 0px; font-size: 14px; font-weight: 700;}
.block-statistic .dataTitle {width: 60%; text-align: right; padding: 10px 0px; font-size: 14px;}
.block-statistic .executionName {padding: 2px 10px; font-size: 14px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;}
.block-statistic .lastIteration {padding-top: 6px;}
.block-statistic .progress-text-left {margin-right: 90px}
.block-statistic .progress-text-left .progress-text {padding-top: 2px; font-size: 14px; padding-right:5px; left: -50px;}
.status-count {margin: auto;}
.status-count tr:first-child td:last-child {color: #000; font-weight: bold;}
.block-statistic .project-info {margin-left: 3px;}
.block-statistic .project-budget {white-space: nowrap;}
.block-statistic .progress-group {margin-top: 10px;}
.block-statistic .progress-group .col {padding-left: 0;}
.block-statistic .progress-percent {margin-top: 4px;}
.block-statistic .weekly-title {font-weight: bold; color: #3C4253;}
.block-statistic .weekly-small {font-size:12px; color: #838A9D;}
.block-statistic .weekly-progress {font-weight: bold; font-size:24px;}
.block-statistic .weekly-name {font-size: 14px; color: #838A9D;}
.block-statistic .weekly-value {font-size: 14px;}
.block-statistic .col-12 .stage {margin-left: 10px;}
.block-statistic .col-12 .waterfall-title {padding-top: 3px;}
.block-statistic .col-12 .waterfall-value {padding-top: 10px; font-size: 18px; font-weight: 600;}
.block-statistic .col-12 .waterfall-title .col {padding-right: 0px; padding-left: 0px;}
.forty-percent {width: 40%;}
</style>
<script>
$(function()
{
var $nav = $('#<?php echo $blockNavId;?>');
$nav.on('click', '.switch-icon', function(e)
{
var isPrev = $(this).is('.prev');
var $activeItem = $nav.children('.active');
var $next = $activeItem[isPrev ? 'prev' : 'next']('li:not(.switch-icon)');
if ($next.length) $next.find('a[data-toggle="tab"]').trigger('click');
else $nav.children('li:not(.switch-icon)')[isPrev ? 'last' : 'first']().find('a[data-toggle="tab"]').trigger('click');
e.preventDefault();
});
var $projectList = $('#activeProject');
if($projectList.length)
{
var projectList = $projectList[0];
$(".col ul.nav").animate({scrollTop: projectList.offsetTop}, "slow");
}
});
</script>
<div class="panel-body">
<div class="table-row">
<?php if(empty($projects)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->block->emptyTip;?></span></p>
</div>
<?php else:?>
<div class="col col-nav">
<ul class="nav nav-stacked nav-secondary scrollbar-hover" id='<?php echo $blockNavId;?>'>
<li class='switch-icon prev'><a><i class='icon icon-arrow-left'></i></a></li>
<?php $selected = key($projects);?>
<?php foreach($projects as $project):?>
<li <?php if($project->id == $selected) echo "class='active' id='activeProject'";?> projectID='<?php echo $project->id;?>'>
<a href="###" title="<?php echo $project->name?>" data-target='<?php echo "#tab3{$blockNavId}Content{$project->id}";?>' data-toggle="tab"><?php echo $project->name;?></a>
<?php echo html::a(helper::createLink('project', 'index', "projectID=$project->id"), "<i class='icon-arrow-right text-primary'></i>", '', "class='btn-view' title={$lang->project->index}");?>
</li>
<?php endforeach;?>
<li class='switch-icon next'><a><i class='icon icon-arrow-right'></i></a></li>
</ul>
</div>
<div class="col tab-content">
<?php foreach($projects as $project):?>
<div class="tab-pane fade<?php if($project->id == $selected) echo ' active in';?>" id='<?php echo "tab3{$blockNavId}Content{$project->id}";?>'>
<div class="table-row">
<?php if(in_array($project->model, array('scrum', 'kanban', 'agileplus'))):?>
<div class='table-row'>
<div class="col-4 text-center">
<div><h4><?php echo $lang->block->storyCount;?></h4></div>
<div>
<div class="col dataTitle"><?php echo $lang->block->allStories . ":";?></div>
<div class="col data"><?php echo $project->allStories;?></div>
</div>
<div>
<div class="col dataTitle"><?php echo $lang->block->finish . ":";?></div>
<div class="col data"><?php echo $project->doneStories;?></div>
</div>
<div>
<div class="col dataTitle"><?php echo $lang->project->surplus . ":";?></div>
<div class="col data"><?php echo $project->leftStories;?></div>
</div>
</div>
<div class="col-4 text-center">
<div><h4><?php echo $lang->block->investment;?></h4></div>
<div>
<div class="col dataTitle"><?php echo $lang->block->totalPeople . ":";?></div>
<div class="col data"><?php echo $project->teamCount;?></div>
</div>
<div>
<div class="col dataTitle"><?php echo $lang->block->estimate . ":";?></div>
<div class="col data"><?php echo $project->estimate . $lang->execution->workHourUnit;?></div>
</div>
<div>
<div class="col dataTitle"><?php echo $lang->block->consumedHours . ":";?></div>
<div class="col data"><?php echo $project->consumed . $lang->execution->workHourUnit;?></div>
</div>
</div>
<div class="col-4 text-center">
<div><h4><?php echo $lang->block->taskCount;?></h4></div>
<div>
<div class="col dataTitle"><?php echo $lang->block->wait . ":";?></div>
<div class="col data"><?php echo $project->waitTasks;?></div>
</div>
<div>
<div class="col dataTitle"><?php echo $lang->block->doing . ":";?></div>
<div class="col data"><?php echo $project->doingTasks;?></div>
</div>
<div>
<div class="col dataTitle"><?php echo $lang->block->done . ":";?></div>
<div class="col data"><?php echo $project->rndDoneTasks;?></div>
</div>
</div>
<div class="col-4 text-center">
<div><h4><?php echo $lang->block->bugCount;?></h4></div>
<div>
<div class="col dataTitle"><?php echo $lang->block->totalBug . ":";?></div>
<div class="col data"><?php echo $project->allBugs;?></div>
</div>
<div>
<div class="col dataTitle"><?php echo $lang->bug->statusList['resolved'] . ":";?></div>
<div class="col data"><?php echo $project->doneBugs;?></div>
</div>
<div>
<div class="col dataTitle"><?php echo $lang->bug->unResolved . ":";?></div>
<div class="col data"><?php echo $project->leftBugs;?></div>
</div>
</div>
</div>
<?php if(!empty($project->executions) and $project->multiple):?>
<div class="table-row project-info">
<div class="col-2 text-right"><h4><?php echo $lang->block->last;?></h4></div>
<div class="table-row lastIteration">
<div class='col-5 text-center executionName'><?php echo html::a($this->createLink('execution', 'task', "executionID={$project->executions[0]->id}"), $project->executions[0]->name, '', "title='{$project->executions[0]->name}'");?></div>
<div class='col-7'>
<div class='progress progress-text-left'>
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="<?php echo $project->executions[0]->hours->progress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $project->executions[0]->hours->progress;?>%">
<span class='progress-text'><?php echo !empty($project->executions[0]->hours->progress) ? $project->executions[0]->hours->progress . '%' : '0%';?></span>
</div>
</div>
</div>
</div>
</div>
<?php endif;?>
<?php else:?>
<div class="col-12">
<div class='table-row text-left weekly-row with-padding'>
<span class='weekly-title'><?php echo $lang->project->weekly;?></span>
<span class='stage text-muted'><?php echo $project->current;?></span>
</div>
<div class='table-row text-center progress-group col-12 with-padding center-block'>
<div class='forty-percent col'>
<div class='progress-num col'>
<span><?php echo $lang->project->progress . ':';?></span>
<span class='project-info'><?php echo $project->progress . '%';?></span>
</div>
<div class='progress progress-percent col-1-8'>
<div class="progress-bar" role="progressbar" aria-valuenow="82" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $project->progress;?>%"></div>
</div>
</div>
<div class="col-1-5">
<span><?php echo $lang->project->teamCount . ':';?></span>
<span class='project-info'><?php echo $project->teamCount;?></span>
</div>
<div class="col-1-5 project-budget">
<span><?php echo $lang->project->budget . ':';?></span>
<span class='project-info'>
<?php
$projectBudget = in_array($this->app->getClientLang(), array('zh-cn','zh-tw')) ? round((float)$project->budget / 10000, 2) . $this->lang->project->tenThousand : round((float)$project->budget, 2);
echo $project->budget != 0 ? $projectBudget : $this->lang->project->future;
?>
</span>
</div>
<div class="col-1-5"></div>
</div>
<div class="table-row text-center waterfall-title small col-12 center-block">
<?php $isChineseLang = in_array($this->app->getClientLang(), array('zh-cn','zh-tw'));?>
<div class="col-1-5"><?php echo $isChineseLang ? $lang->project->pv . '(' . $lang->project->pvTitle . ')' : $lang->project->pv; ?></div>
<div class="col-1-5"><?php echo $isChineseLang ? $lang->project->ev . '(' . $lang->project->evTitle . ')' : $lang->project->ev;?></div>
<div class="col-1-5"><?php echo $isChineseLang ? $lang->project->ac . '(' . $lang->project->acTitle . ')' : $lang->project->ac;?></div>
<div class="col-1-5"><?php echo $isChineseLang ? $lang->project->sv . '(' . $lang->project->svTitle . ')' : $lang->project->sv;?></div>
<div class="col-1-5"><?php echo $isChineseLang ? $lang->project->cv . '(' . $lang->project->cvTitle . ')' : $lang->project->cv;?></div>
</div>
<div class="table-row text-center waterfall-value small col-12 center-block">
<div class="col-1-5"><?php echo $project->pv;?></div>
<div class="col-1-5"><?php echo $project->ev;?></div>
<div class="col-1-5"><?php echo $project->ac;?></div>
<div class="col-1-5"><?php echo $project->sv;?></div>
<div class="col-1-5"><?php echo $project->cv;?></div>
</div>
</div>
<?php endif;?>
</div>
</div>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
</div>
-185
View File
@@ -1,185 +0,0 @@
<style>
.types-line {display: table; padding: 0; margin: 0 auto;}
.types-line > li {display: table-cell;}
.types-line > li > div {position: relative; padding: 18px 5px 5px; text-align: center;}
.types-line > li > div:before {content: ''; display: block; background: #eee; width: 100%; height: 2px; position: absolute; left: 50%; top: 4px;}
.types-line > li:last-child > div:before {display: none;}
.types-line > li > div:after {content: ''; display: block; background: #FFFFFF; box-shadow: 0 2px 4px 0 rgba(170,170,170,0.50), 0 0 5px 0 rgba(0,0,0,0.1); width: 10px; height: 10px; position: absolute; border-radius: 50%; top: 0; left: 50%; margin-left: -2px;}
.types-line > li > div > small {display: block; color: #A6AAB8;}
.types-line > li > div > span {display: block; color: #CBD0DB; font-size: 16px;}
.product-info {position: relative; height: 65px;}
.product-info + .product-info {margin-top: 10px;}
.product-info .progress {position: absolute; left: 10px; top: 35px; right: 90px;}
.product-info .progress-info {position: absolute; left: 8px; top: 10px; width: 180px; font-size: 12px;}
.product-info .type-info {color: #A6AAB8; text-align: center; position: absolute; right: 0; top: 6px; width: 100px;}
.product-info .type-value,
.product-info .type-label {font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #838A9D;}
.progress-pie .progress-info>small {color: #838A9D;}
.product-info .type-value {font-size: 14px;}
.product-info .type-value > strong {font-size: 20px; color: #3C4353;}
.product-info .actions {position: absolute; left: 10px; top: 14px;}
.block-statistic .panel-body {padding-top: 0;}
.block-statistic .tile {margin-bottom: 30px;}
.block-statistic .tile-title {font-size: 18px; color: #A6AAB8;}
.block-statistic .tile-amount {font-size: 48px; margin-bottom: 10px;}
.block-statistic .col-nav {border-right: 1px solid #EBF2FB; width: 260px; padding: 0;}
.block-statistic .nav-secondary > li {position: relative}
.block-statistic .nav-secondary > li > a {font-size: 14px; color: #838A9D; position: relative; box-shadow: none; padding-left: 20px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; transition: all .2s;}
.block-statistic .nav-secondary > li > a:first-child {padding-right: 36px;}
.block-statistic .nav-secondary > li.active > a:first-child {color: #3C4353; background: transparent; box-shadow: none;}
.block-statistic .nav-secondary > li.active > a:first-child:hover,
.block-statistic .nav-secondary > li.active > a:first-child:focus,
.block-statistic .nav-secondary > li > a:first-child:hover {box-shadow: none; border-radius: 4px 0 0 4px; background: #f5f5f5;}
.block-statistic .nav-secondary > li.active > a:first-child:before {content: ' '; display: block; left: -1px; top: 10px; bottom: 10px; width: 4px; background: #006af1; position: absolute;}
.block-statistic .nav-secondary > li > a.btn-view {position: absolute; top: 0; right: 0; bottom: 0; padding: 8px; width: 36px; text-align: center; opacity: 0;}
.block-statistic .nav-secondary > li:hover > a.btn-view {opacity: 1}
.block-statistic .nav-secondary > li.active > a.btn-view {box-shadow: none}
.block-statistic .nav-stacked {overflow: auto; max-height: 247px;}
.block-statistic .progress-pie .progress-info > strong {font-size: 24px;}
.block-statistic .chosen-single {font-size: 16px; font-weight: bold;}
.block-statistic .chosen-container-single .chosen-single div b {top: -2px}
.block-statistic .nav-secondary > li.switch-icon {display: none;}
.block-statistic.block-sm .panel-body {padding-bottom: 10px; position: relative; padding-top: 45px;}
.block-statistic.block-sm .panel-body > .table-row,
.block-statistic.block-sm .panel-body > .table-row > .col {display: block; width: auto;}
.block-statistic.block-sm .panel-body > .table-row > .tab-content {padding: 0; margin: 0 -5px;}
.block-statistic.block-sm .tab-pane > .table-row > .col-5 {width: 125px;}
.block-statistic.block-sm .tab-pane > .table-row > .col-5 > .table-row {padding: 5px 0;}
.block-statistic.block-sm .col-nav {border-left: none; position: absolute; top: 0; left: 15px; right: 15px; background: #f5f5f5; border-radius: 3px;}
.block-statistic.block-sm .nav-secondary {display: table; width: 100%; padding: 0; table-layout: fixed;}
.block-statistic.block-sm .nav-secondary > li {display: none}
.block-statistic.block-sm .nav-secondary > li.switch-icon,
.block-statistic.block-sm .nav-secondary > li.active {display: table-cell; width: 100%; text-align: center;}
.block-statistic.block-sm .nav-secondary > li.active > a:hover {cursor: default; background: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon > a:hover {background: rgba(0,0,0,0.07);}
.block-statistic.block-sm .nav-secondary > li > a {padding: 5px 10px; border-radius: 4px;}
.block-statistic.block-sm .nav-secondary > li > a:before {display: none;}
.block-statistic.block-sm .nav-secondary > li.switch-icon {width: 40px;}
.block-statistic .statistic-menu {height:20px;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view {width: auto; left: 0; right: 0;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view > i {display: none;}
.block-statistic.block-sm .nav-secondary > li.active > a.btn-view:hover {cursor: pointer; background: rgba(0,0,0,.05)}
</style>
<script>
<?php $blockNavId = 'nav-' . uniqid(); ?>
$(function()
{
var $nav = $('#<?php echo $blockNavId;?>');
$nav.on('click', '.switch-icon', function(e)
{
var isPrev = $(this).is('.prev');
var $activeItem = $nav.children('.active');
var $next = $activeItem[isPrev ? 'prev' : 'next']('li:not(.switch-icon)');
if ($next.length) $next.find('a[data-toggle="tab"]').trigger('click');
else $nav.children('li:not(.switch-icon)')[isPrev ? 'last' : 'first']().find('a[data-toggle="tab"]').trigger('click');
e.preventDefault();
});
$('[name^=build]').change(function()
{
var $tab = $('#bugBox' + $(this).val());
$tab.removeClass('hidden');
$tab.find('.progress-pie').progressPie();
$(this).parents('.tab-pane').find('.table-row').not($tab).addClass('hidden');
});
$('.progress-pie').progressPie();
});
</script>
<div class="panel-body block-qa">
<div class="table-row">
<div class="col col-nav">
<ul class="nav nav-stacked nav-secondary" id='<?php echo $blockNavId;?>'>
<li class='switch-icon prev'><a><i class='icon icon-arrow-left'></i></a></li>
<?php $index = 1;?>
<?php foreach($products as $product):?>
<li <?php if($product == reset($products)) echo "class='active'";?> productID='<?php echo $product->id;?>'>
<a href="javascript:;" data-target="#<?php echo $block->id;?>-tabQaProduct<?php echo $product->id;?>" data-toggle="tab" title='<?php echo $product->name;?>'><?php echo $product->name;?></a>
<?php echo html::a(helper::createLink('bug', 'browse', "productID=$product->id"), "<i class='icon-arrow-right text-primary'></i>", '', "class='btn-view' title={$lang->bug->browse}");?></li>
<?php $index++;?>
<?php endforeach;?>
<li class='switch-icon next'><a><i class='icon icon-arrow-right'></i></a></li>
</ul>
</div>
<div class="col tab-content">
<?php $index = 1;?>
<?php foreach($products as $product):?>
<div class="tab-pane fade <?php if($index == 1) echo 'active';?> in" id="<?php echo $block->id;?>-tabQaProduct<?php echo $product->id;?>">
<div class="statistic-menu input-group space w-400px">
<div class="input-group-cell">
<?php if($product->testtask):?>
<?php echo $product->testtask->name;?>
<span class="text-muted small"><a href="<?php echo $this->createLink('testtask', 'browse', "productID={$product->id}");?>" class="text-primary"><?php echo $lang->block->lblTesttask;?> <i class="icon icon-right-circle icon-sm"></i></a></span>
<?php endif;?>
</div>
</div>
<div class="table-row" id='bugBox<?php echo $product->id;?>'>
<div class="col-5 text-middle text-center">
<div class="progress-pie inline-block space progress-pie-100" data-value="<?php echo $product->assignedRate;?>" data-doughnut-size="80">
<canvas width="100" height="100" style="width: 100px; height: 100px;"></canvas>
<div class="progress-info">
<small><?php echo $lang->bug->allBugs;?></small>
<strong><?php echo empty($product->total) ? 0 : html::a($this->createLink('bug', 'browse', "productID={$product->id}&branch=&type=all"), $product->total);?></strong>
</div>
</div>
<div class="text-center small with-padding">
<span class="label label-dot label-primary"></span> &nbsp; <?php echo $lang->bug->assignToMe;?> &nbsp; <strong><?php echo empty($product->assignedToMe) ? 0 : html::a($this->createLink('bug', 'browse', "productID={$product->id}&branch=&browseType=assigntome"), $product->assignedToMe);?></strong>
</div>
</div>
<div class="col-7">
<div class="product-info">
<?php if($product->yesterdayResolved):?>
<div class="progress-info"><i class="icon icon-check-circle text-success icon-sm"></i> <span class="text-muted"><?php echo $lang->bug->yesterdayResolved;?></span> <strong><?php echo $product->yesterdayResolved;?></strong></div>
<?php endif;?>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="<?php echo $product->unresolvedRate;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $product->unresolvedRate;?>%"></div>
</div>
<div class="type-info">
<div class="type-label">
<span><?php echo $lang->bug->unResolved;?></span>
</div>
<div class="type-value">
<strong><?php echo empty($product->unresolved) ? 0 : html::a($this->createLink('bug', 'browse', "productID={$product->id}&branch=&browseType=unresolved"), $product->unresolved);?></strong>
</div>
</div>
</div>
<div class="product-info">
<?php if($product->yesterdayConfirmed):?>
<div class="progress-info"><i class="icon icon-exclamation-sign text-danger icon-sm"></i> <span class="text-muted"><?php echo $lang->bug->yesterdayConfirmed;?></span> <strong><?php echo $product->yesterdayConfirmed;?></strong></div>
<?php endif;?>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="<?php echo $product->unconfirmedRate;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $product->unconfirmedRate;?>%"></div>
</div>
<div class="type-info">
<div class="type-label">
<span><?php echo $lang->bug->unconfirmed;?></span>
</div>
<div class="type-value">
<strong><?php echo empty($product->unconfirmed) ? 0 : html::a($this->createLink('bug', 'browse', "productID={$product->id}&branch=&browseType=unconfirmed"), $product->unconfirmed);?></strong>
</div>
</div>
</div>
<div class="product-info">
<?php if($product->yesterdayClosed):?>
<div class="progress-info"><i class="icon icon-check-circle text-success icon-sm"></i> <span class="text-muted"><?php echo $lang->bug->yesterdayClosed;?></span> <strong><?php echo $product->yesterdayClosed;?></strong></div>
<?php endif;?>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="<?php echo $product->unclosedRate;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $product->unclosedRate;?>%"></div>
</div>
<div class="type-info">
<div class="type-label">
<span><?php echo $lang->bug->unclosed;?></span>
</div>
<div class="type-value">
<strong><?php echo empty($product->unclosed) ? 0 : html::a($this->createLink('bug', 'browse', "productID={$product->id}&branch=&browseType=unclosed"), $product->unclosed);?></strong>
</div>
</div>
</div>
</div>
</div>
</div>
<?php $index++;?>
<?php endforeach;?>
</div>
</div>
</div>
@@ -1,135 +0,0 @@
<style>
.cards-menu > li > a {padding-left: 5px; text-align: left;}
.cards-menu > li > a > i {opacity: .5; display: inline-block; margin-right: 4px; width: 18px; text-align: center;}
.cards-menu > li > a:hover > i {opacity: 1;}
#cards {margin: 0; padding: 0 10px 10px 10px;}
#cards > .col {width: 33.33%;}
.col-side #cards > .col {width: 100%;}
#cards .panel-content {margin: 10px 0; border: 1px solid #DCDCDC; border-radius: 2px; box-shadow: none; height: 146px; cursor: pointer;}
#cards .panel-content:hover {border-color: #006AF1; box-shadow: 0 0 10px 0 rgba(0,0,100,.25);}
#cards .panel-heading {padding: 12px 24px 10px 16px;}
#cards .panel-body {padding: 0 16px 16px;}
#cards .panel-actions {padding: 7px 0; z-index: 0}
#cards .project-type-label {padding: 2px 2px; margin-bottom: 3px;}
#cards .project-name {font-size: 16px; font-weight: normal; display: inline-block; max-width: 75%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle;}
#cards .project-infos {font-size: 12px; padding: 0 15px;}
#cards .project-infos > span {display: inline-block; line-height: 12px;}
#cards .project-infos > span > .icon {font-size: 12px; display: inline-block; position: relative; top: -1px}
#cards .project-infos > span + span {margin-left: 10px;}
#cards .project-detail {position: absolute; bottom: 27px; left: 16px; right: 16px; font-size: 12px; padding-left: 10px;}
#cards .project-detail > p {margin-bottom: 8px;}
#cards .project-detail .progress {height: 4px;}
#cards .project-detail .progress-text-left .progress-text {width: 50px; left: -50px;}
#cards .panel-heading {cursor: pointer;}
#cards .project-stages-container {margin: 0 0 -16px 0; padding: 0 4px; height: 40px; overflow-x: auto; position: relative;}
#cards .project-stages:after {content: ' '; width: 30px; display: block; right: -5px; top: 16px; bottom: -5px; z-index: 1; background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%); position: absolute;}
#cards .project-stages-row {position: relative; height: 30px; z-index: 0;}
#cards .project-stage-item {white-space: nowrap; position: absolute; top: 0; min-width: 48px; padding-top: 13px; color: #838A9D;}
#cards .project-stage-item > div {max-width: 70px; white-space: nowrap; overflow: hidden; text-align: center; text-overflow: ellipsis;}
#cards .project-stage-item:before {content: ' '; display: block; width: 8px; height: 8px; border-radius: 50%; background: #D1D1D1; position: absolute; left: 50%; margin-left: -4px; top: 0; z-index: 1;}
#cards .project-stage-item + .project-stage-item:after {content: ' '; display: block; left: -50%; right: 50%; height: 2px; background-color: #D1D1D1; top: 3px; position: absolute; z-index: 0;}
#cards .project-stage-item.is-going {color: #333;}
#cards .project-stage-item.is-going::before {background-color: #0C64EB;}
#dashboard .block-recentproject .panel-body {padding: 0;}
.execution-name {overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.scrollbar::-webkit-scrollbar:horizontal {height: 5px;}
</style>
<div class="panel-body">
<?php if(empty($projects)):?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<div class='row' id='cards'>
<?php foreach($projects as $projectID => $project):?>
<?php $viewLink = $this->createLink('project', 'index', "projectID=$project->id");?>
<div class='col'>
<div class='panel-content'>
<div class='panel-heading not-move-handler'>
<?php $labelClass = $project->model == 'waterfall' ? 'label-warning' : 'label-info';?>
<span class='project-type-label label <?php echo $labelClass;?> label-outline'><?php echo $lang->project->{$project->model};?></span>
<strong class='project-name' title='<?php echo $project->name;?>'> <?php echo html::a($viewLink, $project->name);?> </strong>
<nav class='panel-actions nav nav-default'>
<li class='dropdown'>
<ul class='dropdown-menu pull-right cards-menu'>
<li><?php common::printIcon('project', 'group', "projectID=$project->id", $project, 'button', 'group', '', '', '', '', '', $project->id);?></li>
<li><?php common::printIcon('project', 'manageMembers', "projectID=$project->id", $project, 'button', 'persons', '', '', '', '', '', $project->id);?></li>
<li><?php common::printicon('project', 'activate', "projectID=$project->id", $project, 'button', '', '', 'iframe', true);?></li>
<li><?php if(common::hasPriv('project', 'edit')) echo html::a($this->createLink('project', 'edit', "projectID=$project->id"), "<i class='icon-edit'></i> " . $lang->project->edit, '', "data-app='{$app->tab}'");?></li>
<li><?php common::printIcon('project', 'start', "projectID=$project->id", $project, 'button', ' icon-play', '', 'iframe', true);?></li>
<li><?php common::printIcon('project', 'suspend', "projectID=$project->id", $project, 'button', ' icon-pause', '', 'iframe', true);?></li>
<li><?php common::printIcon('project', 'close', "projectID=$project->id", $project, 'button', ' icon-off', '', 'iframe', true);?></li>
<li><?php if(common::hasPriv('project', 'delete')) echo html::a($this->createLink('project', 'delete', "projectID=$project->id"), "<i class='icon-trash'></i> " . $lang->project->delete, 'hiddenwin', "data-app='{$app->tab}'");?></li>
</ul>
</li>
</nav>
</div>
<div class='panel-body'>
<div class='project-infos'>
<span><i class='icon icon-group'></i> <?php printf($lang->project->membersUnit, $project->teamCount); ?></span>
<span><i class='icon icon-clock'></i> <?php printf($lang->project->hoursUnit, $project->estimate); ?></span>
</div>
<?php if($project->model === 'waterfall'):?>
<div class='project-detail project-stages'>
<?php
$projectProjects = array();
foreach($project->executions as $project)
{
if($project->grade == 1) $projectProjects[] = $project;
}
?>
<?php if(empty($projectProjects)): ?>
<div class='label label-outline'><?php echo zget($lang->project->statusList, $project->status);?></div>
<?php else: ?>
<p class='text-muted'><?php echo $lang->project->ongoingStage;?></p>
<div class='project-stages-container scrollbar-hover scrollbar'>
<div class='project-stages-row'>
<?php foreach ($projectProjects as $project):?>
<div class='project-stage-item is-<?php echo $project->status;?><?php if($project->status !== 'wait') echo ' is-going';?>'>
<div title="<?php echo $project->name;?>"><?php echo $project->name;?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<?php elseif($project->multiple):?>
<?php $project = empty($project->executions ) ? '' : end($project->executions);?>
<div class='project-detail project-iteration'>
<?php if($project):?>
<p class='text-muted'><?php echo $project->type == 'kanban' ? $lang->project->lastKanban : $lang->project->lastIteration;?></p>
<div class='row'>
<div class='col-xs-5 execution-name' title="<?php echo $project->name;?>"><?php echo html::a($this->createLink('execution', 'task', "executionID={$project->id}"), $project->name);?></div>
<div class='col-xs-7'>
<div class="progress progress-text-left">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="<?php echo $project->hours->progress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $project->hours->progress;?>%">
<span class="progress-text"><?php echo $project->hours->progress;?>%</span>
</div>
</div>
</div>
</div>
<?php endif;?>
</div>
<?php endif;?>
</div>
</div>
</div>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
<script>
/* Auto resize stages */
$('.block-recentproject #cards .project-stages-container').each(function()
{
var $container = $(this);
var $row = $container.children();
var totalWidth = 0;
$row.children().each(function()
{
var $item = $(this);
$item.css('left', totalWidth);
totalWidth += $item.width();
});
$row.css('minWidth', totalWidth);
});
</script>
-58
View File
@@ -1,58 +0,0 @@
<?php
/**
* The release block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(empty($releases)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed tablesorter block-release'>
<thead>
<tr>
<?php if($longBlock):?>
<th class='c-id-xs text-center'><?php echo $lang->idAB?></th>
<th><?php echo $lang->release->product;?></th>
<?php endif;?>
<th><?php echo $lang->release->name;?></th>
<?php if($longBlock):?>
<th><?php echo $lang->release->build;?></th>
<?php endif;?>
<th class='c-date text-center'><?php echo $lang->release->date;?></th>
<th class='text-center w-90px'><?php echo $lang->release->status;?></th>
</tr>
</thead>
<tbody>
<?php foreach($releases as $release):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
$productViewLink = $this->createLink('product', 'browse', "productID={$release->product}");
$buildViewLink = $this->createLink('build', 'view', "buildID={$release->build}");
$releaseViewLink = $this->createLink('release', 'view', "releaseID={$release->id}");
?>
<tr <?php echo $appid?>>
<?php if($longBlock):?>
<td class='text-center'><?php echo sprintf('%03d', $release->id);?></td>
<td title='<?php echo $release->productName?>'><?php echo html::a($productViewLink, $release->productName);?></td>
<?php endif;?>
<td title='<?php echo $release->name?>'><?php echo html::a($releaseViewLink, $release->name);?></td>
<?php if($longBlock):?>
<td title='<?php echo $release->buildName?>'><?php echo html::a($buildViewLink, $release->buildName);?></td>
<?php endif;?>
<td class='text-center'><?php echo $release->date?></td>
<td class='c-status' title='<?php echo zget($lang->release->statusList, $release->status);?>'>
<span class="status-release status-<?php echo $release->status?>"><?php echo zget($lang->release->statusList, $release->status);?></span>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-51
View File
@@ -1,51 +0,0 @@
<?php if(empty($risks)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-risks .c-id {width: 50px;}
.block-risks .c-pri {width: 50px;text-align: center;}
.block-risks .c-plannedClosedDate {width: 100px;}
.block-risks .c-category {width: 80px;}
.block-risks .c-status {width: 80px;}
.block-risks.block-sm .c-status {text-align: center;}
.pri-low {color: #000000;}
.pri-middle {color: #FF9900;}
.pri-high {color: #E53333;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-fixed table-fixed-head table-hover tablesorter block-risks <?php if(!$longBlock) echo 'block-sm'?>'>
<thead>
<tr>
<th class='c-id'><?php echo $lang->idAB?></th>
<th class='c-name'><?php echo $lang->risk->name?></th>
<?php if($longBlock):?>
<th class='c-pri'><?php echo $lang->priAB?></th>
<th class='c-category'><?php echo $lang->risk->category;?></th>
<th class='c-plannedClosedDate'><?php echo $lang->risk->plannedClosedDate;?></th>
<?php endif;?>
<th class='c-status'><?php echo $lang->risk->status;?></th>
</tr>
</thead>
<tbody>
<?php foreach($risks as $risk):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
?>
<tr>
<td class='c-id-xs'><?php echo sprintf('%03d', $risk->id);?></td>
<td class='c-name' title='<?php echo $risk->name?>'><?php echo html::a($this->createLink('risk', 'view', "riskID=$risk->id", '', '', $risk->project), $risk->name)?></td>
<?php if($longBlock):?>
<td class='c-pri'><?php echo "<span class='pri-{$risk->pri}'>" . zget($lang->risk->priList, $risk->pri) . "</span>";?></td>
<td class='c-category'><?php echo zget($lang->risk->categoryList, $risk->category)?></td>
<td class='c-plannedClosedDate'><?php echo $risk->plannedClosedDate == '0000-00-00' ? '' : $risk->plannedClosedDate;?></td>
<?php endif;?>
<?php $status = $this->processStatus('risk', $risk);?>
<td class='c-status' title='<?php echo $status;?>'>
<span class="status-risk status-<?php echo $risk->status?>"><?php echo $status;?></span>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
@@ -1,50 +0,0 @@
<?php if(empty($issues)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-issues .c-id {width: 55px;}
.block-issues .c-status {width: 80px;}
.block-issues.block-sm .c-status {text-align: center;}
.c-assignedTo {width: 100px; padding:0px !important;text-align:center;}
.c-severity, .c-pri {text-align:center;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-issues <?php if(!$longBlock) echo 'block-sm';?>'>
<thead>
<tr>
<th class='c-id'><?php echo $lang->idAB;?></th>
<th class='c-status'><?php echo $lang->issue->type;?></th>
<th class='c-name'> <?php echo $lang->issue->title;?></th>
<?php if($longBlock):?>
<th class='c-status'><?php echo $lang->issue->severity;?></th>
<th class='c-number'><?php echo $lang->issue->pri;?></th>
<th class='c-user'><?php echo $lang->issue->owner;?></th>
<th class='c-assignedTo'><?php echo $lang->issue->assignedTo;?></th>
<?php endif;?>
<th class='c-status'><?php echo $lang->issue->status;?></th>
</tr>
</thead>
<tbody>
<?php foreach($issues as $issue):?>
<?php
$viewLink = $this->createLink('issue', 'view', "issueID={$issue->id}");
?>
<tr>
<td class='c-id-xs'><?php echo sprintf('%03d', $issue->id);?></td>
<td class='c-type'><?php echo zget($lang->issue->typeList, $issue->type);?></td>
<td class='c-name' title='<?php echo $issue->title?>'><?php echo html::a($viewLink, $issue->title);?></td>
<?php if($longBlock):?>
<td class='c-severity'><?php echo zget($lang->issue->severityList, $issue->severity, $issue->severity)?></td>
<td class='c-pri'><?php echo zget($lang->issue->priList, $issue->pri, $issue->pri)?></td>
<td><?php echo zget($users, $issue->owner, $issue->owner)?></td>
<td class='c-assignedTo'><?php echo zget($users, $issue->assignedTo, $issue->assignedTo)?></td>
<?php endif;?>
<td class='c-status'>
<span class="status-issue status-<?php echo $issue->status?>"><?php echo zget($lang->issue->statusList, $issue->status);?></span>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-76
View File
@@ -1,76 +0,0 @@
<?php
/**
* The project list block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<style>
.table .c-progress {width: 60px;}
.table .c-estimate {width: 85px;}
.table .c-burn {width: 150px;}
</style>
<?php if(empty($executionStats)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<div class="panel-body has-table scrollbar-hover">
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-projects tablesorter'>
<thead>
<tr class='text-center'>
<th class='c-name text-left'><?php echo $lang->execution->name;?></th>
<th class="c-date"><?php echo $lang->execution->end;?></th>
<?php if($longBlock):?>
<?php $thClass = common::checkNotCN() ? 'c-estimate' : 'c-hours';?>
<th class="c-status"><?php echo $lang->statusAB;?></th>
<th class='<?php echo $thClass?>'><?php echo $lang->execution->totalEstimate;?></th>
<th class="c-hours"><?php echo $lang->execution->totalConsumed;?></th>
<th class="c-hours"><?php echo $lang->execution->totalLeft;?></th>
<?php endif;?>
<th class="c-progress"><?php echo $lang->execution->progress;?></th>
<?php if($longBlock):?>
<th class='c-burn'><?php echo $lang->execution->burn;?></th>
<?php endif;?>
</tr>
</thead>
<tbody class="text-center">
<?php $id = 0; ?>
<?php foreach($executionStats as $execution):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn text-center' data-id='{$this->get->entry}'" : "class='text-center'";
$viewLink = $this->createLink('execution', 'task', 'executionID=' . $execution->id);
?>
<tr <?php echo $appid?>>
<td class='c-name text-left' title='<?php echo $execution->name;?>'><nobr><?php echo html::a($viewLink, $execution->name, '', "title='$execution->name' class='text-primary'");?></nobr></td>
<td class="c-date"><?php echo $execution->end;?></td>
<?php if($longBlock):?>
<td class="w-70px">
<?php if(isset($execution->delay)):?>
<span class="status-project status-delayed" title='<?php echo $lang->execution->delayed;?>'><?php echo $lang->execution->delayed;?></span>
<?php else:?>
<?php $statusName = $this->processStatus('execution', $execution);?>
<span class="status-project status-<?php echo $execution->status?>" title='<?php echo $statusName;?>'><?php echo $statusName;?></span>
<?php endif;?>
</td>
<td class="c-hours" title="<?php echo $execution->hours->totalEstimate . ' ' . $lang->execution->workHour;?>"><?php echo $execution->hours->totalEstimate . $lang->execution->workHourUnit;?></td>
<td class="c-hours" title="<?php echo $execution->hours->totalConsumed . ' ' . $lang->execution->workHour;?>"><?php echo $execution->hours->totalConsumed . $lang->execution->workHourUnit;?></td>
<td class="c-hours" title="<?php echo $execution->hours->totalLeft . ' ' . $lang->execution->workHour;?>"><?php echo $execution->hours->totalLeft . $lang->execution->workHourUnit;?></td>
<?php endif;?>
<td class="c-progress">
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($execution->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($execution->hours->progress);?></div>
</div>
</td>
<?php if($longBlock):?>
<td id='spark-<?php echo $id++?>' class='no-padding text-left sparkline' values='<?php echo join(',', $execution->burns);?>'></td>
<?php endif;?>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
@@ -1,73 +0,0 @@
<?php
/**
* The scrum overview block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php $vision = $this->config->vision;?>
<?php if(empty($totalData)): ?>
<div class='empty-tip'><?php common::printLink('project', 'create', '', "<i class='icon-plus'></i> " . $lang->project->create, '', "class='btn btn-primary'")?></div>
<?php else:?>
<table class="table table-data">
<thead>
<tr>
<th><?php echo $lang->block->storyCount;?></th>
<th></th>
<th><?php echo $lang->block->investment;?></th>
<th></th>
<th><?php echo $lang->block->taskCount;?></th>
<th></th>
<?php if($vision == 'rnd'):?>
<th><?php echo $lang->block->bugCount;?></th>
<?php endif;?>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th><?php echo $lang->block->totalStory . ':';?></th>
<td><?php echo $totalData[$projectID]->allStories;?></td>
<th><?php echo $lang->block->totalPeople . ':';?></th>
<td><?php echo $totalData[$projectID]->teamCount ? html::a($this->createLink('project', 'team', 'projectID=' . $projectID), $totalData[$projectID]->teamCount) : 0;?></td>
<th><?php echo ($vision == 'rnd' ? $lang->block->wait : $lang->block->totalTask) . ':';?></th>
<td><?php echo $vision == 'rnd' ? $totalData[$projectID]->waitTasks : $totalData[$projectID]->allTasks;?></td>
<?php if($vision == 'rnd'):?>
<th><?php echo $lang->block->totalBug . ':';?></th>
<td><?php echo $totalData[$projectID]->allBugs;?></td>
<?php endif;?>
</tr>
<tr>
<th><?php echo $lang->block->done . ':';?></th>
<td><?php echo $totalData[$projectID]->doneStories;?></td>
<th><?php echo $lang->block->estimate . ':';?></th>
<td><?php echo $totalData[$projectID]->estimate . $lang->execution->workHourUnit;?></td>
<th><?php echo ($vision == 'rnd' ? $lang->block->doing : $lang->block->done) . ':';?></th>
<td><?php echo $vision == 'rnd' ? $totalData[$projectID]->doingTasks: $totalData[$projectID]->liteDoneTasks;?></td>
<?php if($vision == 'rnd'):?>
<th><?php echo $lang->bug->statusList['resolved'] . ':';?></th>
<td><?php echo $totalData[$projectID]->doneBugs;?></td>
<?php endif;?>
</tr>
<tr>
<th><?php echo $lang->block->left . ':';?></th>
<td><?php echo $totalData[$projectID]->leftStories;?></td>
<th><?php echo $lang->block->consumedHours . ':';?></th>
<td><?php echo $totalData[$projectID]->consumed . $lang->execution->workHourUnit;?></td>
<th><?php echo $vision == 'rnd' ? $lang->block->done . ':' : $lang->block->undone;?></th>
<td><?php echo $vision == 'rnd' ? $totalData[$projectID]->rndDoneTasks : $totalData[$projectID]->leftTasks;?></td>
<?php if($vision == 'rnd'):?>
<th><?php echo $lang->bug->unResolved . ':';?></th>
<td><?php echo $totalData[$projectID]->leftBugs;?></td>
<?php endif;?>
</tr>
</tbody>
</table>
<?php endif;?>
-58
View File
@@ -1,58 +0,0 @@
<?php if(empty($risks)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-risks .c-id {width: 40px;}
.block-risks .c-pri {width: 85px; text-align: center;}
.block-risks .c-strategy, .block-risks .c-status {width: 80px;}
.block-risks .c-user, .block-risks .c-rate, .block-risks .c-category {width: 110px;}
.c-rate, .c-category {text-align:center;}
.c-assignedTo {width: 100px; padding:0px !important;text-align:center;}
.pri-low {color: #000000;}
.pri-middle {color: #FF9900;}
.pri-high {color: #E53333;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-risks <?php if(!$longBlock) echo 'block-sm';?>'>
<thead>
<tr>
<th class='c-id'><?php echo $lang->idAB;?></th>
<th class='c-name'><?php echo $lang->risk->name;?></th>
<?php if($longBlock):?>
<th class='c-strategy'> <?php echo $lang->risk->strategy;?></th>
<?php endif;?>
<th class='c-status'><?php echo $lang->risk->status;?></th>
<?php if($longBlock):?>
<th class='c-rate'><?php echo $lang->risk->rate;?></th>
<th class='c-pri'><?php echo $lang->risk->pri;?></th>
<th class='c-assignedTo text-center'><?php echo $lang->risk->assignedTo;?></th>
<th class='c-category'><?php echo $lang->risk->category;?></th>
<?php endif;?>
</tr>
</thead>
<tbody>
<?php foreach($risks as $risk):?>
<?php
$viewLink = $this->createLink('risk', 'view', "riskID={$risk->id}");
?>
<tr>
<td class='c-id-xs'><?php echo sprintf('%03d', $risk->id);?></td>
<td class='c-name' title='<?php echo $risk->name?>'><?php echo html::a($viewLink, $risk->name);?></td>
<?php if($longBlock):?>
<td class='c-strategy'><?php echo zget($lang->risk->strategyList, $risk->strategy, $risk->strategy)?></td>
<?php endif;?>
<td class='c-status'>
<span class="status-risk status-<?php echo $risk->status?>"><?php echo zget($lang->risk->statusList, $risk->status);?></span>
</td>
<?php if($longBlock):?>
<td class='c-rate'><?php echo $risk->rate?></td>
<td class='c-pri'><?php echo "<span class='pri-{$risk->pri}'>" . zget($lang->risk->priList, $risk->pri) . "</span>";?></td>
<td class='c-assignedTo'><?php echo zget($users, $risk->assignedTo, $risk->assignedTo)?></td>
<td class='c-category'><?php echo zget($lang->risk->categoryList, $risk->category, $risk->category)?></td>
<?php endif;?>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-55
View File
@@ -1,55 +0,0 @@
<?php
/**
* The testtask block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(empty($testtasks)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed-head tablesorter block-testtask table-fixed'>
<thead>
<tr class='text-center'>
<th class='text-left'><?php echo $lang->testtask->name;?></th>
<?php if($longBlock and $project->hasProduct):?>
<th class='text-left'><?php echo $lang->testtask->product;?></th>
<?php endif;?>
<?php if($longBlock):?>
<th class='text-left'><?php echo $lang->testtask->project?></th>
<?php endif;?>
<th class='text-left'><?php echo $lang->testtask->build;?></th>
<th class='w-date'><?php echo $lang->testtask->status;?></th>
</tr>
</thead>
<tbody>
<?php foreach($testtasks as $testtask):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
$productViewLink = $this->createLink('product', 'browse', "productID={$testtask->product}");
$projectViewLink = $this->createLink('project', 'view', "projectID={$testtask->project}");
$testtaskViewLink = $this->createLink('testtask', 'view', "testtaskID={$testtask->id}");
$buildViewLink = $this->createLink('build', 'view', "buildID={$testtask->build}");
?>
<tr class='text-center' <?php echo $appid?>>
<td class='text-left text-ellipsis' title='<?php echo $testtask->name?>'><?php echo html::a($testtaskViewLink, $testtask->name);?></td>
<?php if($longBlock and $project->hasProduct):?>
<td class='text-left text-ellipsis' title='<?php echo $testtask->productName?>'><?php echo html::a($productViewLink, $testtask->productName);?></td>
<?php endif;?>
<?php if($longBlock):?>
<td class='text-left text-ellipsis' title='<?php echo $testtask->projectName?>'><?php echo html::a($projectViewLink, $testtask->projectName);?></td>
<?php endif;?>
<td class='text-left text-ellipsis' title='<?php echo $testtask->buildName?>'><?php echo html::a($buildViewLink, $testtask->buildName);?></td>
<td><?php echo zget($lang->testtask->statusList, $testtask->status)?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-44
View File
@@ -1,44 +0,0 @@
<?php
/**
* The sprint block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(!$summary->total): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.status-bars {display: table; width: 80%; height: 140px; padding: 5px; padding-top: 50px; margin: 0; overflow: hidden;}
</style>
<div class="panel-body table-row">
<div class="col-4 text-middle text-center">
<div class="tile">
<div class="tile-title"><?php echo $lang->block->allExecutions;?></div>
<?php $projectLink = $this->createLink('project', 'execution')?>
<div class="tile-amount"><?php echo $summary->total ? html::a($projectLink, $summary->total) : 0;?></div>
</div>
</div>
<div class="col-8 text-middle">
<ul class="status-bars all-statistics">
<li>
<span class="bar" style="height: <?php echo $progress->doing * 100;?>%">
<span class="value"><?php echo $summary->doing;?></span>
</span>
<span class="title"><?php echo $lang->block->doingExecution;?></span>
</li>
<li>
<span class="bar" style="height: <?php echo $progress->closed * 100;?>%">
<span class="value"><?php echo $summary->closed;?></span>
</span>
<span class="title"><?php echo $lang->block->finishExecution;?></span>
</li>
</ul>
</div>
</div>
<?php endif;?>
-68
View File
@@ -1,68 +0,0 @@
<?php
/**
* The story block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php $this->app->loadLang('story');?>
<?php if(empty($stories)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-stories .c-id {width: 55px;}
.block-stories .c-pri {width: 45px;text-align: center;}
.block-stories .c-estimate {width: 65px; text-align: right;}
.block-stories .c-status {width: 80px;}
.block-stories .c-stage {width: 80px;}
.block-stories.block-sm .estimate,
.block-stories.block-sm .c-stage,
.block-stories.block-sm .c-status {text-align: center;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-stories <?php if(!$longBlock) echo 'block-sm'?>'>
<thead>
<tr>
<th class="c-id"><?php echo $lang->idAB?></th>
<th class="c-name"><?php echo $lang->story->title;?></th>
<th class="c-pri"><?php echo $lang->priAB?></th>
<?php if($longBlock):?>
<th class="c-status"><?php echo $lang->statusAB;?></th>
<th class='c-estimate'><?php echo $lang->story->estimateAB;?></th>
<th class='c-stage'><?php echo $lang->story->stageAB;?></th>
<?php endif;?>
</tr>
</thead>
<tbody>
<?php foreach($stories as $story):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
$viewLink = $this->createLink('story', 'view', "storyID={$story->id}");
?>
<tr <?php echo $appid?>>
<td class="c-id-xs"><?php echo sprintf('%03d', $story->id);?></td>
<td class="c-name" style='color: <?php echo $story->color?>' title='<?php echo $story->title?>'><?php echo html::a($viewLink, $story->title);?></td>
<td class="c-pri"><span class="label-pri label-pri-<?php echo $story->pri?>" title="<?php echo zget($lang->story->priList, $story->pri, $story->pri);?>"><?php echo zget($lang->story->priList, $story->pri, $story->pri)?></span></td>
<?php if($longBlock):?>
<?php $status = $this->processStatus('story', $story);?>
<td class='c-status' title='<?php echo $status;?>'>
<span class="status-story status-<?php echo $story->status?>"><?php echo $status;?></span>
</td>
<td class='c-estimate text-center' title="<?php echo $story->estimate . ' ' . $lang->hourCommon;?>"><?php echo $story->estimate . $config->hourUnit?></td>
<td class='c-stage'>
<span class='story-stage-<?php echo $story->stage?>'>
<?php echo zget($lang->story->stageList, $story->stage, $story->stage);?>
</span>
</td>
<?php endif;?>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-65
View File
@@ -1,65 +0,0 @@
<?php
/**
* The task block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(empty($tasks)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-tasks .c-id {width: 55px;}
.block-tasks .c-pri {width: 45px;text-align: center;}
.block-tasks .c-pri-long {width: 80px;}
.block-tasks .c-estimate {width: 60px;text-align: right;}
.block-tasks .c-deadline {width: 95px;}
.block-tasks .c-status {width: 80px;}
.block-tasks.block-sm .c-status {text-align: center;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-tasks <?php if(!$longBlock) echo 'block-sm';?>'>
<thead>
<tr>
<th class='c-id'><?php echo $lang->idAB;?></th>
<th class='c-name'> <?php echo $lang->task->name;?></th>
<th class='c-pri <?php if($longBlock) echo "c-pri-long"?>'><?php echo $lang->priAB?></th>
<th class='c-status'><?php echo $lang->statusAB;?></th>
<?php if($longBlock):?>
<th class='c-deadline'><?php echo $lang->task->deadlineAB;?></th>
<th class='c-estimate'><?php echo $lang->task->estimateAB;?></th>
<?php endif;?>
</tr>
</thead>
<tbody>
<?php foreach($tasks as $task):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
?>
<tr>
<td class='c-id-xs'><?php echo sprintf('%03d', $task->id);?></td>
<?php
$onlybody = $task->executionType == 'kanban' ? true : '';
$class = $task->executionType == 'kanban' ? "class='iframe' data-toggle='modal'" : '';
?>
<td class='c-name' style='color: <?php echo $task->color?>' title='<?php echo $task->name?>'><?php echo html::a($this->createLink('task', 'view', "taskID=$task->id", '', $onlybody, $task->project), $task->name, null, "$class data-width='80%'")?></td>
<td class='c-pri <?php if($longBlock) echo "c-pri-long"?>'><span class='label-pri label-pri-<?php echo $task->pri;?>' title='<?php echo zget($lang->task->priList, $task->pri)?>'><?php echo zget($lang->task->priList, $task->pri)?></span></td>
<?php $status = $this->processStatus('task', $task);?>
<td class='c-status' title='<?php echo $status;?>'>
<span class="status-task status-<?php echo $task->status?>"><?php echo $status;?></span>
</td>
<?php if($longBlock):?>
<td class='c-deadline'><?php if(substr($task->deadline, 0, 4) > 0) echo $task->deadline;?></td>
<td class='c-estimate text-center' title="<?php echo $task->estimate . ' ' . $lang->execution->workHour;?>"><?php echo $task->estimate . $lang->execution->workHourUnit;?></td>
<?php endif;?>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-62
View File
@@ -1,62 +0,0 @@
<?php
/**
* The testtask block view file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package block
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php if(empty($testtasks)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed-head tablesorter block-testtask table-fixed'>
<thead>
<tr class='text-center'>
<?php if($longBlock):?>
<th class='c-id'><?php echo $lang->idAB?></th>
<th class='text-left'><?php echo $lang->testtask->product;?></th>
<?php endif;?>
<th class='text-left'><?php echo $lang->testtask->name;?></th>
<?php if($longBlock):?>
<th class='text-left'><?php echo $lang->testtask->execution . '/' . $lang->testtask->build;?></th>
<?php endif;?>
<th class='c-date'><?php echo $lang->testtask->begin;?></th>
<th class='c-date'><?php echo $lang->testtask->end;?></th>
</tr>
</thead>
<tbody>
<?php foreach($testtasks as $testtask):?>
<?php
$appid = isset($_GET['entry']) ? "class='app-btn' data-id='{$this->get->entry}'" : '';
$productViewLink = $this->createLink('product', 'browse', "productID={$testtask->product}");
$buildViewLink = $this->createLink('build', 'view', "buildID={$testtask->build}");
$testtaskViewLink = $this->createLink('testtask', 'view', "testtaskID={$testtask->id}");
if($testtask->shadow)
{
$testtask->productName = zget($projects, $testtask->project);
$productViewLink = $this->createLink('projectstory', 'story', "projectID={$testtask->project}&productID={$testtask->product}");
}
?>
<tr class='text-center' <?php echo $appid?>>
<?php if($longBlock):?>
<td><?php echo sprintf('%03d', $testtask->id);?></td>
<td class='text-left' title='<?php echo $testtask->productName?>'><?php echo html::a($productViewLink, $testtask->productName);?></td>
<?php endif;?>
<td class='text-left' title='<?php echo $testtask->name?>'><?php echo html::a($testtaskViewLink, $testtask->name);?></td>
<?php if($longBlock):?>
<td class='text-left' title='<?php echo $testtask->projectName . '/' . $testtask->buildName?>'><?php echo html::a($buildViewLink, $testtask->projectName . '/' . $testtask->buildName);?></td>
<?php endif;?>
<td><?php echo $testtask->begin?></td>
<td><?php echo $testtask->end?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>
-47
View File
@@ -1,47 +0,0 @@
<style>
.themeSwitch .table-row {border-top: 0px !important;}
.themeSwitch .theme-container:first-child {padding-top: 1%;}
@media screen and (max-width: 1366px){.themeSwitch .theme-container:first-child {padding-top: 3%;} .theme-container + .theme-container {padding-top: 3%;}}
.themeSwitch .theme-container {border-collapse:separate; border-spacing:10px; border: none;}
.theme {margin-left: 10px; color: #FFF; border: none; background-size: 100% !important; cursor:pointer; border-radius: 4px; position: relative;}
.theme:hover {box-shadow: 0 0 14px rgba(0, 0, 0, 0.4);}
.theme-text {width: 100%;position: absolute; bottom: 6%; left: 50%; transform: translateX(-50%);}
.theme-text .icon {padding-right: 10px;}
.theme-text.active {left: 45%;}
div.theme-default {background: #3785ff;}
div.theme-blue {background: #2b80ff;}
div.theme-green {background: #248f83;}
div.theme-red {background: #f34a5a;}
div.theme-pink {background: #f7889c;}
div.theme-blackberry {background: #304269;}
div.theme-classic {background: #114f8e;}
div.theme-purple {background: #9958dc;}
</style>
<script>
$(function()
{
var $block = $('#block<?php echo $blockID;?>');
var themePosition = "<?php echo 'themePosition-' . $blockID;?>";
$block.find('.themeSwitch .theme').click(function()
{
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop || $block.find('#guideBody').offset().top;
localStorage.setItem(themePosition, scrollTop);
selectTheme($(this).attr('data-value'));
});
})
</script>
<div class='themeSwitch'>
<?php $i = 0;?>
<?php foreach($lang->block->themes as $themeKey => $themeName):?>
<?php if($i % 4 == 0) echo "<div class='col-12 table-row theme-container'>"?>
<div class="theme col-3 theme-<?php echo $themeKey;?>" data-value="<?php echo $themeKey;?>">
<?php echo html::image($config->webRoot . "theme/default/images/guide/theme_{$themeKey}.png");?>
<?php $hidden = $app->cookie->theme == $themeKey ? '' : 'hidden';?>
<?php $active = $app->cookie->theme == $themeKey ? 'active' : '';?>
<div class='theme-text text-center <?php echo $active;?>'><i class="icon icon-check-circle <?php echo $hidden;?>"></i><?php echo $themeName;?></div>
</div>
<?php if($i % 4 == 3) echo "</div>";?>
<?php $i ++;?>
<?php endforeach;?>
</div>
-53
View File
@@ -1,53 +0,0 @@
<style>
.vision-switch {padding: 14px;}
.vision-switch-container {display: flex;}
.vision {width: 47%; border: none; background-color: rgba(230, 240 , 255, 0.4); cursor:pointer; border-radius: 2px; padding: 10px 10px 0 10px;}
.vision + .vision {margin-left: 10px;}
.vision:hover {box-shadow: 0 0 14px rgba(0, 0, 0, 0.12);}
.vision.active {box-shadow: 0 0 0 2px #2E7FFF;}
.vision-img {width: 100%; background-size: 100% !important;}
.vision-title {font-size: 14px; color: #0B0F18; padding: 10px 8px 0;}
.vision-text {font-size: 12px; color: #5E626D; padding: 8px;}
</style>
<?php js::set('vision', $this->config->vision);?>
<div class='vision-switch'>
<p><?php echo $lang->block->visionTitle;?></p>
<div class="vision-switch-container">
<?php foreach($lang->block->visions as $vision):?>
<?php $active = $this->config->vision == $vision->key ? 'active' : '';?>
<a href="<?php echo $this->createLink('my', 'ajaxSwitchVision', "vision=$vision->key");?>" data-type="ajax" class='vision <?php echo $active;?>' data-value="<?php echo $vision->key;?>">
<div class='vision-img'>
<?php if(common::checkNotCN() and $vision->key == 'rnd'):?>
<?php echo html::image($config->webRoot . "theme/default/images/guide/vision_{$vision->key}_en.png");?>
<?php elseif(!common::checkNotCN() and $vision->key == 'rnd'):?>
<?php echo html::image($config->webRoot . "theme/default/images/guide/vision_{$vision->key}_cn.png");?>
<?php else:?>
<?php echo html::image($config->webRoot . "theme/default/images/guide/vision_{$vision->key}.png");?>
<?php endif;?>
</div>
<div class='vision-title'><?php echo $vision->title;?></div>
<div class='vision-text'><?php echo $vision->text;?></div>
</a>
<?php endforeach;?>
</div>
</div>
<script>
$(function()
{
var $block = $('#block<?php echo $blockID;?>');
var visionPosition = "<?php echo 'visionPosition-' . $blockID;?>";
$block.find('.vision-switch .vision').click(function(e)
{
var selectedVision = $(this).data('value');
if (vision == selectedVision)
{
e.preventDefault();
return false;
}
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop || $block.find('#guideBody').offset().top;
localStorage.setItem(visionPosition, scrollTop);
});
})
</script>
@@ -1,24 +0,0 @@
<style>
.block-waterfallestimate table{margin-bottom: 15px}
.block-waterfallestimate table th{width: 135px !important}
</style>
<table class='table table-data'>
<tr>
<th><?php echo $lang->durationestimation->people;?></th>
<td><?php echo $people ? $people : 0;?></td>
<th><?php echo $lang->workestimation->duration;?></th>
<td><?php echo zget($budget, 'duration', 0);?></td>
</tr>
<tr>
<th><?php echo $lang->durationestimation->members;?></th>
<td><?php echo $members;?></td>
<th><?php echo $lang->workestimation->consumed;?></th>
<td><?php echo $consumed ? $consumed : 0;?></td>
</tr>
<tr>
<th><?php echo $lang->workestimation->totalLaborCost;?></th>
<td><?php echo '¥' . zget($budget, 'totalLaborCost', 0);?></td>
<th><?php echo $lang->block->remain;?></th>
<td><?php echo $totalLeft ? $totalLeft : 0;?></td>
</tr>
</table>
@@ -1,216 +0,0 @@
<?php $waterfallGanttID = uniqid('wg'); ?>
<div class="panel-body">
<div id='<?php echo $waterfallGanttID; ?>'>
<?php if(count($products) >= 2): ?>
<?php echo html::select($waterfallGanttID . 'ProductID', $products, $productID, "class='form-control chosen'");?>
<span class='gantt-product-tips'><i>* </i><?php echo $lang->block->selectProduct;?></span>
<?php endif;?>
<?php if(empty($plans['data'])): ?>
<div class='empty-tip'><?php echo $lang->programplan->noData;?></div>
<?php else:?>
<div class='gantt clearfix'>
<div class='gantt-plans pull-left'></div>
<div class='gantt-container scrollbar-hover'>
<div class='gantt-canvas'>
<div class='gantt-today'><div><?php echo $lang->programplan->today; ?></div></div>
</div>
</div>
</div>
<?php endif;?>
</div>
<style>
<?php if(common::checkNotCN()):?>
.gantt-product-tips {position: absolute; top: -32px; left: 280px; opacity: 0.5;}
<?php else:?>
.gantt-product-tips {position: absolute; top: -32px; left: 240px; opacity: 0.5;}
<?php endif;?>
.gantt-product-tips i {vertical-align: middle;}
.block-waterfallgantt > .panel-body {overflow: visible!important}
#<?php echo $waterfallGanttID; ?> {position: relative}
#<?php echo $waterfallGanttID; ?>ProductID_chosen {position: absolute; top: -39px; left: 120px; width: 150px!important}
[lang="zh-cn"] #<?php echo $waterfallGanttID; ?>ProductID_chosen {left: 85px}
.gantt-plans {padding: 20px 0 22px; max-height: 380px; overflow: hidden;}
.gantt-plan {width: 70px; height: 50px; line-height: 50px}
.gantt-plan div {overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.gantt-container {position: absolute; left: 100px; top: 0; right: 0; bottom: -10px; overflow: auto; padding-top: 20px; max-height: 380px;}
.gantt-canvas {border: 1px solid #dddee4; border-style: solid dotted; position: relative}
.gantt-row {height: 50px; position: relative; z-index: 1}
.gantt-row:hover {background-color: rgba(0,0,0,.05)}
.gantt-bar {height: 15px; background: #dddee4; position: absolute; left: 0; top: 10px}
.gantt-bar-progress {height: 15px; margin-bottom: 5px}
.gantt-task-info {white-space: nowrap; width: 100%; overflow: visible}
.gantt-col {position: absolute; z-index: 0; top: 0; border-right: 1px dotted #dddee4}
.gantt-col-time {position: absolute; top: -18px; left: 0}
.gantt-today {position: absolute; top: -16px; left: 0; bottom: 0; border-left: #00da88 dotted 1px;}
.gantt-today > div {position: absolute; top: 0; left: 0; font-size: 12px; line-height: 14px; padding: 0 3px; background: #00da88; color: #fff; white-space: nowrap; z-index: 10;}
</style>
<script>
(function() {
function initWaterfallGanttBlock()
{
/* Init product select control */
var $waterfallGanttProductID = $('#<?php echo $waterfallGanttID; ?>ProductID');
$waterfallGanttProductID.on('change', function()
{
$.get(createLink('product', 'ajaxSetState', 'productID=' + $waterfallGanttProductID.val()), function()
{
refreshBlock($waterfallGanttProductID.closest('.panel'));
});
});
<?php if(!empty($plans['data'])): ?>
var ganttData = <?php echo json_encode($plans['data']); ?>;
if(!ganttData) return;
var plans = [];
var tasks = [];
var plansMap = {};
var startDatetime = Number.MAX_SAFE_INTEGER;
var endDatetime = 0;
var minTimeGap = Number.MAX_SAFE_INTEGER;
var $gantt = $('#<?php echo $waterfallGanttID; ?>');
var ONE_DAY = 24 * 3600 * 1000;
var TIME_GAP_STEP = 7;
var MIN_COL_WIDTH = 60;
$.each(ganttData, function(index, item)
{
plansMap[item.id] = item;
if(item.type == 'plan' && item.parent == '0')
{
item.startDatetime = createDatetime(item.start_date);
item.endDatetime = createDatetime(item.deadline);
startDatetime = Math.min(startDatetime, item.startDatetime);
endDatetime = Math.max(endDatetime, item.endDatetime);
minTimeGap = Math.min(minTimeGap, endDatetime - startDatetime);
item.tasks = [];
item.completeTasks = [];
item.progress = Number.parseFloat(item.taskProgress.replace('%', ''), 10);
plans.push(item);
}
else if(item.type === 'task')
{
item.progress = Number.parseFloat(item.taskProgress.replace('%', ''), 10);
tasks.push(item);
}
});
$.each(tasks, function(index, task)
{
var plan = plansMap[task.parent];
if(typeof(plan) == 'undefined') return;
while(plan.parent > 0)
{
plan = plansMap[plan.parent];
if(typeof(plan) != 'object') return;
}
if(task.progress === 100) plan.completeTasks.push(task);
plan.tasks.push(task);
});
var $plans = $gantt.find('.gantt-plans');
var $ganttContainer = $gantt.find('.gantt-container');
var $ganttCanvas = $gantt.find('.gantt-canvas');
var themeColor = $.getThemeColor('primary');
var canvasHeight = plans.length * 50 + 10;
var days = Math.ceil((endDatetime - startDatetime) / ONE_DAY);
minTimeGap = Math.max(1, Math.ceil(minTimeGap / ONE_DAY));
/* Update gantt plans and bars */
$.each(plans, function(index, plan)
{
var $plan = $('<div class="gantt-plan"></div>');
$plan.append('<div class="strong" title="' + plan.name + '">' + plan.text + '</div>');
$plans.append($plan);
var $bar = $('<div class="gantt-bar"></div>');
$('<div class="gantt-bar-progress bg-primary"></div>').css(
{
width: plan.progress + '%',
background: themeColor,
}).appendTo($bar);
$bar.append('<div class="gantt-task-info text-muted small"><?php echo $lang->programplan->task;?> ' + plan.completeTasks.length + '/' + plan.tasks.length + '</div>').attr('title', $.zui.formatDate(plan.startDatetime, 'yyyy-MM-dd') + '~' + $.zui.formatDate(plan.endDatetime, 'yyyy-MM-dd'));
var $row = $('<div class="gantt-row" data-id="' + plan.id + '"></div>').append($bar);
$ganttCanvas.append($row);
});
/* Layout gantt container */
$ganttContainer.css('left', $plans.width() + 15);
$ganttCanvas.css('height', canvasHeight);
/* Layout gantt */
layoutGantt();
$(window).on('resize', layoutGantt);
setTimeout(layoutGantt, 100);
/* Bind events */
$ganttContainer.on('scroll', function()
{
$plans.scrollTop($ganttContainer.scrollTop());
});
/**
* Layout gantt
*
* @return {void}
*/
function layoutGantt()
{
var minWidth = $ganttContainer.width();
var timeGap = minTimeGap < TIME_GAP_STEP ? minTimeGap : Math.floor(minTimeGap / TIME_GAP_STEP) * TIME_GAP_STEP;
var colsCount = Math.ceil(days / timeGap);
var canvasWidth = Math.max(minWidth, colsCount * MIN_COL_WIDTH);
var colWidth = Math.floor(canvasWidth / colsCount);
var pxPerMs = colWidth / (timeGap * ONE_DAY);
$ganttCanvas.css('width', canvasWidth).find('.gantt-col').remove();
for (var i = 0; i < colsCount; ++i)
{
var $col = $('<div class="gantt-col"></div>');
$col.css(
{
left: i * colWidth,
width: colWidth,
height: canvasHeight
});
var colTime = startDatetime + i * timeGap * ONE_DAY;
$col.append('<div class="gantt-col-time text-muted small">' + $.zui.formatDate(colTime, 'MM/dd') + '</div>');
$ganttCanvas.append($col);
}
$.each(plans, function(index, plan)
{
var $planRow = $gantt.find('.gantt-row[data-id="' + plan.id + '"]');
$planRow.find('.gantt-bar').css(
{
left: Math.floor((plan.startDatetime - startDatetime) * pxPerMs),
width: Math.floor((plan.endDatetime - plan.startDatetime) * pxPerMs)
});
});
$gantt.find('.gantt-today').css('left', (Date.now() - startDatetime) * pxPerMs);
}
/**
* Create date from string
*
* @param {String} dateStr like '2020-08-02'
* @return {Number} Date timestramp
*/
function createDatetime(dateStr)
{
dateStr = dateStr.split('-');
var year = Number.parseInt(dateStr[0].length > 3 ? dateStr[0] : dateStr[2], 10);
var month = Number.parseInt(dateStr[1], 10);
var day = Number.parseInt(dateStr[2].length > 3 ? dateStr[0] : dateStr[2], 10);
return new Date(year, month - 1, day).getTime();
}
<?php endif;?>
}
initWaterfallGanttBlock();
}());
</script>
</div>
@@ -1,50 +0,0 @@
<?php if(empty($issues)): ?>
<div class='empty-tip'><?php echo $lang->block->emptyTip;?></div>
<?php else:?>
<style>
.block-issues .c-id {width: 55px;}
.block-issues .c-status {width: 80px;}
.block-issues.block-sm .c-status {text-align: center;}
.c-assignedTo {width: 100px; padding:0px !important;text-align:center;}
.c-severity, .c-pri {text-align:center;}
</style>
<div class='panel-body has-table scrollbar-hover'>
<table class='table table-borderless table-hover table-fixed table-fixed-head tablesorter block-issues <?php if(!$longBlock) echo 'block-sm';?>'>
<thead>
<tr>
<th class='c-id'><?php echo $lang->idAB;?></th>
<th class='c-status'><?php echo $lang->issue->type;?></th>
<th class='c-name'> <?php echo $lang->issue->title;?></th>
<?php if($longBlock):?>
<th class='c-status'><?php echo $lang->issue->severity;?></th>
<th class='c-number'><?php echo $lang->issue->pri;?></th>
<th class='c-user'><?php echo $lang->issue->owner;?></th>
<th class='c-assignedTo'><?php echo $lang->issue->assignedTo;?></th>
<?php endif;?>
<th class='c-status'><?php echo $lang->issue->status;?></th>
</tr>
</thead>
<tbody>
<?php foreach($issues as $issue):?>
<?php
$viewLink = $this->createLink('issue', 'view', "issueID={$issue->id}");
?>
<tr>
<td class='c-id-xs'><?php echo sprintf('%03d', $issue->id);?></td>
<td class='c-type'><?php echo zget($lang->issue->typeList, $issue->type);?></td>
<td class='c-name' title='<?php echo $issue->title?>'><?php echo html::a($viewLink, $issue->title);?></td>
<?php if($longBlock):?>
<td class='c-severity'><?php echo zget($lang->issue->severityList, $issue->severity, $issue->severity)?></td>
<td class='c-pri'><?php echo zget($lang->issue->priList, $issue->pri, $issue->pri)?></td>
<td><?php echo zget($users, $issue->owner, $issue->owner)?></td>
<td class='c-assignedTo'><?php echo zget($users, $issue->assignedTo, $issue->assignedTo)?></td>
<?php endif;?>
<td class='c-status'>
<span class="status-issue status-<?php echo $issue->status?>"><?php echo zget($lang->issue->statusList, $issue->status);?></span>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endif;?>

Some files were not shown because too many files have changed in this diff Show More