Forráskód Böngészése

1、重新实现数据库初始化
2、新增超级管理员可以转让项目
3、新增超级管理员可以公开项目

Minho 8 éve
szülő
commit
ca2e0478ca

+ 4 - 8
.travis.yml

@@ -18,9 +18,9 @@ script:
 
 before_deploy:
   - sudo apt-get -qq update
-  - GOARCH=amd64 GOOS=linux go build -o godoc_linux_amd64 -ldflags="-w -X main.VERSION=$TAG -X 'main.BUILD_TIME=`date`' -X 'main.GO_VERSION=`go version`'"
-  - GOARCH=amd64 GOOS=darwin go build -o godoc_darwin_amd64 -ldflags="-w -X main.VERSION=$TAG -X 'main.BUILD_TIME=`date`' -X 'main.GO_VERSION=`go version`'"
-  - GOARCH=amd64 GOOS=windows go build -o godoc_windows_amd64.exe -ldflags="-w -X main.VERSION=$TAG -X 'main.BUILD_TIME=`date`' -X 'main.GO_VERSION=`go version`'"
+  - GOARCH=amd64 GOOS=linux go build -o godoc_linux_amd64 -ldflags="-w -X github.com/lifei6671/godoc/conf.VERSION=$TRAVIS_TAG -X 'github.com/lifei6671/godoc/conf.BUILD_TIME=`date`' -X 'conf.GO_VERSION=`github.com/lifei6671/godoc/go version`'"
+  - GOARCH=amd64 GOOS=darwin go build -o godoc_darwin_amd64 -ldflags="-w -X github.com/lifei6671/godoc/conf.VERSION=$TRAVIS_TAG -X 'github.com/lifei6671/godoc/conf.BUILD_TIME=`date`' -X 'conf.GO_VERSION=`github.com/lifei6671/godoc/go version`'"
+  - GOARCH=amd64 GOOS=windows go build -o godoc_windows_amd64.exe -ldflags="-w -X github.com/lifei6671/godoc/conf.VERSION=$TRAVIS_TAG -X 'github.com/lifei6671/godoc/conf.BUILD_TIME=`date`' -X 'conf.GO_VERSION=`github.com/lifei6671/godoc/go version`'"
   - rm -rf commands controllers models modules data routers tasks vendor docs search utils graphics .git Godeps uploads/* .gitignore .travis.yml Dockerfile gide.yaml LICENSE main.go README.md conf/enumerate.go conf/mail.go install.lock *.md
   - cp conf/app.conf.example conf/app.conf
   - zip -r godoc_linux_amd64.zip conf logs static uploads views lib godoc_linux_amd64
@@ -39,8 +39,4 @@ deploy:
     overwrite: true
     tags: true
     all_branches: true
-    go: 1.8
-
-env:
- global:
-   - TAG: 0.1.3
+    go: 1.8

+ 21 - 101
commands/command.go

@@ -1,23 +1,22 @@
 package commands
 
 import (
+	"encoding/gob"
 	"fmt"
 	"net/url"
-	"time"
 	"os"
-	"encoding/gob"
+	"time"
 
-	"github.com/lifei6671/godoc/models"
 	"github.com/astaxie/beego"
-	"github.com/astaxie/beego/orm"
 	"github.com/astaxie/beego/logs"
-	"github.com/lifei6671/godoc/conf"
+	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/gocaptcha"
-
+	"github.com/lifei6671/godoc/conf"
+	"github.com/lifei6671/godoc/models"
 )
 
 // RegisterDataBase 注册数据库
-func RegisterDataBase()  {
+func RegisterDataBase() {
 	host := beego.AppConfig.String("db_host")
 	database := beego.AppConfig.String("db_database")
 	username := beego.AppConfig.String("db_username")
@@ -26,22 +25,21 @@ func RegisterDataBase()  {
 
 	port := beego.AppConfig.String("db_port")
 
-	dataSource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=%s",username,password,host,port,database,url.QueryEscape(timezone))
-
+	dataSource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=%s", username, password, host, port, database, url.QueryEscape(timezone))
 
 	orm.RegisterDataBase("default", "mysql", dataSource)
 
-	location , err := time.LoadLocation(timezone);
+	location, err := time.LoadLocation(timezone)
 	if err == nil {
 		orm.DefaultTimeLoc = location
-	}else{
+	} else {
 		fmt.Println(err)
 	}
 
 }
 
 // RegisterModel 注册Model
-func RegisterModel()  {
+func RegisterModel() {
 	orm.RegisterModelWithPrefix(conf.GetDatabasePrefix(),
 		new(models.Member),
 		new(models.Book),
@@ -57,88 +55,20 @@ func RegisterModel()  {
 
 }
 
-//初始化数据
-func Initialization()  {
-
-	o := orm.NewOrm()
-
-	_,err := o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用注册','ENABLED_REGISTER','false' FROM dual WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLED_REGISTER');`).Exec()
-
-	if err != nil {
-		panic("ENABLED_REGISTER => " + err.Error())
-		os.Exit(1)
-	}
-	_,err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用验证码','ENABLED_CAPTCHA','false' FROM dual WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLED_CAPTCHA');`).Exec()
-
-	if err != nil {
-		panic("ENABLED_CAPTCHA => " + err.Error())
-		os.Exit(1)
-	}
-	_,err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '启用匿名访问','ENABLE_ANONYMOUS','true' FROM dual WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLE_ANONYMOUS');`).Exec()
-
-	if err != nil {
-		panic("ENABLE_ANONYMOUS => " + err.Error())
-		os.Exit(1)
-	}
-	_,err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '站点名称','SITE_NAME','MinDoc' FROM dual WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'SITE_NAME');`).Exec()
-
-	if err != nil {
-		panic("SITE_NAME => " + err.Error())
-		os.Exit(1)
-	}
-
-	member,err := models.NewMember().FindByFieldFirst("account","admin")
-	if err == orm.ErrNoRows {
-
-		member.Account = "admin"
-		member.Avatar = "/static/images/headimgurl.jpg"
-		member.Password = "123456"
-		member.Role = 0
-		member.Email = "[email protected]"
-
-		if err := member.Add(); err != nil {
-			panic("Member.Add => " + err.Error())
-			os.Exit(0)
-		}
-
-		book := models.NewBook()
-
-		book.MemberId = member.MemberId
-		book.BookName = "MinDoc演示项目"
-		book.Status = 0
-		book.Description = "这是一个MinDoc演示项目,该项目是由系统初始化时自动创建。"
-		book.CommentCount = 0
-		book.PrivatelyOwned = 0
-		book.CommentStatus = "closed"
-		book.Identify = "mindoc"
-		book.DocCount = 0
-		book.CommentCount = 0
-		book.Version = time.Now().Unix()
-		book.Cover = conf.GetDefaultCover()
-		book.Editor = "markdown"
-		book.Theme = "default"
-
-		if err := book.Insert(); err != nil {
-			panic("Book.Insert => " + err.Error())
-			os.Exit(0)
-		}
-	}
-}
-
 // RegisterLogger 注册日志
-func RegisterLogger()  {
+func RegisterLogger() {
 
 	logs.SetLogFuncCall(true)
 	logs.SetLogger("console")
 	logs.EnableFuncCallDepth(true)
 	logs.Async()
 
-	if _,err := os.Stat("logs/log.log"); os.IsNotExist(err) {
-		os.MkdirAll("./logs",0777)
+	if _, err := os.Stat("logs/log.log"); os.IsNotExist(err) {
+		os.MkdirAll("./logs", 0777)
 
-		if f,err := os.Create("logs/log.log");err == nil {
+		if f, err := os.Create("logs/log.log"); err == nil {
 			f.Close()
-			beego.SetLogger("file",`{"filename":"logs/log.log"}`)
+			beego.SetLogger("file", `{"filename":"logs/log.log"}`)
 		}
 	}
 
@@ -149,26 +79,16 @@ func RegisterLogger()  {
 // RunCommand 注册orm命令行工具
 func RegisterCommand() {
 
-	if _,err := os.Stat("install.lock"); os.IsNotExist(err){
-		err = orm.RunSyncdb("default",false,true)
-		if err == nil {
-			Initialization()
-			f, _ := os.Create("install.lock")
-			defer f.Close()
-		}else{
-			panic(err.Error())
-			os.Exit(0)
-		}
-	}
-
+	Install()
+	Update()
 	CheckUpdate()
 }
 
-func RegisterFunction()  {
-	beego.AddFuncMap("config",models.GetOptionValue)
+func RegisterFunction() {
+	beego.AddFuncMap("config", models.GetOptionValue)
 }
 
-func init()  {
+func init() {
 	gocaptcha.ReadFonts("./static/fonts", ".ttf")
 	gob.Register(models.Member{})
-}
+}

+ 96 - 0
commands/install.go

@@ -0,0 +1,96 @@
+package commands
+
+import (
+	"fmt"
+	"os"
+	"time"
+
+	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/godoc/conf"
+	"github.com/lifei6671/godoc/models"
+)
+
+//系统安装.
+func Install() {
+	if len(os.Args) >= 2 && os.Args[1] == "install" {
+		fmt.Println("Initializing...")
+
+		err := orm.RunSyncdb("default", false, true)
+		if err == nil {
+			initialization()
+		} else {
+			panic(err.Error())
+			os.Exit(1)
+		}
+		fmt.Println("Install Successfully!")
+		os.Exit(0)
+	}
+}
+
+//初始化数据
+func initialization() {
+
+	o := orm.NewOrm()
+
+	_, err := o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用注册','ENABLED_REGISTER','false' FROM dual WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLED_REGISTER');`).Exec()
+
+	if err != nil {
+		panic("ENABLED_REGISTER => " + err.Error())
+		os.Exit(1)
+	}
+	_, err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用验证码','ENABLED_CAPTCHA','false' FROM dual WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLED_CAPTCHA');`).Exec()
+
+	if err != nil {
+		panic("ENABLED_CAPTCHA => " + err.Error())
+		os.Exit(1)
+	}
+	_, err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '启用匿名访问','ENABLE_ANONYMOUS','true' FROM dual WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLE_ANONYMOUS');`).Exec()
+
+	if err != nil {
+		panic("ENABLE_ANONYMOUS => " + err.Error())
+		os.Exit(1)
+	}
+	_, err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '站点名称','SITE_NAME','MinDoc' FROM dual WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'SITE_NAME');`).Exec()
+
+	if err != nil {
+		panic("SITE_NAME => " + err.Error())
+		os.Exit(1)
+	}
+
+	member, err := models.NewMember().FindByFieldFirst("account", "admin")
+	if err == orm.ErrNoRows {
+
+		member.Account = "admin"
+		member.Avatar = "/static/images/headimgurl.jpg"
+		member.Password = "123456"
+		member.Role = 0
+		member.Email = "[email protected]"
+
+		if err := member.Add(); err != nil {
+			panic("Member.Add => " + err.Error())
+			os.Exit(0)
+		}
+
+		book := models.NewBook()
+
+		book.MemberId = member.MemberId
+		book.BookName = "MinDoc演示项目"
+		book.Status = 0
+		book.Description = "这是一个MinDoc演示项目,该项目是由系统初始化时自动创建。"
+		book.CommentCount = 0
+		book.PrivatelyOwned = 0
+		book.CommentStatus = "closed"
+		book.Identify = "mindoc"
+		book.DocCount = 0
+		book.CommentCount = 0
+		book.Version = time.Now().Unix()
+		book.Cover = conf.GetDefaultCover()
+		book.Editor = "markdown"
+		book.Theme = "default"
+
+		if err := book.Insert(); err != nil {
+			panic("Book.Insert => " + err.Error())
+			os.Exit(0)
+		}
+	}
+}

+ 14 - 9
commands/update.go

@@ -1,16 +1,21 @@
 package commands
 
 import (
-	"os"
-	"net/http"
-	"github.com/astaxie/beego"
 	"encoding/json"
-	"io/ioutil"
 	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
+
+	"github.com/astaxie/beego"
+	"github.com/lifei6671/godoc/conf"
 )
 
-func Update()  {
-	if len(os.Args) > 2 && os.Args[1] == "update" {
+//系统升级.
+func Update() {
+	if len(os.Args) >= 2 && os.Args[1] == "update" {
+
+		fmt.Println("update successed.")
 
 		os.Exit(0)
 	}
@@ -45,8 +50,8 @@ func CheckUpdate() {
 			beego.Error("CheckUpdate => ", err)
 			os.Exit(1)
 		}
-
-		fmt.Println("MinDoc last version => ",result[0].Name)
+		fmt.Println("MinDoc current version => ", conf.VERSION)
+		fmt.Println("MinDoc last version => ", result[0].Name)
 		os.Exit(0)
 	}
-}
+}

+ 28 - 31
conf/enumerate.go

@@ -2,21 +2,22 @@
 package conf
 
 import (
-	"github.com/astaxie/beego"
 	"strings"
+
+	"github.com/astaxie/beego"
 )
 
 // 登录用户的Session名
 const LoginSessionName = "LoginSessionName"
 
-const CaptchaSessionName  = "__captcha__"
+const CaptchaSessionName = "__captcha__"
 
-const RegexpEmail  = `^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$`
+const RegexpEmail = `^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$`
 
 const RegexpAccount = `^[a-zA-Z][a-zA-z0-9]{2,50}$`
 
 // PageSize 默认分页条数.
-const PageSize  = 15
+const PageSize = 15
 
 // 用户权限
 const (
@@ -40,43 +41,45 @@ const (
 )
 
 var (
-	VERSION string
+	VERSION    string
+	BUILD_TIME string
+	GO_VERSION string
 )
 
 // app_key
-func GetAppKey()  (string) {
-	return beego.AppConfig.DefaultString("app_key","godoc")
+func GetAppKey() string {
+	return beego.AppConfig.DefaultString("app_key", "godoc")
 }
 
-func GetDatabasePrefix() string  {
-	return beego.AppConfig.DefaultString("db_prefix","md_")
+func GetDatabasePrefix() string {
+	return beego.AppConfig.DefaultString("db_prefix", "md_")
 }
 
 //获取默认头像
 func GetDefaultAvatar() string {
-	return beego.AppConfig.DefaultString("avatar","/static/images/headimgurl.jpg")
+	return beego.AppConfig.DefaultString("avatar", "/static/images/headimgurl.jpg")
 }
 
 //获取阅读令牌长度.
 func GetTokenSize() int {
-	return beego.AppConfig.DefaultInt("token_size",12)
+	return beego.AppConfig.DefaultInt("token_size", 12)
 }
 
 //获取默认文档封面.
 func GetDefaultCover() string {
-	return beego.AppConfig.DefaultString("cover","/static/images/book.jpg")
+	return beego.AppConfig.DefaultString("cover", "/static/images/book.jpg")
 }
 
 //获取允许的商城文件的类型.
-func GetUploadFileExt()  []string {
-	ext := beego.AppConfig.DefaultString("upload_file_ext","png|jpg|jpeg|gif|txt|doc|docx|pdf")
-	
-	temp := strings.Split(ext,"|")
-	
-	exts := make([]string,len(temp))
-	
+func GetUploadFileExt() []string {
+	ext := beego.AppConfig.DefaultString("upload_file_ext", "png|jpg|jpeg|gif|txt|doc|docx|pdf")
+
+	temp := strings.Split(ext, "|")
+
+	exts := make([]string, len(temp))
+
 	i := 0
-	for _,item := range temp {
+	for _, item := range temp {
 		if item != "" {
 			exts[i] = item
 			i++
@@ -86,23 +89,17 @@ func GetUploadFileExt()  []string {
 }
 
 //判断是否是允许商城的文件类型.
-func IsAllowUploadFileExt(ext string) bool  {
+func IsAllowUploadFileExt(ext string) bool {
 
-	if strings.HasPrefix(ext,".") {
+	if strings.HasPrefix(ext, ".") {
 		ext = string(ext[1:])
 	}
 	exts := GetUploadFileExt()
 
-	for _,item := range exts {
-		if strings.EqualFold(item,ext) {
-			return  true
+	for _, item := range exts {
+		if strings.EqualFold(item, ext) {
+			return true
 		}
 	}
 	return false
 }
-
-//获取当前版本.
-func Version() string {
-
-	return VERSION
-}

+ 1 - 3
controllers/book.go

@@ -161,6 +161,7 @@ func (c *BookController) SaveBook()  {
 	c.JsonResult(0,"ok",bookResult)
 }
 
+//设置项目私有状态.
 func (c *BookController) PrivatelyOwned()  {
 
 	status := c.GetString("status")
@@ -184,7 +185,6 @@ func (c *BookController) PrivatelyOwned()  {
 	if bookResult.RoleId != conf.BookFounder {
 		c.JsonResult(6002,"权限不足")
 	}
-	fmt.Printf("%+v",bookResult)
 
 	book,err := models.NewBook().Find(bookResult.BookId)
 
@@ -193,8 +193,6 @@ func (c *BookController) PrivatelyOwned()  {
 	}
 	book.PrivatelyOwned = state
 
-	logs.Info("",state,status)
-
 	err = book.Update()
 
 	if err != nil {

+ 170 - 108
controllers/manager.go

@@ -1,18 +1,17 @@
 package controllers
 
 import (
-
 	"encoding/json"
 	"html/template"
-	"strings"
 	"regexp"
+	"strings"
 
-	"github.com/lifei6671/godoc/conf"
+	"github.com/astaxie/beego"
 	"github.com/astaxie/beego/logs"
-	"github.com/lifei6671/godoc/utils"
-	"github.com/lifei6671/godoc/models"
 	"github.com/astaxie/beego/orm"
-	"github.com/astaxie/beego"
+	"github.com/lifei6671/godoc/conf"
+	"github.com/lifei6671/godoc/models"
+	"github.com/lifei6671/godoc/utils"
 )
 
 type ManagerController struct {
@@ -29,7 +28,7 @@ func (c *ManagerController) Index() {
 }
 
 // 用户列表.
-func (c *ManagerController) Users()  {
+func (c *ManagerController) Users() {
 	c.Prepare()
 	c.TplName = "manager/users.tpl"
 
@@ -37,9 +36,9 @@ func (c *ManagerController) Users()  {
 		c.Abort("403")
 	}
 
-	pageIndex,_ := c.GetInt("page",0)
+	pageIndex, _ := c.GetInt("page", 0)
 
-	members,totalCount,err := models.NewMember().FindToPager(pageIndex,15)
+	members, totalCount, err := models.NewMember().FindToPager(pageIndex, 15)
 
 	if err != nil {
 		c.Data["ErrorMessage"] = err.Error()
@@ -50,23 +49,23 @@ func (c *ManagerController) Users()  {
 		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, 10, int(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))
 	}
 }
 
-// 添加用户
+// 添加用户.
 func (c *ManagerController) CreateMember() {
 	c.Prepare()
-	if !c.Member.IsAdministrator(){
+	if !c.Member.IsAdministrator() {
 		c.Abort("403")
 	}
 
@@ -75,22 +74,22 @@ func (c *ManagerController) CreateMember() {
 	password2 := strings.TrimSpace(c.GetString("password2"))
 	email := strings.TrimSpace(c.GetString("email"))
 	phone := strings.TrimSpace(c.GetString("phone"))
-	role,_ := c.GetInt("role",1)
-	status,_ := c.GetInt("status",0)
+	role, _ := c.GetInt("role", 1)
+	status, _ := c.GetInt("status", 0)
 
-	if ok,err := regexp.MatchString(conf.RegexpAccount,account); account == "" || !ok || err != nil {
-		c.JsonResult(6001,"账号只能由英文字母数字组成,且在3-50个字符")
+	if ok, err := regexp.MatchString(conf.RegexpAccount, account); account == "" || !ok || err != nil {
+		c.JsonResult(6001, "账号只能由英文字母数字组成,且在3-50个字符")
 	}
-	if  l := strings.Count(password1,"") ; password1 == "" || l > 50 || l < 6{
-		c.JsonResult(6002,"密码必须在6-50个字符之间")
+	if l := strings.Count(password1, ""); password1 == "" || l > 50 || l < 6 {
+		c.JsonResult(6002, "密码必须在6-50个字符之间")
 	}
 	if password1 != password2 {
-		c.JsonResult(6003,"确认密码不正确")
+		c.JsonResult(6003, "确认密码不正确")
 	}
-	if  ok,err := regexp.MatchString(conf.RegexpEmail,email); !ok || err != nil || email == "" {
-		c.JsonResult(6004,"邮箱格式不正确")
+	if ok, err := regexp.MatchString(conf.RegexpEmail, email); !ok || err != nil || email == "" {
+		c.JsonResult(6004, "邮箱格式不正确")
 	}
-	if role != 0 && role != 1 {
+	if role != 0 && role != 1 && role != 2 {
 		role = 1
 	}
 	if status != 0 && status != 1 {
@@ -99,8 +98,8 @@ func (c *ManagerController) CreateMember() {
 
 	member := models.NewMember()
 
-	if _,err := member.FindByAccount(account); err == nil && member.MemberId > 0 {
-		c.JsonResult(6005,"账号已存在")
+	if _, err := member.FindByAccount(account); err == nil && member.MemberId > 0 {
+		c.JsonResult(6005, "账号已存在")
 	}
 
 	member.Account = account
@@ -114,80 +113,81 @@ func (c *ManagerController) CreateMember() {
 	}
 
 	if err := member.Add(); err != nil {
-		c.JsonResult(6006,err.Error())
+		c.JsonResult(6006, err.Error())
 	}
 
-	c.JsonResult(0,"ok",member)
+	c.JsonResult(0, "ok", member)
 }
 
 //更新用户状态.
-func (c *ManagerController) UpdateMemberStatus()  {
+func (c *ManagerController) UpdateMemberStatus() {
 	c.Prepare()
 
 	if !c.Member.IsAdministrator() {
 		c.Abort("403")
 	}
 
-	member_id,_ := c.GetInt("member_id",0)
-	status ,_ := c.GetInt("status",0)
+	member_id, _ := c.GetInt("member_id", 0)
+	status, _ := c.GetInt("status", 0)
 
 	if member_id <= 0 {
-		c.JsonResult(6001,"参数错误")
+		c.JsonResult(6001, "参数错误")
 	}
 	if status != 0 && status != 1 {
 		status = 0
 	}
 	member := models.NewMember()
 
-	if _,err := member.Find(member_id); err != nil {
-		c.JsonResult(6002,"用户不存在")
+	if _, err := member.Find(member_id); err != nil {
+		c.JsonResult(6002, "用户不存在")
 	}
 	member.Status = status
 
-	if err := member.Update();err != nil {
-		logs.Error("",err)
-		c.JsonResult(6003,"用户状态设置失败")
+	if err := member.Update(); err != nil {
+		logs.Error("", err)
+		c.JsonResult(6003, "用户状态设置失败")
 	}
-	c.JsonResult(0,"ok",member)
+	c.JsonResult(0, "ok", member)
 }
 
-func (c *ManagerController) ChangeMemberRole()  {
+//变更用户权限.
+func (c *ManagerController) ChangeMemberRole() {
 	c.Prepare()
 
 	if !c.Member.IsAdministrator() {
 		c.Abort("403")
 	}
 
-	member_id,_ := c.GetInt("member_id",0)
-	role ,_ := c.GetInt("role",0)
+	member_id, _ := c.GetInt("member_id", 0)
+	role, _ := c.GetInt("role", 0)
 	if member_id <= 0 {
-		c.JsonResult(6001,"参数错误")
+		c.JsonResult(6001, "参数错误")
 	}
 	if role != conf.MemberAdminRole && role != conf.MemberGeneralRole {
-		c.JsonResult(6001,"用户权限不正确")
+		c.JsonResult(6001, "用户权限不正确")
 	}
 	member := models.NewMember()
 
-	if _,err := member.Find(member_id); err != nil {
-		c.JsonResult(6002,"用户不存在")
+	if _, err := member.Find(member_id); err != nil {
+		c.JsonResult(6002, "用户不存在")
 	}
 	member.Role = role
 
-	if err := member.Update();err != nil {
-		logs.Error("",err)
-		c.JsonResult(6003,"用户权限设置失败")
+	if err := member.Update(); err != nil {
+		logs.Error("", err)
+		c.JsonResult(6003, "用户权限设置失败")
 	}
 	member.ResolveRoleName()
-	c.JsonResult(0,"ok",member)
+	c.JsonResult(0, "ok", member)
 }
 
-func (c *ManagerController) Books()  {
+func (c *ManagerController) Books() {
 	c.Prepare()
 	c.TplName = "manager/books.tpl"
 
 	pageIndex, _ := c.GetInt("page", 1)
 
-	books,totalCount,err := models.NewBookResult().FindToPager(pageIndex,conf.PageSize)
+	books, totalCount, err := models.NewBookResult().FindToPager(pageIndex, conf.PageSize)
 
 	if err != nil {
 		c.Abort("500")
@@ -197,7 +197,7 @@ func (c *ManagerController) Books()  {
 		html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount)
 
 		c.Data["PageHtml"] = html
-	}else {
+	} else {
 		c.Data["PageHtml"] = ""
 	}
 
@@ -205,80 +205,79 @@ func (c *ManagerController) Books()  {
 }
 
 //编辑项目
-func (c *ManagerController) EditBook()  {
+func (c *ManagerController) EditBook() {
 	c.TplName = "manager/edit_book.tpl"
 	identify := c.GetString(":key")
 
 	if identify == "" {
 		c.Abort("404")
 	}
-	book,err := models.NewBook().FindByFieldFirst("identify",identify)
+	book, err := models.NewBook().FindByFieldFirst("identify", identify)
 	if err != nil {
 		c.Abort("500")
 	}
 	if c.Ctx.Input.IsPost() {
 
 		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"))
-		order_index ,_ := c.GetInt("order_index",0)
+		order_index, _ := c.GetInt("order_index", 0)
 
-		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个标签")
 			}
 		}
 
-
 		book.BookName = book_name
 		book.Description = description
 		book.CommentStatus = comment_status
 		book.Label = tag
 		book.OrderIndex = order_index
 
-		if err := book.Update();err != nil {
-			c.JsonResult(6006,"保存失败")
+		if err := book.Update(); err != nil {
+			c.JsonResult(6006, "保存失败")
 		}
-		c.JsonResult(0,"ok")
+		c.JsonResult(0, "ok")
 	}
 	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 *ManagerController) DeleteBook()  {
+func (c *ManagerController) DeleteBook() {
 	c.Prepare()
 	if !c.Member.IsAdministrator() {
 		c.Abort("403")
 	}
 
-	book_id,_ := c.GetInt("book_id",0)
+	book_id, _ := c.GetInt("book_id", 0)
 
-	if book_id <= 0{
-		c.JsonResult(6001,"参数错误")
+	if book_id <= 0 {
+		c.JsonResult(6001, "参数错误")
 	}
 	book := models.NewBook()
 
 	err := book.ThoroughDeleteBook(book_id)
 
 	if err == orm.ErrNoRows {
-		c.JsonResult(6002,"项目不存在")
+		c.JsonResult(6002, "项目不存在")
 	}
 	if err != nil {
-		logs.Error("DeleteBook => ",err)
-		c.JsonResult(6003,"删除失败")
+		logs.Error("DeleteBook => ", err)
+		c.JsonResult(6003, "删除失败")
 	}
-	c.JsonResult(0,"ok")
+	c.JsonResult(0, "ok")
 }
 
 // CreateToken 创建访问来令牌.
@@ -288,10 +287,10 @@ func (c *ManagerController) CreateToken() {
 
 	identify := c.GetString("identify")
 
-	book,err := models.NewBook().FindByFieldFirst("identify",identify);
+	book, err := models.NewBook().FindByFieldFirst("identify", identify)
 
 	if err != nil {
-		c.JsonResult(6001,"项目不存在")
+		c.JsonResult(6001, "项目不存在")
 	}
 	if action == "create" {
 
@@ -304,17 +303,18 @@ func (c *ManagerController) 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", "")
 	}
 }
 
+//项目设置.
 func (c *ManagerController) Setting() {
 	c.Prepare()
 	c.TplName = "manager/setting.tpl"
@@ -323,14 +323,14 @@ func (c *ManagerController) Setting() {
 		c.Abort("403")
 	}
 
-	options,err := models.NewOption().All()
+	options, err := models.NewOption().All()
 
 	if c.Ctx.Input.IsPost() {
-		for _,item := range options {
+		for _, item := range options {
 			item.OptionValue = c.GetString(item.OptionName)
 			item.InsertOrUpdate()
 		}
-		c.JsonResult(0,"ok")
+		c.JsonResult(0, "ok")
 	}
 
 	if err != nil {
@@ -338,14 +338,60 @@ func (c *ManagerController) Setting() {
 	}
 	c.Data["SITE_TITLE"] = c.Option["SITE_NAME"]
 
-	for _,item := range options {
+	for _, item := range options {
 		c.Data[item.OptionName] = item
 	}
 
+}
+
+// Transfer 转让项目.
+func (c *ManagerController) Transfer() {
+	c.Prepare()
+	account := c.GetString("account")
+
+	if account == "" {
+		c.JsonResult(6004, "接受者账号不能为空")
+	}
+	member, err := models.NewMember().FindByAccount(account)
+
+	if err != nil {
+		logs.Error("FindByAccount => ", err)
+		c.JsonResult(6005, "接受用户不存在")
+	}
+	if member.Status != 0 {
+		c.JsonResult(6006, "接受用户已被禁用")
+	}
+
+	if !c.Member.IsAdministrator() {
+		c.Abort("403")
+	}
+
+	identify := c.GetString("identify")
+
+	book, err := models.NewBook().FindByFieldFirst("identify", identify)
+	if err != nil {
+		c.JsonResult(6001, err.Error())
+	}
+	rel, err := models.NewRelationship().FindFounder(book.BookId)
+
+	if err != nil {
+		beego.Error("FindFounder => ", err)
+		c.JsonResult(6009, "查询项目创始人失败")
+	}
+	if member.MemberId == rel.MemberId {
+		c.JsonResult(6007, "不能转让给自己")
+	}
+
+	err = models.NewRelationship().Transfer(book.BookId, rel.MemberId, member.MemberId)
 
+	if err != nil {
+		logs.Error("Transfer => ", err)
+		c.JsonResult(6008, err.Error())
+	}
+	c.JsonResult(0, "ok")
 }
 
-func (c *ManagerController) Comments()  {
+func (c *ManagerController) Comments() {
 	c.Prepare()
 	c.TplName = "manager/comments.tpl"
 	if !c.Member.IsAdministrator() {
@@ -355,49 +401,65 @@ func (c *ManagerController) Comments()  {
 }
 
 //DeleteComment 标记评论为已删除
-func (c *ManagerController) DeleteComment()  {
+func (c *ManagerController) DeleteComment() {
 	c.Prepare()
 	if !c.Member.IsAdministrator() {
 		c.Abort("403")
 	}
-	comment_id,_ := c.GetInt("comment_id",0)
+	comment_id, _ := c.GetInt("comment_id", 0)
 
 	if comment_id <= 0 {
-		c.JsonResult(6001,"参数错误")
+		c.JsonResult(6001, "参数错误")
 	}
 
 	comment := models.NewComment()
 
-	if _,err := comment.Find(comment_id); err != nil {
-		c.JsonResult(6002,"评论不存在")
+	if _, err := comment.Find(comment_id); err != nil {
+		c.JsonResult(6002, "评论不存在")
 	}
 
 	comment.Approved = 3
 
-	if err := comment.Update("approved");err != nil {
-		c.JsonResult(6003,"删除评论失败")
+	if err := comment.Update("approved"); err != nil {
+		c.JsonResult(6003, "删除评论失败")
 	}
-	c.JsonResult(0,"ok",comment)
+	c.JsonResult(0, "ok", comment)
 }
 
+//设置项目私有状态.
+func (c *ManagerController) PrivatelyOwned() {
 
+	status := c.GetString("status")
+	identify := c.GetString("identify")
 
+	if status != "open" && status != "close" {
+		c.JsonResult(6003, "参数错误")
+	}
+	state := 0
+	if status == "open" {
+		state = 0
+	} else {
+		state = 1
+	}
 
+	if !c.Member.IsAdministrator() {
+		c.Abort("403")
+	}
 
+	book, err := models.NewBook().FindByFieldFirst("identify", identify)
+	if err != nil {
+		c.JsonResult(6001, err.Error())
+	}
 
+	book.PrivatelyOwned = state
 
+	logs.Info("", state, status)
 
+	err = book.Update()
 
-
-
-
-
-
-
-
-
-
-
-
-
-
+	if err != nil {
+		logs.Error("PrivatelyOwned => ", err)
+		c.JsonResult(6004, "保存失败")
+	}
+	c.JsonResult(0, "ok")
+}

+ 1 - 0
controllers/setting.go

@@ -36,6 +36,7 @@ func (c *SettingController) Index()  {
 		if err := member.Update(); err != nil {
 			c.JsonResult(602, err.Error())
 		}
+		c.SetMember(*member)
 		c.JsonResult(0, "ok")
 	}
 }

+ 9 - 15
main.go

@@ -3,37 +3,31 @@ package main
 import (
 	"fmt"
 	"os"
-	_ "github.com/go-sql-driver/mysql"
-	_ "github.com/lifei6671/godoc/routers"
-	_ "github.com/astaxie/beego/session/redis"
+
+	"github.com/astaxie/beego"
 	_ "github.com/astaxie/beego/session/memcache"
 	_ "github.com/astaxie/beego/session/mysql"
-	"github.com/astaxie/beego"
+	_ "github.com/astaxie/beego/session/redis"
+	_ "github.com/go-sql-driver/mysql"
 	"github.com/lifei6671/godoc/commands"
+	"github.com/lifei6671/godoc/conf"
 	"github.com/lifei6671/godoc/controllers"
+	_ "github.com/lifei6671/godoc/routers"
 )
 
-var (
-	VERSION    string
-	BUILD_TIME string
-	GO_VERSION string
-)
-
-
 func main() {
 
-	fmt.Printf("MinDoc version => %s\nbuild time => %s\nstart directory => %s\n%s\n", VERSION, BUILD_TIME, os.Args[0],GO_VERSION)
-
 	commands.RegisterDataBase()
 	commands.RegisterModel()
 	commands.RegisterLogger()
 	commands.RegisterCommand()
 	commands.RegisterFunction()
 
-	beego.SetStaticPath("uploads","uploads")
+	beego.SetStaticPath("uploads", "uploads")
 
 	beego.ErrorController(&controllers.ErrorController{})
 
+	fmt.Printf("MinDoc version => %s\nbuild time => %s\nstart directory => %s\n%s\n", conf.VERSION, conf.BUILD_TIME, os.Args[0], conf.GO_VERSION)
+
 	beego.Run()
 }
-

+ 3 - 3
models/book.go

@@ -160,9 +160,9 @@ func (m *Book) FindToPager(pageIndex, pageSize ,memberId int) (books []*BookResu
 
 	qb2.Select("book.*,rel.member_id","rel.role_id","m.account as create_name").
 		From(m.TableNameWithPrefix() + " AS book").
-		LeftJoin(relationship.TableNameWithPrefix() + " AS rel").
-		On("book.book_id=rel.book_id").
-		LeftJoin(NewMember().TableNameWithPrefix() + " AS m").On("rel.member_id=m.member_id AND rel.role_id=0").
+		LeftJoin(relationship.TableNameWithPrefix() + " AS rel").On("book.book_id=rel.book_id").
+		LeftJoin(relationship.TableNameWithPrefix() + " AS rel1").On("book.book_id=rel1.book_id  AND rel.role_id=0").
+		LeftJoin(NewMember().TableNameWithPrefix() + " AS m").On("rel1.member_id=m.member_id").
 		Where("rel.member_id=?").
 		OrderBy("book.order_index DESC ","book.book_id").Desc().
 		Limit(pageSize).

+ 9 - 0
models/relationship.go

@@ -47,6 +47,15 @@ func (m *Relationship) Find(id int) (*Relationship,error) {
 	return m,err
 }
 
+//查询指定项目的创始人.
+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)
+
+	return m,err
+}
+
 func (m *Relationship) UpdateRoleId(book_id,member_id, role_id int) (*Relationship,error) {
 	o := orm.NewOrm()
 	book := NewBook()

+ 1 - 1
routers/filter.go

@@ -24,7 +24,7 @@ func init()  {
 	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-Version",conf.VERSION)
 		ctx.ResponseWriter.Header().Add("MinDoc-Site","http://www.iminho.me")
 	}
 

+ 2 - 0
routers/router.go

@@ -25,6 +25,8 @@ func init()  {
 	beego.Router("/manager/comments", &controllers.ManagerController{},"*:Comments")
 	beego.Router("/manager/books/token", &controllers.ManagerController{},"post:CreateToken")
 	beego.Router("/manager/setting",&controllers.ManagerController{},"*:Setting")
+	beego.Router("/manager/books/transfer", &controllers.ManagerController{},"post:Transfer")
+	beego.Router("/manager/books/open", &controllers.ManagerController{},"post:PrivatelyOwned")
 
 	beego.Router("/setting", &controllers.SettingController{},"*:Index")
 	beego.Router("/setting/password", &controllers.SettingController{},"*:Password")

+ 109 - 1
views/manager/edit_book.tpl

@@ -38,6 +38,12 @@
                 <div class="m-box">
                     <div class="box-head">
                         <strong class="box-title"> 项目设置</strong>
+                        <button type="button"  class="btn btn-success btn-sm pull-right" data-toggle="modal" data-target="#transferBookModal">转让项目</button>
+                        {{if eq .Model.PrivatelyOwned 1}}
+                        <button type="button"  class="btn btn-success btn-sm pull-right" data-toggle="modal" data-target="#changePrivatelyOwnedModal" style="margin-right: 5px;">转为公有</button>
+                        {{else}}
+                        <button type="button"  class="btn btn-danger btn-sm pull-right" data-toggle="modal" data-target="#changePrivatelyOwnedModal" style="margin-right: 5px;">转为私有</button>
+                        {{end}}
                         <button type="button"  class="btn btn-danger btn-sm pull-right" style="margin-right: 5px;" data-toggle="modal" data-target="#deleteBookModal">删除项目</button>
                     </div>
                 </div>
@@ -119,6 +125,43 @@
     </div>
     {{template "widgets/footer.tpl" .}}
 </div>
+<div class="modal fade" id="changePrivatelyOwnedModal" tabindex="-1" role="dialog" aria-labelledby="changePrivatelyOwnedModalLabel">
+    <div class="modal-dialog" role="document">
+        <form method="post" action="{{urlfor "ManagerController.PrivatelyOwned" }}" id="changePrivatelyOwnedForm">
+            <input type="hidden" name="identify" value="{{.Model.Identify}}">
+            <input type="hidden" name="status" value="{{if eq .Model.PrivatelyOwned 0}}close{{else}}open{{end}}">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">
+                        {{if eq .Model.PrivatelyOwned 0}}
+                        转为私有
+                        {{else}}
+                        转为共有
+                        {{end}}
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    {{if eq .Model.PrivatelyOwned 0}}
+                    <span style="font-size: 14px;font-weight: 400;">确定将项目转为私有吗?</span>
+                    <p></p>
+                    <p class="text error-message">转为私有后需要通过阅读令牌才能访问该项目。</p>
+                    {{else}}
+                    <span style="font-size: 14px;font-weight: 400;"> 确定将项目转为公有吗?</span>
+                    <p></p>
+                    <p class="text error-message">转为公有后所有人都可以访问该项目。</p>
+                    {{end}}
+                </div>
+                <div class="modal-footer">
+                    <span class="error-message" id="form-error-message1"></span>
+                    <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+                    <button type="submit" class="btn btn-primary" data-loading-text="变更中..." id="btnChangePrivatelyOwned">确定</button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+
 <!-- Delete Book Modal -->
 <div class="modal fade" id="deleteBookModal" tabindex="-1" role="dialog" aria-labelledby="deleteBookModalLabel">
     <div class="modal-dialog" role="document">
@@ -143,7 +186,33 @@
         </form>
     </div>
 </div>
-
+<div class="modal fade" id="transferBookModal" tabindex="-1" role="dialog" aria-labelledby="transferBookModalLabel">
+    <div class="modal-dialog" role="document">
+        <form action="{{urlfor "ManagerController.Transfer"}}" method="post" id="transferBookForm">
+            <input type="hidden" name="identify" value="{{.Model.Identify}}">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="myModalLabel">项目转让</h4>
+                </div>
+                <div class="modal-body">
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label">接收账号</label>
+                        <div class="col-sm-10">
+                            <input type="text" name="account" class="form-control" placeholder="接收者账号" id="receiveAccount" maxlength="50">
+                        </div>
+                    </div>
+                    <div class="clearfix"></div>
+                </div>
+                <div class="modal-footer">
+                    <span id="form-error-message3" class="error-message"></span>
+                    <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+                    <button type="submit" id="btnTransferBook" class="btn btn-primary">确定转让</button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
 <script src="/static/jquery/1.12.4/jquery.min.js" type="text/javascript"></script>
 <script src="/static/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
 <script src="/static/webuploader/webuploader.min.js" type="text/javascript"></script>
@@ -213,7 +282,46 @@
                 }
             }
         });
+        $("#transferBookForm").ajaxForm({
+            beforeSubmit : function () {
+                var account = $.trim($("#receiveAccount").val());
+                if (account === ""){
+                    return showError("接受者账号不能为空","#form-error-message3")
+                }
+                $("#btnTransferBook").button("loading");
+            },
+            success : function (res) {
+                if(res.errcode === 0){
+                    window.location = window.location.href;
+                }else{
+                    showError(res.message,"#form-error-message3");
+                }
+                $("#btnTransferBook").button("reset");
+            },
+            error : function () {
+                $("#btnTransferBook").button("reset");
+            }
+        });
+        $("#changePrivatelyOwnedForm").ajaxForm({
+            beforeSubmit :function () {
+                $("#btnChangePrivatelyOwned").button("loading");
+            },
+            success :function (res) {
+                if(res.errcode === 0){
+                    window.location = window.location.href;
+                    return;
+                }else{
+                    showError(res.message,"#form-error-message1");
+                }
+                $("#btnChangePrivatelyOwned").button("reset");
+            },
+            error :function () {
+                showError("服务器异常","#form-error-message1");
+                $("#btnChangePrivatelyOwned").button("reset");
+            }
+        });
     });
+
 </script>
 </body>
 </html>

+ 7 - 1
views/manager/users.tpl

@@ -170,7 +170,7 @@
                 <div class="modal-footer">
                     <span id="form-error-message"></span>
                     <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
-                    <button type="submit" class="btn btn-success">保存</button>
+                    <button type="submit" class="btn btn-success" data-loading-text="保存中..." id="btnAddMember">保存</button>
                 </div>
             </div>
         </form>
@@ -207,6 +207,7 @@
                 if (email === "") {
                     return showError("邮箱不能为空");
                 }
+                $("#btnAddMember").button("loading");
                 return true;
             },
             success : function (res) {
@@ -216,6 +217,11 @@
                 }else{
                     showError(res.message);
                 }
+                $("#btnAddMember").button("reset");
+            },
+            error : function () {
+                showError("服务器异常");
+                $("#btnAddMember").button("reset");
             }
         });