mirror of https://github.com/mindoc-org/mindoc.git
parent
d38417535a
commit
ad67558b80
|
@ -0,0 +1,14 @@
|
|||
FROM golang:1.8.1-alpine
|
||||
|
||||
|
||||
ADD . /go/src/github.com/lifei6671/godoc
|
||||
|
||||
|
||||
WORKDIR /go/src/github.com/lifei6671/godoc
|
||||
|
||||
RUN chmod +x start.sh
|
||||
|
||||
RUN go build -ldflags "-w" && \
|
||||
rm -rf commands controllers models routers search vendor .gitignore .travis.yml Dockerfile gide.yaml LICENSE main.go README.md utils graphics Godeps
|
||||
|
||||
CMD ["./start.sh"]
|
|
@ -4,13 +4,15 @@ import (
|
|||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
"os"
|
||||
"encoding/gob"
|
||||
|
||||
"github.com/lifei6671/godoc/models"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"os"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
|
||||
)
|
||||
|
||||
// RegisterDataBase 注册数据库
|
||||
|
@ -75,7 +77,7 @@ func RegisterLogger() {
|
|||
logs.EnableFuncCallDepth(true)
|
||||
logs.Async()
|
||||
|
||||
beego.BeeLogger.DelLogger("console")
|
||||
//beego.BeeLogger.DelLogger("console")
|
||||
beego.SetLogger("file",`{"filename":"logs/log.log"}`)
|
||||
beego.SetLogFuncCall(true)
|
||||
beego.BeeLogger.Async()
|
||||
|
@ -100,4 +102,8 @@ func RegisterCommand() {
|
|||
|
||||
func RegisterFunction() {
|
||||
beego.AddFuncMap("config",models.GetOptionValue)
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(models.Member{})
|
||||
}
|
|
@ -5,6 +5,12 @@ sessionon = true
|
|||
sessionname = smart_webhook_id
|
||||
copyrequestbody = true
|
||||
|
||||
#默认Session生成Key的秘钥
|
||||
beegoserversessionkey=123456
|
||||
#Session储存方式
|
||||
sessionprovider=file
|
||||
sessionproviderconfig=./logs
|
||||
|
||||
#生成回调地址时完整的域名
|
||||
base_url = https://hook.iminho.me
|
||||
|
||||
|
|
|
@ -6,6 +6,12 @@ sessionon = true
|
|||
sessionname = smart_webhook_id
|
||||
copyrequestbody = true
|
||||
|
||||
#默认Session生成Key的秘钥
|
||||
beegoserversessionkey=123456
|
||||
#Session储存方式
|
||||
sessionprovider=file
|
||||
sessionproviderconfig=./logs
|
||||
|
||||
#生成回调地址时完整的域名
|
||||
base_url = https://hook.iminho.me
|
||||
|
||||
|
|
|
@ -77,3 +77,6 @@ func (c *AccountController) Logout(){
|
|||
c.Redirect(beego.URLFor("AccountController.Login"),302)
|
||||
}
|
||||
|
||||
func (c *AccountController) Captcha() {
|
||||
|
||||
}
|
|
@ -7,32 +7,42 @@ import (
|
|||
"github.com/lifei6671/godoc/models"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/astaxie/beego"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
type BaseController struct {
|
||||
beego.Controller
|
||||
Member *models.Member
|
||||
Option map[string]string
|
||||
EnableAnonymous bool
|
||||
}
|
||||
|
||||
// Prepare 预处理.
|
||||
func (c *BaseController) Prepare (){
|
||||
c.Data["SiteName"] = "MinDoc"
|
||||
c.Data["Member"] = models.Member{}
|
||||
c.EnableAnonymous = false
|
||||
|
||||
|
||||
if member,ok := c.GetSession(conf.LoginSessionName).(models.Member); ok && member.MemberId > 0{
|
||||
c.Member = &member
|
||||
c.Data["Member"] = c.Member
|
||||
}else{
|
||||
c.Member = models.NewMember()
|
||||
c.Member.Find(1)
|
||||
c.Data["Member"] = *c.Member
|
||||
//c.Member = models.NewMember()
|
||||
//c.Member.Find(1)
|
||||
//c.Data["Member"] = *c.Member
|
||||
}
|
||||
c.Data["BaseUrl"] = c.Ctx.Input.Scheme() + "://" + c.Ctx.Request.Host
|
||||
|
||||
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" {
|
||||
c.EnableAnonymous = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -499,7 +499,28 @@ func (c *BookController) Delete() {
|
|||
|
||||
//发布项目
|
||||
func (c *BookController) Release() {
|
||||
c.JsonResult(0,"ok")
|
||||
c.Prepare()
|
||||
|
||||
identify := c.GetString("identify")
|
||||
book ,err := models.NewBookResult().FindByIdentify(identify,c.Member.MemberId)
|
||||
|
||||
if err != nil {
|
||||
if err == models.ErrPermissionDenied {
|
||||
c.JsonResult(6001,"权限不足")
|
||||
}
|
||||
if err == orm.ErrNoRows {
|
||||
c.JsonResult(6002,"项目不存在")
|
||||
}
|
||||
beego.Error(err)
|
||||
c.JsonResult(6003,"未知错误")
|
||||
}
|
||||
if book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder && book.RoleId != conf.BookEditor{
|
||||
c.JsonResult(6003,"权限不足")
|
||||
}
|
||||
|
||||
go models.NewDocument().ReleaseContent(book.BookId)
|
||||
|
||||
c.JsonResult(0,"发布任务已推送到任务队列,稍后将在后台执行。")
|
||||
}
|
||||
|
||||
func (c *BookController) SaveSort() {
|
||||
|
|
|
@ -21,12 +21,135 @@ type DocumentController struct {
|
|||
BaseController
|
||||
}
|
||||
|
||||
func (p *DocumentController) Index() {
|
||||
p.TplName = "document/index.tpl"
|
||||
func isReadable (identify,token string,c *DocumentController) *models.BookResult {
|
||||
book,err := models.NewBook().FindByFieldFirst("identify",identify)
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.Abort("500")
|
||||
}
|
||||
//如果文档是私有的
|
||||
if book.PrivatelyOwned == 1 {
|
||||
|
||||
is_ok := false
|
||||
|
||||
if c.Member != nil{
|
||||
_, err := models.NewRelationship().FindForRoleId(book.BookId, c.Member.MemberId)
|
||||
if err == nil {
|
||||
is_ok = true
|
||||
}
|
||||
}
|
||||
if book.PrivateToken != "" && !is_ok {
|
||||
//如果有访问的Token,并且该项目设置了访问Token,并且和用户提供的相匹配,则记录到Session中.
|
||||
//如果用户未提供Token且用户登录了,则判断用户是否参与了该项目.
|
||||
//如果用户未登录,则从Session中读取Token.
|
||||
if token != "" && strings.EqualFold(token, book.PrivateToken) {
|
||||
c.SetSession(identify, token)
|
||||
|
||||
} else if token, ok := c.GetSession(identify).(string); !ok || !strings.EqualFold(token, book.PrivateToken) {
|
||||
c.Abort("403")
|
||||
}
|
||||
}else{
|
||||
c.Abort("403")
|
||||
}
|
||||
|
||||
}
|
||||
bookResult := book.ToBookResult()
|
||||
|
||||
if c.Member != nil {
|
||||
rel ,err := models.NewRelationship().FindByBookIdAndMemberId(bookResult.BookId,c.Member.MemberId)
|
||||
|
||||
if err == nil {
|
||||
bookResult.MemberId = rel.MemberId
|
||||
bookResult.RoleId = rel.RoleId
|
||||
bookResult.RelationshipId = rel.RelationshipId
|
||||
}
|
||||
}
|
||||
return bookResult
|
||||
}
|
||||
|
||||
func (p *DocumentController) Read() {
|
||||
p.TplName = "document/kancloud.tpl"
|
||||
func (c *DocumentController) Index() {
|
||||
c.Prepare()
|
||||
identify := c.Ctx.Input.Param(":key")
|
||||
token := c.GetString("token")
|
||||
|
||||
if identify == "" {
|
||||
c.Abort("404")
|
||||
}
|
||||
bookResult := isReadable(identify,token,c)
|
||||
|
||||
c.TplName = "document/" + bookResult.Theme + "_read.tpl"
|
||||
|
||||
tree,err := models.NewDocument().CreateDocumentTreeForHtml(bookResult.BookId,0)
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.Abort("500")
|
||||
}
|
||||
|
||||
|
||||
c.Data["Model"] = bookResult
|
||||
c.Data["Result"] = template.HTML(tree)
|
||||
c.Data["Title"] = "概要"
|
||||
c.Data["Content"] = bookResult.Description
|
||||
}
|
||||
|
||||
func (c *DocumentController) Read() {
|
||||
c.Prepare()
|
||||
identify := c.Ctx.Input.Param(":key")
|
||||
token := c.GetString("token")
|
||||
id := c.GetString(":id")
|
||||
|
||||
if identify == "" || id == ""{
|
||||
c.Abort("404")
|
||||
}
|
||||
bookResult := isReadable(identify,token,c)
|
||||
|
||||
c.TplName = "document/" + bookResult.Theme + "_read.tpl"
|
||||
|
||||
doc := models.NewDocument()
|
||||
|
||||
if doc_id,err := strconv.Atoi(id);err == nil {
|
||||
doc,err = doc.Find(doc_id)
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.Abort("500")
|
||||
}
|
||||
}else{
|
||||
doc,err = doc.FindByFieldFirst("identify",id)
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.Abort("500")
|
||||
}
|
||||
}
|
||||
|
||||
if doc.BookId != bookResult.BookId {
|
||||
c.Abort("403")
|
||||
}
|
||||
if c.IsAjax() {
|
||||
var data struct{
|
||||
DocTitle string `json:"doc_title"`
|
||||
Body string `json:"body"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
data.DocTitle = doc.DocumentName
|
||||
data.Body = doc.Release
|
||||
data.Title = doc.DocumentName + " - Powered by MinDoc"
|
||||
|
||||
c.JsonResult(0,"ok",data)
|
||||
}
|
||||
|
||||
tree,err := models.NewDocument().CreateDocumentTreeForHtml(bookResult.BookId,doc.DocumentId)
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.Abort("500")
|
||||
}
|
||||
|
||||
c.Data["Model"] = bookResult
|
||||
c.Data["Result"] = template.HTML(tree)
|
||||
c.Data["Title"] = doc.DocumentName
|
||||
c.Data["Content"] = template.HTML(doc.Release)
|
||||
}
|
||||
|
||||
func (c *DocumentController) Edit() {
|
||||
|
@ -67,7 +190,7 @@ func (c *DocumentController) Edit() {
|
|||
c.Data["Result"] = template.JS("[]")
|
||||
|
||||
trees ,err := models.NewDocument().FindDocumentTree(bookResult.BookId)
|
||||
beego.Info("",trees)
|
||||
|
||||
if err != nil {
|
||||
beego.Error("FindDocumentTree => ", err)
|
||||
}else{
|
||||
|
|
|
@ -1,9 +1,44 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/lifei6671/godoc/models"
|
||||
"github.com/lifei6671/godoc/utils"
|
||||
)
|
||||
|
||||
type HomeController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (p *HomeController) Index() {
|
||||
p.TplName = "home/index.tpl"
|
||||
func (c *HomeController) Index() {
|
||||
c.Prepare()
|
||||
c.TplName = "home/index.tpl"
|
||||
//如果没有开启匿名访问,则跳转到登录页面
|
||||
if !c.EnableAnonymous && c.Member == nil {
|
||||
c.Redirect(beego.URLFor("AccountController.Login"),302)
|
||||
}
|
||||
pageIndex,_ := c.GetInt("page",1)
|
||||
pageSize := 18
|
||||
|
||||
member_id := 0
|
||||
|
||||
if c.Member != nil {
|
||||
member_id = c.Member.MemberId
|
||||
}
|
||||
books,totalCount,err := models.NewBook().FindForHomeToPager(pageIndex,pageSize,member_id)
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.Abort("500")
|
||||
}
|
||||
if totalCount > 0 {
|
||||
html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, pageSize, totalCount)
|
||||
|
||||
c.Data["PageHtml"] = html
|
||||
}else {
|
||||
c.Data["PageHtml"] = ""
|
||||
}
|
||||
|
||||
c.Data["Lists"] = books
|
||||
|
||||
}
|
||||
|
|
|
@ -320,18 +320,27 @@ func (c *ManagerController) Setting() {
|
|||
if !c.Member.IsAdministrator() {
|
||||
c.Abort("403")
|
||||
}
|
||||
if c.Ctx.Input.IsPost() {
|
||||
|
||||
}
|
||||
options,err := models.NewOption().All()
|
||||
|
||||
if c.Ctx.Input.IsPost() {
|
||||
for _,item := range options {
|
||||
item.OptionValue = c.GetString(item.OptionName)
|
||||
item.InsertOrUpdate()
|
||||
}
|
||||
c.JsonResult(0,"ok")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Abort("500")
|
||||
}
|
||||
c.Data["SITE_TITLE"] = c.Option["SITE_NAME"]
|
||||
|
||||
for _,item := range options {
|
||||
c.Data[item.OptionName] = item
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
func (c *ManagerController) Comments() {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
## 日志存放目录
|
1
main.go
1
main.go
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/lifei6671/godoc/routers"
|
||||
_ "github.com/garyburd/redigo/redis"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/lifei6671/godoc/commands"
|
||||
)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/astaxie/beego/orm"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Book struct .
|
||||
|
@ -32,8 +33,10 @@ type Book struct {
|
|||
// CommentStatus 评论设置的状态:open 为允许所有人评论,closed 为不允许评论, group_only 仅允许参与者评论 ,registered_only 仅允许注册者评论.
|
||||
CommentStatus string `orm:"column(comment_status);size(20);default(open)" json:"comment_status"`
|
||||
CommentCount int `orm:"column(comment_count);type(int)" json:"comment_count"`
|
||||
Cover string `orm:"column();size(1000)" json:"cover"`
|
||||
|
||||
//封面地址
|
||||
Cover string `orm:"column(cover);size(1000)" json:"cover"`
|
||||
//主题风格
|
||||
Theme string `orm:"columen(theme);size(255);default(default)" json:"theme"`
|
||||
// CreateTime 创建时间 .
|
||||
CreateTime time.Time `orm:"type(datetime);column(create_time);auto_now_add" json:"create_time"`
|
||||
MemberId int `orm:"column(member_id);size(100)" json:"member_id"`
|
||||
|
@ -109,23 +112,27 @@ func (m *Book) Update(cols... string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (m *Book) FindByField(field string,value interface{}) ([]Book,error) {
|
||||
//根据指定字段查询结果集.
|
||||
func (m *Book) FindByField(field string,value interface{}) ([]*Book,error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
var books []Book
|
||||
_,err := o.QueryTable(conf.GetDatabasePrefix() + m.TableName()).Filter(field,value).All(&books)
|
||||
var books []*Book
|
||||
_,err := o.QueryTable(m.TableNameWithPrefix()).Filter(field,value).All(&books)
|
||||
|
||||
return books,err
|
||||
}
|
||||
|
||||
//根据指定字段查询一个结果.
|
||||
func (m *Book) FindByFieldFirst(field string,value interface{})(*Book,error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
err := o.QueryTable(conf.GetDatabasePrefix() + m.TableName()).Filter(field,value).One(m)
|
||||
err := o.QueryTable(m.TableNameWithPrefix()).Filter(field,value).One(m)
|
||||
|
||||
return m,err
|
||||
|
||||
}
|
||||
|
||||
//分页查询指定用户的项目
|
||||
func (m *Book) FindToPager(pageIndex, pageSize ,memberId int) (books []*BookResult,totalCount int,err error){
|
||||
|
||||
relationship := NewRelationship()
|
||||
|
@ -242,11 +249,75 @@ func (m *Book) ThoroughDeleteBook(id int) error {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func (m *Book) FindForHomeToPager(pageIndex, pageSize ,member_id int) (books []*BookResult,totalCount int,err error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
offset := (pageIndex - 1) * pageSize
|
||||
//如果是登录用户
|
||||
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"
|
||||
|
||||
err = o.Raw(sql1,member_id).QueryRow(&totalCount)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sql2 := `SELECT book.*,rel1.*,member.account AS create_name FROM md_books AS book
|
||||
LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.member_id = ?
|
||||
LEFT JOIN md_relationship AS rel1 ON rel1.book_id = book.book_id AND rel1.role_id = 0
|
||||
LEFT JOIN md_members AS member ON rel1.member_id = member.member_id
|
||||
WHERE rel.relationship_id > 0 OR book.privately_owned = 0 ORDER BY order_index DESC ,book.book_id DESC LIMIT ?,?`
|
||||
|
||||
_,err = o.Raw(sql2,member_id,offset,pageSize).QueryRows(&books)
|
||||
|
||||
return
|
||||
|
||||
}else{
|
||||
count,err1 := o.QueryTable(m.TableNameWithPrefix()).Filter("privately_owned",0).Count()
|
||||
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
totalCount = int(count)
|
||||
|
||||
sql := `SELECT book.*,rel.*,member.account AS create_name FROM md_books AS book
|
||||
LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.role_id = 0
|
||||
LEFT JOIN md_members AS member ON rel.member_id = member.member_id
|
||||
WHERE book.privately_owned = 0 ORDER BY order_index DESC ,book.book_id DESC LIMIT ?,?`
|
||||
|
||||
_,err = o.Raw(sql,offset,pageSize).QueryRows(&books)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (book *Book) ToBookResult() *BookResult {
|
||||
|
||||
m := NewBookResult()
|
||||
|
||||
m.BookId = book.BookId
|
||||
m.BookName = book.BookName
|
||||
m.Identify = book.Identify
|
||||
m.OrderIndex = book.OrderIndex
|
||||
m.Description = strings.Replace(book.Description, "\r\n", "<br/>", -1)
|
||||
m.PrivatelyOwned = book.PrivatelyOwned
|
||||
m.PrivateToken = book.PrivateToken
|
||||
m.DocCount = book.DocCount
|
||||
m.CommentStatus = book.CommentStatus
|
||||
m.CommentCount = book.CommentCount
|
||||
m.CreateTime = book.CreateTime
|
||||
m.ModifyTime = book.ModifyTime
|
||||
m.Cover = book.Cover
|
||||
m.Label = book.Label
|
||||
m.Status = book.Status
|
||||
m.Editor = book.Editor
|
||||
m.Theme = book.Theme
|
||||
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ package models
|
|||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/orm"
|
||||
"strings"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
)
|
||||
|
@ -23,6 +23,7 @@ type BookResult struct {
|
|||
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"`
|
||||
|
@ -39,6 +40,7 @@ func NewBookResult() *BookResult {
|
|||
return &BookResult{}
|
||||
}
|
||||
|
||||
|
||||
// 根据项目标识查询项目以及指定用户权限的信息.
|
||||
func (m *BookResult) FindByIdentify(identify string,member_id int) (*BookResult,error) {
|
||||
if identify == "" || member_id <= 0 {
|
||||
|
@ -77,24 +79,9 @@ func (m *BookResult) FindByIdentify(identify string,member_id int) (*BookResult,
|
|||
return m, err
|
||||
}
|
||||
|
||||
m.BookId = book.BookId
|
||||
m.BookName = book.BookName
|
||||
m.Identify = book.Identify
|
||||
m.OrderIndex = book.OrderIndex
|
||||
m.Description = strings.Replace(book.Description, "\r\n", "<br/>", -1)
|
||||
m.PrivatelyOwned = book.PrivatelyOwned
|
||||
m.PrivateToken = book.PrivateToken
|
||||
m.DocCount = book.DocCount
|
||||
m.CommentStatus = book.CommentStatus
|
||||
m.CommentCount = book.CommentCount
|
||||
m.CreateTime = book.CreateTime
|
||||
m.CreateName = member.Account
|
||||
m.ModifyTime = book.ModifyTime
|
||||
m.Cover = book.Cover
|
||||
m.Label = book.Label
|
||||
m.Status = book.Status
|
||||
m.Editor = book.Editor
|
||||
m = book.ToBookResult()
|
||||
|
||||
m.CreateName = member.Account
|
||||
m.MemberId = relationship.MemberId
|
||||
m.RoleId = relationship.RoleId
|
||||
m.RelationshipId = relationship.RelationshipId
|
||||
|
|
|
@ -112,7 +112,17 @@ func (m *Document) RecursiveDocument(doc_id int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (m *Document) ReleaseContent(book_id int) {
|
||||
|
||||
o := orm.NewOrm()
|
||||
|
||||
_,err := o.Raw("UPDATE md_documents SET `release` = content WHERE book_id =?",book_id).Exec()
|
||||
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ package models
|
|||
|
||||
import (
|
||||
"github.com/astaxie/beego/orm"
|
||||
"bytes"
|
||||
"strconv"
|
||||
"github.com/astaxie/beego"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
type DocumentTree struct {
|
||||
|
@ -9,6 +13,7 @@ type DocumentTree struct {
|
|||
DocumentName string `json:"text"`
|
||||
ParentId interface{} `json:"parent"`
|
||||
Identify string `json:"identify"`
|
||||
BookIdentify string `json:"-"`
|
||||
Version int64 `json:"version"`
|
||||
State *DocumentSelected `json:"state,omitempty"`
|
||||
}
|
||||
|
@ -17,7 +22,7 @@ type DocumentSelected struct {
|
|||
Opened bool `json:"opened"`
|
||||
}
|
||||
|
||||
|
||||
//获取项目的文档树状结构
|
||||
func (m *Document) FindDocumentTree(book_id int) ([]*DocumentTree,error){
|
||||
o := orm.NewOrm()
|
||||
|
||||
|
@ -30,6 +35,7 @@ func (m *Document) FindDocumentTree(book_id int) ([]*DocumentTree,error){
|
|||
if err != nil {
|
||||
return trees,err
|
||||
}
|
||||
book,_ := NewBook().Find(book_id)
|
||||
|
||||
trees = make([]*DocumentTree,count)
|
||||
|
||||
|
@ -41,6 +47,7 @@ func (m *Document) FindDocumentTree(book_id int) ([]*DocumentTree,error){
|
|||
tree.DocumentId = item.DocumentId
|
||||
tree.Identify = item.Identify
|
||||
tree.Version = item.Version
|
||||
tree.BookIdentify = book.Identify
|
||||
if item.ParentId > 0 {
|
||||
tree.ParentId = item.ParentId
|
||||
}else{
|
||||
|
@ -54,3 +61,102 @@ func (m *Document) FindDocumentTree(book_id int) ([]*DocumentTree,error){
|
|||
|
||||
return trees,nil
|
||||
}
|
||||
|
||||
func (m *Document) CreateDocumentTreeForHtml(book_id, selected_id int) (string,error) {
|
||||
trees,err := m.FindDocumentTree(book_id)
|
||||
if err != nil {
|
||||
return "",err
|
||||
}
|
||||
parent_id := getSelectedNode(trees,selected_id)
|
||||
|
||||
buf := bytes.NewBufferString("")
|
||||
|
||||
getDocumentTree(trees,0,selected_id,parent_id,buf)
|
||||
|
||||
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 {
|
||||
return item.DocumentId
|
||||
}else if pid,ok := item.ParentId.(int); ok && item.DocumentId == parent_id{
|
||||
return getSelectedNode(array,pid)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func getDocumentTree(array []*DocumentTree,parent_id int,selected_id int,selected_parent_id int,buf *bytes.Buffer) {
|
||||
buf.WriteString("<ul>")
|
||||
|
||||
for _,item := range array {
|
||||
pid := 0
|
||||
|
||||
if p,ok := item.ParentId.(int);ok {
|
||||
pid = p
|
||||
}
|
||||
if pid == parent_id {
|
||||
/**
|
||||
$selected = $item['doc_id'] == $selected_id ? ' class="jstree-clicked"' : '';
|
||||
$selected_li = $item['doc_id'] == $selected_parent_id ? ' class="jstree-open"' : '';
|
||||
|
||||
$menu .= '<li id="'.$item['doc_id'].'"'.$selected_li.'><a href="'. route('document.show',['doc_id'=> $item['doc_id']]) .'" title="' . htmlspecialchars($item['doc_name']) . '"'.$selected.'>' . $item['doc_name'] .'</a>';
|
||||
|
||||
$key = array_search($item['doc_id'], array_column($array, 'parent_id'));
|
||||
|
||||
if ($key !== false) {
|
||||
self::createTree($item['doc_id'], $array,$selected_id,$selected_parent_id);
|
||||
}
|
||||
$menu .= '</li>';
|
||||
*/
|
||||
selected := ""
|
||||
if item.DocumentId == selected_id {
|
||||
selected = ` class="jstree-clicked"`
|
||||
}
|
||||
selected_li := ""
|
||||
if item.DocumentId == selected_parent_id {
|
||||
selected_li = ` class="jstree-open"`
|
||||
}
|
||||
buf.WriteString("<li id=\"")
|
||||
buf.WriteString(strconv.Itoa(item.DocumentId))
|
||||
buf.WriteString("\"")
|
||||
buf.WriteString(selected_li)
|
||||
buf.WriteString("><a href=\"")
|
||||
if item.Identify != ""{
|
||||
uri := beego.URLFor("DocumentController.Read",":key",item.BookIdentify,":id" ,item.Identify)
|
||||
buf.WriteString(uri)
|
||||
}else{
|
||||
uri := beego.URLFor("DocumentController.Read",":key",item.BookIdentify,":id" ,item.DocumentId)
|
||||
buf.WriteString(uri)
|
||||
}
|
||||
buf.WriteString("\" title=\"")
|
||||
buf.WriteString(template.HTMLEscapeString(item.DocumentName) + "\"")
|
||||
buf.WriteString(selected + ">")
|
||||
buf.WriteString(template.HTMLEscapeString(item.DocumentName) + "</a>")
|
||||
|
||||
for _,sub := range array {
|
||||
if p,ok := sub.ParentId.(int);ok && p == item.DocumentId{
|
||||
getDocumentTree(array,p,selected_id,selected_parent_id,buf)
|
||||
}
|
||||
}
|
||||
buf.WriteString("</li>")
|
||||
|
||||
}
|
||||
}
|
||||
buf.WriteString("</ul>")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ func (m *Member) Login(account string,password string) (*Member,error) {
|
|||
ok,err := utils.PasswordVerify(member.Password,password) ;
|
||||
|
||||
if ok && err == nil {
|
||||
m.ResolveRoleName()
|
||||
return member,nil
|
||||
}
|
||||
|
||||
|
@ -67,22 +68,23 @@ func (m *Member) Login(account string,password string) (*Member,error) {
|
|||
}
|
||||
|
||||
// Add 添加一个用户.
|
||||
func (member *Member) Add () (error) {
|
||||
func (m *Member) Add () (error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
hash ,err := utils.PasswordHash(member.Password);
|
||||
hash ,err := utils.PasswordHash(m.Password);
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
member.Password = hash
|
||||
m.Password = hash
|
||||
|
||||
_,err = o.Insert(member)
|
||||
_,err = o.Insert(m)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.ResolveRoleName()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -103,6 +105,11 @@ func (m *Member) Find(id int) error{
|
|||
if err := o.Read(m); err != nil {
|
||||
return err
|
||||
}
|
||||
m.ResolveRoleName()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Member) ResolveRoleName (){
|
||||
if m.Role == conf.MemberSuperRole {
|
||||
m.RoleName = "超级管理员"
|
||||
}else if m.Role == conf.MemberAdminRole {
|
||||
|
@ -110,17 +117,6 @@ func (m *Member) Find(id int) error{
|
|||
}else if m.Role == conf.MemberGeneralRole {
|
||||
m.RoleName = "普通用户"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Member) ResolveRoleName (){
|
||||
if m.Role == 0 {
|
||||
m.RoleName = "超级管理员"
|
||||
}else if m.Role == 1 {
|
||||
m.RoleName = "管理员"
|
||||
}else if m.Role == 2 {
|
||||
m.RoleName = "普通用户"
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Member) FindByAccount (account string) (*Member,error) {
|
||||
|
@ -154,13 +150,7 @@ func (m *Member) FindToPager(pageIndex, pageSize int) ([]*Member,int64,error) {
|
|||
}
|
||||
|
||||
for _,m := range members {
|
||||
if m.Role == 0 {
|
||||
m.RoleName = "超级管理员"
|
||||
}else if m.Role == 1 {
|
||||
m.RoleName = "管理员"
|
||||
}else if m.Role == 2 {
|
||||
m.RoleName = "普通用户"
|
||||
}
|
||||
m.ResolveRoleName()
|
||||
}
|
||||
return members,totalCount,nil
|
||||
}
|
||||
|
|
|
@ -67,9 +67,10 @@ func GetOptionValue(key, def string) string {
|
|||
func (p *Option) InsertOrUpdate() error {
|
||||
|
||||
o := orm.NewOrm()
|
||||
|
||||
var err error
|
||||
if p.OptionId > 0 {
|
||||
_,err = o.Update(o)
|
||||
_,err = o.Update(p)
|
||||
}else{
|
||||
_,err = o.Insert(p)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package routers
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/lifei6671/godoc/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var FilterUser = func(ctx *context.Context) {
|
||||
_, ok := ctx.Input.Session(conf.LoginSessionName).(models.Member)
|
||||
|
||||
if !ok {
|
||||
ctx.Redirect(302, beego.URLFor("AccountController.Login"))
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
|
@ -12,9 +12,10 @@ func init() {
|
|||
beego.Router("/logout", &controllers.AccountController{},"*:Logout")
|
||||
beego.Router("/register", &controllers.AccountController{},"*:Register")
|
||||
beego.Router("/find_password", &controllers.AccountController{},"*:FindPassword")
|
||||
beego.Router("/captcha", &controllers.AccountController{},"*:Captcha")
|
||||
|
||||
beego.Router("/manager", &controllers.ManagerController{},"*:Index")
|
||||
beego.Router("/manager/users", &controllers.ManagerController{})
|
||||
beego.Router("/manager/users", &controllers.ManagerController{},"*:Users")
|
||||
beego.Router("/manager/member/create", &controllers.ManagerController{},"post:CreateMember")
|
||||
beego.Router("/manager/member/update-member-status",&controllers.ManagerController{},"post:UpdateMemberStatus")
|
||||
beego.Router("/manager/member/change-member-role", &controllers.ManagerController{},"post:ChangeMemberRole")
|
||||
|
@ -47,15 +48,15 @@ func init() {
|
|||
beego.Router("/book/setting/token", &controllers.BookController{},"post:CreateToken")
|
||||
beego.Router("/book/setting/delete", &controllers.BookController{},"post:Delete")
|
||||
|
||||
beego.Router("/docs/:key/edit/?:id", &controllers.DocumentController{},"*:Edit")
|
||||
beego.Router("/docs/upload",&controllers.DocumentController{},"post:Upload")
|
||||
beego.Router("/docs/:key/create",&controllers.DocumentController{},"post:Create")
|
||||
beego.Router("/docs/:key/delete", &controllers.DocumentController{},"post:Delete")
|
||||
beego.Router("/docs/:key/content/?:id",&controllers.DocumentController{},"*:Content")
|
||||
beego.Router("/api/:key/edit/?:id", &controllers.DocumentController{},"*:Edit")
|
||||
beego.Router("/api/upload",&controllers.DocumentController{},"post:Upload")
|
||||
beego.Router("/api/:key/create",&controllers.DocumentController{},"post:Create")
|
||||
beego.Router("/api/:key/delete", &controllers.DocumentController{},"post:Delete")
|
||||
beego.Router("/api/:key/content/?:id",&controllers.DocumentController{},"*:Content")
|
||||
|
||||
|
||||
beego.Router("/docs/:key", &controllers.DocumentController{},"*:Index")
|
||||
beego.Router("/docs/:key/:id", &controllers.DocumentController{},"*:Read")
|
||||
|
||||
beego.Router("/:key/attach_files/:attach_id",&controllers.DocumentController{},"get:DownloadAttachment")
|
||||
beego.Router("/attach_files/:key/:attach_id",&controllers.DocumentController{},"get:DownloadAttachment")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
cd /go/src/github.com/lifei6671/godoc/
|
||||
|
||||
goFile="godoc"
|
||||
|
||||
|
||||
chmod +x $goFile
|
||||
|
||||
if [ ! -f "conf/app.conf" ] ; then
|
||||
cp conf/app.conf.example conf/app.conf
|
||||
fi
|
||||
|
||||
if [ ! -z $db_host ] ; then
|
||||
sed -i 's/^db_host.*/db_host='$db_host'/g' conf/app.conf
|
||||
fi
|
||||
|
||||
if [ ! -z $db_port ] ; then
|
||||
sed -i 's/^db_port.*/db_port='$db_port'/g' conf/app.conf
|
||||
fi
|
||||
|
||||
if [ ! -z $db_database ] ; then
|
||||
sed -i 's/^db_database.*/db_database='$db_database'/g' conf/app.conf
|
||||
fi
|
||||
|
||||
if [ ! -z $db_username ] ; then
|
||||
sed -i 's/^db_username.*/db_username='$db_username'/g' conf/app.conf
|
||||
fi
|
||||
|
||||
if [ ! -z $db_password ] ; then
|
||||
sed -i 's/^db_password.*/db_password='$db_password'/g' conf/app.conf
|
||||
fi
|
||||
|
||||
if [ ! -z $httpport ] ; then
|
||||
sed -i 's/^httpport.*/httpport='$httpport'/g' conf/app.conf
|
||||
fi
|
||||
|
||||
|
||||
./$goFile
|
|
@ -141,6 +141,71 @@ h6 {
|
|||
zoom:1;border-bottom: 1px solid #ddd
|
||||
}
|
||||
|
||||
.m-manual .manual-tab .tab-util {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: -14px
|
||||
}
|
||||
.m-manual .manual-tab .tab-util .item {
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
margin-top: 4px
|
||||
}
|
||||
|
||||
.manual-fullscreen-switch {
|
||||
display: block
|
||||
}
|
||||
|
||||
.manual-fullscreen-switch .open,.manual-fullscreen-switch .close {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
background-color: #5cb85c;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
vertical-align: top;
|
||||
opacity : 1;
|
||||
text-shadow:none;
|
||||
font-weight: 400;
|
||||
}
|
||||
.manual-fullscreen-switch .open:hover,.manual-fullscreen-switch .close:hover {
|
||||
background-color: #449d44;
|
||||
}
|
||||
|
||||
.manual-fullscreen-switch .open:before,.manual-fullscreen-switch .close:before {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.manual-fullscreen-switch .open {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.m-manual.manual-fullscreen-active .manual-fullscreen-switch {
|
||||
/*margin-top: 30px;*/
|
||||
}
|
||||
|
||||
.m-manual.manual-fullscreen-active .manual-fullscreen-switch .open {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.m-manual.manual-fullscreen-active .manual-fullscreen-switch .close {
|
||||
display: none;
|
||||
}
|
||||
.m-manual.manual-fullscreen-active .manual-left .m-copyright,.m-manual.manual-fullscreen-active .manual-left .tab-navg,.m-manual.manual-fullscreen-active .manual-left .tab-wrap{
|
||||
display: none;
|
||||
}
|
||||
.m-manual.manual-fullscreen-active .manual-left{
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.m-manual .manual-tab .tab-navg:after {
|
||||
content: '.';
|
||||
display: block;
|
||||
|
@ -149,7 +214,7 @@ h6 {
|
|||
line-height: 9;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
visibility: hidden
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.m-manual .manual-tab .tab-navg .navg-item {
|
||||
|
@ -246,6 +311,9 @@ h6 {
|
|||
-o-transition-timing-function: linear;
|
||||
-o-transition-delay: 0s
|
||||
}
|
||||
.m-manual.manual-fullscreen-active .manual-right{
|
||||
left: 0;
|
||||
}
|
||||
.m-manual .manual-right .manual-article{
|
||||
background: #ffffff;
|
||||
}
|
||||
|
@ -266,7 +334,9 @@ h6 {
|
|||
color: #7e888b
|
||||
}
|
||||
.manual-article .article-content{
|
||||
max-width: 980px;
|
||||
min-width: 980px;
|
||||
max-width: 98%;
|
||||
padding: 10px 20px;
|
||||
margin-left: auto!important;
|
||||
margin-right: auto!important
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ textarea{
|
|||
position: relative;
|
||||
}
|
||||
.manual-list .list-item .manual-item-standard .cover {
|
||||
border: 1px solid #f3f3f3;
|
||||
border: 1px solid #999999;
|
||||
width: 171px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
|
|
@ -84,6 +84,7 @@ $(function () {
|
|||
if(Object.prototype.toString.call(window.documentCategory) === '[object Array]' && window.documentCategory.length > 0){
|
||||
$.ajax({
|
||||
url : window.releaseURL,
|
||||
data :{"identify" : window.book.identify },
|
||||
type : "post",
|
||||
dataType : "json",
|
||||
success : function (res) {
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/* Make clicks pass-through */
|
||||
#nprogress {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#nprogress .bar {
|
||||
background: #29d;
|
||||
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
/* Fancy blur effect */
|
||||
#nprogress .peg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
|
||||
opacity: 1.0;
|
||||
|
||||
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
||||
-ms-transform: rotate(3deg) translate(0px, -4px);
|
||||
transform: rotate(3deg) translate(0px, -4px);
|
||||
}
|
||||
|
||||
/* Remove these to get rid of the spinner */
|
||||
#nprogress .spinner {
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
#nprogress .spinner-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
box-sizing: border-box;
|
||||
|
||||
border: solid 2px transparent;
|
||||
border-top-color: #29d;
|
||||
border-left-color: #29d;
|
||||
border-radius: 50%;
|
||||
|
||||
-webkit-animation: nprogress-spinner 400ms linear infinite;
|
||||
animation: nprogress-spinner 400ms linear infinite;
|
||||
}
|
||||
|
||||
.nprogress-custom-parent {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nprogress-custom-parent #nprogress .spinner,
|
||||
.nprogress-custom-parent #nprogress .bar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@-webkit-keyframes nprogress-spinner {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
@keyframes nprogress-spinner {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
|
@ -0,0 +1,476 @@
|
|||
/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
|
||||
* @license MIT */
|
||||
|
||||
;(function(root, factory) {
|
||||
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.NProgress = factory();
|
||||
}
|
||||
|
||||
})(this, function() {
|
||||
var NProgress = {};
|
||||
|
||||
NProgress.version = '0.2.0';
|
||||
|
||||
var Settings = NProgress.settings = {
|
||||
minimum: 0.08,
|
||||
easing: 'ease',
|
||||
positionUsing: '',
|
||||
speed: 200,
|
||||
trickle: true,
|
||||
trickleRate: 0.02,
|
||||
trickleSpeed: 800,
|
||||
showSpinner: true,
|
||||
barSelector: '[role="bar"]',
|
||||
spinnerSelector: '[role="spinner"]',
|
||||
parent: 'body',
|
||||
template: '<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates configuration.
|
||||
*
|
||||
* NProgress.configure({
|
||||
* minimum: 0.1
|
||||
* });
|
||||
*/
|
||||
NProgress.configure = function(options) {
|
||||
var key, value;
|
||||
for (key in options) {
|
||||
value = options[key];
|
||||
if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Last number.
|
||||
*/
|
||||
|
||||
NProgress.status = null;
|
||||
|
||||
/**
|
||||
* Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
|
||||
*
|
||||
* NProgress.set(0.4);
|
||||
* NProgress.set(1.0);
|
||||
*/
|
||||
|
||||
NProgress.set = function(n) {
|
||||
var started = NProgress.isStarted();
|
||||
|
||||
n = clamp(n, Settings.minimum, 1);
|
||||
NProgress.status = (n === 1 ? null : n);
|
||||
|
||||
var progress = NProgress.render(!started),
|
||||
bar = progress.querySelector(Settings.barSelector),
|
||||
speed = Settings.speed,
|
||||
ease = Settings.easing;
|
||||
|
||||
progress.offsetWidth; /* Repaint */
|
||||
|
||||
queue(function(next) {
|
||||
// Set positionUsing if it hasn't already been set
|
||||
if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();
|
||||
|
||||
// Add transition
|
||||
css(bar, barPositionCSS(n, speed, ease));
|
||||
|
||||
if (n === 1) {
|
||||
// Fade out
|
||||
css(progress, {
|
||||
transition: 'none',
|
||||
opacity: 1
|
||||
});
|
||||
progress.offsetWidth; /* Repaint */
|
||||
|
||||
setTimeout(function() {
|
||||
css(progress, {
|
||||
transition: 'all ' + speed + 'ms linear',
|
||||
opacity: 0
|
||||
});
|
||||
setTimeout(function() {
|
||||
NProgress.remove();
|
||||
next();
|
||||
}, speed);
|
||||
}, speed);
|
||||
} else {
|
||||
setTimeout(next, speed);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
NProgress.isStarted = function() {
|
||||
return typeof NProgress.status === 'number';
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the progress bar.
|
||||
* This is the same as setting the status to 0%, except that it doesn't go backwards.
|
||||
*
|
||||
* NProgress.start();
|
||||
*
|
||||
*/
|
||||
NProgress.start = function() {
|
||||
if (!NProgress.status) NProgress.set(0);
|
||||
|
||||
var work = function() {
|
||||
setTimeout(function() {
|
||||
if (!NProgress.status) return;
|
||||
NProgress.trickle();
|
||||
work();
|
||||
}, Settings.trickleSpeed);
|
||||
};
|
||||
|
||||
if (Settings.trickle) work();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the progress bar.
|
||||
* This is the *sort of* the same as setting the status to 100%, with the
|
||||
* difference being `done()` makes some placebo effect of some realistic motion.
|
||||
*
|
||||
* NProgress.done();
|
||||
*
|
||||
* If `true` is passed, it will show the progress bar even if its hidden.
|
||||
*
|
||||
* NProgress.done(true);
|
||||
*/
|
||||
|
||||
NProgress.done = function(force) {
|
||||
if (!force && !NProgress.status) return this;
|
||||
|
||||
return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Increments by a random amount.
|
||||
*/
|
||||
|
||||
NProgress.inc = function(amount) {
|
||||
var n = NProgress.status;
|
||||
|
||||
if (!n) {
|
||||
return NProgress.start();
|
||||
} else {
|
||||
if (typeof amount !== 'number') {
|
||||
amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95);
|
||||
}
|
||||
|
||||
n = clamp(n + amount, 0, 0.994);
|
||||
return NProgress.set(n);
|
||||
}
|
||||
};
|
||||
|
||||
NProgress.trickle = function() {
|
||||
return NProgress.inc(Math.random() * Settings.trickleRate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits for all supplied jQuery promises and
|
||||
* increases the progress as the promises resolve.
|
||||
*
|
||||
* @param $promise jQUery Promise
|
||||
*/
|
||||
(function() {
|
||||
var initial = 0, current = 0;
|
||||
|
||||
NProgress.promise = function($promise) {
|
||||
if (!$promise || $promise.state() === "resolved") {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (current === 0) {
|
||||
NProgress.start();
|
||||
}
|
||||
|
||||
initial++;
|
||||
current++;
|
||||
|
||||
$promise.always(function() {
|
||||
current--;
|
||||
if (current === 0) {
|
||||
initial = 0;
|
||||
NProgress.done();
|
||||
} else {
|
||||
NProgress.set((initial - current) / initial);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
/**
|
||||
* (Internal) renders the progress bar markup based on the `template`
|
||||
* setting.
|
||||
*/
|
||||
|
||||
NProgress.render = function(fromStart) {
|
||||
if (NProgress.isRendered()) return document.getElementById('nprogress');
|
||||
|
||||
addClass(document.documentElement, 'nprogress-busy');
|
||||
|
||||
var progress = document.createElement('div');
|
||||
progress.id = 'nprogress';
|
||||
progress.innerHTML = Settings.template;
|
||||
|
||||
var bar = progress.querySelector(Settings.barSelector),
|
||||
perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0),
|
||||
parent = document.querySelector(Settings.parent),
|
||||
spinner;
|
||||
|
||||
css(bar, {
|
||||
transition: 'all 0 linear',
|
||||
transform: 'translate3d(' + perc + '%,0,0)'
|
||||
});
|
||||
|
||||
if (!Settings.showSpinner) {
|
||||
spinner = progress.querySelector(Settings.spinnerSelector);
|
||||
spinner && removeElement(spinner);
|
||||
}
|
||||
|
||||
if (parent != document.body) {
|
||||
addClass(parent, 'nprogress-custom-parent');
|
||||
}
|
||||
|
||||
parent.appendChild(progress);
|
||||
return progress;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the element. Opposite of render().
|
||||
*/
|
||||
|
||||
NProgress.remove = function() {
|
||||
removeClass(document.documentElement, 'nprogress-busy');
|
||||
removeClass(document.querySelector(Settings.parent), 'nprogress-custom-parent');
|
||||
var progress = document.getElementById('nprogress');
|
||||
progress && removeElement(progress);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the progress bar is rendered.
|
||||
*/
|
||||
|
||||
NProgress.isRendered = function() {
|
||||
return !!document.getElementById('nprogress');
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine which positioning CSS rule to use.
|
||||
*/
|
||||
|
||||
NProgress.getPositioningCSS = function() {
|
||||
// Sniff on document.body.style
|
||||
var bodyStyle = document.body.style;
|
||||
|
||||
// Sniff prefixes
|
||||
var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
|
||||
('MozTransform' in bodyStyle) ? 'Moz' :
|
||||
('msTransform' in bodyStyle) ? 'ms' :
|
||||
('OTransform' in bodyStyle) ? 'O' : '';
|
||||
|
||||
if (vendorPrefix + 'Perspective' in bodyStyle) {
|
||||
// Modern browsers with 3D support, e.g. Webkit, IE10
|
||||
return 'translate3d';
|
||||
} else if (vendorPrefix + 'Transform' in bodyStyle) {
|
||||
// Browsers without 3D support, e.g. IE9
|
||||
return 'translate';
|
||||
} else {
|
||||
// Browsers without translate() support, e.g. IE7-8
|
||||
return 'margin';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function clamp(n, min, max) {
|
||||
if (n < min) return min;
|
||||
if (n > max) return max;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) converts a percentage (`0..1`) to a bar translateX
|
||||
* percentage (`-100%..0%`).
|
||||
*/
|
||||
|
||||
function toBarPerc(n) {
|
||||
return (-1 + n) * 100;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Internal) returns the correct CSS for changing the bar's
|
||||
* position given an n percentage, and speed and ease from Settings
|
||||
*/
|
||||
|
||||
function barPositionCSS(n, speed, ease) {
|
||||
var barCSS;
|
||||
|
||||
if (Settings.positionUsing === 'translate3d') {
|
||||
barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
|
||||
} else if (Settings.positionUsing === 'translate') {
|
||||
barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
|
||||
} else {
|
||||
barCSS = { 'margin-left': toBarPerc(n)+'%' };
|
||||
}
|
||||
|
||||
barCSS.transition = 'all '+speed+'ms '+ease;
|
||||
|
||||
return barCSS;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Queues a function to be executed.
|
||||
*/
|
||||
|
||||
var queue = (function() {
|
||||
var pending = [];
|
||||
|
||||
function next() {
|
||||
var fn = pending.shift();
|
||||
if (fn) {
|
||||
fn(next);
|
||||
}
|
||||
}
|
||||
|
||||
return function(fn) {
|
||||
pending.push(fn);
|
||||
if (pending.length == 1) next();
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* (Internal) Applies css properties to an element, similar to the jQuery
|
||||
* css method.
|
||||
*
|
||||
* While this helper does assist with vendor prefixed property names, it
|
||||
* does not perform any manipulation of values prior to setting styles.
|
||||
*/
|
||||
|
||||
var css = (function() {
|
||||
var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ],
|
||||
cssProps = {};
|
||||
|
||||
function camelCase(string) {
|
||||
return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) {
|
||||
return letter.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function getVendorProp(name) {
|
||||
var style = document.body.style;
|
||||
if (name in style) return name;
|
||||
|
||||
var i = cssPrefixes.length,
|
||||
capName = name.charAt(0).toUpperCase() + name.slice(1),
|
||||
vendorName;
|
||||
while (i--) {
|
||||
vendorName = cssPrefixes[i] + capName;
|
||||
if (vendorName in style) return vendorName;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function getStyleProp(name) {
|
||||
name = camelCase(name);
|
||||
return cssProps[name] || (cssProps[name] = getVendorProp(name));
|
||||
}
|
||||
|
||||
function applyCss(element, prop, value) {
|
||||
prop = getStyleProp(prop);
|
||||
element.style[prop] = value;
|
||||
}
|
||||
|
||||
return function(element, properties) {
|
||||
var args = arguments,
|
||||
prop,
|
||||
value;
|
||||
|
||||
if (args.length == 2) {
|
||||
for (prop in properties) {
|
||||
value = properties[prop];
|
||||
if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value);
|
||||
}
|
||||
} else {
|
||||
applyCss(element, args[1], args[2]);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* (Internal) Determines if an element or space separated list of class names contains a class name.
|
||||
*/
|
||||
|
||||
function hasClass(element, name) {
|
||||
var list = typeof element == 'string' ? element : classList(element);
|
||||
return list.indexOf(' ' + name + ' ') >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Adds a class to an element.
|
||||
*/
|
||||
|
||||
function addClass(element, name) {
|
||||
var oldList = classList(element),
|
||||
newList = oldList + name;
|
||||
|
||||
if (hasClass(oldList, name)) return;
|
||||
|
||||
// Trim the opening space.
|
||||
element.className = newList.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Removes a class from an element.
|
||||
*/
|
||||
|
||||
function removeClass(element, name) {
|
||||
var oldList = classList(element),
|
||||
newList;
|
||||
|
||||
if (!hasClass(element, name)) return;
|
||||
|
||||
// Replace the class name.
|
||||
newList = oldList.replace(' ' + name + ' ', ' ');
|
||||
|
||||
// Trim the opening and closing spaces.
|
||||
element.className = newList.substring(1, newList.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Gets a space separated list of the class names on the element.
|
||||
* The list is wrapped with a single space on each end to facilitate finding
|
||||
* matches within the list.
|
||||
*/
|
||||
|
||||
function classList(element) {
|
||||
return (' ' + (element.className || '') + ' ').replace(/\s+/gi, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Removes an element from the DOM.
|
||||
*/
|
||||
|
||||
function removeElement(element) {
|
||||
element && element.parentNode && element.parentNode.removeChild(element);
|
||||
}
|
||||
|
||||
return NProgress;
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="zh-CN" xmlns="http://www.w3.org/1999/html">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
@ -51,7 +51,7 @@
|
|||
<a href="{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" target="_blank"><i class="fa fa-eye"></i> 阅读</a>
|
||||
|
||||
{{if eq .Model.RoleId 0 1 2}}
|
||||
<a href="{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" target="_blank"><i class="fa fa-upload" aria-hidden="true"></i> 发布</a>
|
||||
<button class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" id="btnRelease"><i class="fa fa-upload" aria-hidden="true"></i> 发布</button>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -98,7 +98,27 @@
|
|||
</div>
|
||||
<script src="/static/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="/static/layer/layer.js"></script>
|
||||
<script src="/static/js/main.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$("#btnRelease").on("click",function () {
|
||||
$.ajax({
|
||||
url : "{{urlfor "BookController.Release" ":key" .Model.Identify}}",
|
||||
data :{"identify" : "{{.Model.Identify}}" },
|
||||
type : "post",
|
||||
dataType : "json",
|
||||
success : function (res) {
|
||||
if(res.errcode === 0){
|
||||
layer.msg("发布任务已推送到任务队列,稍后将在后台执行。");
|
||||
}else{
|
||||
layer.msg(res.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -60,7 +60,7 @@
|
|||
<div class="pull-right">
|
||||
<a :href="'{{urlfor "DocumentController.Index" ":key" ""}}' + item.identify" title="查看文档" data-toggle="tooltip"><i class="fa fa-eye"></i> 查看文档</a>
|
||||
<template v-if="item.role_id != 3">
|
||||
<a :href="'/docs/' + item.identify + '/edit'" title="编辑文档" data-toggle="tooltip"><i class="fa fa-edit" aria-hidden="true"></i> 编辑文档</a>
|
||||
<a :href="'/api/' + item.identify + '/edit'" title="编辑文档" data-toggle="tooltip"><i class="fa fa-edit" aria-hidden="true"></i> 编辑文档</a>
|
||||
</template>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>编辑文档 - Powered by MinDoc</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/font-awesome/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="/static/jstree/3.3.4/themes/default/style.min.css" rel="stylesheet">
|
||||
|
||||
<link href="/static/nprogress/nprogress.css" rel="stylesheet">
|
||||
<link href="/static/css/kancloud.css" rel="stylesheet">
|
||||
<link href="/static/css/jstree.css" rel="stylesheet">
|
||||
{{if eq .Model.Editor "markdown"}}
|
||||
<link href="/static/editor.md/css/editormd.preview.css" rel="stylesheet">
|
||||
{{else}}
|
||||
<link href="/static/highlight/styles/zenburn.css" rel="stylesheet">
|
||||
{{end}}
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="/static/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="/static/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div class="m-manual manual-reader">
|
||||
<header class="navbar navbar-static-top manual-head" role="banner">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header pull-left manual-title">
|
||||
<span class="slidebar" id="slidebar"><i class="fa fa-align-justify"></i></span>
|
||||
{{.Model.BookName}}
|
||||
<span style="font-size: 12px;font-weight: 100;"></span>
|
||||
</div>
|
||||
<div class="navbar-header pull-right manual-menu">
|
||||
<div class="dropdown">
|
||||
<button id="dLabel" class="btn btn-default" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
项目
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="dLabel">
|
||||
{{if gt .Member.MemberId 0}}
|
||||
{{if eq .Model.RoleId 0 1 2}}
|
||||
<li><a href="{{urlfor "DocumentController.Edit" ":key" .Model.Identify ":id" ""}}">返回编辑</a> </li>
|
||||
{{end}}
|
||||
<li><a href="{{urlfor "BookController.Index"}}">我的项目</a> </li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
{{end}}
|
||||
{{if eq .Model.PrivatelyOwned 0}}
|
||||
<li><a href="javascript:" data-toggle="modal" data-target="#shareProject">项目分享</a> </li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
{{/*<li><a href="https://wiki.iminho.me/export/1" target="_blank">项目导出</a> </li>*/}}
|
||||
{{end}}
|
||||
|
||||
<li><a href="{{urlfor "HomeController.Index"}}" title="返回首页">返回首页</a> </li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<article class="container-fluid manual-body">
|
||||
<div class="manual-left">
|
||||
<div class="manual-tab">
|
||||
<div class="tab-navg">
|
||||
<span data-mode="view" class="navg-item active"><i class="fa fa-align-justify"></i><b class="text">目录</b></span>
|
||||
</div>
|
||||
<div class="tab-util">
|
||||
<span class="manual-fullscreen-switch">
|
||||
<b class="open fa fa-angle-right" title="展开"></b>
|
||||
<b class="close fa fa-angle-left" title="关闭"></b>
|
||||
</span>
|
||||
</div>
|
||||
<div class="tab-wrap">
|
||||
<div class="tab-item manual-catalog">
|
||||
<div class="catalog-list read-book-preview" id="sidebar">
|
||||
{{.Result}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-copyright">
|
||||
<p>
|
||||
本文档使用 <a href="https://doc.iminho.me" target="_blank">MinDoc</a> 发布
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="manual-right">
|
||||
<div class="manual-article">
|
||||
<div class="article-head">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
|
||||
</div>
|
||||
<div class="col-md-8 text-center">
|
||||
<h1 id="article-title">{{.Title}}</h1>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="article-content">
|
||||
<div class="article-body {{if eq .Model.Editor "markdown"}}markdown-body editormd-preview-container{{else}}editor-content{{end}}" id="page-content">
|
||||
{{.Content}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="manual-progress"><b class="progress-bar"></b></div>
|
||||
</article>
|
||||
<div class="manual-mask"></div>
|
||||
</div>
|
||||
|
||||
<!-- Share Modal -->
|
||||
<div class="modal fade" id="shareProject" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
|
||||
<h4 class="modal-title" id="myModalLabel">项目分享</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="password" class="col-sm-2 control-label">项目地址</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" value="{{.BaseUrl}}{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" class="form-control" onmouseover="this.select()" id="projectUrl" title="项目地址">
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="/static/jstree/3.3.4/jstree.min.js" type="text/javascript"></script>
|
||||
<script type="text/javascript" src="/static/nprogress/nprogress.js"></script>
|
||||
<script type="text/javascript" src="/static/highlight/highlight.js"></script>
|
||||
<script type="text/javascript" src="/static/highlight/highlightjs-line-numbers.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var events = $("body");
|
||||
var catalog = null;
|
||||
function initHighlighting() {
|
||||
$('pre code').each(function (i, block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
|
||||
hljs.initLineNumbersOnLoad();
|
||||
}
|
||||
|
||||
$(function () {
|
||||
window.isFullScreen = false;
|
||||
|
||||
initHighlighting();
|
||||
|
||||
$("#sidebar").jstree({
|
||||
'plugins':["wholerow","types"],
|
||||
"types": {
|
||||
"default" : {
|
||||
"icon" : false // 删除默认图标
|
||||
}
|
||||
},
|
||||
'core' : {
|
||||
'check_callback' : true,
|
||||
"multiple" : false ,
|
||||
'animation' : 0
|
||||
}
|
||||
}).on('select_node.jstree',function (node,selected,event) {
|
||||
$(".m-manual").removeClass('manual-mobile-show-left');
|
||||
var url = selected.node.a_attr.href;
|
||||
|
||||
if(url === window.location.href){
|
||||
return false;
|
||||
}
|
||||
$.ajax({
|
||||
url : url,
|
||||
type : "GET",
|
||||
beforeSend :function (xhr) {
|
||||
var body = events.data('body_' + selected.node.id);
|
||||
var title = events.data('title_' + selected.node.id);
|
||||
var doc_title = events.data('doc_title_' + selected.node.id);
|
||||
|
||||
if(body && title && doc_title){
|
||||
|
||||
$("#page-content").html(body);
|
||||
$("title").text(title);
|
||||
$("#article-title").text(doc_title);
|
||||
|
||||
events.trigger('article.open',url,true);
|
||||
|
||||
return false;
|
||||
}
|
||||
NProgress.start();
|
||||
},
|
||||
success : function (res) {
|
||||
if(res.errcode === 0){
|
||||
var body = res.data.body;
|
||||
var doc_title = res.data.doc_title;
|
||||
var title = res.data.title;
|
||||
|
||||
$("#page-content").html(body);
|
||||
$("title").text(title);
|
||||
$("#article-title").text(doc_title);
|
||||
|
||||
events.data('body_' + selected.node.id,body);
|
||||
events.data('title_' + selected.node.id,title);
|
||||
events.data('doc_title_' + selected.node.id,doc_title);
|
||||
|
||||
events.trigger('article.open',url,false);
|
||||
|
||||
}else{
|
||||
layer.msg("加载失败");
|
||||
}
|
||||
},
|
||||
complete : function () {
|
||||
NProgress.done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#slidebar").on("click",function () {
|
||||
$(".m-manual").addClass('manual-mobile-show-left');
|
||||
});
|
||||
$(".manual-mask").on("click",function () {
|
||||
$(".m-manual").removeClass('manual-mobile-show-left');
|
||||
});
|
||||
|
||||
$(".manual-fullscreen-switch").on("click",function () {
|
||||
isFullScreen = !isFullScreen;
|
||||
if (isFullScreen) {
|
||||
$(".m-manual").addClass('manual-fullscreen-active');
|
||||
} else {
|
||||
$(".m-manual").removeClass('manual-fullscreen-active');
|
||||
}
|
||||
});
|
||||
events.on('article.open', function (event, url,init) {
|
||||
if ('pushState' in history) {
|
||||
|
||||
if (init == false) {
|
||||
history.replaceState({ }, '', url);
|
||||
init = true;
|
||||
} else {
|
||||
history.pushState({ }, '', url);
|
||||
}
|
||||
|
||||
} else {
|
||||
location.hash = url;
|
||||
}
|
||||
initHighlighting();
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -129,7 +129,7 @@
|
|||
<script type="text/javascript" src="/static/layer/layer.js"></script>
|
||||
<script src="/static/to-markdown/dist/to-markdown.js" type="text/javascript"></script>
|
||||
<script src="/static/js/jquery.form.js" type="text/javascript"></script>
|
||||
<script src="/static/js/edirot.js" type="text/javascript"></script>
|
||||
<script src="/static/js/editor.js" type="text/javascript"></script>
|
||||
<script src="/static/js/html-editor.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -160,7 +160,7 @@
|
|||
<script src="/static/editor.md/editormd.js" type="text/javascript"></script>
|
||||
<script type="text/javascript" src="/static/layer/layer.js"></script>
|
||||
<script src="/static/js/jquery.form.js" type="text/javascript"></script>
|
||||
<script src="/static/js/edirot.js" type="text/javascript"></script>
|
||||
<script src="/static/js/editor.js" type="text/javascript"></script>
|
||||
<script src="/static/js/markdown.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,10 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -25,158 +25,27 @@
|
|||
<div class="container manual-body">
|
||||
<div class="row">
|
||||
<div class="manual-list">
|
||||
{{range $index,$item := .Lists}}
|
||||
<div class="list-item">
|
||||
<dl class="manual-item-standard">
|
||||
<dt>
|
||||
<a href="{{urlfor "DocumentController.Index" ":key" "test"}}">
|
||||
<img src="/uploads/201704/b4c17ca29fe7b7f4dec402d7dd7543c6_100.png" class="cover">
|
||||
<a href="{{urlfor "DocumentController.Index" ":key" $item.Identify}}" title="{{$item.BookName}}-{{$item.CreateName}}">
|
||||
<img src="{{$item.Cover}}" class="cover" alt="{{$item.BookName}}-{{$item.CreateName}}">
|
||||
</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<a href="#" class="name">Docker生产环境实践指南</a>
|
||||
<a href="{{urlfor "DocumentController.Index" ":key" $item.Identify}}" class="name" title="{{$item.BookName}}-{{$item.CreateName}}">{{$item.BookName}}</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<span class="author">
|
||||
<b class="text">作者</b>
|
||||
<b class="text">-</b>
|
||||
<b class="text">Minho</b>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<dl class="manual-item-standard">
|
||||
<dt>
|
||||
<a href="#">
|
||||
<img src="/uploads/201704/b4c17ca29fe7b7f4dec402d7dd7543c6_100.png">
|
||||
</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<a href="#" class="name">Docker生产环境实践指南</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<span class="author">
|
||||
<b class="text">作者</b>
|
||||
<b class="text">-</b>
|
||||
<b class="text">Minho</b>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<dl class="manual-item-standard">
|
||||
<dt>
|
||||
<a href="#">
|
||||
<img src="/uploads/201704/b4c17ca29fe7b7f4dec402d7dd7543c6_100.png">
|
||||
</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<a href="#" class="name">Docker生产环境实践指南</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<span class="author">
|
||||
<b class="text">作者</b>
|
||||
<b class="text">-</b>
|
||||
<b class="text">Minho</b>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<dl class="manual-item-standard">
|
||||
<dt>
|
||||
<a href="#">
|
||||
<img src="/uploads/201704/b4c17ca29fe7b7f4dec402d7dd7543c6_100.png">
|
||||
</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<a href="#" class="name">Docker生产环境实践指南</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<span class="author">
|
||||
<b class="text">作者</b>
|
||||
<b class="text">-</b>
|
||||
<b class="text">Minho</b>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<dl class="manual-item-standard">
|
||||
<dt>
|
||||
<a href="#">
|
||||
<img src="/uploads/201704/b4c17ca29fe7b7f4dec402d7dd7543c6_100.png">
|
||||
</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<a href="#" class="name">Docker生产环境实践指南</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<span class="author">
|
||||
<b class="text">作者</b>
|
||||
<b class="text">-</b>
|
||||
<b class="text">Minho</b>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<dl class="manual-item-standard">
|
||||
<dt>
|
||||
<a href="#">
|
||||
<img src="/uploads/201704/b4c17ca29fe7b7f4dec402d7dd7543c6_100.png">
|
||||
</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<a href="#" class="name">Docker生产环境实践指南</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<span class="author">
|
||||
<b class="text">作者</b>
|
||||
<b class="text">-</b>
|
||||
<b class="text">Minho</b>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<dl class="manual-item-standard">
|
||||
<dt>
|
||||
<a href="#">
|
||||
<img src="/uploads/201704/b4c17ca29fe7b7f4dec402d7dd7543c6_100.png">
|
||||
</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<a href="#" class="name">Docker生产环境实践指南</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<span class="author">
|
||||
<b class="text">作者</b>
|
||||
<b class="text">-</b>
|
||||
<b class="text">Minho</b>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<dl class="manual-item-standard">
|
||||
<dt>
|
||||
<a href="#">
|
||||
<img src="/uploads/201704/b4c17ca29fe7b7f4dec402d7dd7543c6_100.png">
|
||||
</a>
|
||||
</dt>
|
||||
<dd>
|
||||
<a href="#" class="name">Docker生产环境实践指南</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<span class="author">
|
||||
<b class="text">作者</b>
|
||||
<b class="text">-</b>
|
||||
<b class="text">Minho</b>
|
||||
<b class="text">{{$item.CreateName}}</b>
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -99,7 +99,21 @@
|
|||
<script src="/static/js/main.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$("#gloablEditForm").ajaxForm({
|
||||
beforeSubmit : function () {
|
||||
var title = $.trim($("#siteName").val());
|
||||
|
||||
if (title === ""){
|
||||
return showError("网站标题不能为空");
|
||||
}
|
||||
},success : function (res) {
|
||||
if(res.errcode === 0) {
|
||||
showSuccess("保存成功")
|
||||
}else{
|
||||
showError(res.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
<header class="navbar navbar-static-top navbar-fixed-top manual-header" role="banner">
|
||||
<div class="container">
|
||||
<div class="navbar-header col-sm-12 col-md-6 col-lg-5">
|
||||
<a href="/" class="navbar-brand">MinDoc</a>
|
||||
<a href="/" class="navbar-brand">
|
||||
{{if .SITE_TITLE}}
|
||||
{{.SITE_TITLE}}
|
||||
{{else}}
|
||||
{{.SITE_NAME}}
|
||||
{{end}}
|
||||
</a>
|
||||
<div class="btn-group dropdown-menu-right pull-right slidebar visible-xs-inline-block visible-sm-inline-block">
|
||||
<button class="btn btn-default dropdown-toggle hidden-lg" type="button" data-toggle="dropdown"><i class="fa fa-align-justify"></i></button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
|
@ -19,6 +25,7 @@
|
|||
</div>
|
||||
<nav class="navbar-collapse hidden-xs hidden-sm" role="navigation">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{{if gt .Member.MemberId 0}}
|
||||
<li>
|
||||
<div class="img user-info" data-toggle="dropdown">
|
||||
<img src="{{.Member.Avatar}}" class="img-circle userbar-avatar">
|
||||
|
@ -45,6 +52,9 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{{else}}
|
||||
<li><a href="{{urlfor "AccountController.Login"}}" title="用户登录">登录</a></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue