Merge branch 'feature-pdf'
This commit is contained in:
@ -2,17 +2,20 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/revel/revel"
|
"github.com/revel/revel"
|
||||||
// "encoding/json"
|
// "encoding/json"
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
. "github.com/leanote/leanote/app/lea"
|
|
||||||
"github.com/leanote/leanote/app/info"
|
"github.com/leanote/leanote/app/info"
|
||||||
|
. "github.com/leanote/leanote/app/lea"
|
||||||
|
"gopkg.in/mgo.v2/bson"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
// "time"
|
"time"
|
||||||
// "github.com/leanote/leanote/app/types"
|
"fmt"
|
||||||
// "io/ioutil"
|
// "github.com/leanote/leanote/app/types"
|
||||||
// "fmt"
|
// "io/ioutil"
|
||||||
// "bytes"
|
// "bytes"
|
||||||
// "os"
|
// "os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Note struct {
|
type Note struct {
|
||||||
@ -293,6 +296,165 @@ func (c Note) SearchNoteByTags(tags []string) revel.Result {
|
|||||||
return c.RenderJson(blogs)
|
return c.RenderJson(blogs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成PDF
|
||||||
|
func (c Note) ToPdf(noteId, appKey string) revel.Result {
|
||||||
|
// 虽然传了cookie但是这里还是不能得到userId, 所以还是通过appKey来验证之
|
||||||
|
appKeyTrue, _ := revel.Config.String("app.secret")
|
||||||
|
if appKeyTrue != appKey {
|
||||||
|
return c.RenderText("error")
|
||||||
|
}
|
||||||
|
note := noteService.GetNoteById(noteId)
|
||||||
|
if note.NoteId == "" {
|
||||||
|
return c.RenderText("error")
|
||||||
|
}
|
||||||
|
|
||||||
|
noteUserId := note.UserId.Hex()
|
||||||
|
content := noteService.GetNoteContent(noteId, noteUserId)
|
||||||
|
userInfo := userService.GetUserInfo(noteUserId)
|
||||||
|
|
||||||
|
//------------------
|
||||||
|
// 将content的图片转换为base64
|
||||||
|
contentStr := content.Content
|
||||||
|
|
||||||
|
siteUrlPattern := configService.GetSiteUrl()
|
||||||
|
if strings.Contains(siteUrlPattern, "https") {
|
||||||
|
siteUrlPattern = strings.Replace(siteUrlPattern, "https", "https*", 1)
|
||||||
|
} else {
|
||||||
|
siteUrlPattern = strings.Replace(siteUrlPattern, "http", "https*", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
regImage, _ := regexp.Compile(`<img .*?(src=('|")` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})("|'))`)
|
||||||
|
|
||||||
|
findsImage := regImage.FindAllStringSubmatch(contentStr, -1) // 查找所有的
|
||||||
|
// [<img src="http://leanote.com/api/getImage?fileId=3354672e8d38f411286b000069" alt="" width="692" height="302" data-mce-src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " file/outputImage 54672e8d38f411286b000069 "]
|
||||||
|
for _, eachFind := range findsImage {
|
||||||
|
if len(eachFind) == 6 {
|
||||||
|
fileId := eachFind[4]
|
||||||
|
// 得到base64编码文件
|
||||||
|
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
|
||||||
|
if fileBase64 == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1
|
||||||
|
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
|
||||||
|
allFixed := strings.Replace(eachFind[0], eachFind[1], "src=\""+fileBase64+"\"", -1)
|
||||||
|
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// markdown
|
||||||
|
if note.IsMarkdown {
|
||||||
|
// 
|
||||||
|
regImageMarkdown, _ := regexp.Compile(`!\[.*?\]\(` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})\)`)
|
||||||
|
findsImageMarkdown := regImageMarkdown.FindAllStringSubmatch(contentStr, -1) // 查找所有的
|
||||||
|
for _, eachFind := range findsImageMarkdown {
|
||||||
|
if len(eachFind) == 3 {
|
||||||
|
fileId := eachFind[2]
|
||||||
|
// 得到base64编码文件
|
||||||
|
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
|
||||||
|
if fileBase64 == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1
|
||||||
|
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
|
||||||
|
allFixed := ""
|
||||||
|
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if note.Tags != nil && len(note.Tags) > 0 && note.Tags[0] != "" {
|
||||||
|
} else {
|
||||||
|
note.Tags = nil
|
||||||
|
}
|
||||||
|
c.RenderArgs["blog"] = note
|
||||||
|
c.RenderArgs["content"] = contentStr
|
||||||
|
c.RenderArgs["userInfo"] = userInfo
|
||||||
|
userBlog := blogService.GetUserBlog(noteUserId)
|
||||||
|
c.RenderArgs["userBlog"] = userBlog
|
||||||
|
|
||||||
|
return c.RenderTemplate("file/pdf.html")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出成PDF
|
||||||
|
func (c Note) ExportPdf(noteId string) revel.Result {
|
||||||
|
re := info.NewRe()
|
||||||
|
userId := c.GetUserId()
|
||||||
|
note := noteService.GetNoteById(noteId)
|
||||||
|
if note.NoteId == "" {
|
||||||
|
re.Msg = "No Note"
|
||||||
|
return c.RenderText("error")
|
||||||
|
}
|
||||||
|
|
||||||
|
noteUserId := note.UserId.Hex()
|
||||||
|
// 是否有权限
|
||||||
|
if noteUserId != userId {
|
||||||
|
// 是否是有权限协作的
|
||||||
|
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
|
||||||
|
re.Msg = "No Perm"
|
||||||
|
return c.RenderText("error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// path 判断是否需要重新生成之
|
||||||
|
guid := NewGuid()
|
||||||
|
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
|
||||||
|
dir := revel.BasePath + "/" + fileUrlPath
|
||||||
|
if !MkdirAll(dir) {
|
||||||
|
return c.RenderText("error, no dir")
|
||||||
|
}
|
||||||
|
filename := guid + ".pdf"
|
||||||
|
path := dir + "/" + filename
|
||||||
|
|
||||||
|
// leanote.com的secret
|
||||||
|
appKey, _ := revel.Config.String("app.secretLeanote")
|
||||||
|
if appKey == "" {
|
||||||
|
appKey, _ = revel.Config.String("app.secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成之
|
||||||
|
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
|
||||||
|
// 默认路径
|
||||||
|
if binPath == "" {
|
||||||
|
binPath = "/usr/local/bin/wkhtmltopdf"
|
||||||
|
}
|
||||||
|
|
||||||
|
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
|
||||||
|
// cc := binPath + " --no-stop-slow-scripts --javascript-delay 10000 \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||||
|
// cc := binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||||
|
// 等待--window-status为done的状态
|
||||||
|
// http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html
|
||||||
|
// wkhtmltopdf参数大全
|
||||||
|
var cc string
|
||||||
|
if note.IsMarkdown {
|
||||||
|
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||||
|
} else {
|
||||||
|
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||||
|
_, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
// http://stackoverflow.com/questions/8588818/chrome-pdf-display-duplicate-headers-received-from-the-server
|
||||||
|
// filenameReturn = strings.Replace(filenameReturn, ",", "-", -1)
|
||||||
|
filenameReturn := note.Title
|
||||||
|
filenameReturn = FixFilename(filenameReturn)
|
||||||
|
if filenameReturn == "" {
|
||||||
|
filenameReturn = "Untitled.pdf"
|
||||||
|
} else {
|
||||||
|
filenameReturn += ".pdf"
|
||||||
|
}
|
||||||
|
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
|
||||||
|
}
|
||||||
|
|
||||||
// 设置/取消Blog; 置顶
|
// 设置/取消Blog; 置顶
|
||||||
func (c Note) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
|
func (c Note) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
|
||||||
re := noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
|
re := noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
|
||||||
|
@ -73,15 +73,9 @@ func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
|
|||||||
return c.RenderJson(re)
|
return c.RenderJson(re)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToImage
|
func (c AdminSetting) ExportPdf(path string) revel.Result {
|
||||||
// 长微博的bin路径phantomJs
|
|
||||||
func (c AdminSetting) ToImage() revel.Result {
|
|
||||||
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
|
|
||||||
return c.RenderTemplate("admin/setting/toImage.html");
|
|
||||||
}
|
|
||||||
func (c AdminSetting) DoToImage(toImageBinPath string) revel.Result {
|
|
||||||
re := info.NewRe()
|
re := info.NewRe()
|
||||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "toImageBinPath", toImageBinPath)
|
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "exportPdfBinPath", path)
|
||||||
return c.RenderJson(re)
|
return c.RenderJson(re)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"os/exec"
|
||||||
|
"os"
|
||||||
// "github.com/leanote/leanote/app/types"
|
// "github.com/leanote/leanote/app/types"
|
||||||
// "io/ioutil"
|
// "io/ioutil"
|
||||||
// "fmt"
|
// "fmt"
|
||||||
@ -580,3 +582,83 @@ func (c ApiNote) GetHistories(noteId string) revel.Result {
|
|||||||
return c.RenderJson(re)
|
return c.RenderJson(re)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// 0.2 新增
|
||||||
|
// 导出成PDF
|
||||||
|
// test localhost:9000/api/note/exportPdf?noteId=554f07bf05fcd15fa9000000&token=562211dc99c37ba6a7000001
|
||||||
|
func (c ApiNote) ExportPdf(noteId string) revel.Result {
|
||||||
|
re := info.NewApiRe()
|
||||||
|
userId := c.getUserId()
|
||||||
|
if noteId == "" {
|
||||||
|
re.Msg = "noteNotExists"
|
||||||
|
return c.RenderJson(re)
|
||||||
|
}
|
||||||
|
|
||||||
|
note := noteService.GetNoteById(noteId)
|
||||||
|
if note.NoteId == "" {
|
||||||
|
re.Msg = "noteNotExists"
|
||||||
|
return c.RenderJson(re)
|
||||||
|
}
|
||||||
|
|
||||||
|
noteUserId := note.UserId.Hex()
|
||||||
|
// 是否有权限
|
||||||
|
if noteUserId != userId {
|
||||||
|
// 是否是有权限协作的
|
||||||
|
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
|
||||||
|
re.Msg = "noteNotExists"
|
||||||
|
return c.RenderJson(re)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// path 判断是否需要重新生成之
|
||||||
|
guid := NewGuid()
|
||||||
|
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
|
||||||
|
dir := revel.BasePath + "/" + fileUrlPath
|
||||||
|
if !MkdirAll(dir) {
|
||||||
|
re.Msg = "noDir"
|
||||||
|
return c.RenderJson(re)
|
||||||
|
}
|
||||||
|
filename := guid + ".pdf"
|
||||||
|
path := dir + "/" + filename
|
||||||
|
|
||||||
|
appKey, _ := revel.Config.String("app.secretLeanote")
|
||||||
|
if appKey == "" {
|
||||||
|
appKey, _ = revel.Config.String("app.secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成之
|
||||||
|
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
|
||||||
|
// 默认路径
|
||||||
|
if binPath == "" {
|
||||||
|
binPath = "/usr/local/bin/wkhtmltopdf"
|
||||||
|
}
|
||||||
|
|
||||||
|
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
|
||||||
|
var cc string
|
||||||
|
if(note.IsMarkdown) {
|
||||||
|
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||||
|
} else {
|
||||||
|
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||||
|
_, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
re.Msg = "sysError"
|
||||||
|
return c.RenderJson(re)
|
||||||
|
}
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
re.Msg = "sysError"
|
||||||
|
return c.RenderJson(re)
|
||||||
|
}
|
||||||
|
|
||||||
|
filenameReturn := note.Title
|
||||||
|
filenameReturn = FixFilename(filenameReturn)
|
||||||
|
if filenameReturn == "" {
|
||||||
|
filenameReturn = "Untitled.pdf"
|
||||||
|
} else {
|
||||||
|
filenameReturn += ".pdf"
|
||||||
|
}
|
||||||
|
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
|
||||||
|
}
|
||||||
|
@ -48,7 +48,7 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
|
|||||||
"FindPasswordUpdate": true,
|
"FindPasswordUpdate": true,
|
||||||
"Suggestion": true,
|
"Suggestion": true,
|
||||||
},
|
},
|
||||||
"Note": map[string]bool{"ToImage": true},
|
"Note": map[string]bool{"ToPdf": true},
|
||||||
"Blog": map[string]bool{"Index": true,
|
"Blog": map[string]bool{"Index": true,
|
||||||
"View": true,
|
"View": true,
|
||||||
"AboutMe": true,
|
"AboutMe": true,
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/leanote/leanote/app/db"
|
||||||
|
"github.com/leanote/leanote/app/info"
|
||||||
. "github.com/leanote/leanote/app/lea"
|
. "github.com/leanote/leanote/app/lea"
|
||||||
"github.com/revel/revel"
|
"github.com/revel/revel"
|
||||||
"github.com/leanote/leanote/app/info"
|
|
||||||
"github.com/leanote/leanote/app/db"
|
|
||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
"time"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
|
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
|
||||||
@ -115,6 +119,53 @@ func (this *FileService) UpdateImage(userId, fileId, title string) bool {
|
|||||||
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
|
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *FileService) GetFileBase64(userId, fileId string) (str string, mine string) {
|
||||||
|
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
fmt.Println(err) // 这里的err其实就是panic传入的内容,55
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
path := this.GetFile(userId, fileId)
|
||||||
|
|
||||||
|
if path == "" {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
path = revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||||
|
|
||||||
|
ff, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
e64 := base64.StdEncoding
|
||||||
|
maxEncLen := e64.EncodedLen(len(ff))
|
||||||
|
encBuf := make([]byte, maxEncLen)
|
||||||
|
|
||||||
|
e64.Encode(encBuf, ff)
|
||||||
|
|
||||||
|
mime := http.DetectContentType(ff)
|
||||||
|
|
||||||
|
str = string(encBuf)
|
||||||
|
return str, mime
|
||||||
|
}
|
||||||
|
|
||||||
|
// 得到图片base64, 图片要在之前添加data:image/png;base64,
|
||||||
|
func (this *FileService) GetImageBase64(userId, fileId string) string {
|
||||||
|
|
||||||
|
str, mime := this.GetFileBase64(userId, fileId)
|
||||||
|
if str == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch mime {
|
||||||
|
case "image/gif", "image/jpeg", "image/pjpeg", "image/png", "image/tiff":
|
||||||
|
return fmt.Sprintf("data:%s;base64,%s", mime, str)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return "data:image/png;base64," + str
|
||||||
|
}
|
||||||
|
|
||||||
// 获取文件路径
|
// 获取文件路径
|
||||||
// 要判断是否具有权限
|
// 要判断是否具有权限
|
||||||
// userId是否具有fileId的访问权限
|
// userId是否具有fileId的访问权限
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
<li id="adminUserNav">
|
<li id="adminUserNav">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<i class="fa fa-users icon">
|
<i class="fa fa-users icon">
|
||||||
@ -157,7 +156,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/t?t=setting/shareNote">
|
<a href="/admin/t?t=setting/share_note">
|
||||||
<span>
|
<span>
|
||||||
Register Share Note
|
Register Share Note
|
||||||
</span>
|
</span>
|
||||||
@ -171,6 +170,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/admin/t?t=setting/export_pdf">
|
||||||
|
<span>
|
||||||
|
Export PDF
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
52
app/views/admin/setting/export_pdf.html
Normal file
52
app/views/admin/setting/export_pdf.html
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{{template "admin/top.html" .}}
|
||||||
|
<div class="m-b-md"> <h3 class="m-b-none">Export PDF</h3></div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<form id="add_user_form">
|
||||||
|
<section class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Wkhtmltopdf Binary Path</label>
|
||||||
|
<input type="text" class="form-control" placeholder="/usr/local/bin/wkhtmltopdf" name="path" value="{{.str.exportPdfBinPath}}">
|
||||||
|
Leanote use <a target="_blank" href="http://wkhtmltopdf.org">wkhtmltopdf</a> to export pdf. You should install it on your server.
|
||||||
|
<br />
|
||||||
|
Please input the path that wkhtmltopdf installed, e.g. <code>/usr/local/bin/wkhtmltopdf</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="panel-footer text-right bg-light lter">
|
||||||
|
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "admin/footer.html" .}}
|
||||||
|
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
init_validator("#add_user_form");
|
||||||
|
|
||||||
|
$("#submit").click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
var t = this;
|
||||||
|
if($("#add_user_form").valid()) {
|
||||||
|
$(t).button('loading');
|
||||||
|
ajaxPost("/adminSetting/exportPdf", getFormJsonData("add_user_form"), function(ret){
|
||||||
|
$(t).button('reset')
|
||||||
|
if(!ret.Ok) {
|
||||||
|
art.alert(ret.Msg)
|
||||||
|
} else {
|
||||||
|
art.tips("Success");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{template "admin/end.html" .}}
|
124
app/views/file/pdf.html
Normal file
124
app/views/file/pdf.html
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{.title}}</title>
|
||||||
|
<link href="/css/bootstrap.css" rel="stylesheet">
|
||||||
|
<link id="styleLink" href="/css/pdf.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
table th, table td {
|
||||||
|
padding: 6px 13px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
table th {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr {
|
||||||
|
background-color: #fff;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
table tr:nth-child(2n) {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
.mce-item-table, .mce-item-table td, .mce-item-table th, .mce-item-table caption {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-collapse: collapse;
|
||||||
|
padding: 6px 13px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
<h1 class="title tex2jax_ignore">
|
||||||
|
{{if .blog.Title}}
|
||||||
|
{{.blog.Title}}
|
||||||
|
{{else}}
|
||||||
|
Untitled
|
||||||
|
{{end}}
|
||||||
|
</h1>
|
||||||
|
<div class="created-time">
|
||||||
|
<!--
|
||||||
|
{{ if .userBlog.Logo}}
|
||||||
|
<img src="{{.userBlog.Logo}}" id="logo">
|
||||||
|
{{else}}
|
||||||
|
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
|
||||||
|
{{end}}
|
||||||
|
{{.userInfo.Username}}
|
||||||
|
-->
|
||||||
|
{{if .blog.Tags}}
|
||||||
|
<img src="{{$.siteUrl}}/images/blog/tag.png" id="tag">
|
||||||
|
{{blogTagsForExport $ .blog.Tags}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="desc">
|
||||||
|
{{if .blog.IsMarkdown }}
|
||||||
|
<div id="markdownContent" style="display: none">
|
||||||
|
<!-- 用textarea装html, 防止得到的值失真 -->
|
||||||
|
<textarea>
|
||||||
|
{{.content | raw}}
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<div id="parsedContent">
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
{{.content | raw}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<div id="footer">
|
||||||
|
<p class="split"></p>
|
||||||
|
<a href="http://leanote.com"><img src="{{.siteUrl}}/images/logo/leanote_icon_blue.png" id="leanote_logo"/></a>
|
||||||
|
<p>
|
||||||
|
<a href="http://leanote.com">Leanote</a>
|
||||||
|
<br />
|
||||||
|
<a href="http://leanote.com/service">Upgrade Account</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script src="{{.siteUrl}}/js/jquery-1.9.0.min.js"></script>
|
||||||
|
|
||||||
|
<link href="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
|
||||||
|
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function ok() {
|
||||||
|
setTimeout(function() {
|
||||||
|
window.status = 'done';
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{if not .blog.IsMarkdown }}
|
||||||
|
<script>
|
||||||
|
$("pre").addClass("prettyprint");
|
||||||
|
prettyPrint();
|
||||||
|
ok();
|
||||||
|
</script>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .blog.IsMarkdown }}
|
||||||
|
<script src="/public/libs/md2html/md2html_for_export.js"></script>
|
||||||
|
<script>
|
||||||
|
var content = $.trim($("#markdownContent textarea").val());
|
||||||
|
md2Html(content, $("#parsedContent"), function(html) {
|
||||||
|
$("pre").addClass("prettyprint");
|
||||||
|
prettyPrint();
|
||||||
|
ok();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{end}}
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -38,6 +38,8 @@ POST /findPasswordUpdate Auth.FindPasswordUpdate
|
|||||||
* /note/copySharedNote Note.CopySharedNote
|
* /note/copySharedNote Note.CopySharedNote
|
||||||
* /note/searchNoteByTags Note.SearchNoteByTags
|
* /note/searchNoteByTags Note.SearchNoteByTags
|
||||||
* /note/setNote2Blog Note.SetNote2Blog
|
* /note/setNote2Blog Note.SetNote2Blog
|
||||||
|
* /note/exportPdf Note.ExportPDF
|
||||||
|
* /note/toPdf Note.ToPdf
|
||||||
# pjax
|
# pjax
|
||||||
GET /note/:noteId Note.Index
|
GET /note/:noteId Note.Index
|
||||||
|
|
||||||
|
@ -907,7 +907,7 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
color: #2e3e4e;
|
color: #346EA9;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
a:hover,
|
a:hover,
|
||||||
|
@ -1088,7 +1088,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #2e3e4e;
|
color: #346EA9;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
@font-face {
|
|
||||||
font-family: Si;
|
|
||||||
src: url("/public/fonts/MSYH.TTF");
|
|
||||||
}
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@ -28,14 +24,14 @@
|
|||||||
}
|
}
|
||||||
*,
|
*,
|
||||||
body {
|
body {
|
||||||
font-family: Si, 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Helvetica Neue', Arial, 'Hiragino Sans GB';
|
font-family: 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Helvetica Neue', Arial, 'Hiragino Sans GB';
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3 {
|
h3 {
|
||||||
font-family: Si, 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Helvetica Neue', Arial, 'Hiragino Sans GB';
|
font-family: 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Helvetica Neue', Arial, 'Hiragino Sans GB';
|
||||||
}
|
}
|
||||||
* {
|
* {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
@ -1,8 +1,3 @@
|
|||||||
@font-face {
|
|
||||||
font-family: Si;
|
|
||||||
src:url("/public/fonts/MSYH.TTF");
|
|
||||||
}
|
|
||||||
|
|
||||||
// font
|
// font
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
@ -31,7 +26,7 @@
|
|||||||
|
|
||||||
@bgColor: #fff;
|
@bgColor: #fff;
|
||||||
@headerBgColor: #fff;
|
@headerBgColor: #fff;
|
||||||
@fontFamily: Si, 'Microsoft YaHei','WenQuanYi Micro Hei','Helvetica Neue',Arial,'Hiragino Sans GB';
|
@fontFamily: 'Microsoft YaHei','WenQuanYi Micro Hei','Helvetica Neue',Arial,'Hiragino Sans GB';
|
||||||
@green: #65bd77;
|
@green: #65bd77;
|
||||||
|
|
||||||
*, body {
|
*, body {
|
@ -1096,6 +1096,12 @@ Note.download = function(url, params) {
|
|||||||
$('<form target="mdImageManager" action="' + url + '" method="GET">' + inputs + '</form>').appendTo('body').submit().remove();
|
$('<form target="mdImageManager" action="' + url + '" method="GET">' + inputs + '</form>').appendTo('body').submit().remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 导出成PDF
|
||||||
|
Note.exportPDF = function(target) {
|
||||||
|
var noteId = $(target).attr("noteId");
|
||||||
|
$('<form target="mdImageManager" action="/note/exportPdf" method="GET"><input name="noteId" value="' + noteId + '"/></form>').appendTo('body').submit().remove();
|
||||||
|
};
|
||||||
|
|
||||||
//--------------
|
//--------------
|
||||||
// read only
|
// read only
|
||||||
|
|
||||||
@ -1406,7 +1412,6 @@ Note.toggleReadOnly = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
note.readOnly = true;
|
note.readOnly = true;
|
||||||
Note.readOnly = true;
|
|
||||||
};
|
};
|
||||||
// 切换到编辑模式
|
// 切换到编辑模式
|
||||||
LEA.toggleWriteable = Note.toggleWriteable = function() {
|
LEA.toggleWriteable = Note.toggleWriteable = function() {
|
||||||
@ -1505,6 +1510,9 @@ Note.initContextmenu = function() {
|
|||||||
{ text: getMsg("publicAsBlog"), alias: 'set2Blog', faIcon: "fa-bold", action: Note.setNote2Blog },
|
{ text: getMsg("publicAsBlog"), alias: 'set2Blog', faIcon: "fa-bold", action: Note.setNote2Blog },
|
||||||
{ text: getMsg("cancelPublic"), alias: 'unset2Blog', faIcon: "fa-undo", action: Note.setNote2Blog },
|
{ text: getMsg("cancelPublic"), alias: 'unset2Blog', faIcon: "fa-undo", action: Note.setNote2Blog },
|
||||||
{ type: "splitLine" },
|
{ type: "splitLine" },
|
||||||
|
// { text: "分享到社区", alias: 'html2Image', icon: "", action: Note.html2Image},
|
||||||
|
{ text: getMsg("exportPdf"), alias: 'exportPDF', faIcon: "fa-file-pdf-o", action: Note.exportPDF},
|
||||||
|
{ type: "splitLine" },
|
||||||
{ text: getMsg("delete"), icon: "", faIcon: "fa-trash-o", action: Note.deleteNote },
|
{ text: getMsg("delete"), icon: "", faIcon: "fa-trash-o", action: Note.deleteNote },
|
||||||
{ text: getMsg("move"), alias: "move", faIcon: "fa-arrow-right",
|
{ text: getMsg("move"), alias: "move", faIcon: "fa-arrow-right",
|
||||||
type: "group",
|
type: "group",
|
||||||
|
632
public/libs/md2html/md2html_for_export.js
Normal file
632
public/libs/md2html/md2html_for_export.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user