Merge remote-tracking branch 'upstream/master'

pull/678/head
wangbin13 2022-03-05 18:29:59 +08:00
commit 0d0582c37c
142 changed files with 172938 additions and 236012 deletions

View File

@ -1,95 +1,135 @@
FROM golang:1.11.4-alpine3.8 AS build FROM amd64/golang:1.13 AS build
#新增 GLIBC ARG TAG=0.0.1
ENV GLIBC_VERSION "2.28-r0"
# Download and install glibc # 编译-环境变量
RUN apk add --update && \ ENV GO111MODULE=on
apk add --no-cache --upgrade \ ENV GOPROXY=https://goproxy.cn,direct
ca-certificates \ ENV CGO_ENABLED=1
gcc \ ENV GOARCH=amd64
g++ \ ENV GOOS=linux
make \
curl \
git
RUN curl -Lo /etc/apk/keys/sgerrand.rsa.pub "https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub" && \
curl -Lo /var/glibc.apk "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-${GLIBC_VERSION}.apk" && \
curl -Lo /var/glibc-bin.apk "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-bin-${GLIBC_VERSION}.apk" && \
apk add /var/glibc-bin.apk /var/glibc.apk && \
/usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib && \
echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf
#掛載 calibre 最新3.x
ENV LD_LIBRARY_PATH $LD_LIBRARY_PATH:/opt/calibre/lib
ENV PATH $PATH:/opt/calibre/bin
RUN curl -Lo /var/linux-installer.py https://download.calibre-ebook.com/linux-installer.py
#RUN mkdir -p /go/src/github.com/lifei6671/ && cd /go/src/github.com/lifei6671/ && git clone https://github.com/mindoc-org/mindoc.git && cd mindoc
# 工作目录
ADD . /go/src/github.com/mindoc-org/mindoc ADD . /go/src/github.com/mindoc-org/mindoc
WORKDIR /go/src/github.com/mindoc-org/mindoc WORKDIR /go/src/github.com/mindoc-org/mindoc
RUN go get -u github.com/golang/dep/cmd/dep && dep ensure && \ # 编译
CGO_ENABLE=1 go build -v -a -o mindoc_linux_amd64 -ldflags="-w -s -X main.VERSION=$TAG -X 'main.BUILD_TIME=`date`' -X 'main.GO_VERSION=`go version`'" && \ RUN go env
rm -rf commands controllers models modules routers tasks vendor docs search data utils graphics .git Godeps uploads/* .gitignore .travis.yml Dockerfile gide.yaml LICENSE main.go README.md conf/enumerate.go conf/mail.go install.lock simsun.ttc RUN go mod tidy -v
RUN go build -o mindoc_linux_amd64 -ldflags "-w -s -X 'main.VERSION=$TAG' -X 'main.BUILD_TIME=`date`' -X 'main.GO_VERSION=`go version`'"
RUN cp conf/app.conf.example conf/app.conf
# 清理不需要的文件
RUN rm appveyor.yml docker-compose.yml Dockerfile .travis.yml .gitattributes .gitignore go.mod go.sum main.go README.md simsun.ttc start.sh conf/*.go
RUN rm -rf cache commands controllers converter .git .github graphics mail models routers utils
ADD start.sh /go/src/github.com/mindoc-org/mindoc # 测试编译的mindoc是否ok
RUN ./mindoc_linux_amd64 version
# 必要的文件复制
ADD simsun.ttc /usr/share/fonts/win/ ADD simsun.ttc /usr/share/fonts/win/
ADD start.sh /go/src/github.com/mindoc-org/mindoc
FROM alpine:latest
LABEL maintainer="longfei6671@163.com" # Ubuntu 20.04
FROM ubuntu:focal
RUN apk add --update && \ # 切换默认shell为bash
apk add --no-cache --upgrade \ SHELL ["/bin/bash", "-c"]
tzdata \
mesa-gl \
python \
qt5-qtbase-x11 \
xdg-utils \
libxrender \
libxcomposite \
xz \
imagemagick \
imagemagick-dev \
msttcorefonts-installer \
fontconfig && \
update-ms-fonts && \
fc-cache -f
COPY --from=build /var/glibc.apk .
COPY --from=build /var/glibc-bin.apk .
COPY --from=build /etc/apk/keys/sgerrand.rsa.pub /etc/apk/keys/sgerrand.rsa.pub
COPY --from=build /var/linux-installer.py .
COPY --from=build /usr/share/fonts/win/simsun.ttc /usr/share/fonts/win/ COPY --from=build /usr/share/fonts/win/simsun.ttc /usr/share/fonts/win/
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=build /go/src/github.com/mindoc-org/mindoc /mindoc COPY --from=build /go/src/github.com/mindoc-org/mindoc /mindoc
RUN apk add glibc-bin.apk glibc.apk && \
/usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib && \
echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf && \
rm -rf glibc.apk glibc-bin.apk /var/cache/apk/* && \
chmod a+r /usr/share/fonts/win/simsun.ttc
ENV LD_LIBRARY_PATH $LD_LIBRARY_PATH:/opt/calibre/lib
ENV PATH $PATH:/opt/calibre/bin
RUN cat linux-installer.py | python -c "import sys; main=lambda x,y:sys.stderr.write('Download failed\n'); exec(sys.stdin.read()); main(install_dir='/opt', isolated=True)" && \
rm -rf /tmp/* linux-installer.py
WORKDIR /mindoc WORKDIR /mindoc
RUN chmod a+r /usr/share/fonts/win/simsun.ttc
# 备份原有源
RUN mv /etc/apt/sources.list /etc/apt/sources.list-backup
# 最小化源缩短apt update时间(ca-certificates必须先安装才支持换tsinghua源)
RUN echo 'deb http://archive.ubuntu.com/ubuntu/ focal main restricted' > /etc/apt/sources.list
RUN apt-get update
RUN apt install -y ca-certificates
# 更换aliyun源(echo多行内容不能以#开头会被docker误判为注释行所以采用\n#开头)
RUN echo $'\
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\
\n# deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse\n\
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\
\n# deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse\n\
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\
\n# deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse\n\
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\
\n# deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse\n\
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse\
\n# deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse'\
> /etc/apt/sources.list
# 时区设置 # 更新软件包信息
RUN apt-get update
# 安装必要的系统工具
RUN apt install -y apt-transport-https ca-certificates curl wget xz-utils
# 时区设置(如果不设置, calibre依赖的tzdata在安装过程中会要求选择时区)
ENV TZ=Asia/Shanghai ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# tzdata的前端类型默认为readlineShell情况下或dialog支持GUI的情况下
ARG DEBIAN_FRONTEND=noninteractive
# 安装时区信息
RUN apt install -y --no-install-recommends tzdata
# 重新配置tzdata软件包使得时区设置生效
RUN dpkg-reconfigure --frontend noninteractive tzdata
# 安装 calibre 依赖的包
RUN apt install -y libgl-dev libnss3-dev libxcomposite-dev libxrandr-dev libxi-dev
# 安装文泉驿字体
RUN apt install -y fonts-wqy-microhei fonts-wqy-zenhei
# 安装中文语言包
RUN apt-get install -y locales language-pack-zh-hans language-pack-zh-hans-base
# 设置默认编码
RUN locale-gen "zh_CN.UTF-8"
RUN update-locale LANG=zh_CN.UTF-8
ENV LANG=zh_CN.UTF-8
ENV LANGUAGE=zh_CN:en
ENV LC_ALL=zh_CN.UTF-8
# 安装-calibre
# RUN apt-get install -y calibre # 此种方式安装省事,但会安装很多额外不需要的软件包,导致体积过大
RUN mkdir -p /tmp/calibre-cache
# 获取最新版本号
RUN curl -s http://code.calibre-ebook.com/latest>/tmp/calibre-cache/version
# 下载最新版本
# RUN wget -O /tmp/calibre-cache/calibre-x86_64.txz -c https://download.calibre-ebook.com/`cat /tmp/calibre-cache/version`/calibre-`cat /tmp/calibre-cache/version`-x86_64.txz
# 使用 download.fastgit.org 替换 github 实现加速
RUN wget -O /tmp/calibre-cache/calibre-x86_64.txz -c https://download.fastgit.org/kovidgoyal/calibre/releases/download/v`cat /tmp/calibre-cache/version`/calibre-`cat /tmp/calibre-cache/version`-x86_64.txz
# 注: 调试阶段下载alibre-5.22.1-x86_64.txz到本地(使用 python -m http.server),加速构建
# RUN wget -O /tmp/calibre-cache/calibre-x86_64.txz -c http://10.96.8.252:8000/calibre-5.22.1-x86_64.txz
# 解压
RUN mkdir -p /opt/calibre
# RUN tar --extract --file=/tmp/calibre-cache/calibre-x86_64.txz --directory /opt/calibre
RUN tar xJof /tmp/calibre-cache/calibre-x86_64.txz -C /opt/calibre
ENV PATH=$PATH:/opt/calibre
# 设置calibre相关环境变量
ENV QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox"
ENV QT_QPA_PLATFORM='offscreen'
# 测试 calibre 可正常使用
RUN ebook-convert --version
# 清理calibre缓存
RUN rm -rf /tmp/calibre-cache
# refer: https://docs.docker.com/engine/reference/builder/#volume
# 数据同步目录
VOLUME /mindoc-sync-host
# refer: https://docs.docker.com/engine/reference/builder/#expose
EXPOSE 8181/tcp
# 如果配置文件不存在就复制
RUN cp --no-clobber /mindoc/conf/app.conf.example /mindoc/conf/app.conf
ENV ZONEINFO=/mindoc/lib/time/zoneinfo.zip ENV ZONEINFO=/mindoc/lib/time/zoneinfo.zip
RUN chmod +x start.sh RUN chmod +x /mindoc/start.sh
CMD ["./start.sh"] ENTRYPOINT ["/bin/bash", "/mindoc/start.sh"]
# https://docs.docker.com/engine/reference/commandline/build/#options
# docker build --progress plain --rm --build-arg TAG=2.0.1 --tag gsw945/mindoc:2.0.1 .
# https://docs.docker.com/engine/reference/commandline/run/#options
# set MINDOC=//d/mindoc # windows
# export MINDOC=/home/ubuntu/mindoc-docker # linux
# docker run --rm -it -p 8181:8181 -v "%MINDOC%":"/mindoc-sync-host" --name mindoc -e MINDOC_ENABLE_EXPORT=true -d gsw945/mindoc:2.0.1

View File

@ -11,7 +11,9 @@ MinDoc 的前身是 [SmartWiki](https://github.com/lifei6671/SmartWiki) 文档
可以用来储存日常接口文档,数据库字典,手册说明等文档。内置项目管理,用户管理,权限管理等功能,能够满足大部分中小团队的文档管理需求。 可以用来储存日常接口文档,数据库字典,手册说明等文档。内置项目管理,用户管理,权限管理等功能,能够满足大部分中小团队的文档管理需求。
演示站点: [https://www.iminho.me/wiki/](https://www.iminho.me/wiki/) ##### 演示站点&文档:
- https://www.iminho.me/wiki/docs/mindoc/
- https://doc.gsw945.com/docs/mindoc-docs/
--- ---
@ -24,6 +26,11 @@ MinDoc 的前身是 [SmartWiki](https://github.com/lifei6671/SmartWiki) 文档
遇到问题请提 [Issues](https://github.com/mindoc-org/mindoc/issues )欢迎使用者和贡献者加入QQ群 `1051164153` 遇到问题请提 [Issues](https://github.com/mindoc-org/mindoc/issues )欢迎使用者和贡献者加入QQ群 `1051164153`
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=bHFR7P3Qp1nsSPbsTw4KN_ZpFLUAblIU&jump_from=webapi"><img border="0" src="https://pub.idqqimg.com/wpa/images/group.png" alt="MinDoc使用&amp;开发交流群" title="MinDoc使用&amp;开发交流群"></a> <a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=bHFR7P3Qp1nsSPbsTw4KN_ZpFLUAblIU&jump_from=webapi"><img border="0" src="https://pub.idqqimg.com/wpa/images/group.png" alt="MinDoc使用&amp;开发交流群" title="MinDoc使用&amp;开发交流群"></a>
对开发感兴趣请关注 [Development](https://github.com/mindoc-org/mindoc/projects/1):
- [Todo List](https://github.com/mindoc-org/mindoc/projects/1#column-13554511)
- [Work in progress](https://github.com/mindoc-org/mindoc/projects/1#column-13554512)
- [Review in progress](https://github.com/mindoc-org/mindoc/projects/1#column-13554513)
--- ---
# 安装与使用 # 安装与使用
@ -35,7 +42,9 @@ MinDoc 的前身是 [SmartWiki](https://github.com/lifei6671/SmartWiki) 文档
对于没有Golang使用经验的用户可以从 [https://github.com/mindoc-org/mindoc/releases](https://github.com/mindoc-org/mindoc/releases) 这里下载编译完的程序。 对于没有Golang使用经验的用户可以从 [https://github.com/mindoc-org/mindoc/releases](https://github.com/mindoc-org/mindoc/releases) 这里下载编译完的程序。
如果有Golang开发经验建议通过编译安装要求golang版本不小于1.13(需支持`CGO`和`go mod`)。 如果有Golang开发经验建议通过编译安装要求golang版本不小于1.13(需支持`CGO`和`go mod`)。
> 注意: CentOS7上GLibC版本低需要源码编译, 编译好的二进制文件无法运行。
## 常规编译
```bash ```bash
# 克隆源码 # 克隆源码
git clone https://github.com/mindoc-org/mindoc.git git clone https://github.com/mindoc-org/mindoc.git
@ -47,7 +56,6 @@ go build -ldflags "-w"
./mindoc install ./mindoc install
# 执行 # 执行
./mindoc ./mindoc
``` ```
MinDoc 如果使用MySQL储存数据则编码必须是`utf8mb4_general_ci`。请在安装前,把数据库配置填充到项目目录下的 `conf/app.conf` 中。 MinDoc 如果使用MySQL储存数据则编码必须是`utf8mb4_general_ci`。请在安装前,把数据库配置填充到项目目录下的 `conf/app.conf` 中。
@ -58,6 +66,31 @@ MinDoc 如果使用MySQL储存数据则编码必须是`utf8mb4_general_ci`。
**默认程序会自动初始化一个超级管理员用户admin 密码123456 。请登录后重新设置密码。** **默认程序会自动初始化一个超级管理员用户admin 密码123456 。请登录后重新设置密码。**
## Linux系统中不依赖gLibC的编译方式
### 安装 musl-gcc
```bash
wget -c http://www.musl-libc.org/releases/musl-1.2.2.tar.gz
tar -xvf musl-1.2.2.tar.gz
cd musl-1.2.2
./configure
make
sudo make install
```
### 使用 musl-gcc 编译 mindoc
```bash
go mod tidy -v
export GOARCH=amd64
export GOOS=linux
# 设置使用musl-gcc
export CC=/usr/local/musl/bin/musl-gcc
# 设置版本
export TRAVIS_TAG=temp-musl-v`date +%y%m%d`
go build -o mindoc_linux_musl_amd64 --ldflags="-linkmode external -extldflags '-static' -w -X 'github.com/mindoc-org/mindoc/conf.VERSION=$TRAVIS_TAG' -X 'github.com/mindoc-org/mindoc/conf.BUILD_TIME=`date`' -X 'github.com/mindoc-org/mindoc/conf.GO_VERSION=`go version`'"
# 验证
./mindoc_linux_amd64 version
```
```bash ```bash
@ -80,26 +113,42 @@ mail_expired=30
# 使用Docker部署 # 使用Docker部署
如果是Docker用户可参考项目内置的Dockerfile文件编译镜像。 如果是Docker用户可参考项目内置的Dockerfile文件自行编译镜像(编译命令见Dockerfile文件底部注释仅供参考)。
在启动镜像时需要提供如下的环境变量:
在启动镜像时需要提供如下的常用环境变量(全部支持的环境变量请参考: [`conf/app.conf.example`](https://github.com/mindoc-org/mindoc/blob/master/conf/app.conf.example))
```ini ```ini
DB_ADAPTER 指定 DB DB_ADAPTER 指定DB类型(默认为sqlite)
MYSQL_PORT_3306_TCP_ADDR MySQL地址 MYSQL_PORT_3306_TCP_ADDR MySQL地址
MYSQL_PORT_3306_TCP_PORT MySQL端口号 MYSQL_PORT_3306_TCP_PORT MySQL端口号
MYSQL_INSTANCE_NAME MySQL数据库名称 MYSQL_INSTANCE_NAME MySQL数据库名称
MYSQL_USERNAME MySQL账号 MYSQL_USERNAME MySQL账号
MYSQL_PASSWORD MySQL密码 MYSQL_PASSWORD MySQL密码
HTTP_PORT 程序监听的端口号 HTTP_PORT 程序监听的端口号
MINDOC_ENABLE_EXPORT 开启导出(默认为false)
``` ```
举个栗子 ### 举个栗子-当前(公开)镜像(信息页面: https://cr.console.aliyun.com/images/cn-hangzhou/mindoc-org/mindoc/detail , 需要登录阿里云账号才可访问列表)
##### Windows
```bash
set MINDOC=//d/mindoc
docker run -it --name=mindoc --restart=always -v "%MINDOC%":"/mindoc-sync-host" -p 8181:8181 -e MINDOC_ENABLE_EXPORT=true -d registry.cn-hangzhou.aliyuncs.com/mindoc-org/mindoc:v2.1-beta.5
```
##### Linux、Mac
```bash
export MINDOC=/home/ubuntu/mindoc-docker
docker run -it --name=mindoc --restart=always -v "${MINDOC}":"/mindoc-sync-host" -p 8181:8181 -e MINDOC_ENABLE_EXPORT=true -d registry.cn-hangzhou.aliyuncs.com/mindoc-org/mindoc:v2.1-beta.5
```
##### 举个栗子-更多环境变量示例(镜像已过期,仅供参考,请以当前镜像为准)
```bash ```bash
# TODO: 新版docker镜像将使用阿里云配置中
docker run -p 8181:8181 --name mindoc -e DB_ADAPTER=mysql -e MYSQL_PORT_3306_TCP_ADDR=10.xxx.xxx.xxx -e MYSQL_PORT_3306_TCP_PORT=3306 -e MYSQL_INSTANCE_NAME=mindoc -e MYSQL_USERNAME=root -e MYSQL_PASSWORD=123456 -e httpport=8181 -d daocloud.io/lifei6671/mindoc:latest docker run -p 8181:8181 --name mindoc -e DB_ADAPTER=mysql -e MYSQL_PORT_3306_TCP_ADDR=10.xxx.xxx.xxx -e MYSQL_PORT_3306_TCP_PORT=3306 -e MYSQL_INSTANCE_NAME=mindoc -e MYSQL_USERNAME=root -e MYSQL_PASSWORD=123456 -e httpport=8181 -d daocloud.io/lifei6671/mindoc:latest
``` ```
#### dockerfile内容参考
- [无需代理直接加速各种 GitHub 资源拉取 | 国内镜像赋能 | 助力开发](https://blog.frytea.com/archives/504/)
- [阿里云 - Ubuntu 镜像](https://developer.aliyun.com/mirror/ubuntu)
### docker-compose 一键安装 ### docker-compose 一键安装
1. 修改配置文件 1. 修改配置文件
@ -176,7 +225,7 @@ docker run -p 8181:8181 --name mindoc -e DB_ADAPTER=mysql -e MYSQL_PORT_3306_TCP
- MySQL 5.6 - MySQL 5.6
- [editor.md](https://github.com/pandao/editor.md) Markdown 编辑器 - [editor.md](https://github.com/pandao/editor.md) Markdown 编辑器
- [Bootstrap](https://github.com/twbs/bootstrap) 3.2 - [Bootstrap](https://github.com/twbs/bootstrap) 3.2
- jQuery(https://github.com/jquery/jquery) 库 - [jQuery](https://github.com/jquery/jquery) 库
- [WebUploader](https://github.com/fex-team/webuploader) 文件上传框架 - [WebUploader](https://github.com/fex-team/webuploader) 文件上传框架
- [NProgress](https://github.com/rstacruz/nprogress) 库 - [NProgress](https://github.com/rstacruz/nprogress) 库
- [jsTree](https://github.com/vakata/jstree) 树状结构库 - [jsTree](https://github.com/vakata/jstree) 树状结构库
@ -187,6 +236,10 @@ docker run -p 8181:8181 --name mindoc -e DB_ADAPTER=mysql -e MYSQL_PORT_3306_TCP
- ~~to-markdown~~[Turndown](https://github.com/domchristie/turndown) HTML转Markdown库 - ~~to-markdown~~[Turndown](https://github.com/domchristie/turndown) HTML转Markdown库
- ~~quill 富文本编辑器~~ - ~~quill 富文本编辑器~~
- [wangEditor](https://github.com/wangeditor-team/wangEditor) 富文本编辑器 - [wangEditor](https://github.com/wangeditor-team/wangEditor) 富文本编辑器
- 参考
- [wangEditor v4.7 富文本编辑器教程](https://www.bookstack.cn/books/wangeditor-4.7-zh)
- [扩展菜单注册太过繁琐 #2493](https://github.com/wangeditor-team/wangEditor/issues/2493)
- 工具: `https://babeljs.io/repl` + `@babel/plugin-transform-classes`
- [Vue.js](https://github.com/vuejs/vue) 框架 - [Vue.js](https://github.com/vuejs/vue) 框架
@ -210,6 +263,3 @@ docker run -p 8181:8181 --name mindoc -e DB_ADAPTER=mysql -e MYSQL_PORT_3306_TCP
一个不纯粹的PHPer一个不自由的 gopher 。 一个不纯粹的PHPer一个不自由的 gopher 。
# 支持 MinDoc
![支付宝](https://raw.githubusercontent.com/lifei6671/mindoc/master/static/images/alipay.png) ![微信支付](https://raw.githubusercontent.com/lifei6671/mindoc/master/static/images/weixin.png)

2
cache/cache.go vendored
View File

@ -7,8 +7,8 @@ import (
"errors" "errors"
"time" "time"
"github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/client/cache" "github.com/beego/beego/v2/client/cache"
"github.com/beego/beego/v2/core/logs"
) )
var bm cache.Cache var bm cache.Cache

View File

@ -8,6 +8,7 @@ import (
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
@ -22,6 +23,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
"github.com/howeyc/fsnotify" "github.com/howeyc/fsnotify"
"github.com/lifei6671/gocaptcha" "github.com/lifei6671/gocaptcha"
"github.com/mindoc-org/mindoc/cache" "github.com/mindoc-org/mindoc/cache"
@ -262,6 +264,12 @@ func RegisterFunction() {
logs.Error("注册函数 urlfor 出错 ->", err) logs.Error("注册函数 urlfor 出错 ->", err)
os.Exit(-1) os.Exit(-1)
} }
//读取配置值(未作任何转换)
err = web.AddFuncMap("conf", conf.CONF)
if err != nil {
logs.Error("注册函数 conf 出错 ->", err)
os.Exit(-1)
}
err = web.AddFuncMap("date_format", func(t time.Time, format string) string { err = web.AddFuncMap("date_format", func(t time.Time, format string) string {
return t.Local().Format(format) return t.Local().Format(format)
}) })
@ -269,6 +277,19 @@ func RegisterFunction() {
logs.Error("注册函数 date_format 出错 ->", err) logs.Error("注册函数 date_format 出错 ->", err)
os.Exit(-1) os.Exit(-1)
} }
err = web.AddFuncMap("i18n", i18n.Tr)
if err != nil {
logs.Error("注册函数 i18n 出错 ->", err)
os.Exit(-1)
}
langs := strings.Split("en-us|zh-cn", "|")
for _, lang := range langs {
if err := i18n.SetMessage(lang, "conf/lang/"+lang+".ini"); err != nil {
logs.Error("Fail to set message file: " + err.Error())
return
}
}
} }
//解析命令 //解析命令
@ -385,15 +406,15 @@ func RegisterCache() {
var redisConfig struct { var redisConfig struct {
Conn string `json:"conn"` Conn string `json:"conn"`
Password string `json:"password"` Password string `json:"password"`
DbNum int `json:"dbNum"` DbNum string `json:"dbNum"`
} }
redisConfig.DbNum = 0 redisConfig.DbNum = "0"
redisConfig.Conn = web.AppConfig.DefaultString("cache_redis_host", "") redisConfig.Conn = web.AppConfig.DefaultString("cache_redis_host", "")
if pwd := web.AppConfig.DefaultString("cache_redis_password", ""); pwd != "" { if pwd := web.AppConfig.DefaultString("cache_redis_password", ""); pwd != "" {
redisConfig.Password = pwd redisConfig.Password = pwd
} }
if dbNum := web.AppConfig.DefaultInt("cache_redis_db", 0); dbNum > 0 { if dbNum := web.AppConfig.DefaultInt("cache_redis_db", 0); dbNum > 0 {
redisConfig.DbNum = dbNum redisConfig.DbNum = strconv.Itoa(dbNum)
} }
bc, err := json.Marshal(&redisConfig) bc, err := json.Marshal(&redisConfig)

View File

@ -10,6 +10,8 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/models" "github.com/mindoc-org/mindoc/models"
"github.com/mindoc-org/mindoc/utils" "github.com/mindoc-org/mindoc/utils"
@ -96,11 +98,16 @@ func ModifyPassword() {
func initialization() { func initialization() {
err := models.NewOption().Init() err := models.NewOption().Init()
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
} }
lang, _ := web.AppConfig.String("default_lang")
err = i18n.SetMessage(lang, "conf/lang/"+lang+".ini")
if err != nil {
panic(fmt.Errorf("initialize locale error: %s", err))
}
member, err := models.NewMember().FindByFieldFirst("account", "admin") member, err := models.NewMember().FindByFieldFirst("account", "admin")
if errors.Is(err, orm.ErrNoRows) { if errors.Is(err, orm.ErrNoRows) {
@ -122,10 +129,10 @@ func initialization() {
book := models.NewBook() book := models.NewBook()
book.MemberId = member.MemberId book.MemberId = member.MemberId
book.BookName = "MinDoc演示项目" book.BookName = i18n.Tr(lang, "init.default_proj_name") //"MinDoc演示项目"
book.Status = 0 book.Status = 0
book.ItemId = 1 book.ItemId = 1
book.Description = "这是一个MinDoc演示项目该项目是由系统初始化时自动创建。" book.Description = i18n.Tr(lang, "init.default_proj_desc") //"这是一个MinDoc演示项目该项目是由系统初始化时自动创建。"
book.CommentCount = 0 book.CommentCount = 0
book.PrivatelyOwned = 0 book.PrivatelyOwned = 0
book.CommentStatus = "closed" book.CommentStatus = "closed"
@ -137,7 +144,7 @@ func initialization() {
book.Editor = "markdown" book.Editor = "markdown"
book.Theme = "default" book.Theme = "default"
if err := book.Insert(); err != nil { if err := book.Insert(lang); err != nil {
panic("初始化项目失败 -> " + err.Error()) panic("初始化项目失败 -> " + err.Error())
} }
} else if err != nil { } else if err != nil {
@ -146,7 +153,7 @@ func initialization() {
if !models.NewItemsets().Exist(1) { if !models.NewItemsets().Exist(1) {
item := models.NewItemsets() item := models.NewItemsets()
item.ItemName = "默认项目空间" item.ItemName = i18n.Tr(lang, "init.default_proj_space") //"默认项目空间"
item.MemberId = 1 item.MemberId = 1
if err := item.Save(); err != nil { if err := item.Save(); err != nil {
panic("初始化项目空间失败 -> " + err.Error()) panic("初始化项目空间失败 -> " + err.Error())

View File

@ -14,7 +14,9 @@ import (
//检查最新版本. //检查最新版本.
func CheckUpdate() { func CheckUpdate() {
resp, err := http.Get("https://api.github.com/repos/lifei6671/mindoc/tags") fmt.Println("MinDoc current version => ", conf.VERSION)
resp, err := http.Get("https://api.github.com/repos/mindoc-org/mindoc/tags")
if err != nil { if err != nil {
logs.Error("CheckUpdate => ", err) logs.Error("CheckUpdate => ", err)
@ -33,7 +35,6 @@ func CheckUpdate() {
} }
err = json.Unmarshal(body, &result) err = json.Unmarshal(body, &result)
fmt.Println("MinDoc current version => ", conf.VERSION)
if err != nil { if err != nil {
logs.Error("CheckUpdate => ", err) logs.Error("CheckUpdate => ", err)
os.Exit(0) os.Exit(0)

View File

@ -8,6 +8,7 @@ sessionon = true
sessionname = mindoc_id sessionname = mindoc_id
copyrequestbody = true copyrequestbody = true
enablexsrf = "${MINDOC_ENABLE_XSRF||false}" enablexsrf = "${MINDOC_ENABLE_XSRF||false}"
enable_iframe = "${MINDOC_ENABLE_IFRAME||false}"
#系统完整URL(http://doc.iminho.me),如果该项不设置,会从请求头中获取地址。 #系统完整URL(http://doc.iminho.me),如果该项不设置,会从请求头中获取地址。
baseurl="${MINDOC_BASE_URL}" baseurl="${MINDOC_BASE_URL}"
@ -230,6 +231,7 @@ dingtalk_qr_key="${MINDOC_DINGTALK_QRKEY}"
# 钉钉扫码登录Secret # 钉钉扫码登录Secret
dingtalk_qr_secret="${MINDOC_DINGTALK_QRSECRET}" dingtalk_qr_secret="${MINDOC_DINGTALK_QRSECRET}"
# i18n config
default_lang="zh-cn"

View File

@ -152,6 +152,11 @@ func GetEnableExport() bool {
return web.AppConfig.DefaultBool("enable_export", true) return web.AppConfig.DefaultBool("enable_export", true)
} }
//是否启用iframe
func GetEnableIframe() bool {
return web.AppConfig.DefaultBool("enable_iframe", false)
}
//同一项目导出线程的并发数 //同一项目导出线程的并发数
func GetExportProcessNum() int { func GetExportProcessNum() int {
exportProcessNum := web.AppConfig.DefaultInt("export_process_num", 1) exportProcessNum := web.AppConfig.DefaultInt("export_process_num", 1)
@ -208,6 +213,15 @@ func IsAllowUploadFileExt(ext string) bool {
return false return false
} }
//读取配置文件值
func CONF(key string, value ...string) string {
defaultValue := ""
if len(value) > 0 {
defaultValue = value[0]
}
return web.AppConfig.DefaultString(key, defaultValue)
}
//重写生成URL的方法加上完整的域名 //重写生成URL的方法加上完整的域名
func URLFor(endpoint string, values ...interface{}) string { func URLFor(endpoint string, values ...interface{}) string {
baseUrl := web.AppConfig.DefaultString("baseurl", "") baseUrl := web.AppConfig.DefaultString("baseurl", "")

570
conf/lang/en-us.ini 100644
View File

@ -0,0 +1,570 @@
[common]
title = mindoc
home = Home
blog = Blog
project_space = Project Space
person_center = Personal Center
my_project = My Project
my_blog = My Article
manage = Management
login = Log In
logout = Log Out
official_website = Official Website
feedback = Feedback
source_code = Source Code
manual = Manual
username = Username
account = Account
email = Email
password = Password
role = Role
captcha = Captcha
keep_login = Stay signed in
forgot_password = Forgot password?
register = Create New Account
dingtalk_login = DingTalk QrCode login
account_recovery = Account recovery
new_password = New password
confirm_password = Confirm password
new_account = Create New Account
setting = Setting
save = Save
edit = Edit
delete = Delete
cancel = Cancel
create = Create
confirm_delete = Confirm
upload_lang = en
js_lang = en
remove = Remove
operate = Operate
confirm = Confirm
creator = Creator
administrator = Administrator
editor = Editor
observer = Observer
back = Back
detail = Detail
admin_right = Reading, writing and management
editor_right = Reading and writing
observer_right = Reading only
yes = yes
no = no
read = Read
generate = Generate
[init]
default_proj_name = MinDoc Demo Project
default_proj_desc = This is a MinDoc demo project, which is automatically created when the system is initialized.
default_proj_space = Default Project Space
blank_doc = Blank document
[message]
tips = Tips
page_not_existed = The page does not exist
no_permission = No enough permissions
keyword_placeholder = input keyword please...
wrong_account_password = Incorrect username or password
wrong_password = Wrong password
click_to_change = Click to change one
logging_in = logging in...
need_relogin = Relogin please.
return_account_login = Return account password login
no_account_yet = No account yet?
has_account = Already have an account?
account_empty = Account cannot be empty
email_empty = Email cannot be empty
password_empty = Password cannot be empty
captcha_empty = Captcha cannot be empty
system_error = System error
processing = Processing...
email_sent = The email is sent successfully, please log in to check it.
confirm_password_empty = Confirm password cannot be empty
incorrect_confirm_password = Incorrect confirm password
illegal_request = Illegal request
account_or_password_empty = Account or Password cannot be empty
captcha_wrong = Incorrect captcha
password_length_invalid = The password cannot be empty and must be between 6-50 characters
mail_expired = Mail has expired
captcha_expired = The verification code has expired, please try again.
user_not_existed = User does not exist
email_not_exist = Email does not exist
failed_save_password = Failed to save password
mail_service_not_enable = Mail service is not enabled
account_disable = Account has been disabled
failed_send_mail = Failed to send mail
sent_too_many_times = Send too many times, please try again later
account_not_support_retrieval = The current user does not support password retrieval
username_invalid_format = The account number can only be composed of English alphanumerics and 3-50 characters
email_invalid_format = Email format is incorrect
account_existed = Username already existed
failed_register = Registration failed, please contact the system administrator
failed_obtain_user_info = Failed to obtain identity information
dingtalk_auto_login_not_enable = DingTalk automatic login function is not enabled
failed_auto_login = Automatic login failed
no_project = No Project
item_not_exist = Item does not exist or has been deleted
item_not_exist_or_no_permit = Item does not exist or has insufficient permissions
doc_not_exist = Document does not exist or has been deleted
doc_not_exist_or_no_permit = Document does not exist or has insufficient permissions
unknown_exception = Unknown Exception
no_data = No Data
project_must_belong_space = Project must belong to a project space, and the administrator can manage it
project_title_placeholder = Title (limit in 30 words)
project_title_tips = Project name cannot exceed 100 characters
project_id_placeholder = Project ID (limit in 30 characters)
project_id_tips = The ID can only contain lowercase letters, numbers, and "-", "." and "_" symbols.
project_desc_placeholder = Project description cannot exceed 500 characters
project_public_desc = (Anyone can access)
project_private_desc = (Only participants or use tokens can access)
project_cover_desc = The Cover can be edit in the settings
confirm_delete_project = Are you sure you want to delete the project?
warning_delete_project = Remove items will not be retrieved.
project_space_empty = Please select project space
project_title_empty = Project title cannot be empty
project_id_empty = Project ID cannot be empty
project_id_existed = Project ID already in use
project_id_error = Project ID error
project_id_length = Project ID must be less than 50 characters
import_file_empty = Please select the file to upload
file_type_placeholder = Please select a Zip file
publish_to_queue = The publish task has been pushed to the task queue and will be executed soon.
team_name_empty = Team name cannot be empty
operate_failed = Operation failed
project_id_desc = The project ID is used to mark the uniqueness of the item and cannot be modified.
history_record_amount_desc = When document history enabled, this value limits the number of history saved per document
corp_id_desc = The footer that appears when the document PDF document is exported
project_desc_desc = The description information is no more than 500 characters, supports markdown syntax
project_desc_tips = The description information is no more than 500 characters
access_pass_desc = The password that needs to be provided to access the project without permit
auto_publish_desc = Enable for each save is automatically published to the latest version
enable_export_desc = Configure the exporter before you start the export, also enable the export function in the profile
enable_share_desc = Sharing is only available for public projects, and private projects do not support sharing
auto_save_desc = Save automatically every 30 seconds
confirm_into_private = Are you sure you want to make your project private?
into_private_notice = Private project need provide access token
confirm_into_public = Are you sure you want to make your project public?
into_public_notice = Public project could be visit by anyone
project_name_empty = Project name cannot be empty
success = Success
failed = Failed
receive_account_empty = The recipient account cannot be empty
receive_account_not_exist = The recipient account not exist
receive_account_disabled = The recipient account disable
cannot_preview = Cannot preview
upload_failed = Upload failed
upload_file_size_limit = The file must be less than 2MB
upload_file_empty = Upload file is empty
uploda_file_type_error = upload file type is wrong
choose_pic_file = Please select a picture
no_doc_in_cur_proj = No documents for the current project
build_doc_tree_error = There was an error building the project document tree
param_error = Parameter error
doc_name_empty = Document name cannot empty
parent_id_not_existed = Parent ID not existed
doc_not_belong_project = The document does not belong to the specified project`
attachment_not_exist = Attachment does not exist
read_file_error = Load file error
confirm_override_doc = The document has been modified, are you sure to override it?
doc_auto_published = The document was automatically published
export_func_disable = The export function is disable
cur_project_export_func_disable = The export function is disable for current Project
cur_project_not_support_md = The Markdown editor is not supported for the current project
export_failed = The export failed, check the system logs
file_converting = The document is being converted in the background, please download it later
unsupport_file_type = Unsupport file type
no_exportable_file = The project has no exportable file
gen_qrcode_failed = Generate QrCode failed
search_result_error = Search error
get_doc_his_failed = Fail to get document history
project_space_not_exist = Project space does not exist
search_placeholder = input keyword please
no_search_result = No search results!
user_exist_in_proj = The user already exists in the project
cannot_change_own_priv = Cannot change own permissions
cannot_delete_self = Cannot delete myself
cannot_handover_myself = Cannot handover to myself
confirm_delete_blog = Confirm delete blog?
delete_blog_tips = Deleted blog cannot be retrieved.
input_proj_id_pls = input project ID please
input_doc_id_pls = input document ID please
blog_digest_tips = blog digest cannot exceed 500 characters
blog_title_empty = blog title cannot be empty
blog_not_exist = Blog does not exist or has been deleted
blog_pwd_incorrect = Access password incorrect
set_pwd_pls = please set password for encrypt blog
unknown_blog_type = unknown blog type
blog_title_tips = blog title cannot exceed 200 characters
ref_doc_prj_not_existed = The Project of reference document not existed
query_ref_doc_error = query reference document failed
ref_doc_not_exist_or_no_permit = reference document does not exist or has insufficient permissions
blog_id_existed = blog id already existed
query_failed = query failed
blog_has_modified = The article has been modified
cur_user_cannot_change_pwd = The current user does not support changing the password
origin_pwd_empty = The origin password cannot be empty
new_pwd_empty = The new password cannot be empty
confirm_pwd_empty = The confirm password cannot be empty
pwd_length = Password must be between 6-18 characters
pwd_length_tips = Password must be between 6-50 characters
wrong_origin_pwd = The origin password incorrect
wrong_confirm_pwd = The confirm passwrod incorrect
same_pwd = The new password must different from the origin
pwd_encrypt_failed = Password encryption failed
team_name_empty = Team name cannot be emtpy
proj_empty = Project cannot be empty
site_name_empty = Site name cannot be empty
proj_space_name_empty = Project space name cannot be empty
proj_space_id_empty = Project space id cannot be empty
proj_space_id_tips = The project space id can only consist of letters and numbers and be between 2-100 characters
project_order_desc = Number only, sort from largest to smallest
project_label_desc = Allows up to 10 labels, use ";" to separate multiple tags
cannot_change_own_status = Cannot change own status
cannot_change_super_status = Cannot change super administrator status
cannot_change_super_priv = Cannot change super administrator permissions
[blog]
author = Author
project_list = Project List
add_project = Add Project
import_project = Import Project
delete_project = Delete Project
project_summary = Project summary
read = Read
edit = Edit
delete = Delete
copy = Copy
view = View
publish = Publish
edit_doc = Edit Document
default_cover = Default Cover
create_time = Create Time
update_time = Update Time
creator = Creator
doc_amount = Number of documents
doc_unit =
project_role = Project Role
last_edit = Last Edited
project_title = Project Title
project_id = Project ID
project_desc = Project description
public = Public
private = Private
public_project = Public Project
private_project = Private Project
summary = Summary
member = Member
team = Team
comment_amount = Number of comments
comment_unit =
member_manage = Member Manage
add_member = Add Member
administrator = Administrator
editor = Editor
observer = Observer
team_manage = Team Manage
add_team = Add Team
team_name = Team name
member_amount = Number of members
join_time = Join Time
project_setting = Project setting
handover_project = Hanover
make_public = Into Public
make_private = Into Privete
history_record_amount = Number of history records
corp_id = corp name
text_editor = editor
project_label = Project Label
project_order = Project Order
access_pass = Access Password
access_token = Access Token
auto_publish = Auto publish
enable_export = Enable Export
enable_share = Enable Share
set_first_as_home = Set the first document as the default homepage
auto_save = Auto Save
cover = Cover
click_change_cover = Click to change the cover
change_cover = Change Cover
preview = Preview
choose = Choose
upload = Upload
recipient_account = Recipient
blog_list = Blog List
add_blog = Add Blog
encryption = encryption
encrypt = encrypt
edit_blog = Edit blog
delete_blog = Delete blog
setting_blog = Setting Blog
no_blog = No Blog
blog_setting = Blog Setting
title = Blog Title
type = Blog Type
normal_blog = Normal Blog
link_blog = Link Blog
ref_doc = Reference Document
blog_status = Blog Status
blog_pwd = Blog Password
blog_digest = Blog Digest
posted_on = Posted on
modified_on = Modified on
prev = prev
next = next
no = no
edit_title = Edit Blog
[doc]
modify_doc = Modify Document
comparison = Comparison
save_merge = Save Merge
prev_diff = Previous difference
next_diff = Next difference
merge_to_left = Merge to left
merge_to_right = Merge to right
exchange_left_right = Exchange left and right
print = Print
download = Download
share = Share
share_project = Share project
share_url = Project URL
contents = Contents
search = Search
expand = Unfold
fold = Fold
close = Close
doc_publish_by = Document is Published by
doc_publish =
edit_doc = Edit Document
backward = backward
save = save
save_as_tpl = save as template
undo = undo
redo = redo
bold = bold
italic = italic
strikethrough = strikethrough
h1 = head 1
h2 = head 2
h3 = head 3
h4 = head 4
h5 = head 5
h6 = head 6
unorder_list = disorder list
order_list = order list
hline = horizontal line
link = link
ref_link = reference link
add_pic = add picture
code = code
code_block = code block
table = add table
quote = quote
gfm_task = GFM task
attachment = attachment
json_to_table = Json converted to table
template = template
close_preview = disable preview
modify_history = modify history
sidebar = sidebar
help = help
publish = publish
document = document
create_doc = create document
attachments = attachments
doc_name = Document name
doc_name_tips = Right-click on the document name of the directory to delete and modify the document name and add subordinate documents
doc_id = Document ID
doc_id_tips = The document ID can only contain lowercase letters, numbers, and "-" and "_" symbols, and can only start with a lowercase letter
expand_desc = (Expand nodes when reading)
fold_desc = (Fold nodes when reading)
empty_contents = Empty contents
empty_contents_desc = Click to expand the subordinate nodes
upload_attachment = Upload attachment
doc_history = Document history
choose_template_type = Choose template type please
normal_tpl = Normal
api_tpl = API
data_dict = Data Dictionary
custom_tpl = Custom
tpl_default_type = Default type
tpl_plain_text = Plain Text
for_api_doc = Used for API Document
code_highlight = support code highlighting
for_data_dict = Used for data dictionary
form_support = Form Support
any_type_doc = Support any type of document
as_global_tpl = Can be set as a global template
tpl_name = Template name
tpl_type = Template type
creator = Creator
create_time = Create time
operation = Operation
global_tpl = Global
global_tpl_desc = (Any Project is available)
project_tpl = Project
project_tpl_desc = (Only the current Project is available)
insert = insert
uploading = Uploading
his_ver = Historic version
update_time = Update time
updater = Updater
version = Version
delete = Delete
recover = Recover
merge = Merge
comparison_title = Document comparison [the left is the historical document, the right is the current document, please merge the documents to the right]
font_size = font size
underscore = underscore
right_intent = right intent
left_intent = left intent
subscript = subscript
superscript = superscript
clean_format = clear format
add_video = add video
formula = formula
font_color = font color
bg_color = background color
input_pwd = input password please
read_pwd = read password
commit = commit
ft_author = Author
ft_last_editor = Last editor
ft_create_time = Create time
ft_update_time = Update time
view_count = Number of views
[project]
prj_space_list = Project Space List
prj_space_list_of = List of Project Space %s
search_title = Show Items of Project Space "%s"
author = Author
no_project = No Project
prj_amount = Project Count
creator = Creator
create_time = Create time
no_project_space = No Project Space
[search]
title = Search
search_title = Show search result for %s
doc = document
prj = project
blog = blog
from_proj = from project
from_blog = from blog
author = author
update_time = update time
no_result = No search result
[page]
first = first
last = last
prev = prev
next = next
[uc]
user_center = User Center
base_info = Basic Info
change_pwd = Change Password
username = Username
nickname = Nickname
realname = Real name
email = Email
mobile = Mobile
description = Description
description_tips = Description cannot exceed 500 characters
avatar = Avatar
change_avatar = Change avatar
password = Password
origin_pwd = Origin password
new_pwd = New password
confirm_pwd = Confirm password
role = Role
type = Type
status = Status
super_admin = Super administrator
admin = Administrator
user = User
normal = Normal
disable = Disable
enable = Enable
create_user = Create User
edit_user = Edit User
pwd_tips = Please leave it blank if you do not change the password, only local users can change the password
[mgr]
dashboard_menu = Dashboard
user_menu = User
team_menu = Team
project_menu = Project
project_space_menu = Project Space
comment_menu = Comment
config_menu = Configure
attachment_menu = Attachment
label_menu = Label
dashboard_mgr = Dashboard
user_mgr = User Management
team_mgr = Team Management
project_mgr = Project Management
project_space_mgr = Project Space Management
comment_mgr = Comment Management
config_mgr = Configure Management
config_file = Configure File
attachment_mgr = Attachment Management
label_mgr = Label Management
label_name = Label name
used_quantity = Used Quantity
proj_amount = Number Of Project
blog_amount = Number Of Blog
member_amount = Number Of Member
comment_amount = Number Of Comment
attachment_amount = Number Of Attachment
member_mgr = Member Management
add_member = Add Member
create_team = Create Team
team_name = Team Name
proj = Project
member = Member
edit_team = Edit Team
team_member_mgr = Team Member Management
team_proj = Team Project
add_proj = Add Project
proj_name = Project name
proj_author = Project author
join_time = Join Time
join_proj = Join
file_name = File name
is_exist = Is Exist
exist = Exist
deleted = Deleted
proj_blog_name = Project/Blog name
doc_name = Document name
file_path = File path
download_url = Download URL
file_size = File size
upload_time = Upload time
download = Download
download_title = Download to local
attachment_name = Attachment name
site_name = Site Name
domain_icp = Domain ICP
site_desc = Site Description
site_desc_tips = Description cannot exceed 500 characters
enable_anonymous_access = Enable anonymous access
enable = Enable
disable = Disable
enable_register = Enable Registration
enable_captcha = Enable Captcha
enable_doc_his = Enable Document Historic
proj_space_name = Project space name
proj_space_id = Project space ID
create_proj_space = Create Project Space
edit_proj_space = Edit Project Space
proj_list = Project List
edit_proj = Edit Project
create_time = Create Time
creator = Creator
doc_amount = Number of Document
last_edit = Last Edit
delete_project = Delete Project

570
conf/lang/zh-cn.ini 100644
View File

@ -0,0 +1,570 @@
[common]
title = 文档在线管理系统
home = 首页
blog = 文章
project_space = 项目空间
person_center = 个人中心
my_project = 我的项目
my_blog = 我的文章
manage = 管理后台
login = 登录
logout = 退出登录
official_website = 官方网站
feedback = 意见反馈
source_code = 项目源码
manual = 使用手册
username = 用户名
account = 账号
email = 邮箱
password = 密码
role = 角色
captcha = 验证码
keep_login = 保持登录
forgot_password = 忘记密码?
register = 立即注册
dingtalk_login = 扫码登录
account_recovery = 找回密码
new_password = 新密码
confirm_password = 确认密码
new_account = 用户注册
setting = 设置
save = 保存
edit = 编辑
delete = 删除
cancel = 取消
create = 创建
confirm_delete = 确定删除
upload_lang = zh
js_lang = zh-CN
remove = 移除
operate = 操作
confirm = 确定
creator = 创始人
administrator = 管理员
editor = 编辑者
observer = 观察者
back = 返回
detail = 详情
admin_right = 拥有阅读、写作和管理权限
editor_right = 拥有阅读和写作权限
observer_right = 拥有阅读权限
yes =
no =
read = 阅读
generate = 生成
[init]
default_proj_name = MinDoc演示项目
default_proj_desc = 这是一个MinDoc演示项目该项目是由系统初始化时自动创建。
default_proj_space = 默认项目空间
blank_doc = 空白文档
[message]
tips = 友情提示
page_not_existed = 页面不存在
no_permission = 权限不足
keyword_placeholder = 请输入关键词...
wrong_account_password = 账号或密码错误
wrong_password = 密码错误
click_to_change = 点击换一张
logging_in = 正在登录...
need_relogin = 请重新登录。
return_account_login = 返回账号密码登录
no_account_yet = 还没有账号?
has_account = 已有账号?
account_empty = 账号不能为空
email_empty = 邮箱不能为空
password_empty = 密码不能为空
captcha_empty = 验证码不能为空
system_error = 系统错误
processing = 正在处理...
email_sent = 邮件发送成功,请登录邮箱查看。
confirm_password_empty = 确认密码不能为空
incorrect_confirm_password = 确认密码输入不正确
illegal_request = 非法请求
account_or_password_empty = 账号或密码不能为空
captcha_wrong = 验证码不正确
password_length_invalid = 密码不能为空且必须在6-50个字符之间
mail_expired = 邮件已失效
captcha_expired = 验证码已过期,请重新操作。
user_not_existed = 用户不存在
email_not_exist = 邮箱不存在
failed_save_password = 保存密码失败
mail_service_not_enable = 未启用邮件服务
account_disable = 账号已被禁用
failed_send_mail = 发送邮件失败
sent_too_many_times = 发送次数太多,请稍候再试
account_not_support_retrieval = 当前用户不支持找回密码
username_invalid_format = 账号只能由英文字母数字组成且在3-50个字符
email_invalid_format = 邮箱格式不正确
account_existed = 账号已存在
failed_register = 注册失败,请联系管理员
failed_obtain_user_info = 获取身份信息失败
dingtalk_auto_login_not_enable = 未开启钉钉自动登录功能
failed_auto_login = 自动登录失败
no_project = 暂无项目
item_not_exist = 项目不存在或已删除
item_not_exist_or_no_permit = 项目不存在或权限不足
doc_not_exist = 文档不存在或已删除
doc_not_exist_or_no_permit = 文档不存在或权限不足
unknown_exception = 未知异常
no_data = 暂无数据
project_must_belong_space = 每个项目必须归属一个项目空间,超级管理员可在后台管理和维护
project_title_placeholder = 项目标题(不超过100字)
project_title_tips = 项目标题不能超过100字符
project_id_placeholder = 项目唯一标识(不超过50字)
project_id_tips = 文档标识只能包含小写字母、数字,以及“-”、“.”和“_”符号.
project_desc_placeholder = 描述信息不超过500个字符
project_public_desc = (任何人都可以访问)
project_private_desc = (只有参与者或使用令牌才能访问)
project_cover_desc = 项目图片可在项目设置中修改
confirm_delete_project = 确定删除项目吗?
warning_delete_project = 删除项目后将无法找回。
project_space_empty = 请选择项目空间
project_title_empty = 项目标题不能为空
project_id_empty = 项目标识不能为空
project_id_existed = 文档标识已被使用
project_id_error = 项目标识有误
project_id_length = 项目标识必须小于50字符
import_file_empty = 请选择需要上传的文件
file_type_placeholder = 请选择Zip或Docx文件
publish_to_queue = 发布任务已推送到任务队列,稍后将在后台执行。
team_name_empty = 团队名称不能为空
operate_failed = 操作失败
project_id_desc = 项目标识用来标记项目的唯一性,不可修改。
history_record_amount_desc = 当开启文档历史时,该值会限制每个文档保存的历史数量
corp_id_desc = 导出文档PDF文档时显示的页脚
project_desc_desc = 描述信息不超过500个字符,支持Markdown语法
project_desc_tips = 项目描述不能大于500字
access_pass_desc = 没有访问权限访问项目时需要提供的密码
auto_publish_desc = 开启后,每次保存会自动发布到最新版本
enable_export_desc = 开启导出前请先配置导出程序,并在配置文件中同时开启导出功能
enable_share_desc = 分享只对公开项目生效,私有项目不支持分享
auto_save_desc = 开启后每隔30秒会自动保存
confirm_into_private = 确定将项目转为私有吗?
into_private_notice = 转为私有后需要通过阅读令牌才能访问该项目。
confirm_into_public = 确定将项目转为公有吗?
into_public_notice = 转为公有后所有人都可以访问该项目。
project_name_empty = 项目名称不能为空
success = 成功
failed = 失败
receive_account_empty = 接受者账号不能为空
receive_account_not_exist = 接受用户不存在
receive_account_disabled = 接受用户已被禁用
cannot_preview = 不能预览
upload_failed = 上传失败
upload_file_size_limit = 文件必须小于2MB
upload_file_empty = 上传文件为空
uploda_file_type_error = 文件类型有误
choose_pic_file = 请选择图片
no_doc_in_cur_proj = 当前项目没有文档
build_doc_tree_error = 生成项目文档树时出错
param_error = 参数错误
doc_name_empty = 文档名称不能为空
parent_id_not_existed = 父分类不存在
doc_not_belong_project = 文档不属于指定的项目
attachment_not_exist = 附件不存在或已删除
read_file_error = 读取文档错误
confirm_override_doc = 文档已被修改确定要覆盖吗?
doc_auto_published = 文档自动发布成功
export_func_disable = 系统没有开启导出功能
cur_project_export_func_disable = 当前项目没有开启导出功能
cur_project_not_support_md = 当前项目不支持Markdown编辑器
export_failed = 导出失败,请查看系统日志
file_converting = 文档正在后台转换,请稍后再下载
unsupport_file_type = 不支持的文件格式
no_exportable_file = 项目没有导出文件
gen_qrcode_failed = 生成二维码失败
search_result_error = 搜索结果错误
get_doc_his_failed = 获取历史失败
project_space_not_exist = 项目空间不存在
search_placeholder = 请输入搜索关键字
no_search_result = 暂无相关搜索结果!
user_exist_in_proj = 用户已存在该项目中
cannot_change_own_priv = 不能变更自己的权限
cannot_delete_self = 不能删除自己
cannot_handover_myself = 不能转让给自己
confirm_delete_blog = 确定删除文章吗?
delete_blog_tips = 删除文章后将无法找回。
input_proj_id_pls = 请输入项目标识
input_doc_id_pls = 请输入文档标识
blog_digest_tips = 文章摘要不超过500个字符
blog_title_empty = 文章标题不能为空
blog_not_exist = 文章不存在或已删除
blog_pwd_incorrect = 文章密码不正确
set_pwd_pls = 加密文章请设置密码
unknown_blog_type = 未知的文章类型
blog_title_tips = 文章标题不能大于200个字符
ref_doc_prj_not_existed = 关联文档的项目不存在
query_ref_doc_error = 查询关联项目文档时出错
ref_doc_not_exist_or_no_permit = 关联文档不存在或权限不足
blog_id_existed = 文章标识已存在
query_failed = 查询失败
blog_has_modified = 文章已被修改
cur_user_cannot_change_pwd = 当前用户不支持修改密码
origin_pwd_empty = 原密码不能为空
new_pwd_empty = 新密码不能为空
confirm_pwd_empty = 确认密码不能为空
pwd_length = 密码必须在6-18字之间
pwd_length_tips = 密码必须在6-50个字符之间
wrong_origin_pwd = 原始密码不正确
wrong_confirm_pwd = 确认密码不正确
same_pwd = 新密码不能和原始密码相同
pwd_encrypt_failed = 密码加密失败
team_name_empty = 团队名称不能为空
proj_empty = 项目不能为空
site_name_empty = 网站标题不能为空
proj_space_name_empty = 项目空间名称不能为空
proj_space_id_empty = 项目空间标识不能为空
proj_space_id_tips = 项目空间标识只能由字母和数字组成且在2-100字符之间
project_order_desc = 只能是数字,序号越大排序越靠前
project_label_desc = 最多允许添加10个标签多个标签请用“;”分割
cannot_change_own_status = 不能变更自己的状态
cannot_change_super_status = 不能变更超级管理员的状态
cannot_change_super_priv = 不能变更超级管理员的权限
[blog]
author = 作者
project_list = 项目列表
add_project = 添加项目
import_project = 导入项目
delete_project = 删除项目
project_summary = 项目概要
read = 阅读
edit = 编辑
delete = 删除
copy = 复制
view = 查看文档
publish = 发布
edit_doc = 编辑文档
default_cover = 默认封面
create_time = 创建时间
update_time = 修改时间
creator = 创建者
doc_amount = 文档数量
doc_unit =
project_role = 项目角色
last_edit = 最后编辑
project_title = 项目标题
project_id = 项目标识
project_desc = 项目描述
public = 公开
private = 私有
public_project = 公开项目
private_project = 私有项目
summary = 概要
member = 成员
team = 团队
comment_amount = 评论数量
comment_unit =
member_manage = 成员管理
add_member = 添加成员
administrator = 管理员
editor = 编辑者
observer = 观察者
team_manage = 团队管理
add_team = 添加团队
team_name = 团队名称
member_amount = 成员数量
join_time = 加入时间
project_setting = 项目设置
handover_project = 转让项目
make_public = 转为公有
make_private = 转为私有
history_record_amount = 历史记录数量
corp_id = 公司名称
text_editor = 编辑器
project_label = 项目标签
project_order = 项目排序
access_pass = 访问密码
access_token = 访问令牌
auto_publish = 自动发布
enable_export = 开启导出
enable_share = 开启分享
set_first_as_home = 设置第一篇文档为默认首页
auto_save = 自动保存
cover = 封面
click_change_cover = 点击图片可修改项目封面
change_cover = 修改封面
preview = 预览
choose = 选择
upload = 上传
recipient_account = 接收者账号
blog_list = 文章列表
add_blog = 添加文章
encryption = 加密
encrypt =
edit_blog = 文章编辑
delete_blog = 删除文章
setting_blog = 文章设置
no_blog = 暂无文章
blog_setting = 文章设置
title = 文章标题
type = 文章类型
normal_blog = 普通文章
link_blog = 链接文章
ref_doc = 关联文档
blog_status = 文章状态
blog_pwd = 文章密码
blog_digest = 文章摘要
posted_on = 发布于
modified_on = 修改于
prev = 上一篇
next = 下一篇
no =
edit_title = 编辑文章
[doc]
modify_doc = 修改文档
comparison = 文档比较
save_merge = 保存合并
prev_diff = 上一处差异
next_diff = 下一处差异
merge_to_left = 合并到左侧
merge_to_right = 合并到右侧
exchange_left_right = 左右切换
print = 打印
download = 下载
share = 分享
share_project = 项目分享
share_url = 项目地址
contents = 目录
search = 搜索
expand = 展开
fold = 收起
close = 关闭
doc_publish_by = 本文档使用
doc_publish = 发布
edit_doc = 编辑文档
backward = 返回
save = 保存
save_as_tpl = 保存为模板
undo = 撤销
redo = 重做
bold = 粗体
italic = 斜体
strikethrough = 删除线
h1 = 标题一
h2 = 标题二
h3 = 标题三
h4 = 标题四
h5 = 标题五
h6 = 标题六
unorder_list = 无序列表
order_list = 有序列表
hline = Horizontal line
link = 链接
ref_link = 引用链接
add_pic = 添加图片
code = 行内代码
code_block = 代码块
table = 添加表格
quote = 引用
gfm_task = GFM 任务列表
attachment = 附件
json_to_table = Json转换为表格
template = 模板
close_preview = 关闭实时预览
modify_history = 修改历史
sidebar = 边栏
help = 使用帮助
publish = 发布
document = 文档
create_doc = 创建文档
attachments = 个附件
doc_name = 文档名称
doc_name_tips = 在目录的文档名上右键可以删除和修改文档名称以及添加下级文档
doc_id = 文档标识
doc_id_tips = 文档标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母开头
expand_desc = (在阅读时会自动展开节点)
fold_desc = (在阅读时会关闭节点)
empty_contents = 空目录
empty_contents_desc = (单击时会展开下级节点)
upload_attachment = 上传附件
doc_history = 文档历史记录
choose_template_type = 请选择模板类型
normal_tpl = 普通文档
api_tpl = API文档
data_dict = 数据字典
custom_tpl = 自定义模板
tpl_default_type = 默认类型
tpl_plain_text = 简单的文本文档
for_api_doc = 用于API文档速写
code_highlight = 支持代码高亮
for_data_dict = 用于数据字典显示
form_support = 表格支持
any_type_doc = 支持任意类型文档
as_global_tpl = 可以设置为全局模板
tpl_name = 模板名称
tpl_type = 模板类型
creator = 创建人
create_time = 创建时间
operation = 操作
global_tpl = 全局
global_tpl_desc = (任何项目都可用)
project_tpl = 项目
project_tpl_desc = (只有当前项目可用)
insert = 插入
uploading = 正在上传
his_ver = 历史版本
update_time = 修改时间
updater = 修改人
version = 版本
delete = 删除
recover = 恢复
merge = 合并
comparison_title = 文档比较【左侧为历史文档,右侧为当前文档,请将文档合并到右侧】
font_size = 字号
underscore = 下划线
right_intent = 右缩进
left_intent = 左缩进
subscript = 下标
superscript = 上标
clean_format = 清空格式
add_video = 添加视频
formula = 公式
font_color = 字体颜色
bg_color = 背景颜色
input_pwd = 请输入密码
read_pwd = 浏览密码
commit = 提交
ft_author = 作者:
ft_last_editor = 最后编辑:
ft_create_time = 创建时间:
ft_update_time = 更新时间:
view_count = 阅读次数
[project]
prj_space_list = 项目空间列表
prj_space_list_of = 项目空间%s的项目列表
search_title = 显示项目空间为"%s"的项目
author = 作者
no_project = 暂无项目
prj_amount = 项目数量
creator = 创建人
create_time = 创建时间
no_project_space = 没有项目空间
[search]
title = 搜索
search_title = 显示"%s"的搜索结果
doc = 文档
prj = 项目
blog = 文章
from_proj = 来自项目
from_blog = 来自文章
author = 作者
update_time = 更新时间
no_result = 暂无相关搜索结果
[page]
first = 首页
last = 末页
prev = 上一页
next = 下一页
[uc]
user_center = 用户中心
base_info = 基本信息
change_pwd = 修改密码
username = 用户名
nickname = 昵称
realname = 真实姓名
email = 邮箱
mobile = 手机号
description = 描述
description_tips = 描述不能超过500字
avatar = 头像
change_avatar = 修改头像
password = 密码
origin_pwd = 原始密码
new_pwd = 新密码
confirm_pwd = 确认密码
role = 角色
type = 类型
status = 状态
super_admin = 超级管理员
admin = 管理员
user = 普通用户
normal = 正常
disable = 禁用
enable = 启用
create_user = 创建用户
edit_user = 编辑用户
pwd_tips = 不修改密码请留空,只支持本地用户修改密码
[mgr]
dashboard_menu = 仪表盘
user_menu = 用户管理
team_menu = 团队管理
project_menu = 项目管理
project_space_menu = 项目空间管理
comment_menu = 评论管理
config_menu = 配置管理
attachment_menu = 附件管理
label_menu = 标签管理
dashboard_mgr = 仪表盘
user_mgr = 用户管理
team_mgr = 团队管理
project_mgr = 项目管理
project_space_mgr = 项目空间管理
comment_mgr = 评论管理
config_mgr = 配置管理
config_file = 配置文件
attachment_mgr = 附件管理
label_mgr = 标签管理
label_name = 标签名称
used_quantity = 使用数量
proj_amount = 项目数量
blog_amount = 文章数量
member_amount = 成员数量
comment_amount = 评论数量
attachment_amount = 附件数量
member_mgr = 成员管理
add_member = 添加成员
create_team = 创建团队
team_name = 团队名称
proj = 项目
member = 成员
edit_team = 编辑团队
team_member_mgr = 团队用户管理
team_proj = 团队项目
add_proj = 添加项目
proj_name = 项目名称
proj_author = 项目作者
join_time = 加入时间
join_proj = 加入项目
file_name = 文件名称
is_exist = 是否存在
exist = 存在
deleted = 已删除
proj_blog_name = 项目/文章名称
doc_name = 文档名称
file_path = 文件路径
download_url = 下载路径
file_size = 文件大小
upload_time = 上传时间
download = 下载
download_title = 下载到本地
attachment_name = 附件名称
site_name = 网站标题
domain_icp = 域名备案
site_desc = 网站描述
site_desc_tips = 描述信息不超过500个字符
enable_anonymous_access = 启用匿名访问
enable = 开启
disable = 关闭
enable_register = 启用注册
enable_captcha = 启用验证码
enable_doc_his = 启用文档历史
proj_space_name = 项目空间名称
proj_space_id = 项目空间标识
create_proj_space = 创建项目空间
edit_proj_space = 编辑项目空间
proj_list = 项目列表
edit_proj = 编辑项目
create_time = 创建时间
creator = 创建者
doc_amount = 文档数量
last_edit = 最后编辑
delete_project = 删除项目

View File

@ -1,6 +1,7 @@
package controllers package controllers
import ( import (
"github.com/beego/i18n"
"net/url" "net/url"
"regexp" "regexp"
"strings" "strings"
@ -55,17 +56,17 @@ func (c *AccountController) Prepare() {
} }
if token == "" { if token == "" {
if c.IsAjax() { if c.IsAjax() {
c.JsonResult(403, "非法请求") c.JsonResult(403, i18n.Tr(c.Lang, "message.illegal_request"))
} else { } else {
c.ShowErrorPage(403, "非法请求") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.illegal_request"))
} }
} }
xsrfToken := c.XSRFToken() xsrfToken := c.XSRFToken()
if xsrfToken != token { if xsrfToken != token {
if c.IsAjax() { if c.IsAjax() {
c.JsonResult(403, "非法请求") c.JsonResult(403, i18n.Tr(c.Lang, "message.illegal_request"))
} else { } else {
c.ShowErrorPage(403, "非法请求") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.illegal_request"))
} }
} }
} }
@ -109,12 +110,12 @@ func (c *AccountController) Login() {
if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") { if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") {
v, ok := c.GetSession(conf.CaptchaSessionName).(string) v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) { if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, "验证码不正确") c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
} }
} }
if account == "" || password == "" { if account == "" || password == "" {
c.JsonResult(6002, "账号或密码不能为空") c.JsonResult(6002, i18n.Tr(c.Lang, "message.account_or_password_empty"))
} }
member, err := models.NewMember().Login(account, password) member, err := models.NewMember().Login(account, password)
@ -137,7 +138,7 @@ func (c *AccountController) Login() {
c.JsonResult(0, "ok", c.referer()) c.JsonResult(0, "ok", c.referer())
} else { } else {
logs.Error("用户登录 ->", err) logs.Error("用户登录 ->", err)
c.JsonResult(500, "账号或密码错误", nil) c.JsonResult(500, i18n.Tr(c.Lang, "message.wrong_account_password"), nil)
} }
} else { } else {
c.Data["url"] = c.referer() c.Data["url"] = c.referer()
@ -150,7 +151,7 @@ func (c *AccountController) DingTalkLogin() {
code := c.GetString("dingtalk_code") code := c.GetString("dingtalk_code")
if code == "" { if code == "" {
c.JsonResult(500, "获取身份信息失败", nil) c.JsonResult(500, i18n.Tr(c.Lang, "message.failed_obtain_user_info"), nil)
} }
appKey, _ := web.AppConfig.String("dingtalk_app_key") appKey, _ := web.AppConfig.String("dingtalk_app_key")
@ -158,7 +159,7 @@ func (c *AccountController) DingTalkLogin() {
tmpReader, _ := web.AppConfig.String("dingtalk_tmp_reader") tmpReader, _ := web.AppConfig.String("dingtalk_tmp_reader")
if appKey == "" || appSecret == "" || tmpReader == "" { if appKey == "" || appSecret == "" || tmpReader == "" {
c.JsonResult(500, "未开启钉钉自动登录功能", nil) c.JsonResult(500, i18n.Tr(c.Lang, "message.dingtalk_auto_login_not_enable"), nil)
c.StopRun() c.StopRun()
} }
@ -166,21 +167,21 @@ func (c *AccountController) DingTalkLogin() {
err := dingtalkAgent.GetAccesstoken() err := dingtalkAgent.GetAccesstoken()
if err != nil { if err != nil {
logs.Warn("获取钉钉临时Token失败 ->", err) logs.Warn("获取钉钉临时Token失败 ->", err)
c.JsonResult(500, "自动登录失败", nil) c.JsonResult(500, i18n.Tr(c.Lang, "message.failed_auto_login"), nil)
c.StopRun() c.StopRun()
} }
userid, err := dingtalkAgent.GetUserIDByCode(code) userid, err := dingtalkAgent.GetUserIDByCode(code)
if err != nil { if err != nil {
logs.Warn("获取钉钉用户ID失败 ->", err) logs.Warn("获取钉钉用户ID失败 ->", err)
c.JsonResult(500, "自动登录失败", nil) c.JsonResult(500, i18n.Tr(c.Lang, "message.failed_auto_login"), nil)
c.StopRun() c.StopRun()
} }
username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid) username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid)
if err != nil { if err != nil {
logs.Warn("获取钉钉用户信息失败 ->", err) logs.Warn("获取钉钉用户信息失败 ->", err)
c.JsonResult(500, "自动登录失败", nil) c.JsonResult(500, i18n.Tr(c.Lang, "message.failed_auto_login"), nil)
c.StopRun() c.StopRun()
} }
@ -309,29 +310,29 @@ func (c *AccountController) Register() {
captcha := c.GetString("code") captcha := c.GetString("code")
if ok, err := regexp.MatchString(conf.RegexpAccount, account); account == "" || !ok || err != nil { if ok, err := regexp.MatchString(conf.RegexpAccount, account); account == "" || !ok || err != nil {
c.JsonResult(6001, "账号只能由英文字母数字组成且在3-50个字符") c.JsonResult(6001, i18n.Tr(c.Lang, "message.username_invalid_format"))
} }
if l := strings.Count(password1, ""); password1 == "" || l > 50 || l < 6 { if l := strings.Count(password1, ""); password1 == "" || l > 50 || l < 6 {
c.JsonResult(6002, "密码必须在6-50个字符之间") c.JsonResult(6002, i18n.Tr(c.Lang, "message.password_length_invalid"))
} }
if password1 != password2 { if password1 != password2 {
c.JsonResult(6003, "确认密码不正确") c.JsonResult(6003, i18n.Tr(c.Lang, "message.incorrect_confirm_password"))
} }
if ok, err := regexp.MatchString(conf.RegexpEmail, email); !ok || err != nil || email == "" { if ok, err := regexp.MatchString(conf.RegexpEmail, email); !ok || err != nil || email == "" {
c.JsonResult(6004, "邮箱格式不正确") c.JsonResult(6004, i18n.Tr(c.Lang, "message.email_invalid_format"))
} }
// 如果开启了验证码 // 如果开启了验证码
if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") { if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") {
v, ok := c.GetSession(conf.CaptchaSessionName).(string) v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) { if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, "验证码不正确") c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
} }
} }
member := models.NewMember() member := models.NewMember()
if _, err := member.FindByAccount(account); err == nil && member.MemberId > 0 { if _, err := member.FindByAccount(account); err == nil && member.MemberId > 0 {
c.JsonResult(6005, "账号已存在") c.JsonResult(6005, i18n.Tr(c.Lang, "message.account_existed"))
} }
member.Account = account member.Account = account
@ -342,7 +343,7 @@ func (c *AccountController) Register() {
member.Email = email member.Email = email
member.Status = 0 member.Status = 0
if err := member.Add(); err != nil { if err := member.Add(); err != nil {
c.JsonResult(6006, "注册失败,请联系系统管理员处理") c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed_register"))
} }
c.JsonResult(0, "ok", member) c.JsonResult(0, "ok", member)
@ -360,39 +361,39 @@ func (c *AccountController) FindPassword() {
captcha := c.GetString("code") captcha := c.GetString("code")
if email == "" { if email == "" {
c.JsonResult(6005, "邮箱地址不能为空") c.JsonResult(6005, i18n.Tr(c.Lang, "message.email_empty"))
} }
if !mailConf.EnableMail { if !mailConf.EnableMail {
c.JsonResult(6004, "未启用邮件服务") c.JsonResult(6004, i18n.Tr(c.Lang, "message.mail_service_not_enable"))
} }
// 如果开启了验证码 // 如果开启了验证码
if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") { if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") {
v, ok := c.GetSession(conf.CaptchaSessionName).(string) v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) { if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, "验证码不正确") c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
} }
} }
member, err := models.NewMember().FindByFieldFirst("email", email) member, err := models.NewMember().FindByFieldFirst("email", email)
if err != nil { if err != nil {
c.JsonResult(6006, "邮箱不存在") c.JsonResult(6006, i18n.Tr(c.Lang, "message.email_not_exist"))
} }
if member.Status != 0 { if member == nil || member.Status != 0 {
c.JsonResult(6007, "账号已被禁用") c.JsonResult(6007, i18n.Tr(c.Lang, "message.account_disable"))
} }
if member.AuthMethod == conf.AuthMethodLDAP { if member == nil || member.AuthMethod == conf.AuthMethodLDAP {
c.JsonResult(6011, "当前用户不支持找回密码") c.JsonResult(6011, i18n.Tr(c.Lang, "message.account_not_support_retrieval"))
} }
count, err := models.NewMemberToken().FindSendCount(email, time.Now().Add(-1*time.Hour), time.Now()) count, err := models.NewMemberToken().FindSendCount(email, time.Now().Add(-1*time.Hour), time.Now())
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6008, "发送邮件失败") c.JsonResult(6008, i18n.Tr(c.Lang, "message.failed_send_mail"))
} }
if count > mailConf.MailNumber { if count > mailConf.MailNumber {
c.JsonResult(6008, "发送次数太多,请稍候再试") c.JsonResult(6008, i18n.Tr(c.Lang, "message.sent_too_many_times"))
} }
memberToken := models.NewMemberToken() memberToken := models.NewMemberToken()
@ -402,7 +403,7 @@ func (c *AccountController) FindPassword() {
memberToken.MemberId = member.MemberId memberToken.MemberId = member.MemberId
memberToken.IsValid = false memberToken.IsValid = false
if _, err := memberToken.InsertOrUpdate(); err != nil { if _, err := memberToken.InsertOrUpdate(); err != nil {
c.JsonResult(6009, "邮件发送失败") c.JsonResult(6009, i18n.Tr(c.Lang, "message.failed_send_mail"))
} }
data := map[string]interface{}{ data := map[string]interface{}{
@ -414,7 +415,7 @@ func (c *AccountController) FindPassword() {
body, err := c.ExecuteViewPathTemplate("account/mail_template.tpl", data) body, err := c.ExecuteViewPathTemplate("account/mail_template.tpl", data)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6003, "邮件发送失败") c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed_send_mail"))
} }
go func(mailConf *conf.SmtpConf, email string, body string) { go func(mailConf *conf.SmtpConf, email string, body string) {
@ -473,17 +474,16 @@ func (c *AccountController) FindPassword() {
if token != "" && email != "" { if token != "" && email != "" {
memberToken, err := models.NewMemberToken().FindByFieldFirst("token", token) memberToken, err := models.NewMemberToken().FindByFieldFirst("token", token)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.Data["ErrorMessage"] = "邮件已失效" c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.mail_expired")
c.TplName = "errors/error.tpl" c.TplName = "errors/error.tpl"
return return
} }
subTime := time.Until(memberToken.SendTime) subTime := time.Until(memberToken.SendTime)
if !strings.EqualFold(memberToken.Email, email) || subTime.Minutes() > float64(mailConf.MailExpired) || !memberToken.ValidTime.IsZero() { if !strings.EqualFold(memberToken.Email, email) || subTime.Minutes() > float64(mailConf.MailExpired) || !memberToken.ValidTime.IsZero() {
c.Data["ErrorMessage"] = "验证码已过期,请重新操作。" c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.captcha_expired")
c.TplName = "errors/error.tpl" c.TplName = "errors/error.tpl"
return return
} }
@ -504,48 +504,46 @@ func (c *AccountController) ValidEmail() {
email := c.GetString("mail") email := c.GetString("mail")
if password1 == "" { if password1 == "" {
c.JsonResult(6001, "密码不能为空") c.JsonResult(6001, i18n.Tr(c.Lang, "message.password_empty"))
} }
if l := strings.Count(password1, ""); l < 6 || l > 50 { if l := strings.Count(password1, ""); l < 6 || l > 50 {
c.JsonResult(6001, "密码不能为空且必须在6-50个字符之间") c.JsonResult(6001, i18n.Tr(c.Lang, "message.password_length_invalid"))
} }
if password2 == "" { if password2 == "" {
c.JsonResult(6002, "确认密码不能为空") c.JsonResult(6002, i18n.Tr(c.Lang, "message.confirm_password_empty"))
} }
if password1 != password2 { if password1 != password2 {
c.JsonResult(6003, "确认密码输入不正确") c.JsonResult(6003, i18n.Tr(c.Lang, "message.incorrect_confirm_password"))
} }
if captcha == "" { if captcha == "" {
c.JsonResult(6004, "验证码不能为空") c.JsonResult(6004, i18n.Tr(c.Lang, "message.captcha_empty"))
} }
v, ok := c.GetSession(conf.CaptchaSessionName).(string) v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) { if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, "验证码不正确") c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
} }
mailConf := conf.GetMailConfig() mailConf := conf.GetMailConfig()
memberToken, err := models.NewMemberToken().FindByFieldFirst("token", token) memberToken, err := models.NewMemberToken().FindByFieldFirst("token", token)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6007, "邮件已失效") c.JsonResult(6007, i18n.Tr(c.Lang, "message.mail_expired"))
} }
subTime := time.Until(memberToken.SendTime) subTime := time.Until(memberToken.SendTime)
if !strings.EqualFold(memberToken.Email, email) || subTime.Minutes() > float64(mailConf.MailExpired) || !memberToken.ValidTime.IsZero() { if !strings.EqualFold(memberToken.Email, email) || subTime.Minutes() > float64(mailConf.MailExpired) || !memberToken.ValidTime.IsZero() {
c.JsonResult(6008, "验证码已过期,请重新操作。") c.JsonResult(6008, i18n.Tr(c.Lang, "message.captcha_expired"))
} }
member, err := models.NewMember().Find(memberToken.MemberId) member, err := models.NewMember().Find(memberToken.MemberId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6005, "用户不存在") c.JsonResult(6005, i18n.Tr(c.Lang, "message.user_not_existed"))
} }
hash, err := utils.PasswordHash(password1) hash, err := utils.PasswordHash(password1)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6006, "保存密码失败") c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed_save_password"))
} }
member.Password = hash member.Password = hash
@ -557,7 +555,7 @@ func (c *AccountController) ValidEmail() {
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6006, "保存密码失败") c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed_save_password"))
} }
c.JsonResult(0, "ok", conf.URLFor("AccountController.Login")) c.JsonResult(0, "ok", conf.URLFor("AccountController.Login"))
} }
@ -565,11 +563,8 @@ func (c *AccountController) ValidEmail() {
// Logout 退出登录 // Logout 退出登录
func (c *AccountController) Logout() { func (c *AccountController) Logout() {
c.SetMember(models.Member{}) c.SetMember(models.Member{})
c.SetSecureCookie(conf.GetAppKey(), "login", "", -3600) c.SetSecureCookie(conf.GetAppKey(), "login", "", -3600)
u := c.Ctx.Request.Header.Get("Referer") u := c.Ctx.Request.Header.Get("Referer")
c.Redirect(conf.URLFor("AccountController.Login", "url", u), 302) c.Redirect(conf.URLFor("AccountController.Login", "url", u), 302)
} }
@ -579,11 +574,6 @@ func (c *AccountController) Captcha() {
captchaImage := gocaptcha.NewCaptchaImage(140, 40, gocaptcha.RandLightColor()) captchaImage := gocaptcha.NewCaptchaImage(140, 40, gocaptcha.RandLightColor())
//if err != nil {
// logs.Error(err)
// c.Abort("500")
//}
captchaImage.DrawNoise(gocaptcha.CaptchaComplexLower) captchaImage.DrawNoise(gocaptcha.CaptchaComplexLower)
// captchaImage.DrawTextNoise(gocaptcha.CaptchaComplexHigh) // captchaImage.DrawTextNoise(gocaptcha.CaptchaComplexHigh)

View File

@ -2,7 +2,6 @@ package controllers
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"io" "io"
"strings" "strings"
@ -14,6 +13,7 @@ import (
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/models" "github.com/mindoc-org/mindoc/models"
"github.com/mindoc-org/mindoc/utils" "github.com/mindoc-org/mindoc/utils"
@ -25,6 +25,7 @@ type BaseController struct {
Option map[string]string Option map[string]string
EnableAnonymous bool EnableAnonymous bool
EnableDocumentHistory bool EnableDocumentHistory bool
Lang string
} }
type CookieRemember struct { type CookieRemember struct {
@ -46,7 +47,6 @@ func (c *BaseController) Prepare() {
c.EnableDocumentHistory = false c.EnableDocumentHistory = false
if member, ok := c.GetSession(conf.LoginSessionName).(models.Member); ok && member.MemberId > 0 { if member, ok := c.GetSession(conf.LoginSessionName).(models.Member); ok && member.MemberId > 0 {
c.Member = &member c.Member = &member
c.Data["Member"] = c.Member c.Data["Member"] = c.Member
} else { } else {
@ -80,6 +80,7 @@ func (c *BaseController) Prepare() {
c.Data["Scripts"] = template.HTML(string(b)) c.Data["Scripts"] = template.HTML(string(b))
} }
c.SetLang()
} }
//判断用户是否登录. //判断用户是否登录.
@ -112,14 +113,16 @@ func (c *BaseController) JsonResult(errCode int, errMsg string, data ...interfac
} }
returnJSON, err := json.Marshal(jsonData) returnJSON, err := json.Marshal(jsonData)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
} }
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json; charset=utf-8") c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json; charset=utf-8")
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache, no-store") c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache, no-store")
io.WriteString(c.Ctx.ResponseWriter, string(returnJSON)) _, err = io.WriteString(c.Ctx.ResponseWriter, string(returnJSON))
if err != nil {
logs.Error(err)
}
c.StopRun() c.StopRun()
} }
@ -136,14 +139,16 @@ func (c *BaseController) CheckJsonError(code int, err error) {
jsonData["message"] = err.Error() jsonData["message"] = err.Error()
returnJSON, err := json.Marshal(jsonData) returnJSON, err := json.Marshal(jsonData)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
} }
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json; charset=utf-8") c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json; charset=utf-8")
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache, no-store") c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache, no-store")
io.WriteString(c.Ctx.ResponseWriter, string(returnJSON)) _, err = io.WriteString(c.Ctx.ResponseWriter, string(returnJSON))
if err != nil {
logs.Error(err)
}
c.StopRun() c.StopRun()
} }
@ -201,3 +206,21 @@ func (c *BaseController) CheckErrorResult(code int, err error) {
c.ShowErrorPage(code, err.Error()) c.ShowErrorPage(code, err.Error())
} }
} }
func (c *BaseController) SetLang() {
hasCookie := false
lang := c.GetString("lang")
if len(lang) == 0 {
lang = c.Ctx.GetCookie("lang")
hasCookie = true
}
if len(lang) == 0 ||
!i18n.IsExist(lang) {
lang, _ = web.AppConfig.String("default_lang")
}
if !hasCookie {
c.Ctx.SetCookie("lang", lang, 1<<31-1, "/")
}
c.Data["Lang"] = lang
c.Lang = lang
}

View File

@ -16,6 +16,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/models" "github.com/mindoc-org/mindoc/models"
"github.com/mindoc-org/mindoc/utils" "github.com/mindoc-org/mindoc/utils"
@ -40,20 +41,20 @@ func (c *BlogController) Index() {
blogId, _ := strconv.Atoi(c.Ctx.Input.Param(":id")) blogId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
if blogId <= 0 { if blogId <= 0 {
c.ShowErrorPage(404, "页面不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.page_not_existed"))
} }
blogReadSession := fmt.Sprintf("blog:read:%d", blogId) blogReadSession := fmt.Sprintf("blog:read:%d", blogId)
blog, err := models.NewBlog().FindFromCache(blogId) blog, err := models.NewBlog().FindFromCache(blogId)
if err != nil { if err != nil {
c.ShowErrorPage(404, "文章不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.blog_not_existed"))
} }
if c.Ctx.Input.IsPost() { if c.Ctx.Input.IsPost() {
password := c.GetString("password") password := c.GetString("password")
if blog.BlogStatus == "password" && password != blog.Password { if blog.BlogStatus == "password" && password != blog.Password {
c.JsonResult(6001, "文章密码不正确") c.JsonResult(6001, i18n.Tr(c.Lang, "message.blog_pwd_incorrect"))
} else if blog.BlogStatus == "password" && password == blog.Password { } else if blog.BlogStatus == "password" && password == blog.Password {
//如果密码输入正确则存入session中 //如果密码输入正确则存入session中
_ = c.CruSession.Set(context.TODO(), blogReadSession, blogId) _ = c.CruSession.Set(context.TODO(), blogReadSession, blogId)
@ -160,33 +161,33 @@ func (c *BlogController) ManageSetting() {
documentId := 0 documentId := 0
if blogTitle == "" { if blogTitle == "" {
c.JsonResult(6001, "文章标题不能为空") c.JsonResult(6001, i18n.Tr(c.Lang, "message.blog_title_empty"))
} }
if strings.Count(blogExcerpt, "") > 500 { if strings.Count(blogExcerpt, "") > 500 {
c.JsonResult(6008, "文章摘要必须小于500字符") c.JsonResult(6008, i18n.Tr(c.Lang, "message.blog_digest_tips"))
} }
if blogStatus != "public" && blogStatus != "password" && blogStatus != "draft" { if blogStatus != "public" && blogStatus != "password" && blogStatus != "draft" {
blogStatus = "public" blogStatus = "public"
} }
if blogStatus == "password" && blogPassword == "" { if blogStatus == "password" && blogPassword == "" {
c.JsonResult(6010, "加密文章请设置密码") c.JsonResult(6010, i18n.Tr(c.Lang, "message.set_pwd_pls"))
} }
if blogType != 0 && blogType != 1 { if blogType != 0 && blogType != 1 {
c.JsonResult(6005, "未知的文章类型") c.JsonResult(6005, i18n.Tr(c.Lang, "message.unknown_blog_type"))
} }
if strings.Count(blogTitle, "") > 200 { if strings.Count(blogTitle, "") > 200 {
c.JsonResult(6002, "文章标题不能大于200个字符") c.JsonResult(6002, i18n.Tr(c.Lang, "message.blog_title_tips"))
} }
//如果是关联文章,需要同步关联的文档 //如果是关联文章,需要同步关联的文档
if blogType == 1 { if blogType == 1 {
book, err := models.NewBook().FindByIdentify(bookIdentify) book, err := models.NewBook().FindByIdentify(bookIdentify)
if err != nil { if err != nil {
c.JsonResult(6011, "关联文档的项目不存在") c.JsonResult(6011, i18n.Tr(c.Lang, "message.ref_doc_not_exist_or_no_permit"))
} }
doc, err := models.NewDocument().FindByIdentityFirst(documentIdentify, book.BookId) doc, err := models.NewDocument().FindByIdentityFirst(documentIdentify, book.BookId)
if err != nil { if err != nil {
c.JsonResult(6003, "查询关联项目文档时出错") c.JsonResult(6003, i18n.Tr(c.Lang, "message.query_failed"))
} }
documentId = doc.DocumentId documentId = doc.DocumentId
@ -195,7 +196,7 @@ func (c *BlogController) ManageSetting() {
bookResult, err := models.NewBookResult().FindByIdentify(book.Identify, c.Member.MemberId) bookResult, err := models.NewBookResult().FindByIdentify(book.Identify, c.Member.MemberId)
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
c.JsonResult(6002, "关联文档不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.ref_doc_not_exist_or_no_permit"))
} }
} }
} }
@ -211,13 +212,13 @@ func (c *BlogController) ManageSetting() {
} }
if err != nil { if err != nil {
c.JsonResult(6003, "文章不存在") c.JsonResult(6003, i18n.Tr(c.Lang, "message.blog_not_exist"))
} }
//如果设置了文章标识 //如果设置了文章标识
if blogIdentify != "" { if blogIdentify != "" {
//如果查询到的文章标识存在并且不是当前文章的id //如果查询到的文章标识存在并且不是当前文章的id
if b, err := models.NewBlog().FindByIdentify(blogIdentify); err == nil && b.BlogId != blogId { if b, err := models.NewBlog().FindByIdentify(blogIdentify); err == nil && b.BlogId != blogId {
c.JsonResult(6004, "文章标识已存在") c.JsonResult(6004, i18n.Tr(c.Lang, "message.blog_id_existed"))
} }
} }
blog.Modified = time.Now() blog.Modified = time.Now()
@ -226,7 +227,7 @@ func (c *BlogController) ManageSetting() {
//如果设置了文章标识 //如果设置了文章标识
if blogIdentify != "" { if blogIdentify != "" {
if models.NewBlog().IsExist(blogIdentify) { if models.NewBlog().IsExist(blogIdentify) {
c.JsonResult(6004, "文章标识已存在") c.JsonResult(6004, i18n.Tr(c.Lang, "message.blog_id_existed"))
} }
} }
@ -253,7 +254,7 @@ func (c *BlogController) ManageSetting() {
if err := blog.Save(); err != nil { if err := blog.Save(); err != nil {
logs.Error("保存文章失败 -> ", err) logs.Error("保存文章失败 -> ", err)
c.JsonResult(6011, "保存文章失败") c.JsonResult(6011, i18n.Tr(c.Lang, "message.failed"))
} else { } else {
c.JsonResult(0, "ok", blog) c.JsonResult(0, "ok", blog)
} }
@ -287,7 +288,7 @@ func (c *BlogController) ManageEdit() {
blogId, _ := c.GetInt("blogId", 0) blogId, _ := c.GetInt("blogId", 0)
if blogId <= 0 { if blogId <= 0 {
c.JsonResult(6001, "文章参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
blogContent := c.GetString("content", "") blogContent := c.GetString("content", "")
blogHtml := c.GetString("htmlContent", "") blogHtml := c.GetString("htmlContent", "")
@ -304,21 +305,21 @@ func (c *BlogController) ManageEdit() {
} }
if err != nil { if err != nil {
logs.Error("查询文章失败 ->", err) logs.Error("查询文章失败 ->", err)
c.JsonResult(6002, "查询文章失败") c.JsonResult(6002, i18n.Tr(c.Lang, "message.query_failed"))
} }
if version > 0 && blog.Version != version && cover != "yes" { if version > 0 && blog.Version != version && cover != "yes" {
c.JsonResult(6005, "文章已被修改") c.JsonResult(6005, i18n.Tr(c.Lang, "message.blog_has_modified"))
} }
//如果是关联文章,需要同步关联的文档 //如果是关联文章,需要同步关联的文档
if blog.BlogType == 1 { if blog.BlogType == 1 {
doc, err := models.NewDocument().Find(blog.DocumentId) doc, err := models.NewDocument().Find(blog.DocumentId)
if err != nil { if err != nil {
logs.Error("查询关联项目文档时出错 ->", err) logs.Error("查询关联项目文档时出错 ->", err)
c.JsonResult(6003, "查询关联项目文档时出错") c.JsonResult(6003, i18n.Tr(c.Lang, "message.query_failed"))
} }
book, err := models.NewBook().Find(doc.BookId) book, err := models.NewBook().Find(doc.BookId)
if err != nil { if err != nil {
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
// 如果不是超级管理员,则校验权限 // 如果不是超级管理员,则校验权限
@ -327,7 +328,7 @@ func (c *BlogController) ManageEdit() {
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
logs.Error("FindByIdentify => ", err) logs.Error("FindByIdentify => ", err)
c.JsonResult(6002, "关联文档不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.ref_doc_not_exist_or_no_permit"))
} }
} }
@ -338,7 +339,7 @@ func (c *BlogController) ManageEdit() {
doc.ModifyAt = c.Member.MemberId doc.ModifyAt = c.Member.MemberId
if err := doc.InsertOrUpdate("markdown", "release", "content", "modify_time", "modify_at"); err != nil { if err := doc.InsertOrUpdate("markdown", "release", "content", "modify_time", "modify_at"); err != nil {
logs.Error("保存关联文档时出错 ->", err) logs.Error("保存关联文档时出错 ->", err)
c.JsonResult(6004, "保存关联文档时出错") c.JsonResult(6004, i18n.Tr(c.Lang, "message.failed"))
} }
} }
@ -349,7 +350,7 @@ func (c *BlogController) ManageEdit() {
if err := blog.Save("blog_content", "blog_release", "modify_at", "modify_time", "version"); err != nil { if err := blog.Save("blog_content", "blog_release", "modify_at", "modify_time", "version"); err != nil {
logs.Error("保存文章失败 -> ", err) logs.Error("保存文章失败 -> ", err)
c.JsonResult(6011, "保存文章失败") c.JsonResult(6011, i18n.Tr(c.Lang, "message.failed"))
} else { } else {
c.JsonResult(0, "ok", blog) c.JsonResult(0, "ok", blog)
} }
@ -358,7 +359,7 @@ func (c *BlogController) ManageEdit() {
blogId, _ := strconv.Atoi(c.Ctx.Input.Param(":id")) blogId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
if blogId <= 0 { if blogId <= 0 {
c.ShowErrorPage(500, "参数错误") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.param_error"))
} }
var blog *models.Blog var blog *models.Blog
var err error var err error
@ -369,7 +370,7 @@ func (c *BlogController) ManageEdit() {
blog, err = models.NewBlog().FindByIdAndMemberId(blogId, c.Member.MemberId) blog, err = models.NewBlog().FindByIdAndMemberId(blogId, c.Member.MemberId)
} }
if err != nil { if err != nil {
c.ShowErrorPage(404, "文章不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.blog_not_exist"))
} }
blog.LinkAttach() blog.LinkAttach()
@ -397,7 +398,7 @@ func (c *BlogController) ManageDelete() {
blogId, _ := c.GetInt("blog_id", 0) blogId, _ := c.GetInt("blog_id", 0)
if blogId <= 0 { if blogId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
var blog *models.Blog var blog *models.Blog
var err error var err error
@ -408,13 +409,13 @@ func (c *BlogController) ManageDelete() {
blog, err = models.NewBlog().FindByIdAndMemberId(blogId, c.Member.MemberId) blog, err = models.NewBlog().FindByIdAndMemberId(blogId, c.Member.MemberId)
} }
if err != nil { if err != nil {
c.JsonResult(6002, "文章不存在或已删除") c.JsonResult(6002, i18n.Tr(c.Lang, "message.blog_not_exist"))
} }
if err := blog.Delete(blogId); err != nil { if err := blog.Delete(blogId); err != nil {
c.JsonResult(6003, "删除失败") c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed"))
} else { } else {
c.JsonResult(0, "删除成功") c.JsonResult(0, i18n.Tr(c.Lang, "message.success"))
} }
} }
@ -425,16 +426,16 @@ func (c *BlogController) Upload() {
blogId, _ := c.GetInt("blogId") blogId, _ := c.GetInt("blogId")
if blogId <= 0 { if blogId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
blog, err := models.NewBlog().Find(blogId) blog, err := models.NewBlog().Find(blogId)
if err != nil { if err != nil {
c.JsonResult(6010, "文章不存在") c.JsonResult(6010, i18n.Tr(c.Lang, "message.blog_not_exist"))
} }
if !c.Member.IsAdministrator() && blog.MemberId != c.Member.MemberId { if !c.Member.IsAdministrator() && blog.MemberId != c.Member.MemberId {
c.JsonResult(6011, "没有文章的访问权限") c.JsonResult(6011, i18n.Tr(c.Lang, "message.no_permission"))
} }
name := "editormd-file-file" name := "editormd-file-file"
@ -444,7 +445,7 @@ func (c *BlogController) Upload() {
name = "editormd-image-file" name = "editormd-image-file"
file, moreFile, err = c.GetFile(name) file, moreFile, err = c.GetFile(name)
if err == http.ErrMissingFile { if err == http.ErrMissingFile {
c.JsonResult(6003, "没有发现需要上传的图片") c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_empty"))
} }
} }
@ -459,18 +460,18 @@ func (c *BlogController) Upload() {
} }
if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize() { if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize() {
c.JsonResult(6009, "查过文件允许的上传最大值") c.JsonResult(6009, i18n.Tr(c.Lang, "message.upload_file_size_limit"))
} }
ext := filepath.Ext(moreFile.Filename) ext := filepath.Ext(moreFile.Filename)
if ext == "" { if ext == "" {
c.JsonResult(6003, "无法解析文件的格式") c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_type_error"))
} }
//如果文件类型设置为 * 标识不限制文件类型 //如果文件类型设置为 * 标识不限制文件类型
if web.AppConfig.DefaultString("upload_file_ext", "") != "*" { if web.AppConfig.DefaultString("upload_file_ext", "") != "*" {
if !conf.IsAllowUploadFileExt(ext) { if !conf.IsAllowUploadFileExt(ext) {
c.JsonResult(6004, "不允许的文件类型") c.JsonResult(6004, i18n.Tr(c.Lang, "message.upload_file_type_error"))
} }
} }
@ -479,7 +480,7 @@ func (c *BlogController) Upload() {
_, err := models.NewBlog().Find(blogId) _, err := models.NewBlog().Find(blogId)
if err != nil { if err != nil {
c.JsonResult(6006, "文档不存在或权限不足") c.JsonResult(6006, i18n.Tr(c.Lang, "message.doc_not_exist_or_no_permit"))
} }
} else { } else {
@ -488,7 +489,7 @@ func (c *BlogController) Upload() {
if err != nil { if err != nil {
logs.Error("查询文章时出错 -> ", err) logs.Error("查询文章时出错 -> ", err)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.JsonResult(6006, "权限不足") c.JsonResult(6006, i18n.Tr(c.Lang, "message.no_permission"))
} }
c.JsonResult(6001, err.Error()) c.JsonResult(6001, err.Error())
@ -507,7 +508,7 @@ func (c *BlogController) Upload() {
if err != nil { if err != nil {
logs.Error("SaveToFile => ", err) logs.Error("SaveToFile => ", err)
c.JsonResult(6005, "保存文件失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} }
var httpPath string var httpPath string
@ -540,14 +541,14 @@ func (c *BlogController) Upload() {
if err := attachment.Insert(); err != nil { if err := attachment.Insert(); err != nil {
os.Remove(filePath) os.Remove(filePath)
logs.Error("保存文件附件失败 -> ", err) logs.Error("保存文件附件失败 -> ", err)
c.JsonResult(6006, "文件保存失败") c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed"))
} }
if attachment.HttpPath == "" { if attachment.HttpPath == "" {
attachment.HttpPath = conf.URLForNotHost("BlogController.Download", ":id", blogId, ":attach_id", attachment.AttachmentId) attachment.HttpPath = conf.URLForNotHost("BlogController.Download", ":id", blogId, ":attach_id", attachment.AttachmentId)
if err := attachment.Update(); err != nil { if err := attachment.Update(); err != nil {
logs.Error("保存文件失败 -> ", attachment.FilePath, err) logs.Error("保存文件失败 -> ", attachment.FilePath, err)
c.JsonResult(6005, "保存文件失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} }
} }
result["attach"] = attachment result["attach"] = attachment
@ -570,21 +571,21 @@ func (c *BlogController) RemoveAttachment() {
blogId, _ := strconv.Atoi(c.Ctx.Input.Param(":id")) blogId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
if attachId <= 0 { if attachId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
blog, err := models.NewBlog().Find(blogId) blog, err := models.NewBlog().Find(blogId)
if err != nil { if err != nil {
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(500, "文档不存在") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.doc_not_exist"))
} else { } else {
c.ShowErrorPage(500, "查询文章时异常") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.query_failed"))
} }
} }
attach, err := models.NewAttachment().Find(attachId) attach, err := models.NewAttachment().Find(attachId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6002, "附件不存在") c.JsonResult(6002, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} }
if !c.Member.IsAdministrator() { if !c.Member.IsAdministrator() {
@ -592,19 +593,19 @@ func (c *BlogController) RemoveAttachment() {
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6003, "文档不存在") c.JsonResult(6003, i18n.Tr(c.Lang, "message.doc_not_exist"))
} }
} }
if blog.BlogType == 1 && attach.BookId != blog.BookId && attach.DocumentId != blog.DocumentId { if blog.BlogType == 1 && attach.BookId != blog.BookId && attach.DocumentId != blog.DocumentId {
c.ShowErrorPage(404, "附件不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} else if attach.BookId != 0 || attach.DocumentId != blogId { } else if attach.BookId != 0 || attach.DocumentId != blogId {
c.ShowErrorPage(404, "附件不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} }
if err := attach.Delete(); err != nil { if err := attach.Delete(); err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6005, "删除失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} }
os.Remove(filepath.Join(conf.WorkingDirectory, attach.FilePath)) os.Remove(filepath.Join(conf.WorkingDirectory, attach.FilePath))
@ -623,15 +624,15 @@ func (c *BlogController) Download() {
blog, err := models.NewBlog().Find(blogId) blog, err := models.NewBlog().Find(blogId)
if err != nil { if err != nil {
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(500, "文档不存在") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.doc_not_exist"))
} else { } else {
c.ShowErrorPage(500, "查询文章时异常") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.query_failed"))
} }
} }
blogReadSession := fmt.Sprintf("blog:read:%d", blogId) blogReadSession := fmt.Sprintf("blog:read:%d", blogId)
//如果没有启动匿名访问,或者设置了访问密码 //如果没有启动匿名访问,或者设置了访问密码
if (c.Member == nil && !c.EnableAnonymous) || (blog.BlogStatus == "password" && password != blog.Password && c.CruSession.Get(context.TODO(), blogReadSession) == nil) { if (c.Member == nil && !c.EnableAnonymous) || (blog.BlogStatus == "password" && password != blog.Password && c.CruSession.Get(context.TODO(), blogReadSession) == nil) {
c.ShowErrorPage(403, "没有下载权限") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
// 查找附件 // 查找附件
@ -639,18 +640,18 @@ func (c *BlogController) Download() {
if err != nil { if err != nil {
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(404, "附件不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} else { } else {
logs.Error("查询附件时出现异常 -> ", err) logs.Error("查询附件时出现异常 -> ", err)
c.ShowErrorPage(500, "查询附件时出现异常") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.query_failed"))
} }
} }
//如果是链接的文章需要校验文档ID是否一致如果不是需要保证附件的项目ID为0且文档的ID等于博文ID //如果是链接的文章需要校验文档ID是否一致如果不是需要保证附件的项目ID为0且文档的ID等于博文ID
if blog.BlogType == 1 && attachment.DocumentId != blog.DocumentId { if blog.BlogType == 1 && attachment.DocumentId != blog.DocumentId {
c.ShowErrorPage(404, "附件不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} else if blog.BlogType != 1 && (attachment.BookId != 0 || attachment.DocumentId != blogId) { } else if blog.BlogType != 1 && (attachment.BookId != 0 || attachment.DocumentId != blogId) {
c.ShowErrorPage(404, "附件不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} }
c.Ctx.Output.Download(filepath.Join(conf.WorkingDirectory, attachment.FilePath), attachment.FileName) c.Ctx.Output.Download(filepath.Join(conf.WorkingDirectory, attachment.FilePath), attachment.FileName)

View File

@ -13,6 +13,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/utils/sqltil" "github.com/mindoc-org/mindoc/utils/sqltil"
"net/http" "net/http"
@ -37,7 +38,7 @@ func (c *BookController) Index() {
pageIndex, _ := c.GetInt("page", 1) pageIndex, _ := c.GetInt("page", 1)
books, totalCount, err := models.NewBook().FindToPager(pageIndex, conf.PageSize, c.Member.MemberId) books, totalCount, err := models.NewBook().FindToPager(pageIndex, conf.PageSize, c.Member.MemberId, c.Lang)
if err != nil { if err != nil {
logs.Error("BookController.Index => ", err) logs.Error("BookController.Index => ", err)
@ -79,7 +80,7 @@ func (c *BookController) Dashboard() {
c.Abort("404") c.Abort("404")
} }
book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId) book, err := models.NewBookResult().SetLang(c.Lang).FindByIdentify(key, c.Member.MemberId)
if err != nil { if err != nil {
if err == models.ErrPermissionDenied { if err == models.ErrPermissionDenied {
c.Abort("403") c.Abort("403")
@ -154,16 +155,16 @@ func (c *BookController) SaveBook() {
itemId, _ := c.GetInt("itemId") itemId, _ := c.GetInt("itemId")
if strings.Count(description, "") > 500 { if strings.Count(description, "") > 500 {
c.JsonResult(6004, "项目描述不能大于500字") c.JsonResult(6004, i18n.Tr(c.Lang, "message.project_desc_tips"))
} }
if commentStatus != "open" && commentStatus != "closed" && commentStatus != "group_only" && commentStatus != "registered_only" { if commentStatus != "open" && commentStatus != "closed" && commentStatus != "group_only" && commentStatus != "registered_only" {
commentStatus = "closed" commentStatus = "closed"
} }
if !models.NewItemsets().Exist(itemId) { if !models.NewItemsets().Exist(itemId) {
c.JsonResult(6006, "项目空间不存在") c.JsonResult(6006, i18n.Tr(c.Lang, "message.project_space_not_exist"))
} }
if editor != "markdown" && editor != "html" { if editor != "markdown" && editor != "html" && editor != "new_html" {
editor = "markdown" editor = "markdown"
} }
@ -204,7 +205,7 @@ func (c *BookController) SaveBook() {
book.AutoSave = 0 book.AutoSave = 0
} }
if err := book.Update(); err != nil { if err := book.Update(); err != nil {
c.JsonResult(6006, "保存失败") c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed"))
} }
bookResult.BookName = bookName bookResult.BookName = bookName
bookResult.Description = description bookResult.Description = description
@ -221,7 +222,7 @@ func (c *BookController) PrivatelyOwned() {
status := c.GetString("status") status := c.GetString("status")
if status != "open" && status != "close" { if status != "open" && status != "close" {
c.JsonResult(6003, "参数错误") c.JsonResult(6003, i18n.Tr(c.Lang, "message.param_error"))
} }
state := 0 state := 0
if status == "open" { if status == "open" {
@ -238,13 +239,13 @@ func (c *BookController) PrivatelyOwned() {
} }
//只有创始人才能变更私有状态 //只有创始人才能变更私有状态
if bookResult.RoleId != conf.BookFounder { if bookResult.RoleId != conf.BookFounder {
c.JsonResult(6002, "权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.no_permission"))
} }
book, err := models.NewBook().Find(bookResult.BookId) book, err := models.NewBook().Find(bookResult.BookId)
if err != nil { if err != nil {
c.JsonResult(6005, "项目不存在") c.JsonResult(6005, i18n.Tr(c.Lang, "message.item_not_exist"))
return return
} }
book.PrivatelyOwned = state book.PrivatelyOwned = state
@ -253,7 +254,7 @@ func (c *BookController) PrivatelyOwned() {
if err != nil { if err != nil {
logs.Error("PrivatelyOwned => ", err) logs.Error("PrivatelyOwned => ", err)
c.JsonResult(6004, "保存失败") c.JsonResult(6004, i18n.Tr(c.Lang, "message.failed"))
} }
logs.Info("用户 【", c.Member.Account, "]修改了项目权限 ->", state) logs.Info("用户 【", c.Member.Account, "]修改了项目权限 ->", state)
c.JsonResult(0, "ok") c.JsonResult(0, "ok")
@ -265,19 +266,19 @@ func (c *BookController) Transfer() {
account := c.GetString("account") account := c.GetString("account")
if account == "" { if account == "" {
c.JsonResult(6004, "接受者账号不能为空") c.JsonResult(6004, i18n.Tr(c.Lang, "message.receive_account_empty"))
} }
member, err := models.NewMember().FindByAccount(account) member, err := models.NewMember().FindByAccount(account)
if err != nil { if err != nil {
logs.Error("FindByAccount => ", err) logs.Error("FindByAccount => ", err)
c.JsonResult(6005, "接受用户不存在") c.JsonResult(6005, i18n.Tr(c.Lang, "message.receive_account_not_exist"))
} }
if member.Status != 0 { if member.Status != 0 {
c.JsonResult(6006, "接受用户已被禁用") c.JsonResult(6006, i18n.Tr(c.Lang, "message.receive_account_disabled"))
} }
if member.MemberId == c.Member.MemberId { if member.MemberId == c.Member.MemberId {
c.JsonResult(6007, "不能转让给自己") c.JsonResult(6007, i18n.Tr(c.Lang, "message.cannot_handover_myself"))
} }
bookResult, err := c.IsPermission() bookResult, err := c.IsPermission()
@ -339,7 +340,8 @@ func (c *BookController) UploadCover() {
fileName := "cover_" + strconv.FormatInt(time.Now().UnixNano(), 16) fileName := "cover_" + strconv.FormatInt(time.Now().UnixNano(), 16)
//附件路径按照项目组织 //附件路径按照项目组织
filePath := filepath.Join("uploads", book.Identify, "images", fileName+ext) // filePath := filepath.Join("uploads", book.Identify, "images", fileName+ext)
filePath := filepath.Join(conf.WorkingDirectory, "uploads", book.Identify, "images", fileName+ext)
path := filepath.Dir(filePath) path := filepath.Dir(filePath)
@ -404,7 +406,7 @@ func (c *BookController) Users() {
pageIndex, _ := c.GetInt("page", 1) pageIndex, _ := c.GetInt("page", 1)
if key == "" { if key == "" {
c.ShowErrorPage(404, "项目不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId) book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
@ -421,7 +423,7 @@ func (c *BookController) Users() {
} }
c.Data["Model"] = *book c.Data["Model"] = *book
members, totalCount, err := models.NewMemberRelationshipResult().FindForUsersByBookId(book.BookId, pageIndex, conf.PageSize) members, totalCount, err := models.NewMemberRelationshipResult().FindForUsersByBookId(c.Lang, book.BookId, pageIndex, conf.PageSize)
if totalCount > 0 { if totalCount > 0 {
pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl()) pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl())
@ -450,25 +452,25 @@ func (c *BookController) Create() {
itemId, _ := c.GetInt("itemId") itemId, _ := c.GetInt("itemId")
if bookName == "" { if bookName == "" {
c.JsonResult(6001, "项目名称不能为空") c.JsonResult(6001, i18n.Tr(c.Lang, "message.project_name_empty"))
} }
if identify == "" { if identify == "" {
c.JsonResult(6002, "项目标识不能为空") c.JsonResult(6002, i18n.Tr(c.Lang, "message.project_id_empty"))
} }
if ok, err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`, identify); !ok || err != nil { if ok, err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`, identify); !ok || err != nil {
c.JsonResult(6003, "项目标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母开头") c.JsonResult(6003, i18n.Tr(c.Lang, "message.project_id_tips"))
} }
if strings.Count(identify, "") > 50 { if strings.Count(identify, "") > 50 {
c.JsonResult(6004, "文档标识不能超过50字") c.JsonResult(6004, i18n.Tr(c.Lang, "message.project_id_length"))
} }
if strings.Count(description, "") > 500 { if strings.Count(description, "") > 500 {
c.JsonResult(6004, "项目描述不能大于500字") c.JsonResult(6004, i18n.Tr(c.Lang, "message.project_desc_tips"))
} }
if privatelyOwned != 0 && privatelyOwned != 1 { if privatelyOwned != 0 && privatelyOwned != 1 {
privatelyOwned = 1 privatelyOwned = 1
} }
if !models.NewItemsets().Exist(itemId) { if !models.NewItemsets().Exist(itemId) {
c.JsonResult(6005, "项目空间不存在") c.JsonResult(6005, i18n.Tr(c.Lang, "message.project_space_not_exist"))
} }
if commentStatus != "open" && commentStatus != "closed" && commentStatus != "group_only" && commentStatus != "registered_only" { if commentStatus != "open" && commentStatus != "closed" && commentStatus != "group_only" && commentStatus != "registered_only" {
commentStatus = "closed" commentStatus = "closed"
@ -505,7 +507,7 @@ func (c *BookController) Create() {
} }
if books, _ := book.FindByField("identify", identify, "book_id"); len(books) > 0 { if books, _ := book.FindByField("identify", identify, "book_id"); len(books) > 0 {
c.JsonResult(6006, "项目标识已存在") c.JsonResult(6006, i18n.Tr(c.Lang, "message.project_id_existed"))
} }
book.BookName = bookName book.BookName = bookName
@ -526,9 +528,9 @@ func (c *BookController) Create() {
book.Editor = "markdown" book.Editor = "markdown"
book.Theme = "default" book.Theme = "default"
if err := book.Insert(); err != nil { if err := book.Insert(c.Lang); err != nil {
logs.Error("Insert => ", err) logs.Error("Insert => ", err)
c.JsonResult(6005, "保存项目失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} }
bookResult, err := models.NewBookResult().FindByIdentify(book.Identify, c.Member.MemberId) bookResult, err := models.NewBookResult().FindByIdentify(book.Identify, c.Member.MemberId)
@ -552,7 +554,7 @@ func (c *BookController) Copy() {
identify := strings.TrimSpace(c.GetString("identify", "")) identify := strings.TrimSpace(c.GetString("identify", ""))
if identify == "" { if identify == "" {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
book := models.NewBook() book := models.NewBook()
err := book.Copy(identify) err := book.Copy(identify)
@ -568,7 +570,7 @@ func (c *BookController) Copy() {
} }
} }
//导入zip压缩包 // 导入zip压缩包或docx
func (c *BookController) Import() { func (c *BookController) Import() {
file, moreFile, err := c.GetFile("import-file") file, moreFile, err := c.GetFile("import-file")
@ -585,32 +587,32 @@ func (c *BookController) Import() {
itemId, _ := c.GetInt("itemId") itemId, _ := c.GetInt("itemId")
if bookName == "" { if bookName == "" {
c.JsonResult(6001, "项目名称不能为空") c.JsonResult(6001, i18n.Tr(c.Lang, "message.project_name_empty"))
} }
if len([]rune(bookName)) > 500 { if len([]rune(bookName)) > 500 {
c.JsonResult(6002, "项目名称不能大于500字") c.JsonResult(6002, "项目名称不能大于500字")
} }
if identify == "" { if identify == "" {
c.JsonResult(6002, "项目标识不能为空") c.JsonResult(6002, i18n.Tr(c.Lang, "message.project_id_empty"))
} }
if ok, err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`, identify); !ok || err != nil { if ok, err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`, identify); !ok || err != nil {
c.JsonResult(6003, "项目标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母开头") c.JsonResult(6003, i18n.Tr(c.Lang, "message.project_id_tips"))
} }
if !models.NewItemsets().Exist(itemId) { if !models.NewItemsets().Exist(itemId) {
c.JsonResult(6007, "项目空间不存在") c.JsonResult(6007, i18n.Tr(c.Lang, "message.project_space_not_exist"))
} }
if strings.Count(identify, "") > 50 { if strings.Count(identify, "") > 50 {
c.JsonResult(6004, "文档标识不能超过50字") c.JsonResult(6004, i18n.Tr(c.Lang, "message.project_id_length"))
} }
ext := filepath.Ext(moreFile.Filename) ext := filepath.Ext(moreFile.Filename)
if !strings.EqualFold(ext, ".zip") { if !strings.EqualFold(ext, ".zip") && !strings.EqualFold(ext, ".docx") {
c.JsonResult(6004, "不支持的文件类型") c.JsonResult(6004, "不支持的文件类型")
} }
if books, _ := models.NewBook().FindByField("identify", identify, "book_id"); len(books) > 0 { if books, _ := models.NewBook().FindByField("identify", identify, "book_id"); len(books) > 0 {
c.JsonResult(6006, "项目标识已存在") c.JsonResult(6006, i18n.Tr(c.Lang, "message.project_id_existed"))
} }
tempPath := filepath.Join(os.TempDir(), c.CruSession.SessionID(context.TODO())) tempPath := filepath.Join(os.TempDir(), c.CruSession.SessionID(context.TODO()))
@ -639,7 +641,11 @@ func (c *BookController) Import() {
book.Editor = "markdown" book.Editor = "markdown"
book.Theme = "default" book.Theme = "default"
go book.ImportBook(tempPath) if strings.EqualFold(ext, ".zip") {
go book.ImportBook(tempPath, c.Lang)
} else if strings.EqualFold(ext, ".docx") {
go book.ImportWordBook(tempPath, c.Lang)
}
logs.Info("用户[", c.Member.Account, "]导入了项目 ->", book) logs.Info("用户[", c.Member.Account, "]导入了项目 ->", book)
@ -655,10 +661,10 @@ func (c *BookController) Import() {
// //
// if err != nil { // if err != nil {
// if err == models.ErrPermissionDenied { // if err == models.ErrPermissionDenied {
// c.JsonResult(403, "权限不足") // c.JsonResult(403, i18n.Tr(c.Lang, "message.no_permission"))
// } // }
// if err == orm.ErrNoRows { // if err == orm.ErrNoRows {
// c.JsonResult(404, "项目不存在") // c.JsonResult(404, i18n.Tr(c.Lang, "message.item_not_exist"))
// } // }
// logs.Error("生成阅读令牌失败 =>", err) // logs.Error("生成阅读令牌失败 =>", err)
// c.JsonResult(6002, err.Error()) // c.JsonResult(6002, err.Error())
@ -666,7 +672,7 @@ func (c *BookController) Import() {
// book := models.NewBook() // book := models.NewBook()
// //
// if _, err := book.Find(bookResult.BookId); err != nil { // if _, err := book.Find(bookResult.BookId); err != nil {
// c.JsonResult(6001, "项目不存在") // c.JsonResult(6001, i18n.Tr(c.Lang, "message.item_not_exist"))
// } // }
// if action == "create" { // if action == "create" {
// if bookResult.PrivatelyOwned == 0 { // if bookResult.PrivatelyOwned == 0 {
@ -708,7 +714,7 @@ func (c *BookController) Delete() {
err = models.NewBook().ThoroughDeleteBook(bookResult.BookId) err = models.NewBook().ThoroughDeleteBook(bookResult.BookId)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.JsonResult(6002, "项目不存在") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
if err != nil { if err != nil {
logs.Error("删除项目 => ", err) logs.Error("删除项目 => ", err)
@ -739,22 +745,22 @@ func (c *BookController) Release() {
if err != nil { if err != nil {
if err == models.ErrPermissionDenied { if err == models.ErrPermissionDenied {
c.JsonResult(6001, "权限不足") c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
} }
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.JsonResult(6002, "项目不存在") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
logs.Error(err) logs.Error(err)
c.JsonResult(6003, "未知错误") c.JsonResult(6003, i18n.Tr(c.Lang, "message.unknown_exception"))
} }
if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder && book.RoleId != conf.BookEditor { if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder && book.RoleId != conf.BookEditor {
c.JsonResult(6003, "权限不足") c.JsonResult(6003, i18n.Tr(c.Lang, "message.no_permission"))
} }
bookId = book.BookId bookId = book.BookId
} }
go models.NewBook().ReleaseContent(bookId) go models.NewBook().ReleaseContent(bookId, c.Lang)
c.JsonResult(0, "发布任务已推送到任务队列,稍后将在后台执行。") c.JsonResult(0, i18n.Tr(c.Lang, "message.publish_to_queue"))
} }
//文档排序. //文档排序.
@ -770,7 +776,7 @@ func (c *BookController) SaveSort() {
if c.Member.IsAdministrator() { if c.Member.IsAdministrator() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil || book == nil { if err != nil || book == nil {
c.JsonResult(6001, "项目不存在") c.JsonResult(6001, i18n.Tr(c.Lang, "message.item_not_exist"))
return return
} }
bookId = book.BookId bookId = book.BookId
@ -782,7 +788,7 @@ func (c *BookController) SaveSort() {
c.Abort("403") c.Abort("403")
} }
if bookResult.RoleId == conf.BookObserver { if bookResult.RoleId == conf.BookObserver {
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookId = bookResult.BookId bookId = bookResult.BookId
} }
@ -806,7 +812,7 @@ func (c *BookController) SaveSort() {
continue continue
} }
if doc.BookId != bookId { if doc.BookId != bookId {
logs.Info("%s", "权限错误") logs.Info("%s", i18n.Tr(c.Lang, "message.no_permission"))
continue continue
} }
sort, ok := item["sort"].(float64) sort, ok := item["sort"].(float64)
@ -846,15 +852,15 @@ func (c *BookController) Team() {
pageIndex, _ := c.GetInt("page", 1) pageIndex, _ := c.GetInt("page", 1)
if key == "" { if key == "" {
c.ShowErrorPage(404, "项目不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId) book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
if err != nil || book == nil { if err != nil || book == nil {
if err == models.ErrPermissionDenied { if err == models.ErrPermissionDenied {
c.ShowErrorPage(403, "权限不足") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
c.ShowErrorPage(500, "系统错误") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
return return
} }
//如果不是创始人也不是管理员则不能操作 //如果不是创始人也不是管理员则不能操作
@ -925,7 +931,7 @@ func (c *BookController) TeamDelete() {
teamId, _ := c.GetInt("teamId") teamId, _ := c.GetInt("teamId")
if teamId <= 0 { if teamId <= 0 {
c.JsonResult(5001, "参数错误") c.JsonResult(5001, i18n.Tr(c.Lang, "message.param_error"))
} }
book, err := c.IsPermission() book, err := c.IsPermission()
@ -990,22 +996,22 @@ func (c *BookController) IsPermission() (*models.BookResult, error) {
identify := c.GetString("identify") identify := c.GetString("identify")
if identify == "" { if identify == "" {
return nil, errors.New("参数错误") return nil, errors.New(i18n.Tr(c.Lang, "message.param_error"))
} }
book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil { if err != nil {
if err == models.ErrPermissionDenied { if err == models.ErrPermissionDenied {
return book, errors.New("权限不足") return book, errors.New(i18n.Tr(c.Lang, "message.no_permission"))
} }
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
return book, errors.New("项目不存在") return book, errors.New(i18n.Tr(c.Lang, "message.item_not_exist"))
} }
return book, err return book, err
} }
if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder { if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder {
return book, errors.New("权限不足") return book, errors.New(i18n.Tr(c.Lang, "message.no_permission"))
} }
return book, nil return book, nil
} }

View File

@ -5,6 +5,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/models" "github.com/mindoc-org/mindoc/models"
) )
@ -20,7 +21,7 @@ func (c *BookMemberController) AddMember() {
roleId, _ := c.GetInt("role_id", 3) roleId, _ := c.GetInt("role_id", 3)
logs.Info(account) logs.Info(account)
if identify == "" || account <= 0 { if identify == "" || account <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
book, err := c.IsPermission() book, err := c.IsPermission()
@ -31,14 +32,14 @@ func (c *BookMemberController) AddMember() {
member := models.NewMember() member := models.NewMember()
if _, err := member.Find(account); err != nil { if _, err := member.Find(account); err != nil {
c.JsonResult(404, "用户不存在") c.JsonResult(404, i18n.Tr(c.Lang, "message.user_not_existed"))
} }
if member.Status == 1 { if member.Status == 1 {
c.JsonResult(6003, "用户已被禁用") c.JsonResult(6003, i18n.Tr(c.Lang, "message.user_disable"))
} }
if _, err := models.NewRelationship().FindForRoleId(book.BookId, member.MemberId); err == nil { if _, err := models.NewRelationship().FindForRoleId(book.BookId, member.MemberId); err == nil {
c.JsonResult(6003, "用户已存在该项目中") c.JsonResult(6003, i18n.Tr(c.Lang, "message.user_exist_in_proj"))
} }
relationship := models.NewRelationship() relationship := models.NewRelationship()
@ -51,7 +52,7 @@ func (c *BookMemberController) AddMember() {
memberRelationshipResult.RoleId = conf.BookRole(roleId) memberRelationshipResult.RoleId = conf.BookRole(roleId)
memberRelationshipResult.RelationshipId = relationship.RelationshipId memberRelationshipResult.RelationshipId = relationship.RelationshipId
memberRelationshipResult.BookId = book.BookId memberRelationshipResult.BookId = book.BookId
memberRelationshipResult.ResolveRoleName() memberRelationshipResult.ResolveRoleName(c.Lang)
c.JsonResult(0, "ok", memberRelationshipResult) c.JsonResult(0, "ok", memberRelationshipResult)
} }
@ -65,33 +66,33 @@ func (c *BookMemberController) ChangeRole() {
role, _ := c.GetInt("role_id", 0) role, _ := c.GetInt("role_id", 0)
if identify == "" || memberId <= 0 { if identify == "" || memberId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
if memberId == c.Member.MemberId { if memberId == c.Member.MemberId {
c.JsonResult(6006, "不能变更自己的权限") c.JsonResult(6006, i18n.Tr(c.Lang, "message.cannot_change_own_priv"))
} }
book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil { if err != nil {
if err == models.ErrPermissionDenied { if err == models.ErrPermissionDenied {
c.JsonResult(403, "权限不足") c.JsonResult(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.JsonResult(404, "项目不存在") c.JsonResult(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
c.JsonResult(6002, err.Error()) c.JsonResult(6002, err.Error())
} }
if book.RoleId != 0 && book.RoleId != 1 { if book.RoleId != 0 && book.RoleId != 1 {
c.JsonResult(403, "权限不足") c.JsonResult(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
member := models.NewMember() member := models.NewMember()
if _, err := member.Find(memberId); err != nil { if _, err := member.Find(memberId); err != nil {
c.JsonResult(6003, "用户不存在") c.JsonResult(6003, i18n.Tr(c.Lang, "message.user_not_existed"))
} }
if member.Status == 1 { if member.Status == 1 {
c.JsonResult(6004, "用户已被禁用") c.JsonResult(6004, i18n.Tr(c.Lang, "message.user_disable"))
} }
relationship, err := models.NewRelationship().UpdateRoleId(book.BookId, memberId, conf.BookRole(role)) relationship, err := models.NewRelationship().UpdateRoleId(book.BookId, memberId, conf.BookRole(role))
@ -105,7 +106,7 @@ func (c *BookMemberController) ChangeRole() {
memberRelationshipResult.RoleId = relationship.RoleId memberRelationshipResult.RoleId = relationship.RoleId
memberRelationshipResult.RelationshipId = relationship.RelationshipId memberRelationshipResult.RelationshipId = relationship.RelationshipId
memberRelationshipResult.BookId = book.BookId memberRelationshipResult.BookId = book.BookId
memberRelationshipResult.ResolveRoleName() memberRelationshipResult.ResolveRoleName(c.Lang)
c.JsonResult(0, "ok", memberRelationshipResult) c.JsonResult(0, "ok", memberRelationshipResult)
} }
@ -116,25 +117,25 @@ func (c *BookMemberController) RemoveMember() {
member_id, _ := c.GetInt("member_id", 0) member_id, _ := c.GetInt("member_id", 0)
if identify == "" || member_id <= 0 { if identify == "" || member_id <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
if member_id == c.Member.MemberId { if member_id == c.Member.MemberId {
c.JsonResult(6006, "不能删除自己") c.JsonResult(6006, i18n.Tr(c.Lang, "message.cannot_delete_self"))
} }
book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil { if err != nil {
if err == models.ErrPermissionDenied { if err == models.ErrPermissionDenied {
c.JsonResult(403, "权限不足") c.JsonResult(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.JsonResult(404, "项目不存在") c.JsonResult(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
c.JsonResult(6002, err.Error()) c.JsonResult(6002, err.Error())
} }
//如果不是创始人也不是管理员则不能操作 //如果不是创始人也不是管理员则不能操作
if book.RoleId != conf.BookFounder && book.RoleId != conf.BookAdmin { if book.RoleId != conf.BookFounder && book.RoleId != conf.BookAdmin {
c.JsonResult(403, "权限不足") c.JsonResult(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
err = models.NewRelationship().DeleteByBookIdAndMemberId(book.BookId, member_id) err = models.NewRelationship().DeleteByBookIdAndMemberId(book.BookId, member_id)
@ -150,15 +151,15 @@ func (c *BookMemberController) IsPermission() (*models.BookResult, error) {
if err != nil { if err != nil {
if err == models.ErrPermissionDenied { if err == models.ErrPermissionDenied {
return book, errors.New("权限不足") return book, errors.New(i18n.Tr(c.Lang, "message.no_permission"))
} }
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
return book, errors.New("项目不存在") return book, errors.New(i18n.Tr(c.Lang, "message.item_not_exist"))
} }
return book, err return book, err
} }
if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder { if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder {
return book, errors.New("权限不足") return book, errors.New(i18n.Tr(c.Lang, "message.no_permission"))
} }
return book, nil return book, nil
} }

View File

@ -18,6 +18,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
"github.com/boombuler/barcode" "github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr" "github.com/boombuler/barcode/qr"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
@ -43,7 +44,7 @@ func (c *DocumentController) Index() {
token := c.GetString("token") token := c.GetString("token")
if identify == "" { if identify == "" {
c.ShowErrorPage(404, "项目不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
// 如果没有开启匿名访问则跳转到登录 // 如果没有开启匿名访问则跳转到登录
@ -66,17 +67,9 @@ func (c *DocumentController) Index() {
c.Data["Content"] = template.HTML(doc.Release) c.Data["Content"] = template.HTML(doc.Release)
c.Data["Description"] = utils.AutoSummary(doc.Release, 120) c.Data["Description"] = utils.AutoSummary(doc.Release, 120)
doc.IncrViewCount(doc.DocumentId)
c.Data["ViewCount"] = doc.ViewCount + 1
c.Data["DocumentId"] = doc.DocumentId
// 获取评论、分页
comments, count, _ := models.NewComment().QueryCommentByDocumentId(doc.DocumentId, 1, conf.PageSize, c.Member)
page := pagination.PageUtil(int(count), 1, conf.PageSize, comments)
c.Data["Page"] = page
} }
} else { } else {
c.Data["Title"] = "概要" c.Data["Title"] = i18n.Tr(c.Lang, "blog.summary")
c.Data["Content"] = template.HTML(blackfriday.Run([]byte(bookResult.Description))) c.Data["Content"] = template.HTML(blackfriday.Run([]byte(bookResult.Description)))
} }
@ -84,14 +77,15 @@ func (c *DocumentController) Index() {
if err != nil { if err != nil {
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(404, "当前项目没有文档") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.no_doc_in_cur_proj"))
} else { } else {
logs.Error("生成项目文档树时出错 -> ", err) logs.Error("生成项目文档树时出错 -> ", err)
c.ShowErrorPage(500, "生成项目文档树时出错") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.build_doc_tree_error"))
} }
} }
c.Data["Model"] = bookResult c.Data["Model"] = bookResult
c.Data["Result"] = template.HTML(tree) c.Data["Result"] = template.HTML(tree)
} }
// 阅读文档 // 阅读文档
@ -105,7 +99,7 @@ func (c *DocumentController) Read() {
c.Data["DocumentId"] = id c.Data["DocumentId"] = id
if identify == "" || id == "" { if identify == "" || id == "" {
c.ShowErrorPage(404, "项目不存或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
// 如果没有开启匿名访问则跳转到登录 // 如果没有开启匿名访问则跳转到登录
@ -119,46 +113,40 @@ func (c *DocumentController) Read() {
c.TplName = fmt.Sprintf("document/%s_read.tpl", bookResult.Theme) c.TplName = fmt.Sprintf("document/%s_read.tpl", bookResult.Theme)
doc := models.NewDocument() doc := models.NewDocument()
if docId, err := strconv.Atoi(id); err == nil { if docId, err := strconv.Atoi(id); err == nil {
doc, err = doc.FromCacheById(docId) doc, err = doc.FromCacheById(docId)
if err != nil || doc == nil { if err != nil || doc == nil {
logs.Error("从缓存中读取文档时失败 ->", err) logs.Error("从缓存中读取文档时失败 ->", err)
c.ShowErrorPage(404, "文档不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.doc_not_exist"))
return return
} }
} else { } else {
doc, err = doc.FromCacheByIdentify(id, bookResult.BookId) doc, err = doc.FromCacheByIdentify(id, bookResult.BookId)
if err != nil || doc == nil { if err != nil || doc == nil {
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(404, "文档不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.doc_not_exist"))
} else { } else {
logs.Error("从缓存查询文档时出错 ->", err) logs.Error("从数据库查询文档时出错 ->", err)
c.ShowErrorPage(500, "未知异常") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.unknown_exception"))
} }
return return
} }
} }
if doc.BookId != bookResult.BookId { if doc.BookId != bookResult.BookId {
c.ShowErrorPage(404, "文档不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.doc_not_exist"))
} }
doc.Lang = c.Lang
doc.Processor() doc.Processor()
c.Data["DocumentId"] = doc.DocumentId
attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId) attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId)
if err == nil { if err == nil {
doc.AttachList = attach doc.AttachList = attach
} }
doc.IncrViewCount(doc.DocumentId) doc.IncrViewCount(doc.DocumentId)
c.Data["ViewCount"] = doc.ViewCount + 1 doc.ViewCount = doc.ViewCount + 1
doc.PutToCache()
// 获取评论、分页
comments, count, _ := models.NewComment().QueryCommentByDocumentId(doc.DocumentId, 1, conf.PageSize, c.Member)
page := pagination.PageUtil(int(count), 1, conf.PageSize, comments)
c.Data["Page"] = page
if c.IsAjax() { if c.IsAjax() {
var data struct { var data struct {
@ -167,16 +155,12 @@ func (c *DocumentController) Read() {
Title string `json:"title"` Title string `json:"title"`
Version int64 `json:"version"` Version int64 `json:"version"`
ViewCount int `json:"view_count"` ViewCount int `json:"view_count"`
DocId int `json:"doc_id"`
Page pagination.Page `json:"page"`
} }
data.DocTitle = doc.DocumentName data.DocTitle = doc.DocumentName
data.Body = doc.Release data.Body = doc.Release
data.Title = doc.DocumentName + " - Powered by MinDoc" data.Title = doc.DocumentName + " - Powered by MinDoc"
data.Version = doc.Version data.Version = doc.Version
data.ViewCount = doc.ViewCount + 1 data.ViewCount = doc.ViewCount
data.DocId = doc.DocumentId
data.Page = page
c.JsonResult(0, "ok", data) c.JsonResult(0, "ok", data)
} }
@ -186,7 +170,7 @@ func (c *DocumentController) Read() {
if err != nil && err != orm.ErrNoRows { if err != nil && err != orm.ErrNoRows {
logs.Error("生成项目文档树时出错 ->", err) logs.Error("生成项目文档树时出错 ->", err)
c.ShowErrorPage(500, "生成项目文档树时出错") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.build_doc_tree_error"))
} }
c.Data["Description"] = utils.AutoSummary(doc.Release, 120) c.Data["Description"] = utils.AutoSummary(doc.Release, 120)
@ -195,6 +179,7 @@ func (c *DocumentController) Read() {
c.Data["Result"] = template.HTML(tree) c.Data["Result"] = template.HTML(tree)
c.Data["Title"] = doc.DocumentName c.Data["Title"] = doc.DocumentName
c.Data["Content"] = template.HTML(doc.Release) c.Data["Content"] = template.HTML(doc.Release)
c.Data["ViewCount"] = doc.ViewCount
} }
// 编辑文档 // 编辑文档
@ -203,7 +188,7 @@ func (c *DocumentController) Edit() {
identify := c.Ctx.Input.Param(":key") identify := c.Ctx.Input.Param(":key")
if identify == "" { if identify == "" {
c.ShowErrorPage(404, "无法解析项目标识") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.project_id_error"))
} }
bookResult := models.NewBookResult() bookResult := models.NewBookResult()
@ -213,7 +198,7 @@ func (c *DocumentController) Edit() {
if c.Member.IsAdministrator() { if c.Member.IsAdministrator() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookResult = models.NewBookResult().ToBookResult(*book) bookResult = models.NewBookResult().ToBookResult(*book)
@ -222,15 +207,15 @@ func (c *DocumentController) Edit() {
if err != nil { if err != nil {
if err == orm.ErrNoRows || err == models.ErrPermissionDenied { if err == orm.ErrNoRows || err == models.ErrPermissionDenied {
c.ShowErrorPage(403, "项目不存在或没有权限") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} else { } else {
logs.Error("查询项目时出错 -> ", err) logs.Error("查询项目时出错 -> ", err)
c.ShowErrorPage(500, "查询项目时出错") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
} }
return return
} }
if bookResult.RoleId == conf.BookObserver { if bookResult.RoleId == conf.BookObserver {
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
} }
@ -238,6 +223,8 @@ func (c *DocumentController) Edit() {
if bookResult.Editor == "markdown" { if bookResult.Editor == "markdown" {
c.TplName = "document/markdown_edit_template.tpl" c.TplName = "document/markdown_edit_template.tpl"
} else if bookResult.Editor == "html" { } else if bookResult.Editor == "html" {
c.TplName = "document/html_edit_template.tpl"
} else if bookResult.Editor == "new_html" {
c.TplName = "document/new_html_edit_template.tpl" c.TplName = "document/new_html_edit_template.tpl"
} else { } else {
c.TplName = "document/" + bookResult.Editor + "_edit_template.tpl" c.TplName = "document/" + bookResult.Editor + "_edit_template.tpl"
@ -284,11 +271,11 @@ func (c *DocumentController) Create() {
isOpen, _ := c.GetInt("is_open", 0) isOpen, _ := c.GetInt("is_open", 0)
if identify == "" { if identify == "" {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
if docName == "" { if docName == "" {
c.JsonResult(6004, "文档名称不能为空") c.JsonResult(6004, i18n.Tr(c.Lang, "message.doc_name_empty"))
} }
bookId := 0 bookId := 0
@ -298,7 +285,7 @@ func (c *DocumentController) Create() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_existed_or_no_permit"))
} }
bookId = book.BookId bookId = book.BookId
@ -307,7 +294,7 @@ func (c *DocumentController) Create() {
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
logs.Error("FindByIdentify => ", err) logs.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_existed_or_no_permit"))
} }
bookId = bookResult.BookId bookId = bookResult.BookId
@ -315,18 +302,18 @@ func (c *DocumentController) Create() {
if docIdentify != "" { if docIdentify != "" {
if ok, err := regexp.MatchString(`[a-z]+[a-zA-Z0-9_.\-]*$`, docIdentify); !ok || err != nil { if ok, err := regexp.MatchString(`[a-z]+[a-zA-Z0-9_.\-]*$`, docIdentify); !ok || err != nil {
c.JsonResult(6003, "文档标识只能包含小写字母、数字,以及“-”、“.”和“_”符号") c.JsonResult(6003, i18n.Tr(c.Lang, "message.project_id_tips"))
} }
d, _ := models.NewDocument().FindByIdentityFirst(docIdentify, bookId) d, _ := models.NewDocument().FindByIdentityFirst(docIdentify, bookId)
if d.DocumentId > 0 && d.DocumentId != docId { if d.DocumentId > 0 && d.DocumentId != docId {
c.JsonResult(6006, "文档标识已被使用") c.JsonResult(6006, i18n.Tr(c.Lang, "message.project_id_existed"))
} }
} }
if parentId > 0 { if parentId > 0 {
doc, err := models.NewDocument().Find(parentId) doc, err := models.NewDocument().Find(parentId)
if err != nil || doc.BookId != bookId { if err != nil || doc.BookId != bookId {
c.JsonResult(6003, "父分类不存在") c.JsonResult(6003, i18n.Tr(c.Lang, "message.parent_id_not_existed"))
} }
} }
@ -351,7 +338,7 @@ func (c *DocumentController) Create() {
if err := document.InsertOrUpdate(); err != nil { if err := document.InsertOrUpdate(); err != nil {
logs.Error("添加或更新文档时出错 -> ", err) logs.Error("添加或更新文档时出错 -> ", err)
c.JsonResult(6005, "保存失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} else { } else {
c.JsonResult(0, "ok", document) c.JsonResult(0, "ok", document)
} }
@ -364,7 +351,7 @@ func (c *DocumentController) Upload() {
isAttach := true isAttach := true
if identify == "" { if identify == "" {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
name := "editormd-file-file" name := "editormd-file-file"
@ -374,7 +361,7 @@ func (c *DocumentController) Upload() {
name = "editormd-image-file" name = "editormd-image-file"
file, moreFile, err = c.GetFile(name) file, moreFile, err = c.GetFile(name)
if err == http.ErrMissingFile || moreFile == nil { if err == http.ErrMissingFile || moreFile == nil {
c.JsonResult(6003, "没有发现需要上传的文件") c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_empty"))
return return
} }
} }
@ -390,17 +377,17 @@ func (c *DocumentController) Upload() {
} }
if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize() { if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize() {
c.JsonResult(6009, "文件大小超过了限定的最大值") c.JsonResult(6009, i18n.Tr(c.Lang, "message.upload_file_size_limit"))
} }
ext := filepath.Ext(moreFile.Filename) ext := filepath.Ext(moreFile.Filename)
//文件必须带有后缀名 //文件必须带有后缀名
if ext == "" { if ext == "" {
c.JsonResult(6003, "无法解析文件的格式") c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_type_error"))
} }
//如果文件类型设置为 * 标识不限制文件类型 //如果文件类型设置为 * 标识不限制文件类型
if conf.IsAllowUploadFileExt(ext) == false { if conf.IsAllowUploadFileExt(ext) == false {
c.JsonResult(6004, "不允许的文件类型") c.JsonResult(6004, i18n.Tr(c.Lang, "message.upload_file_type_error"))
} }
bookId := 0 bookId := 0
@ -410,7 +397,7 @@ func (c *DocumentController) Upload() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
c.JsonResult(6006, "文档不存在或权限不足") c.JsonResult(6006, i18n.Tr(c.Lang, "message.doc_not_exist_or_no_permit"))
} }
bookId = book.BookId bookId = book.BookId
@ -420,7 +407,7 @@ func (c *DocumentController) Upload() {
if err != nil { if err != nil {
logs.Error("DocumentController.Edit => ", err) logs.Error("DocumentController.Edit => ", err)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.JsonResult(6006, "权限不足") c.JsonResult(6006, i18n.Tr(c.Lang, "message.no_permission"))
} }
c.JsonResult(6001, err.Error()) c.JsonResult(6001, err.Error())
@ -428,7 +415,7 @@ func (c *DocumentController) Upload() {
// 如果没有编辑权限 // 如果没有编辑权限
if book.RoleId != conf.BookEditor && book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder { if book.RoleId != conf.BookEditor && book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder {
c.JsonResult(6006, "权限不足") c.JsonResult(6006, i18n.Tr(c.Lang, "message.no_permission"))
} }
bookId = book.BookId bookId = book.BookId
@ -437,11 +424,11 @@ func (c *DocumentController) Upload() {
if docId > 0 { if docId > 0 {
doc, err := models.NewDocument().Find(docId) doc, err := models.NewDocument().Find(docId)
if err != nil { if err != nil {
c.JsonResult(6007, "文档不存在") c.JsonResult(6007, i18n.Tr(c.Lang, "message.doc_not_exist"))
} }
if doc.BookId != bookId { if doc.BookId != bookId {
c.JsonResult(6008, "文档不属于指定的项目") c.JsonResult(6008, i18n.Tr(c.Lang, "message.doc_not_belong_project"))
} }
} }
@ -463,7 +450,7 @@ func (c *DocumentController) Upload() {
if err != nil { if err != nil {
logs.Error("保存文件失败 -> ", err) logs.Error("保存文件失败 -> ", err)
c.JsonResult(6005, "保存文件失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} }
attachment := models.NewAttachment() attachment := models.NewAttachment()
@ -496,7 +483,7 @@ func (c *DocumentController) Upload() {
if err != nil { if err != nil {
os.Remove(filePath) os.Remove(filePath)
logs.Error("文件保存失败 ->", err) logs.Error("文件保存失败 ->", err)
c.JsonResult(6006, "文件保存失败") c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed"))
} }
if attachment.HttpPath == "" { if attachment.HttpPath == "" {
@ -504,7 +491,7 @@ func (c *DocumentController) Upload() {
if err := attachment.Update(); err != nil { if err := attachment.Update(); err != nil {
logs.Error("保存文件失败 ->", err) logs.Error("保存文件失败 ->", err)
c.JsonResult(6005, "保存文件失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} }
} }
@ -546,10 +533,10 @@ func (c *DocumentController) DownloadAttachment() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(404, "项目不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} else { } else {
logs.Error("查找项目时出错 ->", err) logs.Error("查找项目时出错 ->", err)
c.ShowErrorPage(500, "系统错误") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
} }
} }
@ -557,7 +544,7 @@ func (c *DocumentController) DownloadAttachment() {
if c.Member == nil || c.Member.Role != conf.MemberSuperRole { if c.Member == nil || c.Member.Role != conf.MemberSuperRole {
// 如果项目是私有的,并且 token 不正确 // 如果项目是私有的,并且 token 不正确
if (book.PrivatelyOwned == 1 && token == "") || (book.PrivatelyOwned == 1 && book.PrivateToken != token) { if (book.PrivatelyOwned == 1 && token == "") || (book.PrivatelyOwned == 1 && book.PrivateToken != token) {
c.ShowErrorPage(403, "权限不足") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
} }
@ -572,14 +559,14 @@ func (c *DocumentController) DownloadAttachment() {
if err != nil { if err != nil {
logs.Error("查找附件时出错 -> ", err) logs.Error("查找附件时出错 -> ", err)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(404, "附件不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} else { } else {
c.ShowErrorPage(500, "查找附件时出错") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
} }
} }
if attachment.BookId != bookId { if attachment.BookId != bookId {
c.ShowErrorPage(404, "附件不存在或已删除") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} }
c.Ctx.Output.Download(filepath.Join(conf.WorkingDirectory, attachment.FilePath), attachment.FileName) c.Ctx.Output.Download(filepath.Join(conf.WorkingDirectory, attachment.FilePath), attachment.FileName)
@ -592,39 +579,39 @@ func (c *DocumentController) RemoveAttachment() {
attachId, _ := c.GetInt("attach_id") attachId, _ := c.GetInt("attach_id")
if attachId <= 0 { if attachId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
attach, err := models.NewAttachment().Find(attachId) attach, err := models.NewAttachment().Find(attachId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6002, "附件不存在") c.JsonResult(6002, i18n.Tr(c.Lang, "message.attachment_not_exist"))
} }
document, err := models.NewDocument().Find(attach.DocumentId) document, err := models.NewDocument().Find(attach.DocumentId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6003, "文档不存在") c.JsonResult(6003, i18n.Tr(c.Lang, "message.doc_not_exist"))
} }
if c.Member.Role != conf.MemberSuperRole { if c.Member.Role != conf.MemberSuperRole {
rel, err := models.NewRelationship().FindByBookIdAndMemberId(document.BookId, c.Member.MemberId) rel, err := models.NewRelationship().FindByBookIdAndMemberId(document.BookId, c.Member.MemberId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6004, "权限不足") c.JsonResult(6004, i18n.Tr(c.Lang, "message.no_permission"))
} }
if rel.RoleId == conf.BookObserver { if rel.RoleId == conf.BookObserver {
c.JsonResult(6004, "权限不足") c.JsonResult(6004, i18n.Tr(c.Lang, "message.no_permission"))
} }
} }
err = attach.Delete() err = attach.Delete()
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6005, "删除失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} }
os.Remove(filepath.Join(conf.WorkingDirectory, attach.FilePath)) os.Remove(filepath.Join(conf.WorkingDirectory, attach.FilePath))
@ -646,7 +633,7 @@ func (c *DocumentController) Delete() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
logs.Error("FindByIdentify => ", err) logs.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookId = book.BookId bookId = book.BookId
@ -655,31 +642,31 @@ func (c *DocumentController) Delete() {
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
logs.Error("FindByIdentify => ", err) logs.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookId = bookResult.BookId bookId = bookResult.BookId
} }
if docId <= 0 { if docId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
doc, err := models.NewDocument().Find(docId) doc, err := models.NewDocument().Find(docId)
if err != nil { if err != nil {
logs.Error("Delete => ", err) logs.Error("Delete => ", err)
c.JsonResult(6003, "删除失败") c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed"))
} }
// 如果文档所属项目错误 // 如果文档所属项目错误
if doc.BookId != bookId { if doc.BookId != bookId {
c.JsonResult(6004, "参数错误") c.JsonResult(6004, i18n.Tr(c.Lang, "message.param_error"))
} }
// 递归删除项目下的文档以及子文档 // 递归删除项目下的文档以及子文档
err = doc.RecursiveDocument(doc.DocumentId) err = doc.RecursiveDocument(doc.DocumentId)
if err != nil { if err != nil {
c.JsonResult(6005, "删除失败") c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
} }
// 重置文档数量统计 // 重置文档数量统计
@ -705,7 +692,7 @@ func (c *DocumentController) Content() {
if c.Member.IsAdministrator() { if c.Member.IsAdministrator() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil || book == nil { if err != nil || book == nil {
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
return return
} }
@ -716,7 +703,7 @@ func (c *DocumentController) Content() {
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
logs.Error("项目不存在或权限不足 -> ", err) logs.Error("项目不存在或权限不足 -> ", err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookId = bookResult.BookId bookId = bookResult.BookId
@ -724,7 +711,7 @@ func (c *DocumentController) Content() {
} }
if docId <= 0 { if docId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
if c.Ctx.Input.IsPost() { if c.Ctx.Input.IsPost() {
@ -734,19 +721,18 @@ func (c *DocumentController) Content() {
isCover := c.GetString("cover") isCover := c.GetString("cover")
doc, err := models.NewDocument().Find(docId) doc, err := models.NewDocument().Find(docId)
if err != nil || doc == nil { if err != nil || doc == nil {
c.JsonResult(6003, "读取文档错误") c.JsonResult(6003, i18n.Tr(c.Lang, "message.read_file_error"))
return return
} }
if doc.BookId != bookId { if doc.BookId != bookId {
c.JsonResult(6004, "保存的文档不属于指定项目") c.JsonResult(6004, i18n.Tr(c.Lang, "message.dock_not_belong_project"))
} }
if doc.Version != version && !strings.EqualFold(isCover, "yes") { if doc.Version != version && !strings.EqualFold(isCover, "yes") {
logs.Info("%d|", version, doc.Version) logs.Info("%d|", version, doc.Version)
c.JsonResult(6005, "文档已被修改确定要覆盖吗?") c.JsonResult(6005, i18n.Tr(c.Lang, "message.confirm_override_doc"))
} }
history := models.NewDocumentHistory() history := models.NewDocumentHistory()
@ -759,7 +745,7 @@ func (c *DocumentController) Content() {
history.ParentId = doc.ParentId history.ParentId = doc.ParentId
history.Version = time.Now().Unix() history.Version = time.Now().Unix()
history.Action = "modify" history.Action = "modify"
history.ActionName = "修改文档" history.ActionName = i18n.Tr(c.Lang, "doc.modify_doc")
if markdown == "" && content != "" { if markdown == "" && content != "" {
doc.Markdown = content doc.Markdown = content
@ -773,7 +759,7 @@ func (c *DocumentController) Content() {
if err := doc.InsertOrUpdate(); err != nil { if err := doc.InsertOrUpdate(); err != nil {
logs.Error("InsertOrUpdate => ", err) logs.Error("InsertOrUpdate => ", err)
c.JsonResult(6006, "保存失败") c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed"))
} }
// 如果启用了文档历史,则添加历史文档 // 如果启用了文档历史,则添加历史文档
@ -790,9 +776,10 @@ func (c *DocumentController) Content() {
//如果启用了自动发布 //如果启用了自动发布
if autoRelease { if autoRelease {
go func() { go func() {
doc.Lang = c.Lang
err := doc.ReleaseContent() err := doc.ReleaseContent()
if err == nil { if err == nil {
logs.Informational("文档自动发布成功 -> document_id=%d;document_name=%s", doc.DocumentId, doc.DocumentName) logs.Informational(i18n.Tr(c.Lang, "message.doc_auto_published")+"-> document_id=%d;document_name=%s", doc.DocumentId, doc.DocumentName)
} }
}() }()
} }
@ -802,7 +789,7 @@ func (c *DocumentController) Content() {
doc, err := models.NewDocument().Find(docId) doc, err := models.NewDocument().Find(docId)
if err != nil { if err != nil {
c.JsonResult(6003, "文档不存在") c.JsonResult(6003, i18n.Tr(c.Lang, "message.doc_not_exist"))
return return
} }
@ -820,7 +807,7 @@ func (c *DocumentController) Export() {
identify := c.Ctx.Input.Param(":key") identify := c.Ctx.Input.Param(":key")
if identify == "" { if identify == "" {
c.ShowErrorPage(500, "参数错误") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.param_error"))
} }
output := c.GetString("output") output := c.GetString("output")
@ -832,7 +819,7 @@ func (c *DocumentController) Export() {
return return
} }
if !conf.GetEnableExport() { if !conf.GetEnableExport() {
c.ShowErrorPage(500, "系统没有开启导出功能") c.ShowErrorPage(500, i18n.Tr(c.Lang, "export_func_disable"))
} }
bookResult := models.NewBookResult() bookResult := models.NewBookResult()
@ -840,10 +827,10 @@ func (c *DocumentController) Export() {
book, err := models.NewBook().FindByIdentify(identify) book, err := models.NewBook().FindByIdentify(identify)
if err != nil { if err != nil {
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(404, "项目不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} else { } else {
logs.Error("查找项目时出错 ->", err) logs.Error("查找项目时出错 ->", err)
c.ShowErrorPage(500, "查找项目时出错") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
} }
} }
bookResult = models.NewBookResult().ToBookResult(*book) bookResult = models.NewBookResult().ToBookResult(*book)
@ -851,7 +838,7 @@ func (c *DocumentController) Export() {
bookResult = c.isReadable(identify, token) bookResult = c.isReadable(identify, token)
} }
if !bookResult.IsDownload { if !bookResult.IsDownload {
c.ShowErrorPage(200, "当前项目没有开启导出功能") c.ShowErrorPage(200, i18n.Tr(c.Lang, "message.cur_project_export_func_disable"))
} }
if !strings.HasPrefix(bookResult.Cover, "http:://") && !strings.HasPrefix(bookResult.Cover, "https:://") { if !strings.HasPrefix(bookResult.Cover, "http:://") && !strings.HasPrefix(bookResult.Cover, "https:://") {
@ -860,12 +847,12 @@ func (c *DocumentController) Export() {
if output == "markdown" { if output == "markdown" {
if bookResult.Editor != "markdown" { if bookResult.Editor != "markdown" {
c.ShowErrorPage(500, "当前项目不支持Markdown编辑器") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.cur_project_not_support_md"))
} }
p, err := bookResult.ExportMarkdown(c.CruSession.SessionID(context.TODO())) p, err := bookResult.ExportMarkdown(c.CruSession.SessionID(context.TODO()))
if err != nil { if err != nil {
c.ShowErrorPage(500, "导出文档失败") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.failed"))
} }
c.Ctx.Output.Download(p, bookResult.BookName+".zip") c.Ctx.Output.Download(p, bookResult.BookName+".zip")
@ -898,15 +885,15 @@ func (c *DocumentController) Export() {
} else if output == "pdf" || output == "epub" || output == "docx" || output == "mobi" { } else if output == "pdf" || output == "epub" || output == "docx" || output == "mobi" {
if err := models.BackgroundConvert(c.CruSession.SessionID(context.TODO()), bookResult); err != nil && err != gopool.ErrHandlerIsExist { if err := models.BackgroundConvert(c.CruSession.SessionID(context.TODO()), bookResult); err != nil && err != gopool.ErrHandlerIsExist {
c.ShowErrorPage(500, "导出失败,请查看系统日志") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.export_failed"))
} }
c.ShowErrorPage(200, "文档正在后台转换,请稍后再下载") c.ShowErrorPage(200, i18n.Tr(c.Lang, "message.file_converting"))
} else { } else {
c.ShowErrorPage(200, "不支持的文件格式") c.ShowErrorPage(200, i18n.Tr(c.Lang, "message.unsupport_file_type"))
} }
c.ShowErrorPage(404, "项目没有导出文件") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.no_exportable_file"))
} }
// 生成项目访问的二维码 // 生成项目访问的二维码
@ -917,20 +904,20 @@ func (c *DocumentController) QrCode() {
book, err := models.NewBook().FindByIdentify(identify) book, err := models.NewBook().FindByIdentify(identify)
if err != nil || book.BookId <= 0 { if err != nil || book.BookId <= 0 {
c.ShowErrorPage(404, "项目不存在") c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
uri := conf.URLFor("DocumentController.Index", ":key", identify) uri := conf.URLFor("DocumentController.Index", ":key", identify)
code, err := qr.Encode(uri, qr.L, qr.Unicode) code, err := qr.Encode(uri, qr.L, qr.Unicode)
if err != nil { if err != nil {
logs.Error("生成二维码失败 ->", err) logs.Error("生成二维码失败 ->", err)
c.ShowErrorPage(500, "生成二维码失败") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.gen_qrcode_failed"))
} }
code, err = barcode.Scale(code, 150, 150) code, err = barcode.Scale(code, 150, 150)
if err != nil { if err != nil {
logs.Error("生成二维码失败 ->", err) logs.Error("生成二维码失败 ->", err)
c.ShowErrorPage(500, "生成二维码失败") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.gen_qrcode_failed"))
} }
c.Ctx.ResponseWriter.Header().Set("Content-Type", "image/png") c.Ctx.ResponseWriter.Header().Set("Content-Type", "image/png")
@ -940,7 +927,7 @@ func (c *DocumentController) QrCode() {
err = png.Encode(c.Ctx.ResponseWriter, code) err = png.Encode(c.Ctx.ResponseWriter, code)
if err != nil { if err != nil {
logs.Error("生成二维码失败 ->", err) logs.Error("生成二维码失败 ->", err)
c.ShowErrorPage(500, "生成二维码失败") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.gen_qrcode_failed"))
} }
} }
@ -953,7 +940,7 @@ func (c *DocumentController) Search() {
keyword := strings.TrimSpace(c.GetString("keyword")) keyword := strings.TrimSpace(c.GetString("keyword"))
if identify == "" { if identify == "" {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
if !c.EnableAnonymous && !c.isUserLoggedIn() { if !c.EnableAnonymous && !c.isUserLoggedIn() {
@ -966,11 +953,11 @@ func (c *DocumentController) Search() {
docs, err := models.NewDocumentSearchResult().SearchDocument(keyword, bookResult.BookId) docs, err := models.NewDocumentSearchResult().SearchDocument(keyword, bookResult.BookId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6002, "搜索结果错误") c.JsonResult(6002, i18n.Tr(c.Lang, "message.search_result_error"))
} }
if len(docs) < 0 { if len(docs) < 0 {
c.JsonResult(404, "没有数据库") c.JsonResult(404, i18n.Tr(c.Lang, "message.no_data"))
} }
for _, doc := range docs { for _, doc := range docs {
@ -1000,7 +987,7 @@ func (c *DocumentController) History() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
logs.Error("查找项目失败 ->", err) logs.Error("查找项目失败 ->", err)
c.Data["ErrorMessage"] = "项目不存在或权限不足" c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit")
return return
} }
@ -1010,7 +997,7 @@ func (c *DocumentController) History() {
bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
logs.Error("查找项目失败 ->", err) logs.Error("查找项目失败 ->", err)
c.Data["ErrorMessage"] = "项目不存在或权限不足" c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit")
return return
} }
@ -1019,27 +1006,27 @@ func (c *DocumentController) History() {
} }
if docId <= 0 { if docId <= 0 {
c.Data["ErrorMessage"] = "参数错误" c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.param_error")
return return
} }
doc, err := models.NewDocument().Find(docId) doc, err := models.NewDocument().Find(docId)
if err != nil { if err != nil {
logs.Error("Delete => ", err) logs.Error("Delete => ", err)
c.Data["ErrorMessage"] = "获取历史失败" c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.get_doc_his_failed")
return return
} }
// 如果文档所属项目错误 // 如果文档所属项目错误
if doc.BookId != bookId { if doc.BookId != bookId {
c.Data["ErrorMessage"] = "参数错误" c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.param_error")
return return
} }
histories, totalCount, err := models.NewDocumentHistory().FindToPager(docId, pageIndex, conf.PageSize) histories, totalCount, err := models.NewDocumentHistory().FindToPager(docId, pageIndex, conf.PageSize)
if err != nil { if err != nil {
logs.Error("分页查找文档历史失败 ->", err) logs.Error("分页查找文档历史失败 ->", err)
c.Data["ErrorMessage"] = "获取历史失败" c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.get_doc_his_failed")
return return
} }
@ -1063,7 +1050,7 @@ func (c *DocumentController) DeleteHistory() {
historyId, _ := c.GetInt("history_id", 0) historyId, _ := c.GetInt("history_id", 0)
if historyId <= 0 { if historyId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
bookId := 0 bookId := 0
@ -1073,7 +1060,7 @@ func (c *DocumentController) DeleteHistory() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
logs.Error("查找项目失败 ->", err) logs.Error("查找项目失败 ->", err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookId = book.BookId bookId = book.BookId
@ -1081,31 +1068,31 @@ func (c *DocumentController) DeleteHistory() {
bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
logs.Error("查找项目失败 ->", err) logs.Error("查找项目失败 ->", err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookId = bookResult.BookId bookId = bookResult.BookId
} }
if docId <= 0 { if docId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
doc, err := models.NewDocument().Find(docId) doc, err := models.NewDocument().Find(docId)
if err != nil { if err != nil {
logs.Error("Delete => ", err) logs.Error("Delete => ", err)
c.JsonResult(6001, "获取历史失败") c.JsonResult(6001, i18n.Tr(c.Lang, "message.get_doc_his_failed"))
} }
// 如果文档所属项目错误 // 如果文档所属项目错误
if doc.BookId != bookId { if doc.BookId != bookId {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
err = models.NewDocumentHistory().Delete(historyId, docId) err = models.NewDocumentHistory().Delete(historyId, docId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6002, "删除失败") c.JsonResult(6002, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok") c.JsonResult(0, "ok")
@ -1122,7 +1109,7 @@ func (c *DocumentController) RestoreHistory() {
historyId, _ := c.GetInt("history_id", 0) historyId, _ := c.GetInt("history_id", 0)
if historyId <= 0 { if historyId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
bookId := 0 bookId := 0
@ -1131,7 +1118,7 @@ func (c *DocumentController) RestoreHistory() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
logs.Error("FindByIdentify => ", err) logs.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookId = book.BookId bookId = book.BookId
@ -1139,31 +1126,31 @@ func (c *DocumentController) RestoreHistory() {
bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
logs.Error("FindByIdentify => ", err) logs.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
} }
bookId = bookResult.BookId bookId = bookResult.BookId
} }
if docId <= 0 { if docId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
doc, err := models.NewDocument().Find(docId) doc, err := models.NewDocument().Find(docId)
if err != nil { if err != nil {
logs.Error("Delete => ", err) logs.Error("Delete => ", err)
c.JsonResult(6001, "获取历史失败") c.JsonResult(6001, i18n.Tr(c.Lang, "message.get_doc_his_failed"))
} }
// 如果文档所属项目错误 // 如果文档所属项目错误
if doc.BookId != bookId { if doc.BookId != bookId {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
err = models.NewDocumentHistory().Restore(historyId, docId, c.Member.MemberId) err = models.NewDocumentHistory().Restore(historyId, docId, c.Member.MemberId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6002, "删除失败") c.JsonResult(6002, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok", doc) c.JsonResult(0, "ok", doc)
@ -1185,7 +1172,7 @@ func (c *DocumentController) Compare() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
logs.Error("DocumentController.Compare => ", err) logs.Error("DocumentController.Compare => ", err)
c.ShowErrorPage(403, "权限不足") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
return return
} }
@ -1196,7 +1183,7 @@ func (c *DocumentController) Compare() {
bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil || bookResult.RoleId == conf.BookObserver { if err != nil || bookResult.RoleId == conf.BookObserver {
logs.Error("FindByIdentify => ", err) logs.Error("FindByIdentify => ", err)
c.ShowErrorPage(403, "权限不足") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
return return
} }
@ -1206,7 +1193,7 @@ func (c *DocumentController) Compare() {
} }
if historyId <= 0 { if historyId <= 0 {
c.ShowErrorPage(60002, "参数错误") c.ShowErrorPage(60002, i18n.Tr(c.Lang, "message.param_error"))
} }
history, err := models.NewDocumentHistory().Find(historyId) history, err := models.NewDocumentHistory().Find(historyId)
@ -1217,7 +1204,7 @@ func (c *DocumentController) Compare() {
doc, err := models.NewDocument().Find(history.DocumentId) doc, err := models.NewDocument().Find(history.DocumentId)
if err != nil || doc == nil || doc.BookId != bookId { if err != nil || doc == nil || doc.BookId != bookId {
c.ShowErrorPage(60002, "文档不存在或已删除") c.ShowErrorPage(60002, i18n.Tr(c.Lang, "message.doc_not_exist"))
return return
} }
@ -1239,7 +1226,7 @@ func (c *DocumentController) isReadable(identify, token string) *models.BookResu
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.ShowErrorPage(500, "项目不存在") c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
bookResult := models.NewBookResult().ToBookResult(*book) bookResult := models.NewBookResult().ToBookResult(*book)
isOk := false isOk := false
@ -1264,13 +1251,13 @@ func (c *DocumentController) isReadable(identify, token string) *models.BookResu
c.SetSession(identify, token) c.SetSession(identify, token)
} else if token, ok := c.GetSession(identify).(string); !ok || !strings.EqualFold(token, book.PrivateToken) { } else if token, ok := c.GetSession(identify).(string); !ok || !strings.EqualFold(token, book.PrivateToken) {
logs.Info("尝试访问文档但权限不足 ->", identify, token) logs.Info("尝试访问文档但权限不足 ->", identify, token)
c.ShowErrorPage(403, "权限不足") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
} else if password := c.GetString("bPassword", ""); !isOk && book.BookPassword != "" && password != "" { } else if password := c.GetString("bPassword", ""); !isOk && book.BookPassword != "" && password != "" {
//如果设置了密码,则判断密码是否正确 //如果设置了密码,则判断密码是否正确
if book.BookPassword != password { if book.BookPassword != password {
c.JsonResult(5001, "密码错误") c.JsonResult(5001, i18n.Tr(c.Lang, "message.wrong_password"))
} else { } else {
c.SetSession(identify, password) c.SetSession(identify, password)
c.JsonResult(0, "OK") c.JsonResult(0, "OK")
@ -1289,7 +1276,7 @@ func (c *DocumentController) isReadable(identify, token string) *models.BookResu
} }
} else { } else {
logs.Info("尝试访问文档但权限不足 ->", identify, token) logs.Info("尝试访问文档但权限不足 ->", identify, token)
c.ShowErrorPage(403, "权限不足") c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
} }
} }
@ -1303,7 +1290,7 @@ func promptUserToLogIn(c *DocumentController) {
logs.Info(" Access will be redirected to login page(SessionId: " + c.CruSession.SessionID(context.TODO()) + ").") logs.Info(" Access will be redirected to login page(SessionId: " + c.CruSession.SessionID(context.TODO()) + ").")
if c.IsAjax() { if c.IsAjax() {
c.JsonResult(6000, "请重新登录。") c.JsonResult(6000, i18n.Tr(c.Lang, "message.need_relogin"))
} else { } else {
c.Redirect(conf.URLFor("AccountController.Login")+"?url="+url.PathEscape(conf.BaseUrl+c.Ctx.Request.URL.RequestURI()), 302) c.Redirect(conf.URLFor("AccountController.Login")+"?url="+url.PathEscape(conf.BaseUrl+c.Ctx.Request.URL.RequestURI()), 302)
} }

View File

@ -28,14 +28,11 @@ func (c *HomeController) Index() {
pageIndex, _ := c.GetInt("page", 1) pageIndex, _ := c.GetInt("page", 1)
pageSize := 18 pageSize := 18
memberId := 0 memberId := 0
if c.Member != nil { if c.Member != nil {
memberId = c.Member.MemberId memberId = c.Member.MemberId
} }
books, totalCount, err := models.NewBook().FindForHomeToPager(pageIndex, pageSize, memberId) books, totalCount, err := models.NewBook().FindForHomeToPager(pageIndex, pageSize, memberId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.Abort("500") c.Abort("500")
@ -47,6 +44,5 @@ func (c *HomeController) Index() {
c.Data["PageHtml"] = "" c.Data["PageHtml"] = ""
} }
c.Data["TotalPages"] = int(math.Ceil(float64(totalCount) / float64(pageSize))) c.Data["TotalPages"] = int(math.Ceil(float64(totalCount) / float64(pageSize)))
c.Data["Lists"] = books c.Data["Lists"] = books
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/models" "github.com/mindoc-org/mindoc/models"
"github.com/mindoc-org/mindoc/utils" "github.com/mindoc-org/mindoc/utils"
@ -40,17 +41,17 @@ func (c *ManagerController) Index() {
c.TplName = "manager/index.tpl" c.TplName = "manager/index.tpl"
c.Data["Model"] = models.NewDashboard().Query() c.Data["Model"] = models.NewDashboard().Query()
c.Data["Action"] = "index"
} }
// 用户列表. // 用户列表.
func (c *ManagerController) Users() { func (c *ManagerController) Users() {
c.Prepare() c.Prepare()
c.TplName = "manager/users.tpl" c.TplName = "manager/users.tpl"
c.Data["Action"] = "users"
pageIndex, _ := c.GetInt("page", 0) pageIndex, _ := c.GetInt("page", 0)
members, totalCount, err := models.NewMember().FindToPager(pageIndex, conf.PageSize) members, totalCount, err := models.NewMember().FindToPager(pageIndex, conf.PageSize)
if err != nil { if err != nil {
c.Data["ErrorMessage"] = err.Error() c.Data["ErrorMessage"] = err.Error()
return return
@ -68,7 +69,6 @@ func (c *ManagerController) Users() {
} }
b, err := json.Marshal(members) b, err := json.Marshal(members)
if err != nil { if err != nil {
c.Data["Result"] = template.JS("[]") c.Data["Result"] = template.JS("[]")
} else { } else {
@ -89,16 +89,16 @@ func (c *ManagerController) CreateMember() {
status, _ := c.GetInt("status", 0) status, _ := c.GetInt("status", 0)
if ok, err := regexp.MatchString(conf.RegexpAccount, account); account == "" || !ok || err != nil { if ok, err := regexp.MatchString(conf.RegexpAccount, account); account == "" || !ok || err != nil {
c.JsonResult(6001, "账号只能由英文字母数字组成且在3-50个字符") c.JsonResult(6001, i18n.Tr(c.Lang, "message.username_invalid_format"))
} }
if l := strings.Count(password1, ""); password1 == "" || l > 50 || l < 6 { if l := strings.Count(password1, ""); password1 == "" || l > 50 || l < 6 {
c.JsonResult(6002, "密码必须在6-50个字符之间") c.JsonResult(6002, i18n.Tr(c.Lang, "message.pwd_length_tips"))
} }
if password1 != password2 { if password1 != password2 {
c.JsonResult(6003, "确认密码不正确") c.JsonResult(6003, i18n.Tr(c.Lang, "message.wrong_confirm_pwd"))
} }
if ok, err := regexp.MatchString(conf.RegexpEmail, email); !ok || err != nil || email == "" { if ok, err := regexp.MatchString(conf.RegexpEmail, email); !ok || err != nil || email == "" {
c.JsonResult(6004, "邮箱格式不正确") c.JsonResult(6004, i18n.Tr(c.Lang, "message.email_invalid_format"))
} }
if role != 0 && role != 1 && role != 2 { if role != 0 && role != 1 && role != 2 {
role = 1 role = 1
@ -110,7 +110,7 @@ func (c *ManagerController) CreateMember() {
member := models.NewMember() member := models.NewMember()
if _, err := member.FindByAccount(account); err == nil && member.MemberId > 0 { if _, err := member.FindByAccount(account); err == nil && member.MemberId > 0 {
c.JsonResult(6005, "账号已存在") c.JsonResult(6005, i18n.Tr(c.Lang, "message.account_existed"))
} }
member.Account = account member.Account = account
@ -139,7 +139,7 @@ func (c *ManagerController) UpdateMemberStatus() {
status, _ := c.GetInt("status", 0) status, _ := c.GetInt("status", 0)
if member_id <= 0 { if member_id <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
if status != 0 && status != 1 { if status != 0 && status != 1 {
status = 0 status = 0
@ -147,19 +147,19 @@ func (c *ManagerController) UpdateMemberStatus() {
member := models.NewMember() member := models.NewMember()
if _, err := member.Find(member_id); err != nil { if _, err := member.Find(member_id); err != nil {
c.JsonResult(6002, "用户不存在") c.JsonResult(6002, i18n.Tr(c.Lang, "message.user_not_existed"))
} }
if member.MemberId == c.Member.MemberId { if member.MemberId == c.Member.MemberId {
c.JsonResult(6004, "不能变更自己的状态") c.JsonResult(6004, i18n.Tr(c.Lang, "message.cannot_change_own_status"))
} }
if member.Role == conf.MemberSuperRole { if member.Role == conf.MemberSuperRole {
c.JsonResult(6005, "不能变更超级管理员的状态") c.JsonResult(6005, i18n.Tr(c.Lang, "message.cannot_change_super_status"))
} }
member.Status = status member.Status = status
if err := member.Update(); err != nil { if err := member.Update(); err != nil {
logs.Error("", err) logs.Error("", err)
c.JsonResult(6003, "用户状态设置失败") c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok", member) c.JsonResult(0, "ok", member)
} }
@ -171,27 +171,28 @@ func (c *ManagerController) ChangeMemberRole() {
memberId, _ := c.GetInt("member_id", 0) memberId, _ := c.GetInt("member_id", 0)
role, _ := c.GetInt("role", 0) role, _ := c.GetInt("role", 0)
if memberId <= 0 { if memberId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
if role != int(conf.MemberAdminRole) && role != int(conf.MemberGeneralRole) { if role != int(conf.MemberAdminRole) && role != int(conf.MemberGeneralRole) {
c.JsonResult(6001, "用户权限不正确") c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
} }
member := models.NewMember() member := models.NewMember()
if _, err := member.Find(memberId); err != nil { if _, err := member.Find(memberId); err != nil {
c.JsonResult(6002, "用户不存在") c.JsonResult(6002, i18n.Tr(c.Lang, "message.user_not_existed"))
} }
if member.MemberId == c.Member.MemberId { if member.MemberId == c.Member.MemberId {
c.JsonResult(6004, "不能变更自己的权限") c.JsonResult(6004, i18n.Tr(c.Lang, "message.cannot_change_own_priv"))
} }
if member.Role == conf.MemberSuperRole { if member.Role == conf.MemberSuperRole {
c.JsonResult(6005, "不能变更超级管理员的权限") c.JsonResult(6005, i18n.Tr(c.Lang, "message.cannot_change_super_priv"))
} }
member.Role = conf.SystemRole(role) member.Role = conf.SystemRole(role)
if err := member.Update(); err != nil { if err := member.Update(); err != nil {
c.JsonResult(6003, "用户权限设置失败") c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed"))
} }
member.Lang = c.Lang
member.ResolveRoleName() member.ResolveRoleName()
c.JsonResult(0, "ok", member) c.JsonResult(0, "ok", member)
} }
@ -200,7 +201,7 @@ func (c *ManagerController) ChangeMemberRole() {
func (c *ManagerController) EditMember() { func (c *ManagerController) EditMember() {
c.Prepare() c.Prepare()
c.TplName = "manager/edit_users.tpl" c.TplName = "manager/edit_users.tpl"
c.Data["Action"] = "users"
member_id, _ := c.GetInt(":id", 0) member_id, _ := c.GetInt(":id", 0)
if member_id <= 0 { if member_id <= 0 {
@ -208,7 +209,6 @@ func (c *ManagerController) EditMember() {
} }
member, err := models.NewMember().Find(member_id) member, err := models.NewMember().Find(member_id)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.Abort("404") c.Abort("404")
@ -224,7 +224,7 @@ func (c *ManagerController) EditMember() {
member.Description = description member.Description = description
member.RealName = c.GetString("real_name") member.RealName = c.GetString("real_name")
if password1 != "" && password2 != password1 { if password1 != "" && password2 != password1 {
c.JsonResult(6001, "确认密码不正确") c.JsonResult(6001, i18n.Tr(c.Lang, "message.wrong_confirm_pwd"))
} }
if password1 != "" && member.AuthMethod != conf.AuthMethodLDAP { if password1 != "" && member.AuthMethod != conf.AuthMethodLDAP {
member.Password = password1 member.Password = password1
@ -236,7 +236,7 @@ func (c *ManagerController) EditMember() {
password, err := utils.PasswordHash(password1) password, err := utils.PasswordHash(password1)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(6003, "对用户密码加密时出错") c.JsonResult(6003, i18n.Tr(c.Lang, "message.pwd_encrypt_failed"))
} }
member.Password = password member.Password = password
} }
@ -255,30 +255,28 @@ func (c *ManagerController) DeleteMember() {
member_id, _ := c.GetInt("id", 0) member_id, _ := c.GetInt("id", 0)
if member_id <= 0 { if member_id <= 0 {
c.JsonResult(404, "参数错误") c.JsonResult(404, i18n.Tr(c.Lang, "message.param_error"))
} }
member, err := models.NewMember().Find(member_id) member, err := models.NewMember().Find(member_id)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(500, "用户不存在") c.JsonResult(500, i18n.Tr(c.Lang, "message.user_not_existed"))
} }
if member.Role == conf.MemberSuperRole { if member.Role == conf.MemberSuperRole {
c.JsonResult(500, "不能删除超级管理员") c.JsonResult(500, "不能删除超级管理员")
} }
superMember, err := models.NewMember().FindByFieldFirst("role", 0)
superMember, err := models.NewMember().FindByFieldFirst("role", 0)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(5001, "未能找到超级管理员") c.JsonResult(5001, "未能找到超级管理员")
} }
err = models.NewMember().Delete(member_id, superMember.MemberId) err = models.NewMember().Delete(member_id, superMember.MemberId)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
c.JsonResult(5002, "删除失败") c.JsonResult(5002, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok") c.JsonResult(0, "ok")
} }
@ -287,7 +285,7 @@ func (c *ManagerController) DeleteMember() {
func (c *ManagerController) Books() { func (c *ManagerController) Books() {
c.Prepare() c.Prepare()
c.TplName = "manager/books.tpl" c.TplName = "manager/books.tpl"
c.Data["Action"] = "books"
pageIndex, _ := c.GetInt("page", 1) pageIndex, _ := c.GetInt("page", 1)
books, totalCount, err := models.NewBookResult().FindToPager(pageIndex, conf.PageSize) books, totalCount, err := models.NewBookResult().FindToPager(pageIndex, conf.PageSize)
@ -318,7 +316,7 @@ func (c *ManagerController) EditBook() {
c.Prepare() c.Prepare()
c.TplName = "manager/edit_book.tpl" c.TplName = "manager/edit_book.tpl"
c.Data["Action"] = "books"
identify := c.GetString(":key") identify := c.GetString(":key")
if identify == "" { if identify == "" {
@ -328,8 +326,8 @@ func (c *ManagerController) EditBook() {
if err != nil { if err != nil {
c.Abort("500") c.Abort("500")
} }
if c.Ctx.Input.IsPost() {
if c.Ctx.Input.IsPost() {
bookName := strings.TrimSpace(c.GetString("book_name")) bookName := strings.TrimSpace(c.GetString("book_name"))
description := strings.TrimSpace(c.GetString("description", "")) description := strings.TrimSpace(c.GetString("description", ""))
commentStatus := c.GetString("comment_status") commentStatus := c.GetString("comment_status")
@ -344,7 +342,7 @@ func (c *ManagerController) EditBook() {
itemId, _ := c.GetInt("itemId") itemId, _ := c.GetInt("itemId")
if strings.Count(description, "") > 500 { if strings.Count(description, "") > 500 {
c.JsonResult(6004, "项目描述不能大于500字") c.JsonResult(6004, i18n.Tr(c.Lang, "message.project_desc_tips"))
} }
if commentStatus != "open" && commentStatus != "closed" && commentStatus != "group_only" && commentStatus != "registered_only" { if commentStatus != "open" && commentStatus != "closed" && commentStatus != "group_only" && commentStatus != "registered_only" {
commentStatus = "closed" commentStatus = "closed"
@ -356,7 +354,7 @@ func (c *ManagerController) EditBook() {
} }
} }
if !models.NewItemsets().Exist(itemId) { if !models.NewItemsets().Exist(itemId) {
c.JsonResult(6006, "项目空间不存在") c.JsonResult(6006, i18n.Tr(c.Lang, "message.project_space_not_exist"))
} }
book.Publisher = publisher book.Publisher = publisher
book.HistoryCount = historyCount book.HistoryCount = historyCount
@ -390,7 +388,7 @@ func (c *ManagerController) EditBook() {
} }
if err := book.Update(); err != nil { if err := book.Update(); err != nil {
c.JsonResult(6006, "保存失败") c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok") c.JsonResult(0, "ok")
} }
@ -410,18 +408,18 @@ func (c *ManagerController) DeleteBook() {
bookId, _ := c.GetInt("book_id", 0) bookId, _ := c.GetInt("book_id", 0)
if bookId <= 0 { if bookId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
book := models.NewBook() book := models.NewBook()
err := book.ThoroughDeleteBook(bookId) err := book.ThoroughDeleteBook(bookId)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.JsonResult(6002, "项目不存在") c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
if err != nil { if err != nil {
logs.Error("删除失败 -> ", err) logs.Error("删除失败 -> ", err)
c.JsonResult(6003, "删除失败") c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok") c.JsonResult(0, "ok")
} }
@ -436,7 +434,7 @@ func (c *ManagerController) CreateToken() {
book, err := models.NewBook().FindByFieldFirst("identify", identify) book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil { if err != nil {
c.JsonResult(6001, "项目不存在") c.JsonResult(6001, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
if action == "create" { if action == "create" {
@ -447,14 +445,14 @@ func (c *ManagerController) CreateToken() {
book.PrivateToken = string(utils.Krand(conf.GetTokenSize(), utils.KC_RAND_KIND_ALL)) book.PrivateToken = string(utils.Krand(conf.GetTokenSize(), utils.KC_RAND_KIND_ALL))
if err := book.Update(); err != nil { if err := book.Update(); err != nil {
logs.Error("生成阅读令牌失败 => ", err) logs.Error("生成阅读令牌失败 => ", err)
c.JsonResult(6003, "生成阅读令牌失败") c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok", conf.URLFor("DocumentController.Index", ":key", book.Identify, "token", book.PrivateToken)) c.JsonResult(0, "ok", conf.URLFor("DocumentController.Index", ":key", book.Identify, "token", book.PrivateToken))
} else { } else {
book.PrivateToken = "" book.PrivateToken = ""
if err := book.Update(); err != nil { if err := book.Update(); err != nil {
logs.Error("CreateToken => ", err) logs.Error("CreateToken => ", err)
c.JsonResult(6004, "删除令牌失败") c.JsonResult(6004, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok", "") c.JsonResult(0, "ok", "")
} }
@ -464,7 +462,7 @@ func (c *ManagerController) CreateToken() {
func (c *ManagerController) Setting() { func (c *ManagerController) Setting() {
c.Prepare() c.Prepare()
c.TplName = "manager/setting.tpl" c.TplName = "manager/setting.tpl"
c.Data["Action"] = "setting"
options, err := models.NewOption().All() options, err := models.NewOption().All()
if c.Ctx.Input.IsPost() { if c.Ctx.Input.IsPost() {
@ -483,7 +481,6 @@ func (c *ManagerController) Setting() {
for _, item := range options { for _, item := range options {
c.Data[item.OptionName] = item.OptionValue c.Data[item.OptionName] = item.OptionValue
} }
} }
// Transfer 转让项目. // Transfer 转让项目.
@ -492,16 +489,16 @@ func (c *ManagerController) Transfer() {
account := c.GetString("account") account := c.GetString("account")
if account == "" { if account == "" {
c.JsonResult(6004, "接受者账号不能为空") c.JsonResult(6004, i18n.Tr(c.Lang, "message.receive_account_empty"))
} }
member, err := models.NewMember().FindByAccount(account) member, err := models.NewMember().FindByAccount(account)
if err != nil { if err != nil {
logs.Error("FindByAccount => ", err) logs.Error("FindByAccount => ", err)
c.JsonResult(6005, "接受用户不存在") c.JsonResult(6005, i18n.Tr(c.Lang, "message.receive_account_not_exist"))
} }
if member.Status != 0 { if member.Status != 0 {
c.JsonResult(6006, "接受用户已被禁用") c.JsonResult(6006, i18n.Tr(c.Lang, "message.receive_account_disabled"))
} }
if !c.Member.IsAdministrator() { if !c.Member.IsAdministrator() {
@ -549,7 +546,7 @@ func (c *ManagerController) DeleteComment() {
comment_id, _ := c.GetInt("comment_id", 0) comment_id, _ := c.GetInt("comment_id", 0)
if comment_id <= 0 { if comment_id <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
} }
comment := models.NewComment() comment := models.NewComment()
@ -573,7 +570,7 @@ func (c *ManagerController) PrivatelyOwned() {
identify := c.GetString("identify") identify := c.GetString("identify")
if status != "open" && status != "close" { if status != "open" && status != "close" {
c.JsonResult(6003, "参数错误") c.JsonResult(6003, i18n.Tr(c.Lang, "message.param_error"))
} }
state := 0 state := 0
if status == "open" { if status == "open" {
@ -599,7 +596,7 @@ func (c *ManagerController) PrivatelyOwned() {
if err != nil { if err != nil {
logs.Error("PrivatelyOwned => ", err) logs.Error("PrivatelyOwned => ", err)
c.JsonResult(6004, "保存失败") c.JsonResult(6004, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok") c.JsonResult(0, "ok")
} }
@ -608,6 +605,7 @@ func (c *ManagerController) PrivatelyOwned() {
func (c *ManagerController) AttachList() { func (c *ManagerController) AttachList() {
c.Prepare() c.Prepare()
c.TplName = "manager/attach_list.tpl" c.TplName = "manager/attach_list.tpl"
c.Data["Action"] = "attach"
pageIndex, _ := c.GetInt("page", 1) pageIndex, _ := c.GetInt("page", 1)
@ -638,14 +636,14 @@ func (c *ManagerController) AttachList() {
func (c *ManagerController) AttachDetailed() { func (c *ManagerController) AttachDetailed() {
c.Prepare() c.Prepare()
c.TplName = "manager/attach_detailed.tpl" c.TplName = "manager/attach_detailed.tpl"
attach_id, _ := strconv.Atoi(c.Ctx.Input.Param(":id")) c.Data["Action"] = "attach"
attach_id, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
if attach_id <= 0 { if attach_id <= 0 {
c.Abort("404") c.Abort("404")
} }
attach, err := models.NewAttachmentResult().Find(attach_id) attach, err := models.NewAttachmentResult().Find(attach_id)
if err != nil { if err != nil {
logs.Error("AttachDetailed => ", err) logs.Error("AttachDetailed => ", err)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
@ -690,11 +688,10 @@ func (c *ManagerController) AttachDelete() {
func (c *ManagerController) LabelList() { func (c *ManagerController) LabelList() {
c.Prepare() c.Prepare()
c.TplName = "manager/label_list.tpl" c.TplName = "manager/label_list.tpl"
c.Data["Action"] = "label"
pageIndex, _ := c.GetInt("page", 1) pageIndex, _ := c.GetInt("page", 1)
labels, totalCount, err := models.NewLabel().FindToPager(pageIndex, conf.PageSize) labels, totalCount, err := models.NewLabel().FindToPager(pageIndex, conf.PageSize)
if err != nil { if err != nil {
c.ShowErrorPage(50001, err.Error()) c.ShowErrorPage(50001, err.Error())
} }
@ -712,13 +709,12 @@ func (c *ManagerController) LabelList() {
//删除标签 //删除标签
func (c *ManagerController) LabelDelete() { func (c *ManagerController) LabelDelete() {
labelId, err := strconv.Atoi(c.Ctx.Input.Param(":id")) labelId, err := strconv.Atoi(c.Ctx.Input.Param(":id"))
if err != nil { if err != nil {
logs.Error("获取删除标签参数时出错:", err) logs.Error("获取删除标签参数时出错:", err)
c.JsonResult(50001, "参数错误") c.JsonResult(50001, i18n.Tr(c.Lang, "message.param_error"))
} }
if labelId <= 0 { if labelId <= 0 {
c.JsonResult(50001, "参数错误") c.JsonResult(50001, i18n.Tr(c.Lang, "message.param_error"))
} }
label, err := models.NewLabel().FindFirst("label_id", labelId) label, err := models.NewLabel().FindFirst("label_id", labelId)
@ -736,6 +732,7 @@ func (c *ManagerController) LabelDelete() {
func (c *ManagerController) Config() { func (c *ManagerController) Config() {
c.Prepare() c.Prepare()
c.TplName = "manager/config.tpl" c.TplName = "manager/config.tpl"
c.Data["Action"] = "config"
if c.Ctx.Input.IsPost() { if c.Ctx.Input.IsPost() {
content := strings.TrimSpace(c.GetString("configFileTextArea")) content := strings.TrimSpace(c.GetString("configFileTextArea"))
if content == "" { if content == "" {
@ -773,11 +770,10 @@ func (c *ManagerController) Config() {
func (c *ManagerController) Team() { func (c *ManagerController) Team() {
c.Prepare() c.Prepare()
c.TplName = "manager/team.tpl" c.TplName = "manager/team.tpl"
c.Data["Action"] = "team"
pageIndex, _ := c.GetInt("page", 0) pageIndex, _ := c.GetInt("page", 0)
teams, totalCount, err := models.NewTeam().FindToPager(pageIndex, conf.PageSize) teams, totalCount, err := models.NewTeam().FindToPager(pageIndex, conf.PageSize)
if err != nil && err != orm.ErrNoRows { if err != nil && err != orm.ErrNoRows {
c.ShowErrorPage(500, err.Error()) c.ShowErrorPage(500, err.Error())
} }
@ -795,7 +791,6 @@ func (c *ManagerController) Team() {
} }
b, err := json.Marshal(teams) b, err := json.Marshal(teams)
if err != nil { if err != nil {
c.Data["Result"] = template.JS("[]") c.Data["Result"] = template.JS("[]")
} else { } else {
@ -809,7 +804,7 @@ func (c *ManagerController) TeamCreate() {
teamName := c.GetString("teamName") teamName := c.GetString("teamName")
if teamName == "" { if teamName == "" {
c.JsonResult(5001, "团队名称不能为空") c.JsonResult(5001, i18n.Tr(c.Lang, "message.team_name_empty"))
} }
team := models.NewTeam() team := models.NewTeam()
@ -830,10 +825,10 @@ func (c *ManagerController) TeamEdit() {
teamId, _ := c.GetInt("teamId") teamId, _ := c.GetInt("teamId")
if teamName == "" { if teamName == "" {
c.JsonResult(5001, "团队名称不能为空") c.JsonResult(5001, i18n.Tr(c.Lang, "message.team_name_empty"))
} }
if teamId <= 0 { if teamId <= 0 {
c.JsonResult(5002, "团队标识不能为空") c.JsonResult(5002, i18n.Tr(c.Lang, "message.team_id_empty"))
} }
team, err := models.NewTeam().First(teamId) team, err := models.NewTeam().First(teamId)
@ -846,19 +841,16 @@ func (c *ManagerController) TeamEdit() {
c.CheckJsonError(5004, err) c.CheckJsonError(5004, err)
c.JsonResult(0, "OK", team) c.JsonResult(0, "OK", team)
} }
func (c *ManagerController) TeamDelete() { func (c *ManagerController) TeamDelete() {
c.Prepare() c.Prepare()
teamId, _ := c.GetInt("teamId") teamId, _ := c.GetInt("teamId")
if teamId <= 0 { if teamId <= 0 {
c.JsonResult(5002, "团队标识不能为空") c.JsonResult(5002, i18n.Tr(c.Lang, "message.team_id_empty"))
} }
err := models.NewTeam().Delete(teamId) err := models.NewTeam().Delete(teamId)
c.CheckJsonError(5001, err) c.CheckJsonError(5001, err)
c.JsonResult(0, "OK") c.JsonResult(0, "OK")
@ -867,23 +859,21 @@ func (c *ManagerController) TeamDelete() {
func (c *ManagerController) TeamMemberList() { func (c *ManagerController) TeamMemberList() {
c.Prepare() c.Prepare()
c.TplName = "manager/team_member_list.tpl" c.TplName = "manager/team_member_list.tpl"
c.Data["Action"] = "team"
teamId, _ := strconv.Atoi(c.Ctx.Input.Param(":id")) teamId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
if teamId <= 0 {
c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.param_error"))
}
pageIndex, _ := c.GetInt("page", 0) pageIndex, _ := c.GetInt("page", 0)
if teamId <= 0 {
c.ShowErrorPage(500, "参数错误")
}
team, err := models.NewTeam().First(teamId) team, err := models.NewTeam().First(teamId)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.ShowErrorPage(404, "团队不存在") c.ShowErrorPage(404, "团队不存在")
} }
c.CheckErrorResult(500, err) c.CheckErrorResult(500, err)
c.Data["Model"] = team c.Data["Model"] = team
teams, totalCount, err := models.NewTeamMember().FindToPager(teamId, pageIndex, conf.PageSize) teams, totalCount, err := models.NewTeamMember().SetLang(c.Lang).FindToPager(teamId, pageIndex, conf.PageSize)
if err != nil && err != orm.ErrNoRows { if err != nil && err != orm.ErrNoRows {
c.ShowErrorPage(500, err.Error()) c.ShowErrorPage(500, err.Error())
} }
@ -901,7 +891,6 @@ func (c *ManagerController) TeamMemberList() {
} }
b, err := json.Marshal(teams) b, err := json.Marshal(teams)
if err != nil { if err != nil {
logs.Error("编码 JSON 结果失败 ->", err) logs.Error("编码 JSON 结果失败 ->", err)
c.Data["Result"] = template.JS("[]") c.Data["Result"] = template.JS("[]")
@ -918,7 +907,7 @@ func (c *ManagerController) TeamSearchMember() {
keyword := strings.TrimSpace(c.GetString("q")) keyword := strings.TrimSpace(c.GetString("q"))
if teamId <= 0 { if teamId <= 0 {
c.JsonResult(500, "参数错误") c.JsonResult(500, i18n.Tr(c.Lang, "message.param_error"))
} }
searchResult, err := models.NewTeamMember().FindNotJoinMemberByAccount(teamId, keyword, 10) searchResult, err := models.NewTeamMember().FindNotJoinMemberByAccount(teamId, keyword, 10)
@ -936,7 +925,7 @@ func (c *ManagerController) TeamMemberAdd() {
roleId, _ := c.GetInt("roleId") roleId, _ := c.GetInt("roleId")
if teamId <= 0 || memberId <= 0 || roleId <= 0 || roleId > int(conf.BookObserver) { if teamId <= 0 || memberId <= 0 || roleId <= 0 || roleId > int(conf.BookObserver) {
c.JsonResult(5001, "参数不正确") c.JsonResult(5001, i18n.Tr(c.Lang, "message.system_error"))
} }
teamMember := models.NewTeamMember() teamMember := models.NewTeamMember()
@ -965,7 +954,7 @@ func (c *ManagerController) TeamMemberDelete() {
} }
err = teamMember.Delete(teamMember.TeamMemberId) err = teamMember.Delete(teamMember.TeamMemberId)
if err != nil { if err != nil {
c.JsonResult(5002, "删除失败") c.JsonResult(5002, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "ok") c.JsonResult(0, "ok")
} }
@ -976,7 +965,7 @@ func (c *ManagerController) TeamChangeMemberRole() {
roleId, _ := c.GetInt("roleId") roleId, _ := c.GetInt("roleId")
teamId, _ := c.GetInt("teamId") teamId, _ := c.GetInt("teamId")
if memberId <= 0 || roleId <= 0 || teamId <= 0 || roleId > int(conf.BookObserver) { if memberId <= 0 || roleId <= 0 || teamId <= 0 || roleId > int(conf.BookObserver) {
c.JsonResult(5001, "参数错误") c.JsonResult(5001, i18n.Tr(c.Lang, "message.param_error"))
} }
teamMember, err := models.NewTeamMember().ChangeRoleId(teamId, memberId, conf.BookRole(roleId)) teamMember, err := models.NewTeamMember().ChangeRoleId(teamId, memberId, conf.BookRole(roleId))
@ -993,12 +982,12 @@ func (c *ManagerController) TeamChangeMemberRole() {
func (c *ManagerController) TeamBookList() { func (c *ManagerController) TeamBookList() {
c.Prepare() c.Prepare()
c.TplName = "manager/team_book_list.tpl" c.TplName = "manager/team_book_list.tpl"
c.Data["Action"] = "team"
teamId, _ := strconv.Atoi(c.Ctx.Input.Param(":id")) teamId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
pageIndex, _ := c.GetInt("page", 0) pageIndex, _ := c.GetInt("page", 0)
if teamId <= 0 { if teamId <= 0 {
c.JsonResult(5002, "团队标识不能为空") c.JsonResult(5002, i18n.Tr(c.Lang, "message.team_id_empty"))
} }
team, err := models.NewTeam().First(teamId) team, err := models.NewTeam().First(teamId)
@ -1028,7 +1017,6 @@ func (c *ManagerController) TeamBookList() {
} }
b, err := json.Marshal(teams) b, err := json.Marshal(teams)
if err != nil { if err != nil {
logs.Error("编码 JSON 结果失败 ->", err) logs.Error("编码 JSON 结果失败 ->", err)
c.Data["Result"] = template.JS("[]") c.Data["Result"] = template.JS("[]")
@ -1045,7 +1033,7 @@ func (c *ManagerController) TeamBookAdd() {
bookId, _ := c.GetInt("bookId") bookId, _ := c.GetInt("bookId")
if teamId <= 0 || bookId <= 0 { if teamId <= 0 || bookId <= 0 {
c.JsonResult(500, "参数错误") c.JsonResult(500, i18n.Tr(c.Lang, "message.param_error"))
} }
teamRel := models.NewTeamRelationship() teamRel := models.NewTeamRelationship()
teamRel.BookId = bookId teamRel.BookId = bookId
@ -1069,7 +1057,7 @@ func (c *ManagerController) TeamSearchBook() {
keyword := strings.TrimSpace(c.GetString("q")) keyword := strings.TrimSpace(c.GetString("q"))
if teamId <= 0 { if teamId <= 0 {
c.JsonResult(500, "参数错误") c.JsonResult(500, i18n.Tr(c.Lang, "message.param_error"))
} }
searchResult, err := models.NewTeamRelationship().FindNotJoinBookByName(teamId, keyword, 10) searchResult, err := models.NewTeamRelationship().FindNotJoinBookByName(teamId, keyword, 10)
@ -1087,13 +1075,13 @@ func (c *ManagerController) TeamBookDelete() {
teamRelationshipId, _ := c.GetInt("teamRelId") teamRelationshipId, _ := c.GetInt("teamRelId")
if teamRelationshipId <= 0 { if teamRelationshipId <= 0 {
c.JsonResult(500, "参数错误") c.JsonResult(500, i18n.Tr(c.Lang, "message.param_error"))
} }
err := models.NewTeamRelationship().Delete(teamRelationshipId) err := models.NewTeamRelationship().Delete(teamRelationshipId)
if err != nil { if err != nil {
c.JsonResult(5001, "删除失败") c.JsonResult(5001, i18n.Tr(c.Lang, "message.failed"))
} }
c.JsonResult(0, "OK") c.JsonResult(0, "OK")
} }
@ -1102,6 +1090,7 @@ func (c *ManagerController) TeamBookDelete() {
func (c *ManagerController) Itemsets() { func (c *ManagerController) Itemsets() {
c.Prepare() c.Prepare()
c.TplName = "manager/itemsets.tpl" c.TplName = "manager/itemsets.tpl"
c.Data["Action"] = "itemsets"
pageIndex, _ := c.GetInt("page", 0) pageIndex, _ := c.GetInt("page", 0)
items, totalCount, err := models.NewItemsets().FindToPager(pageIndex, conf.PageSize) items, totalCount, err := models.NewItemsets().FindToPager(pageIndex, conf.PageSize)
@ -1123,7 +1112,6 @@ func (c *ManagerController) Itemsets() {
} }
c.Data["Lists"] = items c.Data["Lists"] = items
} }
//编辑或添加项目空间. //编辑或添加项目空间.
@ -1133,14 +1121,14 @@ func (c *ManagerController) ItemsetsEdit() {
itemName := strings.TrimSpace(c.GetString("itemName")) itemName := strings.TrimSpace(c.GetString("itemName"))
itemKey := strings.TrimSpace(c.GetString("itemKey")) itemKey := strings.TrimSpace(c.GetString("itemKey"))
if itemName == "" || itemKey == "" { if itemName == "" || itemKey == "" {
c.JsonResult(5001, "参数错误") c.JsonResult(5001, i18n.Tr(c.Lang, "message.param_error"))
} }
var item *models.Itemsets var item *models.Itemsets
var err error var err error
if itemId > 0 { if itemId > 0 {
if item, err = models.NewItemsets().First(itemId); err != nil { if item, err = models.NewItemsets().First(itemId); err != nil {
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
c.JsonResult(5002, "项目空间不存在") c.JsonResult(5002, i18n.Tr(c.Lang, "message.project_space_not_exist"))
} else { } else {
c.JsonResult(5003, "查询项目空间出错") c.JsonResult(5003, "查询项目空间出错")
} }

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/models" "github.com/mindoc-org/mindoc/models"
"github.com/mindoc-org/mindoc/utils" "github.com/mindoc-org/mindoc/utils"
@ -87,16 +88,16 @@ func (c *SearchController) User() {
key := c.Ctx.Input.Param(":key") key := c.Ctx.Input.Param(":key")
keyword := strings.TrimSpace(c.GetString("q")) keyword := strings.TrimSpace(c.GetString("q"))
if key == "" || keyword == "" { if key == "" || keyword == "" {
c.JsonResult(404, "参数错误") c.JsonResult(404, i18n.Tr(c.Lang, "message.param_error"))
} }
keyword = sqltil.EscapeLike(keyword) keyword = sqltil.EscapeLike(keyword)
book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId) book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
if err != nil { if err != nil {
if err == models.ErrPermissionDenied { if err == models.ErrPermissionDenied {
c.JsonResult(403, "没有权限") c.JsonResult(403, i18n.Tr(c.Lang, "message.no_permission"))
} }
c.JsonResult(500, "项目不存在") c.JsonResult(500, i18n.Tr(c.Lang, "message.item_not_exist"))
} }
//members, err := models.NewMemberRelationshipResult().FindNotJoinUsersByAccount(book.BookId, 10, "%"+keyword+"%") //members, err := models.NewMemberRelationshipResult().FindNotJoinUsersByAccount(book.BookId, 10, "%"+keyword+"%")

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/graphics" "github.com/mindoc-org/mindoc/graphics"
"github.com/mindoc-org/mindoc/models" "github.com/mindoc-org/mindoc/models"
@ -28,7 +29,7 @@ func (c *SettingController) Index() {
description := strings.TrimSpace(c.GetString("description")) description := strings.TrimSpace(c.GetString("description"))
if email == "" { if email == "" {
c.JsonResult(601, "邮箱不能为空") c.JsonResult(601, i18n.Tr(c.Lang, "message.email_empty"))
} }
member := c.Member member := c.Member
member.Email = email member.Email = email
@ -48,34 +49,34 @@ func (c *SettingController) Password() {
if c.Ctx.Input.IsPost() { if c.Ctx.Input.IsPost() {
if c.Member.AuthMethod == conf.AuthMethodLDAP { if c.Member.AuthMethod == conf.AuthMethodLDAP {
c.JsonResult(6009, "当前用户不支持修改密码") c.JsonResult(6009, i18n.Tr(c.Lang, "message.cur_user_cannot_change_pwd"))
} }
password1 := c.GetString("password1") password1 := c.GetString("password1")
password2 := c.GetString("password2") password2 := c.GetString("password2")
password3 := c.GetString("password3") password3 := c.GetString("password3")
if password1 == "" { if password1 == "" {
c.JsonResult(6003, "原密码不能为空") c.JsonResult(6003, i18n.Tr(c.Lang, "message.origin_pwd_empty"))
} }
if password2 == "" { if password2 == "" {
c.JsonResult(6004, "新密码不能为空") c.JsonResult(6004, i18n.Tr(c.Lang, "message.new_pwd_empty"))
} }
if count := strings.Count(password2, ""); count < 6 || count > 18 { if count := strings.Count(password2, ""); count < 6 || count > 18 {
c.JsonResult(6009, "密码必须在6-18字之间") c.JsonResult(6009, i18n.Tr(c.Lang, "message.pwd_length"))
} }
if password2 != password3 { if password2 != password3 {
c.JsonResult(6003, "确认密码不正确") c.JsonResult(6003, "确认密码不正确")
} }
if ok, _ := utils.PasswordVerify(c.Member.Password, password1); !ok { if ok, _ := utils.PasswordVerify(c.Member.Password, password1); !ok {
c.JsonResult(6005, "原始密码不正确") c.JsonResult(6005, i18n.Tr(c.Lang, "message.wrong_origin_pwd"))
} }
if password1 == password2 { if password1 == password2 {
c.JsonResult(6006, "新密码不能和原始密码相同") c.JsonResult(6006, i18n.Tr(c.Lang, "message.same_pwd"))
} }
pwd, err := utils.PasswordHash(password2) pwd, err := utils.PasswordHash(password2)
if err != nil { if err != nil {
c.JsonResult(6007, "密码加密失败") c.JsonResult(6007, i18n.Tr(c.Lang, "message.pwd_encrypt_failed"))
} }
c.Member.Password = pwd c.Member.Password = pwd
if err := c.Member.Update(); err != nil { if err := c.Member.Update(); err != nil {

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/models" "github.com/mindoc-org/mindoc/models"
) )
@ -20,7 +21,7 @@ func (c *TemplateController) isPermission() error {
bookIdentify := c.GetString("identify", "") bookIdentify := c.GetString("identify", "")
if bookIdentify == "" { if bookIdentify == "" {
return errors.New("参数错误") return errors.New(i18n.Tr(c.Lang, "message.param_error"))
} }
if !c.Member.IsAdministrator() { if !c.Member.IsAdministrator() {

View File

@ -1,12 +1,14 @@
MinDoc_New: version: "3"
image: registry.cn-hangzhou.aliyuncs.com/mindoc/mindoc:v2.0-beta.2 services:
mindoc:
image: registry.cn-hangzhou.aliyuncs.com/mindoc-org/mindoc:v2.1-beta.5
container_name: mindoc
privileged: false privileged: false
restart: always restart: always
ports: ports:
- 8181:8181 - 8181:8181
volumes: volumes:
- /var/www/mindoc/database:/mindoc/database - /var/www/mindoc://mindoc-sync-host
- /var/www/mindoc/uploads:/mindoc/uploads
environment: environment:
- MINDOC_RUN_MODE=prod - MINDOC_RUN_MODE=prod
- MINDOC_DB_ADAPTER=sqlite3 - MINDOC_DB_ADAPTER=sqlite3

4
go.mod
View File

@ -4,17 +4,21 @@ go 1.13
require ( require (
github.com/PuerkitoBio/goquery v1.4.1 github.com/PuerkitoBio/goquery v1.4.1
github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8 // indirect
github.com/andybalholm/cascadia v1.2.0 // indirect github.com/andybalholm/cascadia v1.2.0 // indirect
github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5 github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5
github.com/beego/i18n v0.0.0-20161101132742-e9308947f407
github.com/boombuler/barcode v1.0.0 github.com/boombuler/barcode v1.0.0
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/howeyc/fsnotify v0.9.0 github.com/howeyc/fsnotify v0.9.0
github.com/kardianos/service v1.1.0 github.com/kardianos/service v1.1.0
github.com/lib/pq v1.7.0 // indirect github.com/lib/pq v1.7.0 // indirect
github.com/lifei6671/gocaptcha v0.1.1 github.com/lifei6671/gocaptcha v0.1.1
github.com/mattn/go-runewidth v0.0.13
github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/russross/blackfriday/v2 v2.1.0 github.com/russross/blackfriday/v2 v2.1.0
github.com/smartystreets/goconvey v1.6.4 // indirect
gopkg.in/asn1-ber.v1 v1.0.0-00010101000000-000000000000 // indirect gopkg.in/asn1-ber.v1 v1.0.0-00010101000000-000000000000 // indirect
gopkg.in/ldap.v2 v2.5.1 gopkg.in/ldap.v2 v2.5.1
gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect

17
go.sum
View File

@ -3,6 +3,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/PuerkitoBio/goquery v1.4.1 h1:smcIRGdYm/w7JSbcdeLHEMzxmsBQvl8lhf0dSw2nzMI= github.com/PuerkitoBio/goquery v1.4.1 h1:smcIRGdYm/w7JSbcdeLHEMzxmsBQvl8lhf0dSw2nzMI=
github.com/PuerkitoBio/goquery v1.4.1/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA= github.com/PuerkitoBio/goquery v1.4.1/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA=
github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8 h1:1TrMV1HmBApBbM+Hy7RCKZD6UlYWYIPPfoeXomG7+zE=
github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -14,6 +16,8 @@ github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxB
github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5 h1:i0swsv6hmoF6pkeG2S/dvOWwOKFPGDKhrmTlWH6I1nM= github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5 h1:i0swsv6hmoF6pkeG2S/dvOWwOKFPGDKhrmTlWH6I1nM=
github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5/go.mod h1:JlRUJ/NVNygorqjyt7/lQ8R++KSE0qXvxeIfbnIUd7Q= github.com/beego/beego/v2 v2.0.2-0.20210322114547-10ea897525a5/go.mod h1:JlRUJ/NVNygorqjyt7/lQ8R++KSE0qXvxeIfbnIUd7Q=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
github.com/beego/i18n v0.0.0-20161101132742-e9308947f407 h1:WtJfx5HqASTQp7HfiZldnin8KQV2futplF3duGp5PGc=
github.com/beego/i18n v0.0.0-20161101132742-e9308947f407/go.mod h1:KLeFCpAMq2+50NkXC8iiJxLLiiTfTqrGtKEVm+2fk7s=
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@ -92,6 +96,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
@ -101,6 +107,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/service v1.1.0 h1:QV2SiEeWK42P0aEmGcsAgjApw/lRxkwopvT+Gu6t1/0= github.com/kardianos/service v1.1.0 h1:QV2SiEeWK42P0aEmGcsAgjApw/lRxkwopvT+Gu6t1/0=
github.com/kardianos/service v1.1.0/go.mod h1:RrJI2xn5vve/r32U5suTbeaSGoMU6GbNPoj36CVYcHc= github.com/kardianos/service v1.1.0/go.mod h1:RrJI2xn5vve/r32U5suTbeaSGoMU6GbNPoj36CVYcHc=
@ -118,6 +126,8 @@ github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lifei6671/gocaptcha v0.1.1 h1:5cvU3w0bK8eJm1P6AiQoPuicoZVAgKKpREBxXF9IaHo= github.com/lifei6671/gocaptcha v0.1.1 h1:5cvU3w0bK8eJm1P6AiQoPuicoZVAgKKpREBxXF9IaHo=
github.com/lifei6671/gocaptcha v0.1.1/go.mod h1:6QlTU2WzFhzqylAJWSo3OANfKCraGccJwbK01P5fFmI= github.com/lifei6671/gocaptcha v0.1.1/go.mod h1:6QlTU2WzFhzqylAJWSo3OANfKCraGccJwbK01P5fFmI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@ -166,6 +176,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -176,6 +188,10 @@ github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKz
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -254,6 +270,7 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

View File

@ -18,6 +18,8 @@ func ImageCopy(src image.Image, x, y, w, h int) (image.Image, error) {
subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.RGBA) //图片裁剪x0 y0 x1 y1 subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.RGBA) //图片裁剪x0 y0 x1 y1
} else if rgbImg, ok := src.(*image.NRGBA); ok { } else if rgbImg, ok := src.(*image.NRGBA); ok {
subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.NRGBA) //图片裁剪x0 y0 x1 y1 subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.NRGBA) //图片裁剪x0 y0 x1 y1
} else if rgbImg, ok := src.(*image.Paletted); ok {
subImg = rgbImg.SubImage(image.Rect(x, y, x+w, y+h)).(*image.Paletted) //图片裁剪x0 y0 x1 y1
} else { } else {
return subImg, errors.New("图片解码失败") return subImg, errors.New("图片解码失败")

View File

@ -19,6 +19,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/utils" "github.com/mindoc-org/mindoc/utils"
"github.com/mindoc-org/mindoc/utils/cryptil" "github.com/mindoc-org/mindoc/utils/cryptil"
@ -115,7 +116,7 @@ func NewBook() *Book {
} }
//添加一个项目 //添加一个项目
func (book *Book) Insert() error { func (book *Book) Insert(lang string) error {
o := orm.NewOrm() o := orm.NewOrm()
// o.Begin() // o.Begin()
book.BookName = utils.StripTags(book.BookName) book.BookName = utils.StripTags(book.BookName)
@ -141,7 +142,7 @@ func (book *Book) Insert() error {
} }
document := NewDocument() document := NewDocument()
document.BookId = book.BookId document.BookId = book.BookId
document.DocumentName = "空白文档" document.DocumentName = i18n.Tr(lang, "init.blank_doc") //"空白文档"
document.MemberId = book.MemberId document.MemberId = book.MemberId
err = document.InsertOrUpdate() err = document.InsertOrUpdate()
if err != nil { if err != nil {
@ -362,7 +363,7 @@ func (book *Book) FindByIdentify(identify string, cols ...string) (*Book, error)
} }
//分页查询指定用户的项目 //分页查询指定用户的项目
func (book *Book) FindToPager(pageIndex, pageSize, memberId int) (books []*BookResult, totalCount int, err error) { func (book *Book) FindToPager(pageIndex, pageSize, memberId int, lang string) (books []*BookResult, totalCount int, err error) {
o := orm.NewOrm() o := orm.NewOrm()
@ -430,13 +431,13 @@ ORDER BY book.order_index, book.book_id DESC limit ?,?`
books[index].LastModifyText = text.Account + " 于 " + text.ModifyTime.Format("2006-01-02 15:04:05") books[index].LastModifyText = text.Account + " 于 " + text.ModifyTime.Format("2006-01-02 15:04:05")
} }
if book.RoleId == 0 { if book.RoleId == 0 {
book.RoleName = "创始人" book.RoleName = i18n.Tr(lang, "common.creator")
} else if book.RoleId == 1 { } else if book.RoleId == 1 {
book.RoleName = "管理员" book.RoleName = i18n.Tr(lang, "common.administrator")
} else if book.RoleId == 2 { } else if book.RoleId == 2 {
book.RoleName = "编辑者" book.RoleName = i18n.Tr(lang, "common.editor")
} else if book.RoleId == 3 { } else if book.RoleId == 3 {
book.RoleName = "观察者" book.RoleName = i18n.Tr(lang, "common.observer")
} }
} }
} }
@ -631,7 +632,7 @@ WHERE (relationship_id > 0 OR book.privately_owned = 0 or team.team_member_id >
} }
// ReleaseContent 批量发布文档 // ReleaseContent 批量发布文档
func (book *Book) ReleaseContent(bookId int) { func (book *Book) ReleaseContent(bookId int, lang string) {
releaseQueue <- bookId releaseQueue <- bookId
once.Do(func() { once.Do(func() {
go func() { go func() {
@ -652,6 +653,7 @@ func (book *Book) ReleaseContent(bookId int) {
} }
for _, item := range docs { for _, item := range docs {
item.BookId = bookId item.BookId = bookId
item.Lang = lang
_ = item.ReleaseContent() _ = item.ReleaseContent()
} }
@ -678,8 +680,8 @@ func (book *Book) ResetDocumentNumber(bookId int) {
} }
} }
//导入项目 // 导入zip项目
func (book *Book) ImportBook(zipPath string) error { func (book *Book) ImportBook(zipPath string, lang string) error {
if !filetil.FileExists(zipPath) { if !filetil.FileExists(zipPath) {
return errors.New("文件不存在 => " + zipPath) return errors.New("文件不存在 => " + zipPath)
} }
@ -972,7 +974,79 @@ func (book *Book) ImportBook(zipPath string) error {
book.Description = "【项目导入存在错误:" + err.Error() + "】" book.Description = "【项目导入存在错误:" + err.Error() + "】"
} }
logs.Info("项目导入完毕 => ", book.BookName) logs.Info("项目导入完毕 => ", book.BookName)
book.ReleaseContent(book.BookId) book.ReleaseContent(book.BookId, lang)
return err
}
// 导入docx项目
func (book *Book) ImportWordBook(docxPath string, lang string) (err error) {
if !filetil.FileExists(docxPath) {
return errors.New("文件不存在")
}
docxPath = strings.Replace(docxPath, "\\", "/", -1)
o := orm.NewOrm()
o.Insert(book)
relationship := NewRelationship()
relationship.BookId = book.BookId
relationship.RoleId = 0
relationship.MemberId = book.MemberId
err = relationship.Insert()
if err != nil {
logs.Error("插入项目与用户关联 -> ", err)
return err
}
doc := NewDocument()
doc.BookId = book.BookId
doc.MemberId = book.MemberId
docIdentify := strings.Replace(strings.TrimPrefix(docxPath, os.TempDir()+"/"), "/", "-", -1)
if ok, err := regexp.MatchString(`[a-z]+[a-zA-Z0-9_.\-]*$`, docIdentify); !ok || err != nil {
docIdentify = "import-" + docIdentify
}
doc.Identify = docIdentify
if doc.Markdown, err = utils.Docx2md(docxPath, false); err != nil {
logs.Error("导入doc项目转换异常 => ", err)
return err
}
// fmt.Println("===doc.Markdown===")
// fmt.Println(doc.Markdown)
// fmt.Println("==================")
doc.Content = string(blackfriday.Run([]byte(doc.Markdown)))
// fmt.Println("===doc.Content===")
// fmt.Println(doc.Content)
// fmt.Println("==================")
doc.Version = time.Now().Unix()
var docName string
for _, line := range strings.Split(doc.Markdown, "\n") {
if strings.HasPrefix(line, "#") {
docName = strings.TrimLeft(line, "#")
break
}
}
doc.DocumentName = strings.TrimSpace(docName)
doc.DocumentId = book.MemberId
if err := doc.InsertOrUpdate("document_name", "book_id", "markdown", "content"); err != nil {
logs.Error(doc.DocumentId, err)
}
if err != nil {
logs.Error("导入项目异常 => ", err)
book.Description = "【项目导入存在错误:" + err.Error() + "】"
}
logs.Info("项目导入完毕 => ", book.BookName)
book.ReleaseContent(book.BookId, lang)
return err return err
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/converter" "github.com/mindoc-org/mindoc/converter"
"github.com/mindoc-org/mindoc/utils/cryptil" "github.com/mindoc-org/mindoc/utils/cryptil"
@ -70,6 +71,7 @@ type BookResult struct {
IsDisplayComment bool `json:"is_display_comment"` IsDisplayComment bool `json:"is_display_comment"`
IsDownload bool `json:"is_download"` IsDownload bool `json:"is_download"`
AutoSave bool `json:"auto_save"` AutoSave bool `json:"auto_save"`
Lang string
} }
func NewBookResult() *BookResult { func NewBookResult() *BookResult {
@ -85,6 +87,11 @@ func (m *BookResult) String() string {
return string(ret) return string(ret)
} }
func (m *BookResult) SetLang(lang string) *BookResult {
m.Lang = lang
return m
}
// 根据项目标识查询项目以及指定用户权限的信息. // 根据项目标识查询项目以及指定用户权限的信息.
func (m *BookResult) FindByIdentify(identify string, memberId int) (*BookResult, error) { func (m *BookResult) FindByIdentify(identify string, memberId int) (*BookResult, error) {
if identify == "" || memberId <= 0 { if identify == "" || memberId <= 0 {
@ -131,13 +138,13 @@ func (m *BookResult) FindByIdentify(identify string, memberId int) (*BookResult,
} }
if m.RoleId == conf.BookFounder { if m.RoleId == conf.BookFounder {
m.RoleName = "创始人" m.RoleName = i18n.Tr(m.Lang, "common.creator")
} else if m.RoleId == conf.BookAdmin { } else if m.RoleId == conf.BookAdmin {
m.RoleName = "管理员" m.RoleName = i18n.Tr(m.Lang, "common.administrator")
} else if m.RoleId == conf.BookEditor { } else if m.RoleId == conf.BookEditor {
m.RoleName = "编辑者" m.RoleName = i18n.Tr(m.Lang, "common.editor")
} else if m.RoleId == conf.BookObserver { } else if m.RoleId == conf.BookObserver {
m.RoleName = "观察者" m.RoleName = i18n.Tr(m.Lang, "common.observer")
} }
doc := NewDocument() doc := NewDocument()

View File

@ -3,6 +3,8 @@ package models
import ( import (
"time" "time"
"github.com/beego/i18n"
"fmt" "fmt"
"strconv" "strconv"
@ -44,6 +46,8 @@ type Document struct {
IsOpen int `orm:"column(is_open);type(int);default(0)" json:"is_open"` IsOpen int `orm:"column(is_open);type(int);default(0)" json:"is_open"`
ViewCount int `orm:"column(view_count);type(int)" json:"view_count"` ViewCount int `orm:"column(view_count);type(int)" json:"view_count"`
AttachList []*Attachment `orm:"-" json:"attach"` AttachList []*Attachment `orm:"-" json:"attach"`
//i18n
Lang string `orm:"-"`
} }
// 多字段唯一键 // 多字段唯一键
@ -276,7 +280,7 @@ func (item *Document) Processor() *Document {
//处理附件 //处理附件
attachList, err := NewAttachment().FindListByDocumentId(item.DocumentId) attachList, err := NewAttachment().FindListByDocumentId(item.DocumentId)
if err == nil && len(attachList) > 0 { if err == nil && len(attachList) > 0 {
content := bytes.NewBufferString("<div class=\"attach-list\"><strong>附件</strong><ul>") content := bytes.NewBufferString("<div class=\"attach-list\"><strong>" + i18n.Tr(item.Lang, "doc.attachment") + "</strong><ul>")
for _, attach := range attachList { for _, attach := range attachList {
if strings.HasPrefix(attach.HttpPath, "/") { if strings.HasPrefix(attach.HttpPath, "/") {
attach.HttpPath = strings.TrimSuffix(conf.BaseUrl, "/") + attach.HttpPath attach.HttpPath = strings.TrimSuffix(conf.BaseUrl, "/") + attach.HttpPath
@ -306,7 +310,7 @@ func (item *Document) Processor() *Document {
docCreator, err := NewMember().Find(item.MemberId, "real_name", "account") docCreator, err := NewMember().Find(item.MemberId, "real_name", "account")
release := "<div class=\"wiki-bottom\">" release := "<div class=\"wiki-bottom\">"
release += "作者:" release += i18n.Tr(item.Lang, "doc.ft_author")
if err == nil && docCreator != nil { if err == nil && docCreator != nil {
if docCreator.RealName != "" { if docCreator.RealName != "" {
release += docCreator.RealName release += docCreator.RealName
@ -314,19 +318,19 @@ func (item *Document) Processor() *Document {
release += docCreator.Account release += docCreator.Account
} }
} }
release += " &nbsp;创建时间:" + item.CreateTime.Local().Format("2006-01-02 15:04") + "<br>" release += " &nbsp;" + i18n.Tr(item.Lang, "doc.ft_create_time") + item.CreateTime.Local().Format("2006-01-02 15:04") + "<br>"
if item.ModifyAt > 0 { if item.ModifyAt > 0 {
docModify, err := NewMember().Find(item.ModifyAt, "real_name", "account") docModify, err := NewMember().Find(item.ModifyAt, "real_name", "account")
if err == nil { if err == nil {
if docModify.RealName != "" { if docModify.RealName != "" {
release += "最后编辑:" + docModify.RealName release += i18n.Tr(item.Lang, "doc.ft_last_editor") + docModify.RealName
} else { } else {
release += "最后编辑:" + docModify.Account release += i18n.Tr(item.Lang, "doc.ft_last_editor") + docModify.Account
} }
} }
} }
release += " &nbsp;更新时间:" + item.ModifyTime.Local().Format("2006-01-02 15:04") + "<br>" release += " &nbsp;" + i18n.Tr(item.Lang, "doc.ft_update_time") + item.ModifyTime.Local().Format("2006-01-02 15:04") + "<br>"
release += "</div>" release += "</div>"
if selector := docQuery.Find("div.markdown-article").First(); selector.Size() > 0 { if selector := docQuery.Find("div.markdown-article").First(); selector.Size() > 0 {
@ -335,7 +339,7 @@ func (item *Document) Processor() *Document {
selector.First().AppendHtml(release) selector.First().AppendHtml(release)
} }
} }
cdnimg,_ := web.AppConfig.String("cdnimg") cdnimg, _ := web.AppConfig.String("cdnimg")
docQuery.Find("img").Each(func(i int, selection *goquery.Selection) { docQuery.Find("img").Each(func(i int, selection *goquery.Selection) {

View File

@ -22,6 +22,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/utils" "github.com/mindoc-org/mindoc/utils"
) )
@ -44,6 +45,8 @@ type Member struct {
CreateTime time.Time `orm:"type(datetime);column(create_time);auto_now_add" json:"create_time"` CreateTime time.Time `orm:"type(datetime);column(create_time);auto_now_add" json:"create_time"`
CreateAt int `orm:"type(int);column(create_at)" json:"create_at"` CreateAt int `orm:"type(int);column(create_at)" json:"create_at"`
LastLoginTime time.Time `orm:"type(datetime);column(last_login_time);null" json:"last_login_time"` LastLoginTime time.Time `orm:"type(datetime);column(last_login_time);null" json:"last_login_time"`
//i18n
Lang string `orm:"-"`
} }
// TableName 获取对应数据库表名. // TableName 获取对应数据库表名.
@ -327,11 +330,11 @@ func (m *Member) Find(id int, cols ...string) (*Member, error) {
func (m *Member) ResolveRoleName() { func (m *Member) ResolveRoleName() {
if m.Role == conf.MemberSuperRole { if m.Role == conf.MemberSuperRole {
m.RoleName = "超级管理员" m.RoleName = i18n.Tr(m.Lang, "common.administrator")
} else if m.Role == conf.MemberAdminRole { } else if m.Role == conf.MemberAdminRole {
m.RoleName = "管理员" m.RoleName = i18n.Tr(m.Lang, "common.editor")
} else if m.Role == conf.MemberGeneralRole { } else if m.Role == conf.MemberGeneralRole {
m.RoleName = "普通用户" m.RoleName = i18n.Tr(m.Lang, "common.obverser")
} }
} }

View File

@ -4,6 +4,7 @@ import (
"time" "time"
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
) )
@ -54,19 +55,19 @@ func (m *MemberRelationshipResult) FromMember(member *Member) *MemberRelationshi
return m return m
} }
func (m *MemberRelationshipResult) ResolveRoleName() *MemberRelationshipResult { func (m *MemberRelationshipResult) ResolveRoleName(lang string) *MemberRelationshipResult {
if m.RoleId == conf.BookAdmin { if m.RoleId == conf.BookAdmin {
m.RoleName = "管理者" m.RoleName = i18n.Tr(lang, "common.administrator")
} else if m.RoleId == conf.BookEditor { } else if m.RoleId == conf.BookEditor {
m.RoleName = "编辑者" m.RoleName = i18n.Tr(lang, "common.editor")
} else if m.RoleId == conf.BookObserver { } else if m.RoleId == conf.BookObserver {
m.RoleName = "观察者" m.RoleName = i18n.Tr(lang, "common.obverser")
} }
return m return m
} }
// 根据项目ID查询用户 // 根据项目ID查询用户
func (m *MemberRelationshipResult) FindForUsersByBookId(bookId, pageIndex, pageSize int) ([]*MemberRelationshipResult, int, error) { func (m *MemberRelationshipResult) FindForUsersByBookId(lang string, bookId, pageIndex, pageSize int) ([]*MemberRelationshipResult, int, error) {
o := orm.NewOrm() o := orm.NewOrm()
var members []*MemberRelationshipResult var members []*MemberRelationshipResult
@ -92,7 +93,7 @@ func (m *MemberRelationshipResult) FindForUsersByBookId(bookId, pageIndex, pageS
} }
for _, item := range members { for _, item := range members {
item.ResolveRoleName() item.ResolveRoleName(lang)
} }
return members, total_count, nil return members, total_count, nil
} }

View File

@ -5,6 +5,7 @@ import (
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs" "github.com/beego/beego/v2/core/logs"
"github.com/beego/i18n"
"github.com/mindoc-org/mindoc/conf" "github.com/mindoc-org/mindoc/conf"
) )
@ -18,6 +19,7 @@ type TeamMember struct {
Account string `orm:"-" json:"account"` Account string `orm:"-" json:"account"`
RealName string `orm:"-" json:"real_name"` RealName string `orm:"-" json:"real_name"`
Avatar string `orm:"-" json:"avatar"` Avatar string `orm:"-" json:"avatar"`
Lang string `orm:"-"`
} }
// TableName 获取对应数据库表名. // TableName 获取对应数据库表名.
@ -46,6 +48,11 @@ func NewTeamMember() *TeamMember {
return &TeamMember{} return &TeamMember{}
} }
func (m *TeamMember) SetLang(lang string) *TeamMember {
m.Lang = lang
return m
}
func (m *TeamMember) First(id int, cols ...string) (*TeamMember, error) { func (m *TeamMember) First(id int, cols ...string) (*TeamMember, error) {
if id <= 0 { if id <= 0 {
return nil, errors.New("参数错误") return nil, errors.New("参数错误")
@ -173,6 +180,7 @@ func (m *TeamMember) FindToPager(teamId, pageIndex, pageSize int) (list []*TeamM
//将来优化 //将来优化
for _, item := range list { for _, item := range list {
item.Lang = m.Lang
item.Include() item.Include()
} }
return return
@ -187,13 +195,13 @@ func (m *TeamMember) Include() *TeamMember {
m.Avatar = member.Avatar m.Avatar = member.Avatar
} }
if m.RoleId == 0 { if m.RoleId == 0 {
m.RoleName = "创始人" m.RoleName = i18n.Tr(m.Lang, "common.creator") //"创始人"
} else if m.RoleId == 1 { } else if m.RoleId == 1 {
m.RoleName = "管理员" m.RoleName = i18n.Tr(m.Lang, "common.administrator") //"管理员"
} else if m.RoleId == 2 { } else if m.RoleId == 2 {
m.RoleName = "编辑者" m.RoleName = i18n.Tr(m.Lang, "common.editor") //"编辑者"
} else if m.RoleId == 3 { } else if m.RoleId == 3 {
m.RoleName = "观察者" m.RoleName = i18n.Tr(m.Lang, "common.observer") //"观察者"
} }
return m return m
} }

View File

@ -1,11 +1,82 @@
package routers package routers
import ( import (
"crypto/tls"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/context"
"github.com/mindoc-org/mindoc/conf"
"github.com/mindoc-org/mindoc/controllers" "github.com/mindoc-org/mindoc/controllers"
) )
func rt(req *http.Request) (*http.Response, error) {
log.Printf("request received. url=%s", req.URL)
// req.Header.Set("Host", "httpbin.org") // <--- I set it here as well
defer log.Printf("request complete. url=%s", req.URL)
return http.DefaultTransport.RoundTrip(req)
}
// roundTripper makes func signature a http.RoundTripper
type roundTripper func(*http.Request) (*http.Response, error)
func (f roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
type CorsTransport struct {
http.RoundTripper
}
func (t *CorsTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
// refer:
// - https://stackoverflow.com/questions/31535569/golang-how-to-read-response-body-of-reverseproxy/31536962#31536962
// - https://gist.github.com/simon-cj/b4da0b2bca793ec3b8a5abe04c8fca41
resp, err = t.RoundTripper.RoundTrip(req)
logs.Debug(resp)
if err != nil {
return nil, err
}
resp.Header.Del("Access-Control-Request-Method")
resp.Header.Set("Access-Control-Allow-Origin", "*")
return resp, nil
}
func init() { func init() {
web.Any("/cors-anywhere", func(ctx *context.Context) {
u, _ := url.PathUnescape(ctx.Input.Query("url"))
logs.Error("ReverseProxy: ", u)
if len(u) > 0 && strings.HasPrefix(u, "http") {
if strings.TrimRight(conf.BaseUrl, "/") == ctx.Input.Site() {
ctx.Redirect(302, u)
} else {
target, _ := url.Parse(u)
logs.Debug("target: ", target)
proxy := &httputil.ReverseProxy{
Transport: roundTripper(rt),
Director: func(req *http.Request) {
req.Header = ctx.Request.Header
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = target.Path
req.Header.Set("Host", target.Host)
},
}
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
proxy.ServeHTTP(ctx.ResponseWriter, ctx.Request)
}
} else {
ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
ctx.Output.Body([]byte("400 Bad Request"))
}
})
web.Router("/", &controllers.HomeController{}, "*:Index") web.Router("/", &controllers.HomeController{}, "*:Index")
web.Router("/login", &controllers.AccountController{}, "*:Login") web.Router("/login", &controllers.AccountController{}, "*:Login")

View File

@ -1,12 +1,36 @@
#!/bin/sh #!/bin/bash
set -e set -eux
cd /mindoc/ # 数据库等初始化
if [ ! -f "/mindoc/conf/app.conf" ] ; then
cp /mindoc/conf/app.conf.example /mindoc/conf/app.conf
fi
export ZONEINFO=/mindoc/lib/time/zoneinfo.zip
/mindoc/mindoc_linux_amd64 install /mindoc/mindoc_linux_amd64 install
exec /mindoc/mindoc_linux_amd64
# 导出同步检查
mkdir -p /mindoc-sync-host
if ! [ -f "/mindoc-sync-host/sync.sh" ]; then
# 同步方向: docker->HOST 或 HOST -> docker
# echo "export MINDOC_SYNC=" >> /mindoc-sync-host/sync.sh # 不同步
echo "export MINDOC_SYNC=docker2host" >> /mindoc-sync-host/sync.sh # 默认 docker->HOST
# 同步内容
# conf: 配置
# database: sqlite方式数据库
# runtime: 运行时数据(日志等)
# static: 静态文件
# uploads: 上传文件
# views: 页面视图
# echo "export SYNC_LIST='conf;database;runtime;static;uploads;views'" >> /mindoc-sync-host/sync.sh # 同步所有内容
# echo "export SYNC_LIST=" >> /mindoc-sync-host/sync.sh # 不同步任何内容
echo "export SYNC_LIST='conf;database;uploads'" >> /mindoc-sync-host/sync.sh # 同步conf、database、uploads
# 同步操作(sync/copy/sync --dry-run 等具体参考rclone文档host2docker务必谨慎操作)
# echo "export SYNC_ACTION=sync --dry-run" >> /mindoc-sync-host/sync.sh # 无操作且仅显示同步文件信息(--dry-run)
echo "export SYNC_ACTION=sync" >> /mindoc-sync-host/sync.sh # 默认同步
# 同步脚本
echo "source /mindoc/sync_host.sh" >> /mindoc-sync-host/sync.sh
fi
# 同步操作
source /mindoc-sync-host/sync.sh
# 运行
/mindoc/mindoc_linux_amd64

View File

@ -265,7 +265,7 @@ table>tbody>tr:hover{
right: 0; right: 0;
overflow-y: auto; overflow-y: auto;
background-color: #fafafa; background-color: #fafafa;
margin-bottom: 35px; margin-bottom: 60px;
} }
.m-manual .manual-tab .tab-item.active { .m-manual .manual-tab .tab-item.active {

View File

@ -96,6 +96,7 @@ body{
bottom: 0; bottom: 0;
overflow: hidden; overflow: hidden;
border-top: 1px solid #DDDDDD; border-top: 1px solid #DDDDDD;
z-index: 999;
} }
.manual-editor-container .manual-editormd{ .manual-editor-container .manual-editormd{
position: absolute; position: absolute;

84
static/editor.md/editormd.js 100644 → 100755
View File

@ -170,6 +170,7 @@
flowChart : false, // flowChart.js only support IE9+ flowChart : false, // flowChart.js only support IE9+
sequenceDiagram : false, // sequenceDiagram.js only support IE9+ sequenceDiagram : false, // sequenceDiagram.js only support IE9+
mermaid : true, mermaid : true,
mindMap : true, // 脑图
previewCodeHighlight : true, previewCodeHighlight : true,
toolbar : true, // show/hide toolbar toolbar : true, // show/hide toolbar
@ -511,16 +512,14 @@
if (settings.mermaid) { if (settings.mermaid) {
editormd.loadCSS(loadPath + "mermaid/mermaid", function () {
editormd.loadScript(loadPath + "mermaid/mermaid.min", function () { editormd.loadScript(loadPath + "mermaid/mermaid.min", function () {
window.mermaid.initialize({ window.mermaid.initialize({
theme: 'default', theme: 'default',
startOnLoad:true, logLevel: 3,
cloneCssStyles: true, securityLevel: 'loose',
flowchart: { flowchart: { curve: 'basis' },
htmlLabels: false, gantt: { axisFormat: '%m/%d/%Y' },
useMaxWidth:false sequence: { actorMargin: 50 },
},
}); });
mermaid.ganttConfig = { mermaid.ganttConfig = {
axisFormatter: [ axisFormatter: [
@ -553,7 +552,6 @@
_this.loadedDisplay(); _this.loadedDisplay();
} }
}); });
});
} }
if (settings.flowChart) if (settings.flowChart)
{ {
@ -569,6 +567,20 @@
}); });
} }
if (settings.mindMap)
{
editormd.loadScript(loadPath + "mindmap/transform.min", function() {
editormd.loadScript(loadPath + "mindmap/d3@5", function() {
editormd.loadScript(loadPath + "mindmap/view.min", function() {
if (!isLoadedDisplay){
isLoadedDisplay = true;
_this.loadedDisplay();
}
});
});
});
}
if(settings.sequenceDiagram) { if(settings.sequenceDiagram) {
editormd.loadCSS(loadPath + "sequence/sequence-diagram-min", function () { editormd.loadCSS(loadPath + "sequence/sequence-diagram-min", function () {
editormd.loadScript(loadPath + "sequence/webfont", function() { editormd.loadScript(loadPath + "sequence/webfont", function() {
@ -1560,6 +1572,22 @@
return this; return this;
}, },
/**
* - 2020-04-12
*
* @returns {editormd} editormd
*/
mindmapRender:function(){
this.previewContainer.find(".mindmap").each(function(){
var mmap = $(this);
var md_data = window.markmap.transform(mmap.text().trim());
window.markmap.markmap("svg#"+this.id,md_data)
});
return this;
},
/** /**
* *
* FlowChart and SequenceDiagram Renderer * FlowChart and SequenceDiagram Renderer
@ -2069,6 +2097,7 @@
sequenceDiagram : settings.sequenceDiagram, sequenceDiagram : settings.sequenceDiagram,
previewCodeHighlight : settings.previewCodeHighlight, previewCodeHighlight : settings.previewCodeHighlight,
mermaid : settings.mermaid, mermaid : settings.mermaid,
mindMap : settings.mindMap, // 思维导图
}; };
var markedOptions = this.markedOptions = { var markedOptions = this.markedOptions = {
@ -2150,6 +2179,14 @@
} }
} }
// 渲染脑图
if(settings.mindMap){
setTimeout(function(){
_this.mindmapRender();
},10)
}
if (settings.flowChart || settings.sequenceDiagram || settings.mermaid) if (settings.flowChart || settings.sequenceDiagram || settings.mermaid)
{ {
flowchartTimer = setTimeout(function(){ flowchartTimer = setTimeout(function(){
@ -3896,6 +3933,23 @@
{ {
return "<p class=\"" + editormd.classNames.tex + "\">" + code + "</p>"; return "<p class=\"" + editormd.classNames.tex + "\">" + code + "</p>";
} }
else if (/^mindmap/i.test(lang)){
var len = 9 || 32;
var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
var maxPos = $chars.length;
var map_id = '';
var custom_height;
var h = lang.split('>')[1];
if (h != undefined) {
custom_height = h;
} else {
custom_height = 150;
}
for (var i = 0; i < len; i++) {
map_id += $chars.charAt(Math.floor(Math.random() * maxPos));
}
return "<svg class='mindmap' style='width:100%;min-height=150px;height:"+custom_height+"px;' id='mindmap-"+ map_id +"'>"+code+"</svg>";
}
else else
{ {
@ -4183,6 +4237,7 @@
sequenceDiagram : false, sequenceDiagram : false,
previewCodeHighlight : true, previewCodeHighlight : true,
mermaid : true, mermaid : true,
mindMap : true, //思维导图
}; };
editormd.$marked = marked; editormd.$marked = marked;
@ -4213,6 +4268,7 @@
flowChart : settings.flowChart, flowChart : settings.flowChart,
sequenceDiagram : settings.sequenceDiagram, sequenceDiagram : settings.sequenceDiagram,
mermaid : settings.mermaid, mermaid : settings.mermaid,
mindMap : settings.mindMap, // 思维导图
previewCodeHighlight : settings.previewCodeHighlight, previewCodeHighlight : settings.previewCodeHighlight,
}; };
@ -4307,6 +4363,18 @@
} }
} }
// 前台渲染脑图
if(settings.mindMap){
var mindmapHandle = function(){
div.find(".mindmap").each(function(){
var mmap = $(this);
var md_data = window.markmap.transform(mmap.text().trim());
window.markmap.markmap("svg#"+this.id,md_data)
});
}
mindmapHandle();
}
div.getMarkdown = function() { div.getMarkdown = function() {
return saveTo.val(); return saveTo.val();
}; };

File diff suppressed because one or more lines are too long

View File

@ -1,273 +0,0 @@
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
.lang-mermaid .mermaid .label {
color: #333;
}
.lang-mermaid .node rect,
.lang-mermaid .node circle,
.lang-mermaid .node ellipse,
.lang-mermaid .node polygon {
fill: #ECECFF;
stroke: #CCCCFF;
stroke-width: 1px;
}
.lang-mermaid .edgePath .path {
stroke: #333333;
}
.lang-mermaid .edgeLabel {
background-color: #e8e8e8;
}
.lang-mermaid .cluster rect {
fill: #ffffde !important;
rx: 4 !important;
stroke: #aaaa33 !important;
stroke-width: 1px !important;
}
.lang-mermaid .cluster text {
fill: #333;
}
.lang-mermaid .actor {
stroke: #CCCCFF;
fill: #ECECFF;
}
.lang-mermaid text.actor {
fill: black;
stroke: none;
}
.lang-mermaid .actor-line {
stroke: grey;
}
.lang-mermaid .messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #333;
}
.lang-mermaid .messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: #333;
}
.lang-mermaid #arrowhead {
fill: #333;
}
.lang-mermaid #crosshead path {
fill: #333 !important;
stroke: #333 !important;
}
.lang-mermaid .messageText {
fill: #333;
stroke: none;
}
.lang-mermaid .labelBox {
stroke: #CCCCFF;
fill: #ECECFF;
}
.lang-mermaid .labelText {
fill: black;
stroke: none;
}
.lang-mermaid .loopText {
fill: black;
stroke: none;
}
.lang-mermaid .loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #CCCCFF;
}
.lang-mermaid .note {
stroke: #aaaa33;
fill: #fff5ad;
}
.lang-mermaid .noteText {
fill: black;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
/** Section styling */
.lang-mermaid .section {
stroke: none;
opacity: 0.2;
}
.lang-mermaid .section0 {
fill: rgba(102, 102, 255, 0.49);
}
.lang-mermaid .section2 {
fill: #fff400;
}
.lang-mermaid .section1,
.section3 {
fill: white;
opacity: 0.2;
}
.lang-mermaid .sectionTitle0 {
fill: #333;
}
.lang-mermaid .sectionTitle1 {
fill: #333;
}
.lang-mermaid .sectionTitle2 {
fill: #333;
}
.lang-mermaid .sectionTitle3 {
fill: #333;
}
.lang-mermaid .sectionTitle {
text-anchor: start;
font-size: 11px;
text-height: 14px;
}
/* Grid and axis */
.lang-mermaid .grid .tick {
stroke: lightgrey;
opacity: 0.3;
shape-rendering: crispEdges;
}
.lang-mermaid .grid path {
stroke-width: 0;
}
/* Today line */
.lang-mermaid .today {
fill: none;
stroke: red;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
.lang-mermaid .task {
stroke-width: 2;
}
.lang-mermaid .taskText {
text-anchor: middle;
font-size: 11px;
}
.lang-mermaid .taskTextOutsideRight {
fill: black;
text-anchor: start;
font-size: 11px;
}
.lang-mermaid .taskTextOutsideLeft {
fill: black;
text-anchor: end;
font-size: 11px;
}
/* Specific task settings for the sections*/
.lang-mermaid .taskText0,
.lang-mermaid .taskText1,
.lang-mermaid .taskText2,
.lang-mermaid .taskText3 {
fill: white;
}
.lang-mermaid .task0,
.lang-mermaid .task1,
.lang-mermaid .task2,
.lang-mermaid .task3 {
fill: #8a90dd;
stroke: #534fbc;
}
.lang-mermaid .taskTextOutside0,
.lang-mermaid .taskTextOutside2 {
fill: black;
}
.lang-mermaid .taskTextOutside1,
.lang-mermaid .taskTextOutside3 {
fill: black;
}
/* Active task */
.lang-mermaid .active0,
.lang-mermaid .active1,
.lang-mermaid .active2,
.lang-mermaid .active3 {
fill: #bfc7ff;
stroke: #534fbc;
}
.lang-mermaid .activeText0,
.lang-mermaid .activeText1,
.lang-mermaid .activeText2,
.lang-mermaid .activeText3 {
fill: black !important;
}
/* Completed task */
.lang-mermaid .done0,
.lang-mermaid .done1,
.lang-mermaid .done2,
.lang-mermaid .done3 {
stroke: grey;
fill: lightgrey;
stroke-width: 2;
}
.lang-mermaid .doneText0,
.lang-mermaid .doneText1,
.lang-mermaid .doneText2,
.lang-mermaid .doneText3 {
fill: black !important;
}
/* Tasks on the critical line */
.lang-mermaid .crit0,
.lang-mermaid .crit1,
.lang-mermaid .crit2,
.lang-mermaid .crit3 {
stroke: #ff8888;
fill: red;
stroke-width: 2;
}
.lang-mermaid .activeCrit0,
.lang-mermaid .activeCrit1,
.lang-mermaid .activeCrit2,
.lang-mermaid .activeCrit3 {
stroke: #ff8888;
fill: #bfc7ff;
stroke-width: 2;
}
.lang-mermaid .doneCrit0,
.lang-mermaid .doneCrit1,
.lang-mermaid .doneCrit2,
.lang-mermaid .doneCrit3 {
stroke: #ff8888;
fill: lightgrey;
stroke-width: 2;
cursor: pointer;
shape-rendering: crispEdges;
}
.lang-mermaid .doneCritText0,
.lang-mermaid .doneCritText1,
.lang-mermaid .doneCritText2,
.lang-mermaid .doneCritText3 {
fill: black !important;
}
.lang-mermaid .activeCritText0,
.lang-mermaid .activeCritText1,
.lang-mermaid .activeCritText2,
.lang-mermaid .activeCritText3 {
fill: black !important;
}
.lang-mermaid .titleText {
text-anchor: middle;
font-size: 18px;
fill: black;
}
/*
*/
.lang-mermaid .node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
.lang-mermaid div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial;
font-size: 12px;
background: #ffffde;
border: 1px solid #aaaa33;
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

View File

@ -1,275 +0,0 @@
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
.mermaid .label {
color: #323D47;
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: #BDD5EA;
stroke: #81B1DB;
stroke-width: 1px;
}
.edgePath .path {
stroke: lightgrey;
}
.edgeLabel {
background-color: #e8e8e8;
}
.cluster rect {
fill: #6D6D65 !important;
rx: 4 !important;
stroke: rgba(255, 255, 255, 0.25) !important;
stroke-width: 1px !important;
}
.cluster text {
fill: #F9FFFE;
}
.actor {
stroke: #81B1DB;
fill: #BDD5EA;
}
text.actor {
fill: black;
stroke: none;
}
.actor-line {
stroke: lightgrey;
}
.messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: lightgrey;
}
.messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: lightgrey;
}
#arrowhead {
fill: lightgrey !important;
}
#crosshead path {
fill: lightgrey !important;
stroke: lightgrey !important;
}
.messageText {
fill: lightgrey;
stroke: none;
}
.labelBox {
stroke: #81B1DB;
fill: #BDD5EA;
}
.labelText {
fill: #323D47;
stroke: none;
}
.loopText {
fill: lightgrey;
stroke: none;
}
.loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #81B1DB;
}
.note {
stroke: rgba(255, 255, 255, 0.25);
fill: #fff5ad;
}
.noteText {
fill: black;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
/** Section styling */
.section {
stroke: none;
opacity: 0.2;
}
.section0 {
fill: rgba(255, 255, 255, 0.3);
}
.section2 {
fill: #EAE8B9;
}
.section1,
.section3 {
fill: white;
opacity: 0.2;
}
.sectionTitle0 {
fill: #F9FFFE;
}
.sectionTitle1 {
fill: #F9FFFE;
}
.sectionTitle2 {
fill: #F9FFFE;
}
.sectionTitle3 {
fill: #F9FFFE;
}
.sectionTitle {
text-anchor: start;
font-size: 11px;
text-height: 14px;
}
/* Grid and axis */
.grid .tick {
stroke: rgba(255, 255, 255, 0.3);
opacity: 0.3;
shape-rendering: crispEdges;
}
.grid .tick text {
fill: lightgrey;
opacity: 0.5;
}
.grid path {
stroke-width: 0;
}
/* Today line */
.today {
fill: none;
stroke: #DB5757;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
.task {
stroke-width: 1;
}
.taskText {
text-anchor: middle;
font-size: 11px;
}
.taskTextOutsideRight {
fill: #323D47;
text-anchor: start;
font-size: 11px;
}
.taskTextOutsideLeft {
fill: #323D47;
text-anchor: end;
font-size: 11px;
}
/* Specific task settings for the sections*/
.taskText0,
.taskText1,
.taskText2,
.taskText3 {
fill: #323D47;
}
.task0,
.task1,
.task2,
.task3 {
fill: #BDD5EA;
stroke: rgba(255, 255, 255, 0.5);
}
.taskTextOutside0,
.taskTextOutside2 {
fill: lightgrey;
}
.taskTextOutside1,
.taskTextOutside3 {
fill: lightgrey;
}
/* Active task */
.active0,
.active1,
.active2,
.active3 {
fill: #81B1DB;
stroke: rgba(255, 255, 255, 0.5);
}
.activeText0,
.activeText1,
.activeText2,
.activeText3 {
fill: #323D47 !important;
}
/* Completed task */
.done0,
.done1,
.done2,
.done3 {
fill: lightgrey;
}
.doneText0,
.doneText1,
.doneText2,
.doneText3 {
fill: #323D47 !important;
}
/* Tasks on the critical line */
.crit0,
.crit1,
.crit2,
.crit3 {
stroke: #E83737;
fill: #E83737;
stroke-width: 2;
}
.activeCrit0,
.activeCrit1,
.activeCrit2,
.activeCrit3 {
stroke: #E83737;
fill: #81B1DB;
stroke-width: 2;
}
.doneCrit0,
.doneCrit1,
.doneCrit2,
.doneCrit3 {
stroke: #E83737;
fill: lightgrey;
stroke-width: 1;
cursor: pointer;
shape-rendering: crispEdges;
}
.doneCritText0,
.doneCritText1,
.doneCritText2,
.doneCritText3 {
fill: lightgrey !important;
}
.activeCritText0,
.activeCritText1,
.activeCritText2,
.activeCritText3 {
fill: #323D47 !important;
}
.titleText {
text-anchor: middle;
font-size: 18px;
fill: lightgrey;
}
/*
*/
.node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial;
font-size: 12px;
background: #6D6D65;
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

View File

@ -1,353 +0,0 @@
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
.mermaid .label {
font-family: 'trebuchet ms', verdana, arial;
color: #333;
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: #cde498;
stroke: #13540c;
stroke-width: 1px;
}
.edgePath .path {
stroke: green;
stroke-width: 1.5px;
}
.edgeLabel {
background-color: #e8e8e8;
}
.cluster rect {
fill: #cdffb2 !important;
rx: 4 !important;
stroke: #6eaa49 !important;
stroke-width: 1px !important;
}
.cluster text {
fill: #333;
}
.actor {
stroke: #13540c;
fill: #cde498;
}
text.actor {
fill: black;
stroke: none;
}
.actor-line {
stroke: grey;
}
.messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #333;
}
.messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: #333;
}
#arrowhead {
fill: #333;
}
#crosshead path {
fill: #333 !important;
stroke: #333 !important;
}
.messageText {
fill: #333;
stroke: none;
}
.labelBox {
stroke: #326932;
fill: #cde498;
}
.labelText {
fill: black;
stroke: none;
}
.loopText {
fill: black;
stroke: none;
}
.loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #326932;
}
.note {
stroke: #6eaa49;
fill: #fff5ad;
}
.noteText {
fill: black;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
/** Section styling */
.section {
stroke: none;
opacity: 0.2;
}
.section0 {
fill: #6eaa49;
}
.section2 {
fill: #6eaa49;
}
.section1,
.section3 {
fill: white;
opacity: 0.2;
}
.sectionTitle0 {
fill: #333;
}
.sectionTitle1 {
fill: #333;
}
.sectionTitle2 {
fill: #333;
}
.sectionTitle3 {
fill: #333;
}
.sectionTitle {
text-anchor: start;
font-size: 11px;
text-height: 14px;
}
/* Grid and axis */
.grid .tick {
stroke: lightgrey;
opacity: 0.3;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
/* Today line */
.today {
fill: none;
stroke: red;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
.task {
stroke-width: 2;
}
.taskText {
text-anchor: middle;
font-size: 11px;
}
.taskTextOutsideRight {
fill: black;
text-anchor: start;
font-size: 11px;
}
.taskTextOutsideLeft {
fill: black;
text-anchor: end;
font-size: 11px;
}
/* Specific task settings for the sections*/
.taskText0,
.taskText1,
.taskText2,
.taskText3 {
fill: white;
}
.task0,
.task1,
.task2,
.task3 {
fill: #487e3a;
stroke: #13540c;
}
.taskTextOutside0,
.taskTextOutside2 {
fill: black;
}
.taskTextOutside1,
.taskTextOutside3 {
fill: black;
}
/* Active task */
.active0,
.active1,
.active2,
.active3 {
fill: #cde498;
stroke: #13540c;
}
.activeText0,
.activeText1,
.activeText2,
.activeText3 {
fill: black !important;
}
/* Completed task */
.done0,
.done1,
.done2,
.done3 {
stroke: grey;
fill: lightgrey;
stroke-width: 2;
}
.doneText0,
.doneText1,
.doneText2,
.doneText3 {
fill: black !important;
}
/* Tasks on the critical line */
.crit0,
.crit1,
.crit2,
.crit3 {
stroke: #ff8888;
fill: red;
stroke-width: 2;
}
.activeCrit0,
.activeCrit1,
.activeCrit2,
.activeCrit3 {
stroke: #ff8888;
fill: #cde498;
stroke-width: 2;
}
.doneCrit0,
.doneCrit1,
.doneCrit2,
.doneCrit3 {
stroke: #ff8888;
fill: lightgrey;
stroke-width: 2;
cursor: pointer;
shape-rendering: crispEdges;
}
.doneCritText0,
.doneCritText1,
.doneCritText2,
.doneCritText3 {
fill: black !important;
}
.activeCritText0,
.activeCritText1,
.activeCritText2,
.activeCritText3 {
fill: black !important;
}
.titleText {
text-anchor: middle;
font-size: 18px;
fill: black;
}
/*
*/
g.classGroup text {
fill: #13540c;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
g.classGroup rect {
fill: #cde498;
stroke: #13540c;
}
g.classGroup line {
stroke: #13540c;
stroke-width: 1;
}
svg .classLabel .box {
stroke: none;
stroke-width: 0;
fill: #cde498;
opacity: 0.5;
}
svg .classLabel .label {
fill: #13540c;
}
.relation {
stroke: #13540c;
stroke-width: 1;
fill: none;
}
.composition {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#compositionStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#compositionEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
.aggregation {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
#aggregationStart {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
#aggregationEnd {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
#dependencyStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#dependencyEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#extensionStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#extensionEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
.node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial;
font-size: 12px;
background: #cdffb2;
border: 1px solid #6eaa49;
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -3,6 +3,10 @@ $(function () {
js : window.katex.js, js : window.katex.js,
css : window.katex.css css : window.katex.css
}; };
var htmlDecodeList = ["style","script","title","onmouseover","onmouseout","style"];
if (!window.IS_ENABLE_IFRAME) {
htmlDecodeList.unshift("iframe");
}
window.editor = editormd("docEditor", { window.editor = editormd("docEditor", {
width: "100%", width: "100%",
height: "100%", height: "100%",
@ -18,8 +22,8 @@ $(function () {
taskList: true, taskList: true,
flowChart: true, flowChart: true,
mermaid: true, mermaid: true,
htmlDecode: "style,script,iframe,title,onmouseover,onmouseout,style", htmlDecode: htmlDecodeList.join(','),
lineNumbers: false, lineNumbers: true,
sequenceDiagram: true, sequenceDiagram: true,
highlightStyle: window.highlightStyle ? window.highlightStyle : "github", highlightStyle: window.highlightStyle ? window.highlightStyle : "github",
tocStartLevel: 1, tocStartLevel: 1,

View File

@ -0,0 +1,106 @@
/**
* es6class使,
* https://babeljs.io/repl
* @babel/plugin-transform-classes
* `c2b`
*/
function c2b_classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function c2b_createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function c2b_inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true }
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf =
Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function c2b_createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
} else if (call !== void 0) {
throw new TypeError(
"Derived constructors may only return object or undefined"
);
}
return _assertThisInitialized(self);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return self;
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Boolean.prototype.valueOf.call(
Reflect.construct(Boolean, [], function () {})
);
return true;
} catch (e) {
return false;
}
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf
? Object.getPrototypeOf
: function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}

View File

@ -0,0 +1 @@
!function(P,H,k){"use strict";if(1==P.importNode.length&&!H.get("ungap-li")){var D="extends";try{var e={extends:"li"},t=HTMLLIElement,n=function(){return Reflect.construct(t,[],n)};if(n.prototype=k.create(t.prototype),H.define("ungap-li",n,e),!/is="ungap-li"/.test((new n).outerHTML))throw e}catch(e){!function(){var l="attributeChangedCallback",n="connectedCallback",r="disconnectedCallback",e=Element.prototype,i=k.assign,t=k.create,o=k.defineProperties,a=k.getOwnPropertyDescriptor,s=k.setPrototypeOf,u=H.define,c=H.get,f=H.upgrade,p=H.whenDefined,v=t(null),d=new WeakMap,g={childList:!0,subtree:!0};Reflect.ownKeys(self).filter(function(e){return"string"==typeof e&&/^HTML(?!Element)/.test(e)}).forEach(function(e){function t(){}var n=self[e];s(t,n),(t.prototype=n.prototype).constructor=t,(n={})[e]={value:t},o(self,n)}),new MutationObserver(m).observe(P,g),O(Document.prototype,"importNode"),O(Node.prototype,"cloneNode"),o(H,{define:{value:function(e,t,n){if(e=e.toLowerCase(),n&&D in n){v[e]=i({},n,{Class:t});for(var e=n[D]+'[is="'+e+'"]',r=P.querySelectorAll(e),o=0,a=r.length;o<a;o++)A(r[o])}else u.apply(H,arguments)}},get:{value:function(e){return e in v?v[e].Class:c.call(H,e)}},upgrade:{value:function(e){var t=L(e);!t||e instanceof t.Class?f.call(H,e):N(e,t)}},whenDefined:{value:function(e){return e in v?Promise.resolve():p.call(H,e)}}});var h=P.createElement;o(P,{createElement:{value:function(e,t){e=h.call(P,e);return t&&"is"in t&&(e.setAttribute("is",t.is),H.upgrade(e)),e}}});var b=a(e,"attachShadow").value,y=a(e,"innerHTML");function m(e){for(var t=0,n=e.length;t<n;t++){for(var r=e[t],o=r.addedNodes,a=r.removedNodes,i=0,l=o.length;i<l;i++)A(o[i]);for(i=0,l=a.length;i<l;i++)C(a[i])}}function w(e){for(var t=0,n=e.length;t<n;t++){var r=e[t],o=r.attributeName,a=r.oldValue,i=r.target,r=i.getAttribute(o);l in i&&(a!=r||null!=r)&&i[l](o,a,i.getAttribute(o),null)}}function C(e){var t;1===e.nodeType&&((t=L(e))&&e instanceof t.Class&&r in e&&d.get(e)!==r&&(d.set(e,r),Promise.resolve(e).then(T)),E(e,C))}function L(e){e=e.getAttribute("is");return e&&(e=e.toLowerCase())in v?v[e]:null}function M(e){e[n]()}function T(e){e[r]()}function N(e,t){var t=t.Class,n=t.observedAttributes||[];if(s(e,t.prototype),n.length){new MutationObserver(w).observe(e,{attributes:!0,attributeFilter:n,attributeOldValue:!0});for(var r=[],o=0,a=n.length;o<a;o++)r.push({attributeName:n[o],oldValue:null,target:e});w(r)}}function A(e){var t;1===e.nodeType&&((t=L(e))&&(e instanceof t.Class||N(e,t),n in e&&e.isConnected&&d.get(e)!==n&&(d.set(e,n),Promise.resolve(e).then(M))),E(e,A))}function E(e,t){for(var n=e.content,r=(n&&11==n.nodeType?n:e).querySelectorAll("[is]"),o=0,a=r.length;o<a;o++)t(r[o])}function O(e,t){var n=e[t],r={};r[t]={value:function(){var e=n.apply(this,arguments);switch(e.nodeType){case 1:case 11:E(e,A)}return e}},o(e,r)}o(e,{attachShadow:{value:function(){var e=b.apply(this,arguments);return new MutationObserver(m).observe(e,g),e}},innerHTML:{get:y.get,set:function(e){y.set.call(this,e),/\bis=("|')?[a-z0-9_-]+\1/i.test(e)&&E(this,A)}}})}()}}}(document,customElements,Object);

View File

@ -5,7 +5,7 @@
/** /**
* *
*/ */
function openLastSelectedNode() { function openLastSelectedNode() {
//如果文档树或编辑器没有准备好则不加载文档 //如果文档树或编辑器没有准备好则不加载文档
if (window.treeCatalog == null || window.editor == null) { if (window.treeCatalog == null || window.editor == null) {
return false; return false;
@ -81,7 +81,14 @@ function jstree_save(node, parent) {
var index = layer.load(1, { var index = layer.load(1, {
shade: [0.1, '#fff'] //0.1透明度的白色背景 shade: [0.1, '#fff'] //0.1透明度的白色背景
}); });
locales = {
'zh-CN': {
saveSortSucc: '',
},
'en': {
saveSortSucc: 'Save sort success',
}
}
$.ajax({ $.ajax({
url: window.sortURL, url: window.sortURL,
type: "post", type: "post",
@ -89,7 +96,7 @@ function jstree_save(node, parent) {
success: function (res) { success: function (res) {
layer.close(index); layer.close(index);
if (res.errcode === 0) { if (res.errcode === 0) {
layer.msg("保存排序成功"); layer.msg(locales[lang].saveSortSucc);
} else { } else {
layer.msg(res.message); layer.msg(res.message);
} }
@ -138,8 +145,27 @@ function getSiblingSort(node) {
* @param $node * @param $node
*/ */
function openDeleteDocumentDialog($node) { function openDeleteDocumentDialog($node) {
var index = layer.confirm('', { locales = {
btn: ['', ''] //按钮 'zh-CN': {
saveSortSucc: '',
confirmDeleteDoc: '',
confirm: '',
cancel: '',
deleteFailed: '',
confirmLeave: ''
},
'en': {
saveSortSucc: 'Save sort success',
confirmDeleteDoc: 'Are you sure you want to delete this document?',
confirm: 'Confirm',
cancel: 'Cancel',
deleteFailed: 'Delete Failed',
confirmLeave: 'The content you entered has not been saved. Are you sure you want to leave this page?'
}
}
langs = locales[lang];
var index = layer.confirm(langs.confirmDeleteDoc, {
btn: [langs.confirm, langs.cancel] //按钮
}, function () { }, function () {
$.post(window.deleteURL, {"identify": window.book.identify, "doc_id": $node.id}).done(function (res) { $.post(window.deleteURL, {"identify": window.book.identify, "doc_id": $node.id}).done(function (res) {
@ -154,11 +180,11 @@ function openDeleteDocumentDialog($node) {
// console.log(window.documentCategory) // console.log(window.documentCategory)
setLastSelectNode(); setLastSelectNode();
} else { } else {
layer.msg("删除失败", {icon: 2}) layer.msg(lang.deleteFailed, {icon: 2})
} }
}).fail(function () { }).fail(function () {
layer.close(index); layer.close(index);
layer.msg("删除失败", {icon: 2}) layer.msg(lang.deleteFailed, {icon: 2})
}); });
}); });
@ -228,6 +254,14 @@ function pushVueLists($lists) {
* *
*/ */
function releaseBook() { function releaseBook() {
locales = {
'zh-CN': {
publishToQueue: '',
},
'en': {
publishToQueue: 'The publish task has been pushed to the queue</br> and will be executed soon.',
}
}
$.ajax({ $.ajax({
url: window.releaseURL, url: window.releaseURL,
data: {"identify": window.book.identify}, data: {"identify": window.book.identify},
@ -235,7 +269,7 @@ function releaseBook() {
dataType: "json", dataType: "json",
success: function (res) { success: function (res) {
if (res.errcode === 0) { if (res.errcode === 0) {
layer.msg("发布任务已推送到任务队列,稍后将在后台执行。"); layer.msg(locales[lang].publishToQueue);
} else { } else {
layer.msg(res.message); layer.msg(res.message);
} }
@ -298,9 +332,17 @@ function showSuccess($msg, $id) {
} }
window.documentHistory = function () { window.documentHistory = function () {
locales = {
'zh-CN': {
hisVer: '',
},
'en': {
hisVer: 'Historic version',
}
}
layer.open({ layer.open({
type: 2, type: 2,
title: '', title: locales[lang].hisVer,
shadeClose: true, shadeClose: true,
shade: 0.8, shade: 0.8,
area: ['700px', '80%'], area: ['700px', '80%'],
@ -320,6 +362,16 @@ window.documentHistory = function () {
}; };
function uploadImage($id, $callback) { function uploadImage($id, $callback) {
locales = {
'zh-CN': {
unsupportType: '',
uploadFailed: ''
},
'en': {
unsupportType: 'Unsupport image type',
uploadFailed: 'Upload image failed'
}
}
/** 粘贴上传图片 **/ /** 粘贴上传图片 **/
document.getElementById($id).addEventListener('paste', function (e) { document.getElementById($id).addEventListener('paste', function (e) {
if (e.clipboardData && e.clipboardData.items) { if (e.clipboardData && e.clipboardData.items) {
@ -345,7 +397,7 @@ function uploadImage($id, $callback) {
fileName += ".gif"; fileName += ".gif";
break; break;
default : default :
layer.msg("不支持的图片格式"); layer.msg(locales[lang].unsupportType);
return; return;
} }
var form = new FormData(); var form = new FormData();
@ -367,7 +419,7 @@ function uploadImage($id, $callback) {
error: function () { error: function () {
layer.close(layerIndex); layer.close(layerIndex);
$callback('error'); $callback('error');
layer.msg("图片上传失败"); layer.msg(locales[lang].uploadFailed);
}, },
success: function (data) { success: function (data) {
layer.close(layerIndex); layer.close(layerIndex);

View File

@ -1,21 +1,60 @@
$(function () { $(function () {
wangEditor.config.mapAk = window.baiduMapKey;
window.addDocumentModalFormHtml = $(this).find("form").html(); window.addDocumentModalFormHtml = $(this).find("form").html();
wangEditor.config.printLog = false; window.editor = new wangEditor('#htmlEditor');
window.editor = new wangEditor('htmlEditor'); editor.config.mapAk = window.baiduMapKey;
editor.config.printLog = false;
editor.config.showMenuTooltips = true
editor.config.menuTooltipPosition = 'down'
editor.config.uploadImgUrl = window.imageUploadURL; editor.config.uploadImgUrl = window.imageUploadURL;
editor.config.uploadImgFileName = "editormd-image-file"; editor.config.uploadImgFileName = "editormd-image-file";
editor.config.uploadParams = { editor.config.uploadParams = {
"editor" : "wangEditor" "editor" : "wangEditor"
}; };
wangEditor.config.menus.splice(0,0,"|"); editor.config.uploadImgServer = window.imageUploadURL;
wangEditor.config.menus.splice(0,0,"history"); editor.config.customUploadImg = function (resultFiles, insertImgFn) {
wangEditor.config.menus.splice(0,0,"save"); // resultFiles 是 input 中选中的文件列表
wangEditor.config.menus.splice(0,0,"release"); // insertImgFn 是获取图片 url 后,插入到编辑器的方法
wangEditor.config.menus.splice(29,0,"attach") var file = resultFiles[0];
// file type is only image.
if (/^image\//.test(file.type)) {
var form = new FormData();
form.append('editormd-image-file', file, file.name);
var layerIndex = 0;
$.ajax({
url: window.imageUploadURL,
type: "POST",
dataType: "json",
data: form,
processData: false,
contentType: false,
error: function() {
layer.close(layerIndex);
layer.msg("图片上传失败");
},
success: function(data) {
layer.close(layerIndex);
if(data.errcode !== 0){
layer.msg(data.message);
}else{
insertImgFn(data.url);
}
}
});
} else {
console.warn('You could only upload images.');
}
};
/*
editor.config.menus.splice(0,0,"|");
editor.config.menus.splice(0,0,"history");
editor.config.menus.splice(0,0,"save");
editor.config.menus.splice(0,0,"release");
editor.config.menus.splice(29,0,"attach")
//移除地图、背景色 //移除地图、背景色
editor.config.menus = $.map(wangEditor.config.menus, function(item, key) { editor.config.menus = $.map(editor.config.menus, function(item, key) {
if (item === 'fullscreen') { if (item === 'fullscreen') {
return null; return null;
@ -23,16 +62,8 @@ $(function () {
return item; return item;
}); });
*/
window.editor.ready(function () { /*
if(window.documentCategory.length > 0){
var item = window.documentCategory[0];
var $select_node = { node : {id : item.id}};
loadDocument($select_node);
}
});
window.editor.config.uploadImgFns.onload = function (resultText, xhr) { window.editor.config.uploadImgFns.onload = function (resultText, xhr) {
// resultText 服务器端返回的text // resultText 服务器端返回的text
// xhr 是 xmlHttpRequest 对象IE8、9中不支持 // xhr 是 xmlHttpRequest 对象IE8、9中不支持
@ -47,13 +78,15 @@ $(function () {
layer.msg(res.message); layer.msg(res.message);
} }
}; };
*/
window.editor.onchange = function () { window.editor.config.onchange = function (newHtml) {
var saveMenu = window.editor.menus.menuList.find((item) => item.key == 'save');
// 判断内容是否改变 // 判断内容是否改变
if (window.source !== this.$txt.html()) { if (window.source !== window.editor.txt.html()) {
window.editor.menus.save.$domNormal.addClass('selected'); saveMenu.$elem.addClass('selected');
} else { } else {
window.editor.menus.save.$domNormal.removeClass('selected'); saveMenu.$elem.removeClass('selected');
} }
}; };
@ -62,7 +95,11 @@ $(function () {
$("#htmlEditor").css("height","100%"); $("#htmlEditor").css("height","100%");
if(window.documentCategory.length > 0){
var item = window.documentCategory[0];
var $select_node = { node : {id : item.id}};
loadDocument($select_node);
}
/*** /***
* *
@ -78,8 +115,8 @@ $(function () {
if(res.errcode === 0){ if(res.errcode === 0){
window.isLoad = true; window.isLoad = true;
window.editor.clear(); window.editor.txt.clear();
window.editor.$txt.html(res.data.content); window.editor.txt.html(res.data.content);
// 将原始内容备份 // 将原始内容备份
window.source = res.data.content; window.source = res.data.content;
var node = { "id" : res.data.doc_id,'parent' : res.data.parent_id === 0 ? '#' : res.data.parent_id ,"text" : res.data.doc_name,"identify" : res.data.identify,"version" : res.data.version}; var node = { "id" : res.data.doc_id,'parent' : res.data.parent_id === 0 ? '#' : res.data.parent_id ,"text" : res.data.doc_name,"identify" : res.data.identify,"version" : res.data.version};
@ -105,7 +142,7 @@ $(function () {
var index = null; var index = null;
var node = window.selectNode; var node = window.selectNode;
var html = window.editor.$txt.html() ; var html = window.editor.txt.html() ;
var content = ""; var content = "";
if($.trim(html) !== ""){ if($.trim(html) !== ""){
@ -149,7 +186,7 @@ $(function () {
// 更新内容备份 // 更新内容备份
window.source = res.data.content; window.source = res.data.content;
// 触发编辑器 onchange 回调函数 // 触发编辑器 onchange 回调函数
window.editor.onchange(); window.editor.config.onchange();
if(typeof callback === "function"){ if(typeof callback === "function"){
callback(); callback();
} }
@ -267,7 +304,7 @@ $(function () {
}).on('loaded.jstree', function () { }).on('loaded.jstree', function () {
window.treeCatalog = $(this).jstree(); window.treeCatalog = $(this).jstree();
}).on('select_node.jstree', function (node, selected, event) { }).on('select_node.jstree', function (node, selected, event) {
if(window.editor.menus.save.$domNormal.hasClass('selected')) { if(window.editor.menus.menuList.find((item) => item.key == 'save').$elem.hasClass('selected')) {
if(confirm("编辑内容未保存,需要保存吗?")){ if(confirm("编辑内容未保存,需要保存吗?")){
saveDocument(false,function () { saveDocument(false,function () {
loadDocument(selected); loadDocument(selected);
@ -283,7 +320,7 @@ $(function () {
window.releaseBook = function () { window.releaseBook = function () {
if(Object.prototype.toString.call(window.documentCategory) === '[object Array]' && window.documentCategory.length > 0){ if(Object.prototype.toString.call(window.documentCategory) === '[object Array]' && window.documentCategory.length > 0){
if(window.editor.menus.save.$domNormal.hasClass('selected')) { if(window.editor.menus.menuList.find((item) => item.key == 'save').$elem.hasClass('selected')) {
if(confirm("编辑内容未保存,需要保存吗?")) { if(confirm("编辑内容未保存,需要保存吗?")) {
saveDocument(); saveDocument();
} }
@ -305,4 +342,16 @@ $(function () {
layer.msg("没有需要发布的文档") layer.msg("没有需要发布的文档")
} }
}; };
$(window).resize(function(e) {
var $container = $(editor.$textContainerElem.elems[0]);
var $manual = $container.closest('.manual-wangEditor');
var maxHeight = $manual.closest('.manual-editor-container').innerHeight();
var statusHeight = $manual.siblings('.manual-editor-status').outerHeight(true);
var manualHeihgt = maxHeight - statusHeight;
$manual.height(manualHeihgt);
var toolbarHeight = $container.siblings('.w-e-toolbar').outerHeight(true);
$container.height($container.parent().innerHeight() - toolbarHeight);
});
$(window).trigger('resize');
}); });

View File

@ -4,12 +4,66 @@ $(function () {
css : window.katex.css css : window.katex.css
}; };
window.editormdLocales = {
'zh-CN': {
placeholder: ' Markdown ',
contentUnsaved: '',
noDocNeedPublish: '',
loadDocFailed: '',
fetchDocFailed: '',
cannotAddToEmptyNode: '',
overrideModified: '',
confirm: '',
cancel: '',
contentsNameEmpty: '',
addDoc: '',
edit: '',
delete: '',
loadFailed: '',
tplNameEmpty: '',
tplContentEmpty: '',
saveSucc: '',
serverExcept: '',
paramName: '',
paramType: '',
example: '',
remark: '',
},
'en': {
placeholder: 'This editor supports Markdown editing, writing on the left and previewing on the right.',
contentUnsaved: 'The edited content is not saved, need to save it?',
noDocNeedPublish: 'No Document need to be publish',
loadDocFailed: 'Load Document failed',
fetchDocFailed: 'Fetch Document info failed',
cannotAddToEmptyNode: 'Cannot add content to empty node',
overrideModified: 'The document has been modified by someone else, are you sure to overwrite the document?',
confirm: 'Confirm',
cancel: 'Cancel',
contentsNameEmpty: 'Document Name cannot be empty',
addDoc: 'Add Document',
edit: 'Edit',
delete: 'Delete',
loadFailed: 'Failed to load, please try again',
tplNameEmpty: 'Template name cannot be empty',
tplContentEmpty: 'Template content cannot be empty',
saveSucc: 'Save success',
serverExcept: 'Server Exception',
paramName: 'Parameter',
paramType: 'Type',
example: 'Example',
remark: 'Remark',
}
};
var htmlDecodeList = ["style","script","title","onmouseover","onmouseout","style"];
if (!window.IS_ENABLE_IFRAME) {
htmlDecodeList.unshift("iframe");
}
window.editor = editormd("docEditor", { window.editor = editormd("docEditor", {
width: "100%", width: "100%",
height: "100%", height: "100%",
path: window.editormdLib, path: window.editormdLib,
toolbar: true, toolbar: true,
placeholder: "本编辑器支持 Markdown 编辑,左边编写,右边预览。", placeholder: window.editormdLocales[window.lang].placeholder,
imageUpload: true, imageUpload: true,
imageFormats: ["jpg", "jpeg", "gif", "png","svg", "JPG", "JPEG", "GIF", "PNG","SVG"], imageFormats: ["jpg", "jpeg", "gif", "png","svg", "JPG", "JPEG", "GIF", "PNG","SVG"],
imageUploadURL: window.imageUploadURL, imageUploadURL: window.imageUploadURL,
@ -18,8 +72,8 @@ $(function () {
fileUploadURL: window.fileUploadURL, fileUploadURL: window.fileUploadURL,
taskList: true, taskList: true,
flowChart: true, flowChart: true,
htmlDecode: "style,script,iframe,title,onmouseover,onmouseout,style", htmlDecode: htmlDecodeList.join(','),
lineNumbers: false, lineNumbers: true,
sequenceDiagram: true, sequenceDiagram: true,
tocStartLevel: 1, tocStartLevel: 1,
tocm: true, tocm: true,
@ -57,6 +111,7 @@ $(function () {
} }
} }
}); });
window.isLoad = true; window.isLoad = true;
}, },
onchange: function () { onchange: function () {
@ -110,7 +165,7 @@ $(function () {
} else if (name === "release") { } else if (name === "release") {
if (Object.prototype.toString.call(window.documentCategory) === '[object Array]' && window.documentCategory.length > 0) { if (Object.prototype.toString.call(window.documentCategory) === '[object Array]' && window.documentCategory.length > 0) {
if ($("#markdown-save").hasClass('change')) { if ($("#markdown-save").hasClass('change')) {
var confirm_result = confirm("编辑内容未保存,需要保存吗?"); var confirm_result = confirm(editormdLocales[lang].contentUnsaved);
if (confirm_result) { if (confirm_result) {
saveDocument(false, releaseBook); saveDocument(false, releaseBook);
return; return;
@ -119,7 +174,7 @@ $(function () {
releaseBook(); releaseBook();
} else { } else {
layer.msg("没有需要发布的文档") layer.msg(editormdLocales[lang].noDocNeedPublish)
} }
} else if (name === "tasks") { } else if (name === "tasks") {
// 插入 GFM 任务列表 // 插入 GFM 任务列表
@ -141,7 +196,7 @@ $(function () {
} else { } else {
var action = window.editor.toolbarHandlers[name]; var action = window.editor.toolbarHandlers[name];
if (action !== "undefined") { if (!!action && action !== "undefined") {
$.proxy(action, window.editor)(); $.proxy(action, window.editor)();
window.editor.focus(); window.editor.focus();
} }
@ -175,11 +230,11 @@ $(function () {
pushVueLists(res.data.attach); pushVueLists(res.data.attach);
setLastSelectNode($node); setLastSelectNode($node);
} else { } else {
layer.msg("文档加载失败"); layer.msg(editormdLocales[lang].loadDocFailed);
} }
}).fail(function () { }).fail(function () {
layer.close(index); layer.close(index);
layer.msg("文档加载失败"); layer.msg(editormdLocales[lang].loadDocFailed);
}); });
}; };
@ -195,11 +250,11 @@ $(function () {
var version = ""; var version = "";
if (!node) { if (!node) {
layer.msg("获取当前文档信息失败"); layer.msg(editormdLocales[lang].fetchDocFailed);
return; return;
} }
if (node.a_attr && node.a_attr.disabled) { if (node.a_attr && node.a_attr.disabled) {
layer.msg("空节点不能添加内容"); layer.msg(editormdLocales[lang].cannotAddToEmptyNode);
return; return;
} }
@ -246,8 +301,8 @@ $(function () {
} }
} else if(res.errcode === 6005) { } else if(res.errcode === 6005) {
var confirmIndex = layer.confirm('', { var confirmIndex = layer.confirm(editormdLocales[lang].overrideModified, {
btn: ['', ''] // 按钮 btn: [editormdLocales[lang].confirm, editormdLocales[lang].cancel] // 按钮
}, function() { }, function() {
layer.close(confirmIndex); layer.close(confirmIndex);
saveDocument(true, callback); saveDocument(true, callback);
@ -257,7 +312,7 @@ $(function () {
} }
}, },
error : function (XMLHttpRequest, textStatus, errorThrown) { error : function (XMLHttpRequest, textStatus, errorThrown) {
layer.msg("服务器错误:" + errorThrown); layer.msg(window.editormdLocales[window.lang].serverExcept + errorThrown);
}, },
complete :function () { complete :function () {
layer.close(index); layer.close(index);
@ -287,7 +342,7 @@ $(function () {
beforeSubmit: function () { beforeSubmit: function () {
var doc_name = $.trim($("#documentName").val()); var doc_name = $.trim($("#documentName").val());
if (doc_name === "") { if (doc_name === "") {
return showError("目录名称不能为空", "#add-error-message") return showError(editormdLocales[lang].contentsNameEmpty, "#add-error-message")
} }
$("#btnSaveDocument").button("loading"); $("#btnSaveDocument").button("loading");
return true; return true;
@ -347,7 +402,7 @@ $(function () {
"separator_before": false, "separator_before": false,
"separator_after": true, "separator_after": true,
"_disabled": false, "_disabled": false,
"label": "添加文档", "label": window.editormdLocales[window.lang].addDoc,//"添加文档",
"icon": "fa fa-plus", "icon": "fa fa-plus",
"action": function (data) { "action": function (data) {
var inst = $.jstree.reference(data.reference), var inst = $.jstree.reference(data.reference),
@ -360,7 +415,7 @@ $(function () {
"separator_before": false, "separator_before": false,
"separator_after": true, "separator_after": true,
"_disabled": false, "_disabled": false,
"label": "编辑", "label": window.editormdLocales[window.lang].edit,
"icon": "fa fa-edit", "icon": "fa fa-edit",
"action": function (data) { "action": function (data) {
var inst = $.jstree.reference(data.reference); var inst = $.jstree.reference(data.reference);
@ -372,7 +427,7 @@ $(function () {
"separator_before": false, "separator_before": false,
"separator_after": true, "separator_after": true,
"_disabled": false, "_disabled": false,
"label": "删除", "label": window.editormdLocales[window.lang].delete,
"icon": "fa fa-trash-o", "icon": "fa fa-trash-o",
"action": function (data) { "action": function (data) {
var inst = $.jstree.reference(data.reference); var inst = $.jstree.reference(data.reference);
@ -390,7 +445,7 @@ $(function () {
}).on('select_node.jstree', function (node, selected) { }).on('select_node.jstree', function (node, selected) {
if ($("#markdown-save").hasClass('change')) { if ($("#markdown-save").hasClass('change')) {
if (confirm("编辑内容未保存,需要保存吗?")) { if (confirm(window.editormdLocales[window.lang].contentUnsaved)) {
saveDocument(false, function () { saveDocument(false, function () {
loadDocument(selected); loadDocument(selected);
}); });
@ -446,7 +501,7 @@ $(function () {
$("#displayCustomsTemplateList").html($res); $("#displayCustomsTemplateList").html($res);
}, },
error : function () { error : function () {
layer.msg("加载失败请重试"); layer.msg(window.editormdLocales[window.lang].loadFailed);
}, },
complete : function () { complete : function () {
layer.close(index); layer.close(index);
@ -464,11 +519,11 @@ $(function () {
beforeSubmit: function () { beforeSubmit: function () {
var doc_name = $.trim($("#templateName").val()); var doc_name = $.trim($("#templateName").val());
if (doc_name === "") { if (doc_name === "") {
return showError("模板名称不能为空", "#saveTemplateForm .show-error-message"); return showError(window.editormdLocales[window.lang].tplNameEmpty, "#saveTemplateForm .show-error-message");
} }
var content = $("#saveTemplateForm").find("input[name='content']").val(); var content = $("#saveTemplateForm").find("input[name='content']").val();
if (content === ""){ if (content === ""){
return showError("模板内容不能为空", "#saveTemplateForm .show-error-message"); return showError(window.editormdLocales[window.lang].tplContentEmpty, "#saveTemplateForm .show-error-message");
} }
$("#btnSaveTemplate").button("loading"); $("#btnSaveTemplate").button("loading");
@ -478,7 +533,7 @@ $(function () {
success: function ($res) { success: function ($res) {
if($res.errcode === 0){ if($res.errcode === 0){
$("#saveTemplateModal").modal("hide"); $("#saveTemplateModal").modal("hide");
layer.msg("保存成功"); layer.msg(window.editormdLocales[window.lang].saveSucc);
}else{ }else{
return showError($res.message, "#saveTemplateForm .show-error-message"); return showError($res.message, "#saveTemplateForm .show-error-message");
} }
@ -521,7 +576,7 @@ $(function () {
resetEditorChanged(true); resetEditorChanged(true);
$("#displayCustomsTemplateModal").modal("hide"); $("#displayCustomsTemplateModal").modal("hide");
},error : function () { },error : function () {
layer.msg("服务器异常"); layer.msg(window.editormdLocales[window.lang].serverExcept);
} }
}); });
}).on("click",".btn-delete",function () { }).on("click",".btn-delete",function () {
@ -541,7 +596,7 @@ $(function () {
$then.parents("tr").empty().remove(); $then.parents("tr").empty().remove();
} }
},error : function () { },error : function () {
layer.msg("服务器异常"); layer.msg(window.editormdLocales[window.lang].serverExcept);
}, },
complete: function () { complete: function () {
$then.button("reset"); $then.button("reset");
@ -555,7 +610,11 @@ $(function () {
try { try {
var jsonObj = $.parseJSON(content); var jsonObj = $.parseJSON(content);
var data = foreachJson(jsonObj,""); var data = foreachJson(jsonObj,"");
var table = "| 参数名称 | 参数类型 | 示例值 | 备注 |\n| ------------ | ------------ | ------------ | ------------ |\n"; var table = "| " + window.editormdLocales[window.lang].paramName
+ " | " + window.editormdLocales[window.lang].paramType
+ " | " + window.editormdLocales[window.lang].example
+ " | " + window.editormdLocales[window.lang].remark
+ " |\n| ------------ | ------------ | ------------ | ------------ |\n";
$.each(data,function (i,item) { $.each(data,function (i,item) {
table += "|" + item.key + "|" + item.type + "|" + item.value +"| |\n"; table += "|" + item.key + "|" + item.type + "|" + item.value +"| |\n";
}); });

View File

@ -0,0 +1,42 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
let AttachMenu = (function (_BtnMenu) {
c2b_inherits(AttachMenu, _BtnMenu);
var _super = c2b_createSuper(AttachMenu);
function AttachMenu(editor) {
c2b_classCallCheck(this, AttachMenu);
const $elem = E.$(`<div class="w-e-menu" data-title="附件">
<i class="fa fa-paperclip" aria-hidden="true"></i>
</div>`);
return _super.call(this, $elem, editor);
}
c2b_createClass(AttachMenu, [
{
key: "clickHandler",
value: function clickHandler() {
$("#uploadAttachModal").modal("show");
}
},
{
key: "tryChangeActive",
value: function tryChangeActive() {
// this.active();
}
}
]);
return AttachMenu;
})(E.BtnMenu);
var menuKey = 'attach';
E.registerMenu(menuKey, AttachMenu);
})();

View File

@ -0,0 +1,42 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
let HistoryMenu = (function (_BtnMenu) {
c2b_inherits(HistoryMenu, _BtnMenu);
var _super = c2b_createSuper(HistoryMenu);
function HistoryMenu(editor) {
c2b_classCallCheck(this, HistoryMenu);
const $elem = E.$(`<div class="w-e-menu" data-title="history">
<i class="fa fa-history" aria-hidden="true"></i>
</div>`);
return _super.call(this, $elem, editor);
}
c2b_createClass(HistoryMenu, [
{
key: "clickHandler",
value: function clickHandler() {
window.documentHistory();
}
},
{
key: "tryChangeActive",
value: function tryChangeActive() {
// this.active();
}
}
]);
return HistoryMenu;
})(E.BtnMenu);
var menuKey = '';
E.registerMenu(menuKey, HistoryMenu);
})();

View File

@ -0,0 +1,42 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
let ReleaseMenu = (function (_BtnMenu) {
c2b_inherits(ReleaseMenu, _BtnMenu);
var _super = c2b_createSuper(ReleaseMenu);
function ReleaseMenu(editor) {
c2b_classCallCheck(this, ReleaseMenu);
const $elem = E.$(`<div class="w-e-menu" data-title="发布">
<i class="fa fa-cloud-upload" aria-hidden="true"></i>
</div>`);
return _super.call(this, $elem, editor);
}
c2b_createClass(ReleaseMenu, [
{
key: "clickHandler",
value: function clickHandler() {
window.releaseBook();
}
},
{
key: "tryChangeActive",
value: function tryChangeActive() {
// this.active();
}
}
]);
return ReleaseMenu;
})(E.BtnMenu);
var menuKey = 'release';
E.registerMenu(menuKey, ReleaseMenu);
})();

View File

@ -0,0 +1,42 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
let SaveMenu = (function (_BtnMenu) {
c2b_inherits(SaveMenu, _BtnMenu);
var _super = c2b_createSuper(SaveMenu);
function SaveMenu(editor) {
c2b_classCallCheck(this, SaveMenu);
const $elem = E.$(`<div class="w-e-menu" data-title="保存">
<i class="fa fa-floppy-o" aria-hidden="true"></i>
</div>`);
return _super.call(this, $elem, editor);
}
c2b_createClass(SaveMenu, [
{
key: "clickHandler",
value: function clickHandler() {
window.saveDocument();
}
},
{
key: "tryChangeActive",
value: function tryChangeActive() {
// this.active();
}
}
]);
return SaveMenu;
})(E.BtnMenu);
var menuKey = 'save';
E.registerMenu(menuKey, SaveMenu);
})();

View File

@ -0,0 +1,87 @@
customElements.define('x-frame-bypass', class extends HTMLIFrameElement {
static get observedAttributes() {
return ['src']
}
constructor () {
super()
}
attributeChangedCallback () {
this.load(this.src)
}
connectedCallback () {
this.sandbox = '' + this.sandbox || 'allow-forms allow-modals allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts allow-top-navigation-by-user-activation' // all except allow-top-navigation
}
load (url, options) {
if (!url || !url.startsWith('http'))
throw new Error(`X-Frame-Bypass src ${url} does not start with http(s)://`)
console.log('X-Frame-Bypass loading:', url)
this.srcdoc = `<html>
<head>
<style>
.loader {
position: absolute;
top: calc(50% - 25px);
left: calc(50% - 25px);
width: 50px;
height: 50px;
background-color: #333;
border-radius: 50%;
animation: loader 1s infinite ease-in-out;
}
@keyframes loader {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
opacity: 0;
}
}
</style>
</head>
<body>
<div class="loader"></div>
</body>
</html>`
this.fetchProxy(url, options, 0).then(res => res.text()).then(data => {
if (data)
this.srcdoc = data.replace(/<head([^>]*)>/i, `<head$1>
<base href="${url}">
<script>
// X-Frame-Bypass navigation event handlers
document.addEventListener('click', e => {
if (frameElement && document.activeElement && document.activeElement.href) {
e.preventDefault()
frameElement.load(document.activeElement.href)
}
})
document.addEventListener('submit', e => {
if (frameElement && document.activeElement && document.activeElement.form && document.activeElement.form.action) {
e.preventDefault()
if (document.activeElement.form.method === 'post')
frameElement.load(document.activeElement.form.action, {method: 'post', body: new FormData(document.activeElement.form)})
else
frameElement.load(document.activeElement.form.action + '?' + new URLSearchParams(new FormData(document.activeElement.form)))
}
})
</script>`)
}).catch(e => console.error('Cannot load X-Frame-Bypass:', e))
}
fetchProxy (url, options, i) {
const proxies = (options || {}).proxies || [
window.BASE_URL + 'cors-anywhere?url=',
'https://cors-anywhere.herokuapp.com/',
'https://yacdn.org/proxy/',
'https://api.codetabs.com/v1/proxy/?quest='
]
return fetch(proxies[i] + url, options).then(res => {
if (!res.ok)
throw new Error(`${res.status} ${res.statusText}`);
return res
}).catch(error => {
if (i === proxies.length - 1)
throw error
return this.fetchProxy(url, options, i + 1)
})
}
}, {extends: 'iframe'})

View File

@ -1,831 +0,0 @@
/* 编辑器边框颜色 */
/* 菜单颜色、上边框颜色 */
/* 菜单选中状态的颜色 */
/* input focus 时的颜色 */
/* 按钮颜色 */
/* tab selected 状态下的颜色 */
.wangEditor-container {
position: relative;
background-color: #fff;
border: 1px solid #ccc;
z-index: 1;
width: 100%;
}
.wangEditor-container a:focus,
.wangEditor-container button:focus {
outline: none;
}
.wangEditor-container,
.wangEditor-container * {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1;
}
.wangEditor-container img {
border: none;
}
.wangEditor-container .clearfix:after {
content: '';
display: table;
clear: both;
}
.wangEditor-container .clearfix {
*zoom: 1;
}
.wangEditor-container textarea {
border: none;
}
.wangEditor-container textarea:focus {
outline: none;
}
.wangEditor-container .height-tip {
position: absolute;
width: 3px;
background-color: #ccc;
left: 0;
transition: top .2s;
}
.wangEditor-container .txt-toolbar {
position: absolute;
background-color: #fff;
padding: 3px 5px;
border-top: 2px solid #666;
box-shadow: 1px 3px 3px #999;
border-left: 1px\9 solid\9 #ccc\9;
border-bottom: 1px\9 solid\9 #999\9;
border-right: 1px\9 solid\9 #999\9;
}
.wangEditor-container .txt-toolbar .tip-triangle {
display: block;
position: absolute;
width: 0;
height: 0;
border: 5px solid;
border-color: transparent transparent #666 transparent;
top: -12px;
left: 50%;
margin-left: -5px;
}
.wangEditor-container .txt-toolbar a {
color: #666;
display: inline-block;
margin: 0 3px;
padding: 5px;
text-decoration: none;
border-radius: 3px;
}
.wangEditor-container .txt-toolbar a:hover {
background-color: #f1f1f1;
}
.wangEditor-container .img-drag-point {
display: block;
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
cursor: se-resize;
background-color: #666;
margin-left: -6px;
margin-top: -6px;
box-shadow: 1px 1px 5px #999;
}
.wangEditor-container .wangEditor-upload-progress {
position: absolute;
height: 1px;
background: #1e88e5;
width: 0;
display: none;
-webkit-transition: width .5s;
-o-transition: width .5s;
transition: width .5s;
}
.wangEditor-fullscreen {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.wangEditor-container .code-textarea {
resize: none;
width: 100%;
font-size: 14px;
line-height: 1.5;
font-family: 'Verdana';
color: #333;
padding: 0 15px 0 15px;
}
.wangEditor-menu-container {
width: 100%;
border-bottom: 1px solid #f1f1f1;
background-color: #fff;
}
.wangEditor-menu-container a {
text-decoration: none;
}
.wangEditor-menu-container .menu-group {
float: left;
padding: 0 8px;
border-right: 1px solid #f1f1f1;
}
.wangEditor-menu-container .menu-item {
float: left;
position: relative;
text-align: center;
height: 31px;
width: 35px;
}
.wangEditor-menu-container .menu-item:hover {
background-color: #f1f1f1;
}
.wangEditor-menu-container .menu-item a {
display: block;
text-align: center;
color: #666;
width: 100%;
padding: 8px 0;
font-size: 0.9em;
}
.wangEditor-menu-container .menu-item .selected {
color: #1e88e5;
}
.wangEditor-menu-container .menu-item .active {
background-color: #f1f1f1;
}
.wangEditor-menu-container .menu-item .disable {
opacity: 0.5;
filter: alpha(opacity=50);
}
.wangEditor-menu-container .menu-tip {
display: block;
position: absolute;
z-index: 20;
width: 60px;
text-align: center;
background-color: #666;
color: #fff;
padding: 7px 0;
font-size: 12px;
top: 100%;
left: 50%;
margin-left: -30px;
border-radius: 2px;
box-shadow: 1px 1px 5px #999;
display: none;
/*// 小三角
.tip-triangle {
display: block;
position: absolute;
width: 0;
height: 0;
border:5px solid;
border-color: transparent transparent @fore-color transparent;
top: -10px;
left: 50%;
margin-left: -5px;
}*/
}
.wangEditor-menu-container .menu-tip-40 {
width: 40px;
margin-left: -20px;
}
.wangEditor-menu-container .menu-tip-50 {
width: 50px;
margin-left: -25px;
}
.wangEditor-menu-shadow {
/*border-bottom-width: 0;*/
border-bottom: 1px\9 solid\9 #f1f1f1\9;
box-shadow: 0 1px 3px #999;
}
.wangEditor-container .wangEditor-txt {
width: 100%;
text-align: left;
padding: 15px;
padding-top: 0;
margin-top: 5px;
overflow-y: auto;
}
.wangEditor-container .wangEditor-txt p,
.wangEditor-container .wangEditor-txt h1,
.wangEditor-container .wangEditor-txt h2,
.wangEditor-container .wangEditor-txt h3,
.wangEditor-container .wangEditor-txt h4,
.wangEditor-container .wangEditor-txt h5 {
margin: 10px 0;
line-height: 1.8;
}
.wangEditor-container .wangEditor-txt p *,
.wangEditor-container .wangEditor-txt h1 *,
.wangEditor-container .wangEditor-txt h2 *,
.wangEditor-container .wangEditor-txt h3 *,
.wangEditor-container .wangEditor-txt h4 *,
.wangEditor-container .wangEditor-txt h5 * {
line-height: 1.8;
}
.wangEditor-container .wangEditor-txt ul,
.wangEditor-container .wangEditor-txt ol {
padding-left: 20px;
}
.wangEditor-container .wangEditor-txt img {
cursor: pointer;
}
.wangEditor-container .wangEditor-txt img.clicked {
box-shadow: 1px 1px 10px #999;
}
.wangEditor-container .wangEditor-txt table.clicked {
box-shadow: 1px 1px 10px #999;
}
.wangEditor-container .wangEditor-txt pre code {
line-height: 1.5;
}
.wangEditor-container .wangEditor-txt:focus {
outline: none;
}
.wangEditor-container .wangEditor-txt blockquote {
display: block;
border-left: 8px solid #d0e5f2;
padding: 5px 10px;
margin: 10px 0;
line-height: 1.4;
font-size: 100%;
background-color: #f1f1f1;
}
.wangEditor-container .wangEditor-txt table {
border: none;
border-collapse: collapse;
}
.wangEditor-container .wangEditor-txt table td,
.wangEditor-container .wangEditor-txt table th {
border: 1px solid #999;
padding: 3px 5px;
min-width: 50px;
height: 20px;
}
.wangEditor-container .wangEditor-txt pre {
border: 1px solid #ccc;
background-color: #f8f8f8;
padding: 10px;
margin: 5px 0px;
font-size: 0.8em;
border-radius: 3px;
}
.wangEditor-drop-list {
display: none;
position: absolute;
background-color: #fff;
overflow: hidden;
z-index: 10;
transition: height .7s;
border-top: 1px solid #f1f1f1;
box-shadow: 1px 3px 3px #999;
border-left: 1px\9 solid\9 #ccc\9;
border-bottom: 1px\9 solid\9 #999\9;
border-right: 1px\9 solid\9 #999\9;
}
.wangEditor-drop-list a {
text-decoration: none;
display: block;
color: #666;
padding: 3px 5px;
}
.wangEditor-drop-list a:hover {
background-color: #f1f1f1;
}
.wangEditor-drop-panel,
.txt-toolbar {
display: none;
position: absolute;
padding: 10px;
font-size: 14px;
/*border: 1px\9 solid\9 #cccccc\9;*/
background-color: #fff;
z-index: 10;
border-top: 2px solid #666;
box-shadow: 1px 3px 3px #999;
border-left: 1px\9 solid\9 #ccc\9;
border-bottom: 1px\9 solid\9 #999\9;
border-right: 1px\9 solid\9 #999\9;
}
.wangEditor-drop-panel .tip-triangle,
.txt-toolbar .tip-triangle {
display: block;
position: absolute;
width: 0;
height: 0;
border: 5px solid;
border-color: transparent transparent #666 transparent;
top: -12px;
left: 50%;
margin-left: -5px;
}
.wangEditor-drop-panel a,
.txt-toolbar a {
text-decoration: none;
}
.wangEditor-drop-panel input[type=text],
.txt-toolbar input[type=text] {
border: none;
border-bottom: 1px solid #ccc;
font-size: 14px;
height: 20px;
color: #333;
padding: 3px 0;
}
.wangEditor-drop-panel input[type=text]:focus,
.txt-toolbar input[type=text]:focus {
outline: none;
border-bottom: 2px solid #1e88e5;
}
.wangEditor-drop-panel input[type=text].block,
.txt-toolbar input[type=text].block {
display: block;
width: 100%;
}
.wangEditor-drop-panel textarea,
.txt-toolbar textarea {
border: 1px solid #ccc;
}
.wangEditor-drop-panel textarea:focus,
.txt-toolbar textarea:focus {
outline: none;
border-color: #1e88e5;
}
.wangEditor-drop-panel button,
.txt-toolbar button {
font-size: 14px;
color: #1e88e5;
border: none;
padding: 10px;
background-color: #fff;
cursor: pointer;
border-radius: 3px;
}
.wangEditor-drop-panel button:hover,
.txt-toolbar button:hover {
background-color: #f1f1f1;
}
.wangEditor-drop-panel button:focus,
.txt-toolbar button:focus {
outline: none;
}
.wangEditor-drop-panel button.right,
.txt-toolbar button.right {
float: right;
margin-left: 10px;
}
.wangEditor-drop-panel button.gray,
.txt-toolbar button.gray {
color: #999;
}
.wangEditor-drop-panel button.link,
.txt-toolbar button.link {
padding: 5px 10px;
}
.wangEditor-drop-panel button.link:hover,
.txt-toolbar button.link:hover {
background-color: #fff;
text-decoration: underline;
}
.wangEditor-drop-panel .color-item,
.txt-toolbar .color-item {
display: block;
float: left;
width: 25px;
height: 25px;
text-align: center;
padding: 2px;
border-radius: 2px;
text-decoration: underline;
}
.wangEditor-drop-panel .color-item:hover,
.txt-toolbar .color-item:hover {
background-color: #f1f1f1;
}
.wangEditor-drop-panel .list-menu-item,
.txt-toolbar .list-menu-item {
display: block;
float: left;
color: #333;
padding: 5px 5px;
border-radius: 2px;
}
.wangEditor-drop-panel .list-menu-item:hover,
.txt-toolbar .list-menu-item:hover {
background-color: #f1f1f1;
}
.wangEditor-drop-panel table.choose-table,
.txt-toolbar table.choose-table {
border: none;
border-collapse: collapse;
}
.wangEditor-drop-panel table.choose-table td,
.txt-toolbar table.choose-table td {
border: 1px solid #ccc;
width: 16px;
height: 12px;
}
.wangEditor-drop-panel table.choose-table td.active,
.txt-toolbar table.choose-table td.active {
background-color: #ccc;
opacity: .5;
filter: alpha(opacity=50);
}
.wangEditor-drop-panel .panel-tab .tab-container,
.txt-toolbar .panel-tab .tab-container {
margin-bottom: 5px;
}
.wangEditor-drop-panel .panel-tab .tab-container a,
.txt-toolbar .panel-tab .tab-container a {
display: inline-block;
color: #999;
text-align: center;
margin: 0 5px;
padding: 5px 5px;
}
.wangEditor-drop-panel .panel-tab .tab-container a.selected,
.txt-toolbar .panel-tab .tab-container a.selected {
color: #1e88e5;
border-bottom: 2px solid #1e88e5;
}
.wangEditor-drop-panel .panel-tab .content-container .content,
.txt-toolbar .panel-tab .content-container .content {
display: none;
}
.wangEditor-drop-panel .panel-tab .content-container .content a,
.txt-toolbar .panel-tab .content-container .content a {
display: inline-block;
margin: 2px;
padding: 2px;
border-radius: 2px;
}
.wangEditor-drop-panel .panel-tab .content-container .content a:hover,
.txt-toolbar .panel-tab .content-container .content a:hover {
background-color: #f1f1f1;
}
.wangEditor-drop-panel .panel-tab .content-container .selected,
.txt-toolbar .panel-tab .content-container .selected {
display: block;
}
.wangEditor-drop-panel .panel-tab .emotion-content-container,
.txt-toolbar .panel-tab .emotion-content-container {
height: 200px;
overflow-y: auto;
}
.wangEditor-drop-panel .upload-icon-container,
.txt-toolbar .upload-icon-container {
color: #ccc;
text-align: center;
margin: 20px 20px 15px 20px !important;
padding: 5px !important;
font-size: 65px;
cursor: pointer;
border: 2px dotted #f1f1f1;
display: block !important;
}
.wangEditor-drop-panel .upload-icon-container:hover,
.txt-toolbar .upload-icon-container:hover {
color: #666;
border-color: #ccc;
}
.wangEditor-modal {
position: absolute;
top: 50%;
left: 50%;
background-color: #fff;
border-top: 1px solid #f1f1f1;
box-shadow: 1px 3px 3px #999;
border-top: 1px\9 solid\9 #ccc\9;
border-left: 1px\9 solid\9 #ccc\9;
border-bottom: 1px\9 solid\9 #999\9;
border-right: 1px\9 solid\9 #999\9;
}
.wangEditor-modal .wangEditor-modal-close {
position: absolute;
top: 0;
right: 0;
margin-top: -25px;
margin-right: -25px;
font-size: 1.5em;
color: #666;
cursor: pointer;
}
@font-face {
font-family: 'icomoon';
src: url('../fonts/icomoon.eot?-qdfu1s');
src: url('../fonts/icomoon.eot?#iefix-qdfu1s') format('embedded-opentype'), url('../fonts/icomoon.ttf?-qdfu1s') format('truetype'), url('../fonts/icomoon.woff?-qdfu1s') format('woff'), url('../fonts/icomoon.svg?-qdfu1s#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="wangeditor-menu-img-"],
[class*=" wangeditor-menu-img-"] {
font-family: 'icomoon';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.wangeditor-menu-img-link:before {
content: "\e800";
}
.wangeditor-menu-img-unlink:before {
content: "\e801";
}
.wangeditor-menu-img-code:before {
content: "\e802";
}
.wangeditor-menu-img-cancel:before {
content: "\e803";
}
.wangeditor-menu-img-terminal:before {
content: "\e804";
}
.wangeditor-menu-img-angle-down:before {
content: "\e805";
}
.wangeditor-menu-img-font:before {
content: "\e806";
}
.wangeditor-menu-img-bold:before {
content: "\e807";
}
.wangeditor-menu-img-italic:before {
content: "\e808";
}
.wangeditor-menu-img-header:before {
content: "\e809";
}
.wangeditor-menu-img-align-left:before {
content: "\e80a";
}
.wangeditor-menu-img-align-center:before {
content: "\e80b";
}
.wangeditor-menu-img-align-right:before {
content: "\e80c";
}
.wangeditor-menu-img-list-bullet:before {
content: "\e80d";
}
.wangeditor-menu-img-indent-left:before {
content: "\e80e";
}
.wangeditor-menu-img-indent-right:before {
content: "\e80f";
}
.wangeditor-menu-img-list-numbered:before {
content: "\e810";
}
.wangeditor-menu-img-underline:before {
content: "\e811";
}
.wangeditor-menu-img-table:before {
content: "\e812";
}
.wangeditor-menu-img-eraser:before {
content: "\e813";
}
.wangeditor-menu-img-text-height:before {
content: "\e814";
}
.wangeditor-menu-img-brush:before {
content: "\e815";
}
.wangeditor-menu-img-pencil:before {
content: "\e816";
}
.wangeditor-menu-img-minus:before {
content: "\e817";
}
.wangeditor-menu-img-picture:before {
content: "\e818";
}
.wangeditor-menu-img-file-image:before {
content: "\e819";
}
.wangeditor-menu-img-cw:before {
content: "\e81a";
}
.wangeditor-menu-img-ccw:before {
content: "\e81b";
}
.wangeditor-menu-img-music:before {
content: "\e911";
}
.wangeditor-menu-img-play:before {
content: "\e912";
}
.wangeditor-menu-img-location:before {
content: "\e947";
}
.wangeditor-menu-img-happy:before {
content: "\e9df";
}
.wangeditor-menu-img-sigma:before {
content: "\ea67";
}
.wangeditor-menu-img-enlarge2:before {
content: "\e98b";
}
.wangeditor-menu-img-shrink2:before {
content: "\e98c";
}
.wangeditor-menu-img-newspaper:before {
content: "\e904";
}
.wangeditor-menu-img-camera:before {
content: "\e90f";
}
.wangeditor-menu-img-video-camera:before {
content: "\e914";
}
.wangeditor-menu-img-file-zip:before {
content: "\e92b";
}
.wangeditor-menu-img-stack:before {
content: "\e92e";
}
.wangeditor-menu-img-credit-card:before {
content: "\e93f";
}
.wangeditor-menu-img-address-book:before {
content: "\e944";
}
.wangeditor-menu-img-envelop:before {
content: "\e945";
}
.wangeditor-menu-img-drawer:before {
content: "\e95c";
}
.wangeditor-menu-img-download:before {
content: "\e960";
}
.wangeditor-menu-img-upload:before {
content: "\e961";
}
.wangeditor-menu-img-lock:before {
content: "\e98f";
}
.wangeditor-menu-img-unlocked:before {
content: "\e990";
}
.wangeditor-menu-img-wrench:before {
content: "\e991";
}
.wangeditor-menu-img-eye:before {
content: "\e9ce";
}
.wangeditor-menu-img-eye-blocked:before {
content: "\e9d1";
}
.wangeditor-menu-img-command:before {
content: "\ea4e";
}
.wangeditor-menu-img-font2:before {
content: "\ea5c";
}
.wangeditor-menu-img-libreoffice:before {
content: "\eade";
}
.wangeditor-menu-img-quotes-left:before {
content: "\e977";
}
.wangeditor-menu-img-strikethrough:before {
content: "\ea65";
}
.wangeditor-menu-img-desktop:before {
content: "\f108";
}
.wangeditor-menu-img-tablet:before {
content: "\f10a";
}
.wangeditor-menu-img-search-plus:before {
content: "\f00e";
}
.wangeditor-menu-img-search-minus:before {
content: "\f010";
}
.wangeditor-menu-img-trash-o:before {
content: "\f014";
}
.wangeditor-menu-img-align-justify:before {
content: "\f039";
}
.wangeditor-menu-img-arrows-v:before {
content: "\f07d";
}
.wangeditor-menu-img-sigma2:before {
content: "\ea68";
}
.wangeditor-menu-img-omega:before {
content: "\e900";
}
.wangeditor-menu-img-cancel-circle:before {
content: "\e901";
}
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
-webkit-text-size-adjust: none;
}
.hljs-comment,
.diff .hljs-header {
color: #998;
font-style: italic;
}
.hljs-keyword,
.css .rule .hljs-keyword,
.hljs-winutils,
.nginx .hljs-title,
.hljs-subst,
.hljs-request,
.hljs-status {
color: #333;
font-weight: bold;
}
.hljs-number,
.hljs-hexcolor,
.ruby .hljs-constant {
color: #008080;
}
.hljs-string,
.hljs-tag .hljs-value,
.hljs-doctag,
.tex .hljs-formula {
color: #d14;
}
.hljs-title,
.hljs-id,
.scss .hljs-preprocessor {
color: #900;
font-weight: bold;
}
.hljs-list .hljs-keyword,
.hljs-subst {
font-weight: normal;
}
.hljs-class .hljs-title,
.hljs-type,
.vhdl .hljs-literal,
.tex .hljs-command {
color: #458;
font-weight: bold;
}
.hljs-tag,
.hljs-tag .hljs-title,
.hljs-rule .hljs-property,
.django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal;
}
.hljs-attribute,
.hljs-variable,
.lisp .hljs-body,
.hljs-name {
color: #008080;
}
.hljs-regexp {
color: #009926;
}
.hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.lisp .hljs-keyword,
.clojure .hljs-keyword,
.scheme .hljs-keyword,
.tex .hljs-special,
.hljs-prompt {
color: #990073;
}
.hljs-built_in {
color: #0086b3;
}
.hljs-preprocessor,
.hljs-pragma,
.hljs-pi,
.hljs-doctype,
.hljs-shebang,
.hljs-cdata {
color: #999;
font-weight: bold;
}
.hljs-deletion {
background: #fdd;
}
.hljs-addition {
background: #dfd;
}
.diff .hljs-change {
background: #0086b3;
}
.hljs-chunk {
color: #aaa;
}

View File

@ -1,810 +0,0 @@
// ---------- begin 全局颜色配置 ------------
/* 编辑器边框颜色 */
@border-color: #ccc;
/* 菜单颜色、上边框颜色 */
@fore-color: #666;
/* 菜单选中状态的颜色 */
@selected-color: #1e88e5;
/* input focus 时的颜色 */
@focus-input-color: #1e88e5;
/* 按钮颜色 */
@button-color: #1e88e5;
/* tab selected 状态下的颜色 */
@selected-tab-color: #1e88e5;
// ---------- end 全局颜色配置 ------------
.wangEditor-container {
position: relative;
background-color: #fff;
border: 1px solid @border-color;
z-index: 1;
width: 100%;
a:focus,
button:focus{
outline:none;
}
&,* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1;
}
img {
border: none;
}
.clearfix:after {
content: '';
display: table;
clear: both;
}
.clearfix {
*zoom: 1;
}
textarea {
border: none;
&:focus{
outline: none;
}
}
// 显示p head 高度的 tip
.height-tip {
position: absolute;
width: 3px;
background-color: #ccc;
left: 0;
transition: top .2s;
}
// 设置 img table 的 toolbar
.txt-toolbar {
position: absolute;
background-color: #fff;
padding: 3px 5px;
border-top: 2px solid @fore-color;
box-shadow: 1px 3px 3px #999;
// for IE8
border-left: 1px\9 solid\9 #ccc\9;
border-bottom: 1px\9 solid\9 #999\9;
border-right: 1px\9 solid\9 #999\9;
// 小三角
.tip-triangle {
display: block;
position: absolute;
width: 0;
height: 0;
border: 5px solid;
border-color: transparent transparent @fore-color transparent;
top: -12px;
left: 50%;
margin-left: -5px;
}
a {
color: @fore-color;
display: inline-block;
margin: 0 3px;
padding: 5px;
text-decoration: none;
border-radius: 3px;
&:hover {
background-color: #f1f1f1;
}
}
}
// 图品拖拽大小
.img-drag-point {
display: block;
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
cursor: se-resize;
background-color: @fore-color;
margin-left: -6px;
margin-top: -6px;
box-shadow: 1px 1px 5px #999;
}
// 进度条
.wangEditor-upload-progress {
position: absolute;
height: 1px;
background: #1e88e5;
width: 0;
display: none;
-webkit-transition: width .5s;
-o-transition: width .5s;
transition: width .5s;
}
}
.wangEditor-fullscreen {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.wangEditor-container {
.code-textarea {
resize: none;
width: 100%;
font-size: 14px;
line-height: 1.5;
font-family: 'Verdana';
color: #333;
padding: 0 15px 0 15px;
}
}
.wangEditor-menu-container {
width: 100%;
border-bottom: 1px solid #f1f1f1;
background-color: #fff;
a {
text-decoration: none;
}
// 菜单组
.menu-group {
float: left;
padding: 0 8px;
border-right: 1px solid #f1f1f1;
}
// 单个菜单容器
.menu-item {
float: left;
position: relative;
text-align: center;
height: 31px;
width: 35px;
&:hover {
background-color: #f1f1f1;
}
// 菜单
a {
display: block;
text-align: center;
color: @fore-color;
width: 100%;
padding: 8px 0;
font-size: 0.9em;
}
// 菜单选中状态
.selected {
color: @selected-color;
}
// 激活状态
.active {
background-color: #f1f1f1;
}
// 禁用状态
.disable {
opacity: 0.5;
filter: Alpha(opacity=50);
}
}
// tip提示
.menu-tip {
display: block;
position: absolute;
z-index: 20;
width: 60px;
text-align: center;
background-color: @fore-color;
color: #fff;
padding: 7px 0;
font-size: 12px;
top: 100%;
left: 50%;
margin-left: -30px;
border-radius: 2px;
box-shadow: 1px 1px 5px #999;
display: none;
/*// 小三角
.tip-triangle {
display: block;
position: absolute;
width: 0;
height: 0;
border:5px solid;
border-color: transparent transparent @fore-color transparent;
top: -10px;
left: 50%;
margin-left: -5px;
}*/
}
.menu-tip-40 {
width: 40px;
margin-left: -20px;
}
.menu-tip-50 {
width: 50px;
margin-left: -25px;
}
}
.wangEditor-menu-shadow {
/*border-bottom-width: 0;*/
border-bottom: 1px\9 solid\9 #f1f1f1\9;
box-shadow: 0 1px 3px #999;
}
.wangEditor-container {
.wangEditor-txt{
width: 100%;
text-align: left;
padding: 15px;
padding-top: 0;
margin-top: 5px;
overflow-y: auto;
p,h1,h2,h3,h4,h5 {
margin: 10px 0;
line-height: 1.8;
* {
line-height: 1.8;
}
}
ul, ol {
padding-left: 20px;
}
img {
cursor: pointer;
}
img.clicked {
box-shadow: 1px 1px 10px #999;
}
table.clicked {
box-shadow: 1px 1px 10px #999;
}
pre code {
line-height: 1.5;
}
&:focus{
outline: none;
}
}
}
.wangEditor-container {
.wangEditor-txt {
blockquote {
display: block;
border-left: 8px solid #d0e5f2;
padding: 5px 10px;
margin: 10px 0;
line-height: 1.4;
font-size: 100%;
background-color: #f1f1f1;
}
table {
border: none;
border-collapse: collapse;
}
table td,
table th {
border: 1px solid #999;
padding: 3px 5px;
min-width: 50px;
height: 20px;
}
pre {
border: 1px solid #ccc;
background-color: #f8f8f8;
padding: 10px;
margin: 5px 0px;
font-size: 0.8em;
border-radius: 3px;
}
}
}
.wangEditor-drop-list {
display: none;
position: absolute;
background-color: #fff;
overflow: hidden;
z-index: 10;
transition: height .7s;
border-top: 1px solid #f1f1f1;
box-shadow: 1px 3px 3px #999;
// for IE8
border-left: 1px\9 solid\9 #ccc\9;
border-bottom: 1px\9 solid\9 #999\9;
border-right: 1px\9 solid\9 #999\9;
a {
text-decoration: none;
display: block;
color: @fore-color;
padding: 3px 5px;
&:hover {
background-color: #f1f1f1;
}
}
}
.wangEditor-drop-panel,
.txt-toolbar {
display: none;
position: absolute;
padding: 10px;
font-size: 14px;
/*border: 1px\9 solid\9 #cccccc\9;*/
background-color: #fff;
z-index: 10;
border-top: 2px solid @fore-color;
box-shadow: 1px 3px 3px #999;
// for IE8
border-left: 1px\9 solid\9 #ccc\9;
border-bottom: 1px\9 solid\9 #999\9;
border-right: 1px\9 solid\9 #999\9;
// 小三角
.tip-triangle {
display: block;
position: absolute;
width: 0;
height: 0;
border: 5px solid;
border-color: transparent transparent @fore-color transparent;
top: -12px;
left: 50%;
margin-left: -5px;
}
a {
text-decoration: none;
}
// 输入框
input[type=text] {
border: none;
border-bottom: 1px solid #ccc;
font-size: 14px;
height: 20px;
color: #333;
padding: 3px 0;
&:focus{
outline: none;
border-bottom: 2px solid @focus-input-color;
}
}
input[type=text].block {
display: block;
width: 100%;
}
textarea {
border: 1px solid #ccc;
&:focus {
outline: none;
border-color: @focus-input-color;
}
}
// 按钮
button {
font-size: 14px;
color: @button-color;
border: none;
padding: 10px;
background-color: #fff;
cursor: pointer;
border-radius: 3px;
&:hover {
background-color: #f1f1f1;
}
&:focus{
outline: none;
}
}
button.right {
float: right;
margin-left: 10px;
}
button.gray {
color: #999;
}
button.link {
padding: 5px 10px;
&:hover {
background-color: #fff;
text-decoration: underline;
}
}
// 颜色块
.color-item {
display: block;
float: left;
width: 25px;
height: 25px;
text-align: center;
padding: 2px;
border-radius: 2px;
text-decoration: underline;
&:hover {
background-color: #f1f1f1;
}
}
// 列表
.list-menu-item {
display: block;
float: left;
color: #333;
padding: 5px 5px;
border-radius: 2px;
&:hover {
background-color: #f1f1f1;
}
}
// 表格
table.choose-table {
border: none;
border-collapse: collapse;
td {
border: 1px solid #ccc;
width: 16px;
height: 12px;
}
td.active {
background-color: #ccc;
opacity: .5;
filter: Alpha(opacity=50);
}
}
// tab
.panel-tab {
.tab-container {
margin-bottom: 5px;
a {
display: inline-block;
color: #999;
text-align: center;
margin: 0 5px;
padding: 5px 5px;
}
a.selected {
color: @selected-tab-color;
border-bottom: 2px solid @selected-tab-color;
}
}
.content-container {
.content {
display: none;
a {
display: inline-block;
margin: 2px;
padding: 2px;
border-radius: 2px;
&:hover {
background-color: #f1f1f1;
}
}
}
.selected {
display: block;
}
}
.emotion-content-container {
height: 200px;
overflow-y: auto;
}
}
// 上传图片
.upload-icon-container {
color: #ccc;
text-align: center;
margin: 20px 20px 15px 20px !important;
padding: 5px !important;
font-size: 65px;
cursor: pointer;
border: 2px dotted #f1f1f1;
display: block !important;
&:hover {
color: #666;
border-color: #ccc;
}
}
}
.wangEditor-modal {
position: absolute;
top: 50%;
left: 50%;
background-color: #fff;
border-top: 1px solid #f1f1f1;
box-shadow: 1px 3px 3px #999;
// for IE8
border-top: 1px\9 solid\9 #ccc\9;
border-left: 1px\9 solid\9 #ccc\9;
border-bottom: 1px\9 solid\9 #999\9;
border-right: 1px\9 solid\9 #999\9;
// 关闭按钮
.wangEditor-modal-close {
position: absolute;
top: 0;
right: 0;
margin-top: -25px;
margin-right: -25px;
font-size: 1.5em;
color: #666;
cursor: pointer;
}
}
@font-face {
font-family: 'icomoon';
src:url('../fonts/icomoon.eot?-qdfu1s');
src:url('../fonts/icomoon.eot?#iefix-qdfu1s') format('embedded-opentype'),
url('../fonts/icomoon.ttf?-qdfu1s') format('truetype'),
url('../fonts/icomoon.woff?-qdfu1s') format('woff'),
url('../fonts/icomoon.svg?-qdfu1s#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="wangeditor-menu-img-"], [class*=" wangeditor-menu-img-"] {
font-family: 'icomoon';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.wangeditor-menu-img-link:before {content: "\e800";}
.wangeditor-menu-img-unlink:before {content: "\e801";}
.wangeditor-menu-img-code:before {content: "\e802";}
.wangeditor-menu-img-cancel:before {content: "\e803";}
.wangeditor-menu-img-terminal:before {content: "\e804";}
.wangeditor-menu-img-angle-down:before {content: "\e805";}
.wangeditor-menu-img-font:before {content: "\e806";}
.wangeditor-menu-img-bold:before {content: "\e807";}
.wangeditor-menu-img-italic:before {content: "\e808";}
.wangeditor-menu-img-header:before {content: "\e809";}
.wangeditor-menu-img-align-left:before {content: "\e80a";}
.wangeditor-menu-img-align-center:before {content: "\e80b";}
.wangeditor-menu-img-align-right:before {content: "\e80c";}
.wangeditor-menu-img-list-bullet:before {content: "\e80d";}
.wangeditor-menu-img-indent-left:before {content: "\e80e";}
.wangeditor-menu-img-indent-right:before {content: "\e80f";}
.wangeditor-menu-img-list-numbered:before {content: "\e810";}
.wangeditor-menu-img-underline:before {content: "\e811";}
.wangeditor-menu-img-table:before {content: "\e812";}
.wangeditor-menu-img-eraser:before {content: "\e813";}
.wangeditor-menu-img-text-height:before {content: "\e814";}
.wangeditor-menu-img-brush:before {content: "\e815";}
.wangeditor-menu-img-pencil:before {content: "\e816";}
.wangeditor-menu-img-minus:before {content: "\e817";}
.wangeditor-menu-img-picture:before {content: "\e818";}
.wangeditor-menu-img-file-image:before {content: "\e819";}
.wangeditor-menu-img-cw:before {content: "\e81a";}
.wangeditor-menu-img-ccw:before {content: "\e81b";}
.wangeditor-menu-img-music:before {content: "\e911";}
.wangeditor-menu-img-play:before {content: "\e912";}
.wangeditor-menu-img-location:before {content: "\e947";}
.wangeditor-menu-img-happy:before {content: "\e9df";}
.wangeditor-menu-img-sigma:before {content: "\ea67";}
.wangeditor-menu-img-enlarge2:before {content: "\e98b";}
.wangeditor-menu-img-shrink2:before {content: "\e98c";}
.wangeditor-menu-img-newspaper:before{content: "\e904";}
.wangeditor-menu-img-camera:before{content: "\e90f";}
.wangeditor-menu-img-video-camera:before{content: "\e914";}
.wangeditor-menu-img-file-zip:before{content: "\e92b";}
.wangeditor-menu-img-stack:before{content: "\e92e";}
.wangeditor-menu-img-credit-card:before{content: "\e93f";}
.wangeditor-menu-img-address-book:before{content: "\e944";}
.wangeditor-menu-img-envelop:before{content: "\e945";}
.wangeditor-menu-img-drawer:before{content: "\e95c";}
.wangeditor-menu-img-download:before{content: "\e960";}
.wangeditor-menu-img-upload:before{content: "\e961";}
.wangeditor-menu-img-lock:before{content: "\e98f";}
.wangeditor-menu-img-unlocked:before{content: "\e990";}
.wangeditor-menu-img-wrench:before{content: "\e991";}
.wangeditor-menu-img-eye:before{content: "\e9ce";}
.wangeditor-menu-img-eye-blocked:before{content: "\e9d1";}
.wangeditor-menu-img-command:before{content: "\ea4e";}
.wangeditor-menu-img-font2:before{content: "\ea5c";}
.wangeditor-menu-img-libreoffice:before{content: "\eade";}
.wangeditor-menu-img-quotes-left:before{content: "\e977";}
.wangeditor-menu-img-strikethrough:before{content: "\ea65";}
.wangeditor-menu-img-desktop:before{content: "\f108";}
.wangeditor-menu-img-tablet:before{content: "\f10a";}
.wangeditor-menu-img-search-plus:before {
content: "\f00e";
}
.wangeditor-menu-img-search-minus:before {
content: "\f010";
}
.wangeditor-menu-img-trash-o:before {
content: "\f014";
}
.wangeditor-menu-img-align-justify:before {
content: "\f039";
}
.wangeditor-menu-img-arrows-v:before {
content: "\f07d";
}
.wangeditor-menu-img-sigma2:before {
content: "\ea68";
}
.wangeditor-menu-img-omega:before {
content: "\e900";
}
.wangeditor-menu-img-cancel-circle:before {
content: "\e901";
}
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
-webkit-text-size-adjust: none;
}
.hljs-comment,
.diff .hljs-header {
color: #998;
font-style: italic;
}
.hljs-keyword,
.css .rule .hljs-keyword,
.hljs-winutils,
.nginx .hljs-title,
.hljs-subst,
.hljs-request,
.hljs-status {
color: #333;
font-weight: bold;
}
.hljs-number,
.hljs-hexcolor,
.ruby .hljs-constant {
color: #008080;
}
.hljs-string,
.hljs-tag .hljs-value,
.hljs-doctag,
.tex .hljs-formula {
color: #d14;
}
.hljs-title,
.hljs-id,
.scss .hljs-preprocessor {
color: #900;
font-weight: bold;
}
.hljs-list .hljs-keyword,
.hljs-subst {
font-weight: normal;
}
.hljs-class .hljs-title,
.hljs-type,
.vhdl .hljs-literal,
.tex .hljs-command {
color: #458;
font-weight: bold;
}
.hljs-tag,
.hljs-tag .hljs-title,
.hljs-rule .hljs-property,
.django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal;
}
.hljs-attribute,
.hljs-variable,
.lisp .hljs-body,
.hljs-name {
color: #008080;
}
.hljs-regexp {
color: #009926;
}
.hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.lisp .hljs-keyword,
.clojure .hljs-keyword,
.scheme .hljs-keyword,
.tex .hljs-special,
.hljs-prompt {
color: #990073;
}
.hljs-built_in {
color: #0086b3;
}
.hljs-preprocessor,
.hljs-pragma,
.hljs-pi,
.hljs-doctype,
.hljs-shebang,
.hljs-cdata {
color: #999;
font-weight: bold;
}
.hljs-deletion {
background: #fdd;
}
.hljs-addition {
background: #dfd;
}
.diff .hljs-change {
background: #0086b3;
}
.hljs-chunk {
color: #aaa;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -1,76 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe800;" glyph-name="link" horiz-adv-x="950" d="M831.488 264.704q0 23.552-15.36 38.912l-118.784 118.784q-16.384 16.384-38.912 16.384-24.576 0-40.96-18.432 1.024-1.024 10.24-10.24t12.288-12.288 9.216-11.264 7.168-14.336 2.048-15.36q0-23.552-16.384-38.912t-38.912-16.384q-8.192 0-15.36 2.048t-14.336 7.168-11.264 9.216-12.288 12.288-10.24 10.24q-19.456-17.408-19.456-40.96t16.384-38.912l117.76-118.784q15.36-15.36 38.912-15.36 22.528 0 38.912 15.36l83.968 82.944q15.36 16.384 15.36 37.888zM430.080 668.16q0 22.528-15.36 38.912l-117.76 117.76q-16.384 16.384-38.912 16.384t-38.912-15.36l-83.968-83.968q-16.384-15.36-16.384-37.888t16.384-38.912l118.784-118.784q15.36-15.36 38.912-15.36t40.96 17.408q-2.048 2.048-11.264 11.264t-12.288 12.288-8.192 10.24-7.168 14.336-2.048 16.384q0 22.528 15.36 38.912t38.912 15.36q9.216 0 16.384-2.048t14.336-7.168 10.24-8.192 12.288-12.288 11.264-11.264q18.432 17.408 18.432 41.984zM942.080 264.704q0-68.608-49.152-115.712l-83.968-82.944q-47.104-48.128-115.712-48.128-69.632 0-116.736 49.152l-117.76 117.76q-47.104 48.128-47.104 116.736 0 69.632 50.176 118.784l-50.176 50.176q-49.152-50.176-118.784-50.176-68.608 0-116.736 48.128l-118.784 118.784q-48.128 48.128-48.128 116.736t48.128 115.712l83.968 83.968q48.128 47.104 116.736 47.104t116.736-48.128l116.736-118.784q48.128-47.104 48.128-115.712 0-70.656-50.176-119.808l50.176-50.176q49.152 50.176 118.784 50.176 68.608 0 116.736-48.128l118.784-118.784q48.128-48.128 48.128-116.736z" />
<glyph unicode="&#xe801;" glyph-name="unlink" horiz-adv-x="950" d="M250.88 233.984l-146.432-146.432q-5.12-5.12-13.312-5.12-6.144 0-13.312 5.12-5.12 5.12-5.12 13.312t5.12 13.312l146.432 145.408q6.144 5.12 13.312 5.12t13.312-5.12q5.12-5.12 5.12-12.288t-5.12-13.312zM347.136 210.432v-183.296q0-8.192-5.12-13.312t-13.312-5.12-12.288 5.12-5.12 13.312v183.296q0 8.192 5.12 13.312t12.288 5.12 13.312-5.12 5.12-13.312zM219.136 338.432q0-8.192-5.12-13.312t-13.312-5.12h-182.272q-8.192 0-13.312 5.12t-5.12 13.312 5.12 13.312 13.312 5.12h182.272q8.192 0 13.312-5.12t5.12-13.312zM942.080 264.704q0-68.608-49.152-115.712l-83.968-82.944q-47.104-48.128-115.712-48.128-69.632 0-116.736 49.152l-190.464 191.488q-12.288 11.264-24.576 31.744l137.216 10.24 155.648-156.672q15.36-15.36 38.912-15.36t38.912 15.36l83.968 82.944q15.36 16.384 15.36 37.888 0 23.552-15.36 38.912l-156.672 157.696 10.24 136.192q20.48-12.288 31.744-23.552l192.512-192.512q48.128-49.152 48.128-116.736zM588.8 678.4l-136.192-10.24-155.648 156.672q-16.384 16.384-38.912 16.384t-38.912-15.36l-83.968-83.968q-16.384-15.36-16.384-37.888t16.384-38.912l156.672-156.672-10.24-137.216q-20.48 12.288-32.768 24.576l-191.488 191.488q-48.128 49.152-48.128 116.736 0 68.608 48.128 115.712l83.968 83.968q48.128 47.104 116.736 47.104t116.736-48.128l190.464-191.488q12.288-12.288 23.552-32.768zM951.296 631.296q0-8.192-5.12-13.312t-13.312-5.12h-183.296q-8.192 0-13.312 5.12t-5.12 13.312 5.12 12.288 13.312 5.12h183.296q8.192 0 13.312-5.12t5.12-12.288zM640 941.568v-182.272q0-8.192-5.12-13.312t-13.312-5.12-13.312 5.12-5.12 13.312v182.272q0 8.192 5.12 13.312t13.312 5.12 13.312-5.12 5.12-13.312zM872.448 855.552l-146.432-146.432q-6.144-5.12-13.312-5.12t-12.288 5.12q-5.12 6.144-5.12 13.312t5.12 13.312l145.408 145.408q6.144 5.12 13.312 5.12t13.312-5.12q5.12-5.12 5.12-12.288t-5.12-13.312z" />
<glyph unicode="&#xe802;" glyph-name="code" horiz-adv-x="1097" d="M352.256 160.256l-28.672-28.672q-5.12-5.12-12.288-5.12t-13.312 5.12l-266.24 266.24q-6.144 6.144-6.144 13.312t6.144 13.312l266.24 266.24q5.12 6.144 13.312 6.144t12.288-6.144l28.672-28.672q6.144-5.12 6.144-13.312t-6.144-12.288l-224.256-225.28 224.256-224.256q6.144-6.144 6.144-13.312t-6.144-13.312zM690.176 770.56l-212.992-738.304q-2.048-7.168-9.216-11.264t-13.312-1.024l-34.816 9.216q-8.192 3.072-11.264 9.216t-2.048 14.336l212.992 737.28q3.072 8.192 9.216 11.264t13.312 2.048l35.84-10.24q7.168-2.048 11.264-9.216t1.024-13.312zM1065.984 397.824l-266.24-266.24q-6.144-5.12-13.312-5.12t-13.312 5.12l-28.672 28.672q-5.12 6.144-5.12 13.312t5.12 13.312l224.256 224.256-224.256 225.28q-5.12 5.12-5.12 12.288t5.12 13.312l28.672 28.672q6.144 6.144 13.312 6.144t13.312-6.144l266.24-266.24q5.12-5.12 5.12-13.312t-5.12-13.312z" />
<glyph unicode="&#xe803;" glyph-name="cancel" horiz-adv-x="804" d="M741.376 204.288q0-22.528-15.36-38.912l-77.824-77.824q-16.384-15.36-38.912-15.36t-38.912 15.36l-167.936 168.96-167.936-168.96q-16.384-15.36-38.912-15.36t-38.912 15.36l-77.824 77.824q-16.384 16.384-16.384 38.912t16.384 38.912l167.936 167.936-167.936 167.936q-16.384 16.384-16.384 38.912t16.384 38.912l77.824 77.824q16.384 16.384 38.912 16.384t38.912-16.384l167.936-167.936 167.936 167.936q16.384 16.384 38.912 16.384t38.912-16.384l77.824-77.824q15.36-15.36 15.36-38.912t-15.36-38.912l-167.936-167.936 167.936-167.936q15.36-15.36 15.36-38.912z" />
<glyph unicode="&#xe804;" glyph-name="terminal" horiz-adv-x="950" d="M333.824 397.824l-266.24-266.24q-5.12-5.12-12.288-5.12t-13.312 5.12l-28.672 28.672q-6.144 6.144-6.144 13.312t6.144 13.312l224.256 224.256-224.256 225.28q-6.144 5.12-6.144 12.288t6.144 13.312l28.672 28.672q5.12 6.144 13.312 6.144t12.288-6.144l266.24-266.24q6.144-5.12 6.144-13.312t-6.144-13.312zM951.296 136.704v-35.84q0-8.192-5.12-13.312t-13.312-5.12h-548.864q-8.192 0-13.312 5.12t-5.12 13.312v35.84q0 8.192 5.12 13.312t13.312 5.12h548.864q8.192 0 13.312-5.12t5.12-13.312z" />
<glyph unicode="&#xe805;" glyph-name="angle-down" horiz-adv-x="657" d="M614.4 539.136q0-7.168-6.144-13.312l-266.24-266.24q-5.12-5.12-13.312-5.12t-12.288 5.12l-266.24 266.24q-6.144 6.144-6.144 13.312t6.144 13.312l27.648 28.672q6.144 6.144 13.312 6.144t13.312-6.144l224.256-224.256 225.28 224.256q5.12 6.144 13.312 6.144t12.288-6.144l28.672-28.672q6.144-5.12 6.144-13.312z" />
<glyph unicode="&#xe806;" glyph-name="font" horiz-adv-x="950" d="M414.72 640.512l-97.28-257.024q18.432 0 77.824-1.024t91.136-1.024q11.264 0 32.768 1.024-49.152 144.384-104.448 258.048zM0 8.704l1.024 45.056q13.312 4.096 31.744 7.168t32.768 6.144 28.672 8.192 25.6 17.408 17.408 28.672l135.168 352.256 159.744 413.696h73.728q4.096-8.192 6.144-12.288l116.736-274.432q19.456-45.056 61.44-147.456t64.512-156.672q9.216-19.456 33.792-82.944t40.96-96.256q11.264-25.6 19.456-31.744 11.264-9.216 50.176-17.408t48.128-11.264q4.096-22.528 4.096-32.768 0-2.048-1.024-7.168t0-8.192q-35.84 0-108.544 5.12t-109.568 4.096q-43.008 0-122.88-4.096t-101.376-4.096q0 24.576 2.048 44.032l74.752 16.384q1.024 0 7.168 1.024t9.216 2.048 8.192 3.072 9.216 4.096 6.144 4.096 5.12 6.144 1.024 8.192q0 9.216-17.408 55.296t-40.96 101.376-24.576 57.344l-257.024 1.024q-14.336-33.792-44.032-111.616t-28.672-93.184q0-12.288 8.192-21.504t24.576-14.336 27.648-7.168 32.768-5.12 23.552-2.048q1.024-11.264 1.024-32.768 0-5.12-2.048-16.384-32.768 0-99.328 6.144t-99.328 6.144q-5.12 0-15.36-3.072t-12.288-2.048q-46.080-8.192-107.52-8.192z" />
<glyph unicode="&#xe807;" glyph-name="bold" horiz-adv-x="804" d="M317.44 90.624q41.984-18.432 79.872-18.432 215.040 0 215.040 191.488 0 65.536-23.552 103.424-15.36 24.576-35.84 41.984t-37.888 26.624-46.080 14.336-48.128 6.144-54.272 1.024q-40.96 0-57.344-6.144 0-29.696 0-90.112t-1.024-91.136q0-4.096 0-37.888t0-55.296 2.048-48.128 7.168-37.888zM309.248 517.632q23.552-4.096 62.464-4.096 47.104 0 81.92 7.168t62.464 25.6 41.984 51.2 15.36 80.896q0 39.936-16.384 69.632t-46.080 47.104-61.44 24.576-70.656 8.192q-28.672 0-74.752-7.168 0-28.672 3.072-86.016t2.048-87.040q0-15.36 0-46.080t-1.024-45.056q0-26.624 1.024-38.912zM0 8.704l1.024 54.272q9.216 2.048 49.152 9.216t60.416 15.36q4.096 7.168 7.168 15.36t4.096 19.456 4.096 18.432 1.024 21.504 0 19.456v36.864q0 561.152-12.288 585.728-2.048 5.12-12.288 8.192t-25.6 6.144-28.672 4.096-27.648 3.072-17.408 2.048l-2.048 47.104q56.32 1.024 194.56 6.144t212.992 5.12q13.312 0 38.912 0t38.912 0q39.936 0 77.824-7.168t73.728-24.576 61.44-39.936 41.984-60.416 16.384-77.824q0-29.696-9.216-55.296t-22.528-40.96-36.864-32.768-41.984-25.6-48.128-22.528q88.064-20.48 147.456-76.8t58.368-142.336q0-56.32-20.48-102.4t-53.248-74.752-78.848-48.128-93.184-27.648-100.352-8.192q-25.6 0-75.776 2.048t-75.776 1.024q-60.416 0-175.104-6.144t-132.096-7.168z" />
<glyph unicode="&#xe808;" glyph-name="italic" horiz-adv-x="585" d="M0 9.728l10.24 49.152q3.072 1.024 46.080 12.288t63.488 21.504q16.384 19.456 23.552 57.344 1.024 4.096 35.84 165.888t64.512 310.272 29.696 168.96v14.336q-13.312 7.168-30.72 11.264t-39.936 4.096-32.768 3.072l10.24 59.392q19.456-2.048 68.608-4.096t86.016-4.096 68.608-1.024q27.648 0 56.32 1.024t68.608 4.096 56.32 4.096q-2.048-22.528-10.24-51.2-17.408-6.144-58.368-16.384t-61.44-19.456q-5.12-10.24-8.192-23.552t-5.12-23.552-4.096-25.6-4.096-24.576q-15.36-83.968-50.176-239.616t-44.032-202.752q-1.024-5.12-7.168-32.768t-11.264-52.224-9.216-47.104-4.096-32.768l1.024-10.24q9.216-3.072 105.472-18.432-2.048-24.576-9.216-56.32-6.144 0-18.432-1.024t-18.432-1.024q-16.384 0-50.176 6.144t-49.152 6.144q-78.848 1.024-117.76 1.024-28.672 0-80.896-5.12t-69.632-6.144z" />
<glyph unicode="&#xe809;" glyph-name="header" d="M961.536 8.704q-25.6 0-75.776 2.048t-76.8 2.048q-24.576 0-74.752-2.048t-75.776-2.048q-14.336 0-21.504 12.288t-7.168 25.6q0 17.408 9.216 26.624t22.528 9.216 29.696 4.096 25.6 9.216q18.432 11.264 18.432 79.872v223.232q0 12.288-1.024 17.408-7.168 3.072-28.672 3.072h-385.024q-22.528 0-29.696-3.072 0-5.12 0-17.408l-1.024-211.968q0-80.896 21.504-94.208 9.216-5.12 26.624-7.168t32.768-2.048 25.6-8.192 11.264-26.624q0-14.336-7.168-26.624t-20.48-13.312q-26.624 0-79.872 2.048t-78.848 2.048q-24.576 0-72.704-2.048t-72.704-2.048q-13.312 0-20.48 12.288t-7.168 25.6q0 17.408 9.216 25.6t20.48 10.24 26.624 4.096 24.576 9.216q18.432 13.312 18.432 81.92l-1.024 31.744v464.896q0 2.048 1.024 14.336t0 21.504-1.024 21.504-2.048 24.576-4.096 20.48-6.144 18.432-9.216 10.24q-8.192 5.12-25.6 6.144t-29.696 2.048-23.552 7.168-10.24 26.624q0 14.336 6.144 26.624t20.48 13.312q26.624 0 79.872-2.048t78.848-2.048q23.552 0 72.704 2.048t71.68 2.048q14.336 0 21.504-13.312t7.168-26.624q0-17.408-9.216-25.6t-22.528-8.192-28.672-2.048-24.576-7.168q-19.456-12.288-19.456-92.16l1.024-182.272q0-12.288 0-18.432 7.168-2.048 22.528-2.048h399.36q14.336 0 21.504 2.048 1.024 6.144 1.024 18.432v182.272q0 79.872-19.456 92.16-10.24 6.144-33.792 7.168t-37.888 7.168-14.336 28.672q0 14.336 7.168 26.624t21.504 13.312q24.576 0 75.776-2.048t74.752-2.048q24.576 0 73.728 2.048t73.728 2.048q14.336 0 21.504-13.312t7.168-26.624q0-17.408-10.24-25.6t-22.528-8.192-29.696-2.048-24.576-7.168q-20.48-13.312-20.48-92.16l1.024-538.624q0-67.584 19.456-79.872 9.216-6.144 25.6-7.168t30.72-3.072 23.552-9.216 10.24-24.576q0-15.36-6.144-27.648t-20.48-13.312z" />
<glyph unicode="&#xe80a;" glyph-name="align-left" d="M1024 192v-72.704q0-15.36-11.264-25.6t-25.6-11.264h-950.272q-15.36 0-25.6 11.264t-11.264 25.6v72.704q0 15.36 11.264 25.6t25.6 11.264h950.272q15.36 0 25.6-11.264t11.264-25.6zM804.864 411.136v-72.704q0-15.36-11.264-25.6t-25.6-11.264h-731.136q-15.36 0-25.6 11.264t-11.264 25.6v72.704q0 15.36 11.264 25.6t25.6 11.264h731.136q15.36 0 25.6-11.264t11.264-25.6zM951.296 631.296v-73.728q0-14.336-11.264-25.6t-25.6-11.264h-877.568q-15.36 0-25.6 11.264t-11.264 25.6v73.728q0 14.336 11.264 25.6t25.6 10.24h877.568q14.336 0 25.6-10.24t11.264-25.6zM731.136 850.432v-73.728q0-14.336-10.24-25.6t-25.6-10.24h-658.432q-15.36 0-25.6 10.24t-11.264 25.6v73.728q0 14.336 11.264 25.6t25.6 11.264h658.432q14.336 0 25.6-11.264t10.24-25.6z" />
<glyph unicode="&#xe80b;" glyph-name="align-center" d="M1024 192v-72.704q0-15.36-11.264-25.6t-25.6-11.264h-950.272q-15.36 0-25.6 11.264t-11.264 25.6v72.704q0 15.36 11.264 25.6t25.6 11.264h950.272q15.36 0 25.6-11.264t11.264-25.6zM804.864 411.136v-72.704q0-15.36-11.264-25.6t-25.6-11.264h-512q-14.336 0-25.6 11.264t-11.264 25.6v72.704q0 15.36 11.264 25.6t25.6 11.264h512q15.36 0 25.6-11.264t11.264-25.6zM951.296 631.296v-73.728q0-14.336-11.264-25.6t-25.6-11.264h-804.864q-14.336 0-25.6 11.264t-11.264 25.6v73.728q0 14.336 11.264 25.6t25.6 10.24h804.864q14.336 0 25.6-10.24t11.264-25.6zM731.136 850.432v-73.728q0-14.336-10.24-25.6t-25.6-10.24h-366.592q-14.336 0-25.6 10.24t-10.24 25.6v73.728q0 14.336 10.24 25.6t25.6 11.264h366.592q14.336 0 25.6-11.264t10.24-25.6z" />
<glyph unicode="&#xe80c;" glyph-name="align-right" d="M1024 192v-72.704q0-15.36-11.264-25.6t-25.6-11.264h-950.272q-15.36 0-25.6 11.264t-11.264 25.6v72.704q0 15.36 11.264 25.6t25.6 11.264h950.272q15.36 0 25.6-11.264t11.264-25.6zM1024 411.136v-72.704q0-15.36-11.264-25.6t-25.6-11.264h-731.136q-14.336 0-25.6 11.264t-11.264 25.6v72.704q0 15.36 11.264 25.6t25.6 11.264h731.136q15.36 0 25.6-11.264t11.264-25.6zM1024 631.296v-73.728q0-14.336-11.264-25.6t-25.6-11.264h-877.568q-14.336 0-25.6 11.264t-11.264 25.6v73.728q0 14.336 11.264 25.6t25.6 10.24h877.568q15.36 0 25.6-10.24t11.264-25.6zM1024 850.432v-73.728q0-14.336-11.264-25.6t-25.6-10.24h-658.432q-14.336 0-25.6 10.24t-10.24 25.6v73.728q0 14.336 10.24 25.6t25.6 11.264h658.432q15.36 0 25.6-11.264t11.264-25.6z" />
<glyph unicode="&#xe80d;" glyph-name="list-bullet" d="M219.136 155.136q0-45.056-31.744-77.824t-77.824-31.744-77.824 31.744-31.744 77.824 31.744 77.824 77.824 31.744 77.824-31.744 31.744-77.824zM219.136 448q0-46.080-31.744-77.824t-77.824-31.744-77.824 31.744-31.744 77.824 31.744 77.824 77.824 31.744 77.824-31.744 31.744-77.824zM1024 210.432v-109.568q0-7.168-5.12-13.312t-13.312-5.12h-694.272q-8.192 0-13.312 5.12t-5.12 13.312v109.568q0 7.168 5.12 12.288t13.312 6.144h694.272q7.168 0 13.312-6.144t5.12-12.288zM219.136 740.864q0-46.080-31.744-77.824t-77.824-31.744-77.824 31.744-31.744 77.824 31.744 77.824 77.824 31.744 77.824-31.744 31.744-77.824zM1024 503.296v-110.592q0-7.168-5.12-12.288t-13.312-5.12h-694.272q-8.192 0-13.312 5.12t-5.12 12.288v110.592q0 7.168 5.12 12.288t13.312 5.12h694.272q7.168 0 13.312-5.12t5.12-12.288zM1024 795.136v-109.568q0-7.168-5.12-12.288t-13.312-6.144h-694.272q-8.192 0-13.312 6.144t-5.12 12.288v109.568q0 8.192 5.12 13.312t13.312 5.12h694.272q7.168 0 13.312-5.12t5.12-13.312z" />
<glyph unicode="&#xe80e;" glyph-name="indent-left" d="M219.136 648.704v-328.704q0-7.168-5.12-13.312t-13.312-5.12q-7.168 0-12.288 5.12l-164.864 164.864q-5.12 5.12-5.12 13.312t5.12 13.312l164.864 163.84q5.12 5.12 12.288 5.12 8.192 0 13.312-5.12t5.12-13.312zM1024 210.432v-109.568q0-7.168-5.12-13.312t-13.312-5.12h-987.136q-7.168 0-13.312 5.12t-5.12 13.312v109.568q0 7.168 5.12 12.288t13.312 6.144h987.136q7.168 0 13.312-6.144t5.12-12.288zM1024 429.568v-109.568q0-7.168-5.12-13.312t-13.312-5.12h-621.568q-7.168 0-13.312 5.12t-5.12 13.312v109.568q0 7.168 5.12 13.312t13.312 5.12h621.568q7.168 0 13.312-5.12t5.12-13.312zM1024 648.704v-109.568q0-7.168-5.12-12.288t-13.312-6.144h-621.568q-7.168 0-13.312 6.144t-5.12 12.288v109.568q0 8.192 5.12 13.312t13.312 5.12h621.568q7.168 0 13.312-5.12t5.12-13.312zM1024 868.864v-109.568q0-8.192-5.12-13.312t-13.312-5.12h-987.136q-7.168 0-13.312 5.12t-5.12 13.312v109.568q0 7.168 5.12 12.288t13.312 6.144h987.136q7.168 0 13.312-6.144t5.12-12.288z" />
<glyph unicode="&#xe80f;" glyph-name="indent-right" d="M200.704 484.864q0-8.192-5.12-13.312l-163.84-164.864q-5.12-5.12-13.312-5.12-7.168 0-13.312 5.12t-5.12 13.312v328.704q0 8.192 5.12 13.312t13.312 5.12 13.312-5.12l163.84-163.84q5.12-5.12 5.12-13.312zM1024 210.432v-109.568q0-7.168-5.12-13.312t-13.312-5.12h-987.136q-7.168 0-13.312 5.12t-5.12 13.312v109.568q0 7.168 5.12 12.288t13.312 6.144h987.136q7.168 0 13.312-6.144t5.12-12.288zM1024 429.568v-109.568q0-7.168-5.12-13.312t-13.312-5.12h-621.568q-7.168 0-13.312 5.12t-5.12 13.312v109.568q0 7.168 5.12 13.312t13.312 5.12h621.568q7.168 0 13.312-5.12t5.12-13.312zM1024 648.704v-109.568q0-7.168-5.12-12.288t-13.312-6.144h-621.568q-7.168 0-13.312 6.144t-5.12 12.288v109.568q0 8.192 5.12 13.312t13.312 5.12h621.568q7.168 0 13.312-5.12t5.12-13.312zM1024 868.864v-109.568q0-8.192-5.12-13.312t-13.312-5.12h-987.136q-7.168 0-13.312 5.12t-5.12 13.312v109.568q0 7.168 5.12 12.288t13.312 6.144h987.136q7.168 0 13.312-6.144t5.12-12.288z" />
<glyph unicode="&#xe810;" glyph-name="list-numbered" d="M218.112 34.304q0-46.080-31.744-71.68t-76.8-26.624q-61.44 0-98.304 37.888l31.744 50.176q28.672-25.6 61.44-25.6 16.384 0 28.672 8.192t12.288 24.576q0 35.84-60.416 31.744l-14.336 31.744q4.096 6.144 18.432 24.576t24.576 31.744 20.48 21.504v1.024q-9.216 0-27.648-1.024t-27.648 0v-30.72h-60.416v87.040h190.464v-50.176l-54.272-66.56q28.672-6.144 46.080-27.648t17.408-50.176zM219.136 392.704v-91.136h-206.848q-4.096 20.48-4.096 30.72 0 29.696 14.336 53.248t31.744 38.912 37.888 27.648 31.744 24.576 14.336 25.6q0 14.336-9.216 22.528t-22.528 7.168q-25.6 0-46.080-32.768l-48.128 33.792q13.312 28.672 40.96 45.056t60.416 16.384q40.96 0 69.632-23.552t28.672-64.512q0-28.672-19.456-52.224t-43.008-36.864-43.008-28.672-20.48-30.72h72.704v34.816h60.416zM1024 210.432v-109.568q0-8.192-5.12-13.312t-13.312-5.12h-694.272q-8.192 0-13.312 5.12t-5.12 13.312v109.568q0 8.192 5.12 13.312t13.312 5.12h694.272q7.168 0 13.312-6.144t5.12-12.288zM219.136 724.48v-57.344h-191.488v57.344h61.44q0 22.528 0 69.632t1.024 68.608v7.168h-1.024q-5.12-10.24-28.672-30.72l-40.96 43.008 77.824 72.704h60.416v-230.4h61.44zM1024 503.296v-110.592q0-7.168-5.12-12.288t-13.312-5.12h-694.272q-8.192 0-13.312 5.12t-5.12 12.288v110.592q0 7.168 5.12 12.288t13.312 5.12h694.272q7.168 0 13.312-5.12t5.12-12.288zM1024 795.136v-109.568q0-7.168-5.12-12.288t-13.312-6.144h-694.272q-8.192 0-13.312 6.144t-5.12 12.288v109.568q0 8.192 5.12 13.312t13.312 5.12h694.272q7.168 0 13.312-5.12t5.12-13.312z" />
<glyph unicode="&#xe811;" glyph-name="underline" horiz-adv-x="878" d="M27.648 833.024q-21.504 1.024-25.6 2.048l-2.048 50.176q7.168 0 22.528 0 34.816 0 64.512-2.048 75.776-4.096 94.208-4.096 49.152 0 96.256 2.048 66.56 2.048 83.968 3.072 31.744 0 49.152 1.024l-1.024-8.192 1.024-36.864v-5.12q-33.792-5.12-70.656-5.12-33.792 0-45.056-14.336-7.168-7.168-7.168-74.752 0-8.192 0-18.432t0-15.36l1.024-131.072 8.192-159.744q3.072-70.656 28.672-114.688 20.48-33.792 55.296-53.248 50.176-26.624 100.352-26.624 59.392 0 109.568 16.384 31.744 10.24 56.32 28.672 27.648 20.48 37.888 36.864 20.48 31.744 29.696 64.512 12.288 41.984 12.288 131.072 0 45.056-2.048 73.728t-6.144 69.632-8.192 91.136l-2.048 33.792q-3.072 37.888-13.312 50.176-19.456 20.48-44.032 19.456l-57.344-1.024-8.192 2.048 1.024 49.152h48.128l116.736-6.144q44.032-2.048 112.64 6.144l10.24-2.048q3.072-21.504 3.072-28.672 0-4.096-2.048-17.408-25.6-7.168-48.128-8.192-41.984-6.144-45.056-9.216-8.192-9.216-8.192-23.552 0-4.096 0-15.36t1.024-17.408q5.12-11.264 13.312-226.304 3.072-111.616-9.216-174.080-8.192-43.008-23.552-69.632-21.504-36.864-63.488-70.656-43.008-32.768-104.448-50.176-62.464-19.456-145.408-19.456-95.232 0-162.816 26.624t-101.376 69.632q-34.816 43.008-48.128 111.616-9.216 45.056-9.216 135.168v190.464q0 107.52-9.216 121.856-14.336 20.48-83.968 21.504zM877.568 27.136v36.864q0 8.192-5.12 13.312t-13.312 5.12h-840.704q-8.192 0-13.312-5.12t-5.12-13.312v-36.864q0-8.192 5.12-13.312t13.312-5.12h840.704q8.192 0 13.312 5.12t5.12 13.312z" />
<glyph unicode="&#xe812;" glyph-name="table" horiz-adv-x="950" d="M292.864 173.568v109.568q0 8.192-5.12 13.312t-13.312 5.12h-183.296q-7.168 0-13.312-5.12t-5.12-13.312v-109.568q0-8.192 5.12-13.312t13.312-5.12h183.296q8.192 0 13.312 5.12t5.12 13.312zM292.864 392.704v110.592q0 7.168-5.12 12.288t-13.312 5.12h-183.296q-7.168 0-13.312-5.12t-5.12-12.288v-110.592q0-7.168 5.12-12.288t13.312-5.12h183.296q8.192 0 13.312 5.12t5.12 12.288zM584.704 173.568v109.568q0 8.192-5.12 13.312t-12.288 5.12h-183.296q-8.192 0-13.312-5.12t-5.12-13.312v-109.568q0-8.192 5.12-13.312t13.312-5.12h183.296q7.168 0 12.288 5.12t5.12 13.312zM292.864 612.864v109.568q0 8.192-5.12 13.312t-13.312 5.12h-183.296q-7.168 0-13.312-5.12t-5.12-13.312v-109.568q0-8.192 5.12-13.312t13.312-5.12h183.296q8.192 0 13.312 5.12t5.12 13.312zM584.704 392.704v110.592q0 7.168-5.12 12.288t-12.288 5.12h-183.296q-8.192 0-13.312-5.12t-5.12-12.288v-110.592q0-7.168 5.12-12.288t13.312-5.12h183.296q7.168 0 12.288 5.12t5.12 12.288zM877.568 173.568v109.568q0 8.192-5.12 13.312t-13.312 5.12h-182.272q-8.192 0-13.312-5.12t-5.12-13.312v-109.568q0-8.192 5.12-13.312t13.312-5.12h182.272q8.192 0 13.312 5.12t5.12 13.312zM584.704 612.864v109.568q0 8.192-5.12 13.312t-12.288 5.12h-183.296q-8.192 0-13.312-5.12t-5.12-13.312v-109.568q0-8.192 5.12-13.312t13.312-5.12h183.296q7.168 0 12.288 5.12t5.12 13.312zM877.568 392.704v110.592q0 7.168-5.12 12.288t-13.312 5.12h-182.272q-8.192 0-13.312-5.12t-5.12-12.288v-110.592q0-7.168 5.12-12.288t13.312-5.12h182.272q8.192 0 13.312 5.12t5.12 12.288zM877.568 612.864v109.568q0 8.192-5.12 13.312t-13.312 5.12h-182.272q-8.192 0-13.312-5.12t-5.12-13.312v-109.568q0-8.192 5.12-13.312t13.312-5.12h182.272q8.192 0 13.312 5.12t5.12 13.312zM951.296 795.136v-621.568q0-37.888-27.648-64.512t-64.512-26.624h-768q-36.864 0-64.512 26.624t-26.624 64.512v621.568q0 37.888 26.624 64.512t64.512 27.648h768q37.888 0 64.512-27.648t27.648-64.512z" />
<glyph unicode="&#xe813;" glyph-name="eraser" horiz-adv-x="1097" d="M512 155.136l192.512 220.16h-439.296l-192.512-220.16h439.296zM1090.56 770.56q9.216-19.456 6.144-40.96t-17.408-36.864l-512-585.728q-22.528-24.576-55.296-24.576h-439.296q-21.504 0-38.912 11.264t-27.648 31.744q-8.192 19.456-5.12 40.96t17.408 36.864l512 585.728q21.504 24.576 54.272 24.576h439.296q21.504 0 39.936-11.264t26.624-31.744z" />
<glyph unicode="&#xe814;" glyph-name="text-height" d="M996.352 155.136q19.456 0 24.576-10.24t-6.144-25.6l-72.704-92.16q-11.264-15.36-27.648-15.36t-27.648 15.36l-72.704 92.16q-11.264 15.36-6.144 25.6t23.552 10.24h46.080v585.728h-46.080q-18.432 0-23.552 10.24t6.144 25.6l72.704 92.16q11.264 15.36 27.648 15.36t27.648-15.36l72.704-92.16q11.264-15.36 6.144-25.6t-24.576-10.24h-45.056v-585.728h45.056zM46.080 886.272l30.72-15.36q7.168-3.072 120.832-3.072 25.6 0 75.776 1.024t74.752 1.024q21.504 0 61.44 0t61.44 0h167.936q3.072 0 12.288 0t11.264 0 9.216 1.024 10.24 5.12 8.192 10.24l24.576 1.024q2.048 0 7.168-1.024t8.192 0q1.024-63.488 1.024-191.488 0-46.080-2.048-62.464-22.528-8.192-38.912-10.24-14.336 24.576-31.744 72.704-1.024 5.12-6.144 27.648t-8.192 41.984-4.096 20.48q-3.072 4.096-7.168 7.168t-8.192 3.072-8.192 1.024-10.24 1.024-9.216-1.024q-9.216 0-37.888 1.024t-43.008 0-35.84-1.024-40.96-4.096q-5.12-46.080-4.096-76.8 0-54.272 1.024-222.208t1.024-260.096q0-9.216-1.024-40.96t0-52.224 7.168-38.912q22.528-12.288 70.656-24.576t68.608-21.504q2.048-22.528 2.048-28.672 0-8.192-1.024-16.384l-19.456-1.024q-44.032-1.024-124.928 5.12t-117.76 5.12q-28.672 0-87.040-5.12t-86.016-5.12q-2.048 29.696-2.048 29.696v5.12q9.216 15.36 34.816 24.576t56.32 17.408 45.056 15.36q10.24 23.552 10.24 218.112 0 58.368-1.024 173.056t-2.048 174.080v66.56q0 1.024 0 8.192t1.024 14.336-1.024 15.36-2.048 13.312-3.072 8.192q-6.144 7.168-92.16 7.168-18.432 0-53.248-7.168t-45.056-15.36q-11.264-7.168-19.456-40.96t-18.432-63.488-24.576-30.72q-23.552 15.36-31.744 25.6v218.112z" />
<glyph unicode="&#xe815;" glyph-name="brush" d="M922.624 960q39.936 0 70.656-26.624t29.696-66.56q0-35.84-25.6-86.016-189.44-359.424-266.24-430.080-55.296-52.224-123.904-52.224-72.704 0-123.904 53.248t-52.224 124.928q0 73.728 53.248 121.856l364.544 330.752q33.792 30.72 73.728 30.72zM403.456 369.152q22.528-43.008 60.416-74.752t86.016-43.008l1.024-40.96q2.048-121.856-73.728-197.632t-199.68-76.8q-69.632 0-123.904 26.624t-88.064 72.704-49.152 104.448-16.384 125.952q4.096-3.072 23.552-17.408t35.84-25.6 32.768-20.48 26.624-9.216q23.552 0 31.744 20.48 14.336 37.888 32.768 64.512t39.936 43.008 50.176 27.648 58.368 14.336 71.68 6.144z" />
<glyph unicode="&#xe816;" glyph-name="pencil" horiz-adv-x="878" d="M207.872 82.432l51.2 52.224-134.144 134.144-52.224-52.224v-61.44h73.728v-72.704h61.44zM505.856 612.864q0 12.288-12.288 12.288-5.12 0-9.216-4.096l-310.272-309.248q-4.096-4.096-4.096-10.24 0-12.288 13.312-12.288 5.12 0 9.216 4.096l310.272 309.248q3.072 4.096 3.072 10.24zM475.136 722.432l237.568-237.568-475.136-476.16h-237.568v238.592zM865.28 667.136q0-29.696-20.48-51.2l-95.232-95.232-237.568 238.592 95.232 94.208q20.48 21.504 51.2 21.504 29.696 0 52.224-21.504l134.144-134.144q20.48-22.528 20.48-52.224z" />
<glyph unicode="&#xe817;" glyph-name="minus" horiz-adv-x="804" d="M804.864 539.136v-109.568q0-22.528-16.384-38.912t-38.912-15.36h-694.272q-23.552 0-38.912 15.36t-16.384 38.912v109.568q0 23.552 16.384 38.912t38.912 16.384h694.272q22.528 0 38.912-16.384t16.384-38.912z" />
<glyph unicode="&#xe818;" glyph-name="picture" horiz-adv-x="1097" d="M365.568 631.296q0-46.080-31.744-77.824t-77.824-32.768-77.824 32.768-31.744 77.824 31.744 76.8 77.824 32.768 77.824-32.768 31.744-76.8zM951.296 411.136v-256h-804.864v109.568l182.272 183.296 92.16-91.136 291.84 291.84zM1005.568 813.568h-914.432q-7.168 0-12.288-5.12t-6.144-13.312v-694.272q0-8.192 6.144-13.312t12.288-5.12h914.432q7.168 0 13.312 5.12t5.12 13.312v694.272q0 7.168-5.12 13.312t-13.312 5.12zM1096.704 795.136v-694.272q0-37.888-26.624-64.512t-64.512-27.648h-914.432q-36.864 0-64.512 27.648t-26.624 64.512v694.272q0 37.888 26.624 64.512t64.512 27.648h914.432q37.888 0 64.512-27.648t26.624-64.512z" />
<glyph unicode="&#xe819;" glyph-name="file-image" horiz-adv-x="878" d="M838.656 742.912q16.384-16.384 27.648-43.008t11.264-51.2v-657.408q0-23.552-15.36-38.912t-38.912-16.384h-768q-23.552 0-38.912 16.384t-16.384 38.912v913.408q0 23.552 16.384 38.912t38.912 16.384h512q22.528 0 50.176-11.264t43.008-27.648zM584.704 882.176v-215.040h215.040q-5.12 16.384-12.288 23.552l-179.2 179.2q-6.144 7.168-23.552 12.288zM804.864 8.704v585.728h-237.568q-23.552 0-38.912 15.36t-16.384 38.912v238.592h-439.296v-878.592h732.16zM731.136 264.704v-182.272h-584.704v109.568l109.568 109.568 72.704-72.704 220.16 219.136zM256 375.296q-46.080 0-77.824 31.744t-31.744 77.824 31.744 77.824 77.824 31.744 77.824-31.744 31.744-77.824-31.744-77.824-77.824-31.744z" />
<glyph unicode="&#xe81a;" glyph-name="cw" horiz-adv-x="878" d="M877.568 813.568v-256q0-14.336-10.24-25.6t-26.624-11.264h-256q-23.552 0-32.768 23.552-10.24 22.528 7.168 38.912l78.848 78.848q-83.968 78.848-198.656 78.848-59.392 0-113.664-23.552t-93.184-62.464-63.488-93.184-22.528-113.664 22.528-113.664 63.488-93.184 93.184-62.464 113.664-23.552q67.584 0 128 29.696t102.4 83.968q4.096 6.144 13.312 7.168 8.192 0 14.336-5.12l77.824-78.848q5.12-4.096 6.144-11.264t-5.12-13.312q-61.44-75.776-150.528-116.736t-186.368-41.984q-89.088 0-171.008 34.816t-139.264 94.208-94.208 140.288-34.816 169.984 34.816 169.984 94.208 140.288 139.264 94.208 171.008 34.816q83.968 0 161.792-31.744t140.288-90.112l73.728 73.728q16.384 18.432 39.936 8.192 22.528-9.216 22.528-33.792z" />
<glyph unicode="&#xe81b;" glyph-name="ccw" horiz-adv-x="878" d="M877.568 448q0-89.088-34.816-169.984t-93.184-140.288-140.288-94.208-169.984-34.816q-98.304 0-187.392 41.984t-150.528 116.736q-4.096 6.144-4.096 13.312t5.12 11.264l77.824 78.848q6.144 5.12 14.336 5.12 9.216-1.024 13.312-7.168 41.984-54.272 102.4-83.968t129.024-29.696q59.392 0 112.64 23.552t94.208 62.464 62.464 93.184 22.528 113.664-22.528 113.664-62.464 93.184-94.208 62.464-112.64 23.552q-56.32 0-107.52-20.48t-92.16-58.368l78.848-78.848q17.408-16.384 8.192-38.912-10.24-23.552-33.792-23.552h-256q-15.36 0-25.6 11.264t-11.264 25.6v256q0 24.576 22.528 33.792 22.528 10.24 39.936-8.192l73.728-73.728q61.44 58.368 140.288 90.112t162.816 31.744q89.088 0 169.984-34.816t140.288-94.208 93.184-140.288 34.816-169.984z" />
<glyph unicode="&#xe900;" glyph-name="omega" d="M704 64h256l64 128v-256h-384v214.214c131.112 56.484 224 197.162 224 361.786 0 214.432-157.598 382.266-352 382.266-194.406 0-352-167.832-352-382.266 0-164.624 92.886-305.302 224-361.786v-214.214h-384v256l64-128h256v32.59c-187.63 66.46-320 227.402-320 415.41 0 247.424 229.23 448 512 448s512-200.576 512-448c0-188.008-132.37-348.95-320-415.41v-32.59z" />
<glyph unicode="&#xe901;" glyph-name="cancel-circle" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 32c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416zM672 704l-160-160-160 160-96-96 160-160-160-160 96-96 160 160 160-160 96 96-160 160 160 160z" />
<glyph unicode="&#xe904;" glyph-name="newspaper" d="M896 704v128h-896v-704c0-35.346 28.654-64 64-64h864c53.022 0 96 42.978 96 96v544h-128zM832 128h-768v640h768v-640zM128 640h640v-64h-640zM512 512h256v-64h-256zM512 384h256v-64h-256zM512 256h192v-64h-192zM128 512h320v-320h-320z" />
<glyph unicode="&#xe90f;" glyph-name="camera" d="M304 352c0-114.876 93.124-208 208-208s208 93.124 208 208-93.124 208-208 208-208-93.124-208-208zM960 704h-224c-16 64-32 128-96 128h-256c-64 0-80-64-96-128h-224c-35.2 0-64-28.8-64-64v-576c0-35.2 28.8-64 64-64h896c35.2 0 64 28.8 64 64v576c0 35.2-28.8 64-64 64zM512 68c-156.85 0-284 127.148-284 284 0 156.85 127.15 284 284 284 156.852 0 284-127.15 284-284 0-156.852-127.146-284-284-284zM960 512h-128v64h128v-64z" />
<glyph unicode="&#xe911;" glyph-name="music" d="M960 960h64v-736c0-88.366-100.29-160-224-160s-224 71.634-224 160c0 88.368 100.29 160 224 160 62.684 0 119.342-18.4 160-48.040v368.040l-512-113.778v-494.222c0-88.366-100.288-160-224-160s-224 71.634-224 160c0 88.368 100.288 160 224 160 62.684 0 119.342-18.4 160-48.040v624.040l576 128z" />
<glyph unicode="&#xe912;" glyph-name="play" d="M981.188 799.892c-143.632 20.65-302.332 32.108-469.186 32.108-166.86 0-325.556-11.458-469.194-32.108-27.53-107.726-42.808-226.75-42.808-351.892 0-125.14 15.278-244.166 42.808-351.89 143.638-20.652 302.336-32.11 469.194-32.11 166.854 0 325.552 11.458 469.186 32.11 27.532 107.724 42.812 226.75 42.812 351.89 0 125.142-15.28 244.166-42.812 351.892zM384.002 256v384l320-192-320-192z" />
<glyph unicode="&#xe914;" glyph-name="video-camera" d="M384 672c0 88.366 71.634 160 160 160s160-71.634 160-160c0-88.366-71.634-160-160-160s-160 71.634-160 160zM0 672c0 88.366 71.634 160 160 160s160-71.634 160-160c0-88.366-71.634-160-160-160s-160 71.634-160 160zM768 352v96c0 35.2-28.8 64-64 64h-640c-35.2 0-64-28.8-64-64v-320c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v96l256-160v448l-256-160zM640 192h-512v192h512v-192z" />
<glyph unicode="&#xe92b;" glyph-name="file-zip" d="M917.806 730.924c-22.208 30.292-53.174 65.7-87.178 99.704s-69.412 64.964-99.704 87.178c-51.574 37.82-76.592 42.194-90.924 42.194h-496c-44.112 0-80-35.888-80-80v-864c0-44.112 35.884-80 80-80h736c44.112 0 80 35.888 80 80v624c0 14.332-4.372 39.35-42.194 90.924v0 0zM785.374 785.374c30.7-30.7 54.8-58.398 72.58-81.374h-153.954v153.946c22.98-17.78 50.678-41.878 81.374-72.572v0 0zM896 16c0-8.672-7.328-16-16-16h-736c-8.672 0-16 7.328-16 16v864c0 8.672 7.328 16 16 16 0 0 495.956 0.002 496 0v-224c0-17.672 14.322-32 32-32h224v-624zM256 896h128v-64h-128v64zM384 832h128v-64h-128v64zM256 768h128v-64h-128v64zM384 704h128v-64h-128v64zM256 640h128v-64h-128v64zM384 576h128v-64h-128v64zM256 512h128v-64h-128v64zM384 448h128v-64h-128v64zM256 112c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-80v64h-128v-272zM448 192v-64h-128v64h128z" />
<glyph unicode="&#xe92e;" glyph-name="stack" d="M1024 640l-512 256-512-256 512-256 512 256zM512 811.030l342.058-171.030-342.058-171.030-342.058 171.030 342.058 171.030zM921.444 499.278l102.556-51.278-512-256-512 256 102.556 51.278 409.444-204.722zM921.444 307.278l102.556-51.278-512-256-512 256 102.556 51.278 409.444-204.722z" />
<glyph unicode="&#xe93f;" glyph-name="credit-card" d="M928 832h-832c-52.8 0-96-43.2-96-96v-576c0-52.8 43.2-96 96-96h832c52.8 0 96 43.2 96 96v576c0 52.8-43.2 96-96 96zM96 768h832c17.346 0 32-14.654 32-32v-96h-896v96c0 17.346 14.654 32 32 32zM928 128h-832c-17.346 0-32 14.654-32 32v288h896v-288c0-17.346-14.654-32-32-32zM128 320h64v-128h-64zM256 320h64v-128h-64zM384 320h64v-128h-64z" />
<glyph unicode="&#xe944;" glyph-name="address-book" d="M192 960v-1024h768v1024h-768zM576 703.67c70.51 0 127.67-57.16 127.67-127.67s-57.16-127.67-127.67-127.67-127.67 57.16-127.67 127.67 57.16 127.67 127.67 127.67v0zM768 192h-384v64c0 70.696 57.306 128 128 128v0h128c70.696 0 128-57.304 128-128v-64zM64 896h96v-192h-96v192zM64 640h96v-192h-96v192zM64 384h96v-192h-96v192zM64 128h96v-192h-96v192z" />
<glyph unicode="&#xe945;" glyph-name="envelop" d="M928 832h-832c-52.8 0-96-43.2-96-96v-640c0-52.8 43.2-96 96-96h832c52.8 0 96 43.2 96 96v640c0 52.8-43.2 96-96 96zM398.74 409.628l-270.74-210.892v501.642l270.74-290.75zM176.38 704h671.24l-335.62-252-335.62 252zM409.288 398.302l102.712-110.302 102.71 110.302 210.554-270.302h-626.528l210.552 270.302zM625.26 409.628l270.74 290.75v-501.642l-270.74 210.892z" />
<glyph unicode="&#xe947;" glyph-name="location" d="M512 960c-176.732 0-320-143.268-320-320 0-320 320-704 320-704s320 384 320 704c0 176.732-143.27 320-320 320zM512 448c-106.040 0-192 85.96-192 192s85.96 192 192 192 192-85.96 192-192-85.96-192-192-192z" />
<glyph unicode="&#xe95c;" glyph-name="drawer" d="M1016.988 307.99l-256 320c-6.074 7.592-15.266 12.010-24.988 12.010h-448c-9.72 0-18.916-4.418-24.988-12.010l-256-320c-4.538-5.674-7.012-12.724-7.012-19.99v-288c0-35.346 28.654-64 64-64h896c35.348 0 64 28.654 64 64v288c0 7.266-2.472 14.316-7.012 19.99zM960 256h-224l-128-128h-192l-128 128h-224v20.776l239.38 299.224h417.24l239.38-299.224v-20.776zM736 448h-448c-17.672 0-32 14.328-32 32s14.328 32 32 32h448c17.674 0 32-14.328 32-32s-14.326-32-32-32zM800 320h-576c-17.672 0-32 14.326-32 32s14.328 32 32 32h576c17.674 0 32-14.326 32-32s-14.326-32-32-32z" />
<glyph unicode="&#xe960;" glyph-name="download" d="M512 384l256 256h-192v256h-128v-256h-192zM744.726 488.728l-71.74-71.742 260.080-96.986-421.066-157.018-421.066 157.018 260.080 96.986-71.742 71.742-279.272-104.728v-256l512-192 512 192v256z" />
<glyph unicode="&#xe961;" glyph-name="upload" d="M448 384h128v256h192l-256 256-256-256h192zM640 528v-98.712l293.066-109.288-421.066-157.018-421.066 157.018 293.066 109.288v98.712l-384-144v-256l512-192 512 192v256z" />
<glyph unicode="&#xe977;" glyph-name="quotes-left" d="M225 512c123.712 0 224-100.29 224-224 0-123.712-100.288-224-224-224s-224 100.288-224 224l-1 32c0 247.424 200.576 448 448 448v-128c-85.474 0-165.834-33.286-226.274-93.726-11.634-11.636-22.252-24.016-31.83-37.020 11.438 1.8 23.16 2.746 35.104 2.746zM801 512c123.71 0 224-100.29 224-224 0-123.712-100.29-224-224-224s-224 100.288-224 224l-1 32c0 247.424 200.576 448 448 448v-128c-85.474 0-165.834-33.286-226.274-93.726-11.636-11.636-22.254-24.016-31.832-37.020 11.44 1.8 23.16 2.746 35.106 2.746z" />
<glyph unicode="&#xe98b;" glyph-name="enlarge2" d="M1024 960v-416l-160 160-192-192-96 96 192 192-160 160zM448 288l-192-192 160-160h-416v416l160-160 192 192z" />
<glyph unicode="&#xe98c;" glyph-name="shrink2" d="M448 384v-416l-160 160-192-192-96 96 192 192-160 160zM1024 864l-192-192 160-160h-416v416l160-160 192 192z" />
<glyph unicode="&#xe98f;" glyph-name="lock" d="M592 512h-16v192c0 105.87-86.13 192-192 192h-128c-105.87 0-192-86.13-192-192v-192h-16c-26.4 0-48-21.6-48-48v-480c0-26.4 21.6-48 48-48h544c26.4 0 48 21.6 48 48v480c0 26.4-21.6 48-48 48zM192 704c0 35.29 28.71 64 64 64h128c35.29 0 64-28.71 64-64v-192h-256v192z" />
<glyph unicode="&#xe990;" glyph-name="unlocked" d="M768 896c105.87 0 192-86.13 192-192v-192h-128v192c0 35.29-28.71 64-64 64h-128c-35.29 0-64-28.71-64-64v-192h16c26.4 0 48-21.6 48-48v-480c0-26.4-21.6-48-48-48h-544c-26.4 0-48 21.6-48 48v480c0 26.4 21.6 48 48 48h400v192c0 105.87 86.13 192 192 192h128z" />
<glyph unicode="&#xe991;" glyph-name="wrench" d="M1002.934 142.124l-460.552 394.76c21.448 40.298 33.618 86.282 33.618 135.116 0 159.058-128.942 288-288 288-29.094 0-57.172-4.332-83.646-12.354l166.39-166.39c24.89-24.89 24.89-65.62 0-90.51l-101.49-101.49c-24.89-24.89-65.62-24.89-90.51 0l-166.39 166.39c-8.022-26.474-12.354-54.552-12.354-83.646 0-159.058 128.942-288 288-288 48.834 0 94.818 12.17 135.116 33.62l394.76-460.552c22.908-26.724 62.016-28.226 86.904-3.338l101.492 101.492c24.888 24.888 23.386 63.994-3.338 86.902z" />
<glyph unicode="&#xe9ce;" glyph-name="eye" d="M512 768c-223.318 0-416.882-130.042-512-320 95.118-189.958 288.682-320 512-320 223.312 0 416.876 130.042 512 320-95.116 189.958-288.688 320-512 320zM764.45 598.296c60.162-38.374 111.142-89.774 149.434-150.296-38.292-60.522-89.274-111.922-149.436-150.296-75.594-48.218-162.89-73.704-252.448-73.704-89.56 0-176.858 25.486-252.452 73.704-60.158 38.372-111.138 89.772-149.432 150.296 38.292 60.524 89.274 111.924 149.434 150.296 3.918 2.5 7.876 4.922 11.86 7.3-9.96-27.328-15.41-56.822-15.41-87.596 0-141.382 114.616-256 256-256 141.382 0 256 114.618 256 256 0 30.774-5.452 60.268-15.408 87.598 3.978-2.378 7.938-4.802 11.858-7.302v0zM512 544c0-53.020-42.98-96-96-96s-96 42.98-96 96 42.98 96 96 96 96-42.982 96-96z" />
<glyph unicode="&#xe9d1;" glyph-name="eye-blocked" d="M945.942 945.942c-18.746 18.744-49.136 18.744-67.882 0l-202.164-202.164c-51.938 15.754-106.948 24.222-163.896 24.222-223.318 0-416.882-130.042-512-320 41.122-82.124 100.648-153.040 173.022-207.096l-158.962-158.962c-18.746-18.746-18.746-49.136 0-67.882 9.372-9.374 21.656-14.060 33.94-14.060s24.568 4.686 33.942 14.058l864 864c18.744 18.746 18.744 49.138 0 67.884zM416 640c42.24 0 78.082-27.294 90.92-65.196l-121.724-121.724c-37.902 12.838-65.196 48.68-65.196 90.92 0 53.020 42.98 96 96 96zM110.116 448c38.292 60.524 89.274 111.924 149.434 150.296 3.918 2.5 7.876 4.922 11.862 7.3-9.962-27.328-15.412-56.822-15.412-87.596 0-54.89 17.286-105.738 46.7-147.418l-60.924-60.924c-52.446 36.842-97.202 83.882-131.66 138.342zM768 518c0 27.166-4.256 53.334-12.102 77.898l-321.808-321.808c24.568-7.842 50.742-12.090 77.91-12.090 141.382 0 256 114.618 256 256zM830.026 670.026l-69.362-69.362c1.264-0.786 2.53-1.568 3.786-2.368 60.162-38.374 111.142-89.774 149.434-150.296-38.292-60.522-89.274-111.922-149.436-150.296-75.594-48.218-162.89-73.704-252.448-73.704-38.664 0-76.902 4.76-113.962 14.040l-76.894-76.894c59.718-21.462 123.95-33.146 190.856-33.146 223.31 0 416.876 130.042 512 320-45.022 89.916-112.118 166.396-193.974 222.026z" />
<glyph unicode="&#xe9df;" glyph-name="happy" d="M512-64c282.77 0 512 229.23 512 512s-229.23 512-512 512-512-229.23-512-512 229.23-512 512-512zM512 864c229.75 0 416-186.25 416-416s-186.25-416-416-416-416 186.25-416 416 186.25 416 416 416zM512 361.24c115.95 0 226.23 30.806 320 84.92-14.574-178.438-153.128-318.16-320-318.16-166.868 0-305.422 139.872-320 318.304 93.77-54.112 204.050-85.064 320-85.064zM256 608c0 53.019 28.654 96 64 96s64-42.981 64-96c0-53.019-28.654-96-64-96s-64 42.981-64 96zM640 608c0 53.019 28.654 96 64 96s64-42.981 64-96c0-53.019-28.654-96-64-96s-64 42.981-64 96z" />
<glyph unicode="&#xea4e;" glyph-name="command" d="M736 64c-88.224 0-160 71.776-160 160v96h-128v-96c0-88.224-71.776-160-160-160s-160 71.776-160 160 71.776 160 160 160h96v128h-96c-88.224 0-160 71.776-160 160s71.776 160 160 160 160-71.776 160-160v-96h128v96c0 88.224 71.776 160 160 160s160-71.776 160-160-71.776-160-160-160h-96v-128h96c88.224 0 160-71.776 160-160s-71.774-160-160-160zM640 320v-96c0-52.934 43.066-96 96-96s96 43.066 96 96-43.066 96-96 96h-96zM288 320c-52.934 0-96-43.066-96-96s43.066-96 96-96 96 43.066 96 96v96h-96zM448 384h128v128h-128v-128zM640 576h96c52.934 0 96 43.066 96 96s-43.066 96-96 96-96-43.066-96-96v-96zM288 768c-52.934 0-96-43.066-96-96s43.066-96 96-96h96v96c0 52.934-43.064 96-96 96z" />
<glyph unicode="&#xea5c;" glyph-name="font2" d="M799.596 943.792c-90.526 0-148.62 16.208-241.848 16.208-301.284 0-441.792-171.584-441.792-345.872 0-102.678 48.64-136.458 144.564-136.458-6.758 14.864-18.914 31.080-18.914 104.034 0 204.010 77.006 263.458 175.636 267.51 0 0-80.918-793.374-315.778-888.542v-24.672h316.594l108.026 512h197.844l44.072 128h-214.908l51.944 246.19c59.446-12.156 117.542-24.316 167.532-24.316 62.148 0 118.894 18.914 149.968 162.126-37.826-12.16-78.362-16.208-122.94-16.208z" />
<glyph unicode="&#xea65;" glyph-name="strikethrough" d="M1024 448v-64h-234.506c27.504-38.51 42.506-82.692 42.506-128 0-70.878-36.66-139.026-100.58-186.964-59.358-44.518-137.284-69.036-219.42-69.036-82.138 0-160.062 24.518-219.42 69.036-63.92 47.938-100.58 116.086-100.58 186.964h128c0-69.382 87.926-128 192-128s192 58.618 192 128c0 69.382-87.926 128-192 128h-512v64h299.518c-2.338 1.654-4.656 3.324-6.938 5.036-63.92 47.94-100.58 116.086-100.58 186.964s36.66 139.024 100.58 186.964c59.358 44.518 137.282 69.036 219.42 69.036 82.136 0 160.062-24.518 219.42-69.036 63.92-47.94 100.58-116.086 100.58-186.964h-128c0 69.382-87.926 128-192 128s-192-58.618-192-128c0-69.382 87.926-128 192-128 78.978 0 154.054-22.678 212.482-64h299.518z" />
<glyph unicode="&#xea67;" glyph-name="sigma" d="M941.606 225.292l44.394 94.708h38l-64-384h-960v74.242l331.546 391.212-331.546 331.546v227h980l44-256h-34.376l-18.72 38.88c-35.318 73.364-61.904 89.12-138.904 89.12h-662l353.056-353.056-297.42-350.944h542.364c116.008 0 146.648 41.578 173.606 97.292z" />
<glyph unicode="&#xea68;" glyph-name="sigma2" d="M941.606 225.292l44.394 94.708h38l-64-384h-960v74.242l331.546 391.212-331.546 331.546v227h980l44-256h-34.376l-18.72 38.88c-35.318 73.364-61.904 89.12-138.904 89.12h-662l353.056-353.056-297.42-350.944h542.364c116.008 0 146.648 41.578 173.606 97.292z" />
<glyph unicode="&#xeade;" glyph-name="libreoffice" d="M534.626 937.372c-12.444 12.444-37.026 22.628-54.626 22.628h-384c-17.6 0-32-14.4-32-32v-960c0-17.6 14.4-32 32-32h768c17.6 0 32 14.4 32 32v576c0 17.6-10.182 42.182-22.626 54.626l-338.748 338.746zM832 0h-704v896h351.158c2.916-0.48 8.408-2.754 10.81-4.478l337.556-337.554c1.722-2.402 3.996-7.894 4.476-10.81v-543.158zM864 960h-192c-17.6 0-21.818-10.182-9.374-22.626l210.746-210.746c12.446-12.446 22.628-8.228 22.628 9.372v192c0 17.6-14.4 32-32 32z" />
<glyph unicode="&#xf00e;" glyph-name="search-plus" horiz-adv-x="951" d="M585.143 493.714v-36.571q0-7.429-5.429-12.857t-12.857-5.429h-128v-128q0-7.429-5.429-12.857t-12.857-5.429h-36.571q-7.429 0-12.857 5.429t-5.429 12.857v128h-128q-7.429 0-12.857 5.429t-5.429 12.857v36.571q0 7.429 5.429 12.857t12.857 5.429h128v128q0 7.429 5.429 12.857t12.857 5.429h36.571q7.429 0 12.857-5.429t5.429-12.857v-128h128q7.429 0 12.857-5.429t5.429-12.857zM658.286 475.428q0 105.714-75.143 180.857t-180.857 75.143-180.857-75.143-75.143-180.857 75.143-180.857 180.857-75.143 180.857 75.143 75.143 180.857zM950.857 0q0-30.286-21.429-51.714t-51.714-21.429q-30.857 0-51.429 21.714l-196 195.429q-102.286-70.857-228-70.857-81.714 0-156.286 31.714t-128.571 85.714-85.714 128.571-31.714 156.286 31.714 156.286 85.714 128.571 128.571 85.714 156.286 31.714 156.286-31.714 128.571-85.714 85.714-128.571 31.714-156.286q0-125.714-70.857-228l196-196q21.143-21.143 21.143-51.429z" />
<glyph unicode="&#xf010;" glyph-name="search-minus" horiz-adv-x="951" d="M585.143 493.714v-36.571q0-7.429-5.429-12.857t-12.857-5.429h-329.143q-7.429 0-12.857 5.429t-5.429 12.857v36.571q0 7.429 5.429 12.857t12.857 5.429h329.143q7.429 0 12.857-5.429t5.429-12.857zM658.286 475.428q0 105.714-75.143 180.857t-180.857 75.143-180.857-75.143-75.143-180.857 75.143-180.857 180.857-75.143 180.857 75.143 75.143 180.857zM950.857 0q0-30.286-21.429-51.714t-51.714-21.429q-30.857 0-51.429 21.714l-196 195.429q-102.286-70.857-228-70.857-81.714 0-156.286 31.714t-128.571 85.714-85.714 128.571-31.714 156.286 31.714 156.286 85.714 128.571 128.571 85.714 156.286 31.714 156.286-31.714 128.571-85.714 85.714-128.571 31.714-156.286q0-125.714-70.857-228l196-196q21.143-21.143 21.143-51.429z" />
<glyph unicode="&#xf014;" glyph-name="trash-o" horiz-adv-x="805" d="M292.571 530.286v-329.143q0-8-5.143-13.143t-13.143-5.143h-36.571q-8 0-13.143 5.143t-5.143 13.143v329.143q0 8 5.143 13.143t13.143 5.143h36.571q8 0 13.143-5.143t5.143-13.143zM438.857 530.286v-329.143q0-8-5.143-13.143t-13.143-5.143h-36.571q-8 0-13.143 5.143t-5.143 13.143v329.143q0 8 5.143 13.143t13.143 5.143h36.571q8 0 13.143-5.143t5.143-13.143zM585.143 530.286v-329.143q0-8-5.143-13.143t-13.143-5.143h-36.571q-8 0-13.143 5.143t-5.143 13.143v329.143q0 8 5.143 13.143t13.143 5.143h36.571q8 0 13.143-5.143t5.143-13.143zM658.286 116.571v541.714h-512v-541.714q0-12.571 4-23.143t8.286-15.429 6-4.857h475.429q1.714 0 6 4.857t8.286 15.429 4 23.143zM274.286 731.428h256l-27.429 66.857q-4 5.143-9.714 6.286h-181.143q-5.714-1.143-9.714-6.286zM804.571 713.143v-36.571q0-8-5.143-13.143t-13.143-5.143h-54.857v-541.714q0-47.429-26.857-82t-64.571-34.571h-475.429q-37.714 0-64.571 33.429t-26.857 80.857v544h-54.857q-8 0-13.143 5.143t-5.143 13.143v36.571q0 8 5.143 13.143t13.143 5.143h176.571l40 95.429q8.571 21.143 30.857 36t45.143 14.857h182.857q22.857 0 45.143-14.857t30.857-36l40-95.429h176.571q8 0 13.143-5.143t5.143-13.143z" />
<glyph unicode="&#xf039;" glyph-name="align-justify" d="M1024 182.857v-73.143q0-14.857-10.857-25.714t-25.714-10.857h-950.857q-14.857 0-25.714 10.857t-10.857 25.714v73.143q0 14.857 10.857 25.714t25.714 10.857h950.857q14.857 0 25.714-10.857t10.857-25.714zM1024 402.286v-73.143q0-14.857-10.857-25.714t-25.714-10.857h-950.857q-14.857 0-25.714 10.857t-10.857 25.714v73.143q0 14.857 10.857 25.714t25.714 10.857h950.857q14.857 0 25.714-10.857t10.857-25.714zM1024 621.714v-73.143q0-14.857-10.857-25.714t-25.714-10.857h-950.857q-14.857 0-25.714 10.857t-10.857 25.714v73.143q0 14.857 10.857 25.714t25.714 10.857h950.857q14.857 0 25.714-10.857t10.857-25.714zM1024 841.143v-73.143q0-14.857-10.857-25.714t-25.714-10.857h-950.857q-14.857 0-25.714 10.857t-10.857 25.714v73.143q0 14.857 10.857 25.714t25.714 10.857h950.857q14.857 0 25.714-10.857t10.857-25.714z" />
<glyph unicode="&#xf07d;" glyph-name="arrows-v" horiz-adv-x="439" d="M402.286 768q0-14.857-10.857-25.714t-25.714-10.857h-73.143v-585.143h73.143q14.857 0 25.714-10.857t10.857-25.714-10.857-25.714l-146.286-146.286q-10.857-10.857-25.714-10.857t-25.714 10.857l-146.286 146.286q-10.857 10.857-10.857 25.714t10.857 25.714 25.714 10.857h73.143v585.143h-73.143q-14.857 0-25.714 10.857t-10.857 25.714 10.857 25.714l146.286 146.286q10.857 10.857 25.714 10.857t25.714-10.857l146.286-146.286q10.857-10.857 10.857-25.714z" />
<glyph unicode="&#xf108;" glyph-name="desktop" horiz-adv-x="1097" d="M1024 384v475.429q0 7.429-5.429 12.857t-12.857 5.429h-914.286q-7.429 0-12.857-5.429t-5.429-12.857v-475.429q0-7.429 5.429-12.857t12.857-5.429h914.286q7.429 0 12.857 5.429t5.429 12.857zM1097.143 859.428v-621.714q0-37.714-26.857-64.571t-64.571-26.857h-310.857q0-21.143 9.143-44.286t18.286-40.571 9.143-24.857q0-14.857-10.857-25.714t-25.714-10.857h-292.571q-14.857 0-25.714 10.857t-10.857 25.714q0 8 9.143 25.143t18.286 40 9.143 44.571h-310.857q-37.714 0-64.571 26.857t-26.857 64.571v621.714q0 37.714 26.857 64.571t64.571 26.857h914.286q37.714 0 64.571-26.857t26.857-64.571z" />
<glyph unicode="&#xf10a;" glyph-name="tablet" horiz-adv-x="658" d="M365.714 146.286q0 14.857-10.857 25.714t-25.714 10.857-25.714-10.857-10.857-25.714 10.857-25.714 25.714-10.857 25.714 10.857 10.857 25.714zM585.143 237.714v548.571q0 7.429-5.429 12.857t-12.857 5.429h-475.429q-7.429 0-12.857-5.429t-5.429-12.857v-548.571q0-7.429 5.429-12.857t12.857-5.429h475.429q7.429 0 12.857 5.429t5.429 12.857zM658.286 786.286v-621.714q0-37.714-26.857-64.571t-64.571-26.857h-475.429q-37.714 0-64.571 26.857t-26.857 64.571v621.714q0 37.714 26.857 64.571t64.571 26.857h475.429q37.714 0 64.571-26.857t26.857-64.571z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,47 +0,0 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
// 用 createMenu 方法创建菜单
E.createMenu(function (check) {
// 定义菜单id不要和其他菜单id重复。编辑器自带的所有菜单id可通过『参数配置-自定义菜单』一节查看
var menuId = 'attach';
// check将检查菜单配置『参数配置-自定义菜单』一节描述中是否该菜单id如果没有则忽略下面的代码。
if (!check(menuId)) {
return;
}
// this 指向 editor 对象自身
var editor = this;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor, // 编辑器对象
id: menuId, // 菜单id
title: '', // 菜单标题
// 正常状态和选中状态下的dom对象样式需要自定义
$domNormal: $('<a href="#" tabindex="-1"><i class="fa fa-paperclip" aria-hidden="true" name="release"></i></a>'),
$domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="fa fa-paperclip" aria-hidden="true" name="release"></i></a>')
});
// 菜单正常状态下,点击将触发该事件
menu.clickEvent = function (e) {
$("#uploadAttachModal").modal("show");
};
// 菜单选中状态下,点击将触发该事件
menu.clickEventSelected = function (e) {
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
})();

View File

@ -1,47 +0,0 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
// 用 createMenu 方法创建菜单
E.createMenu(function (check) {
// 定义菜单id不要和其他菜单id重复。编辑器自带的所有菜单id可通过『参数配置-自定义菜单』一节查看
var menuId = 'history';
// check将检查菜单配置『参数配置-自定义菜单』一节描述中是否该菜单id如果没有则忽略下面的代码。
if (!check(menuId)) {
return;
}
// this 指向 editor 对象自身
var editor = this;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor, // 编辑器对象
id: menuId, // 菜单id
title: '', // 菜单标题
// 正常状态和选中状态下的dom对象样式需要自定义
$domNormal: $('<a href="#" tabindex="-1"><i class="fa fa-history" aria-hidden="true" name="history"></i></a>'),
$domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="fa fa-history" aria-hidden="true" name="history"></i></a>')
});
// 菜单正常状态下,点击将触发该事件
menu.clickEvent = function (e) {
window.documentHistory();
};
// 菜单选中状态下,点击将触发该事件
menu.clickEventSelected = function (e) {
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
})();

View File

@ -1,47 +0,0 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
// 用 createMenu 方法创建菜单
E.createMenu(function (check) {
// 定义菜单id不要和其他菜单id重复。编辑器自带的所有菜单id可通过『参数配置-自定义菜单』一节查看
var menuId = 'release';
// check将检查菜单配置『参数配置-自定义菜单』一节描述中是否该菜单id如果没有则忽略下面的代码。
if (!check(menuId)) {
return;
}
// this 指向 editor 对象自身
var editor = this;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor, // 编辑器对象
id: menuId, // 菜单id
title: '', // 菜单标题
// 正常状态和选中状态下的dom对象样式需要自定义
$domNormal: $('<a href="#" tabindex="-1"><i class="fa fa-cloud-upload" aria-hidden="true" name="release"></i></a>'),
$domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="fa fa-cloud-upload" aria-hidden="true" name="release"></i></a>')
});
// 菜单正常状态下,点击将触发该事件
menu.clickEvent = function (e) {
window.releaseBook();
};
// 菜单选中状态下,点击将触发该事件
menu.clickEventSelected = function (e) {
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
})();

View File

@ -1,47 +0,0 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
// 用 createMenu 方法创建菜单
E.createMenu(function (check) {
// 定义菜单id不要和其他菜单id重复。编辑器自带的所有菜单id可通过『参数配置-自定义菜单』一节查看
var menuId = 'save';
// check将检查菜单配置『参数配置-自定义菜单』一节描述中是否该菜单id如果没有则忽略下面的代码。
if (!check(menuId)) {
return;
}
// this 指向 editor 对象自身
var editor = this;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor, // 编辑器对象
id: menuId, // 菜单id
title: '', // 菜单标题
// 正常状态和选中状态下的dom对象样式需要自定义
$domNormal: $('<a href="#" tabindex="-1"><i class="fa fa-save" aria-hidden="true" name="save"></i></a>'),
$domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="fa fa-save" aria-hidden="true" name="save"></i></a>')
});
// 菜单正常状态下,点击将触发该事件
menu.clickEvent = function (e) {
window.saveDocument();
};
// 菜单选中状态下,点击将触发该事件
menu.clickEventSelected = function (e) {
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
})();

View File

@ -0,0 +1,14 @@
/**
* @description
* @author wangfupeng
*/
import './assets/style/common.less';
import './assets/style/icon.less';
import './assets/style/menus.less';
import './assets/style/text.less';
import './assets/style/panel.less';
import './assets/style/droplist.less';
import './utils/polyfill';
import Editor from './editor/index';
export * from './menus/menu-constructors/index';
export default Editor;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

43
sync_host.sh 100644
View File

@ -0,0 +1,43 @@
#!/bin/bash
set -eux
if ! [ -x "$(command -v rclone)" ]; then
apt install rclone -y
else
whereis rclone
fi
export MINDOC_SYNC="${MINDOC_SYNC:=}"
export SYNC_LIST="${SYNC_LIST:=}"
export SYNC_ACTION="${SYNC_ACTION:=sync --dry-run}"
export HOST_DIR=/mindoc-sync-host
export DOCKER_DIR=/mindoc
function doSyncCopy() {
if [ -d "${1}" ]
then
rclone $SYNC_ACTION --progress --exclude .git* --exclude .git/** "${1}" "${2}"
fi
}
function doSync() {
case $MINDOC_SYNC in
"docker2host")
doSyncCopy "${DOCKER_DIR}/${1}" "${HOST_DIR}/${1}"
;;
"host2docker")
doSyncCopy "${HOST_DIR}/${1}" "${DOCKER_DIR}/${1}"
;;
*)
printenv | grep MINDOC_SYNC
;;
esac
}
export IFS=";"
if ! [ -z "${SYNC_LIST}" ]; then
for item in $SYNC_LIST; do
doSync "${item}"
done
fi

571
utils/docx2md.go 100644
View File

@ -0,0 +1,571 @@
// https://github.com/mattn/docx2md
// License MIT
package utils
import (
"archive/zip"
"bytes"
"encoding/base64"
"encoding/xml"
"errors"
_ "flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
_ "runtime"
"strconv"
"strings"
"github.com/mattn/go-runewidth"
)
// Relationship is
type Relationship struct {
Text string `xml:",chardata"`
ID string `xml:"Id,attr"`
Type string `xml:"Type,attr"`
Target string `xml:"Target,attr"`
TargetMode string `xml:"TargetMode,attr"`
}
// Relationships is
type Relationships struct {
XMLName xml.Name `xml:"Relationships"`
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
Relationship []Relationship `xml:"Relationship"`
}
// TextVal is
type TextVal struct {
Text string `xml:",chardata"`
Val string `xml:"val,attr"`
}
// NumberingLvl is
type NumberingLvl struct {
Text string `xml:",chardata"`
Ilvl string `xml:"ilvl,attr"`
Tplc string `xml:"tplc,attr"`
Tentative string `xml:"tentative,attr"`
Start TextVal `xml:"start"`
NumFmt TextVal `xml:"numFmt"`
LvlText TextVal `xml:"lvlText"`
LvlJc TextVal `xml:"lvlJc"`
PPr struct {
Text string `xml:",chardata"`
Ind struct {
Text string `xml:",chardata"`
Left string `xml:"left,attr"`
Hanging string `xml:"hanging,attr"`
} `xml:"ind"`
} `xml:"pPr"`
RPr struct {
Text string `xml:",chardata"`
U struct {
Text string `xml:",chardata"`
Val string `xml:"val,attr"`
} `xml:"u"`
RFonts struct {
Text string `xml:",chardata"`
Hint string `xml:"hint,attr"`
} `xml:"rFonts"`
} `xml:"rPr"`
}
// Numbering is
type Numbering struct {
XMLName xml.Name `xml:"numbering"`
Text string `xml:",chardata"`
Wpc string `xml:"wpc,attr"`
Cx string `xml:"cx,attr"`
Cx1 string `xml:"cx1,attr"`
Mc string `xml:"mc,attr"`
O string `xml:"o,attr"`
R string `xml:"r,attr"`
M string `xml:"m,attr"`
V string `xml:"v,attr"`
Wp14 string `xml:"wp14,attr"`
Wp string `xml:"wp,attr"`
W10 string `xml:"w10,attr"`
W string `xml:"w,attr"`
W14 string `xml:"w14,attr"`
W15 string `xml:"w15,attr"`
W16se string `xml:"w16se,attr"`
Wpg string `xml:"wpg,attr"`
Wpi string `xml:"wpi,attr"`
Wne string `xml:"wne,attr"`
Wps string `xml:"wps,attr"`
Ignorable string `xml:"Ignorable,attr"`
AbstractNum []struct {
Text string `xml:",chardata"`
AbstractNumID string `xml:"abstractNumId,attr"`
RestartNumberingAfterBreak string `xml:"restartNumberingAfterBreak,attr"`
Nsid TextVal `xml:"nsid"`
MultiLevelType TextVal `xml:"multiLevelType"`
Tmpl TextVal `xml:"tmpl"`
Lvl []NumberingLvl `xml:"lvl"`
} `xml:"abstractNum"`
Num []struct {
Text string `xml:",chardata"`
NumID string `xml:"numId,attr"`
AbstractNumID TextVal `xml:"abstractNumId"`
} `xml:"num"`
}
type file struct {
rels Relationships
num Numbering
r *zip.ReadCloser
embed bool
list map[string]int
name string
}
// Node is
type Node struct {
XMLName xml.Name
Attrs []xml.Attr `xml:"-"`
Content []byte `xml:",innerxml"`
Nodes []Node `xml:",any"`
}
// UnmarshalXML is
func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
n.Attrs = start.Attr
type node Node
return d.DecodeElement((*node)(n), &start)
}
func escape(s, set string) string {
replacer := []string{}
for _, r := range []rune(set) {
rs := string(r)
replacer = append(replacer, rs, `\`+rs)
}
return strings.NewReplacer(replacer...).Replace(s)
}
func (zf *file) extract(rel *Relationship, w io.Writer) error {
err := os.MkdirAll(
filepath.Join("uploads",
strings.TrimSuffix(zf.name, ".docx"),
filepath.Dir(rel.Target)),
0755)
if err != nil {
return err
}
for _, f := range zf.r.File {
if f.Name != "word/"+rel.Target {
continue
}
rc, err := f.Open()
if err != nil {
return err
}
defer rc.Close()
b := make([]byte, f.UncompressedSize64)
n, err := rc.Read(b)
if err != nil && err != io.EOF {
return err
}
if zf.embed {
fmt.Fprintf(w, "![](data:image/png;base64,%s)",
base64.StdEncoding.EncodeToString(b[:n]))
} else {
err = ioutil.WriteFile(
filepath.Join("uploads",
strings.TrimSuffix(zf.name, ".docx"),
rel.Target),
b, 0644)
if err != nil {
return err
}
fmt.Fprintf(w, "![](%s)", "/"+filepath.Join(
"uploads",
strings.TrimSuffix(zf.name, ".docx"),
escape(rel.Target, "()")))
}
break
}
return nil
}
func attr(attrs []xml.Attr, name string) (string, bool) {
for _, attr := range attrs {
if attr.Name.Local == name {
return attr.Value, true
}
}
return "", false
}
func (zf *file) walk(node *Node, w io.Writer) error {
switch node.XMLName.Local {
case "hyperlink":
fmt.Fprint(w, "[")
var cbuf bytes.Buffer
for _, n := range node.Nodes {
if err := zf.walk(&n, &cbuf); err != nil {
return err
}
}
fmt.Fprint(w, escape(cbuf.String(), "[]"))
fmt.Fprint(w, "]")
fmt.Fprint(w, "(")
if id, ok := attr(node.Attrs, "id"); ok {
for _, rel := range zf.rels.Relationship {
if id == rel.ID {
fmt.Fprint(w, escape(rel.Target, "()"))
break
}
}
}
fmt.Fprint(w, ")")
case "t":
fmt.Fprint(w, string(node.Content))
case "pPr":
code := false
for _, n := range node.Nodes {
switch n.XMLName.Local {
case "ind":
if left, ok := attr(n.Attrs, "left"); ok {
if i, err := strconv.Atoi(left); err == nil && i > 0 {
fmt.Fprint(w, strings.Repeat(" ", i/360))
}
}
case "pStyle":
if val, ok := attr(n.Attrs, "val"); ok {
if strings.HasPrefix(val, "Heading") {
if i, err := strconv.Atoi(val[7:]); err == nil && i > 0 {
fmt.Fprint(w, strings.Repeat("#", i)+" ")
}
} else if val == "Code" {
code = true
} else {
if i, err := strconv.Atoi(val); err == nil && i > 0 {
fmt.Fprint(w, strings.Repeat("#", i)+" ")
}
}
}
case "numPr":
numID := ""
ilvl := ""
numFmt := ""
start := 1
ind := 0
for _, nn := range n.Nodes {
if nn.XMLName.Local == "numId" {
if val, ok := attr(nn.Attrs, "val"); ok {
numID = val
}
}
if nn.XMLName.Local == "ilvl" {
if val, ok := attr(nn.Attrs, "val"); ok {
ilvl = val
}
}
}
for _, num := range zf.num.Num {
if numID != num.NumID {
continue
}
for _, abnum := range zf.num.AbstractNum {
if abnum.AbstractNumID != num.AbstractNumID.Val {
continue
}
for _, ablvl := range abnum.Lvl {
if ablvl.Ilvl != ilvl {
continue
}
if i, err := strconv.Atoi(ablvl.Start.Val); err == nil {
start = i
}
if i, err := strconv.Atoi(ablvl.PPr.Ind.Left); err == nil {
ind = i / 360
}
numFmt = ablvl.NumFmt.Val
break
}
break
}
break
}
fmt.Fprint(w, strings.Repeat(" ", ind))
switch numFmt {
case "decimal", "aiueoFullWidth":
key := fmt.Sprintf("%s:%d", numID, ind)
cur, ok := zf.list[key]
if !ok {
zf.list[key] = start
} else {
zf.list[key] = cur + 1
}
fmt.Fprintf(w, "%d. ", zf.list[key])
case "bullet":
fmt.Fprint(w, "* ")
}
}
}
if code {
fmt.Fprint(w, "`")
}
for _, n := range node.Nodes {
if err := zf.walk(&n, w); err != nil {
return err
}
}
if code {
fmt.Fprint(w, "`")
}
case "tbl":
var rows [][]string
for _, tr := range node.Nodes {
if tr.XMLName.Local != "tr" {
continue
}
var cols []string
for _, tc := range tr.Nodes {
if tc.XMLName.Local != "tc" {
continue
}
var cbuf bytes.Buffer
if err := zf.walk(&tc, &cbuf); err != nil {
return err
}
cols = append(cols, strings.Replace(cbuf.String(), "\n", "", -1))
}
rows = append(rows, cols)
}
maxcol := 0
for _, cols := range rows {
if len(cols) > maxcol {
maxcol = len(cols)
}
}
widths := make([]int, maxcol)
for _, row := range rows {
for i := 0; i < maxcol; i++ {
if i < len(row) {
width := runewidth.StringWidth(row[i])
if widths[i] < width {
widths[i] = width
}
}
}
}
for i, row := range rows {
if i == 0 {
for j := 0; j < maxcol; j++ {
fmt.Fprint(w, "|")
fmt.Fprint(w, strings.Repeat(" ", widths[j]))
}
fmt.Fprint(w, "|\n")
for j := 0; j < maxcol; j++ {
fmt.Fprint(w, "|")
fmt.Fprint(w, strings.Repeat("-", widths[j]))
}
fmt.Fprint(w, "|\n")
}
for j := 0; j < maxcol; j++ {
fmt.Fprint(w, "|")
if j < len(row) {
width := runewidth.StringWidth(row[j])
fmt.Fprint(w, escape(row[j], "|"))
fmt.Fprint(w, strings.Repeat(" ", widths[j]-width))
} else {
fmt.Fprint(w, strings.Repeat(" ", widths[j]))
}
}
fmt.Fprint(w, "|\n")
}
fmt.Fprint(w, "\n")
case "r":
bold := false
italic := false
strike := false
for _, n := range node.Nodes {
if n.XMLName.Local != "rPr" {
continue
}
for _, nn := range n.Nodes {
switch nn.XMLName.Local {
case "b":
bold = true
case "i":
italic = true
case "strike":
strike = true
}
}
}
if strike {
fmt.Fprint(w, "~~")
}
if bold {
fmt.Fprint(w, "**")
}
if italic {
fmt.Fprint(w, "*")
}
var cbuf bytes.Buffer
for _, n := range node.Nodes {
if err := zf.walk(&n, &cbuf); err != nil {
return err
}
}
fmt.Fprint(w, escape(cbuf.String(), `*~\`))
if italic {
fmt.Fprint(w, "*")
}
if bold {
fmt.Fprint(w, "**")
}
if strike {
fmt.Fprint(w, "~~")
}
case "p":
for _, n := range node.Nodes {
if err := zf.walk(&n, w); err != nil {
return err
}
}
fmt.Fprintln(w)
case "blip":
if id, ok := attr(node.Attrs, "embed"); ok {
for _, rel := range zf.rels.Relationship {
if id != rel.ID {
continue
}
if err := zf.extract(&rel, w); err != nil {
return err
}
}
}
case "Fallback":
case "txbxContent":
var cbuf bytes.Buffer
for _, n := range node.Nodes {
if err := zf.walk(&n, &cbuf); err != nil {
return err
}
}
fmt.Fprintln(w, "\n```\n"+cbuf.String()+"```")
default:
for _, n := range node.Nodes {
if err := zf.walk(&n, w); err != nil {
return err
}
}
}
return nil
}
func readFile(f *zip.File) (*Node, error) {
rc, err := f.Open()
defer rc.Close()
b, _ := ioutil.ReadAll(rc)
if err != nil {
return nil, err
}
var node Node
err = xml.Unmarshal(b, &node)
if err != nil {
return nil, err
}
return &node, nil
}
func findFile(files []*zip.File, target string) *zip.File {
for _, f := range files {
if ok, _ := path.Match(target, f.Name); ok {
return f
}
}
return nil
}
func Docx2md(arg string, embed bool) (string, error) {
r, err := zip.OpenReader(arg)
if err != nil {
return "", err
}
defer r.Close()
var rels Relationships
var num Numbering
for _, f := range r.File {
switch f.Name {
case "word/_rels/document.xml.rels":
rc, err := f.Open()
defer rc.Close()
b, _ := ioutil.ReadAll(rc)
if err != nil {
return "", err
}
err = xml.Unmarshal(b, &rels)
if err != nil {
return "", err
}
case "word/numbering.xml":
rc, err := f.Open()
defer rc.Close()
b, _ := ioutil.ReadAll(rc)
if err != nil {
return "", err
}
err = xml.Unmarshal(b, &num)
if err != nil {
return "", err
}
}
}
f := findFile(r.File, "word/document*.xml")
if f == nil {
return "", errors.New("incorrect document")
}
node, err := readFile(f)
if err != nil {
return "", err
}
fileNames := strings.Split(arg, "/")
fileName := fileNames[len(fileNames)-1]
// make sure the file name
if !strings.HasSuffix(fileName, ".docx") {
log.Fatal("File name must end with .docx")
}
var buf bytes.Buffer
zf := &file{
r: r,
rels: rels,
num: num,
embed: embed,
list: make(map[string]int),
name: fileName,
}
err = zf.walk(node, &buf)
if err != nil {
return "", err
}
return buf.String(), nil
}

View File

@ -33,8 +33,9 @@ func StripTags(s string) string {
return src return src
} }
//自动提取文章摘要 //自动提取文章摘要
func AutoSummary(body string,l int) string { func AutoSummary(body string, l int) string {
//匹配图片,如果图片语法是在代码块中,这里同样会处理 //匹配图片,如果图片语法是在代码块中,这里同样会处理
re := regexp.MustCompile(`<p>(.*?)</p>`) re := regexp.MustCompile(`<p>(.*?)</p>`)
@ -45,8 +46,8 @@ func AutoSummary(body string,l int) string {
return "" return ""
} }
content := "" content := ""
for _,s := range contents { for _, s := range contents {
b := strings.Replace(StripTags(s),"\n","", -1) b := strings.Replace(StripTags(s), "\n", "", -1)
if l <= 0 { if l <= 0 {
break break
@ -70,7 +71,9 @@ func SafetyProcessor(html string) string {
docQuery.Find("applet").Remove() docQuery.Find("applet").Remove()
docQuery.Find("frame").Remove() docQuery.Find("frame").Remove()
docQuery.Find("meta").Remove() docQuery.Find("meta").Remove()
if !conf.GetEnableIframe() {
docQuery.Find("iframe").Remove() docQuery.Find("iframe").Remove()
}
docQuery.Find("*").Each(func(i int, selection *goquery.Selection) { docQuery.Find("*").Each(func(i int, selection *goquery.Selection) {
if href, ok := selection.Attr("href"); ok && strings.HasPrefix(href, "javascript:") { if href, ok := selection.Attr("href"); ok && strings.HasPrefix(href, "javascript:") {
@ -117,7 +120,6 @@ func SafetyProcessor(html string) string {
} }
} }
if html, err := docQuery.Html(); err == nil { if html, err := docQuery.Html(); err == nil {
return strings.TrimSuffix(strings.TrimPrefix(strings.TrimSpace(html), "<html><head></head><body>"), "</body></html>") return strings.TrimSuffix(strings.TrimPrefix(strings.TrimSpace(html), "<html><head></head><body>"), "</body></html>")
} }

View File

@ -2,12 +2,15 @@ package pagination
import ( import (
"fmt" "fmt"
"html/template"
"math" "math"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
"html/template"
"github.com/beego/beego/v2/server/web"
"github.com/beego/i18n"
) )
//Pagination 分页器 //Pagination 分页器
@ -19,7 +22,7 @@ type Pagination struct {
} }
//NewPagination 新建分页器 //NewPagination 新建分页器
func NewPagination(req *http.Request, total int, pernum int,baseUrl string) *Pagination { func NewPagination(req *http.Request, total int, pernum int, baseUrl string) *Pagination {
return &Pagination{ return &Pagination{
Request: req, Request: req,
Total: total, Total: total,
@ -49,6 +52,8 @@ func (p *Pagination) Pages() string {
//计算总页数 //计算总页数
var totalPageNum = int(math.Ceil(float64(p.Total) / float64(p.Pernum))) var totalPageNum = int(math.Ceil(float64(p.Total) / float64(p.Pernum)))
lang := p.getLang()
//首页链接 //首页链接
var firstLink string var firstLink string
//上一页链接 //上一页链接
@ -62,20 +67,20 @@ func (p *Pagination) Pages() string {
//首页和上一页链接 //首页和上一页链接
if pagenum > 1 { if pagenum > 1 {
firstLink = fmt.Sprintf(`<li><a href="%s">首页</a></li>`, p.pageURL("1")) firstLink = fmt.Sprintf(`<li><a href="%s">%s</a></li>`, p.pageURL("1"), i18n.Tr(lang, "page.first"))
prevLink = fmt.Sprintf(`<li><a href="%s">上一页</a></li>`, p.pageURL(strconv.Itoa(pagenum-1))) prevLink = fmt.Sprintf(`<li><a href="%s">%s</a></li>`, p.pageURL(strconv.Itoa(pagenum-1)), i18n.Tr(lang, "page.prev"))
} else { } else {
firstLink = `<li class="disabled"><a href="#">首页</a></li>` firstLink = fmt.Sprintf(`<li class="disabled"><a href="#">%s</a></li>`, i18n.Tr(lang, "page.first"))
prevLink = `<li class="disabled"><a href="#">上一页</a></li>` prevLink = fmt.Sprintf(`<li class="disabled"><a href="#">%s</a></li>`, i18n.Tr(lang, "page.prev"))
} }
//末页和下一页 //末页和下一页
if pagenum < totalPageNum { if pagenum < totalPageNum {
lastLink = fmt.Sprintf(`<li><a href="%s">末页</a></li>`, p.pageURL(strconv.Itoa(totalPageNum))) lastLink = fmt.Sprintf(`<li><a href="%s">%s</a></li>`, p.pageURL(strconv.Itoa(totalPageNum)), i18n.Tr(lang, "page.last"))
nextLink = fmt.Sprintf(`<li><a href="%s">下一页</a></li>`, p.pageURL(strconv.Itoa(pagenum+1))) nextLink = fmt.Sprintf(`<li><a href="%s">%s</a></li>`, p.pageURL(strconv.Itoa(pagenum+1)), i18n.Tr(lang, "page.next"))
} else { } else {
lastLink = `<li class="disabled"><a href="#">末页</a></li>` lastLink = fmt.Sprintf(`<li class="disabled"><a href="#">%s</a></li>`, i18n.Tr(lang, "page.last"))
nextLink = `<li class="disabled"><a href="#">下一页</a></li>` nextLink = fmt.Sprintf(`<li class="disabled"><a href="#">%s</a></li>`, i18n.Tr(lang, "page.next"))
} }
//生成中间页码链接 //生成中间页码链接
@ -112,28 +117,18 @@ func (p *Pagination) pageURL(page string) string {
return u.String() return u.String()
} }
type Page struct { func (p *Pagination) getLang() string {
PageNo int `json:"PageNo"` lang, _ := web.AppConfig.String("default_lang")
PageSize int `json:"PageSize"` ulang := p.Request.FormValue("lang")
TotalPage int `json:"TotalPage"` if len(ulang) == 0 {
TotalCount int `json:"TotalCount"` clang, err := p.Request.Cookie("lang")
FirstPage bool `json:"FirstPage"` if err != nil {
LastPage bool `json:"LastPage"` return lang
List interface{} `json:"List"`
}
func PageUtil(count int, pageNo int, pageSize int, list interface{}) Page {
tp := count / pageSize
if count%pageSize > 0 {
tp = count/pageSize + 1
} }
return Page { ulang = clang.Value
PageNo: pageNo,
PageSize: pageSize,
TotalPage: tp,
TotalCount: count,
FirstPage: pageNo == 1,
LastPage: pageNo == tp,
List: list,
} }
if !i18n.IsExist(ulang) {
return lang
}
return ulang
} }

View File

@ -7,7 +7,7 @@
<meta name="renderer" content="webkit" /> <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="MinDoc" /> <meta name="author" content="MinDoc" />
<title>找回密码 - Powered by MinDoc</title> <title>{{i18n .Lang "common.account_recovery"}} - Powered by MinDoc</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -35,13 +35,13 @@
<div class="login-body"> <div class="login-body">
<form role="form" method="post" id="findPasswordForm"> <form role="form" method="post" id="findPasswordForm">
{{ .xsrfdata }} {{ .xsrfdata }}
<h3 class="text-center">找回密码</h3> <h3 class="text-center">{{i18n .Lang "common.account_recovery"}}</h3>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-at"></i> <i class="fa fa-at"></i>
</div> </div>
<input type="text" class="form-control" placeholder="邮箱" name="email" id="email" autocomplete="off"> <input type="text" class="form-control" placeholder="{{i18n .Lang "common.email"}}" name="email" id="email" autocomplete="off">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -49,14 +49,14 @@
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-check-square"></i> <i class="fa fa-check-square"></i>
</div> </div>
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">&nbsp; <input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="{{i18n .Lang "common.captcha"}}" autocomplete="off">&nbsp;
</div> </div>
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张"> <img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="{{i18n .Lang "message.click_to_change"}}">
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="正在处理..." autocomplete="off">找回密码</button> <button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="{{i18n .Lang "message.processing"}}" autocomplete="off">{{i18n .Lang "common.account_recovery"}}</button>
</div> </div>
</form> </form>
@ -88,7 +88,7 @@
var email = $.trim($("#email").val()); var email = $.trim($("#email").val());
if(email === ""){ if(email === ""){
$("#email").tooltip({placement:"auto",title : "邮箱不能为空",trigger : 'manual'}) $("#email").tooltip({placement:"auto",title : "{{i18n .Lang "message.email_empty"}}",trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
$btn.button('reset'); $btn.button('reset');
@ -97,7 +97,7 @@
} }
var code = $.trim($("#code").val()); var code = $.trim($("#code").val());
if(code === ""){ if(code === ""){
$("#code").tooltip({title : '验证码不能为空',trigger : 'manual'}) $("#code").tooltip({title : '{{i18n .Lang "message.captcha_empty"}}',trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
$btn.button('reset'); $btn.button('reset');
@ -113,14 +113,14 @@
layer.msg(res.message); layer.msg(res.message);
$("#btnSendMail").button('reset'); $("#btnSendMail").button('reset');
}else{ }else{
alert("邮件发送成功,请登录邮箱查看。") alert("{{i18n .Lang "message.email_sent"}}")
window.location = res.data; window.location = res.data;
} }
}, },
error :function () { error :function () {
$("#captcha-img").click(); $("#captcha-img").click();
$("#code").val(''); $("#code").val('');
layer.msg('系统错误'); layer.msg('{{i18n .Lang "message.system_error"}}');
$("#btnSendMail").button('reset'); $("#btnSendMail").button('reset');
} }
}); });

View File

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" /> <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="SmartWiki" /> <meta name="author" content="MinDoc" />
<title>找回密码 - Powered by MinDoc</title> <title>{{i18n .Lang "common.account_recovery"}} - Powered by MinDoc</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -37,14 +37,14 @@
{{ .xsrfdata }} {{ .xsrfdata }}
<input type="hidden" name="token" value="{{.Token}}"> <input type="hidden" name="token" value="{{.Token}}">
<input type="hidden" name="mail" value="{{.Email}}"> <input type="hidden" name="mail" value="{{.Email}}">
<h3 class="text-center">找回密码</h3> <h3 class="text-center">{{i18n .Lang "common.account_recovery"}}</h3>
<div class="form-group"> <div class="form-group">
<label for="newPasswd">新密码</label> <label for="newPasswd">{{i18n .Lang "common.new_password"}}</label>
<input type="password" class="form-control" name="password1" id="newPassword" maxlength="20" placeholder="新密码" autocomplete="off"> <input type="password" class="form-control" name="password1" id="newPassword" maxlength="20" placeholder="{{i18n .Lang "common.new_password"}}" autocomplete="off">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="configPasswd">确认密码</label> <label for="configPasswd">{{i18n .Lang "common.confirm_password"}}</label>
<input type="password" class="form-control" id="confirmPassword" name="password2" maxlength="20" placeholder="确认密码" autocomplete="off"> <input type="password" class="form-control" id="confirmPassword" name="password2" maxlength="20" placeholder="{{i18n .Lang "common.confirm_password"}}" autocomplete="off">
</div> </div>
<div class="form-group"> <div class="form-group">
@ -52,13 +52,13 @@
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-check-square"></i> <i class="fa fa-check-square"></i>
</div> </div>
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">&nbsp; <input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="{{i18n .Lang "common.captcha"}}" autocomplete="off">&nbsp;
</div> </div>
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张"> <img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张">
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="正在处理..." autocomplete="off">找回密码</button> <button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="{{i18n .Lang "message.processing"}}" autocomplete="off">{{i18n .Lang "common.account_recovery"}}</button>
</div> </div>
</form> </form>
@ -92,26 +92,26 @@
var code = $.trim($("#code").val()); var code = $.trim($("#code").val());
if(newPassword === ""){ if(newPassword === ""){
$("#newPassword").tooltip({placement:"auto",title : "密码不能为空",trigger : 'manual'}) $("#newPassword").tooltip({placement:"auto",title : "{{i18n .Lang "message.password_empty"}}",trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
return false; return false;
}else if(confirmPassword === ""){ }else if(confirmPassword === ""){
$("#confirmPassword").tooltip({placement:"auto",title : "确认密码不能为空",trigger : 'manual'}) $("#confirmPassword").tooltip({placement:"auto",title : "{{i18n .Lang "message.confirm_password_empty"}}",trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
return false; return false;
}else if(newPassword !== confirmPassword) { }else if(newPassword !== confirmPassword) {
$("#confirmPassword").tooltip({placement:"auto",title : "确认密码输入不正确",trigger : 'manual'}) $("#confirmPassword").tooltip({placement:"auto",title : "{{i18n .Lang "message.incorrect_confirm_password"}}",trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
return false; return false;
}else if(code === ""){ }else if(code === ""){
$("#code").tooltip({title : '验证码不能为空',trigger : 'manual'}) $("#code").tooltip({title : '{{i18n .Lang "message.captcha_empty"}}',trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
@ -134,7 +134,7 @@
error :function () { error :function () {
$("#captcha-img").click(); $("#captcha-img").click();
$("#code").val(''); $("#code").val('');
layer.msg('系统错误'); layer.msg('{{i18n .Lang "message.system_error"}}');
$("#btnSendMail").button('reset'); $("#btnSendMail").button('reset');
} }
}); });

View File

@ -7,7 +7,7 @@
<meta name="renderer" content="webkit" /> <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="MinDoc" /> <meta name="author" content="MinDoc" />
<title>用户登录 - Powered by MinDoc</title> <title>{{i18n .Lang "common.login"}} - Powered by MinDoc</title>
<meta name="keywords" content="MinDoc,文档在线管理系统,WIKI,wiki,wiki在线,文档在线管理,接口文档在线管理,接口文档管理"> <meta name="keywords" content="MinDoc,文档在线管理系统,WIKI,wiki,wiki在线,文档在线管理,接口文档在线管理,接口文档管理">
<meta name="description" content="MinDoc文档在线管理系统 {{.site_description}}"> <meta name="description" content="MinDoc文档在线管理系统 {{.site_description}}">
<!-- Bootstrap --> <!-- Bootstrap -->
@ -30,13 +30,13 @@
<div class="login-body"> <div class="login-body">
<form role="form" method="post"> <form role="form" method="post">
{{ .xsrfdata }} {{ .xsrfdata }}
<h3 class="text-center">用户登录</h3> <h3 class="text-center">{{i18n .Lang "common.login"}}</h3>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-user"></i> <i class="fa fa-user"></i>
</div> </div>
<input type="text" class="form-control" placeholder="邮箱 / 用户名" name="account" id="account" autocomplete="off"> <input type="text" class="form-control" placeholder="{{i18n .Lang "common.email"}} / {{i18n .Lang "common.username"}}" name="account" id="account" autocomplete="off">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -44,7 +44,7 @@
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-lock"></i> <i class="fa fa-lock"></i>
</div> </div>
<input type="password" class="form-control" placeholder="密码" name="password" id="password" autocomplete="off"> <input type="password" class="form-control" placeholder="{{i18n .Lang "common.password"}}" name="password" id="password" autocomplete="off">
</div> </div>
</div> </div>
{{if .ENABLED_CAPTCHA }} {{if .ENABLED_CAPTCHA }}
@ -54,38 +54,38 @@
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-check-square"></i> <i class="fa fa-check-square"></i>
</div> </div>
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">&nbsp; <input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="{{i18n .Lang "common.captcha"}}" autocomplete="off">&nbsp;
</div> </div>
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张"> <img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title={{i18n .Lang "message.click_to_change"}}>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{{end}} {{end}}
{{end}} {{end}}
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" name="is_remember" value="yes"> 保持登录 <input type="checkbox" name="is_remember" value="yes"> {{i18n .Lang "common.keep_login"}}
</label> </label>
<a href="{{urlfor "AccountController.FindPassword" }}" style="display: inline-block;float: right">忘记密码?</a> <a href="{{urlfor "AccountController.FindPassword" }}" style="display: inline-block;float: right">{{i18n .Lang "common.forgot_password"}}</a>
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="button" id="btn-login" class="btn btn-success" style="width: 100%" data-loading-text="正在登录..." autocomplete="off">立即登录</button> <button type="button" id="btn-login" class="btn btn-success" style="width: 100%" data-loading-text="{{i18n .Lang "common.logging_in"}}" autocomplete="off">{{i18n .Lang "common.login"}}</button>
</div> </div>
{{if .ENABLE_QR_DINGTALK}} {{if .ENABLE_QR_DINGTALK}}
<div class="form-group"> <div class="form-group">
<a id="btn-dingtalk-qr" class="btn btn-default" style="width: 100%" data-loading-text="" autocomplete="off">钉钉扫码登录</a> <a id="btn-dingtalk-qr" class="btn btn-default" style="width: 100%" data-loading-text="" autocomplete="off">{{i18n .Lang "common.dingtalk_login"}}</a>
</div> </div>
{{end}} {{end}}
{{if .ENABLED_REGISTER}} {{if .ENABLED_REGISTER}}
{{if ne .ENABLED_REGISTER "false"}} {{if ne .ENABLED_REGISTER "false"}}
<div class="form-group"> <div class="form-group">
还没有账号?<a href="{{urlfor "AccountController.Register" }}" title="立即注册">立即注册</a> {{i18n .Lang "message.no_account_yet"}} <a href="{{urlfor "AccountController.Register" }}" title={{i18n .Lang "common.register"}}>{{i18n .Lang "common.register"}}</a>
</div> </div>
{{end}} {{end}}
{{end}} {{end}}
</form> </form>
<div class="form-group dingtalk-container" style="display: none;"> <div class="form-group dingtalk-container" style="display: none;">
<div id="dingtalk-qr-container"></div> <div id="dingtalk-qr-container"></div>
<a class="btn btn-default btn-dingtalk" style="width: 100%" data-loading-text="" autocomplete="off">返回账号密码登录</a> <a class="btn btn-default btn-dingtalk" style="width: 100%" data-loading-text="" autocomplete="off">{{i18n .Lang "message.return_account_login"}}</a>
</div> </div>
</div> </div>
</div> </div>
@ -196,19 +196,19 @@
var code = $("#code").val(); var code = $("#code").val();
if (account === "") { if (account === "") {
$("#account").tooltip({ placement: "auto", title: "账号不能为空", trigger: 'manual' }) $("#account").tooltip({ placement: "auto", title: "{{i18n .Lang "message.account_empty"}}", trigger: 'manual' })
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
$btn.button('reset'); $btn.button('reset');
return false; return false;
} else if (password === "") { } else if (password === "") {
$("#password").tooltip({ title: '密码不能为空', trigger: 'manual' }) $("#password").tooltip({ title: '{{i18n .Lang "message.password_empty"}}', trigger: 'manual' })
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
$btn.button('reset'); $btn.button('reset');
return false; return false;
} else if (code !== undefined && code === "") { } else if (code !== undefined && code === "") {
$("#code").tooltip({ title: '验证码不能为空', trigger: 'manual' }) $("#code").tooltip({ title: '{{i18n .Lang "message.captcha_empty"}}', trigger: 'manual' })
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
$btn.button('reset'); $btn.button('reset');
@ -236,7 +236,7 @@
error: function () { error: function () {
$("#captcha-img").click(); $("#captcha-img").click();
$("#code").val(''); $("#code").val('');
layer.msg('系统错误'); layer.msg('{{i18n .Lang "message.system_error"}}');
$btn.button('reset'); $btn.button('reset');
} }
}); });

View File

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" /> <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="SmartWiki" /> <meta name="author" content="MinDoc" />
<title>用户注册 - Powered by MinDoc</title> <title>{{i18n .Lang "common.new_account"}} - Powered by MinDoc</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -35,13 +35,13 @@
<div class="login-body"> <div class="login-body">
<form role="form" method="post" id="registerForm"> <form role="form" method="post" id="registerForm">
{{ .xsrfdata }} {{ .xsrfdata }}
<h3 class="text-center">用户注册</h3> <h3 class="text-center">{{i18n .Lang "common.new_account"}}</h3>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-user"></i> <i class="fa fa-user"></i>
</div> </div>
<input type="text" class="form-control" placeholder="用户名" name="account" id="account" autocomplete="off"> <input type="text" class="form-control" placeholder="{{i18n .Lang "common.username"}}" name="account" id="account" autocomplete="off">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -49,7 +49,7 @@
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-lock"></i> <i class="fa fa-lock"></i>
</div> </div>
<input type="password" class="form-control" placeholder="密码" name="password1" id="password1" autocomplete="off"> <input type="password" class="form-control" placeholder="{{i18n .Lang "common.password"}}" name="password1" id="password1" autocomplete="off">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -57,13 +57,13 @@
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-lock"></i> <i class="fa fa-lock"></i>
</div> </div>
<input type="password" class="form-control" placeholder="确认密码" name="password2" id="password2" autocomplete="off"> <input type="password" class="form-control" placeholder="{{i18n .Lang "common.confirm_password"}}" name="password2" id="password2" autocomplete="off">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-addon" style="padding: 6px 9px;"><i class="fa fa-envelope"></i></div> <div class="input-group-addon" style="padding: 6px 9px;"><i class="fa fa-envelope"></i></div>
<input type="email" class="form-control" placeholder="用户邮箱" name="email" id="email" autocomplete="off"> <input type="email" class="form-control" placeholder="{{i18n .Lang "common.email"}}" name="email" id="email" autocomplete="off">
</div> </div>
</div> </div>
@ -72,18 +72,18 @@
<div class="input-group-addon"> <div class="input-group-addon">
<i class="fa fa-check-square"></i> <i class="fa fa-check-square"></i>
</div> </div>
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">&nbsp; <input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="{{i18n .Lang "common.captcha"}}" autocomplete="off">&nbsp;
</div> </div>
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张"> <img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="{{i18n .Lang "message.click_to_change"}}">
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" id="btnRegister" class="btn btn-success" style="width: 100%" data-loading-text="正在注册..." autocomplete="off">立即注册</button> <button type="submit" id="btnRegister" class="btn btn-success" style="width: 100%" data-loading-text="{{i18n .Lang "message.processing"}}" autocomplete="off">{{i18n .Lang "common.register"}}</button>
</div> </div>
{{if ne .ENABLED_REGISTER "false"}} {{if ne .ENABLED_REGISTER "false"}}
<div class="form-group"> <div class="form-group">
已有账号?<a href="{{urlfor "AccountController.Login" }}" title="立即登录">立即登录</a> {{i18n .Lang "message.has_account"}} <a href="{{urlfor "AccountController.Login" }}" title="{{i18n .Lang "common.login"}}">{{i18n .Lang "common.login"}}</a>
</div> </div>
{{end}} {{end}}
</form> </form>
@ -117,28 +117,28 @@
var email = $.trim($("#email").val()); var email = $.trim($("#email").val());
if(account === ""){ if(account === ""){
$("#account").focus().tooltip({placement:"auto",title : "账号不能为空",trigger : 'manual'}) $("#account").focus().tooltip({placement:"auto",title : "{{i18n .Lang "message.account_empty"}}",trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
return false; return false;
}else if(password === ""){ }else if(password === ""){
$("#password").focus().tooltip({title : '密码不能为空',trigger : 'manual'}) $("#password").focus().tooltip({title : '{{i18n .Lang "message.password_empty"}}',trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
return false; return false;
}else if(confirmPassword !== password){ }else if(confirmPassword !== password){
$("#confirm_password").focus().tooltip({title : '确认密码不正确',trigger : 'manual'}) $("#confirm_password").focus().tooltip({title : '{{i18n .Lang "message.confirm_password_empty"}}',trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
return false; return false;
}else if(email === ""){ }else if(email === ""){
$("#email").focus().tooltip({title : '邮箱不能为空',trigger : 'manual'}) $("#email").focus().tooltip({title : '{{i18n .Lang "message.email_empty"}}',trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
return false; return false;
}else if(code !== undefined && code === ""){ }else if(code !== undefined && code === ""){
$("#code").focus().tooltip({title : '验证码不能为空',trigger : 'manual'}) $("#code").focus().tooltip({title : '{{i18n .Lang "message.captcha_empty"}}',trigger : 'manual'})
.tooltip('show') .tooltip('show')
.parents('.form-group').addClass('has-error'); .parents('.form-group').addClass('has-error');
return false; return false;

View File

@ -15,7 +15,6 @@
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/editor.md/lib/mermaid/mermaid.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/editor.md/lib/sequence/sequence-diagram-min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/editor.md/lib/sequence/sequence-diagram-min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/css/kancloud.css" "version"}}" rel="stylesheet"> <link href="{{cdncss "/static/css/kancloud.css" "version"}}" rel="stylesheet">
<link href="{{cdncss "/static/editor.md/css/editormd.preview.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/editor.md/css/editormd.preview.css"}}" rel="stylesheet">
@ -82,13 +81,13 @@
<div class="blog-meta"> <div class="blog-meta">
<div class="item user_img"><img src="{{cdnimg .Model.MemberAvatar}}" align="{{.Model.CreateName}}"> </div> <div class="item user_img"><img src="{{cdnimg .Model.MemberAvatar}}" align="{{.Model.CreateName}}"> </div>
<div class="item">&nbsp;{{.Model.CreateName}}</div> <div class="item">&nbsp;{{.Model.CreateName}}</div>
<div class="item">发布于</div> <div class="item">{{i18n .Lang "blog.posted_on"}}</div>
<div class="item">{{date .Model.Created "Y-m-d H:i:s"}}</div> <div class="item">{{date .Model.Created "Y-m-d H:i:s"}}</div>
<div class="item">{{.Model.ModifyRealName}}</div> <div class="item">{{.Model.ModifyRealName}}</div>
<div class="item">修改于</div> <div class="item">{{i18n .Lang "blog.modified_on"}}</div>
<div class="item">{{date .Model.Modified "Y-m-d H:i:s"}}</div> <div class="item">{{date .Model.Modified "Y-m-d H:i:s"}}</div>
{{if eq .Member.MemberId .Model.MemberId}} {{if eq .Member.MemberId .Model.MemberId}}
<div class="item"><a href='{{urlfor "BlogController.ManageEdit" ":id" .Model.BlogId}}' title="文章编辑"><i class="fa fa-edit"></i> 编辑</a></div> <div class="item"><a href='{{urlfor "BlogController.ManageEdit" ":id" .Model.BlogId}}' title="{{i18n .Lang "blog.edit_blog"}}"><i class="fa fa-edit"></i> {{i18n .Lang "common.edit"}}</a></div>
{{end}} {{end}}
</div> </div>
</div> </div>
@ -96,7 +95,7 @@
<div class="article-body markdown-body editormd-preview-container content"> <div class="article-body markdown-body editormd-preview-container content">
{{.Content}} {{.Content}}
{{if .Model.AttachList}} {{if .Model.AttachList}}
<div class="attach-list"><strong>附件</strong><ul> <div class="attach-list"><strong>{{i18n .Lang "blog.attachment"}}</strong><ul>
{{range $index,$item := .Model.AttachList}} {{range $index,$item := .Model.AttachList}}
<li><a href="{{$item.HttpPath}}" title="{{$item.FileName}}">{{$item.FileName}}</a> </li> <li><a href="{{$item.HttpPath}}" title="{{$item.FileName}}">{{$item.FileName}}</a> </li>
{{end}} {{end}}
@ -106,20 +105,20 @@
</div> </div>
<div class="row blog-footer"> <div class="row blog-footer">
<p> <p>
<span>上一篇</span> <span>{{i18n .Lang "blog.prev"}}</span>
{{if .Previous}} {{if .Previous}}
<a href="{{urlfor "BlogController.Index" ":id" .Previous.BlogId}}" title="{{.Previous.BlogTitle}}">{{.Previous.BlogTitle}} <a href="{{urlfor "BlogController.Index" ":id" .Previous.BlogId}}" title="{{.Previous.BlogTitle}}">{{.Previous.BlogTitle}}
</a> </a>
{{else}} {{else}}
{{i18n .Lang "blog.no"}}
{{end}} {{end}}
</p> </p>
<p> <p>
<span>下一篇</span> <span>{{i18n .Lang "blog.next"}}</span>
{{if .Next}} {{if .Next}}
<a href="{{urlfor "BlogController.Index" ":id" .Next.BlogId}}" title="{{.Next.BlogTitle}}">{{.Next.BlogTitle}}</a> <a href="{{urlfor "BlogController.Index" ":id" .Next.BlogId}}" title="{{.Next.BlogTitle}}">{{.Next.BlogTitle}}</a>
{{else}} {{else}}
{{i18n .Lang "blog.no"}}
{{end}} {{end}}
</p> </p>
</div> </div>

View File

@ -6,7 +6,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" /> <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>请输入文章密码 - Powered by MinDoc</title> <title>{{i18n .Lang "doc.input_pwd"}} - Powered by MinDoc</title>
<script src="{{cdnjs "static/jquery/1.12.4/jquery.min.js"}}"></script> <script src="{{cdnjs "static/jquery/1.12.4/jquery.min.js"}}"></script>
<script src="{{cdnjs "static/js/jquery.form.js"}}"></script> <script src="{{cdnjs "static/js/jquery.form.js"}}"></script>
<style type="text/css"> <style type="text/css">
@ -99,14 +99,14 @@
<form action="{{urlfor "BlogController.Index" ":id" .Model.BlogId}}" method="post" id="auth_form"> <form action="{{urlfor "BlogController.Index" ":id" .Model.BlogId}}" method="post" id="auth_form">
<input type="hidden" value="{{.Model.BlogId}}" name="blogId" /> <input type="hidden" value="{{.Model.BlogId}}" name="blogId" />
<div class="tit"> <div class="tit">
请输入浏览密码 {{i18n .Lang "doc.input_pwd"}}
</div> </div>
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<input type="password" name="password" placeholder="浏览密码" class="inp"/> <input type="password" name="password" placeholder="{{i18n .Lang "blog.access_pass"}}" class="inp"/>
</div> </div>
<div class="btn"> <div class="btn">
<span id="error" style="color: #919191; font-size: 13px;"></span> <span id="error" style="color: #919191; font-size: 13px;"></span>
<input type="submit" value="提交" class="button"/> <input type="submit" value="{{i18n .Lang "doc.commit"}}" class="button"/>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</form> </form>
@ -117,7 +117,7 @@ $("#auth_form").ajaxForm({
beforeSerialize: function () { beforeSerialize: function () {
var pwd = $("#auth_form input[name='password']").val(); var pwd = $("#auth_form input[name='password']").val();
if (pwd === "") { if (pwd === "") {
$("#error").html("请输入密码"); $("#error").html("{{i18n .Lang "doc.input_pwd"}}");
return false; return false;
} }
}, },

View File

@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>文章列表 - Powered by MinDoc</title> <title>{{i18n .Lang "blog.blog_list"}} - Powered by MinDoc</title>
<meta name="keywords" content="MinDoc,文档在线管理系统,WIKI,wiki,wiki在线,文档在线管理,接口文档在线管理,接口文档管理"> <meta name="keywords" content="MinDoc,文档在线管理系统,WIKI,wiki,wiki在线,文档在线管理,接口文档在线管理,接口文档管理">
<meta name="description" content="MinDoc文档在线管理系统 {{.site_description}}"> <meta name="description" content="MinDoc文档在线管理系统 {{.site_description}}">
<!-- Bootstrap --> <!-- Bootstrap -->
@ -30,20 +30,20 @@
<div class="manual-list"> <div class="manual-list">
{{range $index,$item := .Lists}} {{range $index,$item := .Lists}}
<div class="search-item"> <div class="search-item">
<div class="title">{{if eq $item.BlogStatus "password"}}<span class="label"></span>{{end}} <a href="{{urlfor "BlogController.Index" ":id" $item.BlogId}}" title="{{$item.BlogTitle}}">{{$item.BlogTitle}}</a> </div> <div class="title">{{if eq $item.BlogStatus "password"}}<span class="label">{{i18n $.Lang "blog.encrypt"}}</span>{{end}} <a href="{{urlfor "BlogController.Index" ":id" $item.BlogId}}" title="{{$item.BlogTitle}}">{{$item.BlogTitle}}</a> </div>
<div class="description"> <div class="description">
{{$item.BlogExcerpt}} {{$item.BlogExcerpt}}
</div> </div>
{{/*<div class="site">{{urlfor "BlogController.Index" ":id" $item.BlogId}}</div>*/}} {{/*<div class="site">{{urlfor "BlogController.Index" ":id" $item.BlogId}}</div>*/}}
<div class="source"> <div class="source">
<span class="item">作者{{$item.CreateName}}</span> <span class="item">{{i18n $.Lang "blog.author"}}{{$item.CreateName}}</span>
<span class="item">更新时间{{date_format $item.Modified "2006-01-02 15:04:05"}}</span> <span class="item">{{i18n $.Lang "blog.update_time"}}{{date_format $item.Modified "2006-01-02 15:04:05"}}</span>
</div> </div>
</div> </div>
{{else}} {{else}}
<div class="search-empty"> <div class="search-empty">
<img src="{{cdnimg "/static/images/search_empty.png"}}" class="empty-image"> <img src="{{cdnimg "/static/images/search_empty.png"}}" class="empty-image">
<span class="empty-text">暂无文章</span> <span class="empty-text">{{i18n $.Lang "blog.no_blog"}}</span>
</div> </div>
{{end}} {{end}}
<nav class="pagination-container"> <nav class="pagination-container">

111
views/blog/manage_edit.tpl 100644 → 100755
View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>编辑文章 - Powered by MinDoc</title> <title>{{i18n .Lang "blog.edit_title"}} - Powered by MinDoc</title>
<script type="text/javascript"> <script type="text/javascript">
window.baseUrl = "{{.BaseUrl}}"; window.baseUrl = "{{.BaseUrl}}";
window.katex = { js: "{{cdnjs "/static/katex/katex"}}",css: "{{cdncss "/static/katex/katex"}}"}; window.katex = { js: "{{cdnjs "/static/katex/katex"}}",css: "{{cdncss "/static/katex/katex"}}"};
@ -18,12 +18,13 @@
window.blogVersion = {{.Model.Version}}; window.blogVersion = {{.Model.Version}};
window.removeAttachURL = "{{urlfor "BlogController.RemoveAttachment" ":id" .Model.BlogId}}"; window.removeAttachURL = "{{urlfor "BlogController.RemoveAttachment" ":id" .Model.BlogId}}";
window.highlightStyle = "{{.HighlightStyle}}"; window.highlightStyle = "{{.HighlightStyle}}";
window.lang = {{i18n $.Lang "common.js_lang"}};
</script> </script>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/jstree/3.3.4/themes/default/style.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/jstree/3.3.4/themes/default/style.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/editor.md/css/editormd.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/editor.md/css/editormd.css" "version"}}" rel="stylesheet">
<link href="{{cdncss "/static/css/jstree.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/css/jstree.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/webuploader/webuploader.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/webuploader/webuploader.css"}}" rel="stylesheet">
@ -35,50 +36,50 @@
<div class="m-manual manual-editor"> <div class="m-manual manual-editor">
<div class="manual-head" id="editormd-tools" style="min-width: 1200px; position:absolute;"> <div class="manual-head" id="editormd-tools" style="min-width: 1200px; position:absolute;">
<div class="editormd-group"> <div class="editormd-group">
<a href="{{urlfor "BlogController.ManageList"}}" data-toggle="tooltip" data-title="返回"><i class="fa fa-chevron-left" aria-hidden="true"></i></a> <a href="{{urlfor "BlogController.ManageList"}}" data-toggle="tooltip" data-title="{{i18n .Lang "doc.backward"}}"><i class="fa fa-chevron-left" aria-hidden="true"></i></a>
</div> </div>
<div class="editormd-group"> <div class="editormd-group">
<a href="javascript:;" id="markdown-save" data-toggle="tooltip" data-title="保存" class="disabled save"><i class="fa fa-save" aria-hidden="true" name="save"></i></a> <a href="javascript:;" id="markdown-save" data-toggle="tooltip" data-title="{{i18n .Lang "doc.save"}}" class="disabled save"><i class="fa fa-save" aria-hidden="true" name="save"></i></a>
</div> </div>
<div class="editormd-group"> <div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="撤销 (Ctrl-Z)"><i class="fa fa-undo first" name="undo" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.undo"}} (Ctrl-Z)"><i class="fa fa-undo first" name="undo" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="重做 (Ctrl-Y)"><i class="fa fa-repeat last" name="redo" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.redo"}} (Ctrl-Y)"><i class="fa fa-repeat last" name="redo" unselectable="on"></i></a>
</div> </div>
<div class="editormd-group"> <div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="粗体"><i class="fa fa-bold first" name="bold" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.bold"}}"><i class="fa fa-bold first" name="bold" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="斜体"><i class="fa fa-italic item" name="italic" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.italic"}}"><i class="fa fa-italic item" name="italic" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="删除线"><i class="fa fa-strikethrough last" name="del" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.strikethrough"}}"><i class="fa fa-strikethrough last" name="del" unselectable="on"></i></a>
</div> </div>
<div class="editormd-group"> <div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="标题一"><i class="fa editormd-bold first" name="h1" unselectable="on">H1</i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.h1"}}"><i class="fa editormd-bold first" name="h1" unselectable="on">H1</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题二"><i class="fa editormd-bold item" name="h2" unselectable="on">H2</i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.h2"}}"><i class="fa editormd-bold item" name="h2" unselectable="on">H2</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题三"><i class="fa editormd-bold item" name="h3" unselectable="on">H3</i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.h3"}}"><i class="fa editormd-bold item" name="h3" unselectable="on">H3</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题四"><i class="fa editormd-bold item" name="h4" unselectable="on">H4</i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.h4"}}"><i class="fa editormd-bold item" name="h4" unselectable="on">H4</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题五"><i class="fa editormd-bold item" name="h5" unselectable="on">H5</i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.h5"}}"><i class="fa editormd-bold item" name="h5" unselectable="on">H5</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题六"><i class="fa editormd-bold last" name="h6" unselectable="on">H6</i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.h6"}}"><i class="fa editormd-bold last" name="h6" unselectable="on">H6</i></a>
</div> </div>
<div class="editormd-group"> <div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="无序列表"><i class="fa fa-list-ul first" name="list-ul" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.unorder_list"}}"><i class="fa fa-list-ul first" name="list-ul" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="有序列表"><i class="fa fa-list-ol item" name="list-ol" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.order_list"}}"><i class="fa fa-list-ol item" name="list-ol" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="横线"><i class="fa fa-minus last" name="hr" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.hline"}}"><i class="fa fa-minus last" name="hr" unselectable="on"></i></a>
</div> </div>
<div class="editormd-group"> <div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="链接"><i class="fa fa-link first" name="link" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.link"}}"><i class="fa fa-link first" name="link" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="引用链接"><i class="fa fa-anchor item" name="reference-link" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.ref_link"}}"><i class="fa fa-anchor item" name="reference-link" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="添加图片"><i class="fa fa-picture-o item" name="image" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.add_pic"}}"><i class="fa fa-picture-o item" name="image" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="行内代码"><i class="fa fa-code item" name="code" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.code"}}"><i class="fa fa-code item" name="code" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="代码块" unselectable="on"><i class="fa fa-file-code-o item" name="code-block" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.code_block"}}" unselectable="on"><i class="fa fa-file-code-o item" name="code-block" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="添加表格"><i class="fa fa-table item" name="table" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.table"}}"><i class="fa fa-table item" name="table" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="引用"><i class="fa fa-quote-right item" name="quote" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.quote"}}"><i class="fa fa-quote-right item" name="quote" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="GFM 任务列表"><i class="fa fa-tasks item" name="tasks" aria-hidden="true"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.gfm_task"}}"><i class="fa fa-tasks item" name="tasks" aria-hidden="true"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="附件"><i class="fa fa-paperclip item" aria-hidden="true" name="attachment"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.attachment"}}"><i class="fa fa-paperclip item" aria-hidden="true" name="attachment"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="模板"><i class="fa fa-tachometer last" name="template"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.template"}}"><i class="fa fa-tachometer last" name="template"></i></a>
</div> </div>
<div class="editormd-group pull-right"> <div class="editormd-group pull-right">
<a href="javascript:;" data-toggle="tooltip" data-title="关闭实时预览"><i class="fa fa-eye-slash first" name="watch" unselectable="on"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.close_preview"}}"><i class="fa fa-eye-slash first" name="watch" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="使用帮助"><i class="fa fa-question-circle-o last" aria-hidden="true" name="help"></i></a> <a href="javascript:;" data-toggle="tooltip" data-title="{{i18n .Lang "doc.help"}}"><i class="fa fa-question-circle-o last" aria-hidden="true" name="help"></i></a>
</div> </div>
<div class="editormd-group"> <div class="editormd-group">
@ -94,7 +95,7 @@
<div id="docEditor" class="manual-editormd-active"><textarea style="display: none">{{str2html .Model.BlogContent}}</textarea> </div> <div id="docEditor" class="manual-editormd-active"><textarea style="display: none">{{str2html .Model.BlogContent}}</textarea> </div>
</div> </div>
<div class="manual-editor-status"> <div class="manual-editor-status">
<div id="attachInfo" class="item">0 个附件</div> <div id="attachInfo" class="item">0 {{i18n .Lang "doc.attachments"}}</div>
</div> </div>
</div> </div>
@ -109,7 +110,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">上传附件</h4> <h4 class="modal-title" id="myModalLabel">{{i18n .Lang "doc.upload_attachment"}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="attach-drop-panel"> <div class="attach-drop-panel">
@ -146,8 +147,8 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<span id="add-error-message" class="error-message"></span> <span id="add-error-message" class="error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{i18n .Lang "common.cancel"}}</button>
<button type="button" class="btn btn-primary" id="btnUploadAttachFile" data-dismiss="modal">确定</button> <button type="button" class="btn btn-primary" id="btnUploadAttachFile" data-dismiss="modal">{{i18n .Lang "common.confirm"}}</button>
</div> </div>
</div> </div>
</form> </form>
@ -156,44 +157,44 @@
<!-- Modal --> <!-- Modal -->
<div class="modal fade" id="documentTemplateModal" tabindex="-1" role="dialog" aria-labelledby="请选择模板类型" aria-hidden="true"> <div class="modal fade" id="documentTemplateModal" tabindex="-1" role="dialog" aria-labelledby="{{i18n .Lang "doc.choose_template_type"}}" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title" id="modal-title">请选择模板类型</h4> <h4 class="modal-title" id="modal-title">{{i18n .Lang "doc.choose_template_type"}}</h4>
</div> </div>
<div class="modal-body template-list"> <div class="modal-body template-list">
<div class="container"> <div class="container">
<div class="section"> <div class="section">
<a data-type="normal" href="javascript:;"><i class="fa fa-file-o"></i></a> <a data-type="normal" href="javascript:;"><i class="fa fa-file-o"></i></a>
<h3><a data-type="normal" href="javascript:;">普通文档</a></h3> <h3><a data-type="normal" href="javascript:;">{{i18n .Lang "doc.normal_tpl"}}</a></h3>
<ul> <ul>
<li>默认类型</li> <li>{{i18n .Lang "doc.tpl_default_type"}}</li>
<li>简单的文本文档</li> <li>{{i18n .Lang "doc.tpl_plain_text"}}</li>
</ul> </ul>
</div> </div>
<div class="section"> <div class="section">
<a data-type="api" href="javascript:;"><i class="fa fa-file-code-o"></i></a> <a data-type="api" href="javascript:;"><i class="fa fa-file-code-o"></i></a>
<h3><a data-type="api" href="javascript:;">API文档</a></h3> <h3><a data-type="api" href="javascript:;">{{i18n .Lang "doc.api_tpl"}}</a></h3>
<ul> <ul>
<li>用于API文档速写</li> <li>{{i18n .Lang "doc.for_api_doc"}}</li>
<li>支持代码高亮</li> <li>{{i18n .Lang "doc.code_highlight"}}</li>
</ul> </ul>
</div> </div>
<div class="section"> <div class="section">
<a data-type="code" href="javascript:;"><i class="fa fa-book"></i></a> <a data-type="code" href="javascript:;"><i class="fa fa-book"></i></a>
<h3><a data-type="code" href="javascript:;">数据字典</a></h3> <h3><a data-type="code" href="javascript:;">{{i18n .Lang "doc.data_dict"}}</a></h3>
<ul> <ul>
<li>用于数据字典显示</li> <li>{{i18n .Lang "doc.for_data_dict"}}</li>
<li>表格支持</li> <li>{{i18n .Lang "doc.form_support"}}</li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{i18n .Lang "common.cancel"}}</button>
</div> </div>
</div> </div>
</div> </div>
@ -212,7 +213,7 @@
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}"></script> <script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}"></script>
<script src="{{cdnjs "/static/webuploader/webuploader.min.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/webuploader/webuploader.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/jstree/3.3.4/jstree.min.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/jstree/3.3.4/jstree.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/editor.md/editormd.js" "version"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/editor.md/editormd.min.js" "version"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script> <script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script>
<script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/js/array.js" "version"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/js/array.js" "version"}}" type="text/javascript"></script>
@ -220,6 +221,13 @@
<script src="{{cdnjs "/static/js/blog.js" "version"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/js/blog.js" "version"}}" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
editLangPath = {{cdnjs "/static/editor.md/languages/"}} + lang
if(lang != 'zh-CN') {
editormd.loadScript(editLangPath, function(){
window.editor.lang = editormd.defaults.lang;
window.editor.recreate()
});
}
window.vueApp.lists = {{.AttachList}}; window.vueApp.lists = {{.AttachList}};
$("#attachInfo").on("click",function () { $("#attachInfo").on("click",function () {
$("#uploadAttachModal").modal("show"); $("#uploadAttachModal").modal("show");
@ -245,7 +253,7 @@
attachment_id : file.id, attachment_id : file.id,
file_size : file.size, file_size : file.size,
file_name : file.name, file_name : file.name,
message : "正在上传" message : "{{i18n .Lang "doc.uploading"}}"
}; };
window.vueApp.lists.push(item); window.vueApp.lists.push(item);
@ -254,7 +262,8 @@
var item = window.vueApp.lists[i]; var item = window.vueApp.lists[i];
if(item.attachment_id == file.id){ if(item.attachment_id == file.id){
item.state = "error"; item.state = "error";
item.message = "上传失败:" + reason; item.message = "{{i18n .Lang "message.upload_failed"}}:" + reason;
break;
} }
} }
@ -264,7 +273,7 @@
var item = window.vueApp.lists[index]; var item = window.vueApp.lists[index];
if(item.attachment_id === file.id){ if(item.attachment_id === file.id){
if(res.errcode === 0) { if(res.errcode === 0) {
window.vueApp.lists.splice(index, 1, res.attach?res.attach:res.data); window.vueApp.lists.splice(index, 1, res.attach ? res.attach : res.data);
}else{ }else{
item.message = res.message; item.message = res.message;
item.state = "error"; item.state = "error";
@ -278,7 +287,7 @@
$percent.css( 'width', percentage * 100 + '%' ); $percent.css( 'width', percentage * 100 + '%' );
}).on("error", function (type) { }).on("error", function (type) {
if(type === "F_EXCEED_SIZE"){ if(type === "F_EXCEED_SIZE"){
layer.msg("文件超过了限定大小"); layer.msg("{{i18n .Lang "message.upload_file_size_limit"}}");
} }
console.log(type); console.log(type);
}); });

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>我的文章 - Powered by MinDoc</title> <title>{{i18n .Lang "common.my_blog"}} - Powered by MinDoc</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet" type="text/css"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet" type="text/css">
@ -27,16 +27,16 @@
<div class="row"> <div class="row">
<div class="page-left"> <div class="page-left">
<ul class="menu"> <ul class="menu">
<li {{if eq .ControllerName "BookController"}}class="active"{{end}}><a href="{{urlfor "BookController.Index"}}" class="item"><i class="fa fa-sitemap" aria-hidden="true"></i> 我的项目</a> </li> <li {{if eq .ControllerName "BookController"}}class="active"{{end}}><a href="{{urlfor "BookController.Index"}}" class="item"><i class="fa fa-sitemap" aria-hidden="true"></i> {{i18n .Lang "common.my_project"}}</a> </li>
<li {{if eq .ControllerName "BlogController"}}class="active"{{end}}><a href="{{urlfor "BlogController.ManageList"}}" class="item"><i class="fa fa-file" aria-hidden="true"></i> 我的文章</a> </li> <li {{if eq .ControllerName "BlogController"}}class="active"{{end}}><a href="{{urlfor "BlogController.ManageList"}}" class="item"><i class="fa fa-file" aria-hidden="true"></i> {{i18n .Lang "common.my_blog"}}</a> </li>
</ul> </ul>
</div> </div>
<div class="page-right"> <div class="page-right">
<div class="m-box"> <div class="m-box">
<div class="box-head"> <div class="box-head">
<strong class="box-title">文章列表</strong> <strong class="box-title">{{i18n .Lang "blog.blog_list"}}</strong>
&nbsp; &nbsp;
<a href="{{urlfor "BlogController.ManageSetting"}}" class="btn btn-success btn-sm pull-right">添加文章</a> <a href="{{urlfor "BlogController.ManageSetting"}}" class="btn btn-success btn-sm pull-right">{{i18n .Lang "blog.add_blog"}}</a>
</div> </div>
</div> </div>
<div class="box-body" id="blogList"> <div class="box-body" id="blogList">
@ -46,7 +46,7 @@
<div class="content"> <div class="content">
<a class="header" href="{{urlfor "BlogController.Index" ":id" $item.BlogId}}" target="_blank"> <a class="header" href="{{urlfor "BlogController.Index" ":id" $item.BlogId}}" target="_blank">
{{if eq $item.BlogStatus "password"}} {{if eq $item.BlogStatus "password"}}
<div class="ui teal label horizontal" data-tooltip="加密">密</div> <div class="ui teal label horizontal" data-tooltip="{{i18n $.Lang "blog.encryption"}}">{{i18n $.Lang "blog.encrypt"}}</div>
{{end}} {{end}}
{{$item.BlogTitle}} {{$item.BlogTitle}}
</a> </a>
@ -57,16 +57,16 @@
<div> <div>
<div class="ui horizontal small list"> <div class="ui horizontal small list">
<div class="item"><i class="fa fa-clock-o"></i> {{date $item.Modified "Y-m-d H:i:s"}}</div> <div class="item"><i class="fa fa-clock-o"></i> {{date $item.Modified "Y-m-d H:i:s"}}</div>
<div class="item"><a href="{{urlfor "BlogController.ManageEdit" ":id" $item.BlogId}}" title="文章编辑" target="_blank"><i class="fa fa-edit"></i> 编辑</a></div> <div class="item"><a href="{{urlfor "BlogController.ManageEdit" ":id" $item.BlogId}}" title="{{i18n $.Lang "blog.edit_blog"}}" target="_blank"><i class="fa fa-edit"></i> {{i18n $.Lang "blog.edit"}}</a></div>
<div class="item"><a class="delete-btn" title="删除文章" data-id="{{$item.BlogId}}"><i class="fa fa-trash"></i> 删除</a></div> <div class="item"><a class="delete-btn" title="{{i18n $.Lang "blog.delete_blog"}}" data-id="{{$item.BlogId}}"><i class="fa fa-trash"></i> {{i18n $.Lang "blog.delete"}}</a></div>
<div class="item"><a href="{{urlfor "BlogController.ManageSetting" ":id" $item.BlogId}}" title="文章设置" class="setting-btn"><i class="fa fa-gear"></i> 设置</a></div> <div class="item"><a href="{{urlfor "BlogController.ManageSetting" ":id" $item.BlogId}}" title="{{i18n $.Lang "blog.setting_blog"}}" class="setting-btn"><i class="fa fa-gear"></i> {{i18n $.Lang "common.setting"}}</a></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{{else}} {{else}}
<div class="text-center">暂无文章</div> <div class="text-center">{{i18n .Lang "blog.no_blog"}}</div>
{{end}} {{end}}
</div> </div>
<nav class="pagination-container"> <nav class="pagination-container">
@ -88,17 +88,17 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">删除文章</h4> <h4 class="modal-title">{{i18n .Lang "blog.delete_blog"}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<span style="font-size: 14px;font-weight: 400;">确定删除文章吗?</span> <span style="font-size: 14px;font-weight: 400;">{{i18n .Lang "message.confirm_delete_blog"}}</span>
<p></p> <p></p>
<p class="text error-message">删除文章后将无法找回。</p> <p class="text error-message">{{i18n .Lang "message.delete_blog_tips"}}</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<span id="form-error-message2" class="error-message"></span> <span id="form-error-message2" class="error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{i18n .Lang "common.cancel"}}</button>
<button type="submit" id="btnDeleteBlog" class="btn btn-primary" data-loading-text="删除中...">确定删除</button> <button type="submit" id="btnDeleteBlog" class="btn btn-primary" data-loading-text="{{i18n .Lang "message.process"}}">{{i18n .Lang "common.confirm_delete"}}</button>
</div> </div>
</div> </div>
</form> </form>
@ -140,7 +140,7 @@
}, },
error : function () { error : function () {
layer.close(index); layer.close(index);
layer.msg('服务器异常'); layer.msg({{i18n .Lang "message.system_error"}});
} }
}); });
} }
@ -166,7 +166,7 @@
$("#btnDeleteBlog").button("reset"); $("#btnDeleteBlog").button("reset");
}, },
error : function () { error : function () {
showError("服务器异常","#form-error-message2"); showError({{i18n .Lang "message.system_error"}},"#form-error-message2");
$("#btnDeleteBlog").button("reset"); $("#btnDeleteBlog").button("reset");
} }
}); });

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>文章设置 - Powered by MinDoc</title> <title>{{i18n .Lang "blog.blog_setting"}} - Powered by MinDoc</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -25,14 +25,14 @@
<div class="row"> <div class="row">
<div class="page-left"> <div class="page-left">
<ul class="menu"> <ul class="menu">
<li {{if eq .ControllerName "BookController"}}class="active"{{end}}><a href="{{urlfor "BookController.Index"}}" class="item"><i class="fa fa-sitemap" aria-hidden="true"></i> 我的项目</a> </li> <li {{if eq .ControllerName "BookController"}}class="active"{{end}}><a href="{{urlfor "BookController.Index"}}" class="item"><i class="fa fa-sitemap" aria-hidden="true"></i> {{i18n .Lang "common.my_project"}}</a> </li>
<li {{if eq .ControllerName "BlogController"}}class="active"{{end}}><a href="{{urlfor "BlogController.ManageList"}}" class="item"><i class="fa fa-file" aria-hidden="true"></i> 我的文章</a> </li> <li {{if eq .ControllerName "BlogController"}}class="active"{{end}}><a href="{{urlfor "BlogController.ManageList"}}" class="item"><i class="fa fa-file" aria-hidden="true"></i> {{i18n .Lang "common.my_blog"}}</a> </li>
</ul> </ul>
</div> </div>
<div class="page-right"> <div class="page-right">
<div class="m-box"> <div class="m-box">
<div class="box-head"> <div class="box-head">
<strong class="box-title"> 文章设置</strong> <strong class="box-title"> {{i18n .Lang "blog.blog_setting"}}</strong>
</div> </div>
</div> </div>
<div class="box-body"> <div class="box-body">
@ -42,58 +42,58 @@
<input type="hidden" name="document_id" value="{{.Model.DocumentId}}"> <input type="hidden" name="document_id" value="{{.Model.DocumentId}}">
<input type="hidden" name="order_index" value="{{.Model.OrderIndex}}"> <input type="hidden" name="order_index" value="{{.Model.OrderIndex}}">
<div class="form-group"> <div class="form-group">
<label>文章标题</label> <label>{{i18n .Lang "blog.title"}}</label>
<input type="text" class="form-control" name="title" id="title" placeholder="文章标题" value="{{.Model.BlogTitle}}"> <input type="text" class="form-control" name="title" id="title" placeholder="{{i18n .Lang "blog.title"}}" value="{{.Model.BlogTitle}}">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>文章类型</label> <label>{{i18n .Lang "blog.type"}}</label>
<div class="radio"> <div class="radio">
<label class="radio-inline"> <label class="radio-inline">
<input type="radio" {{if eq .Model.BlogType 0}}checked{{end}} name="blog_type" value="0">普通文章<span class="text"></span> <input type="radio" {{if eq .Model.BlogType 0}}checked{{end}} name="blog_type" value="0">{{i18n .Lang "blog.normal_blog"}}<span class="text"></span>
</label> </label>
<label class="radio-inline"> <label class="radio-inline">
<input type="radio" {{if eq .Model.BlogType 1}}checked{{end}} name="blog_type" value="1">链接文章<span class="text"></span> <input type="radio" {{if eq .Model.BlogType 1}}checked{{end}} name="blog_type" value="1">{{i18n .Lang "blog.link_blog"}}<span class="text"></span>
</label> </label>
</div> </div>
</div> </div>
<div class="form-group" id="blogLinkDocument"{{if ne .Model.BlogType 1}} style="display: none;" {{end}}> <div class="form-group" id="blogLinkDocument"{{if ne .Model.BlogType 1}} style="display: none;" {{end}}>
<label>关联文档</label> <label>{{i18n .Lang "blog.ref_doc"}}</label>
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<input type="text" class="form-control" placeholder="请输入项目标识" name="bookIdentify" value="{{.Model.BookIdentify}}"> <input type="text" class="form-control" placeholder="{{i18n .Lang "message.input_proj_id_pls"}}" name="bookIdentify" value="{{.Model.BookIdentify}}">
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<input type="text" class="form-control" placeholder="请输入文档标识" name="documentIdentify" value="{{.Model.DocumentIdentify}}"> <input type="text" class="form-control" placeholder="{{i18n .Lang "message.input_doc_id_pls"}}" name="documentIdentify" value="{{.Model.DocumentIdentify}}">
</div> </div>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>文章状态</label> <label>{{i18n .Lang "blog.blog_status"}}</label>
<div class="radio"> <div class="radio">
<label class="radio-inline"> <label class="radio-inline">
<input type="radio" {{if eq .Model.BlogStatus "public"}}checked{{end}} name="status" value="public">公开<span class="text"></span> <input type="radio" {{if eq .Model.BlogStatus "public"}}checked{{end}} name="status" value="public">{{i18n .Lang "blog.public"}}<span class="text"></span>
</label> </label>
<label class="radio-inline"> <label class="radio-inline">
<input type="radio" {{if eq .Model.BlogStatus "password"}}checked{{end}} name="status" value="password">加密<span class="text"></span> <input type="radio" {{if eq .Model.BlogStatus "password"}}checked{{end}} name="status" value="password">{{i18n .Lang "blog.encryption"}}<span class="text"></span>
</label> </label>
</div> </div>
</div> </div>
<div class="form-group"{{if eq .Model.BlogStatus "public"}} style="display: none;"{{end}} id="blogPassword"> <div class="form-group"{{if eq .Model.BlogStatus "public"}} style="display: none;"{{end}} id="blogPassword">
<label>文章密码</label> <label>{{i18n .Lang "blog.blog_pwd"}}</label>
<input type="password" class="form-control" name="password" id="password" placeholder="文章密码" value="{{.Model.Password}}" maxlength="20"> <input type="password" class="form-control" name="password" id="password" placeholder="{{i18n .Lang "blog.blog_pwd"}}" value="{{.Model.Password}}" maxlength="20">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>文章摘要</label> <label>{{i18n .Lang "blog.blog_digest"}}</label>
<textarea rows="3" class="form-control" name="excerpt" style="height: 90px" placeholder="项目描述">{{.Model.BlogExcerpt}}</textarea> <textarea rows="3" class="form-control" name="excerpt" style="height: 90px" placeholder="{{i18n .Lang "blog.blog_digest"}}">{{.Model.BlogExcerpt}}</textarea>
<p class="text">文章摘要不超过500个字符</p> <p class="text">{{i18n .Lang "message.blog_digest_tips"}}</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" id="btnSaveBlogInfo" class="btn btn-success" data-loading-text="保存中...">保存</button> <button type="submit" id="btnSaveBlogInfo" class="btn btn-success" data-loading-text="{{i18n .Lang "message.processing"}}">{{i18n .Lang "common.save"}}</button>
<a href="{{.Referer}}" title="返回" class="btn btn-info">返回</a> <a href="{{.Referer}}" title="{{i18n .Lang "doc.backward"}}" class="btn btn-info">{{i18n .Lang "doc.backward"}}</a>
<span id="form-error-message" class="error-message"></span> <span id="form-error-message" class="error-message"></span>
</div> </div>
</form> </form>
@ -119,19 +119,19 @@
var title = $.trim($("#title").val()); var title = $.trim($("#title").val());
if (title === ""){ if (title === ""){
return showError("文章标题不能为空"); return showError("{{i18n .Lang "message.blog_title_empty"}}");
} }
$("#btnSaveBlogInfo").button("loading"); $("#btnSaveBlogInfo").button("loading");
},success : function ($res) { },success : function ($res) {
if($res.errcode === 0) { if($res.errcode === 0) {
showSuccess("保存成功"); showSuccess("{{i18n .Lang "message.success"}}");
$("#blogId").val($res.data.blog_id); $("#blogId").val($res.data.blog_id);
}else{ }else{
showError($res.message); showError($res.message);
} }
$("#btnSaveBlogInfo").button("reset"); $("#btnSaveBlogInfo").button("reset");
}, error : function () { }, error : function () {
showError("服务器异常."); showError("{{i18n .Lang "message.system_error"}}");
$("#btnSaveBlogInfo").button("reset"); $("#btnSaveBlogInfo").button("reset");
} }
}).find("input[name='status']").change(function () { }).find("input[name='status']").change(function () {

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>概要 - Powered by MinDoc</title> <title>{{i18n $.Lang "blog.summary"}} - Powered by MinDoc</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -26,11 +26,11 @@
<div class="row"> <div class="row">
<div class="page-left"> <div class="page-left">
<ul class="menu"> <ul class="menu">
<li class="active"><a href="{{urlfor "BookController.Dashboard" ":key" .Model.Identify}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a> </li> <li class="active"><a href="{{urlfor "BookController.Dashboard" ":key" .Model.Identify}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> {{i18n $.Lang "blog.summary"}}</a> </li>
{{if eq .Model.RoleId 0 1}} {{if eq .Model.RoleId 0 1}}
<li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-user" aria-hidden="true"></i> 成员</a> </li> <li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-user" aria-hidden="true"></i> {{i18n $.Lang "blog.member"}}</a> </li>
<li><a href="{{urlfor "BookController.Team" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" aria-hidden="true"></i> 团队</a> </li> <li><a href="{{urlfor "BookController.Team" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" aria-hidden="true"></i> {{i18n $.Lang "blog.team"}}</a> </li>
<li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li> <li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> {{i18n $.Lang "common.setting"}}</a> </li>
{{end}} {{end}}
</ul> </ul>
@ -40,19 +40,19 @@
<div class="box-head"> <div class="box-head">
<strong class="box-title"> <strong class="box-title">
{{if eq .Model.PrivatelyOwned 0}} {{if eq .Model.PrivatelyOwned 0}}
<i class="fa fa-unlock" aria-hidden="true" title="公开项目" data-toggle="tooltip"></i> <i class="fa fa-unlock" aria-hidden="true" title="{{i18n $.Lang "blog.public_project"}}" data-toggle="tooltip"></i>
{{else}} {{else}}
<i class="fa fa-lock" aria-hidden="true" title="私有项目" data-toggle="tooltip"></i> <i class="fa fa-lock" aria-hidden="true" title="{{i18n $.Lang "blog.private_project"}}" data-toggle="tooltip"></i>
{{end}} {{end}}
{{.Model.BookName}} {{.Model.BookName}}
</strong> </strong>
{{if ne .Model.RoleId 3}} {{if ne .Model.RoleId 3}}
<a href="{{urlfor "DocumentController.Edit" ":key" .Model.Identify ":id" ""}}" class="btn btn-default btn-sm pull-right" target="_blank"><i class="fa fa-edit" aria-hidden="true"></i> 编辑</a> <a href="{{urlfor "DocumentController.Edit" ":key" .Model.Identify ":id" ""}}" class="btn btn-default btn-sm pull-right" target="_blank"><i class="fa fa-edit" aria-hidden="true"></i> {{i18n $.Lang "blog.edit"}}</a>
{{end}} {{end}}
<a href="{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" target="_blank"><i class="fa fa-eye"></i> 阅读</a> <a href="{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" target="_blank"><i class="fa fa-eye"></i> {{i18n $.Lang "blog.read"}}</a>
{{if eq .Model.RoleId 0 1 2}} {{if eq .Model.RoleId 0 1 2}}
<button class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" id="btnRelease"><i class="fa fa-upload" aria-hidden="true"></i> 发布</button> <button class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" id="btnRelease"><i class="fa fa-upload" aria-hidden="true"></i> {{i18n $.Lang "blog.publish"}}</button>
{{end}} {{end}}
</div> </div>
</div> </div>
@ -65,32 +65,32 @@
</div> </div>
<div class="list"> <div class="list">
<span class="title">创建者</span> <span class="title">{{i18n $.Lang "blog.creator"}}</span>
<span class="body">{{.Model.CreateName}}</span> <span class="body">{{.Model.CreateName}}</span>
</div> </div>
<div class="list"> <div class="list">
<span class="title">文档数量</span> <span class="title">{{i18n $.Lang "blog.doc_amount"}}</span>
<span class="body">{{.Model.DocCount}} </span> <span class="body">{{.Model.DocCount}} {{i18n $.Lang "blog.doc_unit"}}</span>
</div> </div>
<div class="list"> <div class="list">
<span class="title">创建时间</span> <span class="title">{{i18n $.Lang "blog.create_time"}}</span>
<span class="body"> {{date_format .Model.CreateTime "2006-01-02 15:04:05"}} </span> <span class="body"> {{date_format .Model.CreateTime "2006-01-02 15:04:05"}} </span>
</div> </div>
<div class="list"> <div class="list">
<span class="title">修改时间</span> <span class="title">{{i18n $.Lang "blog.update_time"}}</span>
<span class="body"> {{date_format .Model.CreateTime "2006-01-02 15:04:05"}} </span> <span class="body"> {{date_format .Model.CreateTime "2006-01-02 15:04:05"}} </span>
</div> </div>
<div class="list"> <div class="list">
<span class="title">担任角色</span> <span class="title">{{i18n $.Lang "blog.project_role"}}</span>
<span class="body">{{.Model.RoleName}}</span> <span class="body">{{.Model.RoleName}}</span>
</div> </div>
<!-- {{/* <div class="list"> <!-- {{/* <div class="list">
<span class="title">评论数量</span> <span class="title">{{i18n $.Lang "blog.comment_amount"}}</span>
<span class="body">{{.Model.CommentCount}} </span> <span class="body">{{.Model.CommentCount}} {{i18n $.Lang "blog.comment_unit"}}</span>
</div>*/}} </div>*/}}
--> -->
<div class="list"> <div class="list">
<span class="title">文档标签</span> <span class="title">{{i18n $.Lang "blog.project_desc"}}</span>
<span class="body">{{.Model.Label}}</span> <span class="body">{{.Model.Label}}</span>
</div> </div>
<div class="summary">{{.Description}} </div> <div class="summary">{{.Description}} </div>
@ -116,7 +116,7 @@
dataType : "json", dataType : "json",
success : function (res) { success : function (res) {
if(res.errcode === 0){ if(res.errcode === 0){
layer.msg("发布任务已推送到任务队列,稍后将在后台执行。"); layer.msg("{{i18n $.Lang "message.publish_to_queue"}}");
}else{ }else{
layer.msg(res.message); layer.msg(res.message);
} }

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>我的项目 - Powered by MinDoc</title> <title>{{i18n $.Lang "common.my_project"}} - Powered by MinDoc</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet" type="text/css"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet" type="text/css">
@ -28,30 +28,30 @@
<div class="row"> <div class="row">
<div class="page-left"> <div class="page-left">
<ul class="menu"> <ul class="menu">
<li {{if eq .ControllerName "BookController"}}class="active"{{end}}><a href="{{urlfor "BookController.Index"}}" class="item"><i class="fa fa-sitemap" aria-hidden="true"></i> 我的项目</a> </li> <li {{if eq .ControllerName "BookController"}}class="active"{{end}}><a href="{{urlfor "BookController.Index"}}" class="item"><i class="fa fa-sitemap" aria-hidden="true"></i> {{i18n $.Lang "common.my_project"}}</a> </li>
<li {{if eq .ControllerName "BlogController"}}class="active"{{end}}><a href="{{urlfor "BlogController.ManageList"}}" class="item"><i class="fa fa-file" aria-hidden="true"></i> 我的文章</a> </li> <li {{if eq .ControllerName "BlogController"}}class="active"{{end}}><a href="{{urlfor "BlogController.ManageList"}}" class="item"><i class="fa fa-file" aria-hidden="true"></i> {{i18n $.Lang "common.my_blog"}}</a> </li>
</ul> </ul>
</div> </div>
<div class="page-right"> <div class="page-right">
<div class="m-box"> <div class="m-box">
<div class="box-head"> <div class="box-head">
<strong class="box-title">项目列表</strong> <strong class="box-title">{{i18n $.Lang "blog.project_list"}}</strong>
&nbsp; &nbsp;
<button type="button" data-toggle="modal" data-target="#addBookDialogModal" class="btn btn-success btn-sm pull-right">添加项目</button> <button type="button" data-toggle="modal" data-target="#addBookDialogModal" class="btn btn-success btn-sm pull-right">{{i18n $.Lang "blog.add_project"}}</button>
<button type="button" data-toggle="modal" data-target="#importBookDialogModal" class="btn btn-primary btn-sm pull-right" style="margin-right: 5px;">导入项目</button> <button type="button" data-toggle="modal" data-target="#importBookDialogModal" class="btn btn-primary btn-sm pull-right" style="margin-right: 5px;">{{i18n $.Lang "blog.import_project"}}</button>
</div> </div>
</div> </div>
<div class="box-body" id="bookList"> <div class="box-body" id="bookList">
<div class="book-list"> <div class="book-list">
<template v-if="lists.length <= 0"> <template v-if="lists.length <= 0">
<div class="text-center">暂无数据</div> <div class="text-center">{{i18n $.Lang "message.no_data"}}</div>
</template> </template>
<template v-else> <template v-else>
<div class="list-item" v-for="item in lists"> <div class="list-item" v-for="item in lists">
<div class="book-title"> <div class="book-title">
<div class="pull-left"> <div class="pull-left">
<a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" title="项目概要" data-toggle="tooltip"> <a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" title="{{i18n $.Lang "blog.project_summary"}}" data-toggle="tooltip">
<template v-if="item.privately_owned == 0"> <template v-if="item.privately_owned == 0">
<i class="fa fa-unlock" aria-hidden="true"></i> <i class="fa fa-unlock" aria-hidden="true"></i>
</template> </template>
@ -63,28 +63,28 @@
</div> </div>
<div class="pull-right"> <div class="pull-right">
<div class="btn-group"> <div class="btn-group">
<a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" class="btn btn-default">设置</a> <a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" class="btn btn-default">{{i18n $.Lang "common.setting"}}</a>
<a href="javascript:;" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a href="javascript:;" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span> <span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span> <span class="sr-only">Toggle Dropdown</span>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a :href="'{{urlfor "DocumentController.Index" ":key" ""}}' + item.identify" target="_blank">阅读</a></li> <li><a :href="'{{urlfor "DocumentController.Index" ":key" ""}}' + item.identify" target="_blank">{{i18n $.Lang "blog.read"}}</a></li>
<template v-if="item.role_id != 3"> <template v-if="item.role_id != 3">
<li><a :href="'{{.BaseUrl}}/api/' + item.identify + '/edit'" target="_blank">编辑</a></li> <li><a :href="'{{.BaseUrl}}/api/' + item.identify + '/edit'" target="_blank">{{i18n $.Lang "blog.edit"}}</a></li>
</template> </template>
<template v-if="item.role_id == 0"> <template v-if="item.role_id == 0">
<li><a :href="'javascript:deleteBook(\''+item.identify+'\');'">删除</a></li> <li><a :href="'javascript:deleteBook(\''+item.identify+'\');'">{{i18n $.Lang "blog.delete"}}</a></li>
<li><a :href="'javascript:copyBook(\''+item.identify+'\');'">复制</a></li> <li><a :href="'javascript:copyBook(\''+item.identify+'\');'">{{i18n $.Lang "blog.copy"}}</a></li>
</template> </template>
</ul> </ul>
</div> </div>
{{/*<a :href="'{{urlfor "DocumentController.Index" ":key" ""}}' + item.identify" title="查看文档" data-toggle="tooltip" target="_blank"><i class="fa fa-eye"></i> </a>*/}} {{/*<a :href="'{{urlfor "DocumentController.Index" ":key" ""}}' + item.identify" title="{{i18n $.Lang "blog.view"}}" data-toggle="tooltip" target="_blank"><i class="fa fa-eye"></i> {{i18n $.Lang "blog.view"}}</a>*/}}
{{/*<template v-if="item.role_id != 3">*/}} {{/*<template v-if="item.role_id != 3">*/}}
{{/*<a :href="'/api/' + item.identify + '/edit'" title="编辑文档" data-toggle="tooltip" target="_blank"><i class="fa fa-edit" aria-hidden="true"></i> </a>*/}} {{/*<a :href="'/api/' + item.identify + '/edit'" title="{{i18n $.Lang "blog.edit_doc"}}" data-toggle="tooltip" target="_blank"><i class="fa fa-edit" aria-hidden="true"></i> {{i18n $.Lang "blog.edit_doc"}}</a>*/}}
{{/*</template>*/}} {{/*</template>*/}}
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
@ -94,21 +94,21 @@
&nbsp; &nbsp;
</template> </template>
<template v-else=""> <template v-else="">
<a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" title="项目概要" style="font-size: 12px;"> <a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" title="{{i18n $.Lang "blog.project_summary"}}" style="font-size: 12px;">
${item.description} ${item.description}
</a> </a>
</template> </template>
</div> </div>
<div class="info"> <div class="info">
<span title="创建时间" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-clock-o"></i> <span title="{{i18n $.Lang "blog.create_time"}}" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-clock-o"></i>
${(new Date(item.create_time)).format("yyyy-MM-dd hh:mm:ss")} ${(new Date(item.create_time)).format("yyyy-MM-dd hh:mm:ss")}
</span> </span>
<span title="创建者" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-user"></i> ${item.create_name}</span> <span title="{{i18n $.Lang "blog.creator"}}" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-user"></i> ${item.create_name}</span>
<span title="文档数量" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-pie-chart"></i> ${item.doc_count}</span> <span title="{{i18n $.Lang "blog.doc_amount"}}" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-pie-chart"></i> ${item.doc_count}</span>
<span title="项目角色" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-user-secret"></i> ${item.role_name}</span> <span title="{{i18n $.Lang "blog.project_role"}}" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-user-secret"></i> ${item.role_name}</span>
<template v-if="item.last_modify_text !== ''"> <template v-if="item.last_modify_text !== ''">
<span title="最后编辑" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-pencil"></i> 最后编辑: ${item.last_modify_text}</span> <span title="{{i18n $.Lang "blog.last_edit"}}" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-pencil"></i> {{i18n $.Lang "blog.last_edit"}}: ${item.last_modify_text}</span>
</template> </template>
</div> </div>
@ -133,49 +133,49 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">添加项目</h4> <h4 class="modal-title" id="myModalLabel">{{i18n $.Lang "blog.add_project"}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<div class="pull-left" style="width: 620px"> <div class="pull-left" style="width: 620px">
<div class="form-group required"> <div class="form-group required">
<label class="text-label col-sm-2">项目空间</label> <label class="text-label col-sm-2">{{i18n $.Lang "common.project_space"}}</label>
<div class="col-sm-10"> <div class="col-sm-10">
<select class="js-data-example-ajax-add form-control" multiple="multiple" name="itemId" id="itemId"> <select class="js-data-example-ajax-add form-control" multiple="multiple" name="itemId" id="itemId">
{{if .Item}}<option value="{{.Item.ItemId}}" selected>{{.Item.ItemName}}</option> {{end}} {{if .Item}}<option value="{{.Item.ItemId}}" selected>{{.Item.ItemName}}</option> {{end}}
</select> </select>
<p class="text">每个项目必须归属一个项目空间,超级管理员可在后台管理和维护</p> <p class="text">{{i18n $.Lang "message.project_must_belong_space"}}</p>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="form-group required"> <div class="form-group required">
<label class="text-label col-sm-2">项目标题</label> <label class="text-label col-sm-2">{{i18n $.Lang "blog.project_title"}}</label>
<div class="col-sm-10"> <div class="col-sm-10">
<input type="text" class="form-control" placeholder="标题(不超过100字)" name="book_name" id="bookName"> <input type="text" class="form-control" placeholder="{{i18n $.Lang "message.project_title_placeholder"}}" name="book_name" id="bookName">
<p class="text">项目名称不能超过100字符</p> <p class="text">{{i18n $.Lang "message.project_title_tips"}}</p>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="form-group required"> <div class="form-group required">
<label class="text-label col-sm-2">项目标识</label> <label class="text-label col-sm-2">{{i18n $.Lang "blog.project_id"}}</label>
<div class="col-sm-10"> <div class="col-sm-10">
<input type="text" class="form-control" placeholder="项目唯一标识(不超过50字)" name="identify" id="identify"> <input type="text" class="form-control" placeholder="{{i18n $.Lang "message.project_id_placeholder"}}" name="identify" id="identify">
<p class="text">文档标识只能包含小写字母、数字,以及“-”、“.”和“_”符号.</p> <p class="text">{{i18n $.Lang "message.project_id_tips"}}</p>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<textarea name="description" id="description" class="form-control" placeholder="描述信息不超过500个字符" style="height: 90px;"></textarea> <textarea name="description" id="description" class="form-control" placeholder="{{i18n $.Lang "message.project_desc_placeholder"}}" style="height: 90px;"></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-lg-6"> <div class="col-lg-4">
<label> <label>
<input type="radio" name="privately_owned" value="0" checked> 公开<span class="text">(任何人都可以访问)</span> <input type="radio" name="privately_owned" value="0" checked> {{i18n $.Lang "blog.public"}}<span class="text">{{i18n $.Lang "message.project_public_desc"}}</span>
</label> </label>
</div> </div>
<div class="col-lg-6"> <div class="col-lg-8">
<label> <label>
<input type="radio" name="privately_owned" value="1"> 私有<span class="text">(只要参与者或使用令牌才能访问)</span> <input type="radio" name="privately_owned" value="1"> {{i18n $.Lang "blog.private"}}<span class="text">{{i18n $.Lang "message.project_private_desc"}}</span>
</label> </label>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
@ -183,7 +183,7 @@
</div> </div>
<div class="pull-right text-center" style="width: 235px;"> <div class="pull-right text-center" style="width: 235px;">
<canvas id="bookCover" height="230px" width="170px"><img src="{{cdnimg "/static/images/book.jpg"}}"> </canvas> <canvas id="bookCover" height="230px" width="170px"><img src="{{cdnimg "/static/images/book.jpg"}}"> </canvas>
<p class="text">项目图片可在项目设置中修改</p> <p class="text">{{i18n $.Lang "message.project_cover_desc"}}</p>
</div> </div>
</div> </div>
@ -192,8 +192,8 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<span id="form-error-message"></span> <span id="form-error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{i18n $.Lang "common.cancel"}}</button>
<button type="button" class="btn btn-success" id="btnSaveDocument" data-loading-text="保存中...">保存</button> <button type="button" class="btn btn-success" id="btnSaveDocument" data-loading-text="{{i18n $.Lang "common.processing"}}">{{i18n $.Lang "common.save"}}</button>
</div> </div>
</div> </div>
</form> </form>
@ -207,48 +207,48 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">导入项目</h4> <h4 class="modal-title">{{i18n $.Lang "blog.import_project"}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<div class="form-group required"> <div class="form-group required">
<label class="text-label">项目空间</label> <label class="text-label">{{i18n $.Lang "common.project_space"}}</label>
<select class="js-data-example-ajax-import form-control" multiple="multiple" name="itemId"> <select class="js-data-example-ajax-import form-control" multiple="multiple" name="itemId">
{{if .Item}}<option value="{{.Item.ItemId}}" selected>{{.Item.ItemName}}</option> {{end}} {{if .Item}}<option value="{{.Item.ItemId}}" selected>{{.Item.ItemName}}</option> {{end}}
</select> </select>
<p class="text">每个项目必须归属一个项目空间,超级管理员可在后台管理和维护</p> <p class="text">{{i18n $.Lang "message.project_must_belong_space"}}</p>
</div> </div>
<div class="form-group required"> <div class="form-group required">
<label class="text-label">项目标题</label> <label class="text-label">{{i18n $.Lang "blog.project_title"}}</label>
<input type="text" class="form-control" placeholder="项目标题(不超过100字)" name="book_name" maxlength="100" value=""> <input type="text" class="form-control" placeholder="{{i18n $.Lang "message.project_title_placeholder"}}" name="book_name" maxlength="100" value="">
<p class="text">项目名称不能超过100字符</p> <p class="text">{{i18n $.Lang "blog.project_title_tips"}}</p>
</div> </div>
<div class="form-group required"> <div class="form-group required">
<label class="text-label">项目标识</label> <label class="text-label">{{i18n $.Lang "blog.project_id"}}</label>
<input type="text" class="form-control" placeholder="项目唯一标识(不超过50字)" name="identify" value=""> <input type="text" class="form-control" placeholder="{{i18n $.Lang "message.project_id_placeholder"}}" name="identify" value="">
<div class="clearfix"></div> <div class="clearfix"></div>
<p class="text">文档标识只能包含小写字母、数字,以及“-”、“.”和“_”符号.</p> <p class="text">{{i18n $.Lang "blog.project_id_tips"}}</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="text-label">项目描述</label> <label class="text-label">{{i18n $.Lang "blog.project_desc"}}</label>
<textarea name="description" id="description" class="form-control" placeholder="描述信息不超过500个字符" style="height: 90px;"></textarea> <textarea name="description" id="description" class="form-control" placeholder="{{i18n $.Lang "message.project_desc_placeholder"}}" style="height: 90px;"></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-lg-6"> <div class="col-lg-4">
<label> <label>
<input type="radio" name="privately_owned" value="0" checked> 公开<span class="text">(任何人都可以访问)</span> <input type="radio" name="privately_owned" value="0" checked> {{i18n $.Lang "blog.public"}}<span class="text">{{i18n $.Lang "message.project_public_desc"}}</span>
</label> </label>
</div> </div>
<div class="col-lg-6"> <div class="col-lg-8">
<label> <label>
<input type="radio" name="privately_owned" value="1"> 私有<span class="text">(只要参与者或使用令牌才能访问)</span> <input type="radio" name="privately_owned" value="1"> {{i18n $.Lang "blog.private"}}<span class="text">{{i18n $.Lang "message.project_private_desc"}}</span>
</label> </label>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="file-loading"> <div class="file-loading">
<input id="import-book-upload" name="import-file" type="file" accept=".zip"> <input id="import-book-upload" name="import-file" type="file" accept=".zip,.docx">
</div> </div>
<div id="kartik-file-errors"></div> <div id="kartik-file-errors"></div>
</div> </div>
@ -257,8 +257,8 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<span id="import-book-form-error-message" style="background-color: #ffffff;border: none;margin: 0;padding: 0;"></span> <span id="import-book-form-error-message" style="background-color: #ffffff;border: none;margin: 0;padding: 0;"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{i18n $.Lang "common.cancel"}}</button>
<button type="button" class="btn btn-success" id="btnImportBook" data-loading-text="创建中...">创建</button> <button type="button" class="btn btn-success" id="btnImportBook" data-loading-text="{{i18n $.Lang "common.processing"}}">{{i18n $.Lang "common.create"}}</button>
</div> </div>
</div> </div>
</form> </form>
@ -273,17 +273,17 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">删除项目</h4> <h4 class="modal-title">{{i18n $.Lang "blog.delete_project"}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<span style="font-size: 14px;font-weight: 400;">确定删除项目吗?</span> <span style="font-size: 14px;font-weight: 400;">{{i18n $.Lang "message.confirm_delete_project"}}</span>
<p></p> <p></p>
<p class="text error-message">删除项目后将无法找回。</p> <p class="text error-message">{{i18n $.Lang "message.warning_delete_project"}}</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<span id="form-error-message2" class="error-message"></span> <span id="form-error-message2" class="error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{i18n $.Lang "common.cancel"}}</button>
<button type="submit" id="btnDeleteBook" class="btn btn-primary" data-loading-text="删除中...">确定删除</button> <button type="submit" id="btnDeleteBook" class="btn btn-primary" data-loading-text="{{i18n $.Lang "common.processing"}}">{{i18n $.Lang "common.confirm_delete"}}</button>
</div> </div>
</div> </div>
</form> </form>
@ -415,7 +415,7 @@
}, },
error : function () { error : function () {
layer.close(index); layer.close(index);
layer.msg('服务器异常'); layer.msg('{{i18n $.Lang "message.system_error"}}');
} }
}); });
} }
@ -426,9 +426,9 @@
* */ * */
$("#addBookDialogModal").on("show.bs.modal",function () { $("#addBookDialogModal").on("show.bs.modal",function () {
window.bookDialogModal = $(this).find("#addBookDialogForm").html(); window.bookDialogModal = $(this).find("#addBookDialogForm").html();
drawBookCover("bookCover","默认封面"); drawBookCover("bookCover","{{i18n $.Lang "blog.default_cover"}}");
$('.js-data-example-ajax-add').select2({ $('.js-data-example-ajax-add').select2({
language: "zh-CN", language: "{{i18n $.Lang "common.js_lang"}}",
minimumInputLength : 1, minimumInputLength : 1,
minimumResultsForSearch: Infinity, minimumResultsForSearch: Infinity,
maximumSelectionLength:1, maximumSelectionLength:1,
@ -464,9 +464,9 @@
'showUpload' : false, 'showUpload' : false,
'required': true, 'required': true,
'validateInitialCount': true, 'validateInitialCount': true,
"language" : "zh", "language" : "{{i18n $.Lang "common.upload_lang"}}",
'allowedFileExtensions': ['zip'], 'allowedFileExtensions': ['zip', 'docx'],
'msgPlaceholder' : '请选择Zip文件', 'msgPlaceholder' : '{{i18n $.Lang "message.file_type_placeholder"}}',
'elErrorContainer' : "#import-book-form-error-message", 'elErrorContainer' : "#import-book-form-error-message",
'uploadExtraData' : function () { 'uploadExtraData' : function () {
var book = {}; var book = {};
@ -480,7 +480,7 @@
} }
}); });
$('.js-data-example-ajax-import').select2({ $('.js-data-example-ajax-import').select2({
language: "zh-CN", language: "{{i18n $.Lang "common.js_lang"}}",
minimumInputLength : 1, minimumInputLength : 1,
minimumResultsForSearch: Infinity, minimumResultsForSearch: Infinity,
maximumSelectionLength:1, maximumSelectionLength:1,
@ -513,27 +513,27 @@
var itemId = $("#itemId").val(); var itemId = $("#itemId").val();
if (itemId <= 0) { if (itemId <= 0) {
return showError("请选择项目空间") return showError("{{i18n $.Lang "message.project_space_empty"}}")
} }
var bookName = $.trim($("#bookName").val()); var bookName = $.trim($("#bookName").val());
if (bookName === "") { if (bookName === "") {
return showError("项目标题不能为空") return showError("{{i18n $.Lang "message.project_title_empty"}}")
} }
if (bookName.length > 100) { if (bookName.length > 100) {
return showError("项目标题必须小于100字符"); return showError("{{i18n $.Lang "message.project_title_tips"}}");
} }
var identify = $.trim($("#identify").val()); var identify = $.trim($("#identify").val());
if (identify === "") { if (identify === "") {
return showError("项目标识不能为空"); return showError("{{i18n $.Lang "message.project_id_empty"}}");
} }
if (identify.length > 50) { if (identify.length > 50) {
return showError("项目标识必须小于50字符"); return showError("{{i18n $.Lang "message.project_id_length"}}");
} }
var description = $.trim($("#description").val()); var description = $.trim($("#description").val());
if (description.length > 500) { if (description.length > 500) {
return showError("描述信息不超过500个字符"); return showError("{{i18n $.Lang "message.project_desc_placeholder"}}");
} }
$this.button("loading"); $this.button("loading");
@ -567,7 +567,7 @@
$this.button("reset"); $this.button("reset");
}).error(function () { }).error(function () {
$this.button("reset"); $this.button("reset");
return showError("服务器异常"); return showError("{{i18n $.Lang "message.system_error"}}");
}); });
return false; return false;
}).on("blur","#bookName",function () { }).on("blur","#bookName",function () {
@ -581,30 +581,30 @@
var itemId = $then.find("input[name='itemId']").val(); var itemId = $then.find("input[name='itemId']").val();
if (itemId <= 0) { if (itemId <= 0) {
return showError("请选择项目空间") return showError("{{i18n $.Lang "message.project_space_empty"}}")
} }
var bookName = $.trim($then.find("input[name='book_name']").val()); var bookName = $.trim($then.find("input[name='book_name']").val());
if (bookName === "") { if (bookName === "") {
return showError("项目标题不能为空","#import-book-form-error-message"); return showError("{{i18n $.Lang "message.project_title_empty"}}","#import-book-form-error-message");
} }
if (bookName.length > 100) { if (bookName.length > 100) {
return showError("项目标题必须小于100字符","#import-book-form-error-message"); return showError("{{i18n $.Lang "message.project_title_tips"}}","#import-book-form-error-message");
} }
var identify = $.trim($then.find("input[name='identify']").val()); var identify = $.trim($then.find("input[name='identify']").val());
if (identify === "") { if (identify === "") {
return showError("项目标识不能为空","#import-book-form-error-message"); return showError("{{i18n $.Lang "message.project_id_empty"}}","#import-book-form-error-message");
} }
var description = $.trim($then.find('textarea[name="description"]').val()); var description = $.trim($then.find('textarea[name="description"]').val());
if (description.length > 500) { if (description.length > 500) {
return showError("描述信息不超过500个字符","#import-book-form-error-message"); return showError("{{i18n $.Lang "message.project_decs_placeholder"}}","#import-book-form-error-message");
} }
var filesCount = $('#import-book-upload').fileinput('getFilesCount'); var filesCount = $('#import-book-upload').fileinput('getFilesCount');
if (filesCount <= 0) { if (filesCount <= 0) {
return showError("请选择需要上传的文件","#import-book-form-error-message"); return showError("{{i18n $.Lang "message.import_file_empty"}}","#import-book-form-error-message");
} }
//$("#importBookDialogForm").submit(); //$("#importBookDialogForm").submit();
$("#btnImportBook").button("loading"); $("#btnImportBook").button("loading");
@ -636,7 +636,7 @@
$("#btnDeleteBook").button("reset"); $("#btnDeleteBook").button("reset");
}, },
error : function () { error : function () {
showError("服务器异常","#form-error-message2"); showError("{{i18n $.Lang "message.system_error"}}","#form-error-message2");
$("#btnDeleteBook").button("reset"); $("#btnDeleteBook").button("reset");
} }
}); });

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