Преглед на файлове

1、实现富文本编辑器
2、实现文档转换为PDF、MOBI、EPUB、Word格式
3、实现登录后跳转到来源地址

Minho преди 7 години
родител
ревизия
882d93e7b0
променени са 57 файла, в които са добавени 1563 реда и са изтрити 1466 реда
  1. 36 41
      commands/command.go
  2. 2 2
      commands/daemon/daemon.go
  3. 38 37
      commands/migrate/migrate.go
  4. 15 25
      commands/migrate/migrate_v03.go
  5. 23 14
      conf/enumerate.go
  6. 15 15
      conf/mail.go
  7. 32 17
      controllers/account.go
  8. 31 32
      controllers/base.go
  9. 194 196
      controllers/book.go
  10. 53 54
      controllers/book_member.go
  11. 5 5
      controllers/comment.go
  12. 29 60
      controllers/document.go
  13. 9 7
      controllers/home.go
  14. 12 15
      controllers/label.go
  15. 46 68
      controllers/manager.go
  16. 13 13
      controllers/search.go
  17. 51 54
      controllers/setting.go
  18. 70 26
      converter/converter.go
  19. 1 1
      graphics/file.go
  20. 1 1
      models/attachment.go
  21. 12 12
      models/attachment_result.go
  22. 0 2
      models/base.go
  23. 3 3
      models/blogs.go
  24. 16 14
      models/book.go
  25. 146 143
      models/book_result.go
  26. 35 67
      models/comment.go
  27. 5 7
      models/comment_result.go
  28. 17 16
      models/comment_vote.go
  29. 2 1
      models/convert_book_result.go
  30. 10 10
      models/dashboard.go
  31. 12 7
      models/document.go
  32. 22 20
      models/document_history.go
  33. 37 47
      models/document_tree.go
  34. 9 9
      models/errors.go
  35. 18 18
      models/label.go
  36. 20 32
      models/logs.go
  37. 29 45
      models/member.go
  38. 26 32
      models/member_result.go
  39. 25 25
      models/member_token.go
  40. 11 11
      models/migrations.go
  41. 40 42
      models/options.go
  42. 45 67
      models/relationship.go
  43. 17 15
      routers/filter.go
  44. 0 2
      static/js/markdown.js
  45. 70 0
      utils/cryptil/cryptil.go
  46. 8 22
      utils/file.go
  47. 43 0
      utils/filetil/filetil.go
  48. 9 9
      utils/gob.go
  49. 7 8
      utils/krand.go
  50. 17 28
      utils/ldap.go
  51. 56 52
      utils/pager.go
  52. 8 7
      utils/password.go
  53. 2 2
      utils/template_fun.go
  54. 6 6
      utils/url.go
  55. 101 0
      utils/ziptil/ziptil.go
  56. 2 2
      views/account/login.tpl
  57. 1 0
      views/document/default_read.tpl

+ 36 - 41
commands/command.go

@@ -6,11 +6,12 @@ import (
 	"net/url"
 	"os"
 	"time"
-
+	"log"
 	"flag"
 	"path/filepath"
 	"strings"
 
+	"encoding/json"
 	"github.com/astaxie/beego"
 	"github.com/astaxie/beego/logs"
 	"github.com/astaxie/beego/orm"
@@ -19,14 +20,6 @@ import (
 	"github.com/lifei6671/mindoc/conf"
 	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
-	"log"
-	"encoding/json"
-)
-
-var (
-	ConfigurationFile = "./conf/app.conf"
-	WorkingDirectory  = "./"
-	LogFile           = "./logs"
 )
 
 // RegisterDataBase 注册数据库
@@ -54,8 +47,8 @@ func RegisterDataBase() {
 		}
 	} else if adapter == "sqlite3" {
 		database := beego.AppConfig.String("db_database")
-		if strings.HasPrefix(database,"./") {
-			database = filepath.Join(WorkingDirectory,string(database[1:]))
+		if strings.HasPrefix(database, "./") {
+			database = filepath.Join(conf.WorkingDirectory, string(database[1:]))
 		}
 
 		dbPath := filepath.Dir(database)
@@ -99,11 +92,11 @@ func RegisterLogger(log string) {
 
 		if f, err := os.Create(logPath); err == nil {
 			f.Close()
-			config := make(map[string]interface{},1)
+			config := make(map[string]interface{}, 1)
 
 			config["filename"] = logPath
 
-			b,_ := json.Marshal(config)
+			b, _ := json.Marshal(config)
 
 			beego.SetLogger("file", string(b))
 		}
@@ -133,7 +126,7 @@ func RegisterFunction() {
 
 	beego.AddFuncMap("cdn", func(p string) string {
 		cdn := beego.AppConfig.DefaultString("cdn", "")
-		if strings.HasPrefix(p,"http://") || strings.HasPrefix(p,"https://") {
+		if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
 			return p
 		}
 		if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
@@ -147,7 +140,7 @@ func RegisterFunction() {
 
 	beego.AddFuncMap("cdnjs", func(p string) string {
 		cdn := beego.AppConfig.DefaultString("cdnjs", "")
-		if strings.HasPrefix(p,"http://") || strings.HasPrefix(p,"https://") {
+		if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
 			return p
 		}
 		if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
@@ -160,7 +153,7 @@ func RegisterFunction() {
 	})
 	beego.AddFuncMap("cdncss", func(p string) string {
 		cdn := beego.AppConfig.DefaultString("cdncss", "")
-		if strings.HasPrefix(p,"http://") || strings.HasPrefix(p,"https://") {
+		if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
 			return p
 		}
 		if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
@@ -172,7 +165,7 @@ func RegisterFunction() {
 		return cdn + p
 	})
 	beego.AddFuncMap("cdnimg", func(p string) string {
-		if strings.HasPrefix(p,"http://") || strings.HasPrefix(p,"https://") {
+		if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
 			return p
 		}
 		cdn := beego.AppConfig.DefaultString("cdnimg", "")
@@ -188,62 +181,64 @@ func RegisterFunction() {
 
 func ResolveCommand(args []string) {
 	flagSet := flag.NewFlagSet("MinDoc command: ", flag.ExitOnError)
-	flagSet.StringVar(&ConfigurationFile, "config", "", "MinDoc configuration file.")
-	flagSet.StringVar(&WorkingDirectory, "dir", "", "MinDoc working directory.")
-	flagSet.StringVar(&LogFile, "log", "", "MinDoc log file path.")
+	flagSet.StringVar(&conf.ConfigurationFile, "config", "", "MinDoc configuration file.")
+	flagSet.StringVar(&conf.WorkingDirectory, "dir", "", "MinDoc working directory.")
+	flagSet.StringVar(&conf.LogFile, "log", "", "MinDoc log file path.")
 
 	flagSet.Parse(args)
 
-
-	if WorkingDirectory == "" {
+	if conf.WorkingDirectory == "" {
 		if p, err := filepath.Abs(os.Args[0]); err == nil {
-			WorkingDirectory = filepath.Dir(p)
+			conf.WorkingDirectory = filepath.Dir(p)
 		}
 	}
-	if LogFile == "" {
-		LogFile = filepath.Join(WorkingDirectory,"logs")
+	if conf.LogFile == "" {
+		conf.LogFile = filepath.Join(conf.WorkingDirectory, "logs")
 	}
-	if ConfigurationFile == "" {
-		ConfigurationFile = filepath.Join(WorkingDirectory,"conf","app.conf")
-		config :=  filepath.Join(WorkingDirectory,"conf","app.conf.example")
-		if !utils.FileExists(ConfigurationFile) && utils.FileExists(config){
-			utils.CopyFile(ConfigurationFile,config)
+	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)
 		}
 	}
-	gocaptcha.ReadFonts(filepath.Join(WorkingDirectory,"static","fonts"), ".ttf")
+	gocaptcha.ReadFonts(filepath.Join(conf.WorkingDirectory, "static", "fonts"), ".ttf")
 
-	err := beego.LoadAppConfig("ini", ConfigurationFile)
+	err := beego.LoadAppConfig("ini", conf.ConfigurationFile)
 
 	if err != nil {
 		log.Println("An error occurred:", err)
 		os.Exit(1)
 	}
-	uploads := filepath.Join(WorkingDirectory, "uploads")
+	uploads := filepath.Join(conf.WorkingDirectory, "uploads")
 
-	os.MkdirAll(uploads,0666)
+	os.MkdirAll(uploads, 0666)
 
-	beego.BConfig.WebConfig.StaticDir["/static"] = filepath.Join(WorkingDirectory, "static")
+	beego.BConfig.WebConfig.StaticDir["/static"] = filepath.Join(conf.WorkingDirectory, "static")
 	beego.BConfig.WebConfig.StaticDir["/uploads"] = uploads
-	beego.BConfig.WebConfig.ViewsPath = filepath.Join(WorkingDirectory, "views")
+	beego.BConfig.WebConfig.ViewsPath = filepath.Join(conf.WorkingDirectory, "views")
 
-	fonts := filepath.Join(WorkingDirectory, "static", "fonts")
+	fonts := filepath.Join(conf.WorkingDirectory, "static", "fonts")
 
 	if !utils.FileExists(fonts) {
 		log.Fatal("Font path not exist.")
 	}
-	gocaptcha.ReadFonts(filepath.Join(WorkingDirectory, "static", "fonts"), ".ttf")
+	gocaptcha.ReadFonts(filepath.Join(conf.WorkingDirectory, "static", "fonts"), ".ttf")
 
 	RegisterDataBase()
 	RegisterModel()
-	RegisterLogger(LogFile)
+	RegisterLogger(conf.LogFile)
 }
 
 func init() {
 
+	if configPath ,err := filepath.Abs(conf.ConfigurationFile); err == nil {
+		conf.ConfigurationFile = configPath
+	}
 	gocaptcha.ReadFonts("./static/fonts", ".ttf")
 	gob.Register(models.Member{})
 
-	if p,err := filepath.Abs(os.Args[0]);err == nil{
-		WorkingDirectory = filepath.Dir(p)
+	if p, err := filepath.Abs(os.Args[0]); err == nil {
+		conf.WorkingDirectory = filepath.Dir(p)
 	}
 }

+ 2 - 2
commands/daemon/daemon.go

@@ -19,10 +19,10 @@ type Daemon struct {
 func NewDaemon() *Daemon {
 
 	config := &service.Config{
-		Name:             "mindocd",                         //服务显示名称
+		Name:             "mindocd",                               //服务显示名称
 		DisplayName:      "MinDoc service",                        //服务名称
 		Description:      "A document online management program.", //服务描述
-		WorkingDirectory: commands.WorkingDirectory,
+		WorkingDirectory: conf.WorkingDirectory,
 		Arguments:        os.Args[1:],
 	}
 

+ 38 - 37
commands/migrate/migrate.go

@@ -16,19 +16,18 @@ package migrate
 import (
 	"os"
 
-	"log"
-	"github.com/lifei6671/mindoc/models"
 	"container/list"
 	"fmt"
 	"github.com/astaxie/beego"
 	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/mindoc/models"
+	"log"
 )
 
 var (
-	migrationList = &migrationCache{ }
+	migrationList = &migrationCache{}
 )
 
-
 type MigrationDatabase interface {
 	//获取当前的版本
 	Version() int64
@@ -58,7 +57,7 @@ func RunMigration() {
 
 	if len(os.Args) >= 2 && os.Args[1] == "migrate" {
 
-		migrate,err := models.NewMigration().FindFirst()
+		migrate, err := models.NewMigration().FindFirst()
 
 		if err != nil {
 			//log.Fatalf("migrations table %s", err)
@@ -66,10 +65,10 @@ func RunMigration() {
 		}
 		fmt.Println("Start migration databae... ")
 
-		for el := migrationList.items.Front(); el != nil ; el = el.Next() {
+		for el := migrationList.items.Front(); el != nil; el = el.Next() {
 
 			//如果存在比当前版本大的版本,则依次升级
-			if item,ok := el.Value.(MigrationDatabase); ok && item.Version() > migrate.Version {
+			if item, ok := el.Value.(MigrationDatabase); ok && item.Version() > migrate.Version {
 				err := item.ValidUpdate(migrate.Version)
 				if err != nil {
 					log.Fatal(err)
@@ -112,51 +111,53 @@ func RunMigration() {
 }
 
 //导出数据库的表结构
-func ExportDatabaseTable()  ([]string,error) {
+func ExportDatabaseTable() ([]string, error) {
 	db_adapter := beego.AppConfig.String("db_adapter")
 	db_database := beego.AppConfig.String("db_database")
-	tables := make([]string,0)
+	tables := make([]string, 0)
 
 	o := orm.NewOrm()
 	switch db_adapter {
-	case "mysql":{
-		var lists []orm.Params
+	case "mysql":
+		{
+			var lists []orm.Params
 
-		_,err := o.Raw(fmt.Sprintf("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '%s'",db_database)).Values(&lists)
-		if err != nil {
-			return tables,err
+			_, err := o.Raw(fmt.Sprintf("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '%s'", db_database)).Values(&lists)
+			if err != nil {
+				return tables, err
+			}
+			for _, table := range lists {
+				var results []orm.Params
+
+				_, err = o.Raw(fmt.Sprintf("show create table %s", table["TABLE_NAME"])).Values(&results)
+				if err != nil {
+					return tables, err
+				}
+				tables = append(tables, results[0]["Create Table"].(string))
+			}
+			break
 		}
-		for _,table := range lists {
+	case "sqlite3":
+		{
 			var results []orm.Params
-
-			_,err = o.Raw(fmt.Sprintf("show create table %s",table["TABLE_NAME"])).Values(&results)
+			_, err := o.Raw("SELECT sql FROM sqlite_master WHERE sql IS NOT NULL ORDER BY rootpage ASC").Values(&results)
 			if err != nil {
-				return tables,err
+				return tables, err
 			}
-			tables = append(tables,results[0]["Create Table"].(string))
-		}
-		break;
-	}
-	case "sqlite3": {
-		var results []orm.Params
-		_,err := o.Raw("SELECT sql FROM sqlite_master WHERE sql IS NOT NULL ORDER BY rootpage ASC").Values(&results)
-		if err != nil {
-			return tables,err
-		}
-		for _,item := range results {
-			if sql,ok := item["sql"]; ok {
-				tables = append(tables,sql.(string))
+			for _, item := range results {
+				if sql, ok := item["sql"]; ok {
+					tables = append(tables, sql.(string))
+				}
 			}
+			break
 		}
-		break
-	}
 
 	}
-	return tables,nil
+	return tables, nil
 }
 
-func RegisterMigration()  {
+func RegisterMigration() {
 	migrationList.items = list.New()
 
-	 migrationList.items.PushBack(NewMigrationVersion03())
-}
+	migrationList.items.PushBack(NewMigrationVersion03())
+}

+ 15 - 25
commands/migrate/migrate_v03.go

@@ -2,21 +2,20 @@ package migrate
 
 import (
 	"errors"
+	"fmt"
 	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/models"
-	"time"
-	"fmt"
 	"strings"
+	"time"
 )
 
 type MigrationVersion03 struct {
 	isValid bool
-	tables []string
-
+	tables  []string
 }
 
 func NewMigrationVersion03() *MigrationVersion03 {
-	return &MigrationVersion03{ isValid: false, tables: make([]string,0)}
+	return &MigrationVersion03{isValid: false, tables: make([]string, 0)}
 }
 
 func (m *MigrationVersion03) Version() int64 {
@@ -37,8 +36,7 @@ func (m *MigrationVersion03) ValidForBackupTableSchema() error {
 		return errors.New("The current version failed to verify.")
 	}
 	var err error
-	m.tables,err = ExportDatabaseTable()
-
+	m.tables, err = ExportDatabaseTable()
 
 	return err
 }
@@ -72,11 +70,11 @@ func (m *MigrationVersion03) MigrationNewTableData() error {
 	}
 	o := orm.NewOrm()
 
-	_,err := o.Raw("UPDATE md_members SET auth_method = 'local'").Exec()
+	_, err := o.Raw("UPDATE md_members SET auth_method = 'local'").Exec()
 	if err != nil {
 		return err
 	}
-	_,err = o.Raw("INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用文档历史','ENABLE_DOCUMENT_HISTORY','true' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLE_DOCUMENT_HISTORY');").Exec()
+	_, err = o.Raw("INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用文档历史','ENABLE_DOCUMENT_HISTORY','true' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLE_DOCUMENT_HISTORY');").Exec()
 	if err != nil {
 		return err
 	}
@@ -85,24 +83,24 @@ func (m *MigrationVersion03) MigrationNewTableData() error {
 
 func (m *MigrationVersion03) AddMigrationRecord(version int64) error {
 	o := orm.NewOrm()
-	tables,err := ExportDatabaseTable()
+	tables, err := ExportDatabaseTable()
 
 	if err != nil {
-		return  err
+		return err
 	}
 	migration := models.NewMigration()
 	migration.Version = version
 	migration.Status = "update"
 	migration.CreateTime = time.Now()
-	migration.Name = fmt.Sprintf("update_%d",version)
-	migration.Statements = strings.Join(tables,"\r\n")
+	migration.Name = fmt.Sprintf("update_%d", version)
+	migration.Statements = strings.Join(tables, "\r\n")
 
 	_, err = o.Insert(migration)
 
 	return err
 }
 
-func (m *MigrationVersion03) MigrationCleanup() error  {
+func (m *MigrationVersion03) MigrationCleanup() error {
 
 	return nil
 }
@@ -112,16 +110,16 @@ func (m *MigrationVersion03) RollbackMigration() error {
 		return errors.New("The current version failed to verify.")
 	}
 	o := orm.NewOrm()
-	_,err := o.Raw("ALTER TABLE md_members DROP COLUMN auth_method").Exec()
+	_, err := o.Raw("ALTER TABLE md_members DROP COLUMN auth_method").Exec()
 	if err != nil {
 		return err
 	}
 
-	_,err = o.Raw("DROP TABLE md_document_history").Exec()
+	_, err = o.Raw("DROP TABLE md_document_history").Exec()
 	if err != nil {
 		return err
 	}
-	_,err = o.Raw("DELETE md_options WHERE option_name = 'ENABLE_DOCUMENT_HISTORY'").Exec()
+	_, err = o.Raw("DELETE md_options WHERE option_name = 'ENABLE_DOCUMENT_HISTORY'").Exec()
 
 	if err != nil {
 		return err
@@ -129,11 +127,3 @@ func (m *MigrationVersion03) RollbackMigration() error {
 
 	return nil
 }
-
-
-
-
-
-
-
-

+ 23 - 14
conf/enumerate.go

@@ -20,7 +20,7 @@ const RegexpEmail = "^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-
 const RegexpAccount = `^[a-zA-Z][a-zA-z0-9\.]{2,50}$`
 
 // PageSize 默认分页条数.
-const PageSize = 15
+const PageSize = 10
 
 // 用户权限
 const (
@@ -44,23 +44,31 @@ const (
 )
 
 const (
-	LoggerOperate = "operate"
-	LoggerSystem = "system"
+	LoggerOperate   = "operate"
+	LoggerSystem    = "system"
 	LoggerException = "exception"
-	LoggerDocument = "document"
+	LoggerDocument  = "document"
 )
 const (
 	//本地账户校验
 	AuthMethodLocal = "local"
 	//LDAP用户校验
-	AuthMethodLDAP	= "ldap"
+	AuthMethodLDAP = "ldap"
 )
+
 var (
 	VERSION    string
 	BUILD_TIME string
 	GO_VERSION string
 )
 
+var (
+	ConfigurationFile = "./conf/app.conf"
+	WorkingDirectory  = "./"
+	LogFile           = "./logs"
+	BaseUrl			  = ""
+)
+
 // app_key
 func GetAppKey() string {
 	return beego.AppConfig.DefaultString("app_key", "godoc")
@@ -102,29 +110,30 @@ func GetUploadFileExt() []string {
 	}
 	return exts
 }
+
 // 获取上传文件允许的最大值
 func GetUploadFileSize() int64 {
-	size := beego.AppConfig.DefaultString("upload_file_size","0")
+	size := beego.AppConfig.DefaultString("upload_file_size", "0")
 
-	if strings.HasSuffix(size,"MB") {
-		if s,e := strconv.ParseInt(size[0:len(size) - 2], 10, 64);e == nil {
+	if strings.HasSuffix(size, "MB") {
+		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
 			return s * 1024 * 1024
 		}
 	}
-	if strings.HasSuffix(size,"GB") {
-		if s,e := strconv.ParseInt(size[0:len(size) - 2], 10, 64);e == nil {
+	if strings.HasSuffix(size, "GB") {
+		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
 			return s * 1024 * 1024 * 1024
 		}
 	}
-	if strings.HasSuffix(size,"KB") {
-		if s,e := strconv.ParseInt(size[0:len(size) - 2], 10, 64);e == nil {
+	if strings.HasSuffix(size, "KB") {
+		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
 			return s * 1024
 		}
 	}
-	if s,e := strconv.ParseInt(size, 10, 64);e == nil {
+	if s, e := strconv.ParseInt(size, 10, 64); e == nil {
 		return s * 1024
 	}
-	return  0
+	return 0
 }
 
 //判断是否是允许商城的文件类型.

+ 15 - 15
conf/mail.go

@@ -6,33 +6,33 @@ import (
 )
 
 type SmtpConf struct {
-	EnableMail bool
-	MailNumber int
+	EnableMail   bool
+	MailNumber   int
 	SmtpUserName string
-	SmtpHost string
+	SmtpHost     string
 	SmtpPassword string
-	SmtpPort int
+	SmtpPort     int
 	FormUserName string
-	MailExpired int
+	MailExpired  int
 }
 
 func GetMailConfig() *SmtpConf {
 	user_name := beego.AppConfig.String("smtp_user_name")
 	password := beego.AppConfig.String("smtp_password")
 	smtp_host := beego.AppConfig.String("smtp_host")
-	smtp_port := beego.AppConfig.DefaultInt("smtp_port",25)
+	smtp_port := beego.AppConfig.DefaultInt("smtp_port", 25)
 	form_user_name := beego.AppConfig.String("form_user_name")
 	enable_mail := beego.AppConfig.String("enable_mail")
-	mail_number := beego.AppConfig.DefaultInt("mail_number",5)
+	mail_number := beego.AppConfig.DefaultInt("mail_number", 5)
 
 	c := &SmtpConf{
-		EnableMail : strings.EqualFold(enable_mail,"true"),
-		MailNumber: mail_number,
-		SmtpUserName:user_name,
-		SmtpHost:smtp_host,
-		SmtpPassword:password,
-		FormUserName:form_user_name,
-		SmtpPort:smtp_port,
+		EnableMail:   strings.EqualFold(enable_mail, "true"),
+		MailNumber:   mail_number,
+		SmtpUserName: user_name,
+		SmtpHost:     smtp_host,
+		SmtpPassword: password,
+		FormUserName: form_user_name,
+		SmtpPort:     smtp_port,
 	}
 	return c
-}
+}

+ 32 - 17
controllers/account.go

@@ -14,6 +14,7 @@ import (
 	"github.com/lifei6671/mindoc/conf"
 	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
+	"net/url"
 )
 
 // AccountController 用户登录与注册
@@ -32,14 +33,17 @@ func (c *AccountController) Login() {
 		Time     time.Time
 	}
 
-	// 显式指定的 URL 参数优先;为了统一处理,将之更新到 Session 中
-	turl := c.GetString("turl", "")
-	if turl != "" {
-		c.SetSession("turl", turl)
+	if member, ok := c.GetSession(conf.LoginSessionName).(models.Member); ok && member.MemberId > 0 {
+		u := c.GetString("url")
+		if u == "" {
+			u = c.Ctx.Request.Header.Get("Referer")
+		}
+		if u == "" {
+			u = beego.URLFor("HomeController.Index")
+		}
+		c.Redirect(u,302)
 	}
 
-	beego.Info("AccountController.Login(): turl is: " + turl)
-
 	// 如果 Cookie 中存在登录信息
 	if cookie, ok := c.GetSecureCookie(conf.GetAppKey(), "login"); ok {
 		if err := utils.Decode(cookie, &remember); err == nil {
@@ -81,26 +85,35 @@ func (c *AccountController) Login() {
 					c.SetSecureCookie(conf.GetAppKey(), "login", v)
 				}
 			}
+			u,_ := url.PathUnescape(c.GetString("url"))
+			if u == "" {
+				u = c.Ctx.Request.Header.Get("Referer")
+			}
+			if u == "" {
+				u = beego.URLFor("HomeController.Index")
+			}
 
-			data := c.LoggedIn(true)
-			c.JsonResult(0, "ok", data)
+			c.JsonResult(0, "ok", u)
 		} else {
 			logs.Error("用户登录 =>", err)
 			c.JsonResult(500, "账号或密码错误", nil)
 		}
+	}else{
+		u,_ := url.PathUnescape(c.GetString("url"))
+		if u == "" {
+			u = c.Ctx.Request.Header.Get("Referer")
+		}
+		if u == "" {
+			u = beego.URLFor("HomeController.Index")
+		}
+		c.Data["url"] = url.PathEscape(u)
 	}
 }
 
 // 登录成功后的操作,如重定向到原始请求页面
 func (c *AccountController) LoggedIn(isPost bool) interface{} {
-	turl := ""
-	value := c.GetSession("turl")
-	if value != nil {
-		turl = value.(string)
-	}
-	c.DelSession("turl")
 
-	beego.Info("AccountController.LoggedIn(): turl is: " + turl)
+	turl := c.GetString("url")
 
 	if !isPost {
 		// 检查是否存在 turl 参数,如果有则重定向至 turl 处,否则进入 Home 页面
@@ -111,7 +124,7 @@ func (c *AccountController) LoggedIn(isPost bool) interface{} {
 		return nil
 	} else {
 		var data struct {
-			TURL string `json:"turl"`
+			TURL string `json:"url"`
 		}
 		data.TURL = turl
 		return data
@@ -369,7 +382,9 @@ func (c *AccountController) Logout() {
 
 	c.SetSecureCookie(conf.GetAppKey(), "login", "", -3600)
 
-	c.Redirect(beego.URLFor("AccountController.Login"), 302)
+	u := c.Ctx.Request.Header.Get("Referer")
+
+	c.Redirect(beego.URLFor("AccountController.Login","url",u), 302)
 }
 
 // 验证码

+ 31 - 32
controllers/base.go

@@ -1,53 +1,52 @@
 package controllers
 
 import (
-
 	"bytes"
 
-	"github.com/lifei6671/mindoc/models"
-	"github.com/lifei6671/mindoc/conf"
-	"github.com/astaxie/beego"
-	"strings"
 	"encoding/json"
+	"github.com/astaxie/beego"
+	"github.com/lifei6671/mindoc/conf"
+	"github.com/lifei6671/mindoc/models"
 	"io"
+	"strings"
 )
 
-
 type BaseController struct {
 	beego.Controller
-	Member *models.Member
-	Option map[string]string
-	EnableAnonymous bool
+	Member                *models.Member
+	Option                map[string]string
+	EnableAnonymous       bool
 	EnableDocumentHistory bool
 }
 
 // Prepare 预处理.
-func (c *BaseController) Prepare (){
+func (c *BaseController) Prepare() {
 	c.Data["SiteName"] = "MinDoc"
 	c.Data["Member"] = models.Member{}
 	c.EnableAnonymous = 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.Data["Member"] = c.Member
-	}else{
+	} else {
 		//c.Member = models.NewMember()
 		//c.Member.Find(1)
 		//c.Data["Member"] = *c.Member
 	}
-	c.Data["BaseUrl"] = c.Ctx.Input.Scheme() + "://" + c.Ctx.Request.Host
+	conf.BaseUrl = c.BaseUrl()
+	c.Data["BaseUrl"] = c.BaseUrl()
 
-	if options,err := models.NewOption().All();err == nil {
-		c.Option = make(map[string]string,len(options))
-		for _,item := range options {
+	if options, err := models.NewOption().All(); err == nil {
+		c.Option = make(map[string]string, len(options))
+		for _, item := range options {
 			c.Data[item.OptionName] = item.OptionValue
 			c.Option[item.OptionName] = item.OptionValue
-			if strings.EqualFold(item.OptionName,"ENABLE_ANONYMOUS") && item.OptionValue == "true" {
+			if strings.EqualFold(item.OptionName, "ENABLE_ANONYMOUS") && item.OptionValue == "true" {
 				c.EnableAnonymous = true
 			}
-			if strings.EqualFold(item.OptionName,"ENABLE_DOCUMENT_HISTORY") && item.OptionValue == "true" {
+			if strings.EqualFold(item.OptionName, "ENABLE_DOCUMENT_HISTORY") && item.OptionValue == "true" {
 				c.EnableDocumentHistory = true
 			}
 		}
@@ -68,13 +67,13 @@ func (c *BaseController) SetMember(member models.Member) {
 }
 
 // JsonResult 响应 json 结果
-func (c *BaseController) JsonResult(errCode int,errMsg string,data ...interface{}){
-	jsonData := make(map[string]interface{},3)
+func (c *BaseController) JsonResult(errCode int, errMsg string, data ...interface{}) {
+	jsonData := make(map[string]interface{}, 3)
 
 	jsonData["errcode"] = errCode
 	jsonData["message"] = errMsg
 
-	if len(data) > 0 && data[0] != nil{
+	if len(data) > 0 && data[0] != nil {
 		jsonData["data"] = data[0]
 	}
 
@@ -86,13 +85,13 @@ func (c *BaseController) JsonResult(errCode int,errMsg string,data ...interface{
 
 	c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json; charset=utf-8")
 
-	io.WriteString(c.Ctx.ResponseWriter,string(returnJSON))
+	io.WriteString(c.Ctx.ResponseWriter, string(returnJSON))
 
 	c.StopRun()
 }
 
 // ExecuteViewPathTemplate 执行指定的模板并返回执行结果.
-func (c *BaseController) ExecuteViewPathTemplate(tplName string,data interface{}) (string,error){
+func (c *BaseController) ExecuteViewPathTemplate(tplName string, data interface{}) (string, error) {
 	var buf bytes.Buffer
 
 	viewPath := c.ViewPath
@@ -102,28 +101,28 @@ func (c *BaseController) ExecuteViewPathTemplate(tplName string,data interface{}
 
 	}
 
-	if err := beego.ExecuteViewPathTemplate(&buf,tplName,viewPath,data); err != nil {
-		return "",err
+	if err := beego.ExecuteViewPathTemplate(&buf, tplName, viewPath, data); err != nil {
+		return "", err
 	}
-	return buf.String(),nil
+	return buf.String(), nil
 }
 
 func (c *BaseController) BaseUrl() string {
-	baseUrl := beego.AppConfig.DefaultString("baseurl","")
+	baseUrl := beego.AppConfig.DefaultString("baseurl", "")
 	if baseUrl != "" {
-		if strings.HasSuffix(baseUrl,"/"){
-			baseUrl = strings.TrimSuffix(baseUrl,"/")
+		if strings.HasSuffix(baseUrl, "/") {
+			baseUrl = strings.TrimSuffix(baseUrl, "/")
 		}
-	}else{
+	} else {
 		baseUrl = c.Ctx.Input.Scheme() + "://" + c.Ctx.Request.Host
 	}
 	return baseUrl
 }
 
 //显示错误信息页面.
-func (c *BaseController) ShowErrorPage(errCode int,errMsg string)  {
+func (c *BaseController) ShowErrorPage(errCode int, errMsg string) {
 	c.TplName = "errors/error.tpl"
 	c.Data["ErrorMessage"] = errMsg
 	c.Data["ErrorCode"] = errCode
 	c.StopRun()
-}
+}

+ 194 - 196
controllers/book.go

@@ -1,25 +1,24 @@
 package controllers
 
 import (
-	"strings"
-	"regexp"
-	"strconv"
-	"time"
 	"encoding/json"
-	"html/template"
 	"errors"
 	"fmt"
-	"path/filepath"
+	"html/template"
 	"os"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
 
-	"github.com/lifei6671/mindoc/models"
-	"github.com/lifei6671/mindoc/utils"
 	"github.com/astaxie/beego"
-	"github.com/astaxie/beego/orm"
 	"github.com/astaxie/beego/logs"
+	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/conf"
 	"github.com/lifei6671/mindoc/graphics"
-	"github.com/lifei6671/mindoc/commands"
+	"github.com/lifei6671/mindoc/models"
+	"github.com/lifei6671/mindoc/utils"
 )
 
 type BookController struct {
@@ -32,10 +31,10 @@ func (c *BookController) Index() {
 
 	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)
 
 	if err != nil {
-		logs.Error("BookController.Index => ",err)
+		logs.Error("BookController.Index => ", err)
 		c.Abort("500")
 	}
 
@@ -43,14 +42,14 @@ func (c *BookController) Index() {
 		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount)
 
 		c.Data["PageHtml"] = html
-	}else {
+	} else {
 		c.Data["PageHtml"] = ""
 	}
-	b,err := json.Marshal(books)
+	b, err := json.Marshal(books)
 
-	if err != nil || len(books) <= 0{
+	if err != nil || len(books) <= 0 {
 		c.Data["Result"] = template.JS("[]")
-	}else{
+	} else {
 		c.Data["Result"] = template.JS(string(b))
 	}
 }
@@ -62,11 +61,11 @@ func (c *BookController) Dashboard() {
 
 	key := c.Ctx.Input.Param(":key")
 
-	if key == ""{
+	if key == "" {
 		c.Abort("404")
 	}
 
-	book,err := models.NewBookResult().FindByIdentify(key,c.Member.MemberId)
+	book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
 	if err != nil {
 		if err == models.ErrPermissionDenied {
 			c.Abort("403")
@@ -79,17 +78,17 @@ func (c *BookController) Dashboard() {
 }
 
 // Setting 项目设置 .
-func (c *BookController) Setting()  {
+func (c *BookController) Setting() {
 	c.Prepare()
 	c.TplName = "book/setting.tpl"
 
 	key := c.Ctx.Input.Param(":key")
 
-	if key == ""{
+	if key == "" {
 		c.Abort("404")
 	}
 
-	book,err := models.NewBookResult().FindByIdentify(key,c.Member.MemberId)
+	book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
 	if err != nil {
 		if err == orm.ErrNoRows {
 			c.Abort("404")
@@ -104,225 +103,224 @@ func (c *BookController) Setting()  {
 		c.Abort("403")
 	}
 	if book.PrivateToken != "" {
-		book.PrivateToken = c.BaseUrl() + beego.URLFor("DocumentController.Index",":key",book.Identify,"token",book.PrivateToken)
+		book.PrivateToken = c.BaseUrl() + beego.URLFor("DocumentController.Index", ":key", book.Identify, "token", book.PrivateToken)
 	}
 	c.Data["Model"] = book
 
 }
 
 //保存项目信息
-func (c *BookController) SaveBook()  {
-	bookResult,err := c.IsPermission()
+func (c *BookController) SaveBook() {
+	bookResult, err := c.IsPermission()
 
 	if err != nil {
-		c.JsonResult(6001,err.Error())
+		c.JsonResult(6001, err.Error())
 	}
-	book,err := models.NewBook().Find(bookResult.BookId)
+	book, err := models.NewBook().Find(bookResult.BookId)
 
 	if err != nil {
-		logs.Error("SaveBook => ",err)
-		c.JsonResult(6002,err.Error())
+		logs.Error("SaveBook => ", err)
+		c.JsonResult(6002, err.Error())
 	}
 
 	book_name := strings.TrimSpace(c.GetString("book_name"))
-	description := strings.TrimSpace(c.GetString("description",""))
+	description := strings.TrimSpace(c.GetString("description", ""))
 	comment_status := c.GetString("comment_status")
 	tag := strings.TrimSpace(c.GetString("label"))
 	editor := strings.TrimSpace(c.GetString("editor"))
 	auto_release := strings.TrimSpace(c.GetString("auto_release")) == "on"
 
-	if strings.Count(description,"") > 500 {
-		c.JsonResult(6004,"项目描述不能大于500字")
+	if strings.Count(description, "") > 500 {
+		c.JsonResult(6004, "项目描述不能大于500字")
 	}
 	if comment_status != "open" && comment_status != "closed" && comment_status != "group_only" && comment_status != "registered_only" {
 		comment_status = "closed"
 	}
-	if tag != ""{
-		tags := strings.Split(tag,",")
+	if tag != "" {
+		tags := strings.Split(tag, ",")
 		if len(tags) > 10 {
-			c.JsonResult(6005,"最多允许添加10个标签")
+			c.JsonResult(6005, "最多允许添加10个标签")
 		}
 	}
 	if editor != "markdown" && editor != "html" {
 		editor = "markdown"
 	}
 
-	book.BookName 		= book_name
-	book.Description 	= description
-	book.CommentStatus 	= comment_status
-	book.Label 			= tag
-	book.Editor 		= editor
+	book.BookName = book_name
+	book.Description = description
+	book.CommentStatus = comment_status
+	book.Label = tag
+	book.Editor = editor
 	if auto_release {
 		book.AutoRelease = 1
-	}else{
+	} else {
 		book.AutoRelease = 0
 	}
 
-
-	if err := book.Update();err != nil {
-		c.JsonResult(6006,"保存失败")
+	if err := book.Update(); err != nil {
+		c.JsonResult(6006, "保存失败")
 	}
 	bookResult.BookName = book_name
 	bookResult.Description = description
 	bookResult.CommentStatus = comment_status
 	bookResult.Label = tag
-	c.JsonResult(0,"ok",bookResult)
+	c.JsonResult(0, "ok", bookResult)
 }
 
 //设置项目私有状态.
-func (c *BookController) PrivatelyOwned()  {
+func (c *BookController) PrivatelyOwned() {
 
 	status := c.GetString("status")
 
 	if status != "open" && status != "close" {
-		c.JsonResult(6003,"参数错误")
+		c.JsonResult(6003, "参数错误")
 	}
 	state := 0
 	if status == "open" {
 		state = 0
-	}else{
+	} else {
 		state = 1
 	}
 
-	bookResult,err := c.IsPermission()
+	bookResult, err := c.IsPermission()
 
 	if err != nil {
-		c.JsonResult(6001,err.Error())
+		c.JsonResult(6001, err.Error())
 	}
 	//只有创始人才能变更私有状态
 	if bookResult.RoleId != conf.BookFounder {
-		c.JsonResult(6002,"权限不足")
+		c.JsonResult(6002, "权限不足")
 	}
 
-	book,err := models.NewBook().Find(bookResult.BookId)
+	book, err := models.NewBook().Find(bookResult.BookId)
 
 	if err != nil {
-		c.JsonResult(6005,"项目不存在")
+		c.JsonResult(6005, "项目不存在")
 	}
 	book.PrivatelyOwned = state
 
 	err = book.Update()
 
 	if err != nil {
-		logs.Error("PrivatelyOwned => ",err)
-		c.JsonResult(6004,"保存失败")
+		logs.Error("PrivatelyOwned => ", err)
+		c.JsonResult(6004, "保存失败")
 	}
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
 
 // Transfer 转让项目.
-func (c *BookController) Transfer()  {
+func (c *BookController) Transfer() {
 	c.Prepare()
 	account := c.GetString("account")
 
 	if account == "" {
-		c.JsonResult(6004,"接受者账号不能为空")
+		c.JsonResult(6004, "接受者账号不能为空")
 	}
-	member,err := models.NewMember().FindByAccount(account)
+	member, err := models.NewMember().FindByAccount(account)
 
 	if err != nil {
-		logs.Error("FindByAccount => ",err)
-		c.JsonResult(6005,"接受用户不存在")
+		logs.Error("FindByAccount => ", err)
+		c.JsonResult(6005, "接受用户不存在")
 	}
 	if member.Status != 0 {
-		c.JsonResult(6006,"接受用户已被禁用")
+		c.JsonResult(6006, "接受用户已被禁用")
 	}
 	if member.MemberId == c.Member.MemberId {
-		c.JsonResult(6007,"不能转让给自己")
+		c.JsonResult(6007, "不能转让给自己")
 	}
-	bookResult,err := c.IsPermission()
+	bookResult, err := c.IsPermission()
 
 	if err != nil {
-		c.JsonResult(6001,err.Error())
+		c.JsonResult(6001, err.Error())
 	}
 
-	err = models.NewRelationship().Transfer(bookResult.BookId,c.Member.MemberId,member.MemberId)
+	err = models.NewRelationship().Transfer(bookResult.BookId, c.Member.MemberId, member.MemberId)
 
 	if err != nil {
-		logs.Error("Transfer => ",err)
-		c.JsonResult(6008,err.Error())
+		logs.Error("Transfer => ", err)
+		c.JsonResult(6008, err.Error())
 	}
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
+
 //上传项目封面.
-func (c *BookController) UploadCover()  {
+func (c *BookController) UploadCover() {
 
-	bookResult,err := c.IsPermission()
+	bookResult, err := c.IsPermission()
 
 	if err != nil {
-		c.JsonResult(6001,err.Error())
+		c.JsonResult(6001, err.Error())
 	}
-	book,err := models.NewBook().Find(bookResult.BookId)
+	book, err := models.NewBook().Find(bookResult.BookId)
 
 	if err != nil {
-		logs.Error("SaveBook => ",err)
-		c.JsonResult(6002,err.Error())
+		logs.Error("SaveBook => ", err)
+		c.JsonResult(6002, err.Error())
 	}
 
-	file,moreFile,err := c.GetFile("image-file")
+	file, moreFile, err := c.GetFile("image-file")
 	defer file.Close()
 
 	if err != nil {
-		logs.Error("",err.Error())
-		c.JsonResult(500,"读取文件异常")
+		logs.Error("", err.Error())
+		c.JsonResult(500, "读取文件异常")
 	}
 
 	ext := filepath.Ext(moreFile.Filename)
 
-	if !strings.EqualFold(ext,".png") && !strings.EqualFold(ext,".jpg") && !strings.EqualFold(ext,".gif") && !strings.EqualFold(ext,".jpeg")  {
-		c.JsonResult(500,"不支持的图片格式")
+	if !strings.EqualFold(ext, ".png") && !strings.EqualFold(ext, ".jpg") && !strings.EqualFold(ext, ".gif") && !strings.EqualFold(ext, ".jpeg") {
+		c.JsonResult(500, "不支持的图片格式")
 	}
 
-
-	x1 ,_ := strconv.ParseFloat(c.GetString("x"),10)
-	y1 ,_ := strconv.ParseFloat(c.GetString("y"),10)
-	w1 ,_ := strconv.ParseFloat(c.GetString("width"),10)
-	h1 ,_ := strconv.ParseFloat(c.GetString("height"),10)
+	x1, _ := strconv.ParseFloat(c.GetString("x"), 10)
+	y1, _ := strconv.ParseFloat(c.GetString("y"), 10)
+	w1, _ := strconv.ParseFloat(c.GetString("width"), 10)
+	h1, _ := strconv.ParseFloat(c.GetString("height"), 10)
 
 	x := int(x1)
 	y := int(y1)
 	width := int(w1)
 	height := int(h1)
 
-	fileName := "cover_" +  strconv.FormatInt(time.Now().UnixNano(), 16)
+	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)
 
 	os.MkdirAll(path, os.ModePerm)
 
-	err = c.SaveToFile("image-file",filePath)
+	err = c.SaveToFile("image-file", filePath)
 
 	if err != nil {
-		logs.Error("",err)
-		c.JsonResult(500,"图片保存失败")
+		logs.Error("", err)
+		c.JsonResult(500, "图片保存失败")
 	}
 	defer func(filePath string) {
 		os.Remove(filePath)
 	}(filePath)
 
 	//剪切图片
-	subImg,err := graphics.ImageCopyFromFile(filePath,x,y,width,height)
+	subImg, err := graphics.ImageCopyFromFile(filePath, x, y, width, height)
 
-	if err != nil{
-		logs.Error("graphics.ImageCopyFromFile => ",err)
-		c.JsonResult(500,"图片剪切")
+	if err != nil {
+		logs.Error("graphics.ImageCopyFromFile => ", err)
+		c.JsonResult(500, "图片剪切")
 	}
 
-	filePath = filepath.Join(commands.WorkingDirectory,"uploads",time.Now().Format("200601"),fileName + "_small" + ext)
+	filePath = filepath.Join(conf.WorkingDirectory, "uploads", time.Now().Format("200601"), fileName+"_small"+ext)
 
 	//生成缩略图并保存到磁盘
-	err = graphics.ImageResizeSaveFile(subImg,175,230,filePath)
+	err = graphics.ImageResizeSaveFile(subImg, 175, 230, filePath)
 
 	if err != nil {
-		logs.Error("ImageResizeSaveFile => ",err.Error())
-		c.JsonResult(500,"保存图片失败")
+		logs.Error("ImageResizeSaveFile => ", err.Error())
+		c.JsonResult(500, "保存图片失败")
 	}
 
-	url := "/" +  strings.Replace(strings.TrimPrefix(filePath,commands.WorkingDirectory),"\\","/",-1)
+	url := "/" + strings.Replace(strings.TrimPrefix(filePath, conf.WorkingDirectory), "\\", "/", -1)
 
-	if strings.HasPrefix(url,"//") {
+	if strings.HasPrefix(url, "//") {
 		url = string(url[1:])
 	}
 
@@ -330,15 +328,15 @@ func (c *BookController) UploadCover()  {
 
 	book.Cover = url
 
-	if err := book.Update() ; err != nil {
-		c.JsonResult(6001,"保存图片失败")
+	if err := book.Update(); err != nil {
+		c.JsonResult(6001, "保存图片失败")
 	}
 	//如果原封面不是默认封面则删除
 	if old_cover != conf.GetDefaultCover() {
 		os.Remove("." + old_cover)
 	}
 
-	c.JsonResult(0,"ok",url)
+	c.JsonResult(0, "ok", url)
 }
 
 // Users 用户列表.
@@ -347,13 +345,13 @@ func (c *BookController) Users() {
 	c.TplName = "book/users.tpl"
 
 	key := c.Ctx.Input.Param(":key")
-	pageIndex,_ := c.GetInt("page",1)
+	pageIndex, _ := c.GetInt("page", 1)
 
-	if key == ""{
+	if key == "" {
 		c.Abort("404")
 	}
 
-	book,err := models.NewBookResult().FindByIdentify(key,c.Member.MemberId)
+	book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
 	if err != nil {
 		if err == models.ErrPermissionDenied {
 			c.Abort("403")
@@ -363,20 +361,20 @@ func (c *BookController) Users() {
 
 	c.Data["Model"] = *book
 
-	members,totalCount,err := models.NewMemberRelationshipResult().FindForUsersByBookId(book.BookId,pageIndex,15)
+	members, totalCount, err := models.NewMemberRelationshipResult().FindForUsersByBookId(book.BookId, pageIndex, 15)
 
 	if totalCount > 0 {
 		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, 10, totalCount)
 
 		c.Data["PageHtml"] = html
-	}else{
+	} else {
 		c.Data["PageHtml"] = ""
 	}
-	b,err := json.Marshal(members)
+	b, err := json.Marshal(members)
 
 	if err != nil {
 		c.Data["Result"] = template.JS("[]")
-	}else{
+	} else {
 		c.Data["Result"] = template.JS(string(b))
 	}
 }
@@ -385,28 +383,28 @@ func (c *BookController) Users() {
 func (c *BookController) Create() {
 
 	if c.Ctx.Input.IsPost() {
-		book_name := strings.TrimSpace(c.GetString("book_name",""))
-		identify := strings.TrimSpace(c.GetString("identify",""))
-		description := strings.TrimSpace(c.GetString("description",""))
-		privately_owned,_ := strconv.Atoi(c.GetString("privately_owned"))
+		book_name := strings.TrimSpace(c.GetString("book_name", ""))
+		identify := strings.TrimSpace(c.GetString("identify", ""))
+		description := strings.TrimSpace(c.GetString("description", ""))
+		privately_owned, _ := strconv.Atoi(c.GetString("privately_owned"))
 		comment_status := c.GetString("comment_status")
 
 		if book_name == "" {
-			c.JsonResult(6001,"项目名称不能为空")
+			c.JsonResult(6001, "项目名称不能为空")
 		}
 		if identify == "" {
-			c.JsonResult(6002,"项目标识不能为空")
+			c.JsonResult(6002, "项目标识不能为空")
 		}
-		if ok,err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`,identify); !ok || err != nil {
-			c.JsonResult(6003,"项目标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母开头")
+		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字")
+		if strings.Count(identify, "") > 50 {
+			c.JsonResult(6004, "文档标识不能超过50字")
 		}
-		if strings.Count(description,"") > 500 {
-			c.JsonResult(6004,"项目描述不能大于500字")
+		if strings.Count(description, "") > 500 {
+			c.JsonResult(6004, "项目描述不能大于500字")
 		}
-		if privately_owned !=0 && privately_owned != 1 {
+		if privately_owned != 0 && privately_owned != 1 {
 			privately_owned = 1
 		}
 		if comment_status != "open" && comment_status != "closed" && comment_status != "group_only" && comment_status != "registered_only" {
@@ -415,39 +413,39 @@ func (c *BookController) Create() {
 
 		book := models.NewBook()
 
-		if books,_ := book.FindByField("identify",identify); len(books) > 0 {
-			c.JsonResult(6006,"项目标识已存在")
+		if books, _ := book.FindByField("identify", identify); len(books) > 0 {
+			c.JsonResult(6006, "项目标识已存在")
 		}
 
-		book.BookName 	= book_name
+		book.BookName = book_name
 		book.Description = description
 		book.CommentCount = 0
 		book.PrivatelyOwned = privately_owned
 		book.CommentStatus = comment_status
-		book.Identify 	= identify
-		book.DocCount 	= 0
-		book.MemberId 	= c.Member.MemberId
+		book.Identify = identify
+		book.DocCount = 0
+		book.MemberId = c.Member.MemberId
 		book.CommentCount = 0
-		book.Version 	= time.Now().Unix()
-		book.Cover 	= conf.GetDefaultCover()
-		book.Editor 	= "markdown"
-		book.Theme	= "default"
+		book.Version = time.Now().Unix()
+		book.Cover = conf.GetDefaultCover()
+		book.Editor = "markdown"
+		book.Theme = "default"
 
 		err := book.Insert()
 
 		if err != nil {
-			logs.Error("Insert => ",err)
-			c.JsonResult(6005,"保存项目失败")
+			logs.Error("Insert => ", err)
+			c.JsonResult(6005, "保存项目失败")
 		}
-		bookResult,err := models.NewBookResult().FindByIdentify(book.Identify,c.Member.MemberId)
+		bookResult, err := models.NewBookResult().FindByIdentify(book.Identify, c.Member.MemberId)
 
 		if err != nil {
 			beego.Error(err)
 		}
 
-		c.JsonResult(0,"ok",bookResult)
+		c.JsonResult(0, "ok", bookResult)
 	}
-	c.JsonResult(6001,"error")
+	c.JsonResult(6001, "error")
 }
 
 // CreateToken 创建访问来令牌.
@@ -455,22 +453,22 @@ func (c *BookController) CreateToken() {
 
 	action := c.GetString("action")
 
-	bookResult ,err := c.IsPermission()
+	bookResult, err := c.IsPermission()
 
 	if err != nil {
 		if err == models.ErrPermissionDenied {
-			c.JsonResult(403,"权限不足")
+			c.JsonResult(403, "权限不足")
 		}
 		if err == orm.ErrNoRows {
-			c.JsonResult(404,"项目不存在")
+			c.JsonResult(404, "项目不存在")
 		}
-		logs.Error("生成阅读令牌失败 =>",err)
-		c.JsonResult(6002,err.Error())
+		logs.Error("生成阅读令牌失败 =>", err)
+		c.JsonResult(6002, err.Error())
 	}
 	book := models.NewBook()
 
-	if _,err := book.Find(bookResult.BookId);err != nil {
-		c.JsonResult(6001,"项目不存在")
+	if _, err := book.Find(bookResult.BookId); err != nil {
+		c.JsonResult(6001, "项目不存在")
 	}
 	if action == "create" {
 		if bookResult.PrivatelyOwned == 0 {
@@ -482,14 +480,14 @@ func (c *BookController) CreateToken() {
 			logs.Error("生成阅读令牌失败 => ", err)
 			c.JsonResult(6003, "生成阅读令牌失败")
 		}
-		c.JsonResult(0, "ok", c.BaseUrl() +  beego.URLFor("DocumentController.Index",":key",book.Identify,"token",book.PrivateToken))
-	}else{
+		c.JsonResult(0, "ok", c.BaseUrl()+beego.URLFor("DocumentController.Index", ":key", book.Identify, "token", book.PrivateToken))
+	} else {
 		book.PrivateToken = ""
-		if err := book.Update();err != nil {
-			logs.Error("CreateToken => ",err)
-			c.JsonResult(6004,"删除令牌失败")
+		if err := book.Update(); err != nil {
+			logs.Error("CreateToken => ", err)
+			c.JsonResult(6004, "删除令牌失败")
 		}
-		c.JsonResult(0,"ok","")
+		c.JsonResult(0, "ok", "")
 	}
 }
 
@@ -497,25 +495,25 @@ func (c *BookController) CreateToken() {
 func (c *BookController) Delete() {
 	c.Prepare()
 
-	bookResult ,err := c.IsPermission()
+	bookResult, err := c.IsPermission()
 
 	if err != nil {
-		c.JsonResult(6001,err.Error())
+		c.JsonResult(6001, err.Error())
 	}
 
 	if bookResult.RoleId != conf.BookFounder {
-		c.JsonResult(6002,"只有创始人才能删除项目")
+		c.JsonResult(6002, "只有创始人才能删除项目")
 	}
 	err = models.NewBook().ThoroughDeleteBook(bookResult.BookId)
 
 	if err == orm.ErrNoRows {
-		c.JsonResult(6002,"项目不存在")
+		c.JsonResult(6002, "项目不存在")
 	}
 	if err != nil {
-		logs.Error("删除项目 => ",err)
-		c.JsonResult(6003,"删除失败")
+		logs.Error("删除项目 => ", err)
+		c.JsonResult(6003, "删除失败")
 	}
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
 
 //发布项目.
@@ -524,15 +522,15 @@ func (c *BookController) Release() {
 
 	identify := c.GetString("identify")
 
-	book_id := 0
+	bookId := 0
 
 	if c.Member.IsAdministrator() {
-		book,err := models.NewBook().FindByFieldFirst("identify",identify)
+		book, err := models.NewBook().FindByFieldFirst("identify", identify)
 		if err != nil {
 
 		}
-		book_id = book.BookId
-	}else {
+		bookId = book.BookId
+	} else {
 		book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
 
 		if err != nil {
@@ -548,15 +546,18 @@ func (c *BookController) Release() {
 		if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder && book.RoleId != conf.BookEditor {
 			c.JsonResult(6003, "权限不足")
 		}
-		book_id = book.BookId
+		bookId = book.BookId
 	}
 	go func(identify string) {
-		models.NewDocument().ReleaseContent(book_id)
+		models.NewDocument().ReleaseContent(bookId)
 
+		//当文档发布后,需要删除已缓存的转换项目
+		outputPath := filepath.Join(beego.AppConfig.DefaultString("book_output_path", "cache"), strconv.Itoa(bookId))
+		os.RemoveAll(outputPath)
 
 	}(identify)
 
-	c.JsonResult(0,"发布任务已推送到任务队列,稍后将在后台执行。")
+	c.JsonResult(0, "发布任务已推送到任务队列,稍后将在后台执行。")
 }
 
 //文档排序.
@@ -570,94 +571,91 @@ func (c *BookController) SaveSort() {
 
 	book_id := 0
 	if c.Member.IsAdministrator() {
-		book,err := models.NewBook().FindByFieldFirst("identify",identify)
+		book, err := models.NewBook().FindByFieldFirst("identify", identify)
 		if err != nil {
 
 		}
 		book_id = book.BookId
-	}else{
-		bookResult,err := models.NewBookResult().FindByIdentify(identify,c.Member.MemberId)
+	} else {
+		bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
 		if err != nil {
-			beego.Error("DocumentController.Edit => ",err)
+			beego.Error("DocumentController.Edit => ", err)
 
 			c.Abort("403")
 		}
 		if bookResult.RoleId == conf.BookObserver {
-			c.JsonResult(6002,"项目不存在或权限不足")
+			c.JsonResult(6002, "项目不存在或权限不足")
 		}
 		book_id = bookResult.BookId
 	}
 
-
 	content := c.Ctx.Input.RequestBody
 
 	var docs []map[string]interface{}
 
-	err := json.Unmarshal(content,&docs)
+	err := json.Unmarshal(content, &docs)
 
 	if err != nil {
 		beego.Error(err)
-		c.JsonResult(6003,"数据错误")
+		c.JsonResult(6003, "数据错误")
 	}
 
-	for _,item := range docs {
-		if doc_id,ok := item["id"].(float64);ok {
-			doc,err := models.NewDocument().Find(int(doc_id));
+	for _, item := range docs {
+		if doc_id, ok := item["id"].(float64); ok {
+			doc, err := models.NewDocument().Find(int(doc_id))
 			if err != nil {
 				beego.Error(err)
-				continue;
+				continue
 			}
 			if doc.BookId != book_id {
-				logs.Info("%s","权限错误")
-				continue;
+				logs.Info("%s", "权限错误")
+				continue
 			}
-			sort,ok := item["sort"].(float64);
+			sort, ok := item["sort"].(float64)
 			if !ok {
-				beego.Info("排序数字转换失败 => ",item)
+				beego.Info("排序数字转换失败 => ", item)
 				continue
 			}
-			parent_id,ok := item["parent"].(float64)
+			parent_id, ok := item["parent"].(float64)
 			if !ok {
-				beego.Info("父分类转换失败 => ",item)
+				beego.Info("父分类转换失败 => ", item)
 				continue
 			}
 			if parent_id > 0 {
-				if parent,err := models.NewDocument().Find(int(parent_id)); err != nil || parent.BookId != book_id {
+				if parent, err := models.NewDocument().Find(int(parent_id)); err != nil || parent.BookId != book_id {
 					continue
 				}
 			}
 			doc.OrderSort = int(sort)
 			doc.ParentId = int(parent_id)
 			if err := doc.InsertOrUpdate(); err != nil {
-				fmt.Printf("%s",err.Error())
+				fmt.Printf("%s", err.Error())
 				beego.Error(err)
 			}
-		}else{
-			fmt.Printf("文档ID转换失败 => %+v",item)
+		} else {
+			fmt.Printf("文档ID转换失败 => %+v", item)
 		}
 
 	}
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
 
-
-func (c *BookController) IsPermission() (*models.BookResult,error) {
+func (c *BookController) IsPermission() (*models.BookResult, error) {
 	identify := c.GetString("identify")
 
-	book ,err := models.NewBookResult().FindByIdentify(identify,c.Member.MemberId)
+	book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
 
 	if err != nil {
 		if err == models.ErrPermissionDenied {
-			return book,errors.New("权限不足")
+			return book, errors.New("权限不足")
 		}
 		if err == orm.ErrNoRows {
-			return book,errors.New("项目不存在")
+			return book, errors.New("项目不存在")
 		}
-		return book,err
+		return book, err
 	}
 	if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder {
-		return book,errors.New("权限不足")
+		return book, errors.New("权限不足")
 	}
-	return book,nil
+	return book, nil
 }
-

+ 53 - 54
controllers/book_member.go

@@ -3,10 +3,10 @@ package controllers
 import (
 	"errors"
 
-	"github.com/lifei6671/mindoc/models"
-	"github.com/astaxie/beego/orm"
 	"github.com/astaxie/beego/logs"
+	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/conf"
+	"github.com/lifei6671/mindoc/models"
 )
 
 type BookMemberController struct {
@@ -14,31 +14,31 @@ type BookMemberController struct {
 }
 
 // AddMember 参加参与用户.
-func (c *BookMemberController) AddMember()  {
+func (c *BookMemberController) AddMember() {
 	identify := c.GetString("identify")
 	account := c.GetString("account")
-	role_id,_ := c.GetInt("role_id",3)
+	role_id, _ := c.GetInt("role_id", 3)
 
-	if identify == "" || account == ""{
-		c.JsonResult(6001,"参数错误")
+	if identify == "" || account == "" {
+		c.JsonResult(6001, "参数错误")
 	}
-	book ,err := c.IsPermission()
+	book, err := c.IsPermission()
 
 	if err != nil {
-		c.JsonResult(6001,err.Error())
+		c.JsonResult(6001, err.Error())
 	}
 
 	member := models.NewMember()
 
-	if _,err := member.FindByAccount(account) ; err != nil {
-		c.JsonResult(404,"用户不存在")
+	if _, err := member.FindByAccount(account); err != nil {
+		c.JsonResult(404, "用户不存在")
 	}
 	if member.Status == 1 {
-		c.JsonResult(6003,"用户已被禁用")
+		c.JsonResult(6003, "用户已被禁用")
 	}
 
-	if _,err := models.NewRelationship().FindForRoleId(book.BookId,member.MemberId);err == nil {
-		c.JsonResult(6003,"用户已存在该项目中")
+	if _, err := models.NewRelationship().FindForRoleId(book.BookId, member.MemberId); err == nil {
+		c.JsonResult(6003, "用户已存在该项目中")
 	}
 
 	relationship := models.NewRelationship()
@@ -53,53 +53,52 @@ func (c *BookMemberController) AddMember()  {
 		memberRelationshipResult.BookId = book.BookId
 		memberRelationshipResult.ResolveRoleName()
 
-
-		c.JsonResult(0,"ok",memberRelationshipResult)
+		c.JsonResult(0, "ok", memberRelationshipResult)
 	}
-	c.JsonResult(500,err.Error())
+	c.JsonResult(500, err.Error())
 }
 
 // 变更指定用户在指定项目中的权限
 func (c *BookMemberController) ChangeRole() {
 	identify := c.GetString("identify")
-	member_id,_ := c.GetInt("member_id",0)
-	role,_ := c.GetInt("role_id",0)
+	member_id, _ := c.GetInt("member_id", 0)
+	role, _ := c.GetInt("role_id", 0)
 
-	if identify == "" || member_id <=0 {
-		c.JsonResult(6001,"参数错误")
+	if identify == "" || member_id <= 0 {
+		c.JsonResult(6001, "参数错误")
 	}
 	if member_id == c.Member.MemberId {
-		c.JsonResult(6006,"不能变更自己的权限")
+		c.JsonResult(6006, "不能变更自己的权限")
 	}
-	book ,err := models.NewBookResult().FindByIdentify(identify,c.Member.MemberId)
+	book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
 
 	if err != nil {
 		if err == models.ErrPermissionDenied {
-			c.JsonResult(403,"权限不足")
+			c.JsonResult(403, "权限不足")
 		}
 		if err == orm.ErrNoRows {
-			c.JsonResult(404,"项目不存在")
+			c.JsonResult(404, "项目不存在")
 		}
-		c.JsonResult(6002,err.Error())
+		c.JsonResult(6002, err.Error())
 	}
 	if book.RoleId != 0 && book.RoleId != 1 {
-		c.JsonResult(403,"权限不足")
+		c.JsonResult(403, "权限不足")
 	}
 
 	member := models.NewMember()
 
-	if _,err := member.Find(member_id); err != nil {
-		c.JsonResult(6003,"用户不存在")
+	if _, err := member.Find(member_id); err != nil {
+		c.JsonResult(6003, "用户不存在")
 	}
 	if member.Status == 1 {
-		c.JsonResult(6004,"用户已被禁用")
+		c.JsonResult(6004, "用户已被禁用")
 	}
 
-	relationship,err := models.NewRelationship().UpdateRoleId(book.BookId,member_id,role);
+	relationship, err := models.NewRelationship().UpdateRoleId(book.BookId, member_id, role)
 
 	if err != nil {
-		logs.Error("变更用户在项目中的权限 => ",err)
-		c.JsonResult(6005,err.Error())
+		logs.Error("变更用户在项目中的权限 => ", err)
+		c.JsonResult(6005, err.Error())
 	}
 
 	memberRelationshipResult := models.NewMemberRelationshipResult().FromMember(member)
@@ -108,58 +107,58 @@ func (c *BookMemberController) ChangeRole() {
 	memberRelationshipResult.BookId = book.BookId
 	memberRelationshipResult.ResolveRoleName()
 
-	c.JsonResult(0,"ok",memberRelationshipResult)
+	c.JsonResult(0, "ok", memberRelationshipResult)
 }
 
 // 删除参与者.
-func (c *BookMemberController) RemoveMember()  {
+func (c *BookMemberController) RemoveMember() {
 	identify := c.GetString("identify")
-	member_id,_ := c.GetInt("member_id",0)
+	member_id, _ := c.GetInt("member_id", 0)
 
-	if identify == "" || member_id <=0 {
-		c.JsonResult(6001,"参数错误")
+	if identify == "" || member_id <= 0 {
+		c.JsonResult(6001, "参数错误")
 	}
 	if member_id == c.Member.MemberId {
-		c.JsonResult(6006,"不能删除自己")
+		c.JsonResult(6006, "不能删除自己")
 	}
-	book ,err := models.NewBookResult().FindByIdentify(identify,c.Member.MemberId)
+	book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
 
 	if err != nil {
 		if err == models.ErrPermissionDenied {
-			c.JsonResult(403,"权限不足")
+			c.JsonResult(403, "权限不足")
 		}
 		if err == orm.ErrNoRows {
-			c.JsonResult(404,"项目不存在")
+			c.JsonResult(404, "项目不存在")
 		}
-		c.JsonResult(6002,err.Error())
+		c.JsonResult(6002, err.Error())
 	}
 	//如果不是创始人也不是管理员则不能操作
 	if book.RoleId != conf.BookFounder && book.RoleId != conf.BookAdmin {
-		c.JsonResult(403,"权限不足")
+		c.JsonResult(403, "权限不足")
 	}
-	err = models.NewRelationship().DeleteByBookIdAndMemberId(book.BookId,member_id)
+	err = models.NewRelationship().DeleteByBookIdAndMemberId(book.BookId, member_id)
 
 	if err != nil {
-		c.JsonResult(6007,err.Error())
+		c.JsonResult(6007, err.Error())
 	}
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
 
-func (c *BookMemberController) IsPermission() (*models.BookResult,error) {
+func (c *BookMemberController) IsPermission() (*models.BookResult, error) {
 	identify := c.GetString("identify")
-	book ,err := models.NewBookResult().FindByIdentify(identify,c.Member.MemberId)
+	book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
 
 	if err != nil {
 		if err == models.ErrPermissionDenied {
-			return book,errors.New("权限不足")
+			return book, errors.New("权限不足")
 		}
 		if err == orm.ErrNoRows {
-			return book,errors.New("项目不存在")
+			return book, errors.New("项目不存在")
 		}
-		return book,err
+		return book, err
 	}
 	if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder {
-		return book,errors.New("权限不足")
+		return book, errors.New("权限不足")
 	}
-	return book,nil
-}
+	return book, nil
+}

+ 5 - 5
controllers/comment.go

@@ -4,16 +4,16 @@ type CommentController struct {
 	BaseController
 }
 
-func (c *CommentController) Lists()  {
-	
+func (c *CommentController) Lists() {
+
 }
 
 func (c *CommentController) Create() {
 
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
 
-func (c *CommentController) Index()  {
+func (c *CommentController) Index() {
 	c.Prepare()
 	c.TplName = "comment/index.tpl"
-}
+}

+ 29 - 60
controllers/document.go

@@ -11,7 +11,7 @@ import (
 	"strconv"
 	"strings"
 	"time"
-
+	"net/url"
 	"image/png"
 
 	"bytes"
@@ -21,7 +21,6 @@ import (
 	"github.com/astaxie/beego/orm"
 	"github.com/boombuler/barcode"
 	"github.com/boombuler/barcode/qr"
-	"github.com/lifei6671/mindoc/commands"
 	"github.com/lifei6671/mindoc/conf"
 	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
@@ -101,12 +100,10 @@ func promptUserToLogIn(c *DocumentController) {
 	beego.Info("Access " + c.Ctx.Request.URL.RequestURI() + " not permitted.")
 	beego.Info("  Access will be redirected to login page(SessionId: " + c.CruSession.SessionID() + ").")
 
-	c.SetSession("turl", c.Ctx.Request.URL.RequestURI())
-
 	if c.IsAjax() {
-		c.JsonResult(6000, "需要[重]登录。")
+		c.JsonResult(6000, "请重新登录。")
 	} else {
-		c.Redirect(beego.URLFor("AccountController.Login"), 302)
+		c.Redirect(beego.URLFor("AccountController.Login")+ "?url=" + url.PathEscape(conf.BaseUrl+ c.Ctx.Request.URL.RequestURI()), 302)
 	}
 }
 
@@ -156,8 +153,7 @@ func (c *DocumentController) Read() {
 	token := c.GetString("token")
 	id := c.GetString(":id")
 
-	c.Data["DocumentId"] = id // added by dandycheung, 2017-12-08, for exporting
-	beego.Info("DocumentController.Read(): c.Data[\"DocumentId\"] = ", id, ", IsAjax = ", c.IsAjax())
+	c.Data["DocumentId"] = id
 
 	if identify == "" || id == "" {
 		c.Abort("404")
@@ -393,7 +389,6 @@ func (c *DocumentController) Create() {
 
 	document.Identify = doc_identify
 
-
 	document.Version = time.Now().Unix()
 	document.DocumentName = doc_name
 	document.ParentId = parent_id
@@ -438,11 +433,10 @@ func (c *DocumentController) Upload() {
 	}
 	beego.Info(conf.GetUploadFileSize())
 	beego.Info(moreFile.Size)
-	if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize()  {
-		c.JsonResult(6009,"查过文件允许的上传最大值")
+	if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize() {
+		c.JsonResult(6009, "查过文件允许的上传最大值")
 	}
 
-
 	ext := filepath.Ext(moreFile.Filename)
 
 	if ext == "" {
@@ -498,7 +492,7 @@ func (c *DocumentController) Upload() {
 	}
 
 	fileName := "attach_" + strconv.FormatInt(time.Now().UnixNano(), 16)
-	filePath := filepath.Join(commands.WorkingDirectory, "uploads", time.Now().Format("200601"), fileName+ext)
+	filePath := filepath.Join(conf.WorkingDirectory, "uploads", time.Now().Format("200601"), fileName+ext)
 	path := filepath.Dir(filePath)
 
 	os.MkdirAll(path, os.ModePerm)
@@ -515,7 +509,7 @@ func (c *DocumentController) Upload() {
 	attachment.FileName = moreFile.Filename
 	attachment.CreateAt = c.Member.MemberId
 	attachment.FileExt = ext
-	attachment.FilePath = strings.TrimPrefix(filePath, commands.WorkingDirectory)
+	attachment.FilePath = strings.TrimPrefix(filePath, conf.WorkingDirectory)
 	attachment.DocumentId = doc_id
 
 	if fileInfo, err := os.Stat(filePath); err == nil {
@@ -527,7 +521,7 @@ func (c *DocumentController) Upload() {
 	}
 
 	if strings.EqualFold(ext, ".jpg") || strings.EqualFold(ext, ".jpeg") || strings.EqualFold(ext, ".png") || strings.EqualFold(ext, ".gif") {
-		attachment.HttpPath = "/" + strings.Replace(strings.TrimPrefix(filePath, commands.WorkingDirectory), "\\", "/", -1)
+		attachment.HttpPath = "/" + strings.Replace(strings.TrimPrefix(filePath, conf.WorkingDirectory), "\\", "/", -1)
 		if strings.HasPrefix(attachment.HttpPath, "//") {
 			attachment.HttpPath = string(attachment.HttpPath[1:])
 		}
@@ -621,7 +615,7 @@ func (c *DocumentController) DownloadAttachment() {
 		c.Abort("404")
 	}
 
-	c.Ctx.Output.Download(filepath.Join(commands.WorkingDirectory, attachment.FilePath), attachment.FileName)
+	c.Ctx.Output.Download(filepath.Join(conf.WorkingDirectory, attachment.FilePath), attachment.FileName)
 	c.StopRun()
 }
 
@@ -666,7 +660,7 @@ func (c *DocumentController) RemoveAttachment() {
 		c.JsonResult(6005, "删除失败")
 	}
 
-	os.Remove(filepath.Join(commands.WorkingDirectory, attach.FilePath))
+	os.Remove(filepath.Join(conf.WorkingDirectory, attach.FilePath))
 
 	c.JsonResult(0, "ok", attach)
 }
@@ -819,15 +813,13 @@ func (c *DocumentController) Content() {
 				beego.Error("DocumentHistory InsertOrUpdate => ", err)
 			}
 		}
-		if auto_release  {
+		if auto_release {
 			go func(identify string) {
 				models.NewDocument().ReleaseContent(book_id)
 
-
 			}(identify)
 		}
 
-
 		c.JsonResult(0, "ok", doc)
 	}
 
@@ -844,7 +836,6 @@ func (c *DocumentController) Content() {
 	c.JsonResult(0, "ok", doc)
 }
 
-
 func (c *DocumentController) GetDocumentById(id string) (doc *models.Document, err error) {
 	doc = models.NewDocument()
 	if doc_id, err := strconv.Atoi(id); err == nil {
@@ -894,55 +885,33 @@ func (c *DocumentController) Export() {
 		bookResult = isReadable(identify, token, c)
 	}
 
-	if bookResult.PrivatelyOwned == 0 {
-		// TODO: 私有项目禁止导出
-	}
-
-	if !strings.HasPrefix(bookResult.Cover,"http:://") && !strings.HasPrefix(bookResult.Cover,"https:://"){
+	if !strings.HasPrefix(bookResult.Cover, "http:://") && !strings.HasPrefix(bookResult.Cover, "https:://") {
 		bookResult.Cover = c.BaseUrl() + bookResult.Cover
 	}
 
-	eBookResult,err := bookResult.Converter(c.CruSession.SessionID())
+	eBookResult, err := bookResult.Converter(c.CruSession.SessionID())
 
 	if err != nil {
 		beego.Error("转换文档失败:" + bookResult.BookName + " -> " + err.Error())
 		c.Abort("500")
 	}
 
-
 	if output == "pdf" {
-		c.Ctx.Output.Download(eBookResult.PDFPath, identify + ".pdf")
-
-		//如果没有开启缓存,则10分钟后删除
-		if !bookResult.IsCacheEBook {
-			defer func(pdfpath string) {
-				time.Sleep(time.Minute * 10)
-				os.Remove(filepath.Dir(pdfpath))
-			}(eBookResult.PDFPath)
-		}
-		c.StopRun()
-	}else if output == "epub" {
-		c.Ctx.Output.Download(eBookResult.PDFPath, identify + ".epub")
-
-		//如果没有开启缓存,则10分钟后删除
-		if !bookResult.IsCacheEBook {
-			defer func(pdfpath string) {
-				time.Sleep(time.Minute * 10)
-				os.Remove(filepath.Dir(pdfpath))
-			}(eBookResult.EpubPath)
-		}
-		c.StopRun()
-	}else if output == "mobi" {
-		c.Ctx.Output.Download(eBookResult.PDFPath, identify + ".epub")
-
-		//如果没有开启缓存,则10分钟后删除
-		if !bookResult.IsCacheEBook {
-			defer func(pdfpath string) {
-				time.Sleep(time.Minute * 10)
-				os.Remove(filepath.Dir(pdfpath))
-			}(eBookResult.MobiPath)
-		}
-		c.StopRun()
+		c.Ctx.Output.Download(eBookResult.PDFPath, bookResult.BookName+".pdf")
+
+		c.Abort("200")
+	} else if output == "epub" {
+		c.Ctx.Output.Download(eBookResult.EpubPath, bookResult.BookName+".epub")
+
+		c.Abort("200")
+	} else if output == "mobi" {
+		c.Ctx.Output.Download(eBookResult.MobiPath, bookResult.BookName+".epub")
+
+		c.Abort("200")
+	} else if output == "docx" {
+		c.Ctx.Output.Download(eBookResult.WordPath, bookResult.BookName+".epub")
+
+		c.Abort("200")
 	}
 
 	c.Abort("404")

+ 9 - 7
controllers/home.go

@@ -1,10 +1,12 @@
 package controllers
 
 import (
+	"net/url"
 	"github.com/astaxie/beego"
 	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
 	"math"
+	"github.com/lifei6671/mindoc/conf"
 )
 
 type HomeController struct {
@@ -16,9 +18,9 @@ func (c *HomeController) Index() {
 	c.TplName = "home/index.tpl"
 	//如果没有开启匿名访问,则跳转到登录页面
 	if !c.EnableAnonymous && c.Member == nil {
-		c.Redirect(beego.URLFor("AccountController.Login"),302)
+		c.Redirect(beego.URLFor("AccountController.Login") + "?url=" + url.PathEscape(conf.BaseUrl + c.Ctx.Request.URL.RequestURI()), 302)
 	}
-	pageIndex,_ := c.GetInt("page",1)
+	pageIndex, _ := c.GetInt("page", 1)
 	pageSize := 18
 
 	member_id := 0
@@ -26,7 +28,7 @@ func (c *HomeController) Index() {
 	if c.Member != nil {
 		member_id = c.Member.MemberId
 	}
-	books,totalCount,err := models.NewBook().FindForHomeToPager(pageIndex,pageSize,member_id)
+	books, totalCount, err := models.NewBook().FindForHomeToPager(pageIndex, pageSize, member_id)
 
 	if err != nil {
 		beego.Error(err)
@@ -36,18 +38,18 @@ func (c *HomeController) Index() {
 		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, pageSize, totalCount)
 
 		c.Data["PageHtml"] = html
-	}else {
+	} else {
 		c.Data["PageHtml"] = ""
 	}
 	c.Data["TotalPages"] = int(math.Ceil(float64(totalCount) / float64(pageSize)))
 
 	c.Data["Lists"] = books
 
-	labels ,totalCount,err := models.NewLabel().FindToPager(1,10)
+	labels, totalCount, err := models.NewLabel().FindToPager(1, 10)
 
 	if err != nil {
-		c.Data["Labels"] = make([]*models.Label,0)
-	}else{
+		c.Data["Labels"] = make([]*models.Label, 0)
+	} else {
 		c.Data["Labels"] = labels
 	}
 }

+ 12 - 15
controllers/label.go

@@ -1,10 +1,10 @@
 package controllers
 
 import (
-	"github.com/lifei6671/mindoc/models"
-	"github.com/astaxie/beego/orm"
 	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/conf"
+	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
 	"math"
 )
@@ -18,7 +18,7 @@ func (c *LabelController) Prepare() {
 
 	//如果没有开启你们访问则跳转到登录
 	if !c.EnableAnonymous && c.Member == nil {
-		c.Redirect(beego.URLFor("AccountController.Login"),302)
+		c.Redirect(beego.URLFor("AccountController.Login"), 302)
 		return
 	}
 }
@@ -29,16 +29,16 @@ func (c *LabelController) Index() {
 	c.TplName = "label/index.tpl"
 
 	labelName := c.Ctx.Input.Param(":key")
-	pageIndex,_ := c.GetInt("page",1)
+	pageIndex, _ := c.GetInt("page", 1)
 	if labelName == "" {
 		c.Abort("404")
 	}
-	_,err := models.NewLabel().FindFirst("label_name",labelName)
+	_, err := models.NewLabel().FindFirst("label_name", labelName)
 
 	if err != nil {
 		if err == orm.ErrNoRows {
 			c.Abort("404")
-		}else{
+		} else {
 			beego.Error(err)
 			c.Abort("500")
 		}
@@ -47,7 +47,7 @@ func (c *LabelController) Index() {
 	if c.Member != nil {
 		member_id = c.Member.MemberId
 	}
-	search_result,totalCount,err := models.NewBook().FindForLabelToPager(labelName,pageIndex,conf.PageSize,member_id)
+	search_result, totalCount, err := models.NewBook().FindForLabelToPager(labelName, pageIndex, conf.PageSize, member_id)
 
 	if err != nil {
 		beego.Error(err)
@@ -57,7 +57,7 @@ func (c *LabelController) Index() {
 		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount)
 
 		c.Data["PageHtml"] = html
-	}else {
+	} else {
 		c.Data["PageHtml"] = ""
 	}
 	c.Data["Lists"] = search_result
@@ -69,25 +69,22 @@ func (c *LabelController) List() {
 	c.Prepare()
 	c.TplName = "label/list.tpl"
 
-	pageIndex,_ := c.GetInt("page",1)
+	pageIndex, _ := c.GetInt("page", 1)
 	pageSize := 200
 
-	labels ,totalCount,err := models.NewLabel().FindToPager(pageIndex,pageSize)
+	labels, totalCount, err := models.NewLabel().FindToPager(pageIndex, pageSize)
 
 	if err != nil {
-		c.ShowErrorPage(50001,err.Error())
+		c.ShowErrorPage(50001, err.Error())
 	}
 	if totalCount > 0 {
 		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, pageSize, totalCount)
 
 		c.Data["PageHtml"] = html
-	}else {
+	} else {
 		c.Data["PageHtml"] = ""
 	}
 	c.Data["TotalPages"] = int(math.Ceil(float64(totalCount) / float64(pageSize)))
 
 	c.Data["Labels"] = labels
 }
-
-
-

+ 46 - 68
controllers/manager.go

@@ -13,7 +13,6 @@ import (
 	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
 	"path/filepath"
-	"github.com/lifei6671/mindoc/commands"
 	"strconv"
 )
 
@@ -21,7 +20,7 @@ type ManagerController struct {
 	BaseController
 }
 
-func (c *ManagerController) Prepare (){
+func (c *ManagerController) Prepare() {
 	c.BaseController.Prepare()
 
 	if !c.Member.IsAdministrator() {
@@ -42,7 +41,7 @@ func (c *ManagerController) Users() {
 
 	pageIndex, _ := c.GetInt("page", 0)
 
-	members, totalCount, err := models.NewMember().FindToPager(pageIndex, 15)
+	members, totalCount, err := models.NewMember().FindToPager(pageIndex, conf.PageSize)
 
 	if err != nil {
 		c.Data["ErrorMessage"] = err.Error()
@@ -50,7 +49,7 @@ func (c *ManagerController) Users() {
 	}
 
 	if totalCount > 0 {
-		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, 10, int(totalCount))
+		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, int(totalCount))
 
 		c.Data["PageHtml"] = html
 	} else {
@@ -139,10 +138,10 @@ func (c *ManagerController) UpdateMemberStatus() {
 		c.JsonResult(6002, "用户不存在")
 	}
 	if member.MemberId == c.Member.MemberId {
-		c.JsonResult(6004,"不能变更自己的状态")
+		c.JsonResult(6004, "不能变更自己的状态")
 	}
 	if member.Role == conf.MemberSuperRole {
-		c.JsonResult(6005,"不能变更超级管理员的状态")
+		c.JsonResult(6005, "不能变更超级管理员的状态")
 	}
 	member.Status = status
 
@@ -171,10 +170,10 @@ func (c *ManagerController) ChangeMemberRole() {
 		c.JsonResult(6002, "用户不存在")
 	}
 	if member.MemberId == c.Member.MemberId {
-		c.JsonResult(6004,"不能变更自己的权限")
+		c.JsonResult(6004, "不能变更自己的权限")
 	}
 	if member.Role == conf.MemberSuperRole {
-		c.JsonResult(6005,"不能变更超级管理员的权限")
+		c.JsonResult(6005, "不能变更超级管理员的权限")
 	}
 	member.Role = role
 
@@ -191,13 +190,13 @@ func (c *ManagerController) EditMember() {
 	c.Prepare()
 	c.TplName = "manager/edit_users.tpl"
 
-	member_id,_ := c.GetInt(":id",0)
+	member_id, _ := c.GetInt(":id", 0)
 
 	if member_id <= 0 {
 		c.Abort("404")
 	}
 
-	member ,err := models.NewMember().Find(member_id)
+	member, err := models.NewMember().Find(member_id)
 
 	if err != nil {
 		beego.Error(err)
@@ -213,64 +212,64 @@ func (c *ManagerController) EditMember() {
 		member.Phone = phone
 		member.Description = description
 		if password1 != "" && password2 != password1 {
-			c.JsonResult(6001,"确认密码不正确")
+			c.JsonResult(6001, "确认密码不正确")
 		}
-		if password1 != "" && member.AuthMethod != conf.AuthMethodLDAP{
+		if password1 != "" && member.AuthMethod != conf.AuthMethodLDAP {
 			member.Password = password1
 		}
-		if err := member.Valid(password1 == "");err != nil {
-			c.JsonResult(6002,err.Error())
+		if err := member.Valid(password1 == ""); err != nil {
+			c.JsonResult(6002, err.Error())
 		}
 		if password1 != "" {
-			password,err := utils.PasswordHash(password1)
+			password, err := utils.PasswordHash(password1)
 			if err != nil {
 				beego.Error(err)
-				c.JsonResult(6003,"对用户密码加密时出错")
+				c.JsonResult(6003, "对用户密码加密时出错")
 			}
 			member.Password = password
 		}
-		if err := member.Update();err != nil {
+		if err := member.Update(); err != nil {
 			beego.Error(err)
-			c.JsonResult(6004,"保存失败")
+			c.JsonResult(6004, "保存失败")
 		}
-		c.JsonResult(0,"ok")
+		c.JsonResult(0, "ok")
 	}
 
 	c.Data["Model"] = member
 }
 
 //删除一个用户,并将该用户的所有信息转移到超级管理员上.
-func (c *ManagerController) DeleteMember()  {
+func (c *ManagerController) DeleteMember() {
 	c.Prepare()
-	member_id,_ := c.GetInt("id",0)
+	member_id, _ := c.GetInt("id", 0)
 
 	if member_id <= 0 {
-		c.JsonResult(404,"参数错误")
+		c.JsonResult(404, "参数错误")
 	}
 
-	member ,err := models.NewMember().Find(member_id)
+	member, err := models.NewMember().Find(member_id)
 
 	if err != nil {
 		beego.Error(err)
-		c.JsonResult(500,"用户不存在")
+		c.JsonResult(500, "用户不存在")
 	}
 	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 {
 		beego.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 {
 		beego.Error(err)
-		c.JsonResult(5002,"删除失败")
+		c.JsonResult(5002, "删除失败")
 	}
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
 
 //项目列表.
@@ -572,9 +571,9 @@ func (c *ManagerController) AttachList() {
 		c.Data["PageHtml"] = ""
 	}
 
-	for _,item := range attachList {
+	for _, item := range attachList {
 
-		p := filepath.Join(commands.WorkingDirectory,item.FilePath)
+		p := filepath.Join(conf.WorkingDirectory, item.FilePath)
 
 		item.IsExist = utils.FileExists(p)
 
@@ -583,27 +582,27 @@ func (c *ManagerController) AttachList() {
 }
 
 //附件详情.
-func (c *ManagerController) AttachDetailed()  {
+func (c *ManagerController) AttachDetailed() {
 	c.Prepare()
 	c.TplName = "manager/attach_detailed.tpl"
-	attach_id,_ := strconv.Atoi(c.Ctx.Input.Param(":id"))
+	attach_id, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
 
 	if attach_id <= 0 {
 		c.Abort("404")
 	}
 
-	attach,err := models.NewAttachmentResult().Find(attach_id)
+	attach, err := models.NewAttachmentResult().Find(attach_id)
 
 	if err != nil {
-		beego.Error("AttachDetailed => ",err)
+		beego.Error("AttachDetailed => ", err)
 		if err == orm.ErrNoRows {
 			c.Abort("404")
-		}else{
+		} else {
 			c.Abort("500")
 		}
 	}
 
-	attach.FilePath = filepath.Join(commands.WorkingDirectory,attach.FilePath)
+	attach.FilePath = filepath.Join(conf.WorkingDirectory, attach.FilePath)
 	attach.HttpPath = c.BaseUrl() + attach.HttpPath
 
 	attach.IsExist = utils.FileExists(attach.FilePath)
@@ -612,43 +611,22 @@ func (c *ManagerController) AttachDetailed()  {
 }
 
 //删除附件.
-func (c *ManagerController) AttachDelete()  {
+func (c *ManagerController) AttachDelete() {
 	c.Prepare()
-	attach_id,_ := c.GetInt("attach_id")
+	attach_id, _ := c.GetInt("attach_id")
 
 	if attach_id <= 0 {
 		c.Abort("404")
 	}
-	attach,err := models.NewAttachment().Find(attach_id)
+	attach, err := models.NewAttachment().Find(attach_id)
 
 	if err != nil {
-		beego.Error("AttachDelete => ",err)
-		c.JsonResult(6001,err.Error())
+		beego.Error("AttachDelete => ", err)
+		c.JsonResult(6001, err.Error())
 	}
-	if err := attach.Delete();err != nil {
-		beego.Error("AttachDelete => ",err)
-		c.JsonResult(6002,err.Error())
+	if err := attach.Delete(); err != nil {
+		beego.Error("AttachDelete => ", err)
+		c.JsonResult(6002, err.Error())
 	}
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 13 - 13
controllers/search.go

@@ -1,31 +1,31 @@
 package controllers
 
 import (
-	"github.com/lifei6671/mindoc/models"
+	"github.com/astaxie/beego"
 	"github.com/lifei6671/mindoc/conf"
+	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
-	"github.com/astaxie/beego"
-	"strings"
 	"regexp"
 	"strconv"
+	"strings"
 )
 
 type SearchController struct {
 	BaseController
 }
 
-func (c *SearchController) Index()  {
+func (c *SearchController) Index() {
 	c.Prepare()
 	c.TplName = "search/index.tpl"
 
 	//如果没有开启你们访问则跳转到登录
 	if !c.EnableAnonymous && c.Member == nil {
-		c.Redirect(beego.URLFor("AccountController.Login"),302)
+		c.Redirect(beego.URLFor("AccountController.Login"), 302)
 		return
 	}
 
 	keyword := c.GetString("keyword")
-	pageIndex,_ := c.GetInt("page",1)
+	pageIndex, _ := c.GetInt("page", 1)
 
 	c.Data["BaseUrl"] = c.BaseUrl()
 
@@ -35,7 +35,7 @@ func (c *SearchController) Index()  {
 		if c.Member != nil {
 			member_id = c.Member.MemberId
 		}
-		search_result,totalCount,err := models.NewDocumentSearchResult().FindToPager(keyword,pageIndex,conf.PageSize,member_id)
+		search_result, totalCount, err := models.NewDocumentSearchResult().FindToPager(keyword, pageIndex, conf.PageSize, member_id)
 
 		if err != nil {
 			beego.Error(err)
@@ -45,12 +45,12 @@ func (c *SearchController) Index()  {
 			html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount)
 
 			c.Data["PageHtml"] = html
-		}else {
+		} else {
 			c.Data["PageHtml"] = ""
 		}
 		if len(search_result) > 0 {
-			for _,item := range search_result {
-				item.DocumentName = strings.Replace(item.DocumentName,keyword,"<em>" + keyword + "</em>",-1)
+			for _, item := range search_result {
+				item.DocumentName = strings.Replace(item.DocumentName, keyword, "<em>"+keyword+"</em>", -1)
 
 				if item.Description != "" {
 					src := item.Description
@@ -79,13 +79,13 @@ func (c *SearchController) Index()  {
 
 					if len(r) > 100 {
 						src = string(r[:100])
-					}else{
+					} else {
 						src = string(r)
 					}
-					item.Description = strings.Replace(src, keyword, "<em>" + keyword + "</em>", -1)
+					item.Description = strings.Replace(src, keyword, "<em>"+keyword+"</em>", -1)
 				}
 
-				if item.Identify == ""{
+				if item.Identify == "" {
 					item.Identify = strconv.Itoa(item.DocumentId)
 				}
 				if item.ModifyTime.IsZero() {

+ 51 - 54
controllers/setting.go

@@ -3,24 +3,23 @@ package controllers
 import (
 	"fmt"
 	"os"
-	"strings"
 	"path/filepath"
 	"strconv"
+	"strings"
 	"time"
 
 	"github.com/astaxie/beego/logs"
+	"github.com/lifei6671/mindoc/conf"
+	"github.com/lifei6671/mindoc/graphics"
 	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
-	"github.com/lifei6671/mindoc/graphics"
-	"github.com/lifei6671/mindoc/conf"
-	"github.com/lifei6671/mindoc/commands"
 )
 
 type SettingController struct {
 	BaseController
 }
 
-func (c *SettingController) Index()  {
+func (c *SettingController) Index() {
 	c.TplName = "setting/index.tpl"
 
 	if c.Ctx.Input.IsPost() {
@@ -43,131 +42,129 @@ func (c *SettingController) Index()  {
 	}
 }
 
-func (c *SettingController) Password()  {
+func (c *SettingController) Password() {
 	c.TplName = "setting/password.tpl"
 
 	if c.Ctx.Input.IsPost() {
 		if c.Member.AuthMethod == conf.AuthMethodLDAP {
-			c.JsonResult(6009,"当前用户不支持修改密码")
+			c.JsonResult(6009, "当前用户不支持修改密码")
 		}
 		password1 := c.GetString("password1")
 		password2 := c.GetString("password2")
 		password3 := c.GetString("password3")
 
 		if password1 == "" {
-			c.JsonResult(6003,"原密码不能为空")
+			c.JsonResult(6003, "原密码不能为空")
 		}
 
 		if password2 == "" {
-			c.JsonResult(6004,"新密码不能为空")
+			c.JsonResult(6004, "新密码不能为空")
 		}
-		if count := strings.Count(password2,""); count < 6 || count > 18 {
-			c.JsonResult(6009,"密码必须在6-18字之间")
+		if count := strings.Count(password2, ""); count < 6 || count > 18 {
+			c.JsonResult(6009, "密码必须在6-18字之间")
 		}
 		if password2 != password3 {
-			c.JsonResult(6003,"确认密码不正确")
+			c.JsonResult(6003, "确认密码不正确")
 		}
-		if ok,_ := utils.PasswordVerify(c.Member.Password,password1) ; !ok {
-			c.JsonResult(6005,"原始密码不正确")
+		if ok, _ := utils.PasswordVerify(c.Member.Password, password1); !ok {
+			c.JsonResult(6005, "原始密码不正确")
 		}
 		if password1 == password2 {
-			c.JsonResult(6006,"新密码不能和原始密码相同")
+			c.JsonResult(6006, "新密码不能和原始密码相同")
 		}
-		pwd,err := utils.PasswordHash(password2)
+		pwd, err := utils.PasswordHash(password2)
 		if err != nil {
-			c.JsonResult(6007,"密码加密失败")
+			c.JsonResult(6007, "密码加密失败")
 		}
 		c.Member.Password = pwd
-		if err := c.Member.Update();err != nil {
-			c.JsonResult(6008,err.Error())
+		if err := c.Member.Update(); err != nil {
+			c.JsonResult(6008, err.Error())
 		}
-		c.JsonResult(0,"ok")
+		c.JsonResult(0, "ok")
 	}
 }
 
 // Upload 上传图片
 func (c *SettingController) Upload() {
-	file,moreFile,err := c.GetFile("image-file")
+	file, moreFile, err := c.GetFile("image-file")
 	defer file.Close()
 
 	if err != nil {
-		logs.Error("",err.Error())
-		c.JsonResult(500,"读取文件异常")
+		logs.Error("", err.Error())
+		c.JsonResult(500, "读取文件异常")
 	}
 
 	ext := filepath.Ext(moreFile.Filename)
 
-	if !strings.EqualFold(ext,".png") && !strings.EqualFold(ext,".jpg") && !strings.EqualFold(ext,".gif") && !strings.EqualFold(ext,".jpeg")  {
-		c.JsonResult(500,"不支持的图片格式")
+	if !strings.EqualFold(ext, ".png") && !strings.EqualFold(ext, ".jpg") && !strings.EqualFold(ext, ".gif") && !strings.EqualFold(ext, ".jpeg") {
+		c.JsonResult(500, "不支持的图片格式")
 	}
 
-
-	x1 ,_ := strconv.ParseFloat(c.GetString("x"),10)
-	y1 ,_ := strconv.ParseFloat(c.GetString("y"),10)
-	w1 ,_ := strconv.ParseFloat(c.GetString("width"),10)
-	h1 ,_ := strconv.ParseFloat(c.GetString("height"),10)
+	x1, _ := strconv.ParseFloat(c.GetString("x"), 10)
+	y1, _ := strconv.ParseFloat(c.GetString("y"), 10)
+	w1, _ := strconv.ParseFloat(c.GetString("width"), 10)
+	h1, _ := strconv.ParseFloat(c.GetString("height"), 10)
 
 	x := int(x1)
 	y := int(y1)
 	width := int(w1)
 	height := int(h1)
 
-	fmt.Println(x,x1,y,y1)
+	fmt.Println(x, x1, y, y1)
 
-	fileName := "avatar_" +  strconv.FormatInt(time.Now().UnixNano(), 16)
+	fileName := "avatar_" + strconv.FormatInt(time.Now().UnixNano(), 16)
 
-	filePath := filepath.Join(commands.WorkingDirectory,"uploads" , time.Now().Format("200601") , fileName + ext)
+	filePath := filepath.Join(conf.WorkingDirectory, "uploads", time.Now().Format("200601"), fileName+ext)
 
 	path := filepath.Dir(filePath)
 
 	os.MkdirAll(path, os.ModePerm)
 
-	err = c.SaveToFile("image-file",filePath)
+	err = c.SaveToFile("image-file", filePath)
 
 	if err != nil {
-		logs.Error("",err)
-		c.JsonResult(500,"图片保存失败")
+		logs.Error("", err)
+		c.JsonResult(500, "图片保存失败")
 	}
 
-
 	//剪切图片
-	subImg,err := graphics.ImageCopyFromFile(filePath,x,y,width,height)
+	subImg, err := graphics.ImageCopyFromFile(filePath, x, y, width, height)
 
 	if err != nil {
-		logs.Error("ImageCopyFromFile => ",err)
-		c.JsonResult(6001,"头像剪切失败")
+		logs.Error("ImageCopyFromFile => ", err)
+		c.JsonResult(6001, "头像剪切失败")
 	}
 	os.Remove(filePath)
 
-	filePath = filepath.Join(commands.WorkingDirectory,"uploads" , time.Now().Format("200601") , fileName + "_small" + ext)
+	filePath = filepath.Join(conf.WorkingDirectory, "uploads", time.Now().Format("200601"), fileName+"_small"+ext)
 
-	err = graphics.ImageResizeSaveFile(subImg,120,120,filePath)
+	err = graphics.ImageResizeSaveFile(subImg, 120, 120, filePath)
 	//err = graphics.SaveImage(filePath,subImg)
 
 	if err != nil {
-		logs.Error("保存文件失败 => ",err.Error())
-		c.JsonResult(500,"保存文件失败")
+		logs.Error("保存文件失败 => ", err.Error())
+		c.JsonResult(500, "保存文件失败")
 	}
 
-	url := "/" + strings.Replace(strings.TrimPrefix(filePath,commands.WorkingDirectory),"\\","/",-1)
-	if strings.HasPrefix(url,"//") {
+	url := "/" + strings.Replace(strings.TrimPrefix(filePath, conf.WorkingDirectory), "\\", "/", -1)
+	if strings.HasPrefix(url, "//") {
 		url = string(url[1:])
 	}
 
-	if member,err := models.NewMember().Find(c.Member.MemberId);err == nil {
+	if member, err := models.NewMember().Find(c.Member.MemberId); err == nil {
 		avater := member.Avatar
 
 		member.Avatar = url
-		err := member.Update();
+		err := member.Update()
 		if err == nil {
-			if strings.HasPrefix(avater,"/uploads/") {
-				os.Remove(filepath.Join(commands.WorkingDirectory,avater))
+			if strings.HasPrefix(avater, "/uploads/") {
+				os.Remove(filepath.Join(conf.WorkingDirectory, avater))
 			}
 			c.SetMember(*member)
-		}else{
-			c.JsonResult(60001,"保存头像失败")
+		} else {
+			c.JsonResult(60001, "保存头像失败")
 		}
 	}
 
-	c.JsonResult(0,"ok",url)
-}
+	c.JsonResult(0, "ok", url)
+}

+ 70 - 26
converter/converter.go

@@ -11,14 +11,12 @@ import (
 	"strings"
 
 	"time"
-
 	"os/exec"
-
 	"errors"
 
-	"github.com/TruthHun/gotil/cryptil"
-	"github.com/TruthHun/gotil/filetil"
-	"github.com/TruthHun/gotil/ziptil"
+	"github.com/lifei6671/mindoc/utils/filetil"
+	"github.com/lifei6671/mindoc/utils/ziptil"
+	"github.com/lifei6671/mindoc/utils/cryptil"
 )
 
 type Converter struct {
@@ -67,6 +65,7 @@ var (
 	output       = "output" //文档导出文件夹
 	ebookConvert = "ebook-convert"
 )
+
 // 接口文档 https://manual.calibre-ebook.com/generated/en/ebook-convert.html#table-of-contents
 //根据json配置文件,创建文档转化对象
 func NewConverter(configFile string, debug ...bool) (converter *Converter, err error) {
@@ -124,7 +123,8 @@ func (this *Converter) Convert() (err error) {
 	}
 
 	//将当前文件夹下的所有文件压缩成zip包,然后直接改名成content.epub
-	f := this.BasePath + "/content.epub"
+	f := filepath.Join(this.BasePath, "content.epub")
+	fmt.Println("epub目录 " + f)
 	os.Remove(f) //如果原文件存在了,则删除;
 	if err = ziptil.Zip(f, this.BasePath); err == nil {
 		//创建导出文件夹
@@ -144,6 +144,12 @@ func (this *Converter) Convert() (err error) {
 					}
 				case "pdf":
 					if err = this.convertToPdf(); err != nil {
+						fmt.Println(err)
+						errs = append(errs, err.Error())
+					}
+				case "docx":
+					if err = this.convertToDocx(); err != nil {
+						fmt.Println(err)
 						errs = append(errs, err.Error())
 					}
 				}
@@ -157,6 +163,8 @@ func (this *Converter) Convert() (err error) {
 				fmt.Println(err)
 			}
 		}
+	} else {
+		fmt.Println("压缩目录出错" + err.Error())
 	}
 	return
 }
@@ -164,13 +172,13 @@ func (this *Converter) Convert() (err error) {
 //删除生成导出文档而创建的文件
 func (this *Converter) converterDefer() {
 	//删除不必要的文件
-	os.RemoveAll(this.BasePath + "/META-INF")
-	os.RemoveAll(this.BasePath + "/content.epub")
-	os.RemoveAll(this.BasePath + "/mimetype")
-	os.RemoveAll(this.BasePath + "/toc.ncx")
-	os.RemoveAll(this.BasePath + "/content.opf")
-	os.RemoveAll(this.BasePath + "/titlepage.xhtml") //封面图片待优化
-	os.RemoveAll(this.BasePath + "/summary.html")    //文档目录
+	os.RemoveAll(filepath.Join(this.BasePath, "META-INF"))
+	os.RemoveAll(filepath.Join(this.BasePath, "content.epub"))
+	os.RemoveAll(filepath.Join(this.BasePath, "mimetype"))
+	os.RemoveAll(filepath.Join(this.BasePath, "toc.ncx"))
+	os.RemoveAll(filepath.Join(this.BasePath, "content.opf"))
+	os.RemoveAll(filepath.Join(this.BasePath, "titlepage.xhtml")) //封面图片待优化
+	os.RemoveAll(filepath.Join(this.BasePath, "summary.html"))    //文档目录
 }
 
 //生成metainfo
@@ -182,15 +190,15 @@ func (this *Converter) generateMetaInfo() (err error) {
 			   </rootfiles>
 			</container>
     `
-	folder := this.BasePath + "/META-INF"
+	folder := filepath.Join(this.BasePath, "META-INF")
 	os.MkdirAll(folder, os.ModePerm)
-	err = ioutil.WriteFile(folder+"/container.xml", []byte(xml), os.ModePerm)
+	err = ioutil.WriteFile(filepath.Join(folder, "container.xml"), []byte(xml), os.ModePerm)
 	return
 }
 
 //形成mimetyppe
 func (this *Converter) generateMimeType() (err error) {
-	return ioutil.WriteFile(this.BasePath+"/mimetype", []byte("application/epub+zip"), os.ModePerm)
+	return ioutil.WriteFile(filepath.Join(this.BasePath, "mimetype"), []byte("application/epub+zip"), os.ModePerm)
 }
 
 //生成封面
@@ -216,7 +224,7 @@ func (this *Converter) generateTitlePage() (err error) {
 					</body>
 				</html>
 		`
-		if err = ioutil.WriteFile(this.BasePath+"/titlepage.xhtml", []byte(xml), os.ModePerm); err == nil {
+		if err = ioutil.WriteFile(filepath.Join(this.BasePath, "titlepage.xhtml"), []byte(xml), os.ModePerm); err == nil {
 			this.GeneratedCover = "titlepage.xhtml"
 		}
 	}
@@ -241,7 +249,7 @@ func (this *Converter) generateTocNcx() (err error) {
 	`
 	codes, _ := this.tocToXml(0, 1)
 	ncx = fmt.Sprintf(ncx, this.Config.Language, this.Config.Title, strings.Join(codes, ""))
-	return ioutil.WriteFile(this.BasePath+"/toc.ncx", []byte(ncx), os.ModePerm)
+	return ioutil.WriteFile(filepath.Join(this.BasePath, "toc.ncx"), []byte(ncx), os.ModePerm)
 }
 
 //生成文档目录,即summary.html
@@ -263,7 +271,7 @@ func (this *Converter) generateSummary() (err error) {
 				</body>
 				</html>`
 	summary = fmt.Sprintf(summary, strings.Join(this.tocToSummary(0), ""))
-	return ioutil.WriteFile(this.BasePath+"/summary.html", []byte(summary), os.ModePerm)
+	return ioutil.WriteFile(filepath.Join(this.BasePath, "summary.html"), []byte(summary), os.ModePerm)
 }
 
 //将toc转成toc.ncx文件
@@ -380,6 +388,8 @@ func (this *Converter) generateContentOpf() (err error) {
 						manifestArr = append(manifestArr, fmt.Sprintf(`<item href="%v" id="%v" media-type="%v"/>`, sourcefile, id, mt))
 					}
 				}
+			} else {
+				fmt.Println(file.Path)
 			}
 		}
 
@@ -415,14 +425,14 @@ func (this *Converter) generateContentOpf() (err error) {
 		guide = `<guide>` + guide + `</guide>`
 	}
 	pkg = fmt.Sprintf(pkg, meta, manifest, spine, guide)
-	return ioutil.WriteFile(this.BasePath+"/content.opf", []byte(pkg), os.ModePerm)
+	return ioutil.WriteFile(filepath.Join(this.BasePath, "content.opf"), []byte(pkg), os.ModePerm)
 }
 
 //转成epub
 func (this *Converter) convertToEpub() (err error) {
 	args := []string{
-		this.BasePath + "/content.epub",
-		this.BasePath + "/" + output + "/book.epub",
+		filepath.Join(this.BasePath, "content.epub"),
+		filepath.Join(this.BasePath, output, "book.epub"),
 	}
 	cmd := exec.Command(ebookConvert, args...)
 
@@ -435,8 +445,8 @@ func (this *Converter) convertToEpub() (err error) {
 //转成mobi
 func (this *Converter) convertToMobi() (err error) {
 	args := []string{
-		this.BasePath + "/content.epub",
-		this.BasePath + "/" + output + "/book.mobi",
+		filepath.Join(this.BasePath, "content.epub"),
+		filepath.Join(this.BasePath, output, "book.mobi"),
 	}
 	cmd := exec.Command(ebookConvert, args...)
 	if this.Debug {
@@ -449,8 +459,8 @@ func (this *Converter) convertToMobi() (err error) {
 //转成pdf
 func (this *Converter) convertToPdf() (err error) {
 	args := []string{
-		this.BasePath + "/content.epub",
-		this.BasePath + "/" + output + "/book.pdf",
+		filepath.Join(this.BasePath, "content.epub"),
+		filepath.Join(this.BasePath, output, "book.pdf"),
 	}
 	//页面大小
 	if len(this.Config.PaperSize) > 0 {
@@ -490,6 +500,40 @@ func (this *Converter) convertToPdf() (err error) {
 	}
 
 	cmd := exec.Command(ebookConvert, args...)
+	if this.Debug {
+		fmt.Println(cmd.Args)
+	}
+
+	return cmd.Run()
+}
+
+// 转成word
+func (this *Converter) convertToDocx() (err error) {
+	args := []string{
+		this.BasePath + "/content.epub",
+		this.BasePath + "/" + output + "/book.docx",
+	}
+	args = append(args, "--docx-no-toc")
+
+	//页面大小
+	if len(this.Config.PaperSize) > 0 {
+		args = append(args, "--docx-page-size", this.Config.PaperSize)
+	}
+
+	if len(this.Config.MarginLeft) > 0 {
+		args = append(args, "--docx-page-margin-left", this.Config.MarginLeft)
+	}
+	if len(this.Config.MarginTop) > 0 {
+		args = append(args, "--docx-page-margin-top", this.Config.MarginTop)
+	}
+	if len(this.Config.MarginRight) > 0 {
+		args = append(args, "--docx-page-margin-right", this.Config.MarginRight)
+	}
+	if len(this.Config.MarginBottom) > 0 {
+		args = append(args, "--docx-page-margin-bottom", this.Config.MarginBottom)
+	}
+	cmd := exec.Command(ebookConvert, args...)
+
 	if this.Debug {
 		fmt.Println(cmd.Args)
 	}

+ 1 - 1
graphics/file.go

@@ -13,7 +13,7 @@ import (
 // 将图片保存到指定的路径
 func SaveImage(p string, src image.Image) error {
 
-	os.MkdirAll(filepath.Dir(p),0666)
+	os.MkdirAll(filepath.Dir(p), 0666)
 
 	f, err := os.OpenFile(p, os.O_SYNC|os.O_RDWR|os.O_CREATE, 0666)
 

+ 1 - 1
models/attachment.go

@@ -127,7 +127,7 @@ func (m *Attachment) FindToPager(pageIndex, pageSize int) (attachList []*Attachm
 		} else {
 			attach.DocumentName = "[不存在]"
 		}
-		attach.LocalHttpPath = strings.Replace(item.FilePath,"\\","/",-1)
+		attach.LocalHttpPath = strings.Replace(item.FilePath, "\\", "/", -1)
 
 		attachList = append(attachList, attach)
 	}

+ 12 - 12
models/attachment_result.go

@@ -8,27 +8,27 @@ import (
 
 type AttachmentResult struct {
 	Attachment
-	IsExist bool
-	BookName string
-	DocumentName string
+	IsExist       bool
+	BookName      string
+	DocumentName  string
 	FileShortSize string
-	Account string
+	Account       string
 	LocalHttpPath string
 }
 
 func NewAttachmentResult() *AttachmentResult {
-	return &AttachmentResult{ IsExist : false }
+	return &AttachmentResult{IsExist: false}
 }
 
-func (m *AttachmentResult) Find(id int) (*AttachmentResult,error)  {
+func (m *AttachmentResult) Find(id int) (*AttachmentResult, error) {
 	o := orm.NewOrm()
 
 	attach := NewAttachment()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("attachment_id",id).One(attach)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("attachment_id", id).One(attach)
 
 	if err != nil {
-		return m,err
+		return m, err
 	}
 
 	m.Attachment = *attach
@@ -50,12 +50,12 @@ func (m *AttachmentResult) Find(id int) (*AttachmentResult,error)  {
 
 	if attach.CreateAt > 0 {
 		member := NewMember()
-		if e := o.QueryTable(member.TableNameWithPrefix()).Filter("member_id",attach.CreateAt).One(member,"account");e == nil {
+		if e := o.QueryTable(member.TableNameWithPrefix()).Filter("member_id", attach.CreateAt).One(member, "account"); e == nil {
 			m.Account = member.Account
 		}
 	}
 	m.FileShortSize = utils.FormatBytes(int64(attach.FileSize))
-	m.LocalHttpPath = strings.Replace(m.FilePath,"\\","/",-1)
+	m.LocalHttpPath = strings.Replace(m.FilePath, "\\", "/", -1)
 
-	return m,nil
-}
+	return m, nil
+}

+ 0 - 2
models/base.go

@@ -1,6 +1,4 @@
 package models
 
 type Model struct {
-
 }
-

+ 3 - 3
models/blogs.go

@@ -4,9 +4,9 @@ import "time"
 
 //博文表
 type Blog struct {
-	BlogId int
+	BlogId    int
 	BlogTitle string
-	MemberId int
+	MemberId  int
 	//文字摘要
 	BlogExcerpt string
 	//文章内容
@@ -22,4 +22,4 @@ type Blog struct {
 	ModifyAt int
 	//创建时间
 	Created time.Time
-}
+}

+ 16 - 14
models/book.go

@@ -3,11 +3,14 @@ package models
 import (
 	"time"
 
+	"fmt"
 	"github.com/astaxie/beego"
 	"github.com/astaxie/beego/logs"
 	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/conf"
-	"fmt"
+	"os"
+	"path/filepath"
+	"strconv"
 )
 
 // Book struct .
@@ -16,17 +19,15 @@ type Book struct {
 	// BookName 项目名称.
 	BookName string `orm:"column(book_name);size(500)" json:"book_name"`
 	// Identify 项目唯一标识.
-	Identify   string `orm:"column(identify);size(100);unique" json:"identify"`
+	Identify string `orm:"column(identify);size(100);unique" json:"identify"`
 	//是否是自动发布 0 否/1 是
 	AutoRelease int `orm:"column(auto_release);type(int);default(0)" json:"auto_release"`
-	OrderIndex int    `orm:"column(order_index);type(int);default(0)" json:"order_index"`
+	OrderIndex  int `orm:"column(order_index);type(int);default(0)" json:"order_index"`
 	// Description 项目描述.
 	Description string `orm:"column(description);size(2000)" json:"description"`
 	//发行公司
-	Publisher string 	`orm:"column(publisher);size(500)" json:"publisher"`
-	//是否缓存导出的电子书,如果缓存可能会出现导出的文件不是最新的。 0 为不缓存
-	IsCacheEBook int `orm:"column(is_cache_ebook);type(int);default(0)" json:"is_cache_ebook"`
-	Label       string `orm:"column(label);size(500)" json:"label"`
+	Publisher string `orm:"column(publisher);size(500)" json:"publisher"`
+	Label        string `orm:"column(label);size(500)" json:"label"`
 	// PrivatelyOwned 项目私有: 0 公开/ 1 私有
 	PrivatelyOwned int `orm:"column(privately_owned);type(int);default(0)" json:"privately_owned"`
 	// 当项目是私有时的访问Token.
@@ -122,7 +123,7 @@ func (m *Book) Update(cols ...string) error {
 	temp := NewBook()
 	temp.BookId = m.BookId
 
-	if err := o.Read(temp);err != nil {
+	if err := o.Read(temp); err != nil {
 		return err
 	}
 
@@ -185,7 +186,7 @@ func (m *Book) FindToPager(pageIndex, pageSize, memberId int) (books []*BookResu
 		" LEFT JOIN " + relationship.TableNameWithPrefix() + " AS rel ON book.book_id=rel.book_id AND rel.member_id = ?" +
 		" LEFT JOIN " + relationship.TableNameWithPrefix() + " AS rel1 ON book.book_id=rel1.book_id  AND rel1.role_id=0" +
 		" LEFT JOIN " + NewMember().TableNameWithPrefix() + " AS m ON rel1.member_id=m.member_id " +
-		" WHERE rel.relationship_id > 0 ORDER BY book.order_index DESC,book.book_id DESC LIMIT " + fmt.Sprintf("%d,%d",offset,pageSize)
+		" WHERE rel.relationship_id > 0 ORDER BY book.order_index DESC,book.book_id DESC LIMIT " + fmt.Sprintf("%d,%d", offset, pageSize)
 
 	_, err = o.Raw(sql2, memberId).QueryRows(&books)
 	if err != nil {
@@ -261,6 +262,8 @@ func (m *Book) ThoroughDeleteBook(id int) error {
 		NewLabel().InsertOrUpdateMulti(m.Label)
 	}
 
+	os.RemoveAll(filepath.Join(conf.WorkingDirectory,"uploads","books",strconv.Itoa(id)))
+
 	return o.Commit()
 
 }
@@ -320,7 +323,7 @@ func (m *Book) FindForLabelToPager(keyword string, pageIndex, pageSize, member_i
 	if member_id > 0 {
 		sql1 := "SELECT COUNT(*) FROM md_books AS book LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.member_id = ? WHERE (relationship_id > 0 OR book.privately_owned = 0) AND book.label LIKE ?"
 
-		err = o.Raw(sql1, member_id,keyword).QueryRow(&totalCount)
+		err = o.Raw(sql1, member_id, keyword).QueryRow(&totalCount)
 		if err != nil {
 			return
 		}
@@ -330,12 +333,12 @@ func (m *Book) FindForLabelToPager(keyword string, pageIndex, pageSize, member_i
 			LEFT JOIN md_members AS member ON rel1.member_id = member.member_id
 			WHERE (rel.relationship_id > 0 OR book.privately_owned = 0) AND book.label LIKE ? ORDER BY order_index DESC ,book.book_id DESC LIMIT ?,?`
 
-		_, err = o.Raw(sql2, member_id,keyword, offset, pageSize).QueryRows(&books)
+		_, err = o.Raw(sql2, member_id, keyword, offset, pageSize).QueryRows(&books)
 
 		return
 
 	} else {
-		count, err1 := o.QueryTable(NewBook().TableNameWithPrefix()).Filter("privately_owned", 0).Filter("label__icontains",keyword).Count()
+		count, err1 := o.QueryTable(NewBook().TableNameWithPrefix()).Filter("privately_owned", 0).Filter("label__icontains", keyword).Count()
 
 		if err1 != nil {
 			err = err1
@@ -348,14 +351,13 @@ func (m *Book) FindForLabelToPager(keyword string, pageIndex, pageSize, member_i
 			LEFT JOIN md_members AS member ON rel.member_id = member.member_id
 			WHERE book.privately_owned = 0 AND book.label LIKE ? ORDER BY order_index DESC ,book.book_id DESC LIMIT ?,?`
 
-		_, err = o.Raw(sql,keyword, offset, pageSize).QueryRows(&books)
+		_, err = o.Raw(sql, keyword, offset, pageSize).QueryRows(&books)
 
 		return
 
 	}
 }
 
-
 //重置文档数量
 func (m *Book) ResetDocumentNumber(book_id int) {
 	o := orm.NewOrm()

+ 146 - 143
models/book_result.go

@@ -1,64 +1,64 @@
 package models
 
 import (
-	"time"
 	"bytes"
+	"time"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
 
-	"github.com/astaxie/beego/orm"
+	"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"
-	"strings"
 	"github.com/lifei6671/mindoc/converter"
-	"strconv"
-	"github.com/russross/blackfriday"
-	"path/filepath"
-	"github.com/astaxie/beego"
-	"os"
-	"github.com/PuerkitoBio/goquery"
 	"github.com/lifei6671/mindoc/utils"
+	"github.com/russross/blackfriday"
 )
 
 type BookResult struct {
-	BookId int              `json:"book_id"`
-	BookName string         `json:"book_name"`
-	Identify string         `json:"identify"`
-	OrderIndex int          `json:"order_index"`
-	Description string      `json:"description"`
-	Publisher string 		`json:"publisher"`
-	IsCacheEBook bool		`json:"is_cache_ebook"`
-	PrivatelyOwned int      `json:"privately_owned"`
-	PrivateToken string     `json:"private_token"`
-	DocCount int            `json:"doc_count"`
-	CommentStatus string    `json:"comment_status"`
-	CommentCount int        `json:"comment_count"`
-	CreateTime time.Time    `json:"create_time"`
-	CreateName string 		`json:"create_name"`
-	ModifyTime time.Time	`json:"modify_time"`
-	Cover string            `json:"cover"`
-	Theme string			`json:"theme"`
-	Label string			`json:"label"`
-	MemberId int            `json:"member_id"`
-	Editor string           `json:"editor"`
-	AutoRelease bool		`json:"auto_release"`
-
-	RelationshipId int	`json:"relationship_id"`
-	RoleId int        	`json:"role_id"`
-	RoleName string 	`json:"role_name"`
-	Status int
-
-	LastModifyText string 	`json:"last_modify_text"`
-	IsDisplayComment bool	`json:"is_display_comment"`
+	BookId         int       `json:"book_id"`
+	BookName       string    `json:"book_name"`
+	Identify       string    `json:"identify"`
+	OrderIndex     int       `json:"order_index"`
+	Description    string    `json:"description"`
+	Publisher      string    `json:"publisher"`
+	PrivatelyOwned int       `json:"privately_owned"`
+	PrivateToken   string    `json:"private_token"`
+	DocCount       int       `json:"doc_count"`
+	CommentStatus  string    `json:"comment_status"`
+	CommentCount   int       `json:"comment_count"`
+	CreateTime     time.Time `json:"create_time"`
+	CreateName     string    `json:"create_name"`
+	ModifyTime     time.Time `json:"modify_time"`
+	Cover          string    `json:"cover"`
+	Theme          string    `json:"theme"`
+	Label          string    `json:"label"`
+	MemberId       int       `json:"member_id"`
+	Editor         string    `json:"editor"`
+	AutoRelease    bool      `json:"auto_release"`
+
+	RelationshipId int    `json:"relationship_id"`
+	RoleId         int    `json:"role_id"`
+	RoleName       string `json:"role_name"`
+	Status         int
+
+	LastModifyText   string `json:"last_modify_text"`
+	IsDisplayComment bool   `json:"is_display_comment"`
 }
 
 func NewBookResult() *BookResult {
 	return &BookResult{}
 }
 
-
 // 根据项目标识查询项目以及指定用户权限的信息.
-func (m *BookResult) FindByIdentify(identify string,member_id int) (*BookResult,error) {
+func (m *BookResult) FindByIdentify(identify string, member_id int) (*BookResult, error) {
 	if identify == "" || member_id <= 0 {
-		return m,ErrInvalidParameter
+		return m, ErrInvalidParameter
 	}
 	o := orm.NewOrm()
 
@@ -93,11 +93,10 @@ func (m *BookResult) FindByIdentify(identify string,member_id int) (*BookResult,
 
 	m = NewBookResult().ToBookResult(*book)
 
-	m.CreateName 		= member.Account
-	m.MemberId 		= relationship.MemberId
-	m.RoleId		= relationship.RoleId
-	m.RelationshipId	= relationship.RelationshipId
-
+	m.CreateName = member.Account
+	m.MemberId = relationship.MemberId
+	m.RoleId = relationship.RoleId
+	m.RelationshipId = relationship.RelationshipId
 
 	if m.RoleId == conf.BookFounder {
 		m.RoleName = "创始人"
@@ -123,10 +122,10 @@ func (m *BookResult) FindByIdentify(identify string,member_id int) (*BookResult,
 	return m, nil
 }
 
-func (m *BookResult) FindToPager(pageIndex, pageSize int) (books []*BookResult,totalCount int,err error)  {
+func (m *BookResult) FindToPager(pageIndex, pageSize int) (books []*BookResult, totalCount int, err error) {
 	o := orm.NewOrm()
 
-	count,err := o.QueryTable(NewBook().TableNameWithPrefix()).Count()
+	count, err := o.QueryTable(NewBook().TableNameWithPrefix()).Count()
 
 	if err != nil {
 		return
@@ -140,9 +139,9 @@ func (m *BookResult) FindToPager(pageIndex, pageSize int) (books []*BookResult,t
 			LEFT JOIN md_members AS m ON rel.member_id = m.member_id
 		ORDER BY book.order_index DESC ,book.book_id DESC  LIMIT ?,?`
 
-	offset := (pageIndex -1 )* pageSize
+	offset := (pageIndex - 1) * pageSize
 
-	_,err = o.Raw(sql,offset,pageSize).QueryRows(&books)
+	_, err = o.Raw(sql, offset, pageSize).QueryRows(&books)
 
 	return
 }
@@ -169,7 +168,6 @@ func (m *BookResult) ToBookResult(book Book) *BookResult {
 	m.Theme = book.Theme
 	m.AutoRelease = book.AutoRelease == 1
 	m.Publisher = book.Publisher
-	m.IsCacheEBook = book.IsCacheEBook == 1
 
 	if book.Theme == "" {
 		m.Theme = "default"
@@ -180,128 +178,139 @@ func (m *BookResult) ToBookResult(book Book) *BookResult {
 	return m
 }
 
-func (m *BookResult) Converter(sessionId string) (ConvertBookResult,error) {
+func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {
 
 	convertBookResult := ConvertBookResult{}
-	outputPath := filepath.Join(beego.AppConfig.DefaultString("book_output_path","cache"),sessionId,strconv.Itoa(m.BookId))
 
-	if m.IsCacheEBook {
-		outputPath = filepath.Join(beego.AppConfig.DefaultString("book_output_path","cache"),strconv.Itoa(m.BookId))
-	}
+	outputPath := filepath.Join(conf.WorkingDirectory,"uploads","books", strconv.Itoa(m.BookId))
+	viewPath := beego.BConfig.WebConfig.ViewsPath
 
-	if m.IsCacheEBook {
-		pdfpath := filepath.Join(outputPath,"output","book.pdf")
-		epubpath := filepath.Join(outputPath,"output","book.epub")
-		mobipath := filepath.Join(outputPath,"output","book.mobi")
+	pdfpath := filepath.Join(outputPath, "book.pdf")
+	epubpath := filepath.Join(outputPath, "book.epub")
+	mobipath := filepath.Join(outputPath, "book.mobi")
+	docxpath := filepath.Join(outputPath, "book.docx")
 
-		if utils.FileExists(pdfpath) && utils.FileExists(epubpath) && utils.FileExists(mobipath){
-			convertBookResult.EpubPath = epubpath
-			convertBookResult.MobiPath = mobipath
-			convertBookResult.PDFPath = pdfpath
-			return convertBookResult,nil
-		}
+	//先将转换的文件储存到临时目录
+	tempOutputPath :=  filepath.Join(os.TempDir(),"sessionId") //filepath.Abs(filepath.Join("cache", sessionId))
+
+	os.MkdirAll(outputPath, 0766)
+	os.MkdirAll(tempOutputPath, 0766)
+
+
+	if utils.FileExists(pdfpath) && utils.FileExists(epubpath) && utils.FileExists(mobipath) && utils.FileExists(docxpath) {
+		convertBookResult.EpubPath = epubpath
+		convertBookResult.MobiPath = mobipath
+		convertBookResult.PDFPath = pdfpath
+		convertBookResult.WordPath = docxpath
+		return convertBookResult, nil
 	}
+
+
 	docs, err := NewDocument().FindListByBookId(m.BookId)
 	if err != nil {
-		return convertBookResult,err
+		return convertBookResult, err
 	}
 
-	tocList := make([]converter.Toc,0)
+	tocList := make([]converter.Toc, 0)
 
 	for _, item := range docs {
 		if item.ParentId == 0 {
 			toc := converter.Toc{
-				Id: item.DocumentId,
-				Link: strconv.Itoa(item.DocumentId) + ".html",
-				Pid: item.ParentId,
+				Id:    item.DocumentId,
+				Link:  strconv.Itoa(item.DocumentId) + ".html",
+				Pid:   item.ParentId,
 				Title: item.DocumentName,
 			}
 
-			tocList = append(tocList,toc)
+			tocList = append(tocList, toc)
 		}
 	}
 	for _, item := range docs {
 		if item.ParentId != 0 {
 			toc := converter.Toc{
-				Id: item.DocumentId,
-				Link: strconv.Itoa(item.DocumentId) + ".html",
-				Pid: item.ParentId,
+				Id:    item.DocumentId,
+				Link:  strconv.Itoa(item.DocumentId) + ".html",
+				Pid:   item.ParentId,
 				Title: item.DocumentName,
 			}
-			tocList = append(tocList,toc)
+			tocList = append(tocList, toc)
 		}
 	}
 
 	ebookConfig := converter.Config{
-		Charset :      "utf-8",
-		Cover :        m.Cover,
-		Timestamp :    time.Now().Format("2006-01-02 15:04:05"),
-		Description :  string(blackfriday.MarkdownBasic([]byte(m.Description))),
-		Footer :       "<p style='color:#8E8E8E;font-size:12px;'>本文档使用 <a href='https://www.iminho.me' style='text-decoration:none;color:#1abc9c;font-weight:bold;'>MinDoc</a> 构建 <span style='float:right'>- _PAGENUM_ -</span></p>",
-		Header :       "<p style='color:#8E8E8E;font-size:12px;'>_SECTION_</p>",
-		Identifier :   "",
-		Language :     "zh-CN",
-		Creator :      m.CreateName,
-		Publisher :    m.Publisher,
-		Contributor :  m.Publisher,
-		Title :        m.BookName,
-		Format:        []string{"epub", "mobi", "pdf"},
-		FontSize :     "14",
-		PaperSize :    "a4",
-		MarginLeft :   "72",
-		MarginRight :  "72",
-		MarginTop :    "72",
-		MarginBottom : "72",
-		Toc :          tocList,
-		More :         []string{},
-
+		Charset:      "utf-8",
+		Cover:        m.Cover,
+		Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
+		Description:  string(blackfriday.MarkdownBasic([]byte(m.Description))),
+		Footer:       "<p style='color:#8E8E8E;font-size:12px;'>本文档使用 <a href='https://www.iminho.me' style='text-decoration:none;color:#1abc9c;font-weight:bold;'>MinDoc</a> 构建 <span style='float:right'>- _PAGENUM_ -</span></p>",
+		Header:       "<p style='color:#8E8E8E;font-size:12px;'>_SECTION_</p>",
+		Identifier:   "",
+		Language:     "zh-CN",
+		Creator:      m.CreateName,
+		Publisher:    m.Publisher,
+		Contributor:  m.Publisher,
+		Title:        m.BookName,
+		Format:       []string{"epub", "mobi", "pdf", "docx"},
+		FontSize:     "14",
+		PaperSize:    "a4",
+		MarginLeft:   "72",
+		MarginRight:  "72",
+		MarginTop:    "72",
+		MarginBottom: "72",
+		Toc:          tocList,
+		More:         []string{},
 	}
 
-
-	os.MkdirAll(outputPath, 0766)
-	if outputPath, err = filepath.Abs(outputPath); err != nil {
+	if tempOutputPath, err = filepath.Abs(tempOutputPath); err != nil {
 		beego.Error("导出目录配置错误:" + err.Error())
-		return convertBookResult,err
+		return convertBookResult, err
 	}
 
-	viewPath := beego.BConfig.WebConfig.ViewsPath
-	baseUrl := beego.AppConfig.DefaultString("baseurl","")
-
-	for _,item := range docs {
+	for _, item := range docs {
 		name := strconv.Itoa(item.DocumentId)
-		fpath := filepath.Join(outputPath,name + ".html")
+		fpath := filepath.Join(tempOutputPath, name+".html")
 
 		f, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0777)
 		if err != nil {
-			return convertBookResult,err
+			return convertBookResult, err
 		}
 		var buf bytes.Buffer
 
-		if err := beego.ExecuteViewPathTemplate(&buf,"document/export.tpl",viewPath,map[string]interface{}{"Model": m, "Lists": item, "BaseUrl": baseUrl}); err != nil {
-			return convertBookResult,err
+		if err := beego.ExecuteViewPathTemplate(&buf, "document/export.tpl", viewPath, map[string]interface{}{"Model": m, "Lists": item, "BaseUrl": conf.BaseUrl}); err != nil {
+			return convertBookResult, err
 		}
 		html := buf.String()
 
-
 		if err != nil {
 
 			f.Close()
-			return convertBookResult,err
+			return convertBookResult, err
 		}
 
 		bufio := bytes.NewReader(buf.Bytes())
 
 		doc, err := goquery.NewDocumentFromReader(bufio)
 		doc.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
-			if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/uploads/") {
-				contentSelection.SetAttr("src", baseUrl + src)
+			if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/") {
+				//contentSelection.SetAttr("src", baseUrl + src)
+				spath := filepath.Join(conf.WorkingDirectory, src)
+
+				if ff, e := ioutil.ReadFile(spath); e == nil {
+
+					encodeString := base64.StdEncoding.EncodeToString(ff)
+
+					src = "data:image/" + filepath.Ext(src) + ";base64," + encodeString
+
+					contentSelection.SetAttr("src", src)
+				}
+
 			}
 		})
 
 		html, err = doc.Html()
 		if err != nil {
 			f.Close()
-			return convertBookResult,err
+			return convertBookResult, err
 		}
 
 		// html = strings.Replace(html, "<img src=\"/uploads", "<img src=\"" + c.BaseUrl() + "/uploads", -1)
@@ -310,35 +319,29 @@ func (m *BookResult) Converter(sessionId string) (ConvertBookResult,error) {
 		f.Close()
 	}
 	eBookConverter := &converter.Converter{
-		BasePath : outputPath,
-		Config :   ebookConfig,
-		Debug :    false,
+		BasePath: tempOutputPath,
+		Config:   ebookConfig,
+		Debug:    true,
 	}
 
-	if err := eBookConverter.Convert();err != nil {
-		beego.Error("转换文件错误:" + m.BookName +" => "+ err.Error())
-		return convertBookResult,err
+	if err := eBookConverter.Convert(); err != nil {
+		beego.Error("转换文件错误:" + m.BookName + " => " + err.Error())
+		return convertBookResult, err
 	}
-	convertBookResult.MobiPath = filepath.Join(outputPath,"output","book.mobi")
-	convertBookResult.PDFPath = filepath.Join(outputPath,"output","book.pdf")
-	convertBookResult.EpubPath = filepath.Join(outputPath,"output","book.epub")
-	return  convertBookResult,nil
+	beego.Info("文档转换完成:" + m.BookName)
+	defer func(p string) {
+		os.RemoveAll(p)
+	}(tempOutputPath)
+
+	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"))
+
+	convertBookResult.MobiPath = mobipath
+	convertBookResult.PDFPath = pdfpath
+	convertBookResult.EpubPath = epubpath
+	convertBookResult.WordPath = docxpath
+
+	return convertBookResult, nil
 }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 35 - 67
models/comment.go

@@ -1,76 +1,77 @@
 package models
 
 import (
-	"time"
-	"github.com/lifei6671/mindoc/conf"
-	"github.com/astaxie/beego/orm"
 	"errors"
+	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/mindoc/conf"
+	"time"
 )
 
 //Comment struct
 type Comment struct {
-	CommentId int			`orm:"pk;auto;unique;column(comment_id)" json:"comment_id"`
-	Floor int 			`orm:"column(floor);type(unsigned);default(0)" json:"floor"`
-	BookId int			`orm:"column(book_id);type(int)" json:"book_id"`
+	CommentId int `orm:"pk;auto;unique;column(comment_id)" json:"comment_id"`
+	Floor     int `orm:"column(floor);type(unsigned);default(0)" json:"floor"`
+	BookId    int `orm:"column(book_id);type(int)" json:"book_id"`
 	// DocumentId 评论所属的文档.
-	DocumentId int			`orm:"column(document_id);type(int)" json:"document_id"`
+	DocumentId int `orm:"column(document_id);type(int)" json:"document_id"`
 	// Author 评论作者.
-	Author string			`orm:"column(author);size(100)" json:"author"`
+	Author string `orm:"column(author);size(100)" json:"author"`
 	//MemberId 评论用户ID.
-	MemberId int			`orm:"column(member_id);type(int)" json:"member_id"`
+	MemberId int `orm:"column(member_id);type(int)" json:"member_id"`
 	// IPAddress 评论者的IP地址
-	IPAddress string		`orm:"column(ip_address);size(100)" json:"ip_address"`
+	IPAddress string `orm:"column(ip_address);size(100)" json:"ip_address"`
 	// 评论日期.
-	CommentDate time.Time		`orm:"type(datetime);column(comment_date);auto_now_add" json:"comment_date"`
+	CommentDate time.Time `orm:"type(datetime);column(comment_date);auto_now_add" json:"comment_date"`
 	//Content 评论内容.
-	Content string			`orm:"column(content);size(2000)" json:"content"`
+	Content string `orm:"column(content);size(2000)" json:"content"`
 	// Approved 评论状态:0 待审核/1 已审核/2 垃圾评论/ 3 已删除
-	Approved int			`orm:"column(approved);type(int)" json:"approved"`
+	Approved int `orm:"column(approved);type(int)" json:"approved"`
 	// UserAgent 评论者浏览器内容
-	UserAgent string		`orm:"column(user_agent);size(500)" json:"user_agent"`
+	UserAgent string `orm:"column(user_agent);size(500)" json:"user_agent"`
 	// Parent 评论所属父级
-	ParentId int			`orm:"column(parent_id);type(int);default(0)" json:"parent_id"`
-	AgreeCount int			`orm:"column(agree_count);type(int);default(0)" json:"agree_count"`
-	AgainstCount int		`orm:"column(against_count);type(int);default(0)" json:"against_count"`
+	ParentId     int `orm:"column(parent_id);type(int);default(0)" json:"parent_id"`
+	AgreeCount   int `orm:"column(agree_count);type(int);default(0)" json:"agree_count"`
+	AgainstCount int `orm:"column(against_count);type(int);default(0)" json:"against_count"`
 }
 
 // TableName 获取对应数据库表名.
 func (m *Comment) TableName() string {
 	return "comments"
 }
+
 // TableEngine 获取数据使用的引擎.
 func (m *Comment) TableEngine() string {
 	return "INNODB"
 }
 
-func (m *Comment) TableNameWithPrefix() string  {
+func (m *Comment) TableNameWithPrefix() string {
 	return conf.GetDatabasePrefix() + m.TableName()
 }
 
 func NewComment() *Comment {
 	return &Comment{}
 }
-func (m *Comment) Find(id int) (*Comment,error) {
+func (m *Comment) Find(id int) (*Comment, error) {
 	if id <= 0 {
-		return m,ErrInvalidParameter
+		return m, ErrInvalidParameter
 	}
 	o := orm.NewOrm()
-	err :=  o.Read(m)
+	err := o.Read(m)
 
-	return m,err
+	return m, err
 }
 
-func (m *Comment) Update(cols... string)  error {
+func (m *Comment) Update(cols ...string) error {
 	o := orm.NewOrm()
 
-	_,err := o.Update(m,cols...)
+	_, err := o.Update(m, cols...)
 
 	return err
 }
 
 //Insert 添加一条评论.
 func (m *Comment) Insert() error {
-	if m.DocumentId <= 0{
+	if m.DocumentId <= 0 {
 		return errors.New("评论文档不存在")
 	}
 	if m.Content == "" {
@@ -89,28 +90,28 @@ func (m *Comment) Insert() error {
 
 	document := NewDocument()
 	//如果评论的文档不存在
-	if _,err := document.Find(m.DocumentId); err != nil {
+	if _, err := document.Find(m.DocumentId); err != nil {
 		return err
 	}
-	book ,err := NewBook().Find(document.BookId);
+	book, err := NewBook().Find(document.BookId)
 	//如果评论的项目不存在
 	if err != nil {
 		return err
 	}
 	//如果已关闭评论
-	if book.CommentStatus == "closed"{
+	if book.CommentStatus == "closed" {
 		return ErrCommentClosed
 	}
-	if book.CommentStatus == "registered_only" && m.MemberId <= 0{
+	if book.CommentStatus == "registered_only" && m.MemberId <= 0 {
 		return ErrPermissionDenied
 	}
 	//如果仅参与者评论
 	if book.CommentStatus == "group_only" {
-		if m.MemberId <= 0{
+		if m.MemberId <= 0 {
 			return ErrPermissionDenied
 		}
 		rel := NewRelationship()
-		if _,err := rel.FindForRoleId(book.BookId,m.MemberId);err != nil {
+		if _, err := rel.FindForRoleId(book.BookId, m.MemberId); err != nil {
 			return ErrPermissionDenied
 		}
 	}
@@ -118,51 +119,18 @@ func (m *Comment) Insert() error {
 	if m.MemberId > 0 {
 		member := NewMember()
 		//如果用户不存在
-		if _,err := member.Find(m.MemberId) ; err != nil {
+		if _, err := member.Find(m.MemberId); err != nil {
 			return ErrMemberNoExist
 		}
 		//如果用户被禁用
 		if member.Status == 1 {
 			return ErrMemberDisabled
 		}
-	}else if m.Author == "" {
+	} else if m.Author == "" {
 		m.Author = "[匿名用户]"
 	}
 	m.BookId = book.BookId
-	_,err = o.Insert(m)
+	_, err = o.Insert(m)
 
 	return err
 }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 5 - 7
models/comment_result.go

@@ -4,11 +4,11 @@ import "github.com/astaxie/beego/orm"
 
 type CommentResult struct {
 	Comment
-	Author string		`json:"author"`
-	ReplyAccount string 	`json:"reply_account"`
+	Author       string `json:"author"`
+	ReplyAccount string `json:"reply_account"`
 }
 
-func (m *CommentResult) FindForDocumentToPager(doc_id, page_index,page_size int) (comments []*CommentResult,totalCount int,err error)  {
+func (m *CommentResult) FindForDocumentToPager(doc_id, page_index, page_size int) (comments []*CommentResult, totalCount int, err error) {
 
 	o := orm.NewOrm()
 
@@ -25,12 +25,11 @@ FROM md_comments AS comment
 
 WHERE comment.document_id = ? ORDER BY comment.comment_id DESC LIMIT 0,10`
 
-
 	offset := (page_index - 1) * page_size
 
-	_,err = o.Raw(sql1,doc_id,offset, page_size).QueryRows(&comments)
+	_, err = o.Raw(sql1, doc_id, offset, page_size).QueryRows(&comments)
 
-	v,err := o.QueryTable(m.TableNameWithPrefix()).Filter("document_id",doc_id).Count()
+	v, err := o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", doc_id).Count()
 
 	if err == nil {
 		totalCount = int(v)
@@ -38,4 +37,3 @@ WHERE comment.document_id = ? ORDER BY comment.comment_id DESC LIMIT 0,10`
 
 	return
 }
-

+ 17 - 16
models/comment_vote.go

@@ -1,30 +1,31 @@
 package models
 
 import (
-	"time"
-	"github.com/lifei6671/mindoc/conf"
 	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/mindoc/conf"
+	"time"
 )
 
 type CommentVote struct {
-	VoteId int			`orm:"column(vote_id);pk;auto;unique" json:"vote_id"`
-	CommentId int			`orm:"column(comment_id);type(int);index" json:"comment_id"`
-	CommentMemberId int 		`orm:"column(comment_member_id);type(int);index;default(0)" json:"comment_member_id"`
-	VoteMemberId int		`orm:"column(vote_member_id);type(int);index" json:"vote_member_id"`
-	VoteState int 			`orm:"column(vote_state);type(int)" json:"vote_state"`
-	CreateTime time.Time		`orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
+	VoteId          int       `orm:"column(vote_id);pk;auto;unique" json:"vote_id"`
+	CommentId       int       `orm:"column(comment_id);type(int);index" json:"comment_id"`
+	CommentMemberId int       `orm:"column(comment_member_id);type(int);index;default(0)" json:"comment_member_id"`
+	VoteMemberId    int       `orm:"column(vote_member_id);type(int);index" json:"vote_member_id"`
+	VoteState       int       `orm:"column(vote_state);type(int)" json:"vote_state"`
+	CreateTime      time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
 }
 
 // TableName 获取对应数据库表名.
 func (m *CommentVote) TableName() string {
 	return "comment_votes"
 }
+
 // TableEngine 获取数据使用的引擎.
 func (m *CommentVote) TableEngine() string {
 	return "INNODB"
 }
 
-func (m *CommentVote) TableNameWithPrefix() string  {
+func (m *CommentVote) TableNameWithPrefix() string {
 	return conf.GetDatabasePrefix() + m.TableName()
 }
 func (u *CommentVote) TableUnique() [][]string {
@@ -35,15 +36,15 @@ func (u *CommentVote) TableUnique() [][]string {
 func NewCommentVote() *CommentVote {
 	return &CommentVote{}
 }
-func (m *CommentVote) InsertOrUpdate() (*CommentVote,error)  {
+func (m *CommentVote) InsertOrUpdate() (*CommentVote, error) {
 	o := orm.NewOrm()
 
 	if m.VoteId > 0 {
-		_,err := o.Update(m)
-		return m,err
-	}else{
-		_,err := o.Insert(m)
+		_, err := o.Update(m)
+		return m, err
+	} else {
+		_, err := o.Insert(m)
 
-		return m,err
+		return m, err
 	}
-}
+}

+ 2 - 1
models/convert_book_result.go

@@ -2,7 +2,8 @@ package models
 
 // 转换结果
 type ConvertBookResult struct {
-	PDFPath string
+	PDFPath  string
 	EpubPath string
 	MobiPath string
+	WordPath string
 }

+ 10 - 10
models/dashboard.go

@@ -3,34 +3,34 @@ package models
 import "github.com/astaxie/beego/orm"
 
 type Dashboard struct {
-	BookNumber int64                `json:"book_number"`
-	DocumentNumber int64        `json:"document_number"`
-	MemberNumber int64        `json:"member_number"`
-	CommentNumber int64        `json:"comment_number"`
-	AttachmentNumber int64        `json:"attachment_number"`
+	BookNumber       int64 `json:"book_number"`
+	DocumentNumber   int64 `json:"document_number"`
+	MemberNumber     int64 `json:"member_number"`
+	CommentNumber    int64 `json:"comment_number"`
+	AttachmentNumber int64 `json:"attachment_number"`
 }
 
 func NewDashboard() *Dashboard {
 	return &Dashboard{}
 }
 
-func (m *Dashboard) Query() (*Dashboard) {
+func (m *Dashboard) Query() *Dashboard {
 	o := orm.NewOrm()
 
-	book_number,_ := o.QueryTable(NewBook().TableNameWithPrefix()).Count()
+	book_number, _ := o.QueryTable(NewBook().TableNameWithPrefix()).Count()
 
 	m.BookNumber = book_number
 
-	document_count,_ := o.QueryTable(NewDocument().TableNameWithPrefix()).Count()
+	document_count, _ := o.QueryTable(NewDocument().TableNameWithPrefix()).Count()
 	m.DocumentNumber = document_count
 
-	member_number,_ := o.QueryTable(NewMember().TableNameWithPrefix()).Count()
+	member_number, _ := o.QueryTable(NewMember().TableNameWithPrefix()).Count()
 	m.MemberNumber = member_number
 
 	//comment_number,_ := o.QueryTable(NewComment().TableNameWithPrefix()).Count()
 	m.CommentNumber = 0
 
-	attachment_number,_ := o.QueryTable(NewAttachment().TableNameWithPrefix()).Count()
+	attachment_number, _ := o.QueryTable(NewAttachment().TableNameWithPrefix()).Count()
 
 	m.AttachmentNumber = attachment_number
 

+ 12 - 7
models/document.go

@@ -9,6 +9,9 @@ import (
 	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/conf"
 	"strings"
+	"os"
+	"path/filepath"
+	"strconv"
 )
 
 // Document struct.
@@ -121,12 +124,12 @@ func (m *Document) RecursiveDocument(doc_id int) error {
 }
 
 //发布文档
-func (m *Document) ReleaseContent(book_id int) {
+func (m *Document) ReleaseContent(bookId int) {
 
 	o := orm.NewOrm()
 
 	var docs []*Document
-	_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).All(&docs, "document_id", "content")
+	_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", bookId).All(&docs, "document_id", "content")
 
 	if err != nil {
 		beego.Error("发布失败 => ", err)
@@ -134,12 +137,12 @@ func (m *Document) ReleaseContent(book_id int) {
 	}
 	for _, item := range docs {
 		item.Release = item.Content
-		attach_list, err := NewAttachment().FindListByDocumentId(item.DocumentId)
-		if err == nil && len(attach_list) > 0 {
+		attachList, err := NewAttachment().FindListByDocumentId(item.DocumentId)
+		if err == nil && len(attachList) > 0 {
 			content := bytes.NewBufferString("<div class=\"attach-list\"><strong>附件</strong><ul>")
-			for _, attach := range attach_list {
-				if strings.HasPrefix(attach.HttpPath,"/"){
-					attach.HttpPath = strings.TrimSuffix(beego.AppConfig.DefaultString("baseurl",""),"/") + attach.HttpPath
+			for _, attach := range attachList {
+				if strings.HasPrefix(attach.HttpPath, "/") {
+					attach.HttpPath = strings.TrimSuffix(beego.AppConfig.DefaultString("baseurl", ""), "/") + attach.HttpPath
 				}
 				li := fmt.Sprintf("<li><a href=\"%s\" target=\"_blank\" title=\"%s\">%s</a></li>", attach.HttpPath, attach.FileName, attach.FileName)
 
@@ -151,6 +154,8 @@ func (m *Document) ReleaseContent(book_id int) {
 		_, err = o.Update(item, "release")
 		if err != nil {
 			beego.Error(fmt.Sprintf("发布失败 => %+v", item), err)
+		}else {
+			os.RemoveAll(filepath.Join(conf.WorkingDirectory,"uploads","books",strconv.Itoa(bookId)))
 		}
 	}
 }

+ 22 - 20
models/document_history.go

@@ -23,14 +23,14 @@ type DocumentHistory struct {
 }
 
 type DocumentHistorySimpleResult struct {
-	HistoryId    int                `json:"history_id"`
-	ActionName   string             `json:"action_name"`
-	MemberId     int 		`json:"member_id"`
-	Account      string 		`json:"account"`
-	ModifyAt     int 		`json:"modify_at"`
-	ModifyName   string 		`json:"modify_name"`
-	ModifyTime   time.Time 		`json:"modify_time"`
-	Version      int64 		`json:"version"`
+	HistoryId  int       `json:"history_id"`
+	ActionName string    `json:"action_name"`
+	MemberId   int       `json:"member_id"`
+	Account    string    `json:"account"`
+	ModifyAt   int       `json:"modify_at"`
+	ModifyName string    `json:"modify_name"`
+	ModifyTime time.Time `json:"modify_time"`
+	Version    int64     `json:"version"`
 }
 
 // TableName 获取对应数据库表名.
@@ -50,12 +50,13 @@ func (m *DocumentHistory) TableNameWithPrefix() string {
 func NewDocumentHistory() *DocumentHistory {
 	return &DocumentHistory{}
 }
-func (m *DocumentHistory) Find(id int) (*DocumentHistory,error) {
+func (m *DocumentHistory) Find(id int) (*DocumentHistory, error) {
 	o := orm.NewOrm()
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id",id).One(m)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id", id).One(m)
 
-	return m,err
+	return m, err
 }
+
 //清空指定文档的历史.
 func (m *DocumentHistory) Clear(doc_id int) error {
 	o := orm.NewOrm()
@@ -66,19 +67,19 @@ func (m *DocumentHistory) Clear(doc_id int) error {
 }
 
 //删除历史.
-func (m *DocumentHistory) Delete(history_id,doc_id int) error {
+func (m *DocumentHistory) Delete(history_id, doc_id int) error {
 	o := orm.NewOrm()
 
-	_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id",history_id).Filter("document_id",doc_id).Delete()
+	_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id", history_id).Filter("document_id", doc_id).Delete()
 
 	return err
 }
 
 //恢复指定历史的文档.
-func (m *DocumentHistory) Restore(history_id,doc_id,uid int) error {
+func (m *DocumentHistory) Restore(history_id, doc_id, uid int) error {
 	o := orm.NewOrm()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id", history_id).Filter("document_id",doc_id).One(m)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id", history_id).Filter("document_id", doc_id).One(m)
 
 	if err != nil {
 		return err
@@ -113,17 +114,18 @@ func (m *DocumentHistory) Restore(history_id,doc_id,uid int) error {
 	return err
 }
 
-func (m *DocumentHistory) InsertOrUpdate() (history *DocumentHistory,err error)  {
+func (m *DocumentHistory) InsertOrUpdate() (history *DocumentHistory, err error) {
 	o := orm.NewOrm()
 	history = m
 
 	if m.HistoryId > 0 {
-		_,err = o.Update(m)
-	}else{
-		_,err = o.Insert(m)
+		_, err = o.Update(m)
+	} else {
+		_, err = o.Insert(m)
 	}
 	return
 }
+
 //分页查询指定文档的历史.
 func (m *DocumentHistory) FindToPager(doc_id, page_index, page_size int) (docs []*DocumentHistorySimpleResult, totalCount int, err error) {
 
@@ -139,7 +141,7 @@ LEFT JOIN md_members AS m1 ON history.member_id = m1.member_id
 LEFT JOIN md_members AS m2 ON history.modify_at = m2.member_id
 WHERE history.document_id = ? ORDER BY history.history_id DESC LIMIT ?,?;`
 
-	_, err = o.Raw(sql,doc_id,offset,page_size).QueryRows(&docs)
+	_, err = o.Raw(sql, doc_id, offset, page_size).QueryRows(&docs)
 
 	if err != nil {
 		return

+ 37 - 47
models/document_tree.go

@@ -1,48 +1,49 @@
 package models
 
 import (
-	"github.com/astaxie/beego/orm"
 	"bytes"
-	"strconv"
 	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
 	"html/template"
+	"math"
+	"strconv"
 )
 
 type DocumentTree struct {
-	DocumentId int               	`json:"id"`
-	DocumentName string 		`json:"text"`
-	ParentId interface{}            `json:"parent"`
-	Identify string 		`json:"identify"`
-	BookIdentify string 		`json:"-"`
-	Version int64			`json:"version"`
-	State *DocumentSelected         `json:"state,omitempty"`
+	DocumentId   int               `json:"id"`
+	DocumentName string            `json:"text"`
+	ParentId     interface{}       `json:"parent"`
+	Identify     string            `json:"identify"`
+	BookIdentify string            `json:"-"`
+	Version      int64             `json:"version"`
+	State        *DocumentSelected `json:"state,omitempty"`
 }
 type DocumentSelected struct {
-	Selected bool        	`json:"selected"`
-	Opened bool        	`json:"opened"`
+	Selected bool `json:"selected"`
+	Opened   bool `json:"opened"`
 }
 
 //获取项目的文档树状结构
-func (m *Document) FindDocumentTree(book_id int) ([]*DocumentTree,error){
+func (m *Document) FindDocumentTree(book_id int) ([]*DocumentTree, error) {
 	o := orm.NewOrm()
 
-	trees := make([]*DocumentTree,0)
+	trees := make([]*DocumentTree, 0)
 
 	var docs []*Document
 
-	count ,err := o.QueryTable(m).Filter("book_id",book_id).OrderBy("order_sort","document_id").All(&docs,"document_id","version","document_name","parent_id","identify")
+	count, err := o.QueryTable(m).Filter("book_id", book_id).OrderBy("order_sort", "document_id").Limit(math.MaxInt32).All(&docs, "document_id", "version", "document_name", "parent_id", "identify")
 
 	if err != nil {
-		return trees,err
+		return trees, err
 	}
-	book,_ := NewBook().Find(book_id)
+	book, _ := NewBook().Find(book_id)
 
-	trees = make([]*DocumentTree,count)
+	trees = make([]*DocumentTree, count)
 
-	for index,item := range docs {
+	for index, item := range docs {
 		tree := &DocumentTree{}
-		if index == 0{
-			tree.State = &DocumentSelected{ Selected: true, Opened: true }
+		if index == 0 {
+			tree.State = &DocumentSelected{Selected: true, Opened: true}
 		}
 		tree.DocumentId = item.DocumentId
 		tree.Identify = item.Identify
@@ -50,7 +51,7 @@ func (m *Document) FindDocumentTree(book_id int) ([]*DocumentTree,error){
 		tree.BookIdentify = book.Identify
 		if item.ParentId > 0 {
 			tree.ParentId = item.ParentId
-		}else{
+		} else {
 			tree.ParentId = "#"
 		}
 
@@ -59,41 +60,41 @@ func (m *Document) FindDocumentTree(book_id int) ([]*DocumentTree,error){
 		trees[index] = tree
 	}
 
-	return trees,nil
+	return trees, nil
 }
 
-func (m *Document) CreateDocumentTreeForHtml(book_id, selected_id int) (string,error) {
-	trees,err := m.FindDocumentTree(book_id)
+func (m *Document) CreateDocumentTreeForHtml(book_id, selected_id int) (string, error) {
+	trees, err := m.FindDocumentTree(book_id)
 	if err != nil {
-		return "",err
+		return "", err
 	}
-	parent_id := getSelectedNode(trees,selected_id)
+	parent_id := getSelectedNode(trees, selected_id)
 
 	buf := bytes.NewBufferString("")
 
-	getDocumentTree(trees,0,selected_id,parent_id,buf)
+	getDocumentTree(trees, 0, selected_id, parent_id, buf)
+
+	return buf.String(), nil
 
-	return buf.String(),nil
-	
 }
 
 //使用递归的方式获取指定ID的顶级ID
 func getSelectedNode(array []*DocumentTree, parent_id int) int {
 
-	for _,item := range array {
-		if _,ok := item.ParentId.(string); ok && item.DocumentId == parent_id  {
+	for _, item := range array {
+		if _, ok := item.ParentId.(string); ok && item.DocumentId == parent_id {
 			return item.DocumentId
-		}else if pid,ok := item.ParentId.(int); ok  && item.DocumentId == parent_id{
-			return getSelectedNode(array,pid)
+		} else if pid, ok := item.ParentId.(int); ok && item.DocumentId == parent_id {
+			return getSelectedNode(array, pid)
 		}
 	}
-	return  0
+	return 0
 }
 
-func getDocumentTree(array []*DocumentTree,parent_id int,selected_id int,selected_parent_id int,buf *bytes.Buffer)   {
+func getDocumentTree(array []*DocumentTree, parent_id int, selected_id int, selected_parent_id int, buf *bytes.Buffer) {
 	buf.WriteString("<ul>")
 
-	for _,item := range array {
+	for _, item := range array {
 		pid := 0
 
 		if p, ok := item.ParentId.(int); ok {
@@ -138,14 +139,3 @@ func getDocumentTree(array []*DocumentTree,parent_id int,selected_id int,selecte
 	}
 	buf.WriteString("</ul>")
 }
-
-
-
-
-
-
-
-
-
-
-

+ 9 - 9
models/errors.go

@@ -5,16 +5,16 @@ import "errors"
 
 var (
 	// ErrMemberNoExist 用户不存在.
-	ErrMemberNoExist  = errors.New("用户不存在")
-	ErrMemberExist  = errors.New("用户已存在")
-	ErrMemberDisabled = errors.New("用户被禁用")
-	ErrMemberEmailEmpty = errors.New("用户邮箱不能为空")
-	ErrMemberEmailExist = errors.New("用户邮箱已被使用")
-	ErrMemberDescriptionTooLong = errors.New("用户描述必须小于500字")
-	ErrMemberEmailFormatError = errors.New("邮箱格式不正确")
+	ErrMemberNoExist             = errors.New("用户不存在")
+	ErrMemberExist               = errors.New("用户已存在")
+	ErrMemberDisabled            = errors.New("用户被禁用")
+	ErrMemberEmailEmpty          = errors.New("用户邮箱不能为空")
+	ErrMemberEmailExist          = errors.New("用户邮箱已被使用")
+	ErrMemberDescriptionTooLong  = errors.New("用户描述必须小于500字")
+	ErrMemberEmailFormatError    = errors.New("邮箱格式不正确")
 	ErrMemberPasswordFormatError = errors.New("密码必须在6-50个字符之间")
-	ErrMemberAccountFormatError = errors.New("账号只能由英文字母数字组成,且在3-50个字符")
-	ErrMemberRoleError = errors.New("用户权限不正确")
+	ErrMemberAccountFormatError  = errors.New("账号只能由英文字母数字组成,且在3-50个字符")
+	ErrMemberRoleError           = errors.New("用户权限不正确")
 	// ErrorMemberPasswordError 密码错误.
 	ErrorMemberPasswordError = errors.New("用户密码错误")
 	//ErrorMemberAuthMethodInvalid 不支持此认证方式

+ 18 - 18
models/label.go

@@ -1,35 +1,36 @@
 package models
 
 import (
-	"github.com/lifei6671/mindoc/conf"
 	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/mindoc/conf"
 	"strings"
 )
 
 type Label struct {
-	LabelId int		`orm:"column(label_id);pk;auto;unique;" json:"label_id"`
-	LabelName string	`orm:"column(label_name);size(50);unique" json:"label_name"`
-	BookNumber int		`orm:"column(book_number)" json:"book_number"`
+	LabelId    int    `orm:"column(label_id);pk;auto;unique;" json:"label_id"`
+	LabelName  string `orm:"column(label_name);size(50);unique" json:"label_name"`
+	BookNumber int    `orm:"column(book_number)" json:"book_number"`
 }
 
 // TableName 获取对应数据库表名.
 func (m *Label) TableName() string {
 	return "label"
 }
+
 // TableEngine 获取数据使用的引擎.
 func (m *Label) TableEngine() string {
 	return "INNODB"
 }
 
-func (m *Label)TableNameWithPrefix() string {
-	return conf.GetDatabasePrefix() +  m.TableName()
+func (m *Label) TableNameWithPrefix() string {
+	return conf.GetDatabasePrefix() + m.TableName()
 }
 
 func NewLabel() *Label {
 	return &Label{}
 }
 
-func (m *Label) FindFirst(field string, value interface{}) (*Label,error){
+func (m *Label) FindFirst(field string, value interface{}) (*Label, error) {
 	o := orm.NewOrm()
 
 	err := o.QueryTable(m.TableNameWithPrefix()).Filter(field, value).One(m)
@@ -41,27 +42,27 @@ func (m *Label) FindFirst(field string, value interface{}) (*Label,error){
 func (m *Label) InsertOrUpdate(labelName string) error {
 	o := orm.NewOrm()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("label_name",labelName).One(m)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("label_name", labelName).One(m)
 	if err != nil && err != orm.ErrNoRows {
 		return err
 	}
-	count,_ := o.QueryTable(NewBook().TableNameWithPrefix()).Filter("label__icontains",labelName).Count()
+	count, _ := o.QueryTable(NewBook().TableNameWithPrefix()).Filter("label__icontains", labelName).Count()
 	m.BookNumber = int(count)
 	m.LabelName = labelName
 
 	if err == orm.ErrNoRows {
 		err = nil
 		m.LabelName = labelName
-		_,err = o.Insert(m)
-	}else{
-		_,err = o.Update(m)
+		_, err = o.Insert(m)
+	} else {
+		_, err = o.Update(m)
 	}
 	return err
 }
 
 //批量插入或更新标签.
-func (m *Label) InsertOrUpdateMulti(labels string)  {
-	if  labels != "" {
+func (m *Label) InsertOrUpdateMulti(labels string) {
+	if labels != "" {
 		labelArray := strings.Split(labels, ",")
 
 		for _, label := range labelArray {
@@ -73,10 +74,10 @@ func (m *Label) InsertOrUpdateMulti(labels string)  {
 }
 
 //分页查找标签.
-func (m *Label) FindToPager(pageIndex, pageSize int) (labels []*Label,totalCount int,err error) {
+func (m *Label) FindToPager(pageIndex, pageSize int) (labels []*Label, totalCount int, err error) {
 	o := orm.NewOrm()
 
-	count,err := o.QueryTable(m.TableNameWithPrefix()).Count()
+	count, err := o.QueryTable(m.TableNameWithPrefix()).Count()
 
 	if err != nil {
 		return
@@ -85,8 +86,7 @@ func (m *Label) FindToPager(pageIndex, pageSize int) (labels []*Label,totalCount
 
 	offset := (pageIndex - 1) * pageSize
 
-	_,err = o.QueryTable(m.TableNameWithPrefix()).OrderBy("-book_number").Offset(offset).Limit(pageSize).All(&labels)
+	_, err = o.QueryTable(m.TableNameWithPrefix()).OrderBy("-book_number").Offset(offset).Limit(pageSize).All(&labels)
 
 	return
 }
-

+ 20 - 32
models/logs.go

@@ -1,38 +1,39 @@
 package models
 
 import (
-	"time"
+	"errors"
+	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/conf"
 	"sync/atomic"
-	"github.com/astaxie/beego/orm"
-	"errors"
+	"time"
 )
 
-var loggerQueue = &logQueue{ channel : make(chan *Logger,100),isRuning : 0 }
+var loggerQueue = &logQueue{channel: make(chan *Logger, 100), isRuning: 0}
 
 type logQueue struct {
-	channel chan *Logger
+	channel  chan *Logger
 	isRuning int32
 }
 
 // Logger struct .
 type Logger struct {
-	LoggerId int64		`orm:"pk;auto;unique;column(log_id)" json:"log_id"`
-	MemberId int		`orm:"column(member_id);type(int)" json:"member_id"`
+	LoggerId int64 `orm:"pk;auto;unique;column(log_id)" json:"log_id"`
+	MemberId int   `orm:"column(member_id);type(int)" json:"member_id"`
 	// 日志类别:operate 操作日志/ system 系统日志/ exception 异常日志 / document 文档操作日志
-	Category string		`orm:"column(category);size(255);default(operate)" json:"category"`
-	Content string		`orm:"column(content);type(text)" json:"content"`
-	OriginalData string	`orm:"column(original_data);type(text)" json:"original_data"`
-	PresentData string	`orm:"column(present_data);type(text)" json:"present_data"`
-	CreateTime time.Time	`orm:"type(datetime);column(create_time);auto_now_add" json:"create_time"`
-	UserAgent string	`orm:"column(user_agent);size(500)" json:"user_agent"`
-	IPAddress string	`orm:"column(ip_address);size(255)" json:"ip_address"`
+	Category     string    `orm:"column(category);size(255);default(operate)" json:"category"`
+	Content      string    `orm:"column(content);type(text)" json:"content"`
+	OriginalData string    `orm:"column(original_data);type(text)" json:"original_data"`
+	PresentData  string    `orm:"column(present_data);type(text)" json:"present_data"`
+	CreateTime   time.Time `orm:"type(datetime);column(create_time);auto_now_add" json:"create_time"`
+	UserAgent    string    `orm:"column(user_agent);size(500)" json:"user_agent"`
+	IPAddress    string    `orm:"column(ip_address);size(255)" json:"ip_address"`
 }
 
 // TableName 获取对应数据库表名.
 func (m *Logger) TableName() string {
 	return "logs"
 }
+
 // TableEngine 获取数据使用的引擎.
 func (m *Logger) TableEngine() string {
 	return "INNODB"
@@ -57,32 +58,19 @@ func (m *Logger) Add() error {
 	}
 	loggerQueue.channel <- m
 	if atomic.LoadInt32(&(loggerQueue.isRuning)) <= 0 {
-		atomic.AddInt32(&(loggerQueue.isRuning),1)
+		atomic.AddInt32(&(loggerQueue.isRuning), 1)
 		go addLoggerAsync()
 	}
 	return nil
 }
 
-func addLoggerAsync()  {
-	defer atomic.AddInt32(&(loggerQueue.isRuning),-1)
+func addLoggerAsync() {
+	defer atomic.AddInt32(&(loggerQueue.isRuning), -1)
 	o := orm.NewOrm()
 
-	for{
-		logger := <- loggerQueue.channel
+	for {
+		logger := <-loggerQueue.channel
 
 		o.Insert(logger)
 	}
 }
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 29 - 45
models/member.go

@@ -15,6 +15,7 @@ import (
 	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/conf"
 	"github.com/lifei6671/mindoc/utils"
+	"math"
 )
 
 type Member struct {
@@ -108,7 +109,7 @@ func (m *Member) ldapLogin(account string, password string) (*Member, error) {
 		beego.AppConfig.String("ldap_base"),
 		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 		//修改objectClass通过配置文件获取值
-		fmt.Sprintf("(&(%s)(%s=%s))",beego.AppConfig.String("ldap_filter"), beego.AppConfig.String("ldap_attribute"), account),
+		fmt.Sprintf("(&(%s)(%s=%s))", beego.AppConfig.String("ldap_filter"), beego.AppConfig.String("ldap_attribute"), account),
 		[]string{"dn", "mail"},
 		nil,
 	)
@@ -279,7 +280,7 @@ func (m *Member) Valid(is_hash_password bool) error {
 		return ErrMemberEmailEmpty
 	}
 	//用户描述必须小于500字
-	if strings.Count(m.Description,"") > 500 {
+	if strings.Count(m.Description, "") > 500 {
 		return ErrMemberDescriptionTooLong
 	}
 	if m.Role != conf.MemberGeneralRole && m.Role != conf.MemberSuperRole && m.Role != conf.MemberAdminRole {
@@ -289,48 +290,47 @@ func (m *Member) Valid(is_hash_password bool) error {
 		m.Status = 0
 	}
 	//邮箱格式校验
-	if  ok,err := regexp.MatchString(conf.RegexpEmail,m.Email); !ok || err != nil || m.Email == "" {
+	if ok, err := regexp.MatchString(conf.RegexpEmail, m.Email); !ok || err != nil || m.Email == "" {
 		return ErrMemberEmailFormatError
 	}
 	//如果是未加密密码,需要校验密码格式
 	if !is_hash_password {
-		if  l := strings.Count(m.Password,"") ; m.Password == "" || l > 50 || l < 6{
+		if l := strings.Count(m.Password, ""); m.Password == "" || l > 50 || l < 6 {
 			return ErrMemberPasswordFormatError
 		}
 	}
 	//校验邮箱是否呗使用
-	if member,err := NewMember().FindByFieldFirst("email",m.Account); err == nil && member.MemberId > 0 {
+	if member, err := NewMember().FindByFieldFirst("email", m.Account); err == nil && member.MemberId > 0 {
 		if m.MemberId > 0 && m.MemberId != member.MemberId {
 			return ErrMemberEmailExist
 		}
-		if m.MemberId <= 0{
-			return  ErrMemberEmailExist
+		if m.MemberId <= 0 {
+			return ErrMemberEmailExist
 		}
 	}
 
-	if m.MemberId > 0{
+	if m.MemberId > 0 {
 		//校验用户是否存在
-		if _,err := NewMember().Find(m.MemberId);err != nil {
+		if _, err := NewMember().Find(m.MemberId); err != nil {
 			return err
 		}
-	}else{
+	} else {
 		//校验账号格式是否正确
-		if ok,err := regexp.MatchString(conf.RegexpAccount,m.Account); m.Account == "" || !ok || err != nil {
+		if ok, err := regexp.MatchString(conf.RegexpAccount, m.Account); m.Account == "" || !ok || err != nil {
 			return ErrMemberAccountFormatError
 		}
 		//校验账号是否被使用
-		if member,err := NewMember().FindByAccount(m.Account); err == nil && member.MemberId > 0 {
+		if member, err := NewMember().FindByAccount(m.Account); err == nil && member.MemberId > 0 {
 			return ErrMemberExist
 		}
 	}
 
-
 	return nil
 }
 
 //删除一个用户.
 
-func (m *Member) Delete(oldId int,newId int) error {
+func (m *Member) Delete(oldId int, newId int) error {
 	o := orm.NewOrm()
 
 	err := o.Begin()
@@ -339,39 +339,39 @@ func (m *Member) Delete(oldId int,newId int) error {
 		return err
 	}
 
-	_,err = o.Raw("DELETE FROM md_members WHERE member_id = ?",oldId).Exec()
+	_, err = o.Raw("DELETE FROM md_members WHERE member_id = ?", oldId).Exec()
 	if err != nil {
 		o.Rollback()
 		return err
 	}
-	_,err = o.Raw("UPDATE md_attachment SET `create_at` = ? WHERE `create_at` = ?",newId,oldId).Exec()
+	_, err = o.Raw("UPDATE md_attachment SET `create_at` = ? WHERE `create_at` = ?", newId, oldId).Exec()
 
 	if err != nil {
 		o.Rollback()
 		return err
 	}
 
-	_,err = o.Raw("UPDATE md_books SET member_id = ? WHERE member_id = ?",newId,oldId).Exec()
+	_, err = o.Raw("UPDATE md_books SET member_id = ? WHERE member_id = ?", newId, oldId).Exec()
 	if err != nil {
 		o.Rollback()
 		return err
 	}
-	_,err = o.Raw("UPDATE md_document_history SET member_id=? WHERE member_id = ?",newId,oldId).Exec()
+	_, err = o.Raw("UPDATE md_document_history SET member_id=? WHERE member_id = ?", newId, oldId).Exec()
 	if err != nil {
 		o.Rollback()
 		return err
 	}
-	_,err = o.Raw("UPDATE md_document_history SET modify_at=? WHERE modify_at = ?",newId,oldId).Exec()
+	_, err = o.Raw("UPDATE md_document_history SET modify_at=? WHERE modify_at = ?", newId, oldId).Exec()
 	if err != nil {
 		o.Rollback()
 		return err
 	}
-	_,err = o.Raw("UPDATE md_documents SET member_id = ? WHERE member_id = ?;",newId,oldId).Exec()
+	_, err = o.Raw("UPDATE md_documents SET member_id = ? WHERE member_id = ?;", newId, oldId).Exec()
 	if err != nil {
 		o.Rollback()
 		return err
 	}
-	_,err = o.Raw("UPDATE md_documents SET modify_at = ? WHERE modify_at = ?",newId,oldId).Exec()
+	_, err = o.Raw("UPDATE md_documents SET modify_at = ? WHERE modify_at = ?", newId, oldId).Exec()
 	if err != nil {
 		o.Rollback()
 		return err
@@ -386,53 +386,37 @@ func (m *Member) Delete(oldId int,newId int) error {
 	//}
 	var relationship_list []*Relationship
 
-	_,err = o.QueryTable(NewRelationship().TableNameWithPrefix()).Filter("member_id",oldId).All(&relationship_list)
+	_, err = o.QueryTable(NewRelationship().TableNameWithPrefix()).Filter("member_id", oldId).Limit(math.MaxInt32).All(&relationship_list)
 
 	if err == nil {
-		for _,relationship := range relationship_list {
+		for _, relationship := range relationship_list {
 			//如果存在创始人,则删除
 			if relationship.RoleId == 0 {
 				rel := NewRelationship()
 
-				err = o.QueryTable(relationship.TableNameWithPrefix()).Filter("book_id",relationship.BookId).Filter("member_id",newId).One(rel)
+				err = o.QueryTable(relationship.TableNameWithPrefix()).Filter("book_id", relationship.BookId).Filter("member_id", newId).One(rel)
 				if err == nil {
-					if _,err := o.Delete(relationship) ; err != nil{
+					if _, err := o.Delete(relationship); err != nil {
 						beego.Error(err)
 					}
 					relationship.RelationshipId = rel.RelationshipId
 				}
 				relationship.MemberId = newId
 				relationship.RoleId = 0
-				if _,err := o.Update(relationship) ; err != nil{
+				if _, err := o.Update(relationship); err != nil {
 					beego.Error(err)
 				}
-			}else{
-				if _,err := o.Delete(relationship) ; err != nil{
+			} else {
+				if _, err := o.Delete(relationship); err != nil {
 					beego.Error(err)
 				}
 			}
 		}
 	}
 
-	if err = o.Commit();err != nil {
+	if err = o.Commit(); err != nil {
 		o.Rollback()
 		return err
 	}
 	return nil
 }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 26 - 32
models/member_result.go

@@ -1,27 +1,27 @@
 package models
 
 import (
-	"time"
 	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/mindoc/conf"
+	"time"
 )
 
 type MemberRelationshipResult struct {
-	MemberId int		`json:"member_id"`
-	Account string 		`json:"account"`
-	Description string	`json:"description"`
-	Email string 		`json:"email"`
-	Phone string 		`json:"phone"`
-	Avatar string 		`json:"avatar"`
-	Role int		`json:"role"`	//用户角色:0 管理员/ 1 普通用户
-	Status int 		`json:"status"`	//用户状态:0 正常/1 禁用
-	CreateTime time.Time	`json:"create_time"`
-	CreateAt int		`json:"create_at"`
-	RelationshipId int      `json:"relationship_id"`
-	BookId int		`json:"book_id"`
+	MemberId       int       `json:"member_id"`
+	Account        string    `json:"account"`
+	Description    string    `json:"description"`
+	Email          string    `json:"email"`
+	Phone          string    `json:"phone"`
+	Avatar         string    `json:"avatar"`
+	Role           int       `json:"role"`   //用户角色:0 管理员/ 1 普通用户
+	Status         int       `json:"status"` //用户状态:0 正常/1 禁用
+	CreateTime     time.Time `json:"create_time"`
+	CreateAt       int       `json:"create_at"`
+	RelationshipId int       `json:"relationship_id"`
+	BookId         int       `json:"book_id"`
 	// RoleId 角色:0 创始人(创始人不能被移除) / 1 管理员/2 编辑者/3 观察者
-	RoleId int		`json:"role_id"`
-	RoleName string		`json:"role_name"`
+	RoleId   int    `json:"role_id"`
+	RoleName string `json:"role_name"`
 }
 
 func NewMemberRelationshipResult() *MemberRelationshipResult {
@@ -43,18 +43,18 @@ func (m *MemberRelationshipResult) FromMember(member *Member) *MemberRelationshi
 	return m
 }
 
-func (m *MemberRelationshipResult) ResolveRoleName () *MemberRelationshipResult {
+func (m *MemberRelationshipResult) ResolveRoleName() *MemberRelationshipResult {
 	if m.RoleId == conf.BookAdmin {
 		m.RoleName = "管理者"
-	}else if m.RoleId == conf.BookEditor {
+	} else if m.RoleId == conf.BookEditor {
 		m.RoleName = "编辑者"
-	}else if m.RoleId == conf.BookObserver {
+	} else if m.RoleId == conf.BookObserver {
 		m.RoleName = "观察者"
 	}
 	return m
 }
 
-func (m *MemberRelationshipResult) FindForUsersByBookId(book_id ,pageIndex, pageSize int) ([]*MemberRelationshipResult,int,error) {
+func (m *MemberRelationshipResult) FindForUsersByBookId(book_id, pageIndex, pageSize int) ([]*MemberRelationshipResult, int, error) {
 	o := orm.NewOrm()
 
 	var members []*MemberRelationshipResult
@@ -65,28 +65,22 @@ func (m *MemberRelationshipResult) FindForUsersByBookId(book_id ,pageIndex, page
 
 	var total_count int
 
-	err := o.Raw(sql2,book_id).QueryRow(&total_count)
+	err := o.Raw(sql2, book_id).QueryRow(&total_count)
 
 	if err != nil {
-		return members,0,err
+		return members, 0, err
 	}
 
-	offset := (pageIndex-1) * pageSize
+	offset := (pageIndex - 1) * pageSize
 
-	_,err = o.Raw(sql1,book_id,offset,pageSize).QueryRows(&members)
+	_, err = o.Raw(sql1, book_id, offset, pageSize).QueryRows(&members)
 
 	if err != nil {
-		return members,0,err
+		return members, 0, err
 	}
 
-	for _,item := range members {
+	for _, item := range members {
 		item.ResolveRoleName()
 	}
-	return members,total_count,nil
+	return members, total_count, nil
 }
-
-
-
-
-
-

+ 25 - 25
models/member_token.go

@@ -1,66 +1,66 @@
 package models
 
 import (
-	"time"
-	"github.com/lifei6671/mindoc/conf"
 	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/mindoc/conf"
+	"time"
 )
 
 type MemberToken struct {
-	TokenId int		`orm:"column(token_id);pk;auto;unique" json:"token_id"`
-	MemberId int		`orm:"column(member_id);type(int)" json:"member_id"`
-	Token string		`orm:"column(token);size(150);index" json:"token"`
-	Email string 		`orm:"column(email);size(255)" json:"email"`
-	IsValid bool		`orm:"column(is_valid)" json:"is_valid"`
-	ValidTime time.Time	`orm:"column(valid_time);null" json:"valid_time"`
-	SendTime time.Time	`orm:"column(send_time);auto_now_add;type(datetime)" json:"send_time"`
+	TokenId   int       `orm:"column(token_id);pk;auto;unique" json:"token_id"`
+	MemberId  int       `orm:"column(member_id);type(int)" json:"member_id"`
+	Token     string    `orm:"column(token);size(150);index" json:"token"`
+	Email     string    `orm:"column(email);size(255)" json:"email"`
+	IsValid   bool      `orm:"column(is_valid)" json:"is_valid"`
+	ValidTime time.Time `orm:"column(valid_time);null" json:"valid_time"`
+	SendTime  time.Time `orm:"column(send_time);auto_now_add;type(datetime)" json:"send_time"`
 }
 
-
 // TableName 获取对应数据库表名.
 func (m *MemberToken) TableName() string {
 	return "member_token"
 }
+
 // TableEngine 获取数据使用的引擎.
 func (m *MemberToken) TableEngine() string {
 	return "INNODB"
 }
 
-func (m *MemberToken)TableNameWithPrefix() string {
-	return conf.GetDatabasePrefix() +  m.TableName()
+func (m *MemberToken) TableNameWithPrefix() string {
+	return conf.GetDatabasePrefix() + m.TableName()
 }
 
 func NewMemberToken() *MemberToken {
 	return &MemberToken{}
 }
 
-func (m *MemberToken)  InsertOrUpdate() (*MemberToken,error){
+func (m *MemberToken) InsertOrUpdate() (*MemberToken, error) {
 	o := orm.NewOrm()
 
 	if m.TokenId > 0 {
-		_,err := o.Update(m)
-		return m,err
+		_, err := o.Update(m)
+		return m, err
 	}
-	_,err := o.Insert(m)
+	_, err := o.Insert(m)
 
-	return m,err
+	return m, err
 }
 
-func (m *MemberToken) FindByFieldFirst(field string,value interface{}) (*MemberToken,error) {
+func (m *MemberToken) FindByFieldFirst(field string, value interface{}) (*MemberToken, error) {
 	o := orm.NewOrm()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter(field,value).OrderBy("-token_id").One(m)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter(field, value).OrderBy("-token_id").One(m)
 
-	return m,err
+	return m, err
 }
 
-func (m *MemberToken) FindSendCount(mail string,start_time time.Time,end_time time.Time) (int ,error) {
+func (m *MemberToken) FindSendCount(mail string, start_time time.Time, end_time time.Time) (int, error) {
 	o := orm.NewOrm()
 
-	c,err := o.QueryTable(m.TableNameWithPrefix()).Filter("send_time__gte",start_time.Format("2006-01-02 15:04:05")).Filter("send_time__lte",end_time.Format("2006-01-02 15:04:05")).Count()
+	c, err := o.QueryTable(m.TableNameWithPrefix()).Filter("send_time__gte", start_time.Format("2006-01-02 15:04:05")).Filter("send_time__lte", end_time.Format("2006-01-02 15:04:05")).Count()
 
 	if err != nil {
-		return 0,err
+		return 0, err
 	}
-	return int(c),nil
-}
+	return int(c), nil
+}

+ 11 - 11
models/migrations.go

@@ -1,18 +1,18 @@
 package models
 
 import (
-	"time"
-	"github.com/lifei6671/mindoc/conf"
 	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/mindoc/conf"
+	"time"
 )
 
 type Migration struct {
-	MigrationId int		`orm:"column(migration_id);pk;auto;unique;" json:"migration_id"`
-	Name string		`orm:"column(name);size(500)" json:"name"`
-	Statements string	`orm:"column(statements);type(text);null" json:"statements"`
-	Status string		`orm:"column(status);default(update)" json:"status"`
-	CreateTime time.Time	`orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
-	Version int64 		`orm:"type(bigint);column(version);unique" json:"version"`
+	MigrationId int       `orm:"column(migration_id);pk;auto;unique;" json:"migration_id"`
+	Name        string    `orm:"column(name);size(500)" json:"name"`
+	Statements  string    `orm:"column(statements);type(text);null" json:"statements"`
+	Status      string    `orm:"column(status);default(update)" json:"status"`
+	CreateTime  time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
+	Version     int64     `orm:"type(bigint);column(version);unique" json:"version"`
 }
 
 // TableName 获取对应数据库表名.
@@ -33,10 +33,10 @@ func NewMigration() *Migration {
 	return &Migration{}
 }
 
-func (m *Migration) FindFirst() (*Migration,error) {
+func (m *Migration) FindFirst() (*Migration, error) {
 	o := orm.NewOrm()
 
 	err := o.QueryTable(m.TableNameWithPrefix()).OrderBy("-migration_id").One(m)
 
-	return m,err
-}
+	return m, err
+}

+ 40 - 42
models/options.go

@@ -5,147 +5,145 @@ import (
 	"github.com/lifei6671/mindoc/conf"
 )
 
-
 // Option struct .
 type Option struct {
-	OptionId int		`orm:"column(option_id);pk;auto;unique;" json:"option_id"`
-	OptionTitle string	`orm:"column(option_title);size(500)" json:"option_title"`
-	OptionName string	`orm:"column(option_name);unique;size(80)" json:"option_name"`
-	OptionValue string	`orm:"column(option_value);type(text);null" json:"option_value"`
-	Remark string		`orm:"column(remark);type(text);null" json:"remark"`
+	OptionId    int    `orm:"column(option_id);pk;auto;unique;" json:"option_id"`
+	OptionTitle string `orm:"column(option_title);size(500)" json:"option_title"`
+	OptionName  string `orm:"column(option_name);unique;size(80)" json:"option_name"`
+	OptionValue string `orm:"column(option_value);type(text);null" json:"option_value"`
+	Remark      string `orm:"column(remark);type(text);null" json:"remark"`
 }
 
 // TableName 获取对应数据库表名.
 func (m *Option) TableName() string {
 	return "options"
 }
+
 // TableEngine 获取数据使用的引擎.
 func (m *Option) TableEngine() string {
 	return "INNODB"
 }
 
-func (m *Option)TableNameWithPrefix() string {
-	return conf.GetDatabasePrefix() +  m.TableName()
+func (m *Option) TableNameWithPrefix() string {
+	return conf.GetDatabasePrefix() + m.TableName()
 }
 
-
-func NewOption() *Option  {
+func NewOption() *Option {
 	return &Option{}
 }
 
-func (p *Option) Find(id int) (*Option,error) {
+func (p *Option) Find(id int) (*Option, error) {
 	o := orm.NewOrm()
 
 	p.OptionId = id
 
-	if err := o.Read(p);err != nil {
-		return p,err
+	if err := o.Read(p); err != nil {
+		return p, err
 	}
-	return  p,nil
+	return p, nil
 }
 
-func (p *Option) FindByKey(key string) (*Option,error) {
+func (p *Option) FindByKey(key string) (*Option, error) {
 	o := orm.NewOrm()
 
 	p.OptionName = key
-	if err := o.Read(p);err != nil {
-		return p,err
+	if err := o.Read(p); err != nil {
+		return p, err
 	}
-	return  p,nil
+	return p, nil
 }
 
 func GetOptionValue(key, def string) string {
 
-	if option,err := NewOption().FindByKey(key); err == nil {
+	if option, err := NewOption().FindByKey(key); err == nil {
 		return option.OptionValue
 	}
 	return def
 }
 
-func (p *Option) InsertOrUpdate() error  {
+func (p *Option) InsertOrUpdate() error {
 
 	o := orm.NewOrm()
 
 	var err error
 
-
-	if p.OptionId > 0 || o.QueryTable(p.TableNameWithPrefix()).Filter("option_name",p.OptionName).Exist() {
-		_,err = o.Update(p)
-	}else{
-		_,err = o.Insert(p)
+	if p.OptionId > 0 || o.QueryTable(p.TableNameWithPrefix()).Filter("option_name", p.OptionName).Exist() {
+		_, err = o.Update(p)
+	} else {
+		_, err = o.Insert(p)
 	}
 	return err
 }
 
-func (p *Option) InsertMulti(option... Option )  (error){
+func (p *Option) InsertMulti(option ...Option) error {
 
 	o := orm.NewOrm()
 
-	_,err := o.InsertMulti(len(option),option)
+	_, err := o.InsertMulti(len(option), option)
 	return err
 }
 
-func (p *Option) All() ([]*Option,error)  {
+func (p *Option) All() ([]*Option, error) {
 	o := orm.NewOrm()
 	var options []*Option
 
-	_,err := o.QueryTable(p.TableNameWithPrefix()).All(&options)
+	_, err := o.QueryTable(p.TableNameWithPrefix()).All(&options)
 
 	if err != nil {
-		return options,err
+		return options, err
 	}
-	return options,nil
+	return options, nil
 }
 
 func (m *Option) Init() error {
 
 	o := orm.NewOrm()
 
-	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name","ENABLED_REGISTER").Exist() {
+	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name", "ENABLED_REGISTER").Exist() {
 		option := NewOption()
 		option.OptionValue = "false"
 		option.OptionName = "ENABLED_REGISTER"
 		option.OptionTitle = "是否启用注册"
-		if _,err := o.Insert(option);err != nil {
+		if _, err := o.Insert(option); err != nil {
 			return err
 		}
 	}
-	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name","ENABLE_DOCUMENT_HISTORY").Exist() {
+	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name", "ENABLE_DOCUMENT_HISTORY").Exist() {
 		option := NewOption()
 		option.OptionValue = "true"
 		option.OptionName = "ENABLE_DOCUMENT_HISTORY"
 		option.OptionTitle = "是否启用文档历史"
-		if _,err := o.Insert(option);err != nil {
+		if _, err := o.Insert(option); err != nil {
 			return err
 		}
 	}
-	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name","ENABLED_CAPTCHA").Exist() {
+	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name", "ENABLED_CAPTCHA").Exist() {
 		option := NewOption()
 		option.OptionValue = "true"
 		option.OptionName = "ENABLED_CAPTCHA"
 		option.OptionTitle = "是否启用验证码"
-		if _,err := o.Insert(option);err != nil {
+		if _, err := o.Insert(option); err != nil {
 			return err
 		}
 	}
-	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name","ENABLE_ANONYMOUS").Exist() {
+	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name", "ENABLE_ANONYMOUS").Exist() {
 		option := NewOption()
 		option.OptionValue = "false"
 		option.OptionName = "ENABLE_ANONYMOUS"
 		option.OptionTitle = "启用匿名访问"
-		if _,err := o.Insert(option);err != nil {
+		if _, err := o.Insert(option); err != nil {
 			return err
 		}
 	}
-	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name","SITE_NAME").Exist() {
+	if !o.QueryTable(m.TableNameWithPrefix()).Filter("option_name", "SITE_NAME").Exist() {
 		option := NewOption()
 		option.OptionValue = "MinDoc"
 		option.OptionName = "SITE_NAME"
 		option.OptionTitle = "站点名称"
-		if _,err := o.Insert(option);err != nil {
+		if _, err := o.Insert(option); err != nil {
 			return err
 		}
 	}
 
 	return nil
-}
+}

+ 45 - 67
models/relationship.go

@@ -1,21 +1,20 @@
 package models
 
 import (
-	"github.com/lifei6671/mindoc/conf"
-	"github.com/astaxie/beego/orm"
 	"errors"
 	"github.com/astaxie/beego/logs"
+	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/mindoc/conf"
 )
 
 type Relationship struct {
-	RelationshipId int	`orm:"pk;auto;unique;column(relationship_id)" json:"relationship_id"`
-	MemberId int		`orm:"column(member_id);type(int)" json:"member_id"`
-	BookId int		`orm:"column(book_id);type(int)" json:"book_id"`
+	RelationshipId int `orm:"pk;auto;unique;column(relationship_id)" json:"relationship_id"`
+	MemberId       int `orm:"column(member_id);type(int)" json:"member_id"`
+	BookId         int `orm:"column(book_id);type(int)" json:"book_id"`
 	// RoleId 角色:0 创始人(创始人不能被移除) / 1 管理员/2 编辑者/3 观察者
-	RoleId int		`orm:"column(role_id);type(int)" json:"role_id"`
+	RoleId int `orm:"column(role_id);type(int)" json:"role_id"`
 }
 
-
 // TableName 获取对应数据库表名.
 func (m *Relationship) TableName() string {
 	return "relationship"
@@ -40,97 +39,97 @@ func NewRelationship() *Relationship {
 	return &Relationship{}
 }
 
-func (m *Relationship) Find(id int) (*Relationship,error) {
+func (m *Relationship) Find(id int) (*Relationship, error) {
 	o := orm.NewOrm()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("relationship_id",id).One(m)
-	return m,err
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("relationship_id", id).One(m)
+	return m, err
 }
 
 //查询指定项目的创始人.
-func (m *Relationship) FindFounder(book_id int) (*Relationship,error) {
+func (m *Relationship) FindFounder(book_id int) (*Relationship, error) {
 	o := orm.NewOrm()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id",book_id).Filter("role_id",0).One(m)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).Filter("role_id", 0).One(m)
 
-	return m,err
+	return m, err
 }
 
-func (m *Relationship) UpdateRoleId(book_id,member_id, role_id int) (*Relationship,error) {
+func (m *Relationship) UpdateRoleId(book_id, member_id, role_id int) (*Relationship, error) {
 	o := orm.NewOrm()
 	book := NewBook()
 	book.BookId = book_id
 
 	if err := o.Read(book); err != nil {
 		logs.Error("UpdateRoleId => ", err)
-		return m,errors.New("项目不存在")
+		return m, errors.New("项目不存在")
 	}
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("member_id",member_id).Filter("book_id",book_id).One(m)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("member_id", member_id).Filter("book_id", book_id).One(m)
 
 	if err == orm.ErrNoRows {
 		m = NewRelationship()
 		m.BookId = book_id
 		m.MemberId = member_id
 		m.RoleId = role_id
-	}else if err != nil {
-		return m,err
-	}else if m.RoleId == conf.BookFounder{
-		return m,errors.New("不能变更创始人的权限")
+	} else if err != nil {
+		return m, err
+	} else if m.RoleId == conf.BookFounder {
+		return m, errors.New("不能变更创始人的权限")
 	}
 	m.RoleId = role_id
 
 	if m.RelationshipId > 0 {
-		_,err = o.Update(m)
-	}else{
-		_,err = o.Insert(m)
+		_, err = o.Update(m)
+	} else {
+		_, err = o.Insert(m)
 	}
 
-	return m,err
+	return m, err
 
 }
 
-func (m *Relationship) FindForRoleId(book_id ,member_id int) (int,error) {
+func (m *Relationship) FindForRoleId(book_id, member_id int) (int, error) {
 	o := orm.NewOrm()
 
 	relationship := NewRelationship()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id",book_id).Filter("member_id",member_id).One(relationship)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).Filter("member_id", member_id).One(relationship)
 
 	if err != nil {
 
-		return 0,err
+		return 0, err
 	}
-	return relationship.RoleId,nil
+	return relationship.RoleId, nil
 }
 
-func (m *Relationship) FindByBookIdAndMemberId(book_id ,member_id int) (*Relationship,error) {
+func (m *Relationship) FindByBookIdAndMemberId(book_id, member_id int) (*Relationship, error) {
 	o := orm.NewOrm()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id",book_id).Filter("member_id",member_id).One(m)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).Filter("member_id", member_id).One(m)
 
-	return m,err
+	return m, err
 }
 
-func (m *Relationship) Insert() error  {
+func (m *Relationship) Insert() error {
 	o := orm.NewOrm()
 
-	_,err :=  o.Insert(m)
+	_, err := o.Insert(m)
 
 	return err
 }
 
-func (m *Relationship) Update() error  {
+func (m *Relationship) Update() error {
 	o := orm.NewOrm()
 
-	_,err :=  o.Update(m)
+	_, err := o.Update(m)
 
 	return err
 }
 
-func (m *Relationship) DeleteByBookIdAndMemberId(book_id,member_id int) error {
+func (m *Relationship) DeleteByBookIdAndMemberId(book_id, member_id int) error {
 	o := orm.NewOrm()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id",book_id).Filter("member_id",member_id).One(m)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).Filter("member_id", member_id).One(m)
 
 	if err == orm.ErrNoRows {
 		return errors.New("用户未参与该项目")
@@ -138,22 +137,22 @@ func (m *Relationship) DeleteByBookIdAndMemberId(book_id,member_id int) error {
 	if m.RoleId == conf.BookFounder {
 		return errors.New("不能删除创始人")
 	}
-	_,err = o.Delete(m)
+	_, err = o.Delete(m)
 
 	if err != nil {
-		logs.Error("删除项目参与者 => ",err)
+		logs.Error("删除项目参与者 => ", err)
 		return errors.New("删除失败")
 	}
 	return nil
 
 }
 
-func (m *Relationship) Transfer(book_id,founder_id,receive_id int) error {
+func (m *Relationship) Transfer(book_id, founder_id, receive_id int) error {
 	o := orm.NewOrm()
 
 	founder := NewRelationship()
 
-	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id",book_id).Filter("member_id",founder_id).One(founder)
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).Filter("member_id", founder_id).One(founder)
 
 	if err != nil {
 		return err
@@ -163,7 +162,7 @@ func (m *Relationship) Transfer(book_id,founder_id,receive_id int) error {
 	}
 	receive := NewRelationship()
 
-	err = o.QueryTable(m.TableNameWithPrefix()).Filter("book_id",book_id).Filter("member_id",receive_id).One(receive)
+	err = o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).Filter("member_id", receive_id).One(receive)
 
 	if err != orm.ErrNoRows && err != nil {
 		return err
@@ -176,17 +175,17 @@ func (m *Relationship) Transfer(book_id,founder_id,receive_id int) error {
 	receive.RoleId = conf.BookFounder
 	receive.BookId = book_id
 
-	if err := founder.Update();err != nil {
+	if err := founder.Update(); err != nil {
 		o.Rollback()
 		return err
 	}
 	if receive.RelationshipId > 0 {
-		if _,err := o.Update(receive);err != nil {
+		if _, err := o.Update(receive); err != nil {
 			o.Rollback()
 			return err
 		}
-	}else{
-		if _,err := o.Insert(receive);err != nil {
+	} else {
+		if _, err := o.Insert(receive); err != nil {
 			o.Rollback()
 			return err
 		}
@@ -194,24 +193,3 @@ func (m *Relationship) Transfer(book_id,founder_id,receive_id int) error {
 
 	return o.Commit()
 }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 17 - 15
routers/filter.go

@@ -1,43 +1,45 @@
 package routers
 
 import (
+	"encoding/json"
 	"github.com/astaxie/beego"
 	"github.com/astaxie/beego/context"
 	"github.com/lifei6671/mindoc/conf"
 	"github.com/lifei6671/mindoc/models"
-	"encoding/json"
+	"net/url"
 )
 
-func init()  {
+func init() {
 	var FilterUser = func(ctx *context.Context) {
 		_, ok := ctx.Input.Session(conf.LoginSessionName).(models.Member)
 
 		if !ok {
 			if ctx.Input.IsAjax() {
-				jsonData := make(map[string]interface{},3)
+				jsonData := make(map[string]interface{}, 3)
 
 				jsonData["errcode"] = 403
 				jsonData["message"] = "请登录后再操作"
+
 				returnJSON, _ := json.Marshal(jsonData)
 
 				ctx.ResponseWriter.Write(returnJSON)
-			}else{
-				ctx.Redirect(302, beego.URLFor("AccountController.Login"))
+			} else {
+				ctx.Redirect(302, beego.URLFor("AccountController.Login") + "?url=" + url.PathEscape(ctx.Request.URL.RequestURI()))
 			}
 		}
 	}
-	beego.InsertFilter("/manager",beego.BeforeRouter,FilterUser)
-	beego.InsertFilter("/manager/*",beego.BeforeRouter,FilterUser)
-	beego.InsertFilter("/setting",beego.BeforeRouter,FilterUser)
-	beego.InsertFilter("/setting/*",beego.BeforeRouter,FilterUser)
-	beego.InsertFilter("/book",beego.BeforeRouter,FilterUser)
-	beego.InsertFilter("/book/*",beego.BeforeRouter,FilterUser)
-	beego.InsertFilter("/api/*",beego.BeforeRouter,FilterUser)
+	beego.InsertFilter("/manager", beego.BeforeRouter, FilterUser)
+	beego.InsertFilter("/manager/*", beego.BeforeRouter, FilterUser)
+	beego.InsertFilter("/setting", beego.BeforeRouter, FilterUser)
+	beego.InsertFilter("/setting/*", beego.BeforeRouter, FilterUser)
+	beego.InsertFilter("/book", beego.BeforeRouter, FilterUser)
+	beego.InsertFilter("/book/*", beego.BeforeRouter, FilterUser)
+	beego.InsertFilter("/api/*", beego.BeforeRouter, FilterUser)
 
 	var FinishRouter = func(ctx *context.Context) {
-		ctx.ResponseWriter.Header().Add("MinDoc-Version",conf.VERSION)
-		ctx.ResponseWriter.Header().Add("MinDoc-Site","http://www.iminho.me")
+		ctx.ResponseWriter.Header().Add("MinDoc-Version", conf.VERSION)
+		ctx.ResponseWriter.Header().Add("MinDoc-Site", "https://www.iminho.me")
 	}
 
-	beego.InsertFilter("/*",beego.BeforeRouter ,FinishRouter, false)
+	beego.InsertFilter("/*", beego.BeforeRouter, FinishRouter, false)
 }

+ 0 - 2
static/js/markdown.js

@@ -141,7 +141,6 @@ $(function () {
         $.get(window.editURL + $node.node.id ).done(function (res) {
             layer.close(index);
 
-            resetEditor();
             if (res.errcode === 0) {
                 window.isLoad = true;
                 try {
@@ -153,7 +152,6 @@ $(function () {
                 }
                 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 };
                 pushDocumentCategory(node);
-                console.log(node);
                 window.selectNode = node;
                 pushVueLists(res.data.attach);
             } else {

+ 70 - 0
utils/cryptil/cryptil.go

@@ -0,0 +1,70 @@
+package cryptil
+
+import (
+	"crypto/hmac"
+	"crypto/md5"
+	"crypto/sha1"
+	"encoding/base64"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//对称加密与解密之加密【从Beego中提取出来的】
+//@param            value           需要加密的字符串
+//@param            secret          加密密钥
+//@return           encrypt			返回的加密后的字符串
+func Encrypt(value, secret string) (encrypt string) {
+	vs := base64.URLEncoding.EncodeToString([]byte(value))
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	h := hmac.New(sha1.New, []byte(secret))
+	fmt.Fprintf(h, "%s%s", vs, timestamp)
+	sig := fmt.Sprintf("%02x", h.Sum(nil))
+	return strings.Join([]string{vs, timestamp, sig}, ".")
+}
+
+//对称加密与解密之解密【从Beego中提取出来的】
+//@param            value           需要解密的字符串
+//@param            secret          密钥
+//@return           decrypt			返回解密后的字符串
+func Decrypt(value, secret string) (decrypt string) {
+	parts := strings.SplitN(value, ".", 3)
+	if len(parts) != 3 {
+		return ""
+	}
+	vs := parts[0]
+	timestamp := parts[1]
+	sig := parts[2]
+	h := hmac.New(sha1.New, []byte(secret))
+	fmt.Fprintf(h, "%s%s", vs, timestamp)
+	if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
+		return ""
+	}
+	res, _ := base64.URLEncoding.DecodeString(vs)
+	return string(res)
+}
+
+//MD5加密
+//@param			str			需要加密的字符串
+//@param			salt		盐值
+//@return			CryptStr	加密后返回的字符串
+func Md5Crypt(str string, salt ...interface{}) (CryptStr string) {
+	if l := len(salt); l > 0 {
+		slice := make([]string, l+1)
+		str = fmt.Sprintf(str+strings.Join(slice, "%v"), salt...)
+	}
+	return fmt.Sprintf("%x", md5.Sum([]byte(str)))
+}
+
+//SHA1加密
+//@param			str			需要加密的字符串
+//@param			salt		盐值
+//@return			CryptStr	加密后返回的字符串
+func Sha1Crypt(str string, salt ...interface{}) (CryptStr string) {
+	if l := len(salt); l > 0 {
+		slice := make([]string, l+1)
+		str = fmt.Sprintf(str+strings.Join(slice, "%v"), salt...)
+	}
+	return fmt.Sprintf("%x", sha1.Sum([]byte(str)))
+}

+ 8 - 22
utils/file.go

@@ -1,15 +1,15 @@
 package utils
 
 import (
-	"strings"
-	"os"
 	"fmt"
-	"path/filepath"
 	"io"
 	"math"
+	"os"
+	"path/filepath"
+	"strings"
 )
 
-func AbsolutePath(p string) (string,error) {
+func AbsolutePath(p string) (string, error) {
 
 	if strings.HasPrefix(p, "~") {
 		home := os.Getenv("HOME")
@@ -21,9 +21,9 @@ func AbsolutePath(p string) (string,error) {
 	s, err := filepath.Abs(p)
 
 	if nil != err {
-		return  "",err
+		return "", err
 	}
-	return s,nil
+	return s, nil
 }
 
 // FileExists reports whether the named file or directory exists.
@@ -58,18 +58,13 @@ func FormatBytes(size int64) string {
 
 	i := 0
 
-	for ; s >= 1024 && i < 4 ; i ++ {
+	for ; s >= 1024 && i < 4; i++ {
 		s /= 1024
 	}
 
-
-	return fmt.Sprintf("%.2f%s",s,units[i])
+	return fmt.Sprintf("%.2f%s", s, units[i])
 }
 
-
-
-
-
 func Round(val float64, places int) float64 {
 	var t float64
 	f := math.Pow10(places)
@@ -97,12 +92,3 @@ func Round(val float64, places int) float64 {
 
 	return t
 }
-
-
-
-
-
-
-
-
-

+ 43 - 0
utils/filetil/filetil.go

@@ -0,0 +1,43 @@
+package filetil
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+//==================================
+//更多文件和目录的操作,使用filepath包和os包
+//==================================
+
+//返回的目录扫描结果
+type FileList struct {
+	IsDir   bool   //是否是目录
+	Path    string //文件路径
+	Ext     string //文件扩展名
+	Name    string //文件名
+	Size    int64  //文件大小
+	ModTime int64  //文件修改时间戳
+}
+
+//目录扫描
+//@param			dir			需要扫描的目录
+//@return			fl			文件列表
+//@return			err			错误
+func ScanFiles(dir string) (fl []FileList, err error) {
+	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+		if err == nil {
+			path = strings.Replace(path, "\\", "/", -1) //文件路径处理
+			fl = append(fl, FileList{
+				IsDir:   info.IsDir(),
+				Path:    path,
+				Ext:     strings.ToLower(filepath.Ext(path)),
+				Name:    info.Name(),
+				Size:    info.Size(),
+				ModTime: info.ModTime().Unix(),
+			})
+		}
+		return err
+	})
+	return
+}

+ 9 - 9
utils/gob.go

@@ -6,25 +6,25 @@ import (
 )
 
 //解码
-func Decode(value string,r interface{}) (error) {
+func Decode(value string, r interface{}) error {
 
-	network := bytes.NewBuffer([]byte(value));
+	network := bytes.NewBuffer([]byte(value))
 
 	dec := gob.NewDecoder(network)
 
-	return dec.Decode(r);
+	return dec.Decode(r)
 }
 
 //编码
-func Encode(value interface{}) (string,error) {
-	network:= bytes.NewBuffer(nil);
+func Encode(value interface{}) (string, error) {
+	network := bytes.NewBuffer(nil)
 
 	enc := gob.NewEncoder(network)
 
-	err := enc.Encode(value);
+	err := enc.Encode(value)
 	if err != nil {
-		return  "",err;
+		return "", err
 	}
 
-	return  network.String(),nil;
-}
+	return network.String(), nil
+}

+ 7 - 8
utils/krand.go

@@ -1,16 +1,15 @@
 package utils
 
 import (
-	"time"
 	"math/rand"
+	"time"
 )
 
-
 const (
-	KC_RAND_KIND_NUM   = 0  // 纯数字
-	KC_RAND_KIND_LOWER = 1  // 小写字母
-	KC_RAND_KIND_UPPER = 2  // 大写字母
-	KC_RAND_KIND_ALL   = 3  // 数字、大小写字母
+	KC_RAND_KIND_NUM   = 0 // 纯数字
+	KC_RAND_KIND_LOWER = 1 // 小写字母
+	KC_RAND_KIND_UPPER = 2 // 大写字母
+	KC_RAND_KIND_ALL   = 3 // 数字、大小写字母
 )
 
 // 随机字符串
@@ -18,12 +17,12 @@ func Krand(size int, kind int) []byte {
 	ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
 	is_all := kind > 2 || kind < 0
 	rand.Seed(time.Now().UnixNano())
-	for i :=0; i < size; i++ {
+	for i := 0; i < size; i++ {
 		if is_all { // random ikind
 			ikind = rand.Intn(3)
 		}
 		scope, base := kinds[ikind][0], kinds[ikind][1]
-		result[i] = uint8(base+rand.Intn(scope))
+		result[i] = uint8(base + rand.Intn(scope))
 	}
 
 	return result

+ 17 - 28
utils/ldap.go

@@ -1,11 +1,12 @@
 package utils
 
 import (
-	"gopkg.in/ldap.v2"
-	"fmt"
 	"errors"
+	"fmt"
 	"github.com/astaxie/beego"
+	"gopkg.in/ldap.v2"
 )
+
 /*
 对应的config
 ldap:
@@ -23,20 +24,20 @@ func ValidLDAPLogin(password string) (result bool, err error) {
 	err = nil
 	lc, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "192.168.3.104", 389))
 	if err != nil {
-		beego.Error("Dial => ",err)
+		beego.Error("Dial => ", err)
 		return
 	}
 
 	defer lc.Close()
 	err = lc.Bind("cn=admin,dc=minho,dc=com", "123456")
 	if err != nil {
-		beego.Error("Bind => ",err)
+		beego.Error("Bind => ", err)
 		return
 	}
 	searchRequest := ldap.NewSearchRequest(
 		"DC=minho,DC=com",
 		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
-		fmt.Sprintf("(&(objectClass=User)(%s=%s))","mail", "[email protected]"),
+		fmt.Sprintf("(&(objectClass=User)(%s=%s))", "mail", "[email protected]"),
 		[]string{"dn"},
 		nil,
 	)
@@ -49,7 +50,7 @@ func ValidLDAPLogin(password string) (result bool, err error) {
 		err = errors.New("ldap.no_user_found_or_many_users_found")
 		return
 	}
-	fmt.Printf("%+v = %d",searchResult.Entries,len(searchResult.Entries))
+	fmt.Printf("%+v = %d", searchResult.Entries, len(searchResult.Entries))
 
 	userdn := searchResult.Entries[0].DN
 
@@ -57,7 +58,7 @@ func ValidLDAPLogin(password string) (result bool, err error) {
 	if err == nil {
 		result = true
 	} else {
-		beego.Error("Bind2 => ",err)
+		beego.Error("Bind2 => ", err)
 		err = nil
 	}
 	return
@@ -66,12 +67,12 @@ func ValidLDAPLogin(password string) (result bool, err error) {
 func AddMember(account, password string) error {
 	lc, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "192.168.3.104", 389))
 	if err != nil {
-		beego.Error("Dial => ",err)
+		beego.Error("Dial => ", err)
 		return err
 	}
 
 	defer lc.Close()
-	user := fmt.Sprintf("cn=%s,dc=minho,dc=com",account)
+	user := fmt.Sprintf("cn=%s,dc=minho,dc=com", account)
 
 	member := ldap.NewAddRequest(user)
 
@@ -81,35 +82,35 @@ func AddMember(account, password string) error {
 
 	if err == nil {
 
-		err = lc.Bind(user,"")
+		err = lc.Bind(user, "")
 		if err != nil {
-			beego.Error("Bind => ",err)
+			beego.Error("Bind => ", err)
 			return err
 		}
 		passwordModifyRequest := ldap.NewPasswordModifyRequest(user, "", "1q2w3e__ABC")
 		_, err = lc.PasswordModify(passwordModifyRequest)
 
 		if err != nil {
-			beego.Error("PasswordModify => ",err)
+			beego.Error("PasswordModify => ", err)
 			return err
 		}
 		return nil
 	}
-	beego.Error("Add => ",err)
+	beego.Error("Add => ", err)
 	return err
 }
 
 func ModifyPassword(account, old_password, new_password string) error {
 	l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "192.168.3.104", 389))
 	if err != nil {
-		beego.Error("Dial => ",err)
+		beego.Error("Dial => ", err)
 	}
 	defer l.Close()
 
-	user := fmt.Sprintf("cn=%s,dc=minho,dc=com",account)
+	user := fmt.Sprintf("cn=%s,dc=minho,dc=com", account)
 	err = l.Bind(user, old_password)
 	if err != nil {
-		beego.Error("Bind => ",err)
+		beego.Error("Bind => ", err)
 		return err
 	}
 
@@ -122,15 +123,3 @@ func ModifyPassword(account, old_password, new_password string) error {
 	}
 	return nil
 }
-
-
-
-
-
-
-
-
-
-
-
-

+ 56 - 52
utils/pager.go

@@ -6,8 +6,8 @@ import (
 	con "strconv"
 	"strings"
 
-	"github.com/astaxie/beego/orm"
 	"fmt"
+	"github.com/astaxie/beego/orm"
 	"math"
 )
 
@@ -25,7 +25,7 @@ type PageOptions struct {
 	NextPageText        string //下一页文字 默认"下一页"
 	EnableFirstLastLink bool   //是否启用首尾连接 默认false 建议开启
 	EnablePreNexLink    bool   //是否启用上一页,下一页连接 默认false 建议开启
-	TotalPages	    int
+	TotalPages          int
 }
 
 /**
@@ -42,7 +42,7 @@ func GetPagesInfo(tableName string, currentpage int, pagesize int, conditions st
 	}
 	var rs orm.RawSeter
 	o := orm.NewOrm()
-	var totalItem, totalpages int = 0, 0                                                          //总条数,总页数
+	var totalItem, totalpages int = 0, 0                                                            //总条数,总页数
 	o.Raw("SELECT count(*) FROM " + tableName + "  where 1 > 0 " + conditions).QueryRow(&totalItem) //获取总条数
 	if totalItem <= pagesize {
 		totalpages = 1
@@ -58,22 +58,22 @@ func GetPagesInfo(tableName string, currentpage int, pagesize int, conditions st
 }
 
 /**
- * 返回总记录条数,总页数,当前页面数据,分页html
- * 根据分页选项,生成分页连接 下面是一个实例:
-     func (this *MainController) Test() {
-        var po util.PageOptions
-        po.EnablePreNexLink = true
-        po.EnableFirstLastLink = true
-        po.LinkItemCount = 7
-        po.TableName = "help_topic"
-        cp, _ := this.GetInt("pno")
-        po.CurrentPage = int(cp)
-        _,_,_ pager := util.GetPagerLinks(&po, this.Ctx)
-        this.Data["Email"] = html.HTML(pager)
-        this.TplName = "test.html"
-    }
+* 返回总记录条数,总页数,当前页面数据,分页html
+* 根据分页选项,生成分页连接 下面是一个实例:
+    func (this *MainController) Test() {
+       var po util.PageOptions
+       po.EnablePreNexLink = true
+       po.EnableFirstLastLink = true
+       po.LinkItemCount = 7
+       po.TableName = "help_topic"
+       cp, _ := this.GetInt("pno")
+       po.CurrentPage = int(cp)
+       _,_,_ pager := util.GetPagerLinks(&po, this.Ctx)
+       this.Data["Email"] = html.HTML(pager)
+       this.TplName = "test.html"
+   }
 */
-func GetPagerLinks(po *PageOptions,requestURI string) (int, int, orm.RawSeter, html.HTML) {
+func GetPagerLinks(po *PageOptions, requestURI string) (int, int, orm.RawSeter, html.HTML) {
 	str := ""
 	totalItem, totalpages, rs := GetPagesInfo(po.TableName, po.CurrentPage, po.PageSize, po.Conditions)
 	po = setDefault(po, totalpages)
@@ -84,7 +84,7 @@ func GetPagerLinks(po *PageOptions,requestURI string) (int, int, orm.RawSeter, h
 		if po.CurrentPage < po.LinkItemCount {
 			str = fun2(po, totalpages) //123456789...200
 		} else {
-			if po.CurrentPage + po.LinkItemCount < totalpages {
+			if po.CurrentPage+po.LinkItemCount < totalpages {
 				str = fun3(po, totalpages)
 			} else {
 				str = fun4(po, totalpages)
@@ -94,18 +94,19 @@ func GetPagerLinks(po *PageOptions,requestURI string) (int, int, orm.RawSeter, h
 	return totalItem, totalpages, rs, html.HTML(str)
 }
 
-func GetPagerHtml(requestURI string,pageIndex, pageSize,totalCount int) (html.HTML){
+func GetPagerHtml(requestURI string, pageIndex, pageSize, totalCount int) html.HTML {
 	po := &PageOptions{
-		CurrentPage: pageIndex,
-		PageSize: pageSize,
-		EnableFirstLastLink : true,
-		ParamName : "page",
-		TotalPages:int(math.Ceil(float64(totalCount) / float64(pageSize))),
+		CurrentPage:         pageIndex,
+		PageSize:            pageSize,
+		EnableFirstLastLink: true,
+		ParamName:           "page",
+		TotalPages:          int(math.Ceil(float64(totalCount) / float64(pageSize))),
+		LinkItemCount:		 pageSize,
 	}
 	totalPages := int(math.Ceil(float64(totalCount) / float64(pageSize)))
 
-	setDefault(po,totalPages)
-	DealUri(po,requestURI)
+	setDefault(po, totalPages)
+	DealUri(po, requestURI)
 	str := ""
 	if totalPages <= po.LinkItemCount {
 		str = fun1(po, totalPages) //显示完全  12345678910
@@ -113,16 +114,16 @@ func GetPagerHtml(requestURI string,pageIndex, pageSize,totalCount int) (html.HT
 		if po.CurrentPage < po.LinkItemCount {
 			str = fun2(po, totalPages) //123456789...200
 		} else {
-			if po.CurrentPage + po.LinkItemCount < totalPages {
+			if po.CurrentPage+po.LinkItemCount < totalPages {
 				str = fun3(po, totalPages)
 			} else {
 				str = fun4(po, totalPages)
 			}
 		}
 	}
-	str = strings.Replace(str,"?&","?",-1)
+	str = strings.Replace(str, "?&", "?", -1)
 	//str = strings.Replace(str,"&&","&",-1)
-	return  html.HTML(str)
+	return html.HTML(str)
 }
 
 /**
@@ -136,19 +137,19 @@ func DealUri(po *PageOptions, requestURI string) {
 		arr2 := strings.Split(arr[1], "&")
 		for _, v := range arr2 {
 			if !strings.Contains(v, po.ParamName) {
-				if strings.HasSuffix(rs,"&") {
-					rs +=  v
-				}else{
-					rs +=  v + "&"
+				if strings.HasSuffix(rs, "&") {
+					rs += v
+				} else {
+					rs += v + "&"
 				}
 				//rs += "&" + v
 			}
 		}
-		if strings.HasPrefix(rs,"&") {
+		if strings.HasPrefix(rs, "&") {
 			rs = string(rs[1:])
 		}
-		if strings.HasSuffix(rs,"&"){
-			rs = string(rs[0:strings.Count(rs,"")-1])
+		if strings.HasSuffix(rs, "&") {
+			rs = string(rs[0 : strings.Count(rs, "")-1])
 		}
 		rs = arr[0] + "?" + rs
 		fmt.Println(rs)
@@ -167,8 +168,11 @@ func fun4(po *PageOptions, totalPages int) string {
 	rs := ""
 	rs += getHeader(po, totalPages)
 	rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(1) + "'>" + con.Itoa(1) + "</a></li>"
-	rs += "<li><a href=''>...</a></li>"
-	for i := totalPages - po.LinkItemCount; i <= totalPages; i++ {
+
+		rs += "<li><a href=\"#\" class=\"@3\">...</a></li>"
+
+
+	for i := totalPages - po.LinkItemCount-1; i <= totalPages; i++ {
 		if po.CurrentPage != i {
 			rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a></li>"
 		} else {
@@ -187,15 +191,15 @@ func fun3(po *PageOptions, totalpages int) string {
 	var rs string = ""
 	rs += getHeader(po, totalpages)
 	rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(1) + "'>" + con.Itoa(1) + "</a></li>"
-	rs += "<a href=''>...</a>"
+	rs += "<li><a href=\"#\" class=\"@1\">...</a></li>"
 	for i := po.CurrentPage - po.LinkItemCount/2 + 1; i <= po.CurrentPage+po.LinkItemCount/2-1; i++ {
 		if po.CurrentPage != i {
-			rs += "<a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a>"
+			rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a></li>"
 		} else {
-			rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) +  "  <span class=\"sr-only\">(current)</span></a></li>"
+			rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) + "  <span class=\"sr-only\">(current)</span></a></li>"
 		}
 	}
-	rs += "<a href=''>...</a>"
+	rs += "<li><a href=\"#\" class=\"@2\">...</a></li>"
 	rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(totalpages) + "'>" + con.Itoa(totalpages) + "</a></li>"
 	rs += getFooter(po, totalpages)
 	return rs
@@ -207,18 +211,18 @@ func fun3(po *PageOptions, totalpages int) string {
  * 123456789...200
  */
 func fun2(po *PageOptions, totalpages int) string {
-	var rs string = ""
+	rs := ""
 	rs += getHeader(po, totalpages)
-	for i := 1; i <= po.LinkItemCount+1; i++ {
-		if i == po.LinkItemCount {
-			rs += "<li><a href=\"" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "\">...</a></li>"
+	for i := 1; i <= po.LinkItemCount+2; i++ {
+		if i == po.LinkItemCount+2 {
+			rs += "<li class=\"@4\"><a href=\"" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "\">...</a></li>"
 		} else if i == po.LinkItemCount+1 {
 			rs += "<li><a href=\"" + po.Href + "&" + po.ParamName + "=" + con.Itoa(totalpages) + "\">" + con.Itoa(totalpages) + "</a></li>"
 		} else {
 			if po.CurrentPage != i {
 				rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a></li>"
 			} else {
-				rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) +  "  <span class=\"sr-only\">(current)</span></a></li>"
+				rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) + "  <span class=\"sr-only\">(current)</span></a></li>"
 			}
 		}
 	}
@@ -239,7 +243,7 @@ func fun1(po *PageOptions, totalpages int) string {
 
 			rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a></li>"
 		} else {
-			rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) +  "  <span class=\"sr-only\">(current)</span></a></li>"
+			rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) + "  <span class=\"sr-only\">(current)</span></a></li>"
 		}
 	}
 	rs += getFooter(po, totalpages)
@@ -255,7 +259,7 @@ func getHeader(po *PageOptions, totalpages int) string {
 	if po.EnableFirstLastLink { //当首页,尾页都设定的时候,就显示
 		if po.CurrentPage == 1 {
 			rs += "<li" + judgeDisable(po, totalpages, 0) + " class=\"disabled\"><a href=\"###\">" + po.FirstPageText + "</a></li>"
-		}else{
+		} else {
 			rs += "<li" + judgeDisable(po, totalpages, 0) + " ><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(1) + "'>" + po.FirstPageText + "</a></li>"
 		}
 	}
@@ -281,7 +285,7 @@ func getFooter(po *PageOptions, totalpages int) string {
 	if po.EnableFirstLastLink { //当首页,尾页都设定的时候,就显示
 		if po.CurrentPage == totalpages {
 			rs += "<li " + judgeDisable(po, totalpages, 1) + " class=\"disabled\"><a href=\"###\">" + po.LastPageText + "</a></li>"
-		}else{
+		} else {
 			rs += "<li " + judgeDisable(po, totalpages, 1) + " ><a href=\"" + po.Href + "&" + po.ParamName + "=" + con.Itoa(totalpages) + "\">" + po.LastPageText + "</a></li>"
 		}
 	}
@@ -339,4 +343,4 @@ func judgeDisable(po *PageOptions, totalpages int, h_f int) string {
 		}
 	}
 	return rs
-}
+}

+ 8 - 7
utils/password.go

@@ -1,15 +1,14 @@
 package utils
 
-
 import (
-	"crypto/rand"
-	mt "math/rand"
 	"crypto/md5"
+	"crypto/rand"
 	"crypto/sha256"
 	"crypto/sha512"
 	"encoding/base64"
 	"encoding/hex"
 	"io"
+	mt "math/rand"
 	"strconv"
 	"strings"
 )
@@ -20,6 +19,7 @@ const (
 	stretching_password = 500
 	salt_local_secret   = "ahfw*&TGdsfnbi*^Wt"
 )
+
 //加密密码
 func PasswordHash(pass string) (string, error) {
 
@@ -45,8 +45,9 @@ func PasswordHash(pass string) (string, error) {
 	return password, nil
 
 }
+
 //校验密码是否有效
-func  PasswordVerify(hashing string, pass string) (bool, error) {
+func PasswordVerify(hashing string, pass string) (bool, error) {
 	data := trim_salt_hash(hashing)
 
 	interation, _ := strconv.ParseInt(data["interation_string"], 10, 64)
@@ -56,7 +57,7 @@ func  PasswordVerify(hashing string, pass string) (bool, error) {
 		return false, err
 	}
 
-	if (data["salt_secret"]+delmiter+data["interation_string"]+delmiter+has+delmiter+data["salt"]) == hashing {
+	if (data["salt_secret"] + delmiter + data["interation_string"] + delmiter + has + delmiter + data["salt"]) == hashing {
 		return true, nil
 	} else {
 		return false, nil
@@ -110,7 +111,7 @@ func trim_salt_hash(hash string) map[string]string {
 }
 func salt(secret string) (string, error) {
 
-	buf := make([]byte, saltSize, saltSize + md5.Size)
+	buf := make([]byte, saltSize, saltSize+md5.Size)
 	_, err := io.ReadFull(rand.Reader, buf)
 	if err != nil {
 		return "", err
@@ -134,4 +135,4 @@ func salt_secret() (string, error) {
 
 func randInt(min int, max int) int {
 	return min + mt.Intn(max-min)
-}
+}

+ 2 - 2
utils/template_fun.go

@@ -1,5 +1,5 @@
 package utils
 
-func Asset(p string,cdn string) string {
-	return cdn + p;
+func Asset(p string, cdn string) string {
+	return cdn + p
 }

+ 6 - 6
utils/url.go

@@ -8,17 +8,17 @@ func JoinURI(elem ...string) string {
 	}
 	uri := ""
 
-	for i,u := range elem {
-		u = strings.Replace(u,"\\","/",-1)
+	for i, u := range elem {
+		u = strings.Replace(u, "\\", "/", -1)
 
 		if i == 0 {
-			if !strings.HasSuffix(u,"/") {
+			if !strings.HasSuffix(u, "/") {
 				u = u + "/"
 			}
 			uri = u
-		}else{
-			u = strings.Replace(u,"//","/",-1)
-			if strings.HasPrefix(u,"/") {
+		} else {
+			u = strings.Replace(u, "//", "/", -1)
+			if strings.HasPrefix(u, "/") {
 				u = string(u[1:])
 			}
 			uri += u

+ 101 - 0
utils/ziptil/ziptil.go

@@ -0,0 +1,101 @@
+package ziptil
+
+import (
+	"archive/zip"
+	"errors"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/lifei6671/mindoc/utils/filetil"
+)
+
+//解压zip文件
+//@param			zipFile			需要解压的zip文件
+//@param			dest			需要解压到的目录
+//@return			err				返回错误
+func Unzip(zipFile, dest string) (err error) {
+	dest = strings.TrimSuffix(dest, "/") + "/"
+	// 打开一个zip格式文件
+	r, err := zip.OpenReader(zipFile)
+	if err != nil {
+		return err
+	}
+	defer r.Close()
+	// 迭代压缩文件中的文件,打印出文件中的内容
+	for _, f := range r.File {
+		if !f.FileInfo().IsDir() { //非目录,且不包含__MACOSX
+			if folder := dest + filepath.Dir(f.Name); !strings.Contains(folder, "__MACOSX") {
+				os.MkdirAll(folder, 0777)
+				if fcreate, err := os.Create(dest + strings.TrimPrefix(f.Name, "./")); err == nil {
+					if rc, err := f.Open(); err == nil {
+						io.Copy(fcreate, rc)
+						rc.Close() //不要用defer来关闭,如果文件太多的话,会报too many open files 的错误
+						fcreate.Close()
+					} else {
+						fcreate.Close()
+						return err
+					}
+				} else {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+//压缩指定文件或文件夹
+//@param			dest			压缩后的zip文件目标,如/usr/local/hello.zip
+//@param			filepath		需要压缩的文件或者文件夹
+//@return			err				错误。如果返回错误,则会删除dest文件
+func Zip(dest string, filepath ...string) (err error) {
+	if len(filepath) == 0 {
+		return errors.New("lack of file")
+	}
+	//创建文件
+	fzip, err := os.Create(dest)
+	if err != nil {
+		return err
+	}
+	defer fzip.Close()
+
+	var filelist []filetil.FileList
+	for _, file := range filepath {
+		if info, err := os.Stat(file); err == nil {
+			if info.IsDir() { //目录,则扫描文件
+				if f, _ := filetil.ScanFiles(file); len(f) > 0 {
+					filelist = append(filelist, f...)
+				}
+			} else { //文件
+				filelist = append(filelist, filetil.FileList{
+					IsDir: false,
+					Name:  info.Name(),
+					Path:  file,
+				})
+			}
+		} else {
+			return err
+		}
+	}
+	w := zip.NewWriter(fzip)
+	defer w.Close()
+	for _, file := range filelist {
+		if !file.IsDir {
+			if fw, err := w.Create(strings.TrimLeft(file.Path, "./")); err != nil {
+				return err
+			} else {
+				if filecontent, err := ioutil.ReadFile(file.Path); err != nil {
+					return err
+				} else {
+					if _, err = fw.Write(filecontent); err != nil {
+						return err
+					}
+				}
+			}
+		}
+	}
+	return
+}

+ 2 - 2
views/account/login.tpl

@@ -131,7 +131,7 @@
                 return false;
             } else {
                 $.ajax({
-                    url: "{{urlfor "AccountController.Login"}}",
+                    url: "{{urlfor "AccountController.Login" "url" .url}}",
                     data: $("form").serializeArray(),
                     dataType: "json",
                     type: "POST",
@@ -142,7 +142,7 @@
                             layer.msg(res.message);
                             $btn.button('reset');
                         } else {
-                            turl = res.data.turl;
+                            turl = res.data;
                             if (turl === "") {
                                 turl = "/";
                             }

+ 1 - 0
views/document/default_read.tpl

@@ -65,6 +65,7 @@
                         <li><a href="{{urlfor "DocumentController.Export" ":key" .Model.Identify "output" "pdf"}}" target="_blank">PDF</a> </li>
                         <li><a href="{{urlfor "DocumentController.Export" ":key" .Model.Identify "output" "epub"}}" target="_blank">EPUB</a> </li>
                         <li><a href="{{urlfor "DocumentController.Export" ":key" .Model.Identify "output" "mobi"}}" target="_blank">MOBI</a> </li>
+                        <li><a href="{{urlfor "DocumentController.Export" ":key" .Model.Identify "output" "docx"}}" target="_blank">Word</a> </li>
                     </ul>
                 </div>