diff --git a/commands/command.go b/commands/command.go
index c5e15c59..558df139 100644
--- a/commands/command.go
+++ b/commands/command.go
@@ -2,28 +2,29 @@ package commands
import (
"encoding/gob"
+ "flag"
"fmt"
+ "log"
"net/url"
"os"
- "time"
- "log"
- "flag"
"path/filepath"
"strings"
+ "time"
"encoding/json"
+
"github.com/astaxie/beego"
- "github.com/astaxie/beego/logs"
- "github.com/astaxie/beego/orm"
- "github.com/lifei6671/gocaptcha"
- "github.com/lifei6671/mindoc/commands/migrate"
- "github.com/lifei6671/mindoc/conf"
- "github.com/lifei6671/mindoc/models"
- "github.com/lifei6671/mindoc/utils"
- "github.com/lifei6671/mindoc/cache"
beegoCache "github.com/astaxie/beego/cache"
_ "github.com/astaxie/beego/cache/memcache"
_ "github.com/astaxie/beego/cache/redis"
+ "github.com/astaxie/beego/logs"
+ "github.com/astaxie/beego/orm"
+ "github.com/lifei6671/gocaptcha"
+ "github.com/lifei6671/mindoc/cache"
+ "github.com/lifei6671/mindoc/commands/migrate"
+ "github.com/lifei6671/mindoc/conf"
+ "github.com/lifei6671/mindoc/models"
+ "github.com/lifei6671/mindoc/utils/filetil"
)
// RegisterDataBase 注册数据库
@@ -42,16 +43,15 @@ func RegisterDataBase() {
if err == nil {
orm.DefaultTimeLoc = location
} else {
- beego.Error("加载时区配置信息失败,请检查是否存在ZONEINFO环境变量:",err)
+ beego.Error("加载时区配置信息失败,请检查是否存在ZONEINFO环境变量:", err)
}
-
port := beego.AppConfig.String("db_port")
dataSource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=%s", username, password, host, port, database, url.QueryEscape(timezone))
if err := orm.RegisterDataBase("default", "mysql", dataSource); err != nil {
- beego.Error("注册默认数据库失败:",err)
+ beego.Error("注册默认数据库失败:", err)
os.Exit(1)
}
} else if adapter == "sqlite3" {
@@ -67,9 +67,9 @@ func RegisterDataBase() {
err := orm.RegisterDataBase("default", "sqlite3", database)
if err != nil {
- beego.Error("注册默认数据库失败:",err)
+ beego.Error("注册默认数据库失败:", err)
}
- }else{
+ } else {
beego.Error("不支持的数据库类型.")
os.Exit(1)
}
@@ -150,15 +150,15 @@ func RegisterFunction() {
}
//如果没有设置cdn,则使用baseURL拼接
if cdn == "" {
- baseUrl := beego.AppConfig.DefaultString("baseurl","")
+ baseUrl := beego.AppConfig.DefaultString("baseurl", "")
- if strings.HasPrefix(p,"/") && strings.HasSuffix(baseUrl,"/") {
+ if strings.HasPrefix(p, "/") && strings.HasSuffix(baseUrl, "/") {
return baseUrl + p[1:]
}
- if !strings.HasPrefix(p,"/") && !strings.HasSuffix(baseUrl,"/") {
+ if !strings.HasPrefix(p, "/") && !strings.HasSuffix(baseUrl, "/") {
return baseUrl + "/" + p
}
- return baseUrl + p
+ return baseUrl + p
}
if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
return cdn + string(p[1:])
@@ -169,12 +169,12 @@ func RegisterFunction() {
return cdn + p
})
- beego.AddFuncMap("cdnjs",conf.URLForWithCdnJs)
- beego.AddFuncMap("cdncss",conf.URLForWithCdnCss)
+ beego.AddFuncMap("cdnjs", conf.URLForWithCdnJs)
+ beego.AddFuncMap("cdncss", conf.URLForWithCdnCss)
beego.AddFuncMap("cdnimg", conf.URLForWithCdnImage)
//重写url生成,支持配置域名以及域名前缀
beego.AddFuncMap("urlfor", conf.URLFor)
- beego.AddFuncMap("date_format", func(t time.Time,format string) string {
+ beego.AddFuncMap("date_format", func(t time.Time, format string) string {
return t.Local().Format(format)
})
}
@@ -199,8 +199,8 @@ func ResolveCommand(args []string) {
if conf.ConfigurationFile == "" {
conf.ConfigurationFile = filepath.Join(conf.WorkingDirectory, "conf", "app.conf")
config := filepath.Join(conf.WorkingDirectory, "conf", "app.conf.example")
- if !utils.FileExists(conf.ConfigurationFile) && utils.FileExists(config) {
- utils.CopyFile(conf.ConfigurationFile, config)
+ if !filetil.FileExists(conf.ConfigurationFile) && filetil.FileExists(config) {
+ filetil.CopyFile(conf.ConfigurationFile, config)
}
}
gocaptcha.ReadFonts(filepath.Join(conf.WorkingDirectory, "static", "fonts"), ".ttf")
@@ -221,7 +221,7 @@ func ResolveCommand(args []string) {
fonts := filepath.Join(conf.WorkingDirectory, "static", "fonts")
- if !utils.FileExists(fonts) {
+ if !filetil.FileExists(fonts) {
log.Fatal("Font path not exist.")
}
gocaptcha.ReadFonts(filepath.Join(conf.WorkingDirectory, "static", "fonts"), ".ttf")
@@ -233,31 +233,30 @@ func ResolveCommand(args []string) {
}
//注册缓存管道
-func RegisterCache() {
- isOpenCache := beego.AppConfig.DefaultBool("cache",false)
+func RegisterCache() {
+ isOpenCache := beego.AppConfig.DefaultBool("cache", false)
if !isOpenCache {
cache.Init(&cache.NullCache{})
}
beego.Info("正常初始化缓存配置.")
cacheProvider := beego.AppConfig.String("cache_provider")
if cacheProvider == "file" {
- cacheFilePath := beego.AppConfig.DefaultString("cache_file_path","./runtime/cache/")
+ cacheFilePath := beego.AppConfig.DefaultString("cache_file_path", "./runtime/cache/")
if strings.HasPrefix(cacheFilePath, "./") {
cacheFilePath = filepath.Join(conf.WorkingDirectory, string(cacheFilePath[1:]))
}
fileCache := beegoCache.NewFileCache()
+ fileConfig := make(map[string]string, 0)
- fileConfig := make(map[string]string,0)
+ fileConfig["CachePath"] = cacheFilePath
+ fileConfig["DirectoryLevel"] = beego.AppConfig.DefaultString("cache_file_dir_level", "2")
+ fileConfig["EmbedExpiry"] = beego.AppConfig.DefaultString("cache_file_expiry", "120")
+ fileConfig["FileSuffix"] = beego.AppConfig.DefaultString("cache_file_suffix", ".bin")
- fileConfig["CachePath"] = cacheFilePath
- fileConfig["DirectoryLevel"] = beego.AppConfig.DefaultString("cache_file_dir_level","2")
- fileConfig["EmbedExpiry"] = beego.AppConfig.DefaultString("cache_file_expiry","120")
- fileConfig["FileSuffix"] = beego.AppConfig.DefaultString("cache_file_suffix",".bin")
-
- bc,err := json.Marshal(&fileConfig)
+ bc, err := json.Marshal(&fileConfig)
if err != nil {
- beego.Error("初始化Redis缓存失败:",err)
+ beego.Error("初始化Redis缓存失败:", err)
os.Exit(1)
}
@@ -265,61 +264,61 @@ func RegisterCache() {
cache.Init(fileCache)
- }else if cacheProvider == "memory" {
- cacheInterval := beego.AppConfig.DefaultInt("cache_memory_interval",60)
+ } else if cacheProvider == "memory" {
+ cacheInterval := beego.AppConfig.DefaultInt("cache_memory_interval", 60)
memory := beegoCache.NewMemoryCache()
beegoCache.DefaultEvery = cacheInterval
cache.Init(memory)
- }else if cacheProvider == "redis"{
- var redisConfig struct{
- Conn string `json:"conn"`
+ } else if cacheProvider == "redis" {
+ var redisConfig struct {
+ Conn string `json:"conn"`
Password string `json:"password"`
- DbNum int `json:"dbNum"`
+ DbNum int `json:"dbNum"`
}
redisConfig.DbNum = 0
- redisConfig.Conn = beego.AppConfig.DefaultString("cache_redis_host","")
- if pwd := beego.AppConfig.DefaultString("cache_redis_password","");pwd != "" {
+ redisConfig.Conn = beego.AppConfig.DefaultString("cache_redis_host", "")
+ if pwd := beego.AppConfig.DefaultString("cache_redis_password", ""); pwd != "" {
redisConfig.Password = pwd
}
- if dbNum := beego.AppConfig.DefaultInt("cache_redis_db",0); dbNum > 0 {
+ if dbNum := beego.AppConfig.DefaultInt("cache_redis_db", 0); dbNum > 0 {
redisConfig.DbNum = dbNum
}
- bc,err := json.Marshal(&redisConfig)
+ bc, err := json.Marshal(&redisConfig)
if err != nil {
- beego.Error("初始化Redis缓存失败:",err)
+ beego.Error("初始化Redis缓存失败:", err)
os.Exit(1)
}
- redisCache,err := beegoCache.NewCache("redis",string(bc))
+ redisCache, err := beegoCache.NewCache("redis", string(bc))
if err != nil {
- beego.Error("初始化Redis缓存失败:",err)
+ beego.Error("初始化Redis缓存失败:", err)
os.Exit(1)
}
cache.Init(redisCache)
- }else if cacheProvider == "memcache" {
+ } else if cacheProvider == "memcache" {
- var memcacheConfig struct{
+ var memcacheConfig struct {
Conn string `json:"conn"`
}
- memcacheConfig.Conn = beego.AppConfig.DefaultString("cache_memcache_host","")
+ memcacheConfig.Conn = beego.AppConfig.DefaultString("cache_memcache_host", "")
- bc,err := json.Marshal(&memcacheConfig)
+ bc, err := json.Marshal(&memcacheConfig)
if err != nil {
- beego.Error("初始化Redis缓存失败:",err)
+ beego.Error("初始化Redis缓存失败:", err)
os.Exit(1)
}
- memcache,err := beegoCache.NewCache("memcache",string(bc))
+ memcache, err := beegoCache.NewCache("memcache", string(bc))
if err != nil {
- beego.Error("初始化Memcache缓存失败:",err)
+ beego.Error("初始化Memcache缓存失败:", err)
os.Exit(1)
}
cache.Init(memcache)
- }else {
+ } else {
cache.Init(&cache.NullCache{})
beego.Warn("不支持的缓存管道,缓存将禁用.")
return
@@ -329,7 +328,7 @@ func RegisterCache() {
func init() {
- if configPath ,err := filepath.Abs(conf.ConfigurationFile); err == nil {
+ if configPath, err := filepath.Abs(conf.ConfigurationFile); err == nil {
conf.ConfigurationFile = configPath
}
gocaptcha.ReadFonts("./static/fonts", ".ttf")
diff --git a/controllers/book.go b/controllers/book.go
index a5637c23..0a1ec53c 100644
--- a/controllers/book.go
+++ b/controllers/book.go
@@ -12,6 +12,8 @@ import (
"strings"
"time"
+ "net/http"
+
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
@@ -20,8 +22,6 @@ import (
"github.com/lifei6671/mindoc/models"
"github.com/lifei6671/mindoc/utils"
"github.com/lifei6671/mindoc/utils/pagination"
- "net/http"
- "github.com/lifei6671/mindoc/converter"
"gopkg.in/russross/blackfriday.v2"
)
@@ -42,14 +42,14 @@ func (c *BookController) Index() {
c.Abort("500")
}
- for i,book := range books {
+ for i, book := range books {
books[i].Description = utils.StripTags(string(blackfriday.Run([]byte(book.Description))))
books[i].ModifyTime = book.ModifyTime.Local()
books[i].CreateTime = book.CreateTime.Local()
}
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())
c.Data["PageHtml"] = pager.HtmlPages()
} else {
c.Data["PageHtml"] = ""
@@ -140,10 +140,10 @@ func (c *BookController) SaveBook() {
editor := strings.TrimSpace(c.GetString("editor"))
autoRelease := strings.TrimSpace(c.GetString("auto_release")) == "on"
publisher := strings.TrimSpace(c.GetString("publisher"))
- historyCount,_ := c.GetInt("history_count",0)
+ historyCount, _ := c.GetInt("history_count", 0)
isDownload := strings.TrimSpace(c.GetString("is_download")) == "on"
enableShare := strings.TrimSpace(c.GetString("enable_share")) == "on"
- isUseFirstDocument := strings.TrimSpace(c.GetString("is_use_first_document")) == "on"
+ isUseFirstDocument := strings.TrimSpace(c.GetString("is_use_first_document")) == "on"
if strings.Count(description, "") > 500 {
c.JsonResult(6004, "项目描述不能大于500字")
@@ -177,17 +177,17 @@ func (c *BookController) SaveBook() {
}
if isDownload {
book.IsDownload = 0
- }else{
+ } else {
book.IsDownload = 1
}
if enableShare {
book.IsEnableShare = 0
- }else{
+ } else {
book.IsEnableShare = 1
}
if isUseFirstDocument {
book.IsUseFirstDocument = 1
- }else{
+ } else {
book.IsUseFirstDocument = 0
}
if err := book.Update(); err != nil {
@@ -397,7 +397,7 @@ func (c *BookController) Users() {
members, totalCount, err := models.NewMemberRelationshipResult().FindForUsersByBookId(book.BookId, pageIndex, 15)
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())
c.Data["PageHtml"] = pager.HtmlPages()
} else {
c.Data["PageHtml"] = ""
@@ -411,7 +411,6 @@ func (c *BookController) Users() {
}
}
-
// Create 创建项目.
func (c *BookController) Create() {
@@ -446,9 +445,8 @@ func (c *BookController) Create() {
book := models.NewBook()
book.Cover = conf.GetDefaultCover()
-
//如果客户端上传了项目封面则直接保存
- if file, moreFile, err := c.GetFile("image-file");err == nil {
+ if file, moreFile, err := c.GetFile("image-file"); err == nil {
defer file.Close()
ext := filepath.Ext(moreFile.Filename)
@@ -458,7 +456,7 @@ func (c *BookController) Create() {
fileName := "cover_" + strconv.FormatInt(time.Now().UnixNano(), 16)
- filePath := filepath.Join("uploads", time.Now().Format("200601"), fileName + ext)
+ filePath := filepath.Join("uploads", time.Now().Format("200601"), fileName+ext)
path := filepath.Dir(filePath)
@@ -475,8 +473,6 @@ func (c *BookController) Create() {
}
}
-
-
if books, _ := book.FindByField("identify", identify); len(books) > 0 {
c.JsonResult(6006, "项目标识已存在")
}
@@ -495,9 +491,7 @@ func (c *BookController) Create() {
book.Editor = "markdown"
book.Theme = "default"
-
-
- if err := book.Insert();err != nil {
+ if err := book.Insert(); err != nil {
logs.Error("Insert => ", err)
c.JsonResult(6005, "保存项目失败")
}
@@ -512,7 +506,7 @@ func (c *BookController) Create() {
c.JsonResult(6001, "error")
}
-//导入
+//导入zip压缩包
func (c *BookController) Import() {
file, moreFile, err := c.GetFile("import-file")
@@ -522,24 +516,44 @@ func (c *BookController) Import() {
defer file.Close()
+ bookName := strings.TrimSpace(c.GetString("book_name"))
+ identify := strings.TrimSpace(c.GetString("identify"))
+
+ if bookName == "" {
+ c.JsonResult(6001, "项目名称不能为空")
+ }
+ if len([]rune(bookName)) > 500 {
+ c.JsonResult(6002, "项目名称不能大于500字")
+ }
+ if identify == "" {
+ c.JsonResult(6002, "项目标识不能为空")
+ }
+ if ok, err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`, identify); !ok || err != nil {
+ c.JsonResult(6003, "项目标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母开头")
+ }
+ if strings.Count(identify, "") > 50 {
+ c.JsonResult(6004, "文档标识不能超过50字")
+ }
+
beego.Info(moreFile.Filename)
ext := filepath.Ext(moreFile.Filename)
- if !strings.EqualFold(ext,".doc") || !strings.EqualFold(ext,".docx") {
- c.JsonResult(6004,"不支持的文件类型")
+ if !strings.EqualFold(ext, ".zip") {
+ c.JsonResult(6004, "不支持的文件类型")
}
- tempPath := filepath.Join(os.TempDir(),c.CruSession.SessionID())
+ tempPath := filepath.Join(os.TempDir(), c.CruSession.SessionID())
- os.MkdirAll(tempPath,0766)
+ os.MkdirAll(tempPath, 0766)
- tempPath = filepath.Join(tempPath,moreFile.Filename)
+ tempPath = filepath.Join(tempPath, moreFile.Filename)
err = c.SaveToFile("import-file", tempPath)
- converter.Resolve(tempPath)
+ go models.NewBook().ImportBook(tempPath)
+ c.JsonResult(0, "项目正在后台转换中,请稍后查看")
}
// CreateToken 创建访问来令牌.
diff --git a/controllers/manager.go b/controllers/manager.go
index 93080219..fb0c247d 100644
--- a/controllers/manager.go
+++ b/controllers/manager.go
@@ -6,16 +6,18 @@ import (
"regexp"
"strings"
+ "math"
+ "path/filepath"
+ "strconv"
+
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/mindoc/conf"
"github.com/lifei6671/mindoc/models"
"github.com/lifei6671/mindoc/utils"
- "path/filepath"
- "strconv"
+ "github.com/lifei6671/mindoc/utils/filetil"
"github.com/lifei6671/mindoc/utils/pagination"
- "math"
"gopkg.in/russross/blackfriday.v2"
)
@@ -52,7 +54,7 @@ func (c *ManagerController) Users() {
}
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())
c.Data["PageHtml"] = pager.HtmlPages()
} else {
c.Data["PageHtml"] = ""
@@ -110,7 +112,7 @@ func (c *ManagerController) CreateMember() {
member.Avatar = conf.GetDefaultAvatar()
member.CreateAt = c.Member.MemberId
member.Email = email
- member.RealName = strings.TrimSpace(c.GetString("real_name",""))
+ member.RealName = strings.TrimSpace(c.GetString("real_name", ""))
if phone != "" {
member.Phone = phone
}
@@ -290,13 +292,13 @@ func (c *ManagerController) Books() {
if totalCount > 0 {
//html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, 8, totalCount)
- pager := pagination.NewPagination(c.Ctx.Request,totalCount,conf.PageSize, c.BaseUrl())
+ pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl())
c.Data["PageHtml"] = pager.HtmlPages()
} else {
c.Data["PageHtml"] = ""
}
- for i,book := range books {
+ for i, book := range books {
books[i].Description = utils.StripTags(string(blackfriday.Run([]byte(book.Description))))
books[i].ModifyTime = book.ModifyTime.Local()
books[i].CreateTime = book.CreateTime.Local()
@@ -328,10 +330,10 @@ func (c *ManagerController) EditBook() {
orderIndex, _ := c.GetInt("order_index", 0)
isDownload := strings.TrimSpace(c.GetString("is_download")) == "on"
enableShare := strings.TrimSpace(c.GetString("enable_share")) == "on"
- isUseFirstDocument := strings.TrimSpace(c.GetString("is_use_first_document")) == "on"
+ isUseFirstDocument := strings.TrimSpace(c.GetString("is_use_first_document")) == "on"
autoRelease := strings.TrimSpace(c.GetString("auto_release")) == "on"
publisher := strings.TrimSpace(c.GetString("publisher"))
- historyCount,_ := c.GetInt("history_count",0)
+ historyCount, _ := c.GetInt("history_count", 0)
if strings.Count(description, "") > 500 {
c.JsonResult(6004, "项目描述不能大于500字")
@@ -360,17 +362,17 @@ func (c *ManagerController) EditBook() {
}
if isDownload {
book.IsDownload = 0
- }else{
+ } else {
book.IsDownload = 1
}
if enableShare {
book.IsEnableShare = 0
- }else{
+ } else {
book.IsEnableShare = 1
}
if isUseFirstDocument {
book.IsUseFirstDocument = 1
- }else{
+ } else {
book.IsUseFirstDocument = 0
}
@@ -613,7 +615,7 @@ func (c *ManagerController) AttachList() {
p := filepath.Join(conf.WorkingDirectory, item.FilePath)
- item.IsExist = utils.FileExists(p)
+ item.IsExist = filetil.FileExists(p)
}
c.Data["Lists"] = attachList
@@ -643,7 +645,7 @@ func (c *ManagerController) AttachDetailed() {
attach.FilePath = filepath.Join(conf.WorkingDirectory, attach.FilePath)
attach.HttpPath = conf.URLForWithCdnImage(attach.HttpPath)
- attach.IsExist = utils.FileExists(attach.FilePath)
+ attach.IsExist = filetil.FileExists(attach.FilePath)
c.Data["Model"] = attach
}
@@ -691,43 +693,27 @@ func (c *ManagerController) LabelList() {
c.Data["Lists"] = labels
}
+
//删除标签
-func (c *ManagerController) LabelDelete(){
- labelId,err := strconv.Atoi(c.Ctx.Input.Param(":id"))
+func (c *ManagerController) LabelDelete() {
+ labelId, err := strconv.Atoi(c.Ctx.Input.Param(":id"))
if err != nil {
- beego.Error("获取删除标签参数时出错:",err)
- c.JsonResult(50001,"参数错误")
+ beego.Error("获取删除标签参数时出错:", err)
+ c.JsonResult(50001, "参数错误")
}
if labelId <= 0 {
- c.JsonResult(50001,"参数错误")
+ c.JsonResult(50001, "参数错误")
}
- label,err := models.NewLabel().FindFirst("label_id",labelId)
+ label, err := models.NewLabel().FindFirst("label_id", labelId)
if err != nil {
- beego.Error("查询标签时出错:",err)
- c.JsonResult(50001,"查询标签时出错:" + err.Error())
+ beego.Error("查询标签时出错:", err)
+ c.JsonResult(50001, "查询标签时出错:"+err.Error())
}
- if err := label.Delete();err != nil {
- c.JsonResult(50002,"删除失败:" + err.Error())
- }else{
- c.JsonResult(0,"ok")
+ if err := label.Delete(); err != nil {
+ c.JsonResult(50002, "删除失败:"+err.Error())
+ } else {
+ c.JsonResult(0, "ok")
}
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/converter/import.go b/converter/import.go
deleted file mode 100644
index a2f4ddda..00000000
--- a/converter/import.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package converter
-
-import (
- "errors"
- "github.com/lifei6671/mindoc/utils"
- "os"
- "path/filepath"
- "crypto/md5"
- "io"
- "fmt"
- "os/exec"
- "github.com/lifei6671/mindoc/utils/ziptil"
- "io/ioutil"
- "encoding/xml"
-)
-
-type ResolveResult struct {
-
-}
-
-type XmlResult struct {
- XMLName xml.Name `xml:"ncx"`
- Head XmlHead `xml:"head"`
- NavMap XmlTocNavMap `xml:"navMap"`
- Title string `xml:"docTitle>text"`
-}
-
-type XmlHead struct {
- XMLName xml.Name `xml:"head"`
- Meta []XmlMeta `xml:"meta"`
-}
-
-type XmlMeta struct {
- XMLName xml.Name `xml:"meta"`
- Content string `xml:"content,attr"`
- Name string `xml:"name,attr"`
-
-}
-type XmlDocTitle struct {
- Text string `xml:"text"`
-}
-type XmlTocNavMap struct {
- XMLName xml.Name `xml:"navMap"`
- NavPoint []XmlNavPoint `xml:"navPoint"`
-}
-
-type XmlNavPoint struct {
- XMLName xml.Name `xml:"navPoint"`
- Content XmlContent `xml:"content"`
- NavLabel string `xml:"navLabel>text"`
-}
-
-type XmlContent struct {
- XMLName xml.Name `xml:"content"`
- Src string `xml:"src,attr"`
-}
-
-type XmlNavLabel struct {
-
-}
-
-func Resolve(p string) (ResolveResult,error) {
- result := ResolveResult{
-
- }
-
- if !utils.FileExists(p) {
- return result,errors.New("文件不存在 " + p)
- }
-
- w := md5.New()
- io.WriteString(w, p) //将str写入到w中
- md5str := fmt.Sprintf("%x", w.Sum(nil)) //w.Sum(nil)将w的hash转成[]byte格式
-
- tempPath := filepath.Join(os.TempDir(),md5str)
-
- os.MkdirAll(tempPath,0766)
-
- epub := filepath.Join(tempPath , "book.epub")
-
- args := []string{p,epub}
-
- cmd := exec.Command(ebookConvert, args...)
-
-
- if err := cmd.Run(); err != nil {
- fmt.Println("执行转换命令失败:" + err.Error())
- return result,err
- }
- fmt.Println(epub)
-
- unzipPath := filepath.Join(tempPath,"output")
-
- if err := ziptil.Unzip(epub, unzipPath); err != nil {
- fmt.Println("解压缩失败:" + err.Error())
- return result,err
- }
- xmlPath := filepath.Join(unzipPath,"toc.ncx")
-
- data,err := ioutil.ReadFile(xmlPath);
-
- if err != nil {
- fmt.Println("toc.ncx 文件不存在:" + err.Error())
- return result,err
- }
- v := XmlResult{}
-
- err = xml.Unmarshal([]byte(data), &v)
-
- if err != nil {
- fmt.Println("解析XML失败:" + err.Error())
- return result,err
- }
-
- fmt.Println(v)
-
- return result,nil
-}
\ No newline at end of file
diff --git a/models/attachment.go b/models/attachment.go
index bdf08740..231356c9 100644
--- a/models/attachment.go
+++ b/models/attachment.go
@@ -6,11 +6,12 @@ import (
"os"
+ "strings"
+
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/mindoc/conf"
- "github.com/lifei6671/mindoc/utils"
- "strings"
+ "github.com/lifei6671/mindoc/utils/filetil"
)
// Attachment struct .
@@ -97,7 +98,7 @@ func (m *Attachment) FindToPager(pageIndex, pageSize int) (attachList []*Attachm
if err != nil {
- return nil,0,err
+ return nil, 0, err
}
totalCount = int(total)
offset := (pageIndex - 1) * pageSize
@@ -113,7 +114,7 @@ func (m *Attachment) FindToPager(pageIndex, pageSize int) (attachList []*Attachm
for _, item := range list {
attach := &AttachmentResult{}
attach.Attachment = *item
- attach.FileShortSize = utils.FormatBytes(int64(attach.FileSize))
+ attach.FileShortSize = filetil.FormatBytes(int64(attach.FileSize))
book := NewBook()
diff --git a/models/attachment_result.go b/models/attachment_result.go
index 2b832f25..34a0c85a 100644
--- a/models/attachment_result.go
+++ b/models/attachment_result.go
@@ -1,9 +1,10 @@
package models
import (
- "github.com/astaxie/beego/orm"
- "github.com/lifei6671/mindoc/utils"
"strings"
+
+ "github.com/astaxie/beego/orm"
+ "github.com/lifei6671/mindoc/utils/filetil"
)
type AttachmentResult struct {
@@ -54,7 +55,7 @@ func (m *AttachmentResult) Find(id int) (*AttachmentResult, error) {
m.Account = member.Account
}
}
- m.FileShortSize = utils.FormatBytes(int64(attach.FileSize))
+ m.FileShortSize = filetil.FormatBytes(int64(attach.FileSize))
m.LocalHttpPath = strings.Replace(m.FilePath, "\\", "/", -1)
return m, nil
diff --git a/models/book.go b/models/book.go
index e93ed336..a202e30d 100644
--- a/models/book.go
+++ b/models/book.go
@@ -11,6 +11,17 @@ import (
"os"
"path/filepath"
"strconv"
+ "crypto/md5"
+ "io"
+ "errors"
+ "github.com/lifei6671/mindoc/utils/filetil"
+ "github.com/lifei6671/mindoc/utils/ziptil"
+ "strings"
+ "regexp"
+ "io/ioutil"
+ "github.com/lifei6671/mindoc/utils/cryptil"
+ "github.com/lifei6671/mindoc/utils/requests"
+ "gopkg.in/russross/blackfriday.v2"
)
// Book struct .
@@ -77,6 +88,7 @@ func NewBook() *Book {
return &Book{}
}
+//添加一个项目
func (m *Book) Insert() error {
o := orm.NewOrm()
// o.Begin()
@@ -124,7 +136,7 @@ func (m *Book) Find(id int) (*Book, error) {
return m, err
}
-
+//更新一个项目
func (m *Book) Update(cols ...string) error {
o := orm.NewOrm()
@@ -164,6 +176,7 @@ func (m *Book) FindByFieldFirst(field string, value interface{}) (*Book, error)
}
+//根据项目标识查询项目
func (m *Book) FindByIdentify(identify string) (*Book, error) {
o := orm.NewOrm()
@@ -375,3 +388,131 @@ func (m *Book) ResetDocumentNumber(bookId int) {
beego.Error(err)
}
}
+
+func (book *Book)ImportBook(zipPath string) error {
+ if !filetil.FileExists(zipPath) {
+ return errors.New("文件不存在 => " + zipPath)
+ }
+
+
+ w := md5.New()
+ io.WriteString(w, zipPath) //将str写入到w中
+ io.WriteString(w, time.Now().String())
+ io.WriteString(w,book.BookName)
+ md5str := fmt.Sprintf("%x", w.Sum(nil)) //w.Sum(nil)将w的hash转成[]byte格式
+
+ tempPath := strings.Replace(filepath.Join(os.TempDir(), md5str),"\\","/",-1)
+
+ os.MkdirAll(tempPath, 0766)
+ //如果加压缩失败
+ if err := ziptil.Unzip(zipPath,tempPath);err != nil {
+ return err
+ }
+
+
+ err := filepath.Walk(tempPath, func(path string, info os.FileInfo, err error) error {
+ path = strings.Replace(path,"\\","/",-1)
+ if path == tempPath {
+ return nil
+ }
+ if !info.IsDir() {
+ ext := filepath.Ext(info.Name())
+ if strings.EqualFold(ext ,".md") || strings.EqualFold(ext , ".markdown" ) {
+ doc := NewDocument()
+ doc.BookId = book.BookId
+ docIdentify := strings.Replace(strings.TrimPrefix(path, tempPath+"/"), "/", "-", -1)
+
+ if ok, err := regexp.MatchString(`[a-z]+[a-zA-Z0-9_.\-]*$`, docIdentify); !ok || err != nil {
+ docIdentify = "import-" + docIdentify
+ }
+
+ doc.Identify = docIdentify
+
+ re := regexp.MustCompile(`!\[(.*?)\]\((.*?)\)`)
+ markdown, err := ioutil.ReadFile(path);
+ if err != nil {
+ return err
+ }
+ doc.Markdown = re.ReplaceAllStringFunc(string(markdown), func(image string) string {
+
+ images := re.FindAllSubmatch([]byte(image), -1);
+ if len(images) <= 0 || len(images[0]) < 3 {
+ return image
+ }
+ originalImageUrl := string(images[0][2])
+ imageUrl := strings.Replace(string(originalImageUrl),"\\","/",-1)
+
+ //如果是本地路径,则需要将图片复制到项目目录
+ if !strings.HasPrefix(imageUrl, "http://") && !strings.HasPrefix(imageUrl, "https://") {
+ if strings.HasPrefix(imageUrl, "/") {
+ imageUrl = filepath.Join(tempPath, imageUrl)
+ } else if strings.HasPrefix(imageUrl, "./") {
+ imageUrl = filepath.Join(filepath.Dir(path), strings.TrimPrefix(imageUrl, "./"))
+ } else if strings.HasPrefix(imageUrl, "../") {
+ imageUrl = filepath.Join(filepath.Dir(path), imageUrl)
+ } else {
+ imageUrl = filepath.Join(filepath.Dir(path), imageUrl)
+ }
+ imageUrl = strings.Replace(imageUrl,"\\","/",-1)
+ dstFile := filepath.Join(conf.WorkingDirectory,"uploads",time.Now().Format("200601"),strings.TrimPrefix(imageUrl,tempPath))
+
+ if filetil.FileExists(imageUrl) {
+ filetil.CopyFile(imageUrl,dstFile)
+
+ imageUrl = strings.TrimPrefix(dstFile,conf.WorkingDirectory)
+
+ if !strings.HasPrefix(imageUrl,"/") && !strings.HasPrefix(imageUrl,"\\"){
+ imageUrl = "/" + imageUrl
+ }
+ }
+
+ }else{
+ imageExt := cryptil.Md5Crypt(imageUrl) + filepath.Ext(imageUrl)
+
+ dstFile := filepath.Join(conf.WorkingDirectory,"uploads",time.Now().Format("200601"),imageExt)
+
+ if err := requests.DownloadAndSaveFile(imageUrl,dstFile) ;err == nil {
+ imageUrl = strings.TrimPrefix(strings.Replace(dstFile,"\\","/",-1),strings.Replace(conf.WorkingDirectory,"\\","/",-1))
+ if !strings.HasPrefix(imageUrl,"/") && !strings.HasPrefix(imageUrl,"\\"){
+ imageUrl = "/" + imageUrl
+ }
+ }
+ }
+
+ imageUrl = strings.Replace(strings.TrimSuffix(image,originalImageUrl + ")") + imageUrl + ")","\\","/",-1)
+ beego.Info(imageUrl)
+ return imageUrl
+ })
+ doc.Content = string(blackfriday.Run([]byte(doc.Markdown)))
+ doc.Release = doc.Content
+
+ //beego.Info(content)
+ //images := re.FindAllSubmatch(markdown,-1);
+ //
+ //for _,image := range images {
+ // originalImageUrl := string(image[1])
+ // imageUrl := string(originalImageUrl)
+ //
+ // if !strings.HasPrefix(imageUrl,"http://") && !strings.HasPrefix(imageUrl,"https://") {
+ // if strings.HasPrefix(imageUrl, "/") {
+ // imageUrl = filepath.Join(tempPath, imageUrl)
+ // } else if strings.HasPrefix(imageUrl, "./") {
+ // imageUrl = filepath.Join(filepath.Dir(path), strings.TrimPrefix(imageUrl, "./"))
+ // } else if strings.HasPrefix(imageUrl, "../") {
+ // imageUrl = filepath.Join(filepath.Dir(path), imageUrl)
+ // }else{
+ // imageUrl = filepath.Join(filepath.Dir(path), imageUrl)
+ // }
+ //
+ // }
+ // beego.Info(imageUrl)
+ //}
+ }
+ }
+
+
+ return nil
+ })
+
+ return err
+}
\ No newline at end of file
diff --git a/models/book_result.go b/models/book_result.go
index 3feceb93..7dd65517 100644
--- a/models/book_result.go
+++ b/models/book_result.go
@@ -2,24 +2,24 @@ package models
import (
"bytes"
- "time"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
+ "time"
"encoding/base64"
+
"github.com/PuerkitoBio/goquery"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/mindoc/conf"
"github.com/lifei6671/mindoc/converter"
- "github.com/lifei6671/mindoc/utils"
- "gopkg.in/russross/blackfriday.v2"
- "github.com/lifei6671/mindoc/utils/ziptil"
"github.com/lifei6671/mindoc/utils/filetil"
+ "github.com/lifei6671/mindoc/utils/ziptil"
+ "gopkg.in/russross/blackfriday.v2"
)
type BookResult struct {
@@ -36,7 +36,7 @@ type BookResult struct {
CommentCount int `json:"comment_count"`
CreateTime time.Time `json:"create_time"`
CreateName string `json:"create_name"`
- RealName string `json:"real_name"`
+ RealName string `json:"real_name"`
ModifyTime time.Time `json:"modify_time"`
Cover string `json:"cover"`
Theme string `json:"theme"`
@@ -44,18 +44,18 @@ type BookResult struct {
MemberId int `json:"member_id"`
Editor string `json:"editor"`
AutoRelease bool `json:"auto_release"`
- HistoryCount int `json:"history_count"`
+ HistoryCount int `json:"history_count"`
- RelationshipId int `json:"relationship_id"`
- RoleId int `json:"role_id"`
- RoleName string `json:"role_name"`
- Status int `json:"status"`
- IsEnableShare bool `json:"is_enable_share"`
- IsUseFirstDocument bool `json:"is_use_first_document"`
+ RelationshipId int `json:"relationship_id"`
+ RoleId int `json:"role_id"`
+ RoleName string `json:"role_name"`
+ Status int `json:"status"`
+ IsEnableShare bool `json:"is_enable_share"`
+ IsUseFirstDocument bool `json:"is_use_first_document"`
LastModifyText string `json:"last_modify_text"`
IsDisplayComment bool `json:"is_display_comment"`
- IsDownload bool `json:"is_download"`
+ IsDownload bool `json:"is_download"`
}
func NewBookResult() *BookResult {
@@ -211,7 +211,7 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
convertBookResult := ConvertBookResult{}
- outputPath := filepath.Join(conf.WorkingDirectory,"uploads","books", strconv.Itoa(m.BookId))
+ outputPath := filepath.Join(conf.WorkingDirectory, "uploads", "books", strconv.Itoa(m.BookId))
viewPath := beego.BConfig.WebConfig.ViewsPath
pdfpath := filepath.Join(outputPath, "book.pdf")
@@ -220,7 +220,7 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
docxpath := filepath.Join(outputPath, "book.docx")
//先将转换的文件储存到临时目录
- tempOutputPath := filepath.Join(os.TempDir(),sessionId,m.Identify) //filepath.Abs(filepath.Join("cache", sessionId))
+ tempOutputPath := filepath.Join(os.TempDir(), sessionId, m.Identify) //filepath.Abs(filepath.Join("cache", sessionId))
os.MkdirAll(outputPath, 0766)
os.MkdirAll(tempOutputPath, 0766)
@@ -229,7 +229,7 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
os.RemoveAll(p)
}(tempOutputPath)
- if utils.FileExists(pdfpath) && utils.FileExists(epubpath) && utils.FileExists(mobipath) && utils.FileExists(docxpath) {
+ if filetil.FileExists(pdfpath) && filetil.FileExists(epubpath) && filetil.FileExists(mobipath) && filetil.FileExists(docxpath) {
convertBookResult.EpubPath = epubpath
convertBookResult.MobiPath = mobipath
convertBookResult.PDFPath = pdfpath
@@ -237,7 +237,6 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
return convertBookResult, nil
}
-
docs, err := NewDocument().FindListByBookId(m.BookId)
if err != nil {
return convertBookResult, err
@@ -293,7 +292,7 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
More: []string{},
}
if m.Publisher != "" {
- ebookConfig.Footer = "
本文档由 "+ m.Publisher +" 生成- _PAGENUM_ -
"
+ ebookConfig.Footer = "本文档由 " + m.Publisher + " 生成- _PAGENUM_ -
"
}
if m.RealName != "" {
ebookConfig.Creator = m.RealName
@@ -354,35 +353,31 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
f.Close()
}
- filetil.CopyFile(filepath.Join(conf.WorkingDirectory,"static","css","kancloud.css"),filepath.Join(tempOutputPath,"styles","css","kancloud.css"))
- filetil.CopyFile(filepath.Join(conf.WorkingDirectory,"static","css","export.css"),filepath.Join(tempOutputPath,"styles","css","export.css"))
- filetil.CopyFile(filepath.Join(conf.WorkingDirectory,"static","editor.md","css","editormd.preview.css"),filepath.Join(tempOutputPath,"styles","editor.md","css","editormd.preview.css"))
- filetil.CopyFile(filepath.Join(conf.WorkingDirectory,"static","prettify","themes","prettify.css"),filepath.Join(tempOutputPath,"styles","prettify","themes","prettify.css"))
- filetil.CopyFile(filepath.Join(conf.WorkingDirectory,"static","css,","markdown.preview.css"),filepath.Join(tempOutputPath,"styles","css","markdown.preview.css"))
- filetil.CopyFile(filepath.Join(conf.WorkingDirectory,"static","highlight","styles","vs.css"),filepath.Join(tempOutputPath,"styles","highlight","styles","vs.css"))
- filetil.CopyFile(filepath.Join(conf.WorkingDirectory,"static","katex","katex.min.css"),filepath.Join(tempOutputPath,"styles","katex","katex.min.css"))
-
+ filetil.CopyFile(filepath.Join(conf.WorkingDirectory, "static", "css", "kancloud.css"), filepath.Join(tempOutputPath, "styles", "css", "kancloud.css"))
+ filetil.CopyFile(filepath.Join(conf.WorkingDirectory, "static", "css", "export.css"), filepath.Join(tempOutputPath, "styles", "css", "export.css"))
+ filetil.CopyFile(filepath.Join(conf.WorkingDirectory, "static", "editor.md", "css", "editormd.preview.css"), filepath.Join(tempOutputPath, "styles", "editor.md", "css", "editormd.preview.css"))
+ filetil.CopyFile(filepath.Join(conf.WorkingDirectory, "static", "prettify", "themes", "prettify.css"), filepath.Join(tempOutputPath, "styles", "prettify", "themes", "prettify.css"))
+ filetil.CopyFile(filepath.Join(conf.WorkingDirectory, "static", "css,", "markdown.preview.css"), filepath.Join(tempOutputPath, "styles", "css", "markdown.preview.css"))
+ filetil.CopyFile(filepath.Join(conf.WorkingDirectory, "static", "highlight", "styles", "vs.css"), filepath.Join(tempOutputPath, "styles", "highlight", "styles", "vs.css"))
+ filetil.CopyFile(filepath.Join(conf.WorkingDirectory, "static", "katex", "katex.min.css"), filepath.Join(tempOutputPath, "styles", "katex", "katex.min.css"))
eBookConverter := &converter.Converter{
- BasePath: tempOutputPath,
- OutputPath: strings.TrimSuffix(tempOutputPath,"sources"),
- Config: ebookConfig,
- Debug: true,
+ BasePath: tempOutputPath,
+ OutputPath: strings.TrimSuffix(tempOutputPath, "sources"),
+ Config: ebookConfig,
+ Debug: true,
}
-
-
if err := eBookConverter.Convert(); err != nil {
beego.Error("转换文件错误:" + m.BookName + " => " + err.Error())
return convertBookResult, err
}
beego.Info("文档转换完成:" + m.BookName)
-
- utils.CopyFile(mobipath, filepath.Join(tempOutputPath, "output", "book.mobi"))
- utils.CopyFile(pdfpath, filepath.Join(tempOutputPath, "output", "book.pdf"))
- utils.CopyFile(epubpath, filepath.Join(tempOutputPath, "output", "book.epub"))
- utils.CopyFile(docxpath, filepath.Join(tempOutputPath, "output", "book.docx"))
+ filetil.CopyFile(mobipath, filepath.Join(tempOutputPath, "output", "book.mobi"))
+ filetil.CopyFile(pdfpath, filepath.Join(tempOutputPath, "output", "book.pdf"))
+ filetil.CopyFile(epubpath, filepath.Join(tempOutputPath, "output", "book.epub"))
+ filetil.CopyFile(docxpath, filepath.Join(tempOutputPath, "output", "book.docx"))
convertBookResult.MobiPath = mobipath
convertBookResult.PDFPath = pdfpath
@@ -393,45 +388,45 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
}
//导出Markdown原始文件
-func (m *BookResult) ExportMarkdown(sessionId string)(string, error){
- outputPath := filepath.Join(conf.WorkingDirectory,"uploads","books", strconv.Itoa(m.BookId), "book.zip")
+func (m *BookResult) ExportMarkdown(sessionId string) (string, error) {
+ outputPath := filepath.Join(conf.WorkingDirectory, "uploads", "books", strconv.Itoa(m.BookId), "book.zip")
- os.MkdirAll(filepath.Dir(outputPath),0644)
+ os.MkdirAll(filepath.Dir(outputPath), 0644)
- tempOutputPath := filepath.Join(os.TempDir(),sessionId,"markdown")
+ tempOutputPath := filepath.Join(os.TempDir(), sessionId, "markdown")
defer os.RemoveAll(tempOutputPath)
- err := exportMarkdown(tempOutputPath,0,m.BookId)
+ err := exportMarkdown(tempOutputPath, 0, m.BookId)
if err != nil {
- return "",err
+ return "", err
}
- if err := ziptil.Compress(outputPath,tempOutputPath);err != nil {
- beego.Error("导出Markdown失败=>",err)
- return "",err
+ if err := ziptil.Compress(outputPath, tempOutputPath); err != nil {
+ beego.Error("导出Markdown失败=>", err)
+ return "", err
}
- return outputPath,nil
+ return outputPath, nil
}
-func exportMarkdown(p string,parentId int,bookId int) (error){
+func exportMarkdown(p string, parentId int, bookId int) error {
o := orm.NewOrm()
var docs []*Document
- _,err := o.QueryTable(NewDocument().TableNameWithPrefix()).Filter("book_id",bookId).Filter("parent_id",parentId).All(&docs)
+ _, err := o.QueryTable(NewDocument().TableNameWithPrefix()).Filter("book_id", bookId).Filter("parent_id", parentId).All(&docs)
if err != nil {
- beego.Error("导出Markdown失败=>",err)
+ beego.Error("导出Markdown失败=>", err)
return err
}
- for _,doc := range docs {
+ for _, doc := range docs {
//获取当前文档的子文档数量,如果数量不为0,则将当前文档命名为READMD.md并设置成目录。
- subDocCount,err := o.QueryTable(NewDocument().TableNameWithPrefix()).Filter("parent_id",doc.DocumentId).Count()
+ subDocCount, err := o.QueryTable(NewDocument().TableNameWithPrefix()).Filter("parent_id", doc.DocumentId).Count()
if err != nil {
- beego.Error("导出Markdown失败=>",err)
+ beego.Error("导出Markdown失败=>", err)
return err
}
@@ -439,27 +434,27 @@ func exportMarkdown(p string,parentId int,bookId int) (error){
if subDocCount > 0 {
if doc.Identify != "" {
- docPath = filepath.Join(p, doc.Identify,"README.md")
+ docPath = filepath.Join(p, doc.Identify, "README.md")
} else {
- docPath = filepath.Join(p, strconv.Itoa(doc.DocumentId),"README.md")
+ docPath = filepath.Join(p, strconv.Itoa(doc.DocumentId), "README.md")
}
- }else{
+ } else {
if doc.Identify != "" {
- docPath = filepath.Join(p, doc.Identify + ".md")
+ docPath = filepath.Join(p, doc.Identify+".md")
} else {
- docPath = filepath.Join(p, doc.DocumentName + ".md")
+ docPath = filepath.Join(p, doc.DocumentName+".md")
}
}
- dirPath := filepath.Dir(docPath);
+ dirPath := filepath.Dir(docPath)
- os.MkdirAll(dirPath,0766)
+ os.MkdirAll(dirPath, 0766)
- if err := ioutil.WriteFile(docPath,[]byte(doc.Markdown),0644);err != nil {
- beego.Error("导出Markdown失败=>",err)
+ if err := ioutil.WriteFile(docPath, []byte(doc.Markdown), 0644); err != nil {
+ beego.Error("导出Markdown失败=>", err)
return err
}
if subDocCount > 0 {
- if err = exportMarkdown(dirPath,doc.DocumentId,bookId);err != nil {
+ if err = exportMarkdown(dirPath, doc.DocumentId, bookId); err != nil {
return err
}
}
@@ -468,36 +463,13 @@ func exportMarkdown(p string,parentId int,bookId int) (error){
}
//查询项目的第一篇文档
-func (m *BookResult) FindFirstDocumentByBookId(bookId int) (*Document,error) {
+func (m *BookResult) FindFirstDocumentByBookId(bookId int) (*Document, error) {
o := orm.NewOrm()
doc := NewDocument()
- err := o.QueryTable(doc.TableNameWithPrefix()).Filter("book_id", bookId).Filter("parent_id",0).OrderBy("order_sort").One(doc)
+ err := o.QueryTable(doc.TableNameWithPrefix()).Filter("book_id", bookId).Filter("parent_id", 0).OrderBy("order_sort").One(doc)
- return doc,err
+ return doc, err
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/models/document.go b/models/document.go
index 83a1040c..4124881f 100644
--- a/models/document.go
+++ b/models/document.go
@@ -39,6 +39,12 @@ type Document struct {
AttachList []*Attachment `orm:"-" json:"attach"`
}
+// 多字段唯一键
+func (m *Document) TableUnique() [][]string {
+ return [][]string{
+ []string{"book_id", "identify"},
+ }
+}
// TableName 获取对应数据库表名.
func (m *Document) TableName() string {
return "documents"
@@ -93,15 +99,6 @@ func (m *Document) InsertOrUpdate(cols ...string) error {
return nil
}
-////根据指定字段查询一条文档.
-//func (m *Document) FindByFieldFirst(field string, v interface{}) (*Document, error) {
-//
-// o := orm.NewOrm()
-//
-// err := o.QueryTable(m.TableNameWithPrefix()).Filter(field, v).One(m)
-//
-// return m, err
-//}
//根据文档识别编号和项目id获取一篇文档
func (m *Document) FindByIdentityFirst(identify string,bookId int) (*Document,error) {
o := orm.NewOrm()
@@ -120,11 +117,6 @@ func (m *Document) RecursiveDocument(docId int) error {
o.Delete(doc)
NewDocumentHistory().Clear(doc.DocumentId)
}
- //
- //var docs []*Document
- //
- //_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("parent_id", doc_id).All(&docs)
-
var maps []orm.Params
_, err := o.Raw("SELECT document_id FROM " + m.TableNameWithPrefix() + " WHERE parent_id=" + strconv.Itoa(docId)).Values(&maps)
@@ -255,6 +247,7 @@ func (m *Document) FromCacheById(id int) (*Document,error) {
}()
return m.Find(id)
}
+
//根据文档标识从缓存中查询文档
func (m *Document) FromCacheByIdentify(identify string,bookId int) (*Document,error) {
b := cache.Get(fmt.Sprintf("Document.BookId.%d.Identify.%s",bookId , identify))
@@ -280,3 +273,4 @@ func (m *Document) FindListByBookId(bookId int) (docs []*Document, err error) {
return
}
+
diff --git a/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput-rtl.css b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput-rtl.css
new file mode 100644
index 00000000..bc36a778
--- /dev/null
+++ b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput-rtl.css
@@ -0,0 +1,101 @@
+/*!
+ * bootstrap-fileinput v4.4.7
+ * http://plugins.krajee.com/file-input
+ *
+ * Krajee RTL (Right To Left) default styling for bootstrap-fileinput.
+ *
+ * Author: Kartik Visweswaran
+ * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com
+ *
+ * Licensed under the BSD 3-Clause
+ * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md
+ */
+.kv-rtl .close, .kv-rtl .krajee-default .file-actions, .kv-rtl .krajee-default .file-other-error {
+ float: left;
+}
+
+.kv-rtl .krajee-default.file-preview-frame, .kv-rtl .krajee-default .file-drag-handle, .kv-rtl .krajee-default .file-upload-indicator {
+ float: right;
+}
+
+.kv-rtl .file-zoom-dialog, .kv-rtl .file-error-message pre, .kv-rtl .file-error-message ul {
+ text-align: right;
+}
+
+.kv-rtl {
+ direction: rtl;
+}
+
+.kv-rtl .floating-buttons {
+ left: 10px;
+ right: auto;
+}
+
+.kv-rtl .floating-buttons .btn-kv {
+ margin-left: 0;
+ margin-right: 3px;
+}
+
+.kv-rtl .file-caption-icon {
+ left: auto;
+ right: 8px;
+}
+
+.kv-rtl .file-drop-zone {
+ margin: 12px 12px 12px 15px;
+}
+
+.kv-rtl .btn-prev {
+ right: 1px;
+ left: auto;
+}
+
+.kv-rtl .btn-next {
+ left: 1px;
+ right: auto;
+}
+
+.kv-rtl .pull-right, .kv-rtl .float-right {
+ float: left !important;
+}
+
+.kv-rtl .pull-left, .kv-rtl .float-left {
+ float: right !important;
+}
+
+.kv-rtl .kv-zoom-title {
+ direction: ltr;
+}
+
+.kv-rtl .krajee-default.file-preview-frame {
+ box-shadow: -1px 1px 5px 0 #a2958a;
+}
+
+.kv-rtl .krajee-default.file-preview-frame:not(.file-preview-error):hover {
+ box-shadow: -3px 3px 5px 0 #333;
+}
+
+.kv-rtl .kv-zoom-actions .btn-kv {
+ margin-left: 0;
+ margin-right: 3px;
+}
+
+.kv-rtl .file-caption.icon-visible .file-caption-name {
+ padding-left: 0;
+ padding-right: 15px;
+}
+
+.kv-rtl .input-group-btn:last-child > .btn {
+ border-radius: 4px 0 0 4px;
+}
+
+.kv-rtl .input-group .form-control:first-child {
+ border-radius: 0 4px 4px 0;
+}
+
+.kv-rtl .btn-file input[type=file] {
+ right: auto;
+ left: 0;
+ text-align: left;
+ background: none repeat scroll 100% 0 transparent;
+}
\ No newline at end of file
diff --git a/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput-rtl.min.css b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput-rtl.min.css
new file mode 100644
index 00000000..70508f67
--- /dev/null
+++ b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput-rtl.min.css
@@ -0,0 +1,12 @@
+/*!
+ * bootstrap-fileinput v4.4.7
+ * http://plugins.krajee.com/file-input
+ *
+ * Krajee RTL (Right To Left) default styling for bootstrap-fileinput.
+ *
+ * Author: Kartik Visweswaran
+ * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com
+ *
+ * Licensed under the BSD 3-Clause
+ * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md
+ */.kv-rtl .close,.kv-rtl .krajee-default .file-actions,.kv-rtl .krajee-default .file-other-error{float:left}.kv-rtl .krajee-default .file-drag-handle,.kv-rtl .krajee-default .file-upload-indicator,.kv-rtl .krajee-default.file-preview-frame{float:right}.kv-rtl .file-error-message pre,.kv-rtl .file-error-message ul,.kv-rtl .file-zoom-dialog{text-align:right}.kv-rtl{direction:rtl}.kv-rtl .floating-buttons{left:10px;right:auto}.kv-rtl .floating-buttons .btn-kv{margin-left:0;margin-right:3px}.kv-rtl .file-caption-icon{left:auto;right:8px}.kv-rtl .file-drop-zone{margin:12px 12px 12px 15px}.kv-rtl .btn-prev{right:1px;left:auto}.kv-rtl .btn-next{left:1px;right:auto}.kv-rtl .float-right,.kv-rtl .pull-right{float:left!important}.kv-rtl .float-left,.kv-rtl .pull-left{float:right!important}.kv-rtl .kv-zoom-title{direction:ltr}.kv-rtl .krajee-default.file-preview-frame{box-shadow:-1px 1px 5px 0 #a2958a}.kv-rtl .krajee-default.file-preview-frame:not(.file-preview-error):hover{box-shadow:-3px 3px 5px 0 #333}.kv-rtl .kv-zoom-actions .btn-kv{margin-left:0;margin-right:3px}.kv-rtl .file-caption.icon-visible .file-caption-name{padding-left:0;padding-right:15px}.kv-rtl .input-group-btn:last-child>.btn{border-radius:4px 0 0 4px}.kv-rtl .input-group .form-control:first-child{border-radius:0 4px 4px 0}.kv-rtl .btn-file input[type=file]{right:auto;left:0;text-align:left;background:100% 0 none}
\ No newline at end of file
diff --git a/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput.css b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput.css
new file mode 100644
index 00000000..08fdb44a
--- /dev/null
+++ b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput.css
@@ -0,0 +1,552 @@
+/*!
+ * bootstrap-fileinput v4.4.7
+ * http://plugins.krajee.com/file-input
+ *
+ * Krajee default styling for bootstrap-fileinput.
+ *
+ * Author: Kartik Visweswaran
+ * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com
+ *
+ * Licensed under the BSD 3-Clause
+ * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md
+ */
+.file-loading input[type=file], input[type=file].file-loading {
+ width: 0;
+ height: 0;
+}
+
+.kv-hidden, .file-caption-icon, .file-zoom-dialog .modal-header:before, .file-zoom-dialog .modal-header:after, .file-input-new .file-preview, .file-input-new .close, .file-input-new .glyphicon-file, .file-input-new .fileinput-remove-button, .file-input-new .fileinput-upload-button, .file-input-new .no-browse .input-group-btn, .file-input-ajax-new .fileinput-remove-button, .file-input-ajax-new .fileinput-upload-button, .file-input-ajax-new .no-browse .input-group-btn, .hide-content .kv-file-content {
+ display: none;
+}
+
+.btn-file input[type=file], .file-caption-icon, .file-preview .fileinput-remove, .krajee-default .file-thumb-progress, .file-zoom-dialog .btn-navigate, .file-zoom-dialog .floating-buttons {
+ position: absolute;
+}
+
+.file-loading:before, .btn-file, .file-caption, .file-preview, .krajee-default.file-preview-frame, .krajee-default .file-thumbnail-footer, .file-zoom-dialog .modal-dialog {
+ position: relative;
+}
+
+.file-error-message pre, .file-error-message ul, .krajee-default .file-actions, .krajee-default .file-other-error {
+ text-align: left;
+}
+
+.file-error-message pre, .file-error-message ul {
+ margin: 0;
+}
+
+.krajee-default .file-drag-handle, .krajee-default .file-upload-indicator {
+ float: left;
+ margin: 5px 0 -5px;
+ width: 16px;
+ height: 16px;
+}
+
+.krajee-default .file-thumb-progress .progress, .krajee-default .file-thumb-progress .progress-bar {
+ height: 11px;
+ font-size: 9px;
+ line-height: 10px;
+}
+
+.krajee-default .file-caption-info, .krajee-default .file-size-info {
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 160px;
+ height: 15px;
+ margin: auto;
+}
+
+.file-zoom-content > .file-object.type-video, .file-zoom-content > .file-object.type-flash, .file-zoom-content > .file-object.type-image {
+ max-width: 100%;
+ max-height: 100%;
+ width: auto;
+}
+
+.file-zoom-content > .file-object.type-video, .file-zoom-content > .file-object.type-flash {
+ height: 100%;
+}
+
+.file-zoom-content > .file-object.type-pdf, .file-zoom-content > .file-object.type-html, .file-zoom-content > .file-object.type-text, .file-zoom-content > .file-object.type-default {
+ width: 100%;
+}
+
+.rotate-2 {
+ transform: rotateY(180deg);
+}
+
+.rotate-3 {
+ transform: rotate(180deg);
+}
+
+.rotate-4 {
+ transform: rotate(180deg) rotateY(180deg);
+}
+
+.rotate-5 {
+ transform: rotate(270deg) rotateY(180deg);
+}
+
+.rotate-6 {
+ transform: rotate(90deg);
+}
+
+.rotate-7 {
+ transform: rotate(90deg) rotateY(180deg);
+}
+
+.rotate-8 {
+ transform: rotate(270deg);
+}
+
+.file-loading:before {
+ content: " Loading...";
+ display: inline-block;
+ padding-left: 20px;
+ line-height: 16px;
+ font-size: 13px;
+ font-variant: small-caps;
+ color: #999;
+ background: transparent url(../img/loading.gif) top left no-repeat;
+}
+
+.file-object {
+ margin: 0 0 -5px 0;
+ padding: 0;
+}
+
+.btn-file {
+ overflow: hidden;
+}
+
+.btn-file input[type=file] {
+ top: 0;
+ right: 0;
+ min-width: 100%;
+ min-height: 100%;
+ text-align: right;
+ opacity: 0;
+ background: none repeat scroll 0 0 transparent;
+ cursor: inherit;
+ display: block;
+}
+
+.btn-file ::-ms-browse {
+ font-size: 10000px;
+ width: 100%;
+ height: 100%;
+}
+
+.file-caption .file-caption-name {
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ box-shadow: none;
+ border: none;
+ background: none;
+ outline: none;
+}
+
+.file-caption.icon-visible .file-caption-icon {
+ display: inline-block;
+}
+
+.file-caption.icon-visible .file-caption-name {
+ padding-left: 15px;
+}
+
+.file-caption-icon {
+ line-height: 1;
+ left: 8px;
+}
+
+.file-error-message {
+ color: #a94442;
+ background-color: #f2dede;
+ margin: 5px;
+ border: 1px solid #ebccd1;
+ border-radius: 4px;
+ padding: 15px;
+}
+
+.file-error-message pre {
+ margin: 5px 0;
+}
+
+.file-caption-disabled {
+ background-color: #eee;
+ cursor: not-allowed;
+ opacity: 1;
+}
+
+.file-preview {
+ border-radius: 5px;
+ border: 1px solid #ddd;
+ padding: 8px;
+ width: 100%;
+ margin-bottom: 5px;
+}
+
+.file-preview .btn-xs {
+ padding: 1px 5px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+
+.file-preview .fileinput-remove {
+ top: 1px;
+ right: 1px;
+ line-height: 10px;
+}
+
+.file-preview .clickable {
+ cursor: pointer;
+}
+
+.file-preview-image {
+ font: 40px Impact, Charcoal, sans-serif;
+ color: #008000;
+}
+
+.krajee-default.file-preview-frame {
+ margin: 8px;
+ border: 1px solid #ddd;
+ box-shadow: 1px 1px 5px 0 #a2958a;
+ padding: 6px;
+ float: left;
+ text-align: center;
+}
+
+.krajee-default.file-preview-frame .kv-file-content {
+ width: 213px;
+ height: 160px;
+}
+
+.krajee-default.file-preview-frame .file-thumbnail-footer {
+ height: 70px;
+}
+
+.krajee-default.file-preview-frame:not(.file-preview-error):hover {
+ box-shadow: 3px 3px 5px 0 #333;
+}
+
+.krajee-default .file-preview-text {
+ display: block;
+ color: #428bca;
+ border: 1px solid #ddd;
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+ outline: none;
+ padding: 8px;
+ resize: none;
+}
+
+.krajee-default .file-preview-html {
+ border: 1px solid #ddd;
+ padding: 8px;
+ overflow: auto;
+}
+
+.krajee-default .file-other-icon {
+ font-size: 6em;
+}
+
+.krajee-default .file-footer-buttons {
+ float: right;
+}
+
+.krajee-default .file-footer-caption {
+ display: block;
+ text-align: center;
+ padding-top: 4px;
+ font-size: 11px;
+ color: #777;
+ margin-bottom: 15px;
+}
+
+.krajee-default .file-preview-error {
+ opacity: 0.65;
+ box-shadow: none;
+}
+
+.krajee-default .file-thumb-progress {
+ height: 11px;
+ top: 37px;
+ left: 0;
+ right: 0;
+}
+
+.krajee-default.kvsortable-ghost {
+ background: #e1edf7;
+ border: 2px solid #a1abff;
+}
+
+.krajee-default .file-preview-other:hover {
+ opacity: 0.8;
+}
+
+.krajee-default .file-preview-frame:not(.file-preview-error) .file-footer-caption:hover {
+ color: #000;
+}
+
+.kv-upload-progress .progress {
+ height: 20px;
+ line-height: 20px;
+ margin: 10px 0;
+ overflow: hidden;
+}
+
+.kv-upload-progress .progress-bar {
+ height: 20px;
+ line-height: 20px;
+}
+
+/*noinspection CssOverwrittenProperties*/
+.file-zoom-dialog .file-other-icon {
+ font-size: 22em;
+ font-size: 50vmin;
+}
+
+.file-zoom-dialog .modal-dialog {
+ width: auto;
+}
+
+.file-zoom-dialog .modal-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.file-zoom-dialog .btn-navigate {
+ padding: 0;
+ margin: 0;
+ background: transparent;
+ text-decoration: none;
+ outline: none;
+ opacity: 0.7;
+ top: 45%;
+ font-size: 4em;
+ color: #1c94c4;
+}
+
+.file-zoom-dialog .btn-navigate:not([disabled]):hover {
+ outline: none;
+ box-shadow: none;
+ opacity: 0.6;
+}
+
+.file-zoom-dialog .floating-buttons {
+ top: 5px;
+ right: 10px;
+}
+
+.file-zoom-dialog .btn-navigate[disabled] {
+ opacity: 0.3;
+}
+
+.file-zoom-dialog .btn-prev {
+ left: 1px;
+}
+
+.file-zoom-dialog .btn-next {
+ right: 1px;
+}
+
+.file-zoom-dialog .kv-zoom-title {
+ font-weight: 300;
+ color: #999;
+ max-width: 50%;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.file-input-new .no-browse .form-control {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+
+.file-input-ajax-new .no-browse .form-control {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+
+.file-caption-main {
+ width: 100%;
+}
+
+.file-thumb-loading {
+ background: transparent url(../img/loading.gif) no-repeat scroll center center content-box !important;
+}
+
+.file-drop-zone {
+ border: 1px dashed #aaa;
+ border-radius: 4px;
+ height: 100%;
+ text-align: center;
+ vertical-align: middle;
+ margin: 12px 15px 12px 12px;
+ padding: 5px;
+}
+
+.file-drop-zone.clickable:hover {
+ border: 2px dashed #999;
+}
+
+.file-drop-zone.clickable:focus {
+ border: 2px solid #5acde2;
+}
+
+.file-drop-zone .file-preview-thumbnails {
+ cursor: default;
+}
+
+.file-drop-zone-title {
+ color: #aaa;
+ font-size: 1.6em;
+ padding: 85px 10px;
+ cursor: default;
+}
+
+.file-highlighted {
+ border: 2px dashed #999 !important;
+ background-color: #eee;
+}
+
+.file-uploading {
+ background: url(../img/loading-sm.gif) no-repeat center bottom 10px;
+ opacity: 0.65;
+}
+
+@media (min-width: 576px) {
+ .file-zoom-dialog .modal-dialog {
+ max-width: 500px;
+ }
+}
+
+@media (min-width: 992px) {
+ .file-zoom-dialog .modal-lg {
+ max-width: 800px;
+ }
+}
+
+.file-zoom-fullscreen.modal {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.file-zoom-fullscreen .modal-dialog {
+ position: fixed;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ max-width: 100%;
+ max-height: 100%;
+}
+
+.file-zoom-fullscreen .modal-content {
+ border-radius: 0;
+ box-shadow: none;
+}
+
+.file-zoom-fullscreen .modal-body {
+ overflow-y: auto;
+}
+
+.btn-kv {
+ display: inline-block;
+ text-align: center;
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ padding: 0;
+ font-size: 90%;
+ border-radius: 0.2rem;
+}
+
+.floating-buttons {
+ z-index: 3000;
+}
+
+.floating-buttons .btn-kv {
+ margin-left: 3px;
+ z-index: 3000;
+}
+
+.file-zoom-content {
+ height: 480px;
+ text-align: center;
+}
+
+.file-zoom-content .file-preview-image {
+ max-height: 100%;
+}
+
+.file-zoom-content .file-preview-video {
+ max-height: 100%;
+}
+
+.file-zoom-content .is-portrait-gt4 {
+ margin-top: 60px;
+}
+
+.file-zoom-content > .file-object.type-image {
+ height: auto;
+ min-height: inherit;
+}
+
+.file-zoom-content > .file-object.type-audio {
+ width: auto;
+ height: 30px;
+}
+
+@media screen and (max-width: 767px) {
+ .file-preview-thumbnails {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ }
+
+ .file-zoom-dialog .modal-header {
+ flex-direction: column;
+ }
+}
+
+@media screen and (max-width: 350px) {
+ .krajee-default.file-preview-frame .kv-file-content {
+ width: 160px;
+ }
+}
+
+.file-loading[dir=rtl]:before {
+ background: transparent url(../img/loading.gif) top right no-repeat;
+ padding-left: 0;
+ padding-right: 20px;
+}
+
+.file-sortable .file-drag-handle {
+ cursor: move;
+ opacity: 1;
+}
+
+.file-sortable .file-drag-handle:hover {
+ opacity: 0.7;
+}
+
+.clickable .file-drop-zone-title {
+ cursor: pointer;
+}
+
+.kv-zoom-actions .btn-kv {
+ margin-left: 3px;
+}
+
+.file-preview-initial.sortable-chosen {
+ background-color: #d9edf7;
+}
\ No newline at end of file
diff --git a/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput.min.css b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput.min.css
new file mode 100644
index 00000000..90b36a6e
--- /dev/null
+++ b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput.min.css
@@ -0,0 +1,12 @@
+/*!
+ * bootstrap-fileinput v4.4.7
+ * http://plugins.krajee.com/file-input
+ *
+ * Krajee default styling for bootstrap-fileinput.
+ *
+ * Author: Kartik Visweswaran
+ * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com
+ *
+ * Licensed under the BSD 3-Clause
+ * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md
+ */.file-loading input[type=file],input[type=file].file-loading{width:0;height:0}.file-caption-icon,.file-input-ajax-new .fileinput-remove-button,.file-input-ajax-new .fileinput-upload-button,.file-input-ajax-new .no-browse .input-group-btn,.file-input-new .close,.file-input-new .file-preview,.file-input-new .fileinput-remove-button,.file-input-new .fileinput-upload-button,.file-input-new .glyphicon-file,.file-input-new .no-browse .input-group-btn,.file-zoom-dialog .modal-header:after,.file-zoom-dialog .modal-header:before,.hide-content .kv-file-content,.kv-hidden{display:none}.btn-file input[type=file],.file-caption-icon,.file-preview .fileinput-remove,.file-zoom-dialog .btn-navigate,.file-zoom-dialog .floating-buttons,.krajee-default .file-thumb-progress{position:absolute}.btn-file,.file-caption,.file-loading:before,.file-preview,.file-zoom-dialog .modal-dialog,.krajee-default .file-thumbnail-footer,.krajee-default.file-preview-frame{position:relative}.file-error-message pre,.file-error-message ul,.krajee-default .file-actions,.krajee-default .file-other-error{text-align:left}.file-error-message pre,.file-error-message ul{margin:0}.krajee-default .file-drag-handle,.krajee-default .file-upload-indicator{float:left;margin:5px 0 -5px;width:16px;height:16px}.krajee-default .file-thumb-progress .progress,.krajee-default .file-thumb-progress .progress-bar{height:11px;font-size:9px;line-height:10px}.krajee-default .file-caption-info,.krajee-default .file-size-info{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:160px;height:15px;margin:auto}.file-zoom-content>.file-object.type-flash,.file-zoom-content>.file-object.type-image,.file-zoom-content>.file-object.type-video{max-width:100%;max-height:100%;width:auto}.file-zoom-content>.file-object.type-flash,.file-zoom-content>.file-object.type-video{height:100%}.file-zoom-content>.file-object.type-default,.file-zoom-content>.file-object.type-html,.file-zoom-content>.file-object.type-pdf,.file-zoom-content>.file-object.type-text{width:100%}.rotate-2{transform:rotateY(180deg)}.rotate-3{transform:rotate(180deg)}.rotate-4{transform:rotate(180deg) rotateY(180deg)}.rotate-5{transform:rotate(270deg) rotateY(180deg)}.rotate-6{transform:rotate(90deg)}.rotate-7{transform:rotate(90deg) rotateY(180deg)}.rotate-8{transform:rotate(270deg)}.file-loading:before{content:" Loading...";display:inline-block;padding-left:20px;line-height:16px;font-size:13px;font-variant:small-caps;color:#999;background:url(../img/loading.gif) top left no-repeat}.file-object{margin:0 0 -5px;padding:0}.btn-file{overflow:hidden}.btn-file input[type=file]{top:0;right:0;min-width:100%;min-height:100%;text-align:right;opacity:0;background:none;cursor:inherit;display:block}.btn-file ::-ms-browse{font-size:10000px;width:100%;height:100%}.file-caption .file-caption-name{width:100%;margin:0;padding:0;box-shadow:none;border:none;background:0 0;outline:0}.file-caption.icon-visible .file-caption-icon{display:inline-block}.file-caption.icon-visible .file-caption-name{padding-left:15px}.file-caption-icon{line-height:1;left:8px}.file-error-message{color:#a94442;background-color:#f2dede;margin:5px;border:1px solid #ebccd1;border-radius:4px;padding:15px}.file-error-message pre{margin:5px 0}.file-caption-disabled{background-color:#eee;cursor:not-allowed;opacity:1}.file-preview{border-radius:5px;border:1px solid #ddd;padding:8px;width:100%;margin-bottom:5px}.file-preview .btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.file-preview .fileinput-remove{top:1px;right:1px;line-height:10px}.file-preview .clickable{cursor:pointer}.file-preview-image{font:40px Impact,Charcoal,sans-serif;color:green}.krajee-default.file-preview-frame{margin:8px;border:1px solid #ddd;box-shadow:1px 1px 5px 0 #a2958a;padding:6px;float:left;text-align:center}.krajee-default.file-preview-frame .kv-file-content{width:213px;height:160px}.krajee-default.file-preview-frame .file-thumbnail-footer{height:70px}.krajee-default.file-preview-frame:not(.file-preview-error):hover{box-shadow:3px 3px 5px 0 #333}.krajee-default .file-preview-text{display:block;color:#428bca;border:1px solid #ddd;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;outline:0;padding:8px;resize:none}.krajee-default .file-preview-html{border:1px solid #ddd;padding:8px;overflow:auto}.krajee-default .file-other-icon{font-size:6em}.krajee-default .file-footer-buttons{float:right}.krajee-default .file-footer-caption{display:block;text-align:center;padding-top:4px;font-size:11px;color:#777;margin-bottom:15px}.krajee-default .file-preview-error{opacity:.65;box-shadow:none}.krajee-default .file-thumb-progress{height:11px;top:37px;left:0;right:0}.krajee-default.kvsortable-ghost{background:#e1edf7;border:2px solid #a1abff}.krajee-default .file-preview-other:hover{opacity:.8}.krajee-default .file-preview-frame:not(.file-preview-error) .file-footer-caption:hover{color:#000}.kv-upload-progress .progress{height:20px;line-height:20px;margin:10px 0;overflow:hidden}.kv-upload-progress .progress-bar{height:20px;line-height:20px}.file-zoom-dialog .file-other-icon{font-size:22em;font-size:50vmin}.file-zoom-dialog .modal-dialog{width:auto}.file-zoom-dialog .modal-header{display:flex;align-items:center;justify-content:space-between}.file-zoom-dialog .btn-navigate{padding:0;margin:0;background:0 0;text-decoration:none;outline:0;opacity:.7;top:45%;font-size:4em;color:#1c94c4}.file-zoom-dialog .btn-navigate:not([disabled]):hover{outline:0;box-shadow:none;opacity:.6}.file-zoom-dialog .floating-buttons{top:5px;right:10px}.file-zoom-dialog .btn-navigate[disabled]{opacity:.3}.file-zoom-dialog .btn-prev{left:1px}.file-zoom-dialog .btn-next{right:1px}.file-zoom-dialog .kv-zoom-title{font-weight:300;color:#999;max-width:50%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-input-ajax-new .no-browse .form-control,.file-input-new .no-browse .form-control{border-top-right-radius:4px;border-bottom-right-radius:4px}.file-caption-main{width:100%}.file-thumb-loading{background:url(../img/loading.gif) center center no-repeat content-box!important}.file-drop-zone{border:1px dashed #aaa;border-radius:4px;height:100%;text-align:center;vertical-align:middle;margin:12px 15px 12px 12px;padding:5px}.file-drop-zone.clickable:hover{border:2px dashed #999}.file-drop-zone.clickable:focus{border:2px solid #5acde2}.file-drop-zone .file-preview-thumbnails{cursor:default}.file-drop-zone-title{color:#aaa;font-size:1.6em;padding:85px 10px;cursor:default}.file-highlighted{border:2px dashed #999!important;background-color:#eee}.file-uploading{background:url(../img/loading-sm.gif) center bottom 10px no-repeat;opacity:.65}@media (min-width:576px){.file-zoom-dialog .modal-dialog{max-width:500px}}@media (min-width:992px){.file-zoom-dialog .modal-lg{max-width:800px}}.file-zoom-fullscreen.modal{position:fixed;top:0;right:0;bottom:0;left:0}.file-zoom-fullscreen .modal-dialog{position:fixed;margin:0;padding:0;width:100%;height:100%;max-width:100%;max-height:100%}.file-zoom-fullscreen .modal-content{border-radius:0;box-shadow:none}.file-zoom-fullscreen .modal-body{overflow-y:auto}.btn-kv{display:inline-block;text-align:center;width:30px;height:30px;line-height:30px;padding:0;font-size:90%;border-radius:.2rem}.floating-buttons{z-index:3000}.floating-buttons .btn-kv{margin-left:3px;z-index:3000}.file-zoom-content{height:480px;text-align:center}.file-zoom-content .file-preview-image,.file-zoom-content .file-preview-video{max-height:100%}.file-zoom-content .is-portrait-gt4{margin-top:60px}.file-zoom-content>.file-object.type-image{height:auto;min-height:inherit}.file-zoom-content>.file-object.type-audio{width:auto;height:30px}@media screen and (max-width:767px){.file-preview-thumbnails{display:flex;justify-content:center;align-items:center;flex-direction:column}.file-zoom-dialog .modal-header{flex-direction:column}}@media screen and (max-width:350px){.krajee-default.file-preview-frame .kv-file-content{width:160px}}.file-loading[dir=rtl]:before{background:url(../img/loading.gif) top right no-repeat;padding-left:0;padding-right:20px}.file-sortable .file-drag-handle{cursor:move;opacity:1}.file-sortable .file-drag-handle:hover{opacity:.7}.clickable .file-drop-zone-title{cursor:pointer}.kv-zoom-actions .btn-kv{margin-left:3px}.file-preview-initial.sortable-chosen{background-color:#d9edf7}
\ No newline at end of file
diff --git a/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/img/loading-sm.gif b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/img/loading-sm.gif
new file mode 100644
index 00000000..44e3b7a0
Binary files /dev/null and b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/img/loading-sm.gif differ
diff --git a/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/img/loading.gif b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/img/loading.gif
new file mode 100644
index 00000000..0ea146c0
Binary files /dev/null and b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/img/loading.gif differ
diff --git a/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/js/fileinput.js b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/js/fileinput.js
new file mode 100644
index 00000000..1272befd
--- /dev/null
+++ b/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/js/fileinput.js
@@ -0,0 +1,4312 @@
+/*!
+ * bootstrap-fileinput v4.4.7
+ * http://plugins.krajee.com/file-input
+ *
+ * Author: Kartik Visweswaran
+ * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com
+ *
+ * Licensed under the BSD 3-Clause
+ * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md
+ */
+(function (factory) {
+ "use strict";
+ //noinspection JSUnresolvedVariable
+ if (typeof define === 'function' && define.amd) { // jshint ignore:line
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory); // jshint ignore:line
+ } else { // noinspection JSUnresolvedVariable
+ if (typeof module === 'object' && module.exports) { // jshint ignore:line
+ // Node/CommonJS
+ // noinspection JSUnresolvedVariable
+ module.exports = factory(require('jquery')); // jshint ignore:line
+ } else {
+ // Browser globals
+ factory(window.jQuery);
+ }
+ }
+}(function ($) {
+ "use strict";
+
+ $.fn.fileinputLocales = {};
+ $.fn.fileinputThemes = {};
+
+ String.prototype.setTokens = function (replacePairs) {
+ var str = this.toString(), key, re;
+ for (key in replacePairs) {
+ if (replacePairs.hasOwnProperty(key)) {
+ re = new RegExp("\{" + key + "\}", "g");
+ str = str.replace(re, replacePairs[key]);
+ }
+ }
+ return str;
+ };
+
+ var $h, FileInput;
+
+ // fileinput helper object for all global variables and internal helper methods
+ //noinspection JSUnresolvedVariable
+ $h = {
+ FRAMES: '.kv-preview-thumb',
+ SORT_CSS: 'file-sortable',
+ OBJECT_PARAMS: '\n' +
+ '\n' +
+ '\n' +
+ '\n' +
+ '\n' +
+ '\n',
+ DEFAULT_PREVIEW: '\n' +
+ '{previewFileIcon}\n' +
+ '
',
+ MODAL_ID: 'kvFileinputModal',
+ MODAL_EVENTS: ['show', 'shown', 'hide', 'hidden', 'loaded'],
+ objUrl: window.URL || window.webkitURL,
+ compare: function (input, str, exact) {
+ return input !== undefined && (exact ? input === str : input.match(str));
+ },
+ isIE: function (ver) {
+ // check for IE versions < 11
+ if (navigator.appName !== 'Microsoft Internet Explorer') {
+ return false;
+ }
+ if (ver === 10) {
+ return new RegExp('msie\\s' + ver, 'i').test(navigator.userAgent);
+ }
+ var div = document.createElement("div"), status;
+ div.innerHTML = "";
+ status = div.getElementsByTagName("i").length;
+ document.body.appendChild(div);
+ div.parentNode.removeChild(div);
+ return status;
+ },
+ initModal: function ($modal) {
+ var $body = $('body');
+ if ($body.length) {
+ $modal.appendTo($body);
+ }
+ },
+ isEmpty: function (value, trim) {
+ return value === undefined || value === null || value.length === 0 || (trim && $.trim(value) === '');
+ },
+ isArray: function (a) {
+ return Array.isArray(a) || Object.prototype.toString.call(a) === '[object Array]';
+ },
+ ifSet: function (needle, haystack, def) {
+ def = def || '';
+ return (haystack && typeof haystack === 'object' && needle in haystack) ? haystack[needle] : def;
+ },
+ cleanArray: function (arr) {
+ if (!(arr instanceof Array)) {
+ arr = [];
+ }
+ return arr.filter(function (e) {
+ return (e !== undefined && e !== null);
+ });
+ },
+ spliceArray: function (arr, index) {
+ var i, j = 0, out = [];
+ if (!(arr instanceof Array)) {
+ return [];
+ }
+ for (i = 0; i < arr.length; i++) {
+ if (i !== index) {
+ out[j] = arr[i];
+ j++;
+ }
+ }
+ return out;
+ },
+ getNum: function (num, def) {
+ def = def || 0;
+ if (typeof num === "number") {
+ return num;
+ }
+ if (typeof num === "string") {
+ num = parseFloat(num);
+ }
+ return isNaN(num) ? def : num;
+ },
+ hasFileAPISupport: function () {
+ return !!(window.File && window.FileReader);
+ },
+ hasDragDropSupport: function () {
+ var div = document.createElement('div');
+ /** @namespace div.draggable */
+ /** @namespace div.ondragstart */
+ /** @namespace div.ondrop */
+ return !$h.isIE(9) &&
+ (div.draggable !== undefined || (div.ondragstart !== undefined && div.ondrop !== undefined));
+ },
+ hasFileUploadSupport: function () {
+ return $h.hasFileAPISupport() && window.FormData;
+ },
+ hasBlobSupport: function () {
+ try {
+ return !!window.Blob && Boolean(new Blob());
+ } catch (e) {
+ return false;
+ }
+ },
+ hasArrayBufferViewSupport: function () {
+ try {
+ return new Blob([new Uint8Array(100)]).size === 100;
+ } catch (e) {
+ return false;
+ }
+ },
+ dataURI2Blob: function (dataURI) {
+ //noinspection JSUnresolvedVariable
+ var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder ||
+ window.MSBlobBuilder, canBlob = $h.hasBlobSupport(), byteStr, arrayBuffer, intArray, i, mimeStr, bb,
+ canProceed = (canBlob || BlobBuilder) && window.atob && window.ArrayBuffer && window.Uint8Array;
+ if (!canProceed) {
+ return null;
+ }
+ if (dataURI.split(',')[0].indexOf('base64') >= 0) {
+ byteStr = atob(dataURI.split(',')[1]);
+ } else {
+ byteStr = decodeURIComponent(dataURI.split(',')[1]);
+ }
+ arrayBuffer = new ArrayBuffer(byteStr.length);
+ intArray = new Uint8Array(arrayBuffer);
+ for (i = 0; i < byteStr.length; i += 1) {
+ intArray[i] = byteStr.charCodeAt(i);
+ }
+ mimeStr = dataURI.split(',')[0].split(':')[1].split(';')[0];
+ if (canBlob) {
+ return new Blob([$h.hasArrayBufferViewSupport() ? intArray : arrayBuffer], {type: mimeStr});
+ }
+ bb = new BlobBuilder();
+ bb.append(arrayBuffer);
+ return bb.getBlob(mimeStr);
+ },
+ arrayBuffer2String: function (buffer) {
+ //noinspection JSUnresolvedVariable
+ if (window.TextDecoder) {
+ // noinspection JSUnresolvedFunction
+ return new TextDecoder("utf-8").decode(buffer);
+ }
+ var array = Array.prototype.slice.apply(new Uint8Array(buffer)), out = '', i = 0, len, c, char2, char3;
+ len = array.length;
+ while (i < len) {
+ c = array[i++];
+ switch (c >> 4) { // jshint ignore:line
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ // 0xxxxxxx
+ out += String.fromCharCode(c);
+ break;
+ case 12:
+ case 13:
+ // 110x xxxx 10xx xxxx
+ char2 = array[i++];
+ out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); // jshint ignore:line
+ break;
+ case 14:
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ char2 = array[i++];
+ char3 = array[i++];
+ out += String.fromCharCode(((c & 0x0F) << 12) | // jshint ignore:line
+ ((char2 & 0x3F) << 6) | // jshint ignore:line
+ ((char3 & 0x3F) << 0)); // jshint ignore:line
+ break;
+ }
+ }
+ return out;
+ },
+ isHtml: function (str) {
+ var a = document.createElement('div');
+ a.innerHTML = str;
+ for (var c = a.childNodes, i = c.length; i--;) {
+ if (c[i].nodeType === 1) {
+ return true;
+ }
+ }
+ return false;
+ },
+ isSvg: function (str) {
+ return str.match(/^\s*<\?xml/i) && (str.match(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ },
+ replaceTags: function (str, tags) {
+ var out = str;
+ if (!tags) {
+ return out;
+ }
+ $.each(tags, function (key, value) {
+ if (typeof value === "function") {
+ value = value();
+ }
+ out = out.split(key).join(value);
+ });
+ return out;
+ },
+ cleanMemory: function ($thumb) {
+ var data = $thumb.is('img') ? $thumb.attr('src') : $thumb.find('source').attr('src');
+ /** @namespace $h.objUrl.revokeObjectURL */
+ $h.objUrl.revokeObjectURL(data);
+ },
+ findFileName: function (filePath) {
+ var sepIndex = filePath.lastIndexOf('/');
+ if (sepIndex === -1) {
+ sepIndex = filePath.lastIndexOf('\\');
+ }
+ return filePath.split(filePath.substring(sepIndex, sepIndex + 1)).pop();
+ },
+ checkFullScreen: function () {
+ //noinspection JSUnresolvedVariable
+ return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
+ document.msFullscreenElement;
+ },
+ toggleFullScreen: function (maximize) {
+ var doc = document, de = doc.documentElement;
+ if (de && maximize && !$h.checkFullScreen()) {
+ /** @namespace document.requestFullscreen */
+ /** @namespace document.msRequestFullscreen */
+ /** @namespace document.mozRequestFullScreen */
+ /** @namespace document.webkitRequestFullscreen */
+ /** @namespace Element.ALLOW_KEYBOARD_INPUT */
+ if (de.requestFullscreen) {
+ de.requestFullscreen();
+ } else if (de.msRequestFullscreen) {
+ de.msRequestFullscreen();
+ } else if (de.mozRequestFullScreen) {
+ de.mozRequestFullScreen();
+ } else if (de.webkitRequestFullscreen) {
+ de.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
+ }
+ } else {
+ /** @namespace document.exitFullscreen */
+ /** @namespace document.msExitFullscreen */
+ /** @namespace document.mozCancelFullScreen */
+ /** @namespace document.webkitExitFullscreen */
+ if (doc.exitFullscreen) {
+ doc.exitFullscreen();
+ } else if (doc.msExitFullscreen) {
+ doc.msExitFullscreen();
+ } else if (doc.mozCancelFullScreen) {
+ doc.mozCancelFullScreen();
+ } else if (doc.webkitExitFullscreen) {
+ doc.webkitExitFullscreen();
+ }
+ }
+ },
+ moveArray: function (arr, oldIndex, newIndex) {
+ if (newIndex >= arr.length) {
+ var k = newIndex - arr.length;
+ while ((k--) + 1) {
+ arr.push(undefined);
+ }
+ }
+ arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]);
+ return arr;
+ },
+ cleanZoomCache: function ($el) {
+ var $cache = $el.closest('.kv-zoom-cache-theme');
+ if (!$cache.length) {
+ $cache = $el.closest('.kv-zoom-cache');
+ }
+ $cache.remove();
+ },
+ setOrientation: function (buffer, callback) {
+ var scanner = new DataView(buffer), idx = 0, value = 1, // Non-rotated is the default
+ maxBytes, uInt16, exifLength;
+ if (scanner.getUint16(idx) !== 0xFFD8 || buffer.length < 2) { // not a proper JPEG
+ if (callback) {
+ callback();
+ }
+ return;
+ }
+ idx += 2;
+ maxBytes = scanner.byteLength;
+ while (idx < maxBytes - 2) {
+ uInt16 = scanner.getUint16(idx);
+ idx += 2;
+ switch (uInt16) {
+ case 0xFFE1: // Start of EXIF
+ exifLength = scanner.getUint16(idx);
+ maxBytes = exifLength - idx;
+ idx += 2;
+ break;
+ case 0x0112: // Orientation tag
+ value = scanner.getUint16(idx + 6, false);
+ maxBytes = 0; // Stop scanning
+ break;
+ }
+ }
+ if (callback) {
+ callback(value);
+ }
+ },
+ validateOrientation: function (file, callback) {
+ if (!window.FileReader || !window.DataView) {
+ return; // skip orientation if pre-requisite libraries not supported by browser
+ }
+ var reader = new FileReader(), buffer;
+ reader.onloadend = function () {
+ buffer = reader.result;
+ $h.setOrientation(buffer, callback);
+ };
+ reader.readAsArrayBuffer(file);
+ },
+ adjustOrientedImage: function ($img, isZoom) {
+ var offsetContTop, offsetTop, newTop;
+ if (!$img.hasClass('is-portrait-gt4')) {
+ return;
+ }
+ if (isZoom) {
+ $img.css({width: $img.parent().height()});
+ return;
+ } else {
+ $img.css({height: 'auto', width: $img.height()});
+ }
+ offsetContTop = $img.parent().offset().top;
+ offsetTop = $img.offset().top;
+ newTop = offsetContTop - offsetTop;
+ $img.css('margin-top', newTop);
+ },
+ closeButton: function (css) {
+ css = css ? 'close ' + css : 'close';
+ return '';
+ }
+ };
+ FileInput = function (element, options) {
+ var self = this;
+ self.$element = $(element);
+ self.$parent = self.$element.parent();
+ if (!self._validate()) {
+ return;
+ }
+ self.isPreviewable = $h.hasFileAPISupport();
+ self.isIE9 = $h.isIE(9);
+ self.isIE10 = $h.isIE(10);
+ if (self.isPreviewable || self.isIE9) {
+ self._init(options);
+ self._listen();
+ }
+ self.$element.removeClass('file-loading');
+ };
+ //noinspection JSUnusedGlobalSymbols
+ FileInput.prototype = {
+ constructor: FileInput,
+ _cleanup: function () {
+ var self = this;
+ self.reader = null;
+ self.formdata = {};
+ self.uploadCount = 0;
+ self.uploadStatus = {};
+ self.uploadLog = [];
+ self.uploadAsyncCount = 0;
+ self.loadedImages = [];
+ self.totalImagesCount = 0;
+ self.ajaxRequests = [];
+ self.clearStack();
+ self.fileInputCleared = false;
+ self.fileBatchCompleted = true;
+ if (!self.isPreviewable) {
+ self.showPreview = false;
+ }
+ self.isError = false;
+ self.ajaxAborted = false;
+ self.cancelling = false;
+ },
+ _init: function (options, refreshMode) {
+ var self = this, f, $el = self.$element, $cont, t, tmp;
+ self.options = options;
+ $.each(options, function (key, value) {
+ switch (key) {
+ case 'minFileCount':
+ case 'maxFileCount':
+ case 'minFileSize':
+ case 'maxFileSize':
+ case 'maxFilePreviewSize':
+ case 'resizeImageQuality':
+ case 'resizeIfSizeMoreThan':
+ case 'progressUploadThreshold':
+ case 'initialPreviewCount':
+ case 'zoomModalHeight':
+ case 'minImageHeight':
+ case 'maxImageHeight':
+ case 'minImageWidth':
+ case 'maxImageWidth':
+ self[key] = $h.getNum(value);
+ break;
+ default:
+ self[key] = value;
+ break;
+ }
+ });
+ if (self.rtl) { // swap buttons for rtl
+ tmp = self.previewZoomButtonIcons.prev;
+ self.previewZoomButtonIcons.prev = self.previewZoomButtonIcons.next;
+ self.previewZoomButtonIcons.next = tmp;
+ }
+ if (!refreshMode) {
+ self._cleanup();
+ }
+ self.$form = $el.closest('form');
+ self._initTemplateDefaults();
+ self.uploadFileAttr = !$h.isEmpty($el.attr('name')) ? $el.attr('name') : 'file_data';
+ t = self._getLayoutTemplate('progress');
+ self.progressTemplate = t.replace('{class}', self.progressClass);
+ self.progressCompleteTemplate = t.replace('{class}', self.progressCompleteClass);
+ self.progressErrorTemplate = t.replace('{class}', self.progressErrorClass);
+ self.dropZoneEnabled = $h.hasDragDropSupport() && self.dropZoneEnabled;
+ self.isDisabled = $el.attr('disabled') || $el.attr('readonly');
+ if (self.isDisabled) {
+ $el.attr('disabled', true);
+ }
+ self.isAjaxUpload = $h.hasFileUploadSupport() && !$h.isEmpty(self.uploadUrl);
+ self.isClickable = self.browseOnZoneClick && self.showPreview &&
+ (self.isAjaxUpload && self.dropZoneEnabled || !$h.isEmpty(self.defaultPreviewContent));
+ self.slug = typeof options.slugCallback === "function" ? options.slugCallback : self._slugDefault;
+ self.mainTemplate = self.showCaption ? self._getLayoutTemplate('main1') : self._getLayoutTemplate('main2');
+ self.captionTemplate = self._getLayoutTemplate('caption');
+ self.previewGenericTemplate = self._getPreviewTemplate('generic');
+ if (!self.imageCanvas && self.resizeImage && (self.maxImageWidth || self.maxImageHeight)) {
+ self.imageCanvas = document.createElement('canvas');
+ self.imageCanvasContext = self.imageCanvas.getContext('2d');
+ }
+ if ($h.isEmpty($el.attr('id'))) {
+ $el.attr('id', $h.uniqId());
+ }
+ self.namespace = '.fileinput_' + $el.attr('id').replace(/-/g, '_');
+ if (self.$container === undefined) {
+ self.$container = self._createContainer();
+ } else {
+ self._refreshContainer();
+ }
+ $cont = self.$container;
+ self.$dropZone = $cont.find('.file-drop-zone');
+ self.$progress = $cont.find('.kv-upload-progress');
+ self.$btnUpload = $cont.find('.fileinput-upload');
+ self.$captionContainer = $h.getElement(options, 'elCaptionContainer', $cont.find('.file-caption'));
+ self.$caption = $h.getElement(options, 'elCaptionText', $cont.find('.file-caption-name'));
+ if (!$h.isEmpty(self.msgPlaceholder)) {
+ f = $el.attr('multiple') ? self.filePlural : self.fileSingle;
+ self.$caption.attr('placeholder', self.msgPlaceholder.replace('{files}', f));
+ }
+ self.$captionIcon = self.$captionContainer.find('.file-caption-icon');
+ self.$previewContainer = $h.getElement(options, 'elPreviewContainer', $cont.find('.file-preview'));
+ self.$preview = $h.getElement(options, 'elPreviewImage', $cont.find('.file-preview-thumbnails'));
+ self.$previewStatus = $h.getElement(options, 'elPreviewStatus', $cont.find('.file-preview-status'));
+ self.$errorContainer = $h.getElement(options, 'elErrorContainer', self.$previewContainer.find('.kv-fileinput-error'));
+ self._validateDisabled();
+ if (!$h.isEmpty(self.msgErrorClass)) {
+ $h.addCss(self.$errorContainer, self.msgErrorClass);
+ }
+ if (!refreshMode) {
+ self.$errorContainer.hide();
+ self.previewInitId = "preview-" + $h.uniqId();
+ self._initPreviewCache();
+ self._initPreview(true);
+ self._initPreviewActions();
+ self._setFileDropZoneTitle();
+ if (self.$parent.hasClass('file-loading')) {
+ self.$container.insertBefore(self.$parent);
+ self.$parent.remove();
+ }
+ }
+ if ($el.attr('disabled')) {
+ self.disable();
+ }
+ self._initZoom();
+ if (self.hideThumbnailContent) {
+ $h.addCss(self.$preview, 'hide-content');
+ }
+ },
+ _initTemplateDefaults: function () {
+ var self = this, tMain1, tMain2, tPreview, tFileIcon, tClose, tCaption, tBtnDefault, tBtnLink, tBtnBrowse,
+ tModalMain, tModal, tProgress, tSize, tFooter, tActions, tActionDelete, tActionUpload, tActionDownload,
+ tActionZoom, tActionDrag, tIndicator, tTagBef, tTagBef1, tTagBef2, tTagAft, tGeneric, tHtml, tImage,
+ tText, tOffice, tVideo, tAudio, tFlash, tObject, tPdf, tOther, tZoomCache, vDefaultDim;
+ tMain1 = '{preview}\n' +
+ '\n' +
+ '';
+ tMain2 = '{preview}\n\n\n{remove}\n{cancel}\n{upload}\n{browse}\n';
+ tPreview = '\n' +
+ ' {close}' +
+ '
\n' +
+ '
\n' +
+ '
\n' +
+ '
' +
+ '
\n' +
+ '
\n' +
+ '
\n' +
+ '
';
+ tClose = $h.closeButton('fileinput-remove');
+ tFileIcon = '';
+ tCaption = '\n' +
+ ' \n' +
+ ' \n' +
+ '
';
+ //noinspection HtmlUnknownAttribute
+ tBtnDefault = '';
+ //noinspection HtmlUnknownAttribute
+ tBtnLink = '{icon} {label}';
+ //noinspection HtmlUnknownAttribute
+ tBtnBrowse = '{icon} {label}
';
+ tModalMain = '';
+ tModal = '\n' +
+ '
\n' +
+ ' \n' +
+ '
\n' +
+ '
\n' +
+ '
\n' + '{prev} {next}\n' +
+ '
\n' +
+ '
\n' +
+ '
\n';
+ tProgress = '\n' +
+ '
\n' +
+ ' {status}\n' +
+ '
\n' +
+ '
';
+ tSize = ' ({sizeText})';
+ tFooter = '';
+ tActions = '\n' +
+ ' \n' +
+ '
\n' +
+ '{drag}\n' +
+ '';
+ //noinspection HtmlUnknownAttribute
+ tActionDelete = '\n';
+ tActionUpload = '';
+ tActionDownload = '{downloadIcon}';
+ tActionZoom = '';
+ tActionDrag = '{dragIcon}';
+ tIndicator = '{indicator}
';
+ tTagBef = '\n';
+ tTagBef2 = tTagBef + ' title="{caption}">
\n';
+ tTagAft = '
{footer}\n
\n';
+ tGeneric = '{content}\n';
+ tHtml = '
{data}
\n';
+ tImage = '

\n';
+ tText = '
\n';
+ tOffice = '
';
+ tVideo = '
\n';
+ tAudio = '
\n';
+ tFlash = '
-
+
+
+
+
+