Sfoglia il codice sorgente

feat:实现项目密码访问功能

lifei6671 7 anni fa
parent
commit
7b3e4f0672

+ 27 - 27
controllers/AccountController.go

@@ -221,7 +221,7 @@ func (c *AccountController) Register() {
 // 找回密码
 func (c *AccountController) FindPassword() {
 	c.TplName = "account/find_password_setp1.tpl"
-	mail_conf := conf.GetMailConfig()
+	mailConf := conf.GetMailConfig()
 
 	if c.Ctx.Input.IsPost() {
 
@@ -231,7 +231,7 @@ func (c *AccountController) FindPassword() {
 		if email == "" {
 			c.JsonResult(6005, "邮箱地址不能为空")
 		}
-		if !mail_conf.EnableMail {
+		if !mailConf.EnableMail {
 			c.JsonResult(6004, "未启用邮件服务")
 		}
 
@@ -260,23 +260,23 @@ func (c *AccountController) FindPassword() {
 			beego.Error(err)
 			c.JsonResult(6008, "发送邮件失败")
 		}
-		if count > mail_conf.MailNumber {
+		if count > mailConf.MailNumber {
 			c.JsonResult(6008, "发送次数太多,请稍候再试")
 		}
 
-		member_token := models.NewMemberToken()
+		memberToken := models.NewMemberToken()
 
-		member_token.Token = string(utils.Krand(32, utils.KC_RAND_KIND_ALL))
-		member_token.Email = email
-		member_token.MemberId = member.MemberId
-		member_token.IsValid = false
-		if _, err := member_token.InsertOrUpdate(); err != nil {
+		memberToken.Token = string(utils.Krand(32, utils.KC_RAND_KIND_ALL))
+		memberToken.Email = email
+		memberToken.MemberId = member.MemberId
+		memberToken.IsValid = false
+		if _, err := memberToken.InsertOrUpdate(); err != nil {
 			c.JsonResult(6009, "邮件发送失败")
 		}
 
 		data := map[string]interface{}{
 			"SITE_NAME": c.Option["SITE_NAME"],
-			"url":       conf.URLFor("AccountController.FindPassword", "token", member_token.Token, "mail", email),
+			"url":       conf.URLFor("AccountController.FindPassword", "token", memberToken.Token, "mail", email),
 			"BaseUrl":   c.BaseUrl(),
 		}
 
@@ -332,16 +332,16 @@ func (c *AccountController) FindPassword() {
 			//if err != nil {
 			//	beego.Error("邮件发送失败 => ", email, err)
 			//}
-		}(mail_conf, email, body)
+		}(mailConf, email, body)
 
 		c.JsonResult(0, "ok", conf.URLFor("AccountController.Login"))
 	}
 
 	token := c.GetString("token")
-	mail := c.GetString("mail")
+	email := c.GetString("mail")
 
-	if token != "" && mail != "" {
-		member_token, err := models.NewMemberToken().FindByFieldFirst("token", token)
+	if token != "" && email != "" {
+		memberToken, err := models.NewMemberToken().FindByFieldFirst("token", token)
 
 		if err != nil {
 			beego.Error(err)
@@ -349,15 +349,15 @@ func (c *AccountController) FindPassword() {
 			c.TplName = "errors/error.tpl"
 			return
 		}
-		sub_time := member_token.SendTime.Sub(time.Now())
+		subTime := memberToken.SendTime.Sub(time.Now())
 
-		if !strings.EqualFold(member_token.Email, mail) || sub_time.Minutes() > float64(mail_conf.MailExpired) || !member_token.ValidTime.IsZero() {
+		if !strings.EqualFold(memberToken.Email, email) || subTime.Minutes() > float64(mailConf.MailExpired) || !memberToken.ValidTime.IsZero() {
 			c.Data["ErrorMessage"] = "验证码已过期,请重新操作。"
 			c.TplName = "errors/error.tpl"
 			return
 		}
-		c.Data["Email"] = member_token.Email
-		c.Data["Token"] = member_token.Token
+		c.Data["Email"] = memberToken.Email
+		c.Data["Token"] = memberToken.Token
 		c.TplName = "account/find_password_setp2.tpl"
 
 	}
@@ -370,7 +370,7 @@ func (c *AccountController) ValidEmail() {
 	password2 := c.GetString("password2")
 	captcha := c.GetString("code")
 	token := c.GetString("token")
-	mail := c.GetString("mail")
+	email := c.GetString("mail")
 
 	if password1 == "" {
 		c.JsonResult(6001, "密码不能为空")
@@ -392,20 +392,20 @@ func (c *AccountController) ValidEmail() {
 		c.JsonResult(6001, "验证码不正确")
 	}
 
-	mail_conf := conf.GetMailConfig()
-	member_token, err := models.NewMemberToken().FindByFieldFirst("token", token)
+	mailConf := conf.GetMailConfig()
+	memberToken, err := models.NewMemberToken().FindByFieldFirst("token", token)
 
 	if err != nil {
 		beego.Error(err)
 		c.JsonResult(6007, "邮件已失效")
 	}
-	sub_time := member_token.SendTime.Sub(time.Now())
+	subTime := memberToken.SendTime.Sub(time.Now())
 
-	if !strings.EqualFold(member_token.Email, mail) || sub_time.Minutes() > float64(mail_conf.MailExpired) || !member_token.ValidTime.IsZero() {
+	if !strings.EqualFold(memberToken.Email, email) || subTime.Minutes() > float64(mailConf.MailExpired) || !memberToken.ValidTime.IsZero() {
 
 		c.JsonResult(6008, "验证码已过期,请重新操作。")
 	}
-	member, err := models.NewMember().Find(member_token.MemberId)
+	member, err := models.NewMember().Find(memberToken.MemberId)
 	if err != nil {
 		beego.Error(err)
 		c.JsonResult(6005, "用户不存在")
@@ -420,9 +420,9 @@ func (c *AccountController) ValidEmail() {
 	member.Password = hash
 
 	err = member.Update("password")
-	member_token.ValidTime = time.Now()
-	member_token.IsValid = true
-	member_token.InsertOrUpdate()
+	memberToken.ValidTime = time.Now()
+	memberToken.IsValid = true
+	memberToken.InsertOrUpdate()
 
 	if err != nil {
 		beego.Error(err)

+ 5 - 0
controllers/BaseController.go

@@ -79,6 +79,10 @@ func (c *BaseController) Prepare() {
 	}
 
 }
+//判断用户是否登录.
+func (c *BaseController)isUserLoggedIn() bool {
+	return c.Member != nil && c.Member.MemberId > 0
+}
 
 // SetMember 获取或设置当前登录用户信息,如果 MemberId 小于 0 则标识删除 Session
 func (c *BaseController) SetMember(member models.Member) {
@@ -189,6 +193,7 @@ func (c *BaseController) ShowErrorPage(errCode int, errMsg string) {
 	}
 }
 
+
 func (c *BaseController) CheckErrorResult(code int,err error) {
 	if err != nil {
 		c.ShowErrorPage(code, err.Error())

+ 3 - 5
controllers/BookController.go

@@ -73,15 +73,12 @@ func (c *BookController) Dashboard() {
 	if key == "" {
 		c.Abort("404")
 	}
-	if c.Member == nil {
-		c.ShowErrorPage(500,"aaaa")
-	}
+
 	book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
 	if err != nil {
 		if err == models.ErrPermissionDenied {
 			c.Abort("403")
 		}
-		beego.Error(err)
 		c.Abort("500")
 	}
 
@@ -172,6 +169,7 @@ func (c *BookController) SaveBook() {
 	book.Editor = editor
 	book.HistoryCount = historyCount
 	book.IsDownload = 0
+	book.BookPassword = c.GetString("bPassword")
 
 	if autoRelease {
 		book.AutoRelease = 1
@@ -282,7 +280,7 @@ func (c *BookController) Transfer() {
 	err = models.NewRelationship().Transfer(bookResult.BookId, c.Member.MemberId, member.MemberId)
 
 	if err != nil {
-		logs.Error("Transfer => ", err)
+		logs.Error("转让项目失败 -> ", err)
 		c.JsonResult(6008, err.Error())
 	}
 	c.JsonResult(0, "ok")

+ 36 - 89
controllers/DocumentController.go

@@ -1,7 +1,6 @@
 package controllers
 
 import (
-	"container/list"
 	"encoding/json"
 	"html/template"
 	"net/http"
@@ -14,9 +13,6 @@ import (
 	"net/url"
 	"image/png"
 	"fmt"
-	"bytes"
-
-	"github.com/PuerkitoBio/goquery"
 	"github.com/astaxie/beego"
 	"github.com/astaxie/beego/orm"
 	"github.com/boombuler/barcode"
@@ -49,12 +45,12 @@ func (c *DocumentController) Index() {
 	}
 
 	// 如果没有开启匿名访问则跳转到登录
-	if !c.EnableAnonymous && !isUserLoggedIn(c) {
+	if !c.EnableAnonymous && !c.isUserLoggedIn() {
 		promptUserToLogIn(c)
 		return
 	}
 
-	bookResult := isReadable(identify, token, c)
+	bookResult := c.isReadable(identify, token)
 
 	c.TplName = "document/" + bookResult.Theme + "_read.tpl"
 
@@ -104,12 +100,12 @@ func (c *DocumentController) Read() {
 	}
 
 	// 如果没有开启匿名访问则跳转到登录
-	if !c.EnableAnonymous && !isUserLoggedIn(c) {
+	if !c.EnableAnonymous && !c.isUserLoggedIn() {
 		promptUserToLogIn(c)
 		return
 	}
 
-	bookResult := isReadable(identify, token, c)
+	bookResult := c.isReadable(identify, token)
 
 	c.TplName = fmt.Sprintf("document/%s_read.tpl", bookResult.Theme)
 
@@ -246,7 +242,7 @@ func (c *DocumentController) Edit() {
 
 	if conf.GetUploadFileSize() > 0 {
 		c.Data["UploadFileSize"] = conf.GetUploadFileSize()
-	}else{
+	} else {
 		c.Data["UploadFileSize"] = "undefined";
 	}
 }
@@ -815,7 +811,7 @@ func (c *DocumentController) Export() {
 	token := c.GetString("token")
 
 	// 如果没有开启匿名访问则跳转到登录
-	if !c.EnableAnonymous && !isUserLoggedIn(c) {
+	if !c.EnableAnonymous && !c.isUserLoggedIn() {
 		promptUserToLogIn(c)
 		return
 	}
@@ -836,7 +832,7 @@ func (c *DocumentController) Export() {
 		}
 		bookResult = models.NewBookResult().ToBookResult(*book)
 	} else {
-		bookResult = isReadable(identify, token, c)
+		bookResult = c.isReadable(identify, token)
 	}
 	if !bookResult.IsDownload {
 		c.ShowErrorPage(200, "当前项目没有开启导出功能")
@@ -944,12 +940,12 @@ func (c *DocumentController) Search() {
 		c.JsonResult(6001, "参数错误")
 	}
 
-	if !c.EnableAnonymous && !isUserLoggedIn(c) {
+	if !c.EnableAnonymous && !c.isUserLoggedIn() {
 		promptUserToLogIn(c)
 		return
 	}
 
-	bookResult := isReadable(identify, token, c)
+	bookResult := c.isReadable(identify, token)
 
 	docs, err := models.NewDocumentSearchResult().SearchDocument(keyword, bookResult.BookId)
 	if err != nil {
@@ -1220,64 +1216,8 @@ func (c *DocumentController) Compare() {
 	}
 }
 
-// 递归生成文档序列数组
-func RecursiveFun(parentId int, prefix, dpath string, c *DocumentController, book *models.BookResult, docs []*models.Document, paths *list.List) {
-	for _, item := range docs {
-		if item.ParentId == parentId {
-			EachFun(prefix, dpath, c, book, item, paths)
-
-			for _, sub := range docs {
-				if sub.ParentId == item.DocumentId {
-					prefix += strconv.Itoa(item.ParentId) + strconv.Itoa(item.OrderSort) + strconv.Itoa(item.DocumentId)
-					RecursiveFun(item.DocumentId, prefix, dpath, c, book, docs, paths)
-					break
-				}
-			}
-		}
-	}
-}
-
-func EachFun(prefix, dpath string, c *DocumentController, book *models.BookResult, item *models.Document, paths *list.List) {
-	name := prefix + strconv.Itoa(item.ParentId) + strconv.Itoa(item.OrderSort) + strconv.Itoa(item.DocumentId)
-	fpath := dpath + "/" + name + ".html"
-	paths.PushBack(fpath)
-
-	f, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0777)
-	if err != nil {
-		beego.Error(err)
-		c.ShowErrorPage(500, "系统错误")
-	}
-
-	html, err := c.ExecuteViewPathTemplate("document/export.tpl", map[string]interface{}{"Model": book, "Lists": item, "BaseUrl": c.BaseUrl()})
-	if err != nil {
-		f.Close()
-		beego.Error(err)
-		c.ShowErrorPage(500, "系统错误")
-	}
-
-	buf := bytes.NewReader([]byte(html))
-	doc, err := goquery.NewDocumentFromReader(buf)
-	doc.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
-		if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/uploads/") {
-			contentSelection.SetAttr("src", c.BaseUrl()+src)
-		}
-	})
-
-	html, err = doc.Html()
-	if err != nil {
-		f.Close()
-		beego.Error(err)
-		c.ShowErrorPage(500, "系统错误")
-	}
-
-	// html = strings.Replace(html, "<img src=\"/uploads", "<img src=\"" + c.BaseUrl() + "/uploads", -1)
-
-	f.WriteString(html)
-	f.Close()
-}
-
 // 判断用户是否可以阅读文档
-func isReadable(identify, token string, c *DocumentController) *models.BookResult {
+func (c *DocumentController) isReadable(identify, token string) *models.BookResult {
 	book, err := models.NewBook().FindByFieldFirst("identify", identify)
 
 	if err != nil {
@@ -1287,7 +1227,7 @@ func isReadable(identify, token string, c *DocumentController) *models.BookResul
 	bookResult := models.NewBookResult().ToBookResult(*book)
 	isOk := false
 
-	if c.Member != nil {
+	if c.isUserLoggedIn() {
 		roleId, err := models.NewBook().FindForRoleId(book.BookId, c.Member.MemberId)
 		if err == nil {
 			isOk = true
@@ -1296,9 +1236,9 @@ func isReadable(identify, token string, c *DocumentController) *models.BookResul
 		}
 	}
 	// 如果文档是私有的
-	if book.PrivatelyOwned == 1 && !c.Member.IsAdministrator() {
+	if book.PrivatelyOwned == 1 && (!c.isUserLoggedIn() || !c.Member.IsAdministrator()) {
 
-		if book.PrivateToken != "" && !isOk {
+		if book.PrivateToken != "" && !isOk && token != "" {
 			// 如果有访问的 Token,并且该项目设置了访问 Token,并且和用户提供的相匹配,则记录到 Session 中。
 			// 如果用户未提供 Token 且用户登录了,则判断用户是否参与了该项目。
 			// 如果用户未登录,则从 Session 中读取 Token。
@@ -1307,29 +1247,36 @@ func isReadable(identify, token string, c *DocumentController) *models.BookResul
 			} else if token, ok := c.GetSession(identify).(string); !ok || !strings.EqualFold(token, book.PrivateToken) {
 				c.ShowErrorPage(403, "权限不足")
 			}
+		} else if password := c.GetString("bPassword", "");!isOk && book.BookPassword != "" && password != ""{
+
+			//如果设置了密码,则判断密码是否正确
+			if book.BookPassword != password {
+				c.JsonResult(5001, "密码错误")
+			} else {
+				c.SetSession(identify, password)
+				c.JsonResult(0,"OK")
+			}
+
 		} else if !isOk {
-			c.ShowErrorPage(403, "权限不足")
+			//如果设置了密码,则显示密码输入页面
+			if book.BookPassword != "" {
+				//判断已存在的密码是否正确
+				if password, ok := c.GetSession(identify).(string); !ok || !strings.EqualFold(password, book.BookPassword) {
+					body, err := c.ExecuteViewPathTemplate("document/document_password.tpl", map[string]string{"Identify": book.Identify});
+					if err != nil {
+						beego.Error("显示密码页面失败 ->", err)
+					}
+					c.CustomAbort(200, body)
+				}
+			} else {
+				c.ShowErrorPage(403, "权限不足")
+			}
 		}
 	}
 
-	//// 判断是否需要显示评论框
-	//if bookResult.CommentStatus == "closed" {
-	//	bookResult.IsDisplayComment = false
-	//} else if bookResult.CommentStatus == "open" {
-	//	bookResult.IsDisplayComment = true
-	//} else if bookResult.CommentStatus == "group_only" {
-	//	bookResult.IsDisplayComment = bookResult.RelationshipId > 0
-	//} else if bookResult.CommentStatus == "registered_only" {
-	//	bookResult.IsDisplayComment = true
-	//}
-
 	return bookResult
 }
 
-func isUserLoggedIn(c *DocumentController) bool {
-	return c.Member != nil && c.Member.MemberId > 0
-}
-
 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() + ").")

+ 3 - 1
models/BookModel.go

@@ -47,6 +47,8 @@ type Book struct {
 	PrivatelyOwned int `orm:"column(privately_owned);type(int);default(0)" json:"privately_owned"`
 	// 当项目是私有时的访问Token.
 	PrivateToken string `orm:"column(private_token);size(500);null" json:"private_token"`
+	//访问密码.
+	BookPassword string `orm:"column(book_password);size(500);null" json:"book_password"`
 	//状态:0 正常/1 已删除
 	Status int `orm:"column(status);type(int);default(0)" json:"status"`
 	//默认的编辑器.
@@ -919,7 +921,7 @@ where mtr.book_id = ? and mtm.member_id = ? order by mtm.role_id asc limit 1;`
 
 	if err != nil {
 		beego.Error("查询用户项目角色出错 -> book_id=", bookId, " member_id=", memberId, err)
-		return 0, nil
+		return 0, err
 	}
 	return conf.BookRole(roleId), nil
 }

+ 4 - 3
models/BookResult.go

@@ -39,6 +39,7 @@ type BookResult struct {
 	Publisher      string    `json:"publisher"`
 	PrivatelyOwned int       `json:"privately_owned"`
 	PrivateToken   string    `json:"private_token"`
+	BookPassword   string    `json:"book_password"`
 	DocCount       int       `json:"doc_count"`
 	CommentStatus  string    `json:"comment_status"`
 	CommentCount   int       `json:"comment_count"`
@@ -93,11 +94,11 @@ func (m *BookResult) FindByIdentify(identify string, memberId int) (*BookResult,
 	err := NewBook().QueryTable().Filter("identify", identify).One(&book)
 
 	if err != nil {
-		beego.Error("获取项目失败 ->",err)
+		beego.Error("获取项目失败 ->", err)
 		return m, err
 	}
 
-	roleId,err := NewBook().FindForRoleId(book.BookId,memberId)
+	roleId, err := NewBook().FindForRoleId(book.BookId, memberId)
 
 	if err != nil {
 		return m, ErrPermissionDenied
@@ -126,7 +127,6 @@ func (m *BookResult) FindByIdentify(identify string, memberId int) (*BookResult,
 		m.RealName = member.RealName
 	}
 
-
 	if m.RoleId == conf.BookFounder {
 		m.RoleName = "创始人"
 	} else if m.RoleId == conf.BookAdmin {
@@ -185,6 +185,7 @@ func (m *BookResult) ToBookResult(book Book) *BookResult {
 	m.Description = strings.Replace(book.Description, "\r\n", "<br/>", -1)
 	m.PrivatelyOwned = book.PrivatelyOwned
 	m.PrivateToken = book.PrivateToken
+	m.BookPassword = book.BookPassword
 	m.DocCount = book.DocCount
 	m.CommentStatus = book.CommentStatus
 	m.CommentCount = book.CommentCount

+ 0 - 0
views/book/edit.tpl


+ 6 - 1
views/book/setting.tpl

@@ -26,7 +26,7 @@
                 <ul class="menu">
                     <li><a href="{{urlfor "BookController.Dashboard" ":key" .Model.Identify}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a> </li>
                     <li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-user" aria-hidden="true"></i> 成员</a> </li>
-                    <li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" aria-hidden="true"></i> 团队</a> </li>
+                    <li><a href="{{urlfor "BookController.Team" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" aria-hidden="true"></i> 团队</a> </li>
                     <li class="active"><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
                 </ul>
 
@@ -124,6 +124,11 @@
                         </div>
                     </div>
                 </div>
+                <div class="form-group">
+                    <label>访问密码</label>
+                    <input type="text" name="bPassword" id="bPassword" class="form-control" placeholder="访问密码" value="{{.Model.BookPassword}}">
+                    <p class="text">没有访问权限访问项目时需要提供的密码</p>
+                </div>
                 {{end}}
 
                 <div class="form-group">

+ 4 - 8
views/book/team.tpl

@@ -27,15 +27,11 @@
         <div class="row">
             <div class="page-left">
                 <ul class="menu">
-                    <li><a href="{{urlfor "BookController.Dashboard" ":key" .Model.Identify}}" class="item"><i
-                            class="fa fa-dashboard" aria-hidden="true"></i> 概要</a></li>
-                    <li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i
-                            class="fa fa-user" aria-hidden="true"></i> 成员</a></li>
-                    <li class="active"><a href="{{urlfor "BookController.Team" ":key" .Model.Identify}}" class="item"><i
-                            class="fa fa-group" aria-hidden="true"></i> 团队</a></li>
+                    <li><a href="{{urlfor "BookController.Dashboard" ":key" .Model.Identify}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a></li>
+                    <li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-user" aria-hidden="true"></i> 成员</a></li>
+                    <li class="active"><a href="{{urlfor "BookController.Team" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" aria-hidden="true"></i> 团队</a></li>
                 {{if eq .Model.RoleId 0 1}}
-                    <li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i
-                            class="fa fa-gear" aria-hidden="true"></i> 设置</a></li>
+                    <li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a></li>
                 {{end}}
                 </ul>
 

+ 5 - 1
views/document/default_read.tpl

@@ -45,7 +45,11 @@
                 {{if gt .Member.MemberId 0}}
                 {{if eq .Model.RoleId 0 1 2}}
                 <div class="dropdown pull-right">
-                   <a href="{{urlfor "DocumentController.Edit" ":key" .Model.Identify ":id" ""}}" class="btn btn-default"><i class="fa fa-edit" aria-hidden="true"></i> 编辑</a>
+                    <a href="{{urlfor "DocumentController.Edit" ":key" .Model.Identify ":id" ""}}" class="btn btn-default"><i class="fa fa-edit" aria-hidden="true"></i> 编辑</a>
+                    {{if eq .Model.RoleId 0 1}}
+                    <a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="btn btn-success"><i class="fa fa-user" aria-hidden="true"></i> 成员</a>
+                    <a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="btn btn-primary"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a>
+                    {{end}}
                 </div>
                 {{end}}
                 {{end}}

+ 135 - 0
views/document/document_password.tpl

@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    <meta charset="utf-8">
+    <link rel="shortcut icon" href="{{cdnimg "favicon.ico"}}">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+    <meta name="renderer" content="webkit" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>请输入项目密码 - Powered by MinDoc</title>
+    <script src="{{cdnjs "static/jquery/1.12.4/jquery.min.js"}}"></script>
+    <script src="{{cdnjs "static/js/jquery.form.js"}}"></script>
+    <style type="text/css">
+    body{ background: #f2f2f2;}
+    .d_button{ cursor: pointer;}
+    @media(min-width : 450px){
+        .auth_form{
+            width: 400px;
+            border: 1px solid #ccc;
+            background-color: #fff;
+            position: absolute;
+            top: 20%;
+            left: 50%;
+            margin-left: -220px;
+            padding: 20px;
+        }
+    .tit{
+        font-size: 18px;
+    }
+        .inp{
+            height: 30px;
+            width: 387px;
+            font-size: 14px;
+            padding: 5px;
+        }
+        .btn{
+            margin-top: 10px;
+            float: right;
+        }
+    }
+    @media(max-width : 449px){
+        body{
+            margin: 0 auto;
+        }
+    .auth_form{
+        background-color: #fff;
+        border-top: 1px solid #ccc;
+        border-bottom: 1px solid #ccc;
+        width: 100%;
+        margin-top: 40px;
+    }
+        .shell{
+            width: 90%;
+            margin: 10px auto;
+        }
+        .tit{
+            font-size: 18px;
+        }
+        .inp{
+            height: 30px;
+            width: 96.5%;
+            font-size: 14px;
+            padding: 5px;
+        }
+        .btn{
+            margin-top: 10px;
+            float: right;
+        }
+    }
+    .clear{
+        clear: both;
+    }
+    .button {
+        color: #fff;
+        background-color: #428bca;
+        border-radius: 4px;
+        display: inline-block;
+        padding: 6px 12px;
+        margin-bottom: 0;
+        font-size: 14px;
+        font-weight: 400;
+        line-height: 1.42857143;
+        text-align: center;
+        white-space: nowrap;
+        vertical-align: middle;
+        -ms-touch-action: manipulation;
+        touch-action: manipulation;
+        cursor: pointer;
+        -webkit-user-select: none;
+        -moz-user-select: none;
+        -ms-user-select: none;
+        user-select: none;
+        border: 1px solid #357ebd;
+    }
+    </style>
+</head>
+<body>
+<div class="auth_form">
+<div class="shell">
+        <form action="{{urlfor "DocumentController.Index" ":key" .Identify}}" method="post" id="auth_form">
+            <div class="tit">请输入阅读密码</div>
+            <div style="margin-top: 10px;">
+                <input type="password" name="bPassword" placeholder="浏览密码" class="inp"/>
+            </div>
+            <div class="btn">
+                <span id="error" style="color: #919191; font-size: 13px;"></span>
+                <input type="submit" value="提交" class="button"/>
+            </div>
+            <div class="clear"></div>
+        </form>
+</div>
+</div>
+<script type="text/javascript">
+$("#auth_form").ajaxForm({
+    beforeSerialize: function () {
+        var pwd = $("#auth_form input[name='bPassword']").val();
+        if (pwd === "") {
+            $("#error").html("请输入密码");
+            return false;
+        }
+    },
+    dataType: "json",
+    success: function (res) {
+        if (res.errcode === 0) {
+            window.location.reload();
+        } else {
+            $("#auth_form input[name='password']").val("").focus();
+            $("#error").html(res.message);
+        }
+    }
+});
+</script>
+
+        <script src="{{cdnjs "static/bootstrap/js/bootstrap.min.js"}}"></script>
+</body>
+</html>