Merge branch 'dev-life' into dev
Conflicts: app/init.go app/service/UserService.go conf/app.conf conf/routes mongodb_backup/leanote_install_data/albums.metadata.json mongodb_backup/leanote_install_data/attachs.metadata.json mongodb_backup/leanote_install_data/blog_comments.metadata.json mongodb_backup/leanote_install_data/blog_likes.metadata.json mongodb_backup/leanote_install_data/blog_singles.metadata.json mongodb_backup/leanote_install_data/configs.bson mongodb_backup/leanote_install_data/configs.metadata.json mongodb_backup/leanote_install_data/email_logs.metadata.json mongodb_backup/leanote_install_data/files.metadata.json mongodb_backup/leanote_install_data/find_pwds.metadata.json mongodb_backup/leanote_install_data/group_users.metadata.json mongodb_backup/leanote_install_data/groups.metadata.json mongodb_backup/leanote_install_data/has_share_notes.metadata.json mongodb_backup/leanote_install_data/leanote.ShareNotes.metadata.json mongodb_backup/leanote_install_data/leanote.has_share_notes.metadata.jso n mongodb_backup/leanote_install_data/note_content_histories.metadata.json mongodb_backup/leanote_install_data/note_contents.metadata.json mongodb_backup/leanote_install_data/note_images.metadata.json mongodb_backup/leanote_install_data/notebooks.bson mongodb_backup/leanote_install_data/notebooks.metadata.json mongodb_backup/leanote_install_data/notes.bson mongodb_backup/leanote_install_data/notes.metadata.json mongodb_backup/leanote_install_data/reports.metadata.json mongodb_backup/leanote_install_data/sessions.metadata.json mongodb_backup/leanote_install_data/share_notebooks.metadata.json mongodb_backup/leanote_install_data/share_notes.metadata.json mongodb_backup/leanote_install_data/suggestions.metadata.json mongodb_backup/leanote_install_data/system.indexes.bson mongodb_backup/leanote_install_data/tag_count.metadata.json mongodb_backup/leanote_install_data/tags.metadata.json mongodb_backup/leanote_install_data/themes.metadata.json mongodb_backup/leanote_install_data/tokens.metadata.json mongodb_backup/leanote_install_data/user_blogs.metadata.json mongodb_backup/leanote_install_data/users.bson mongodb_backup/leanote_install_data/users.metadata.json public/blog/themes/default/theme.json public/blog/themes/elegant/theme.json public/blog/themes/nav_fixed/theme.json public/images/logo/leanote_icon_blue.jpg public/js/app/note-min.js public/js/app/note.js public/js/app/notebook-min.js public/js/app/notebook.js public/js/app/tag-min.js public/js/app/tag.js public/tinymce/plugins/paste/plugin.dev.js public/tinymce/plugins/spellchecker/plugin.min.js public/tinymce/tinymce.dev.js public/tinymce/tinymce.jquery.dev.js
This commit is contained in:
@ -100,7 +100,7 @@ func (c Attach) uploadAttach(noteId string) (re info.Re) {
|
||||
id := bson.NewObjectId();
|
||||
fileInfo.AttachId = id
|
||||
fileId = id.Hex()
|
||||
Ok, resultMsg = attachService.AddAttach(fileInfo)
|
||||
Ok, resultMsg = attachService.AddAttach(fileInfo, false)
|
||||
if resultMsg != "" {
|
||||
resultMsg = c.Message(resultMsg)
|
||||
}
|
||||
|
@ -196,29 +196,77 @@ func (c Blog) getCates(userBlog info.UserBlog) {
|
||||
}
|
||||
|
||||
var i = 0
|
||||
cates := make([]map[string]string, len(notebooks))
|
||||
cates := make([]*info.Cate, len(notebooks))
|
||||
|
||||
// 先要保证已有的是正确的排序
|
||||
cateIds := userBlog.CateIds
|
||||
has := map[string]bool{} // cateIds中有的
|
||||
cateMap := map[string]*info.Cate{}
|
||||
if cateIds != nil && len(cateIds) > 0 {
|
||||
for _, cateId := range cateIds {
|
||||
if n, ok := notebooksMap[cateId]; ok {
|
||||
cates[i] = map[string]string{"Title": n.Title, "UrlTitle": c.getCateUrlTitle(&n), "CateId": n.NotebookId.Hex()}
|
||||
parentNotebookId := ""
|
||||
if n.ParentNotebookId != "" {
|
||||
parentNotebookId = n.ParentNotebookId.Hex()
|
||||
}
|
||||
cates[i] = &info.Cate{Title: n.Title, UrlTitle: c.getCateUrlTitle(&n), CateId: n.NotebookId.Hex(), ParentCateId: parentNotebookId}
|
||||
cateMap[cates[i].CateId] = cates[i]
|
||||
i++
|
||||
has[cateId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// 之后
|
||||
|
||||
// 之后添加没有排序的
|
||||
for _, n := range notebooks {
|
||||
id := n.NotebookId.Hex()
|
||||
if !has[id] {
|
||||
cates[i] = map[string]string{"Title": n.Title, "UrlTitle": c.getCateUrlTitle(&n), "CateId": id}
|
||||
parentNotebookId := ""
|
||||
if n.ParentNotebookId != "" {
|
||||
parentNotebookId = n.ParentNotebookId.Hex()
|
||||
}
|
||||
cates[i] = &info.Cate{Title: n.Title, UrlTitle: c.getCateUrlTitle(&n), CateId: id, ParentCateId: parentNotebookId}
|
||||
cateMap[cates[i].CateId] = cates[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// LogJ(">>")
|
||||
// LogJ(cates)
|
||||
|
||||
// 建立层级
|
||||
hasParent := map[string]bool{} // 有父的cate
|
||||
for _, cate := range cates {
|
||||
parentCateId := cate.ParentCateId
|
||||
if parentCateId != "" {
|
||||
if parentCate, ok := cateMap[parentCateId]; ok {
|
||||
// Log("________")
|
||||
// LogJ(parentCate)
|
||||
// LogJ(cate)
|
||||
if parentCate.Children == nil {
|
||||
parentCate.Children = []*info.Cate{cate}
|
||||
} else {
|
||||
parentCate.Children = append(parentCate.Children, cate)
|
||||
}
|
||||
hasParent[cate.CateId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 得到没有父的cate, 作为第一级cate
|
||||
catesTree := []*info.Cate{}
|
||||
for _, cate := range cates {
|
||||
if !hasParent[cate.CateId] {
|
||||
catesTree = append(catesTree, cate)
|
||||
}
|
||||
}
|
||||
|
||||
Log("cates")
|
||||
LogJ(cates)
|
||||
LogJ(catesTree);
|
||||
|
||||
c.RenderArgs["cates"] = cates
|
||||
c.RenderArgs["catesTree"] = catesTree
|
||||
}
|
||||
|
||||
// 单页
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"os/exec"
|
||||
"strings"
|
||||
// "time"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
@ -124,9 +125,7 @@ func (c Note) Index(noteId string) revel.Result {
|
||||
|
||||
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
|
||||
|
||||
|
||||
// return c.RenderTemplate("note/note.html")
|
||||
|
||||
// return c.RenderTemplate("note/note.html")
|
||||
if isDev, _ := revel.Config.Bool("mode.dev"); isDev {
|
||||
return c.RenderTemplate("note/note-dev.html")
|
||||
} else {
|
||||
@ -169,7 +168,7 @@ type NoteOrContent struct {
|
||||
Title string
|
||||
Desc string
|
||||
ImgSrc string
|
||||
Tags []string
|
||||
Tags string
|
||||
Content string
|
||||
Abstract string
|
||||
IsNew bool
|
||||
@ -192,7 +191,7 @@ func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
|
||||
NoteId: bson.ObjectIdHex(noteOrContent.NoteId),
|
||||
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
|
||||
Title: noteOrContent.Title,
|
||||
Tags: noteOrContent.Tags,
|
||||
Tags: strings.Split(noteOrContent.Tags, ","),
|
||||
Desc: noteOrContent.Desc,
|
||||
ImgSrc: noteOrContent.ImgSrc,
|
||||
IsBlog: noteOrContent.IsBlog,
|
||||
@ -225,23 +224,32 @@ func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
|
||||
noteUpdate["Title"] = noteOrContent.Title;
|
||||
}
|
||||
|
||||
if c.Has("Tags[]") {
|
||||
if c.Has("Tags") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Tags"] = noteOrContent.Tags;
|
||||
noteUpdate["Tags"] = strings.Split(noteOrContent.Tags, ",");
|
||||
}
|
||||
|
||||
// web端不控制
|
||||
if needUpdateNote {
|
||||
noteService.UpdateNote(noteOrContent.UserId, c.GetUserId(),
|
||||
noteOrContent.NoteId, noteUpdate)
|
||||
noteService.UpdateNote(c.GetUserId(),
|
||||
noteOrContent.NoteId, noteUpdate, -1)
|
||||
}
|
||||
|
||||
//-------------
|
||||
|
||||
afterContentUsn := 0
|
||||
contentOk := false
|
||||
contentMsg := ""
|
||||
if c.Has("Content") {
|
||||
noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(),
|
||||
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract)
|
||||
// noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(),
|
||||
// noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract)
|
||||
contentOk, contentMsg, afterContentUsn = noteService.UpdateNoteContent(c.GetUserId(),
|
||||
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract, needUpdateNote, -1)
|
||||
}
|
||||
|
||||
Log(afterContentUsn)
|
||||
Log(contentOk)
|
||||
Log(contentMsg)
|
||||
|
||||
return c.RenderJson(true)
|
||||
}
|
||||
|
||||
@ -291,10 +299,12 @@ func (c Note) SearchNoteByTags(tags []string) revel.Result {
|
||||
// 这是脚本调用, 没有cookie, 不执行权限控制, 通过传来的appKey判断
|
||||
func (c Note) ToImage(noteId, appKey string) revel.Result {
|
||||
// 虽然传了cookie但是这里还是不能得到userId, 所以还是通过appKey来验证之
|
||||
/*
|
||||
appKeyTrue, _ := revel.Config.String("app.secret")
|
||||
if appKeyTrue != appKey {
|
||||
return c.RenderText("")
|
||||
}
|
||||
*/
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
return c.RenderText("")
|
||||
@ -413,6 +423,75 @@ func (c Note) Html2Image(noteId string) revel.Result {
|
||||
|
||||
}
|
||||
|
||||
// 导出成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.RenderJson(re)
|
||||
}
|
||||
|
||||
noteUserId := note.UserId.Hex()
|
||||
// 是否有权限
|
||||
if noteUserId != userId {
|
||||
// 是否是有权限协作的
|
||||
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
|
||||
re.Msg = "No Perm"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
// path 判断是否需要重新生成之
|
||||
fileUrlPath := "upload/" + noteUserId + "/images/weibo"
|
||||
dir := revel.BasePath + "/public/" + fileUrlPath
|
||||
if !ClearDir(dir) {
|
||||
re.Msg = "No Dir"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
filename := note.Title + ".pdf";
|
||||
if note.Title == "" {
|
||||
filename = "Untitled.pdf";
|
||||
}
|
||||
path := dir + "/" + filename
|
||||
|
||||
// cookie
|
||||
cookieName := revel.CookiePrefix + "_SESSION"
|
||||
cookie, err := c.Request.Cookie(cookieName)
|
||||
cookieStr := cookie.String()
|
||||
cookieValue := ""
|
||||
if err == nil && len(cookieStr) > len(cookieName) {
|
||||
cookieValue = cookieStr[len(cookieName)+1:]
|
||||
}
|
||||
Log(cookieValue)
|
||||
|
||||
appKey, _ := revel.Config.String("app.secret")
|
||||
// cookieDomain, _ := revel.Config.String("cookie.domain")
|
||||
// 生成之
|
||||
url := configService.GetSiteUrl() + "/note/toImage?noteId=" + noteId + "&appKey=" + appKey;
|
||||
// /Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/b.js
|
||||
binPath := "/usr/local/bin/wkhtmltopdf" // configService.GetGlobalStringConfig("toImageBinPath")
|
||||
if binPath == "" {
|
||||
return c.RenderJson(re);
|
||||
}
|
||||
// cc := binPath + " \"" + url + "\" \"" + path + "\" \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
cc := binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||
Log(cc);
|
||||
b, err := cmd.Output()
|
||||
if err == nil {
|
||||
re.Ok = true
|
||||
re.Id = fileUrlPath + "/" + filename
|
||||
} else {
|
||||
re.Msg = string(b)
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 设置/取消Blog; 置顶
|
||||
func (c Note) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
|
||||
re := noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
|
||||
|
@ -37,7 +37,8 @@ func (c Notebook) AddNotebook(notebookId, title, parentNotebookId string) revel.
|
||||
if(parentNotebookId != "") {
|
||||
notebook.ParentNotebookId = bson.ObjectIdHex(parentNotebookId)
|
||||
}
|
||||
re := notebookService.AddNotebook(notebook)
|
||||
|
||||
re, notebook := notebookService.AddNotebook(notebook)
|
||||
|
||||
if(re) {
|
||||
return c.RenderJson(notebook)
|
||||
@ -74,4 +75,4 @@ func (c Notebook) DragNotebooks(data string) revel.Result {
|
||||
func (c Notebook) SetNotebook2Blog(notebookId string, isBlog bool) revel.Result {
|
||||
re := notebookService.ToBlog(c.GetUserId(), notebookId, isBlog)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
30
app/controllers/TagController.go
Normal file
30
app/controllers/TagController.go
Normal file
@ -0,0 +1,30 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// "os/exec"
|
||||
)
|
||||
|
||||
type Tag struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// 更新Tag
|
||||
func (c Tag) UpdateTag(tag string) revel.Result {
|
||||
ret := info.NewRe()
|
||||
ret.Ok = true
|
||||
ret.Item = tagService.AddOrUpdateTag(c.GetUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
func (c Tag) DeleteTag(tag string) revel.Result {
|
||||
ret := info.Re{}
|
||||
ret.Ok = true
|
||||
ret.Item = tagService.DeleteTag(c.GetUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
}
|
@ -21,4 +21,10 @@ func (c AdminUpgrade) UpgradeBetaToBeta2() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = upgradeService.UpgradeBetaToBeta2(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c AdminUpgrade) UpgradeBeta3ToBeta4() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = upgradeService.Api(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
}
|
463
app/controllers/api/API列表-v0.1.md
Normal file
463
app/controllers/api/API列表-v0.1.md
Normal file
@ -0,0 +1,463 @@
|
||||
# API 列表
|
||||
|
||||
## 前言
|
||||
|
||||
### api url
|
||||
|
||||
所有api的url前面带/api/, 如:
|
||||
|
||||
`/api/user/info?userId=xxxx&token=xxxx`
|
||||
|
||||
除了/auth/login, /auth/register外其它的都需要另外带参数token=xxxx
|
||||
|
||||
### 文件目录结构
|
||||
* 所有API的Controller都在app/api文件夹下
|
||||
* 文件命名: Api功能Controller.go, 如ApiUserController.go
|
||||
* 结构体命名为 Api功能, 如ApiUser
|
||||
* API公用Controller: ApiBaseController
|
||||
* init.go 注入service和定义拦截器
|
||||
|
||||
### 流程
|
||||
用户登录后返回一个token, 以后所有的请求都携带该token.
|
||||
在init.go中的拦截器会得到token并调用sessionService判断是否登录了
|
||||
|
||||
## 返回值结构
|
||||
* 全部返回JSON, JSON, 除二进制文件(图片, 附件外), 如果返回其它非JSON格式的值, 肯定是出错了
|
||||
* 错误信息全部返回 {Ok: false, Msg: "相应的错误信息"}
|
||||
* 正确信息返回分两种:
|
||||
1. 一些操作型的api, 比如updateUsername, updateLogo之类的, 成功后返回 {Ok: true, Msg:""}
|
||||
2. 一些获取型api, 如getNote, 全部返回真实的返回数据, 如返回笔记:
|
||||
```
|
||||
{
|
||||
"NoteId": "54bdc7e305fcd13ea3000000",
|
||||
"NotebookId": "54bdc65599c37b0da9000003",
|
||||
"UserId": "54bdc65599c37b0da9000002",
|
||||
"Title": "笔记标题",
|
||||
"Desc": "",
|
||||
"Tags": null,
|
||||
"Abstract": "",
|
||||
"Content": "",
|
||||
"IsMarkdown": false,
|
||||
"IsBlog": false,
|
||||
"IsTrash": true,
|
||||
"IsDeleted": false,
|
||||
"Usn": 15,
|
||||
"Files": [],
|
||||
"CreatedTime": "2015-01-20T11:13:41.34+08:00",
|
||||
"UpdatedTime": "2015-01-20T11:13:41.34+08:00",
|
||||
"PublicTime": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
```
|
||||
* 时间类型全是返回如 "2015-01-20T11:13:41.34+08:00" 的格式, (因为golang的time转成Json就是这样, 历史原因)
|
||||
|
||||
-----------------
|
||||
|
||||
## Auth 登录与注册
|
||||
|
||||
### /auth/login 登录
|
||||
|
||||
```
|
||||
参数: email, pwd
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {"Ok":false, "Msg":"用户名或密码有误"}
|
||||
正确: 比如:
|
||||
{
|
||||
"Ok":true,
|
||||
"Token":"5500830738f41138e90003232",
|
||||
"UserId":"52d26b4e99c37b609a000001",
|
||||
"Email":"leanote@leanote.com",
|
||||
"Username":"leanote"
|
||||
}
|
||||
```
|
||||
|
||||
登录成功后将使用token作为之后的请求
|
||||
|
||||
### /auth/logout 注销
|
||||
```
|
||||
参数: token
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
### /auth/register 注册
|
||||
```
|
||||
参数: email, pwd
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
## User 用户
|
||||
### /user/info 获取用户信息
|
||||
```
|
||||
参数: userId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.User
|
||||
```
|
||||
|
||||
### /user/updateUsername 修改用户名
|
||||
```
|
||||
参数: username(新用户名)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
### /user/updatePwd 修改密码
|
||||
```
|
||||
参数: oldPwd(旧密码), pwd(新密码)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
### /user/updateLogo 修改头像
|
||||
```
|
||||
参数: file(文件)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
### /user/getSyncState 获取最新同步状态
|
||||
```
|
||||
参数: 无
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {LastSyncUsn: 3232, LastSyncTime: "上次同步时间"(暂时无用)}
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## Notebook 笔记本
|
||||
|
||||
### /notebook/getSyncNotebooks 得到需要同步的笔记本
|
||||
```
|
||||
参数: afterUsn(int, 在此usn后的笔记本是需要同步的), maxEntry(int, 最大要同步的量)
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Notebook] 数组
|
||||
```
|
||||
|
||||
### /notebook/getNotebooks 得到所有笔记本
|
||||
```
|
||||
无参数
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Notebook] 数组
|
||||
```
|
||||
|
||||
### /notebook/addNotebook 添加笔记本
|
||||
```
|
||||
参数: title(string), parentNotebookId(string, 父notebookId, 可空), seq(int) 排列
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg:""}
|
||||
成功: type.Notebook
|
||||
```
|
||||
|
||||
### /notebook/updateNotebook 修改笔记本
|
||||
```
|
||||
参数: notebookId, title, parentNotebookId, seq(int), usn(int)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, msg: ""} msg == "conflict" 表示冲突
|
||||
成功: type.Notebook
|
||||
```
|
||||
|
||||
### /notebook/deleteNotebook 删除笔记本
|
||||
```
|
||||
参数: notebookId, usn(int)
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, msg: ""} msg == "conflict" 表示冲突
|
||||
成功: {Ok: true}
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## Note 笔记
|
||||
|
||||
|
||||
### /note/getSyncNotes 获取需要同步的笔记
|
||||
```
|
||||
参数: afterUsn(int, 在此usn后的笔记是需要同步的), maxEntry(int, 最大要同步的量)
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Note] 数组, 笔记不包含Abstract和Content
|
||||
```
|
||||
|
||||
|
||||
### /note/getNotes 获得某笔记本下的笔记(无内容)
|
||||
```
|
||||
参数: notebookId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Note] 数组, 笔记不包含Abstract和Content
|
||||
```
|
||||
|
||||
### /note/getNoteAndContent 获得笔记与内容
|
||||
```
|
||||
参数: noteId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.Note
|
||||
```
|
||||
|
||||
### /note/getNoteContent 获得笔记内容
|
||||
```
|
||||
参数: noteId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.NoteContent
|
||||
```
|
||||
|
||||
### /note/addNote 添加笔记
|
||||
```
|
||||
参数: (注意首字大写)
|
||||
NotebookId string 必传
|
||||
Title string 必传
|
||||
Tags []string 可选
|
||||
Content string 必传
|
||||
Abstract string 可选, 当是markdown笔记时必须传
|
||||
IsMarkdown bool 可选
|
||||
Files []type.NoteFiles 数组 可选
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.Note, 不包含Abstract, Content
|
||||
```
|
||||
|
||||
** 关于笔记中的图片/附件**
|
||||
客户端应该添加一个"图片/附件表"来存元数据, 图片应该要缓存到本地, 附件可在需要的时候再调用相应api获取.
|
||||
|
||||
Content中的数据, 图片,附件在Leanote的链接是, 所以, 不管你在本地的笔记中是以什么形式来保存图片,附件的链接的,请addNote和updateNote时务必将链接修改成leanote服务器上的链接.
|
||||
http://leanote.com/api/file/getImage?fileId=xx
|
||||
单个附件:
|
||||
http://leanote.com/api/file/getAttach?fileId=xx
|
||||
所有附件:
|
||||
http://leanote.com/api/file/getAllAttachs?noteId=xxx
|
||||
```
|
||||
|
||||
**注意:**
|
||||
addNote时必须要把Files, 和相关的图片/附件一起传到服务器中
|
||||
其中Files(文件的元数据)和其它字段以POST方式传出, 而真正数据则以http的multipart传入, 每个文件的name为"FileDatas[LocalFileId]"
|
||||
|
||||
图片在笔记内的链接必须为: http://leanote.com/api/file/getImage?fileId=LocalFileId或FileId
|
||||
附件如果插入到了笔记内容内, 其链接必须为: http://leanote.com/api/file/getAttach?fileId=LocalFileId或FileId
|
||||
其中, fileId为文件在本地的LocalFileId或服务器上的FileId
|
||||
|
||||
服务器端会生成FileId传给Client. Client在本地必须要建立LocalFileId与FileId的关联.
|
||||
|
||||
如果笔记内容传了, 且笔记内有图片, 则必须要传Files 文件元数据, 因为Server端还要对内容内的图片, 附件链接进行修改, 可能你传过去的是LocalFileId, 服务器端会将LocalFileId替换成FileId存到数据库中.
|
||||
|
||||
同样适用于 updateNote
|
||||
|
||||
http://leanote.com 不绝对, 因为用户可以自建服务, 所以在开发时需要可配置
|
||||
|
||||
### /note/updateNote 更新笔记
|
||||
当更新了笔记某个属性时, 只要传某个属性就行, 其它不用传, 比如把笔记拉入了trash, 那么就传IsTrash: true
|
||||
```
|
||||
参数: (注意首字大写)
|
||||
NoteId string 必传
|
||||
Usn int 必传
|
||||
NotebookId string 可选
|
||||
Title string 可选
|
||||
Tags []string 可选
|
||||
Content string 可选
|
||||
Abstract string 可选, 当是markdown笔记时必须传
|
||||
IsMarkdown bool 可选
|
||||
IsTrash bool 是否是trash 可选
|
||||
Files []type.NoteFiles 数组 可选
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, msg: ''} msg == 'conflict' 表示冲突
|
||||
成功: type.Note, 不包含Abstract和Content
|
||||
```
|
||||
|
||||
### /note/deleteTrash 彻底删除笔记
|
||||
```
|
||||
参数: noteId, usn
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, msg: ''} msg == 'conflict' 表示冲突
|
||||
成功: type.UpdateRet
|
||||
```
|
||||
|
||||
-------
|
||||
|
||||
## Tag 标签
|
||||
|
||||
### /tag/getSyncTags 获取需要同步的标签
|
||||
```
|
||||
参数: afterUsn(int, 在此usn后的标签是需要同步的), maxEntry(int, 最大要同步的量)
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Tag] 数组
|
||||
```
|
||||
|
||||
### /tag/addTag 添加标签
|
||||
```
|
||||
参数: tag(string)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.Tag
|
||||
```
|
||||
|
||||
### /tag/deleteTag 删除标签
|
||||
```
|
||||
参数: tag(string)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.UpdateRet
|
||||
```
|
||||
|
||||
### File 文件(获取图片, 附件)
|
||||
|
||||
### /file/getImage 获取图片
|
||||
```
|
||||
参数: fileId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: 非二进制文件数据
|
||||
成功: 二进制文件
|
||||
```
|
||||
|
||||
### /file/getAttach 获取附件
|
||||
```
|
||||
参数: fileId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: 非二进制文件数据
|
||||
成功: 二进制文件
|
||||
```
|
||||
|
||||
### /file/getAllAttachs 获取所有附件
|
||||
```
|
||||
参数: noteId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: 非二进制文件数据
|
||||
成功: 二进制文件
|
||||
```
|
||||
|
||||
--------
|
||||
|
||||
## 数据类型
|
||||
|
||||
### type.User 用户信息
|
||||
|
||||
```
|
||||
User {
|
||||
UserId string
|
||||
Username string
|
||||
Email string
|
||||
Verified bool
|
||||
Logo string
|
||||
}
|
||||
```
|
||||
|
||||
### type.Notebook 笔记本
|
||||
|
||||
```
|
||||
Notebook {
|
||||
NotebookId
|
||||
UserId
|
||||
ParentNotebookId // 上级
|
||||
Seq int // 排序
|
||||
Title string
|
||||
IsBlog bool
|
||||
CreatedTime time.Time
|
||||
UpdatedTime time.Time
|
||||
|
||||
// 更新序号
|
||||
Usn int // UpdateSequenceNum
|
||||
}
|
||||
```
|
||||
|
||||
### type.Note 笔记
|
||||
```
|
||||
Note {
|
||||
NoteId string
|
||||
NotebookId string
|
||||
UserId string
|
||||
Title string
|
||||
Tags []string
|
||||
Content string
|
||||
IsMarkdown bool
|
||||
IsBlog bool
|
||||
IsTrash bool
|
||||
Files []NoteFile // 图片, 附件
|
||||
CreatedTime time.Time
|
||||
UpdatedTime time.Time
|
||||
PublicTime time.Time
|
||||
|
||||
// 更新序号
|
||||
Usn int
|
||||
}
|
||||
```
|
||||
|
||||
### type.NoteContent 笔记内容
|
||||
```
|
||||
NoteContent {
|
||||
NoteId string
|
||||
UserId string
|
||||
Content string
|
||||
}
|
||||
```
|
||||
|
||||
### type.NoteFile 笔记文件(图片,附件)
|
||||
```
|
||||
NoteFile {
|
||||
FileId string // 服务器端Id
|
||||
LocalFileId string // 客户端Id
|
||||
Type string // images/png, doc, xls, 根据fileName确定
|
||||
Title string
|
||||
HasBody bool // 传过来的值是否要更新内容, 如果有true, 则必须传文件
|
||||
IsAttach bool // 是否是附件, 不是附件就是图片
|
||||
}
|
||||
```
|
||||
|
||||
### type.Tag 标签
|
||||
```
|
||||
Tag {
|
||||
TagId string
|
||||
UserId string
|
||||
Tag string
|
||||
CreatedTime
|
||||
UpdatedTime
|
||||
IsDeleted bool // 删除位
|
||||
// 更新序号
|
||||
Usn
|
||||
}
|
||||
```
|
||||
|
||||
### type.UpdateRe 更新后返回的值, 包含Usn
|
||||
```
|
||||
ReUpdate {
|
||||
Ok bool
|
||||
Msg string
|
||||
|
||||
// 更新序号
|
||||
Usn int
|
||||
}
|
||||
```
|
47
app/controllers/api/API设计-v0.1.md
Normal file
47
app/controllers/api/API设计-v0.1.md
Normal file
@ -0,0 +1,47 @@
|
||||
# API设计
|
||||
|
||||
By life (life@leanote.com)
|
||||
|
||||
## api url
|
||||
|
||||
所有api的url前面带/api/, 如:
|
||||
|
||||
`/api/user/info?userId=xxxx&token=xxxx`
|
||||
|
||||
## 文件目录结构
|
||||
* 所有API的Controller都在app/api文件夹下
|
||||
* 文件命名: Api功能Controller.go, 如ApiUserController.go
|
||||
* 结构体命名为 Api功能, 如ApiUser
|
||||
* API公用Controller: ApiBaseController
|
||||
* init.go 注入service和定义拦截器
|
||||
|
||||
## 流程
|
||||
用户登录后返回一个token, 以后所有的请求都携带该token.
|
||||
在init.go中的拦截器会得到token并调用sessionService判断是否登录了
|
||||
|
||||
## 返回值结构
|
||||
* 全部返回JSON, JSON, 除二进制文件(图片, 附件外), 如果返回其它非JSON格式的值, 肯定是出错了
|
||||
* 错误信息全部返回 {Ok: false, Msg: ""}
|
||||
* 正确信息全部返回真实的返回数据, 如返回笔记:
|
||||
```
|
||||
{
|
||||
"NoteId": "54bdc7e305fcd13ea3000000",
|
||||
"NotebookId": "54bdc65599c37b0da9000003",
|
||||
"UserId": "54bdc65599c37b0da9000002",
|
||||
"Title": "asdfads",
|
||||
"Desc": "",
|
||||
"Tags": null,
|
||||
"Abstract": "",
|
||||
"Content": "",
|
||||
"IsMarkdown": false,
|
||||
"IsBlog": false,
|
||||
"IsTrash": true,
|
||||
"IsDeleted": false,
|
||||
"Usn": 15,
|
||||
"Files": [],
|
||||
"CreatedTime": "2015-01-20T11:13:41.34+08:00",
|
||||
"UpdatedTime": "2015-01-20T11:13:41.34+08:00",
|
||||
"PublicTime": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
```
|
||||
* 时间类型全是返回如 "2015-01-20T11:13:41.34+08:00" 的格式, (因为golang的time转成Json就是这样, 历史原因)
|
69
app/controllers/api/ApiAuthController.go
Normal file
69
app/controllers/api/ApiAuthController.go
Normal file
@ -0,0 +1,69 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "strconv"
|
||||
)
|
||||
|
||||
// 用户登录后生成一个token, 将这个token保存到session中
|
||||
// 以后每次的请求必须带这个token, 并从session中获取userId
|
||||
|
||||
// 用户登录/注销/找回密码
|
||||
|
||||
type ApiAuth struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 登录
|
||||
// [ok]
|
||||
// 成功返回 {Ok: true, Item: token }
|
||||
// 失败返回 {Ok: false, Msg: ""}
|
||||
func (c ApiAuth) Login(email, pwd string) revel.Result {
|
||||
var msg = ""
|
||||
|
||||
userInfo := authService.Login(email, pwd)
|
||||
if userInfo.Email != "" {
|
||||
token := bson.NewObjectId().Hex()
|
||||
sessionService.SetUserId(token, userInfo.UserId.Hex())
|
||||
return c.RenderJson(info.AuthOk{Ok: true, Token: token, UserId: userInfo.UserId, Email: userInfo.Email, Username: userInfo.Username})
|
||||
} else {
|
||||
// 登录错误, 则错误次数++
|
||||
msg = "wrongUsernameOrPassword"
|
||||
}
|
||||
return c.RenderJson(info.ApiRe{Ok: false, Msg: c.Message(msg)})
|
||||
}
|
||||
|
||||
// 注销
|
||||
// [Ok]
|
||||
func (c ApiAuth) Logout() revel.Result {
|
||||
token := c.getToken()
|
||||
sessionService.Clear(token)
|
||||
re := info.NewApiRe()
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 注册
|
||||
// [Ok]
|
||||
// 成功后并不返回用户ID, 需要用户重新登录
|
||||
func (c ApiAuth) Register(email, pwd string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if !configService.IsOpenRegister() {
|
||||
re.Msg = "notOpenRegister" // 未开放注册
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 注册
|
||||
re.Ok, re.Msg = authService.Register(email, pwd, "")
|
||||
return c.RenderJson(re)
|
||||
}
|
181
app/controllers/api/ApiBaseController.go
Normal file
181
app/controllers/api/ApiBaseController.go
Normal file
@ -0,0 +1,181 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "encoding/json"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"os"
|
||||
// "fmt"
|
||||
"io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 公用Controller, 其它Controller继承它
|
||||
type ApiBaseContrller struct {
|
||||
controllers.BaseController // 不能用*BaseController
|
||||
}
|
||||
|
||||
// 得到token, 这个token是在AuthInterceptor设到Session中的
|
||||
func (c ApiBaseContrller) getToken() string {
|
||||
return c.Session["_token"]
|
||||
}
|
||||
|
||||
// userId
|
||||
// _userId是在AuthInterceptor设置的
|
||||
func (c ApiBaseContrller) getUserId() string {
|
||||
return c.Session["_userId"]
|
||||
}
|
||||
|
||||
// 得到用户信息
|
||||
func (c ApiBaseContrller) getUserInfo() info.User {
|
||||
userId := c.Session["_userId"]
|
||||
if userId == "" {
|
||||
return info.User{}
|
||||
}
|
||||
return userService.GetUserInfo(userId)
|
||||
}
|
||||
|
||||
// 上传附件
|
||||
func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg string, id string) {
|
||||
userId := c.getUserId();
|
||||
|
||||
// 判断是否有权限为笔记添加附件
|
||||
// 如果笔记还没有添加是不是会有问题
|
||||
/*
|
||||
if !shareService.HasUpdateNotePerm(noteId, userId) {
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
file, handel, err := c.Request.FormFile(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// > 5M?
|
||||
maxFileSize := configService.GetUploadSize("uploadAttachSize");
|
||||
if maxFileSize <= 0 {
|
||||
maxFileSize = 1000
|
||||
}
|
||||
if(float64(len(data)) > maxFileSize * float64(1024*1024)) {
|
||||
msg = "fileIsTooLarge"
|
||||
return
|
||||
}
|
||||
|
||||
// 生成上传路径
|
||||
filePath := "files/" + userId + "/attachs"
|
||||
dir := revel.BasePath + "/" + filePath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
_, ext := SplitFilename(filename) // .doc
|
||||
filename = NewGuid() + ext
|
||||
toPath := dir + "/" + filename;
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// add File to db
|
||||
fileType := ""
|
||||
if ext != "" {
|
||||
fileType = strings.ToLower(ext[1:])
|
||||
}
|
||||
filesize := GetFilesize(toPath)
|
||||
fileInfo := info.Attach{AttachId: bson.NewObjectId(),
|
||||
Name: filename,
|
||||
Title: handel.Filename,
|
||||
NoteId: bson.ObjectIdHex(noteId),
|
||||
UploadUserId: bson.ObjectIdHex(userId),
|
||||
Path: filePath + "/" + filename,
|
||||
Type: fileType,
|
||||
Size: filesize}
|
||||
|
||||
ok, msg = attachService.AddAttach(fileInfo, true)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id = fileInfo.AttachId.Hex()
|
||||
return
|
||||
}
|
||||
|
||||
// 上传图片
|
||||
func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok bool, msg string, id string) {
|
||||
if isAttach {
|
||||
return c.uploadAttach(name, noteId)
|
||||
}
|
||||
file, handel, err := c.Request.FormFile(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
// 生成上传路径
|
||||
fileUrlPath := "files/" + c.getUserId() + "/images"
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
_, ext := SplitFilename(filename)
|
||||
if(ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg") {
|
||||
msg = "notImage"
|
||||
return
|
||||
}
|
||||
|
||||
filename = NewGuid() + ext
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
maxFileSize := configService.GetUploadSize("uploadImageSize");
|
||||
if maxFileSize <= 0 {
|
||||
maxFileSize = 1000
|
||||
}
|
||||
|
||||
// > 2M?
|
||||
if(float64(len(data)) > maxFileSize * float64(1024*1024)) {
|
||||
msg = "fileIsTooLarge"
|
||||
return
|
||||
}
|
||||
|
||||
toPath := dir + "/" + filename;
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 改变成gif图片
|
||||
_, toPathGif := TransToGif(toPath, 0, true)
|
||||
filename = GetFilename(toPathGif)
|
||||
filesize := GetFilesize(toPathGif)
|
||||
fileUrlPath += "/" + filename
|
||||
|
||||
// File
|
||||
fileInfo := info.File{FileId: bson.NewObjectId(),
|
||||
Name: filename,
|
||||
Title: handel.Filename,
|
||||
Path: fileUrlPath,
|
||||
Size: filesize}
|
||||
ok, msg = fileService.AddImage(fileInfo, "", c.getUserId(), true)
|
||||
if ok {
|
||||
id = fileInfo.FileId.Hex()
|
||||
}
|
||||
return
|
||||
}
|
164
app/controllers/api/ApiFileController.go
Normal file
164
app/controllers/api/ApiFileController.go
Normal file
@ -0,0 +1,164 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "github.com/leanote/leanote/app/lea/netutil"
|
||||
// "github.com/leanote/leanote/app/info"
|
||||
// "io/ioutil"
|
||||
"os"
|
||||
// "strconv"
|
||||
"io"
|
||||
"time"
|
||||
"strings"
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
)
|
||||
|
||||
// 文件操作, 图片, 头像上传, 输出
|
||||
|
||||
type ApiFile struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
/*
|
||||
// 协作时复制图片到owner
|
||||
func (c ApiFile) CopyImage(userId, fileId, toUserId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
re.Ok, re.Id = fileService.CopyImage(userId, fileId, toUserId)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// get all images by userId with page
|
||||
func (c ApiFile) GetImages(albumId, key string, page int) revel.Result {
|
||||
imagesPage := fileService.ListImagesWithPage(c.getUserId(), albumId, key, page, 12)
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.Item = imagesPage
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c ApiFile) UpdateImageTitle(fileId, title string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = fileService.UpdateImageTitle(c.getUserId(), fileId, title)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c ApiFile) DeleteImage(fileId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = fileService.DeleteImage(c.getUserId(), fileId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
//-----------
|
||||
|
||||
// 输出image
|
||||
// [OK]
|
||||
func (c ApiFile) GetImage(fileId string) revel.Result {
|
||||
path := fileService.GetFile(c.getUserId(), fileId) // 得到路径
|
||||
if path == "" {
|
||||
return c.RenderText("")
|
||||
}
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
file, _ := os.Open(fn)
|
||||
return c.RenderFile(file, revel.Inline) // revel.Attachment
|
||||
}
|
||||
|
||||
// 下载附件
|
||||
// [OK]
|
||||
func (c ApiFile) GetAttach(fileId string) revel.Result {
|
||||
attach := attachService.GetAttach(fileId, c.getUserId()); // 得到路径
|
||||
path := attach.Path
|
||||
if path == "" {
|
||||
return c.RenderText("No Such File")
|
||||
}
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
file, _ := os.Open(fn)
|
||||
return c.RenderBinary(file, attach.Title, revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
|
||||
// 下载所有附件
|
||||
// [OK]
|
||||
func (c ApiFile) GetAllAttachs(noteId string) revel.Result {
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
return c.RenderText("")
|
||||
}
|
||||
// 得到文件列表
|
||||
attachs := attachService.ListAttachs(noteId, c.getUserId())
|
||||
if attachs == nil || len(attachs) == 0 {
|
||||
return c.RenderText("")
|
||||
}
|
||||
|
||||
/*
|
||||
dir := revel.BasePath + "/files/tmp"
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
*/
|
||||
|
||||
filename := note.Title + ".tar.gz"
|
||||
if note.Title == "" {
|
||||
filename = "all.tar.gz"
|
||||
}
|
||||
|
||||
// file write
|
||||
fw, err := os.Create(revel.BasePath + "/files/" + filename)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
|
||||
|
||||
// gzip write
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
|
||||
// tar write
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
// 遍历文件列表
|
||||
for _, attach := range attachs {
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(attach.Path, "/")
|
||||
fr, err := os.Open(fn)
|
||||
fileInfo, _ := fr.Stat()
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// 信息头
|
||||
h := new(tar.Header)
|
||||
h.Name = attach.Title
|
||||
h.Size = fileInfo.Size()
|
||||
h.Mode = int64(fileInfo.Mode())
|
||||
h.ModTime = fileInfo.ModTime()
|
||||
|
||||
// 写信息头
|
||||
err = tw.WriteHeader(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 写文件
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} // for
|
||||
|
||||
// tw.Close()
|
||||
// gw.Close()
|
||||
// fw.Close()
|
||||
// file, _ := os.Open(dir + "/" + filename)
|
||||
// fw.Seek(0, 0)
|
||||
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
|
573
app/controllers/api/ApiNoteController.go
Normal file
573
app/controllers/api/ApiNoteController.go
Normal file
@ -0,0 +1,573 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "bytes"
|
||||
// "os"
|
||||
)
|
||||
|
||||
// 笔记API
|
||||
|
||||
type ApiNote struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 获取同步的笔记
|
||||
// > afterUsn的笔记
|
||||
// 无Desc, Abstract, 有Files
|
||||
/*
|
||||
{
|
||||
"NoteId": "55195fa199c37b79be000005",
|
||||
"NotebookId": "55195fa199c37b79be000002",
|
||||
"UserId": "55195fa199c37b79be000001",
|
||||
"Title": "Leanote语法Leanote语法Leanote语法Leanote语法Leanote语法",
|
||||
"Desc": "",
|
||||
"Tags": null,
|
||||
"Abstract": "",
|
||||
"Content": "",
|
||||
"IsMarkdown": true,
|
||||
"IsBlog": false,
|
||||
"IsTrash": false,
|
||||
"Usn": 5,
|
||||
"Files": [],
|
||||
"CreatedTime": "2015-03-30T22:37:21.695+08:00",
|
||||
"UpdatedTime": "2015-03-30T22:37:21.724+08:00",
|
||||
"PublicTime": "2015-03-30T22:37:21.695+08:00"
|
||||
}
|
||||
*/
|
||||
func (c ApiNote) GetSyncNotes(afterUsn, maxEntry int) revel.Result {
|
||||
if maxEntry == 0 {
|
||||
maxEntry = 100
|
||||
}
|
||||
notes := noteService.GetSyncNotes(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(notes)
|
||||
}
|
||||
|
||||
// 得到笔记本下的笔记
|
||||
// [OK]
|
||||
func (c ApiNote) GetNotes(notebookId string) revel.Result {
|
||||
if notebookId != "" && !bson.IsObjectIdHex(notebookId) {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "notebookIdInvalid"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
_, notes := noteService.ListNotes(c.getUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(noteService.ToApiNotes(notes))
|
||||
}
|
||||
|
||||
// 得到trash
|
||||
// [OK]
|
||||
func (c ApiNote) GetTrashNotes() revel.Result {
|
||||
_, notes := noteService.ListNotes(c.getUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(noteService.ToApiNotes(notes))
|
||||
}
|
||||
|
||||
// get Note
|
||||
// [OK]
|
||||
/*
|
||||
{
|
||||
"NoteId": "550c0bee2ec82a2eb5000000",
|
||||
"NotebookId": "54a1676399c37b1c77000004",
|
||||
"UserId": "54a1676399c37b1c77000002",
|
||||
"Title": "asdfadsf--=",
|
||||
"Desc": "",
|
||||
"Tags": [
|
||||
""
|
||||
],
|
||||
"Abstract": "",
|
||||
"Content": "",
|
||||
"IsMarkdown": false,
|
||||
"IsBlog": false,
|
||||
"IsTrash": false,
|
||||
"Usn": 8,
|
||||
"Files": [
|
||||
{
|
||||
"FileId": "551975d599c37b970f000002",
|
||||
"LocalFileId": "",
|
||||
"Type": "",
|
||||
"Title": "",
|
||||
"HasBody": false,
|
||||
"IsAttach": false
|
||||
},
|
||||
{
|
||||
"FileId": "551975de99c37b970f000003",
|
||||
"LocalFileId": "",
|
||||
"Type": "doc",
|
||||
"Title": "李铁-简历-ali-print-en.doc",
|
||||
"HasBody": false,
|
||||
"IsAttach": true
|
||||
},
|
||||
{
|
||||
"FileId": "551975de99c37b970f000004",
|
||||
"LocalFileId": "",
|
||||
"Type": "doc",
|
||||
"Title": "李铁-简历-ali-print.doc",
|
||||
"HasBody": false,
|
||||
"IsAttach": true
|
||||
}
|
||||
],
|
||||
"CreatedTime": "2015-03-20T20:00:52.463+08:00",
|
||||
"UpdatedTime": "2015-03-31T00:12:44.967+08:00",
|
||||
"PublicTime": "2015-03-20T20:00:52.463+08:00"
|
||||
}
|
||||
*/
|
||||
func (c ApiNote) GetNote(noteId string) revel.Result {
|
||||
if !bson.IsObjectIdHex(noteId) {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "noteIdInvalid"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
note := noteService.GetNote(noteId, c.getUserId())
|
||||
if note.NoteId == "" {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "notExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
apiNotes := noteService.ToApiNotes([]info.Note{note})
|
||||
return c.RenderJson(apiNotes[0])
|
||||
}
|
||||
|
||||
// 得到note和内容
|
||||
// [OK]
|
||||
func (c ApiNote) GetNoteAndContent(noteId string) revel.Result {
|
||||
noteAndContent := noteService.GetNoteAndContent(noteId, c.getUserId())
|
||||
|
||||
apiNotes := noteService.ToApiNotes([]info.Note{noteAndContent.Note})
|
||||
apiNote := apiNotes[0]
|
||||
apiNote.Content = noteAndContent.Content
|
||||
return c.RenderJson(apiNote)
|
||||
}
|
||||
|
||||
// 处理笔记内容数据 http://leanote.com/file/outputImage -> https://leanote.com/api/file/getImage
|
||||
// 图片, 附件都替换
|
||||
func (c ApiNote) fixContent(content string) string {
|
||||
// TODO, 这个url需要从config中取
|
||||
// baseUrl := "http://leanote.com"
|
||||
baseUrl := configService.GetSiteUrl()
|
||||
// baseUrl := "http://localhost:9000"
|
||||
|
||||
patterns := []map[string]string{
|
||||
map[string]string{"src": "src", "middle": "/file/outputImage", "param": "fileId", "to": "getImage?fileId="},
|
||||
map[string]string{"src": "href", "middle": "/attach/download", "param": "attachId", "to": "getAttach?fileId="},
|
||||
map[string]string{"src": "href", "middle": "/attach/downloadAll", "param": "noteId", "to": "getAllAttachs?noteId="},
|
||||
}
|
||||
|
||||
for _, eachPattern := range patterns {
|
||||
|
||||
// src="http://leanote.com/file/outputImage?fileId=5503537b38f4111dcb0000d1"
|
||||
// href="http://leanote.com/attach/download?attachId=5504243a38f4111dcb00017d"
|
||||
// href="http://leanote.com/attach/downloadAll?noteId=55041b6a38f4111dcb000159"
|
||||
|
||||
regImage, _ := regexp.Compile(eachPattern["src"] + `=('|")`+ baseUrl + eachPattern["middle"] + `\?` + eachPattern["param"] + `=([a-z0-9A-Z]{24})("|')`)
|
||||
findsImage := regImage.FindAllStringSubmatch(content, -1) // 查找所有的
|
||||
|
||||
// [[src='http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" ' 54672e8d38f411286b000069 "] [src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " 54672e8d38f411286b000069 "] [src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " 54672e8d38f411286b000069 "] [src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " 54672e8d38f411286b000069 "]]
|
||||
for _, eachFind := range findsImage {
|
||||
// [src='http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" ' 54672e8d38f411286b000069 "]
|
||||
if len(eachFind) == 4 {
|
||||
content = strings.Replace(content,
|
||||
eachFind[0],
|
||||
eachPattern["src"] + "=\"" + baseUrl + "/api/file/" + eachPattern["to"] + eachFind[2] + "\"",
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
// markdown处理
|
||||
// 
|
||||
// [selection 2.html](http://leanote.com/attach/download?attachId=5504262638f4111dcb00017f)
|
||||
// [all.tar.gz](http://leanote.com/attach/downloadAll?noteId=5503b57d59f81b4eb4000000)
|
||||
|
||||
pre := "!" // 默认图片
|
||||
if eachPattern["src"] == "href" { // 是attach
|
||||
pre = ""
|
||||
}
|
||||
|
||||
regImageMarkdown, _ := regexp.Compile(pre + `\[(.*?)\]\(`+ baseUrl + eachPattern["middle"] + `\?` + eachPattern["param"] + `=([a-z0-9A-Z]{24})\)`)
|
||||
findsImageMarkdown := regImageMarkdown.FindAllStringSubmatch(content, -1) // 查找所有的
|
||||
// [[ 5503537b38f4111dcb0000d1] [ 5503537b38f4111dcb0000d1]]
|
||||
for _, eachFind := range findsImageMarkdown {
|
||||
// [ 你好啊, 我很好, 为什么? 5503537b38f4111dcb0000d1]
|
||||
if len(eachFind) == 3 {
|
||||
content = strings.Replace(content, eachFind[0], pre + "[" + eachFind[1] + "](" + baseUrl + "/api/file/" + eachPattern["to"] + eachFind[2] + ")", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
// content里的image, attach链接是
|
||||
// https://leanote.com/api/file/getImage?fileId=xx
|
||||
// https://leanote.com/api/file/getAttach?fileId=xx
|
||||
// 将fileId=映射成ServerFileId, 这里的fileId可能是本地的FileId
|
||||
func (c ApiNote) fixPostNotecontent(noteOrContent *info.ApiNote) {
|
||||
if noteOrContent.Content == "" {
|
||||
return
|
||||
}
|
||||
files := noteOrContent.Files
|
||||
if files != nil && len(files) > 0 {
|
||||
for _, file := range files {
|
||||
if file.LocalFileId != "" {
|
||||
noteOrContent.Content = strings.Replace(noteOrContent.Content, "fileId=" + file.LocalFileId, "fileId=" + file.FileId, -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 得到内容
|
||||
// [OK]
|
||||
func (c ApiNote) GetNoteContent(noteId string) revel.Result {
|
||||
// re := info.NewRe()
|
||||
noteContent := noteService.GetNoteContent(noteId, c.getUserId())
|
||||
if noteContent.Content != "" {
|
||||
noteContent.Content = c.fixContent(noteContent.Content)
|
||||
}
|
||||
|
||||
apiNoteContent := info.ApiNoteContent{
|
||||
NoteId: noteContent.NoteId,
|
||||
UserId: noteContent.UserId,
|
||||
Content: noteContent.Content,
|
||||
}
|
||||
|
||||
// re.Item = noteContent
|
||||
return c.RenderJson(apiNoteContent)
|
||||
}
|
||||
|
||||
// 添加笔记
|
||||
// [OK]
|
||||
func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
|
||||
userId := bson.ObjectIdHex(c.getUserId())
|
||||
re := info.NewRe()
|
||||
myUserId := userId
|
||||
// 为共享新建?
|
||||
/*
|
||||
if noteOrContent.FromUserId != "" {
|
||||
userId = bson.ObjectIdHex(noteOrContent.FromUserId)
|
||||
}
|
||||
*/
|
||||
// Log(noteOrContent.Title)
|
||||
// LogJ(noteOrContent)
|
||||
/*
|
||||
LogJ(c.Params)
|
||||
for name, _ := range c.Params.Files {
|
||||
Log(name)
|
||||
file, _, _ := c.Request.FormFile(name)
|
||||
LogJ(file)
|
||||
}
|
||||
*/
|
||||
// return c.RenderJson(re)
|
||||
if noteOrContent.NotebookId == "" || !bson.IsObjectIdHex(noteOrContent.NotebookId) {
|
||||
re.Msg = "notebookIdNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
noteId := bson.NewObjectId()
|
||||
// TODO 先上传图片/附件, 如果不成功, 则返回false
|
||||
//
|
||||
attachNum := 0
|
||||
if noteOrContent.Files != nil && len(noteOrContent.Files) > 0 {
|
||||
for i, file := range noteOrContent.Files {
|
||||
if file.HasBody {
|
||||
if file.LocalFileId != "" {
|
||||
// FileDatas[54c7ae27d98d0329dd000000]
|
||||
ok, msg, fileId := c.upload("FileDatas["+file.LocalFileId+"]", noteId.Hex(), file.IsAttach)
|
||||
|
||||
if !ok {
|
||||
re.Ok = false
|
||||
if msg != "" {
|
||||
Log(msg)
|
||||
Log(file.LocalFileId)
|
||||
re.Msg = "fileUploadError"
|
||||
}
|
||||
// 报不是图片的错误没关系, 证明客户端传来非图片的数据
|
||||
if msg != "notImage" {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
} else {
|
||||
// 建立映射
|
||||
file.FileId = fileId
|
||||
noteOrContent.Files[i] = file
|
||||
|
||||
if file.IsAttach {
|
||||
attachNum++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.fixPostNotecontent(¬eOrContent)
|
||||
|
||||
// Log("Add")
|
||||
// LogJ(noteOrContent)
|
||||
|
||||
// return c.RenderJson(re)
|
||||
|
||||
note := info.Note{UserId: userId,
|
||||
NoteId: noteId,
|
||||
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
|
||||
Title: noteOrContent.Title,
|
||||
Tags: noteOrContent.Tags,
|
||||
Desc: noteOrContent.Desc,
|
||||
// ImgSrc: noteOrContent.ImgSrc,
|
||||
IsBlog: noteOrContent.IsBlog,
|
||||
IsMarkdown: noteOrContent.IsMarkdown,
|
||||
AttachNum: attachNum,
|
||||
}
|
||||
noteContent := info.NoteContent{NoteId: note.NoteId,
|
||||
UserId: userId,
|
||||
IsBlog: note.IsBlog,
|
||||
Content: noteOrContent.Content,
|
||||
Abstract: noteOrContent.Abstract}
|
||||
|
||||
// 通过内容得到Desc, abstract
|
||||
note.Desc = SubStringHTMLToRaw(noteContent.Content, 50)
|
||||
noteContent.Abstract = SubStringHTML(noteContent.Content, 200, "")
|
||||
|
||||
note = noteService.AddNoteAndContentApi(note, noteContent, myUserId)
|
||||
|
||||
if note.NoteId == "" {
|
||||
re.Ok = false
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 添加需要返回的
|
||||
noteOrContent.NoteId = note.NoteId.Hex()
|
||||
noteOrContent.Usn = note.Usn
|
||||
noteOrContent.CreatedTime = note.CreatedTime
|
||||
noteOrContent.UpdatedTime = note.UpdatedTime
|
||||
noteOrContent.UserId = c.getUserId()
|
||||
noteOrContent.IsMarkdown = note.IsMarkdown
|
||||
// 删除一些不要返回的
|
||||
noteOrContent.Content = ""
|
||||
noteOrContent.Abstract = ""
|
||||
// apiNote := info.NoteToApiNote(note, noteOrContent.Files)
|
||||
return c.RenderJson(noteOrContent)
|
||||
}
|
||||
|
||||
// 更新笔记
|
||||
// [OK]
|
||||
func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
|
||||
re := info.NewReUpdate()
|
||||
|
||||
noteUpdate := bson.M{}
|
||||
needUpdateNote := false
|
||||
|
||||
noteId := noteOrContent.NoteId
|
||||
|
||||
if noteOrContent.NoteId == "" {
|
||||
re.Msg = "noteIdNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if noteOrContent.Usn <= 0 {
|
||||
re.Msg = "usnNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// Log("_____________")
|
||||
// LogJ(noteOrContent)
|
||||
/*
|
||||
LogJ(c.Params.Files)
|
||||
LogJ(c.Request.Header)
|
||||
LogJ(c.Params.Values)
|
||||
*/
|
||||
|
||||
// 先判断USN的问题, 因为很可能添加完附件后, 会有USN冲突, 这时附件就添错了
|
||||
userId := c.getUserId()
|
||||
note := noteService.GetNote(noteId, userId)
|
||||
if note.NoteId == "" {
|
||||
re.Msg = "notExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
if note.Usn != noteOrContent.Usn {
|
||||
re.Msg = "conflict"
|
||||
Log("conflict")
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 如果传了files
|
||||
// TODO 测试
|
||||
/*
|
||||
for key, v := range c.Params.Values {
|
||||
Log(key)
|
||||
Log(v)
|
||||
}
|
||||
*/
|
||||
// Log(c.Has("Files[0]"))
|
||||
if c.Has("Files[0][LocalFileId]") {
|
||||
// LogJ(c.Params.Files)
|
||||
if noteOrContent.Files != nil && len(noteOrContent.Files) > 0 {
|
||||
for i, file := range noteOrContent.Files {
|
||||
if file.HasBody {
|
||||
if file.LocalFileId != "" {
|
||||
// FileDatas[54c7ae27d98d0329dd000000]
|
||||
ok, msg, fileId := c.upload("FileDatas["+file.LocalFileId+"]", noteId, file.IsAttach)
|
||||
if !ok {
|
||||
Log("upload file error")
|
||||
re.Ok = false
|
||||
if msg == "" {
|
||||
re.Msg = "fileUploadError"
|
||||
} else {
|
||||
re.Msg = msg
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
} else {
|
||||
// 建立映射
|
||||
file.FileId = fileId
|
||||
noteOrContent.Files[i] = file
|
||||
}
|
||||
} else {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log("after upload")
|
||||
// LogJ(noteOrContent.Files)
|
||||
}
|
||||
|
||||
// 移到外面来, 删除最后一个file时也要处理, 不然总删不掉
|
||||
// 附件问题, 根据Files, 有些要删除的, 只留下这些
|
||||
attachService.UpdateOrDeleteAttachApi(noteId, userId, noteOrContent.Files)
|
||||
|
||||
// Desc前台传来
|
||||
if c.Has("Desc") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Desc"] = noteOrContent.Desc
|
||||
}
|
||||
/*
|
||||
if c.Has("ImgSrc") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["ImgSrc"] = noteOrContent.ImgSrc
|
||||
}
|
||||
*/
|
||||
if c.Has("Title") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Title"] = noteOrContent.Title
|
||||
}
|
||||
if c.Has("IsTrash") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["IsTrash"] = noteOrContent.IsTrash
|
||||
}
|
||||
|
||||
// 是否是博客
|
||||
if c.Has("IsBlog") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["IsBlog"] = noteOrContent.IsBlog
|
||||
}
|
||||
|
||||
/*
|
||||
Log(c.Has("tags[0]"))
|
||||
Log(c.Has("Tags[]"))
|
||||
for key, v := range c.Params.Values {
|
||||
Log(key)
|
||||
Log(v)
|
||||
}
|
||||
*/
|
||||
|
||||
if c.Has("Tags[0]") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Tags"] = noteOrContent.Tags
|
||||
}
|
||||
|
||||
if c.Has("NotebookId") {
|
||||
if bson.IsObjectIdHex(noteOrContent.NotebookId) {
|
||||
needUpdateNote = true
|
||||
noteUpdate["NotebookId"] = bson.ObjectIdHex(noteOrContent.NotebookId)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Has("Content") {
|
||||
// 通过内容得到Desc, abstract
|
||||
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Content, 50)
|
||||
}
|
||||
|
||||
afterNoteUsn := 0
|
||||
noteOk := false
|
||||
noteMsg := ""
|
||||
if needUpdateNote {
|
||||
noteOk, noteMsg, afterNoteUsn = noteService.UpdateNote(c.getUserId(), noteOrContent.NoteId, noteUpdate, noteOrContent.Usn)
|
||||
if !noteOk {
|
||||
re.Ok = false
|
||||
re.Msg = noteMsg
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
//-------------
|
||||
afterContentUsn := 0
|
||||
contentOk := false
|
||||
contentMsg := ""
|
||||
if c.Has("Content") {
|
||||
// 把fileId替换下
|
||||
c.fixPostNotecontent(¬eOrContent)
|
||||
// 如果传了Abstract就用之
|
||||
if noteOrContent.Abstract == "" {
|
||||
noteOrContent.Abstract = SubStringHTML(noteOrContent.Content, 200, "")
|
||||
}
|
||||
// Log("--------> afte fixed")
|
||||
// Log(noteOrContent.Content)
|
||||
contentOk, contentMsg, afterContentUsn = noteService.UpdateNoteContent(c.getUserId(),
|
||||
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract, needUpdateNote, noteOrContent.Usn)
|
||||
}
|
||||
|
||||
if needUpdateNote {
|
||||
re.Ok = noteOk
|
||||
re.Msg = noteMsg
|
||||
re.Usn = afterNoteUsn
|
||||
} else {
|
||||
re.Ok = contentOk
|
||||
re.Msg = contentMsg
|
||||
re.Usn = afterContentUsn
|
||||
}
|
||||
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
noteOrContent.Content = ""
|
||||
noteOrContent.Usn = re.Usn
|
||||
noteOrContent.UpdatedTime = time.Now()
|
||||
|
||||
// Log("after upload")
|
||||
// LogJ(noteOrContent.Files)
|
||||
noteOrContent.UserId = c.getUserId()
|
||||
|
||||
return c.RenderJson(noteOrContent)
|
||||
}
|
||||
|
||||
// 删除trash
|
||||
func (c ApiNote) DeleteTrash(noteId string, usn int) revel.Result {
|
||||
re := info.NewReUpdate()
|
||||
re.Ok, re.Msg, re.Usn = trashService.DeleteTrashApi(noteId, c.getUserId(), usn)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 得到历史列表
|
||||
/*
|
||||
func (c ApiNote) GetHistories(noteId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
histories := noteContentHistoryService.ListHistories(noteId, c.getUserId())
|
||||
if len(histories) > 0 {
|
||||
re.Ok = true
|
||||
re.Item = histories
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
*/
|
106
app/controllers/api/ApiNotebookController.go
Normal file
106
app/controllers/api/ApiNotebookController.go
Normal file
@ -0,0 +1,106 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
)
|
||||
|
||||
// 笔记本API
|
||||
|
||||
type ApiNotebook struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 从Notebook -> ApiNotebook
|
||||
func (c ApiNotebook) fixNotebooks(notebooks []info.Notebook) []info.ApiNotebook {
|
||||
if notebooks == nil {
|
||||
return nil
|
||||
}
|
||||
apiNotebooks := make([]info.ApiNotebook, len(notebooks))
|
||||
for i, notebook := range notebooks {
|
||||
apiNotebooks[i] = c.fixNotebook(¬ebook)
|
||||
}
|
||||
return apiNotebooks
|
||||
}
|
||||
func (c ApiNotebook) fixNotebook(notebook *info.Notebook) info.ApiNotebook {
|
||||
if notebook == nil {
|
||||
return info.ApiNotebook{}
|
||||
}
|
||||
return info.ApiNotebook{
|
||||
NotebookId : notebook.NotebookId,
|
||||
UserId : notebook.UserId,
|
||||
ParentNotebookId : notebook.ParentNotebookId,
|
||||
Seq : notebook.Seq,
|
||||
Title : notebook.Title,
|
||||
UrlTitle : notebook.UrlTitle,
|
||||
IsBlog : notebook.IsBlog,
|
||||
CreatedTime : notebook.CreatedTime,
|
||||
UpdatedTime : notebook.UpdatedTime,
|
||||
Usn: notebook.Usn,
|
||||
IsDeleted: notebook.IsDeleted,
|
||||
}
|
||||
}
|
||||
|
||||
// 获取同步的笔记本
|
||||
// [OK]
|
||||
// > afterUsn的笔记
|
||||
// 返回 {ChunkHighUsn: 本下最大的usn, 借此可以知道是否还有, Notebooks: []}
|
||||
func (c ApiNotebook) GetSyncNotebooks(afterUsn, maxEntry int) revel.Result {
|
||||
if maxEntry == 0 {
|
||||
maxEntry = 100
|
||||
}
|
||||
notebooks := notebookService.GeSyncNotebooks(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(c.fixNotebooks(notebooks))
|
||||
}
|
||||
|
||||
// 得到用户的所有笔记本
|
||||
// [OK]
|
||||
// info.SubNotebooks
|
||||
func (c ApiNotebook) GetNotebooks() revel.Result {
|
||||
notebooks := notebookService.GeSyncNotebooks(c.getUserId(), 0, 99999)
|
||||
return c.RenderJson(c.fixNotebooks(notebooks))
|
||||
}
|
||||
|
||||
// 添加notebook
|
||||
// [OK]
|
||||
func (c ApiNotebook) AddNotebook(title, parentNotebookId string, seq int) revel.Result {
|
||||
notebook := info.Notebook{NotebookId: bson.NewObjectId(),
|
||||
Title: title,
|
||||
Seq: seq,
|
||||
UserId: bson.ObjectIdHex(c.getUserId())}
|
||||
if parentNotebookId != "" && bson.IsObjectIdHex(parentNotebookId) {
|
||||
notebook.ParentNotebookId = bson.ObjectIdHex(parentNotebookId)
|
||||
}
|
||||
re := info.NewRe()
|
||||
re.Ok, notebook = notebookService.AddNotebook(notebook)
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
return c.RenderJson(c.fixNotebook(¬ebook))
|
||||
}
|
||||
|
||||
// 修改笔记
|
||||
// [OK]
|
||||
func (c ApiNotebook) UpdateNotebook(notebookId, title, parentNotebookId string, seq, usn int) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
|
||||
ok, msg, notebook := notebookService.UpdateNotebookApi(c.getUserId(), notebookId, title, parentNotebookId, seq, usn)
|
||||
if !ok {
|
||||
re.Ok = false
|
||||
re.Msg = msg
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
LogJ(notebook)
|
||||
return c.RenderJson(c.fixNotebook(¬ebook))
|
||||
}
|
||||
|
||||
// 删除笔记本
|
||||
// [OK]
|
||||
func (c ApiNotebook) DeleteNotebook(notebookId string, usn int) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
re.Ok, re.Msg = notebookService.DeleteNotebookForce(c.getUserId(), notebookId, usn)
|
||||
return c.RenderJson(re)
|
||||
}
|
56
app/controllers/api/ApiTagController.go
Normal file
56
app/controllers/api/ApiTagController.go
Normal file
@ -0,0 +1,56 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/revel/revel"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
)
|
||||
|
||||
// 标签API
|
||||
|
||||
type ApiTag struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 获取同步的标签
|
||||
// [OK]
|
||||
// > afterUsn的笔记
|
||||
// 返回 {ChunkHighUsn: 本下最大的usn, 借此可以知道是否还有, Notebooks: []}
|
||||
func (c ApiTag) GetSyncTags(afterUsn, maxEntry int) revel.Result {
|
||||
if maxEntry == 0 {
|
||||
maxEntry = 100
|
||||
}
|
||||
tags := tagService.GeSyncTags(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(tags)
|
||||
}
|
||||
|
||||
// 添加Tag
|
||||
// [OK]
|
||||
// 不会产生冲突, 即使里面有
|
||||
// 返回
|
||||
/*
|
||||
{
|
||||
"TagId": "551978dd99c37b9bc5000001",
|
||||
"UserId": "54a1676399c37b1c77000002",
|
||||
"Tag": "32",
|
||||
"Usn": 25,
|
||||
"Count": 1,
|
||||
"CreatedTime": "2015-03-31T00:25:01.149312407+08:00",
|
||||
"UpdatedTime": "2015-03-31T00:25:01.149312407+08:00",
|
||||
"IsDeleted": false
|
||||
}
|
||||
*/
|
||||
func (c ApiTag) AddTag(tag string) revel.Result {
|
||||
ret := tagService.AddOrUpdateTag(c.getUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
// [OK]
|
||||
func (c ApiTag) DeleteTag(tag string, usn int) revel.Result {
|
||||
re := info.NewReUpdate()
|
||||
re.Ok, re.Msg, re.Usn = tagService.DeleteTagApi(c.getUserId(), tag, usn)
|
||||
return c.RenderJson(re)
|
||||
}
|
155
app/controllers/api/ApiUserController.go
Normal file
155
app/controllers/api/ApiUserController.go
Normal file
@ -0,0 +1,155 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"time"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
"io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
"os"
|
||||
|
||||
// "path"
|
||||
// "strconv"
|
||||
)
|
||||
|
||||
type ApiUser struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
// [OK]
|
||||
func (c ApiUser) Info() revel.Result {
|
||||
re := info.NewApiRe()
|
||||
|
||||
userInfo := c.getUserInfo()
|
||||
if userInfo.UserId == "" {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
apiUser := info.ApiUser{
|
||||
UserId: userInfo.UserId.Hex(),
|
||||
Username: userInfo.Username,
|
||||
Email: userInfo.Email,
|
||||
Logo: userInfo.Logo,
|
||||
Verified: userInfo.Verified,
|
||||
}
|
||||
return c.RenderJson(apiUser)
|
||||
}
|
||||
|
||||
// 修改用户名
|
||||
// [OK]
|
||||
func (c ApiUser) UpdateUsername(username string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if c.GetUsername() == "demo" {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if re.Ok, re.Msg = Vd("username", username); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = userService.UpdateUsername(c.getUserId(), username)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
// [OK]
|
||||
func (c ApiUser) UpdatePwd(oldPwd, pwd string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if c.GetUsername() == "demo" {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", oldPwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
re.Ok, re.Msg = userService.UpdatePwd(c.getUserId(), oldPwd, pwd)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 获得同步状态
|
||||
// [OK]
|
||||
func (c ApiUser) GetSyncState() revel.Result {
|
||||
ret := bson.M{"LastSyncUsn": userService.GetUsn(c.getUserId()), "LastSyncTime": time.Now().Unix()}
|
||||
return c.RenderJson(ret)
|
||||
}
|
||||
|
||||
|
||||
// 头像设置
|
||||
// 参数file=文件
|
||||
// 成功返回{Logo: url} 头像新url
|
||||
// [OK]
|
||||
func (c ApiUser) UpdateLogo() revel.Result {
|
||||
ok, msg, url := c.uploadImage()
|
||||
|
||||
if ok {
|
||||
ok = userService.UpdateAvatar(c.getUserId(), url)
|
||||
return c.RenderJson(map[string]string{"Logo": url})
|
||||
} else {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = msg
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
// 上传图片
|
||||
func (c ApiUser) uploadImage() (ok bool, msg, url string) {
|
||||
var fileUrlPath = ""
|
||||
ok = false
|
||||
|
||||
file, handel, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
// 生成上传路径
|
||||
fileUrlPath = "public/upload/" + c.getUserId() + "/images/logo"
|
||||
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
|
||||
var ext string
|
||||
|
||||
_, ext = SplitFilename(filename)
|
||||
if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
|
||||
msg = "notImage"
|
||||
return
|
||||
}
|
||||
|
||||
filename = NewGuid() + ext
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
LogJ(err)
|
||||
return
|
||||
}
|
||||
|
||||
// > 5M?
|
||||
if len(data) > 5*1024*1024 {
|
||||
msg = "fileIsTooLarge"
|
||||
return
|
||||
}
|
||||
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
LogJ(err)
|
||||
return
|
||||
}
|
||||
|
||||
ok = true
|
||||
url = configService.GetSiteUrl() + "/" + fileUrlPath + "/" + filename
|
||||
return
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/leanote/leanote/app/info"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
// "strconv"
|
||||
)
|
||||
|
||||
type ApiUser struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// 修改用户名, 需要重置session
|
||||
func (c ApiUser) Info() revel.Result {
|
||||
Log("APIUser");
|
||||
return c.RenderTemplate("home/index.html");
|
||||
// return nil;
|
||||
}
|
144
app/controllers/api/init.go
Normal file
144
app/controllers/api/init.go
Normal file
@ -0,0 +1,144 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var userService *service.UserService
|
||||
var noteService *service.NoteService
|
||||
var trashService *service.TrashService
|
||||
var notebookService *service.NotebookService
|
||||
var noteContentHistoryService *service.NoteContentHistoryService
|
||||
var authService *service.AuthService
|
||||
var shareService *service.ShareService
|
||||
var blogService *service.BlogService
|
||||
var tagService *service.TagService
|
||||
var pwdService *service.PwdService
|
||||
var tokenService *service.TokenService
|
||||
var suggestionService *service.SuggestionService
|
||||
var albumService *service.AlbumService
|
||||
var noteImageService *service.NoteImageService
|
||||
var fileService *service.FileService
|
||||
var attachService *service.AttachService
|
||||
var configService *service.ConfigService
|
||||
var emailService *service.EmailService
|
||||
var sessionService *service.SessionService
|
||||
|
||||
var pageSize = 1000
|
||||
var defaultSortField = "UpdatedTime"
|
||||
var leanoteUserId = "admin" // 不能更改
|
||||
|
||||
// 状态
|
||||
const (
|
||||
S_DEFAULT = iota // 0
|
||||
S_NOT_LOGIN // 1
|
||||
S_WRONG_USERNAME_PASSWORD // 2
|
||||
S_WRONG_CAPTCHA // 3
|
||||
S_NEED_CAPTCHA // 4
|
||||
S_NOT_OPEN_REGISTER // 4
|
||||
)
|
||||
|
||||
// 拦截器
|
||||
// 不需要拦截的url
|
||||
var commonUrl = map[string]map[string]bool{"ApiAuth": map[string]bool{"Login": true,
|
||||
"Register": true,
|
||||
},
|
||||
// 文件的操作也不用登录, userId会从session中获取
|
||||
"ApiFile": map[string]bool{"GetImage": true,
|
||||
"GetAttach": true,
|
||||
"GetAllAttachs": true,
|
||||
},
|
||||
}
|
||||
|
||||
func needValidate(controller, method string) bool {
|
||||
// 在里面
|
||||
if v, ok := commonUrl[controller]; ok {
|
||||
// 在commonUrl里
|
||||
if _, ok2 := v[method]; ok2 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
// controller不在这里的, 肯定要验证
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 这里得到token, 若不是login, logout等公用操作, 必须验证是否已登录
|
||||
func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
// 得到token /api/user/info?userId=xxx&token=xxxxx
|
||||
token := c.Params.Values.Get("token")
|
||||
noToken := false
|
||||
if token == "" {
|
||||
// 若无, 则取sessionId
|
||||
token = c.Session.Id()
|
||||
noToken = true
|
||||
}
|
||||
c.Session["_token"] = token
|
||||
|
||||
// 全部变成首字大写
|
||||
var controller = strings.Title(c.Name)
|
||||
var method = strings.Title(c.MethodName)
|
||||
|
||||
// 验证是否已登录
|
||||
// 通过sessionService判断该token下是否有userId, 并返回userId
|
||||
userId := sessionService.GetUserId(token)
|
||||
if noToken && userId == "" {
|
||||
// 从session中获取, api/file/getImage, api/file/getAttach, api/file/getAllAttach
|
||||
// 客户端
|
||||
userId, _ = c.Session["UserId"];
|
||||
}
|
||||
c.Session["_userId"] = userId
|
||||
|
||||
// 是否需要验证?
|
||||
if !needValidate(controller, method) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if userId != "" {
|
||||
return nil // 已登录
|
||||
}
|
||||
|
||||
// 没有登录, 返回错误的信息, 需要登录
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "NOTLOGIN"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// interceptors
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiAuth{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiUser{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiFile{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiNote{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiTag{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiNotebook{})
|
||||
}
|
||||
|
||||
// 最外层init.go调用
|
||||
// 获取service, 单例
|
||||
func InitService() {
|
||||
notebookService = service.NotebookS
|
||||
noteService = service.NoteS
|
||||
noteContentHistoryService = service.NoteContentHistoryS
|
||||
trashService = service.TrashS
|
||||
shareService = service.ShareS
|
||||
userService = service.UserS
|
||||
tagService = service.TagS
|
||||
blogService = service.BlogS
|
||||
tokenService = service.TokenS
|
||||
noteImageService = service.NoteImageS
|
||||
fileService = service.FileS
|
||||
albumService = service.AlbumS
|
||||
attachService = service.AttachS
|
||||
pwdService = service.PwdS
|
||||
suggestionService = service.SuggestionS
|
||||
authService = service.AuthS
|
||||
configService = service.ConfigS
|
||||
emailService = service.EmailS
|
||||
sessionService = service.SessionS
|
||||
}
|
@ -28,7 +28,7 @@ var Groups *mgo.Collection
|
||||
var GroupUsers *mgo.Collection
|
||||
|
||||
var Tags *mgo.Collection
|
||||
//var TagNotes *mgo.Collection
|
||||
var NoteTags *mgo.Collection
|
||||
var TagCounts *mgo.Collection
|
||||
|
||||
var UserBlogs *mgo.Collection
|
||||
@ -116,7 +116,7 @@ func Init(url, dbname string) {
|
||||
|
||||
// tag
|
||||
Tags = Session.DB(dbname).C("tags")
|
||||
// TagNotes = Session.DB(dbname).C("tag_notes")
|
||||
NoteTags = Session.DB(dbname).C("note_tags")
|
||||
TagCounts = Session.DB(dbname).C("tag_count")
|
||||
|
||||
// blog
|
||||
|
119
app/info/Api.go
Normal file
119
app/info/Api.go
Normal file
@ -0,0 +1,119 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"time"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
//---------
|
||||
// 数据结构
|
||||
//---------
|
||||
type NoteFile struct {
|
||||
FileId string // 服务器端Id
|
||||
LocalFileId string // 客户端Id
|
||||
Type string // images/png, doc, xls, 根据fileName确定
|
||||
Title string
|
||||
HasBody bool // 传过来的值是否要更新内容
|
||||
IsAttach bool // 是否是附件, 不是附件就是图片
|
||||
}
|
||||
type ApiNote struct {
|
||||
NoteId string
|
||||
NotebookId string
|
||||
UserId string
|
||||
Title string
|
||||
Desc string
|
||||
// ImgSrc string
|
||||
Tags []string
|
||||
Abstract string
|
||||
Content string
|
||||
IsMarkdown bool
|
||||
// FromUserId string // 为共享而新建
|
||||
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
|
||||
IsTrash bool
|
||||
IsDeleted bool
|
||||
Usn int
|
||||
Files []NoteFile
|
||||
CreatedTime time.Time
|
||||
UpdatedTime time.Time
|
||||
PublicTime time.Time
|
||||
}
|
||||
|
||||
|
||||
// 内容
|
||||
type ApiNoteContent struct {
|
||||
NoteId bson.ObjectId `bson:"_id,omitempty"`
|
||||
UserId bson.ObjectId `bson:"UserId"`
|
||||
|
||||
Content string `Content`
|
||||
|
||||
// CreatedTime time.Time `CreatedTime`
|
||||
// UpdatedTime time.Time `UpdatedTime`
|
||||
}
|
||||
|
||||
// 转换
|
||||
func NoteToApiNote(note Note, files []NoteFile) ApiNote {
|
||||
apiNote := ApiNote{}
|
||||
return apiNote
|
||||
}
|
||||
|
||||
//----------
|
||||
// 用户信息
|
||||
//----------
|
||||
|
||||
type ApiUser struct {
|
||||
UserId string
|
||||
Username string
|
||||
Email string
|
||||
Verified bool
|
||||
Logo string
|
||||
}
|
||||
|
||||
//----------
|
||||
// Notebook
|
||||
//----------
|
||||
type ApiNotebook struct {
|
||||
NotebookId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
|
||||
UserId bson.ObjectId `bson:"UserId"`
|
||||
ParentNotebookId bson.ObjectId `bson:"ParentNotebookId,omitempty"` // 上级
|
||||
Seq int `Seq` // 排序
|
||||
Title string `Title` // 标题
|
||||
UrlTitle string `UrlTitle` // Url标题 2014/11.11加
|
||||
IsBlog bool `IsBlog,omitempty` // 是否是Blog 2013/12/29 新加
|
||||
CreatedTime time.Time `CreatedTime,omitempty`
|
||||
UpdatedTime time.Time `UpdatedTime,omitempty`
|
||||
Usn int `Usn` // UpdateSequenceNum
|
||||
IsDeleted bool `IsDeleted`
|
||||
}
|
||||
|
||||
//---------
|
||||
// api 返回
|
||||
//---------
|
||||
|
||||
// 一般返回
|
||||
type ApiRe struct {
|
||||
Ok bool
|
||||
Msg string
|
||||
}
|
||||
|
||||
func NewApiRe() ApiRe {
|
||||
return ApiRe{Ok: false}
|
||||
}
|
||||
|
||||
// auth
|
||||
type AuthOk struct {
|
||||
Ok bool
|
||||
Token string
|
||||
UserId bson.ObjectId
|
||||
Email string
|
||||
Username string
|
||||
}
|
||||
|
||||
// 供notebook, note, tag更新的返回数据用
|
||||
type ReUpdate struct {
|
||||
Ok bool
|
||||
Msg string
|
||||
Usn int
|
||||
}
|
||||
func NewReUpdate() ReUpdate {
|
||||
return ReUpdate{Ok: false}
|
||||
}
|
@ -47,3 +47,11 @@ type Archive struct {
|
||||
MonthAchives []ArchiveMonth
|
||||
Posts []*Post
|
||||
}
|
||||
|
||||
type Cate struct {
|
||||
CateId string
|
||||
ParentCateId string
|
||||
Title string
|
||||
UrlTitle string
|
||||
Children []*Cate
|
||||
}
|
||||
|
@ -34,12 +34,17 @@ type Note struct {
|
||||
IsMarkdown bool `IsMarkdown` // 是否是markdown笔记, 默认是false
|
||||
|
||||
AttachNum int `AttachNum` // 2014/9/21, attachments num
|
||||
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
UpdatedTime time.Time `UpdatedTime`
|
||||
RecommendTime time.Time `RecommendTime,omitempty` // 推荐时间
|
||||
PublicTime time.Time `PublicTime,omitempty` // 发表时间, 公开为博客则设置
|
||||
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了
|
||||
|
||||
// 2015/1/15, 更新序号
|
||||
Usn int `Usn` // UpdateSequenceNum
|
||||
|
||||
IsDeleted bool `IsDeleted` // 删除位
|
||||
}
|
||||
|
||||
// 内容
|
||||
|
@ -19,6 +19,10 @@ type Notebook struct {
|
||||
IsBlog bool `IsBlog,omitempty` // 是否是Blog 2013/12/29 新加
|
||||
CreatedTime time.Time `CreatedTime,omitempty`
|
||||
UpdatedTime time.Time `UpdatedTime,omitempty`
|
||||
|
||||
// 2015/1/15, 更新序号
|
||||
Usn int `Usn` // UpdateSequenceNum
|
||||
IsDeleted bool `IsDeleted`
|
||||
}
|
||||
|
||||
// 仅仅是为了返回前台
|
||||
|
@ -13,6 +13,8 @@ type Session struct {
|
||||
|
||||
LoginTimes int `LoginTimes` // 登录错误时间
|
||||
Captcha string `Captcha` // 验证码
|
||||
|
||||
UserId string `UserId` // API时有值UserId
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
UpdatedTime time.Time `UpdatedTime` // 更新时间, expire这个时间会自动清空
|
||||
|
@ -2,6 +2,7 @@ package info
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 这里主要是为了统计每个tag的note数目
|
||||
@ -21,6 +22,18 @@ type Tag struct {
|
||||
Tags []string `Tags`
|
||||
}
|
||||
|
||||
// v2 版标签
|
||||
type NoteTag struct {
|
||||
TagId bson.ObjectId `bson:"_id"`
|
||||
UserId bson.ObjectId `UserId` // 谁的
|
||||
Tag string `Tag` // UserId, Tag是唯一索引
|
||||
Usn int `Usn` // Update Sequence Number
|
||||
Count int `Count` // 笔记数
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
UpdatedTime time.Time `UpdatedTime`
|
||||
IsDeleted bool `IsDeleted` // 删除位
|
||||
}
|
||||
|
||||
type TagCount struct {
|
||||
TagCountId bson.ObjectId `bson:"_id,omitempty"`
|
||||
UserId bson.ObjectId `UserId` // 谁的
|
||||
@ -28,6 +41,7 @@ type TagCount struct {
|
||||
IsBlog bool `IsBlog` // 是否是博客的tag统计
|
||||
Count int `Count` // 统计数量
|
||||
}
|
||||
|
||||
/*
|
||||
type TagsCounts []TagCount
|
||||
func (this TagsCounts) Len() int {
|
||||
@ -39,4 +53,4 @@ func (this TagsCounts) Less(i, j int) bool {
|
||||
func (this TagsCounts) Swap(i, j int) {
|
||||
this[i], this[j] = this[j], this[i]
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
@ -36,22 +36,26 @@ type User struct {
|
||||
ThirdType int `ThirdType` // 第三方类型
|
||||
|
||||
// 用户的帐户类型
|
||||
|
||||
ImageNum int `bson:"ImageNum" json:"-"` // 图片数量
|
||||
ImageSize int `bson:"ImageSize" json:"-"` // 图片大小
|
||||
AttachNum int `bson:"AttachNum" json:"-"` // 附件数量
|
||||
AttachSize int `bson:"AttachSize" json:"-"` // 附件大小
|
||||
FromUserId bson.ObjectId `FromUserId,omitempty` // 邀请的用户
|
||||
|
||||
ImageNum int `bson:"ImageNum" json:"-"` // 图片数量
|
||||
ImageSize int `bson:"ImageSize" json:"-"` // 图片大小
|
||||
AttachNum int `bson:"AttachNum" json:"-"` // 附件数量
|
||||
AttachSize int `bson:"AttachSize" json:"-"` // 附件大小
|
||||
FromUserId bson.ObjectId `FromUserId,omitempty` // 邀请的用户
|
||||
|
||||
AccountType string `bson:"AccountType" json:"-"` // normal(为空), premium
|
||||
AccountStartTime time.Time `bson:"AccountStartTime" json:"-"` // 开始日期
|
||||
AccountEndTime time.Time `bson:"AccountEndTime" json:"-"` // 结束日期
|
||||
// 阈值
|
||||
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
|
||||
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
|
||||
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
|
||||
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
|
||||
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
|
||||
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
|
||||
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
|
||||
// 2015/1/15, 更新序号
|
||||
Usn int `Usn` // UpdateSequenceNum , 全局的
|
||||
FullSyncBefore time.Time `bson:"FullSyncBefore"` // 需要全量同步的时间, 如果 > 客户端的LastSyncTime, 则需要全量更新
|
||||
}
|
||||
|
||||
type UserAccount struct {
|
||||
@ -59,11 +63,11 @@ type UserAccount struct {
|
||||
AccountStartTime time.Time `bson:"AccountStartTime" json:"-"` // 开始日期
|
||||
AccountEndTime time.Time `bson:"AccountEndTime" json:"-"` // 结束日期
|
||||
// 阈值
|
||||
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
|
||||
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
|
||||
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
|
||||
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
|
||||
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
|
||||
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
|
||||
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
}
|
||||
|
||||
// 用户与博客信息结合, 公开
|
||||
@ -75,6 +79,6 @@ type UserAndBlog struct {
|
||||
BlogTitle string `BlogTitle` // 博客标题
|
||||
BlogLogo string `BlogLogo` // 博客Logo
|
||||
BlogUrl string `BlogUrl` // 博客链接, 主页
|
||||
|
||||
|
||||
BlogUrls // 各个页面
|
||||
}
|
||||
|
10
app/init.go
10
app/init.go
@ -6,6 +6,7 @@ import (
|
||||
"github.com/leanote/leanote/app/service"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
"github.com/leanote/leanote/app/controllers/api"
|
||||
"github.com/leanote/leanote/app/controllers/admin"
|
||||
"github.com/leanote/leanote/app/controllers/member"
|
||||
_ "github.com/leanote/leanote/app/lea/binder"
|
||||
@ -373,5 +374,12 @@ func init() {
|
||||
admin.InitService()
|
||||
member.InitService()
|
||||
service.ConfigS.InitGlobalConfigs()
|
||||
<<<<<<< HEAD
|
||||
});
|
||||
}
|
||||
}
|
||||
=======
|
||||
api.InitService()
|
||||
})
|
||||
|
||||
}
|
||||
>>>>>>> dev-life
|
||||
|
@ -122,9 +122,44 @@ func ReplaceAll(oldStr, pattern, newStr string) string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// 获取纯文本
|
||||
func SubStringHTMLToRaw(param string, length int) (result string) {
|
||||
if param == "" {
|
||||
return ""
|
||||
}
|
||||
result = ""
|
||||
n := 0
|
||||
var temp rune // 中文问题, 用rune来解决
|
||||
rStr := []rune(param)
|
||||
isCode := false
|
||||
for i := 0; i < len(rStr); i++ {
|
||||
temp = rStr[i]
|
||||
if temp == '<' {
|
||||
isCode = true
|
||||
continue
|
||||
} else if temp == '>' {
|
||||
isCode = false
|
||||
result += " "; // 空格
|
||||
continue
|
||||
}
|
||||
if !isCode {
|
||||
result += string(temp)
|
||||
n++
|
||||
if n >= length {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 获取摘要, HTML
|
||||
func SubStringHTML(param string, length int, end string) string {
|
||||
// 先取出<pre></pre>占位..
|
||||
if param == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 先取出<pre></pre>占位..
|
||||
result := ""
|
||||
|
||||
// 1
|
||||
@ -196,6 +231,7 @@ func SubStringHTML(param string, length int, end string) string {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 是否是合格的密码
|
||||
func IsGoodPwd(pwd string) (bool, string) {
|
||||
if pwd == "" {
|
||||
@ -212,7 +248,7 @@ func IsEmail(email string) bool {
|
||||
if email == "" {
|
||||
return false;
|
||||
}
|
||||
ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[0-9a-zA-Z]{2,3}$`, email)
|
||||
ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-z\-A-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,3}$`, email)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
// "github.com/leanote/leanote/app/controllers/api"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -54,15 +55,34 @@ func nextKey(key string) string {
|
||||
return key[:fieldLen]
|
||||
}
|
||||
var leanoteStructBinder = revel.Binder{
|
||||
// name == "noteOrContent"
|
||||
Bind: func(params *revel.Params, name string, typ reflect.Type) reflect.Value {
|
||||
result := reflect.New(typ).Elem()
|
||||
result := reflect.New(typ).Elem() // 创建一个该类型的, 然后其field从所有的param去取
|
||||
fieldValues := make(map[string]reflect.Value)
|
||||
for key, _ := range params.Values {
|
||||
// fmt.Println(name)
|
||||
// fmt.Println(typ) // api.NoteFiles
|
||||
// name = files[0], files[1], noteContent
|
||||
// fmt.Println(params.Values)
|
||||
/*
|
||||
map[Title:[test1] METHOD:[POST] NotebookId:[54c4f51705fcd14031000002]
|
||||
files[1][FileId]:[]
|
||||
controller:[note]
|
||||
files[1][LocalFileId]:[54c7ae27d98d0329dd000000] files[1][HasBody]:[true] files[0][FileId]:[] files[0][LocalFileId]:[54c7ae855e94ea2dba000000] action:[addNote] Content:[<p>lifedddddd</p><p><img src="app://leanote/data/54bdc65599c37b0da9000002/images/1422368307147_2.png" alt="" data-mce-src="app://leanote/data/54bdc65599c37b0da9000002/images/1422368307147_2.png" style="display: block; margin-left: auto; margin-right: auto;"></p><p><img src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae27d98d0329dd000000" alt="" data-mce-src="http://127.0.0.1:8008/api/file/getImg?fileId=54c7ae27d98d0329dd000000"></p><p><br></p><p><img src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae855e94ea2dba000000" alt="" data-mce-src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae855e94ea2dba000000" style="display: block; margin-left: auto; margin-right: auto;"></p><p><br></p><p><br></p>] IsBlog:[false] token:[user1]
|
||||
files[0][HasBody]:[true]]
|
||||
*/
|
||||
nameIsSlice := strings.Contains(name, "[")
|
||||
// fmt.Println(params.Values["files[1]"])
|
||||
// fmt.Println(params.Values["Title"])
|
||||
for key, _ := range params.Values {// Title, Content, Files
|
||||
// 这里, 如果没有点, 默认就是a.
|
||||
// life
|
||||
// fmt.Println("key:" + key); // files[0][LocalFileId]
|
||||
// fmt.Println("name:" + name); // files[0][LocalFileId]
|
||||
var suffix string
|
||||
var noPrefix = false
|
||||
if !strings.HasPrefix(key, name + ".") {
|
||||
if nameIsSlice && strings.HasPrefix(key, name) {
|
||||
suffix = key[len(name)+1:len(key)-1] // files[0][LocalFileId] 去掉 => LocalFileId
|
||||
} else if !strings.HasPrefix(key, name + ".") {
|
||||
noPrefix = true
|
||||
suffix = key
|
||||
// continue
|
||||
@ -71,13 +91,18 @@ var leanoteStructBinder = revel.Binder{
|
||||
// Strip off the prefix. e.g. foo.bar.baz => bar.baz
|
||||
suffix = key[len(name)+1:]
|
||||
}
|
||||
// fmt.Println(suffix);
|
||||
|
||||
fieldName := nextKey(suffix) // e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar"
|
||||
// fmt.Println(fieldName);
|
||||
fieldLen := len(fieldName)
|
||||
|
||||
if _, ok := fieldValues[fieldName]; !ok {
|
||||
// Time to bind this field. Get it and make sure we can set it.
|
||||
fieldName = strings.Title(fieldName) // 传过来title, 但struct是Title
|
||||
// fmt.Println("xx: " + fieldName)
|
||||
fieldValue := result.FieldByName(fieldName)
|
||||
// fmt.Println(fieldValue)
|
||||
if !fieldValue.IsValid() {
|
||||
continue
|
||||
}
|
||||
@ -87,8 +112,16 @@ var leanoteStructBinder = revel.Binder{
|
||||
var boundVal reflect.Value
|
||||
// 没有name前缀
|
||||
if(noPrefix) {
|
||||
// life
|
||||
// fmt.Println("<<")
|
||||
// fmt.Println(strings.Title(key[:fieldLen]));
|
||||
boundVal = revel.Bind(params, key[:fieldLen], fieldValue.Type())
|
||||
} else {
|
||||
// fmt.Println("final")
|
||||
// fmt.Println(key[:len(name)+1+fieldLen]) // files[0][HasBody
|
||||
if nameIsSlice {
|
||||
fieldLen += 1
|
||||
}
|
||||
boundVal = revel.Bind(params, key[:len(name)+1+fieldLen], fieldValue.Type())
|
||||
}
|
||||
fieldValue.Set(boundVal)
|
||||
@ -116,5 +149,8 @@ func init() {
|
||||
revel.TypeBinders[reflect.TypeOf(info.UserBlogComment{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.UserBlogStyle{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.Notebook{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.UserAccount{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(controllers.NoteOrContent{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.ApiNote{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.NoteFile{})] = leanoteStructBinder
|
||||
}
|
@ -179,8 +179,6 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
|
||||
// 将该basePath下的所有文件提出
|
||||
files := ListDir(basePath)
|
||||
Log(basePath)
|
||||
LogJ(files);
|
||||
for _, t := range files {
|
||||
if !strings.Contains(t, ".html") {
|
||||
continue;
|
||||
@ -215,7 +213,8 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
|
||||
|
||||
////////////////////
|
||||
// 错误显示
|
||||
//
|
||||
|
||||
|
||||
type ErrorResult struct {
|
||||
RenderArgs map[string]interface{}
|
||||
@ -290,13 +289,12 @@ func (r ErrorResult) Apply(req *revel.Request, resp *revel.Response) {
|
||||
r.RenderArgs["Router"] = revel.MainRouter
|
||||
|
||||
// 不是preview就不要显示错误了
|
||||
LogJ(revelError)
|
||||
// if r.IsPreview {
|
||||
if r.IsPreview {
|
||||
var b bytes.Buffer
|
||||
out := io.Writer(resp.Out)
|
||||
// out = ioutil.Discard
|
||||
err = tmpl.Execute(&b, r.RenderArgs)
|
||||
resp.WriteHeader(http.StatusOK, "text/html; charset=utf-8")
|
||||
b.WriteTo(out)
|
||||
// }
|
||||
}
|
||||
}
|
@ -1,30 +1,47 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AttachService struct {
|
||||
}
|
||||
|
||||
// add attach
|
||||
func (this *AttachService) AddAttach(attach info.Attach) (ok bool, msg string) {
|
||||
// api调用时, 添加attach之前是没有note的
|
||||
// fromApi表示是api添加的, updateNote传过来的, 此时不要incNote's usn, 因为updateNote会inc的
|
||||
func (this *AttachService) AddAttach(attach info.Attach, fromApi bool) (ok bool, msg string) {
|
||||
attach.CreatedTime = time.Now()
|
||||
ok = db.Insert(db.Attachs, attach)
|
||||
|
||||
note := noteService.GetNoteById(attach.NoteId.Hex())
|
||||
|
||||
// api调用时, 添加attach之前是没有note的
|
||||
var userId string
|
||||
if note.NoteId != "" {
|
||||
userId = note.UserId.Hex()
|
||||
} else {
|
||||
userId = attach.UploadUserId.Hex()
|
||||
}
|
||||
|
||||
if ok {
|
||||
// 更新笔记的attachs num
|
||||
this.updateNoteAttachNum(attach.NoteId, 1)
|
||||
}
|
||||
|
||||
return
|
||||
if !fromApi {
|
||||
// 增长note's usn
|
||||
noteService.IncrNoteUsn(attach.NoteId.Hex(), userId)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 更新笔记的附件个数
|
||||
@ -50,17 +67,32 @@ func (this *AttachService) ListAttachs(noteId, userId string) []info.Attach {
|
||||
if !shareService.HasUpdateNotePerm(noteId, userId) {
|
||||
return attachs
|
||||
}
|
||||
|
||||
|
||||
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)
|
||||
|
||||
|
||||
return attachs
|
||||
}
|
||||
|
||||
// api调用, 通过noteIds得到note's attachs, 通过noteId归类返回
|
||||
func (this *AttachService) getAttachsByNoteIds(noteIds []bson.ObjectId) map[string][]info.Attach {
|
||||
attachs := []info.Attach{}
|
||||
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.M{"$in": noteIds}}, &attachs)
|
||||
noteAttchs := make(map[string][]info.Attach)
|
||||
for _, attach := range attachs {
|
||||
noteId := attach.NoteId.Hex()
|
||||
if itAttachs, ok := noteAttchs[noteId]; ok {
|
||||
noteAttchs[noteId] = append(itAttachs, attach)
|
||||
} else {
|
||||
noteAttchs[noteId] = []info.Attach{attach}
|
||||
}
|
||||
}
|
||||
return noteAttchs
|
||||
}
|
||||
|
||||
func (this *AttachService) UpdateImageTitle(userId, fileId, title string) bool {
|
||||
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
|
||||
}
|
||||
|
||||
|
||||
// Delete note to delete attas firstly
|
||||
func (this *AttachService) DeleteAllAttachs(noteId, userId string) bool {
|
||||
note := noteService.GetNoteById(noteId)
|
||||
@ -73,7 +105,7 @@ func (this *AttachService) DeleteAllAttachs(noteId, userId string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@ -93,7 +125,11 @@ func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string)
|
||||
attach.Path = strings.TrimLeft(attach.Path, "/")
|
||||
err := os.Remove(revel.BasePath + "/" + attach.Path)
|
||||
if err == nil {
|
||||
return true, "delete file error"
|
||||
// userService.UpdateAttachSize(note.UserId.Hex(), -attach.Size)
|
||||
// 修改note Usn
|
||||
noteService.IncrNoteUsn(attach.NoteId.Hex(), userId)
|
||||
|
||||
return true, "delete file success"
|
||||
}
|
||||
return false, "delete file error"
|
||||
}
|
||||
@ -107,37 +143,37 @@ func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string)
|
||||
// userId是否具有attach的访问权限
|
||||
func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attach) {
|
||||
if attachId == "" {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
attach = info.Attach{}
|
||||
db.Get(db.Attachs, attachId, &attach)
|
||||
path := attach.Path
|
||||
if path == "" {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
note := noteService.GetNoteById(attach.NoteId.Hex())
|
||||
|
||||
|
||||
// 判断权限
|
||||
|
||||
|
||||
// 笔记是否是公开的
|
||||
if note.IsBlog {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 笔记是否是我的
|
||||
if note.UserId.Hex() == userId {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 我是否有权限查看或协作
|
||||
if shareService.HasReadNotePerm(attach.NoteId.Hex(), userId) {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
attach = info.Attach{}
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// 复制笔记时需要复制附件
|
||||
@ -145,31 +181,58 @@ func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attac
|
||||
func (this *AttachService) CopyAttachs(noteId, toNoteId, toUserId string) bool {
|
||||
attachs := []info.Attach{}
|
||||
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)
|
||||
|
||||
|
||||
// 复制之
|
||||
toNoteIdO := bson.ObjectIdHex(toNoteId)
|
||||
for _, attach := range attachs {
|
||||
attach.AttachId = ""
|
||||
attach.NoteId = toNoteIdO
|
||||
|
||||
|
||||
// 文件复制一份
|
||||
_, ext := SplitFilename(attach.Name)
|
||||
newFilename := NewGuid() + ext
|
||||
dir := "files/" + toUserId + "/attachs"
|
||||
filePath := dir + "/" + newFilename
|
||||
err := os.MkdirAll(revel.BasePath + "/" + dir, 0755)
|
||||
err := os.MkdirAll(revel.BasePath+"/"+dir, 0755)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = CopyFile(revel.BasePath + "/" + attach.Path, revel.BasePath + "/" + filePath)
|
||||
_, err = CopyFile(revel.BasePath+"/"+attach.Path, revel.BasePath+"/"+filePath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
attach.Name = newFilename
|
||||
attach.Path = filePath
|
||||
|
||||
this.AddAttach(attach)
|
||||
|
||||
this.AddAttach(attach, false)
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 只留下files的数据, 其它的都删除
|
||||
func (this *AttachService) UpdateOrDeleteAttachApi(noteId, userId string, files []info.NoteFile) bool {
|
||||
// 现在数据库内的
|
||||
attachs := this.ListAttachs(noteId, userId)
|
||||
|
||||
nowAttachs := map[string]bool{}
|
||||
if files != nil {
|
||||
for _, file := range files {
|
||||
if file.IsAttach && file.FileId != "" {
|
||||
nowAttachs[file.FileId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, attach := range attachs {
|
||||
fileId := attach.AttachId.Hex()
|
||||
if !nowAttachs[fileId] {
|
||||
// 需要删除的
|
||||
// TODO 权限验证去掉
|
||||
this.DeleteAttach(fileId, userId)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ func (this *AuthService) Register(email, pwd, fromUserId string) (bool, string)
|
||||
func (this *AuthService) register(user info.User) (bool, string) {
|
||||
if userService.AddUser(user) {
|
||||
// 添加笔记本, 生活, 学习, 工作
|
||||
userId := user.UserId.Hex();
|
||||
notebook := info.Notebook{
|
||||
Seq: -1,
|
||||
UserId: user.UserId}
|
||||
@ -62,8 +63,6 @@ func (this *AuthService) register(user info.User) (bool, string) {
|
||||
notebookService.AddNotebook(notebook);
|
||||
}
|
||||
|
||||
email := user.Email
|
||||
|
||||
// 添加leanote -> 该用户的共享
|
||||
registerSharedUserId := configService.GetGlobalStringConfig("registerSharedUserId")
|
||||
if(registerSharedUserId != "") {
|
||||
@ -74,20 +73,23 @@ func (this *AuthService) register(user info.User) (bool, string) {
|
||||
// 添加共享笔记本
|
||||
for _, notebook := range registerSharedNotebooks {
|
||||
perm, _ := strconv.Atoi(notebook["perm"])
|
||||
shareService.AddShareNotebook(notebook["notebookId"], perm, registerSharedUserId, email);
|
||||
shareService.AddShareNotebookToUserId(notebook["notebookId"], perm, registerSharedUserId, userId);
|
||||
}
|
||||
|
||||
// 添加共享笔记
|
||||
for _, note := range registerSharedNotes {
|
||||
perm, _ := strconv.Atoi(note["perm"])
|
||||
shareService.AddShareNote(note["noteId"], perm, registerSharedUserId, email);
|
||||
shareService.AddShareNoteToUserId(note["noteId"], perm, registerSharedUserId, userId);
|
||||
}
|
||||
|
||||
// 复制笔记
|
||||
for _, noteId := range registerCopyNoteIds {
|
||||
note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex());
|
||||
noteUpdate := bson.M{"IsBlog": true}
|
||||
noteService.UpdateNote(user.UserId.Hex(), user.UserId.Hex(), note.NoteId.Hex(), noteUpdate)
|
||||
// Log(noteId)
|
||||
// Log("Copy")
|
||||
// LogJ(note)
|
||||
noteUpdate := bson.M{"IsBlog": false} // 不要是博客
|
||||
noteService.UpdateNote(user.UserId.Hex(), note.NoteId.Hex(), noteUpdate, -1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +97,7 @@ func (this *AuthService) register(user info.User) (bool, string) {
|
||||
// 添加一条userBlog
|
||||
blogService.UpdateUserBlog(info.UserBlog{UserId: user.UserId,
|
||||
Title: user.Username + " 's Blog",
|
||||
SubTitle: "love leanote!",
|
||||
SubTitle: "Love Leanote!",
|
||||
AboutMe: "Hello, I am (^_^)",
|
||||
CanComment: true,
|
||||
})
|
||||
|
@ -69,9 +69,14 @@ func (this *BlogService) GetBlogItem(note info.Note) (blog info.BlogItem) {
|
||||
}
|
||||
|
||||
// 得到用户共享的notebooks
|
||||
// 3/19 博客不是deleted
|
||||
func (this *BlogService) ListBlogNotebooks(userId string) []info.Notebook {
|
||||
notebooks := []info.Notebook{}
|
||||
db.ListByQ(db.Notebooks, bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true}, ¬ebooks)
|
||||
orQ := []bson.M{
|
||||
bson.M{"IsDeleted": false},
|
||||
bson.M{"IsDeleted": bson.M{"$exists": false}},
|
||||
}
|
||||
db.ListByQ(db.Notebooks, bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "$or": orQ}, ¬ebooks)
|
||||
return notebooks
|
||||
}
|
||||
|
||||
@ -1094,13 +1099,15 @@ func (this *BlogService) SortSingles(userId string, singleIds []string) (ok bool
|
||||
|
||||
// 得到用户的博客url
|
||||
func (this *BlogService) GetUserBlogUrl(userBlog *info.UserBlog, username string) string {
|
||||
if userBlog.Domain != "" && configService.AllowCustomDomain() {
|
||||
return configService.GetUserUrl(userBlog.Domain)
|
||||
} else if userBlog.SubDomain != "" {
|
||||
return configService.GetUserSubUrl(userBlog.SubDomain)
|
||||
}
|
||||
if username == "" {
|
||||
username = userBlog.UserId.Hex()
|
||||
if userBlog != nil {
|
||||
if userBlog.Domain != "" && configService.AllowCustomDomain() {
|
||||
return configService.GetUserUrl(userBlog.Domain)
|
||||
} else if userBlog.SubDomain != "" {
|
||||
return configService.GetUserSubUrl(userBlog.SubDomain)
|
||||
}
|
||||
if username == "" {
|
||||
username = userBlog.UserId.Hex()
|
||||
}
|
||||
}
|
||||
return configService.GetBlogUrl() + "/" + username
|
||||
}
|
||||
|
@ -580,5 +580,5 @@ func (this *ConfigService) HomePageIsAdminsBlog() bool {
|
||||
}
|
||||
|
||||
func (this *ConfigService) GetVersion() string {
|
||||
return "1.0-beta2"
|
||||
return "1.0-beta.4"
|
||||
}
|
||||
|
@ -243,5 +243,9 @@ func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, strin
|
||||
|
||||
// 是否是我的文件
|
||||
func (this *FileService) IsMyFile(userId, fileId string) bool {
|
||||
// 如果有问题会panic
|
||||
if !bson.IsObjectIdHex(fileId) || !bson.IsObjectIdHex(userId) {
|
||||
return false;
|
||||
}
|
||||
return db.Has(db.Files, bson.M{"UserId": bson.ObjectIdHex(userId), "_id": bson.ObjectIdHex(fileId)})
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ func (this *NoteImageService) GetNoteIds(imageId string) ([]bson.ObjectId) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO 这个web可以用, 但api会传来, 不用用了
|
||||
// 解析内容中的图片, 建立图片与note的关系
|
||||
// <img src="/file/outputImage?fileId=12323232" />
|
||||
// 图片必须是我的, 不然不添加
|
||||
@ -38,20 +39,21 @@ func (this *NoteImageService) UpdateNoteImages(userId, noteId, imgSrc, content s
|
||||
if imgSrc != "" {
|
||||
content = "<img src=\"" + imgSrc + "\" >" + content
|
||||
}
|
||||
reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})")
|
||||
// life 添加getImage
|
||||
reg, _ := regexp.Compile("(outputImage|getImage)\\?fileId=([a-z0-9A-Z]{24})")
|
||||
find := reg.FindAllStringSubmatch(content, -1) // 查找所有的
|
||||
|
||||
|
||||
// 删除旧的
|
||||
db.DeleteAll(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(noteId)})
|
||||
|
||||
|
||||
// 添加新的
|
||||
var fileId string
|
||||
noteImage := info.NoteImage{NoteId: bson.ObjectIdHex(noteId)}
|
||||
hasAdded := make(map[string]bool)
|
||||
if find != nil && len(find) > 0 {
|
||||
for _, each := range find {
|
||||
if each != nil && len(each) == 2 {
|
||||
fileId = each[1]
|
||||
if each != nil && len(each) == 3 {
|
||||
fileId = each[2] // 现在有两个子表达式了
|
||||
// 之前没能添加过的
|
||||
if _, ok := hasAdded[fileId]; !ok {
|
||||
Log(fileId)
|
||||
@ -105,3 +107,48 @@ func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId,
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
//
|
||||
func (this *NoteImageService) getImagesByNoteIds(noteIds []bson.ObjectId) map[string][]info.File {
|
||||
noteNoteImages := []info.NoteImage{}
|
||||
db.ListByQ(db.NoteImages, bson.M{"NoteId": bson.M{"$in": noteIds}}, ¬eNoteImages)
|
||||
|
||||
// 得到imageId, 再去files表查所有的Files
|
||||
imageIds := []bson.ObjectId{}
|
||||
|
||||
// 图片1 => N notes
|
||||
imageIdNotes := map[string][]string{} // imageId => [noteId1, noteId2, ...]
|
||||
for _, noteImage := range noteNoteImages {
|
||||
imageId := noteImage.ImageId
|
||||
imageIds = append(imageIds, imageId)
|
||||
|
||||
imageIdHex := imageId.Hex()
|
||||
noteId := noteImage.NoteId.Hex()
|
||||
if notes, ok := imageIdNotes[imageIdHex]; ok {
|
||||
imageIdNotes[imageIdHex] = append(notes, noteId)
|
||||
} else {
|
||||
imageIdNotes[imageIdHex] = []string{noteId}
|
||||
}
|
||||
}
|
||||
|
||||
// 得到所有files
|
||||
files := []info.File{}
|
||||
db.ListByQ(db.Files, bson.M{"_id": bson.M{"$in": imageIds}}, &files)
|
||||
|
||||
// 建立note->file关联
|
||||
noteImages := make(map[string][]info.File)
|
||||
for _, file := range files {
|
||||
fileIdHex := file.FileId.Hex() // == imageId
|
||||
// 这个fileIdHex有哪些notes呢?
|
||||
if notes, ok := imageIdNotes[fileIdHex]; ok {
|
||||
for _, noteId := range notes {
|
||||
if files, ok2 := noteImages[noteId]; ok2 {
|
||||
noteImages[noteId] = append(files, file)
|
||||
} else {
|
||||
noteImages[noteId] = []info.File{file}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noteImages
|
||||
}
|
||||
|
@ -44,14 +44,115 @@ func (this *NoteService) GetNoteAndContent(noteId, userId string) (noteAndConten
|
||||
return info.NoteAndContent{note, noteContent}
|
||||
}
|
||||
|
||||
// 获取同步的笔记
|
||||
// > afterUsn的笔记
|
||||
func (this *NoteService) GetSyncNotes(userId string, afterUsn, maxEntry int) []info.ApiNote {
|
||||
notes := []info.Note{}
|
||||
q := db.Notes.Find(bson.M{
|
||||
"UserId": bson.ObjectIdHex(userId),
|
||||
"Usn": bson.M{"$gt": afterUsn},
|
||||
});
|
||||
q.Sort("Usn").Limit(maxEntry).All(¬es)
|
||||
|
||||
return this.ToApiNotes(notes)
|
||||
}
|
||||
|
||||
// note与apiNote的转换
|
||||
func (this *NoteService) ToApiNotes(notes []info.Note) []info.ApiNote {
|
||||
// 2, 得到所有图片, 附件信息
|
||||
// 查images表, attachs表
|
||||
if len(notes) > 0 {
|
||||
noteIds := make([]bson.ObjectId, len(notes));
|
||||
for i, note := range notes {
|
||||
noteIds[i] = note.NoteId
|
||||
}
|
||||
noteFilesMap := this.getFiles(noteIds)
|
||||
// 生成info.ApiNote
|
||||
apiNotes := make([]info.ApiNote, len(notes))
|
||||
for i, note := range notes {
|
||||
noteId := note.NoteId.Hex()
|
||||
apiNotes[i] = this.ToApiNote(¬e, noteFilesMap[noteId])
|
||||
}
|
||||
return apiNotes
|
||||
}
|
||||
// 返回空的
|
||||
return []info.ApiNote{}
|
||||
}
|
||||
|
||||
|
||||
// note与apiNote的转换
|
||||
func (this *NoteService) ToApiNote(note *info.Note, files []info.NoteFile) info.ApiNote {
|
||||
apiNote := info.ApiNote{
|
||||
NoteId: note.NoteId.Hex(),
|
||||
NotebookId: note.NotebookId.Hex(),
|
||||
UserId : note.UserId.Hex(),
|
||||
Title : note.Title,
|
||||
Tags : note.Tags,
|
||||
IsMarkdown : note.IsMarkdown,
|
||||
IsBlog : note.IsBlog,
|
||||
IsTrash : note.IsTrash,
|
||||
IsDeleted : note.IsDeleted,
|
||||
Usn : note.Usn,
|
||||
CreatedTime : note.CreatedTime,
|
||||
UpdatedTime : note.UpdatedTime,
|
||||
PublicTime : note.PublicTime,
|
||||
Files: files,
|
||||
}
|
||||
return apiNote
|
||||
}
|
||||
|
||||
// getDirtyNotes, 把note的图片, 附件信息都发送给客户端
|
||||
// 客户端保存到本地, 再获取图片, 附件
|
||||
|
||||
// 得到所有图片, 附件信息
|
||||
// 查images表, attachs表
|
||||
// [待测]
|
||||
func (this *NoteService) getFiles(noteIds []bson.ObjectId) map[string][]info.NoteFile {
|
||||
noteImages := noteImageService.getImagesByNoteIds(noteIds);
|
||||
noteAttachs := attachService.getAttachsByNoteIds(noteIds)
|
||||
|
||||
noteFilesMap := map[string][]info.NoteFile{}
|
||||
|
||||
for _, noteId := range noteIds {
|
||||
noteIdHex := noteId.Hex()
|
||||
noteFiles := []info.NoteFile{}
|
||||
// images
|
||||
if images, ok := noteImages[noteIdHex]; ok {
|
||||
for _, image := range images {
|
||||
noteFiles = append(noteFiles, info.NoteFile{
|
||||
FileId: image.FileId.Hex(),
|
||||
Type: image.Type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// attach
|
||||
if attachs, ok := noteAttachs[noteIdHex]; ok {
|
||||
for _, attach := range attachs {
|
||||
noteFiles = append(noteFiles, info.NoteFile{
|
||||
FileId: attach.AttachId.Hex(),
|
||||
Type: attach.Type,
|
||||
Title: attach.Title,
|
||||
IsAttach: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
noteFilesMap[noteIdHex] = noteFiles
|
||||
}
|
||||
|
||||
return noteFilesMap
|
||||
}
|
||||
|
||||
// 列出note, 排序规则, 还有分页
|
||||
// CreatedTime, UpdatedTime, title 来排序
|
||||
func (this *NoteService) ListNotes(userId, notebookId string,
|
||||
isTrash bool, pageNumber, pageSize int, sortField string, isAsc bool, isBlog bool) (count int, notes []info.Note) {
|
||||
notes = []info.Note{}
|
||||
skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, sortField, isAsc)
|
||||
|
||||
// 不是trash的
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsTrash": isTrash}
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsTrash": isTrash, "IsDeleted": false}
|
||||
if isBlog {
|
||||
query["IsBlog"] = true
|
||||
}
|
||||
@ -121,7 +222,7 @@ func (this *NoteService) ListNoteContentByNoteIds(noteIds []bson.ObjectId) (note
|
||||
// 首先要判断Notebook是否是Blog, 是的话设为blog
|
||||
// [ok]
|
||||
|
||||
func (this *NoteService) AddNote(note info.Note) info.Note {
|
||||
func (this *NoteService) AddNote(note info.Note, fromApi bool) info.Note {
|
||||
if(note.NoteId.Hex() == "") {
|
||||
noteId := bson.NewObjectId();
|
||||
note.NoteId = noteId;
|
||||
@ -131,14 +232,19 @@ func (this *NoteService) AddNote(note info.Note) info.Note {
|
||||
note.IsTrash = false
|
||||
note.UpdatedUserId = note.UserId
|
||||
note.UrlTitle = GetUrTitle(note.UserId.Hex(), note.Title, "note")
|
||||
note.Usn = userService.IncrUsn(note.UserId.Hex())
|
||||
|
||||
// 设为blog
|
||||
notebookId := note.NotebookId.Hex()
|
||||
note.IsBlog = notebookService.IsBlog(notebookId)
|
||||
|
||||
if note.IsBlog {
|
||||
// api会传IsBlog, web不会传
|
||||
if !fromApi {
|
||||
note.PublicTime = note.UpdatedTime
|
||||
// 设为blog
|
||||
note.IsBlog = notebookService.IsBlog(notebookId)
|
||||
}
|
||||
// if note.IsBlog {
|
||||
note.PublicTime = note.UpdatedTime
|
||||
// }
|
||||
|
||||
db.Insert(db.Notes, note)
|
||||
|
||||
@ -156,7 +262,7 @@ func (this *NoteService) AddSharedNote(note info.Note, myUserId bson.ObjectId) i
|
||||
// 判断我是否有权限添加
|
||||
if shareService.HasUpdateNotebookPerm(note.UserId.Hex(), myUserId.Hex(), note.NotebookId.Hex()) {
|
||||
note.CreatedUserId = myUserId // 是我给共享我的人创建的
|
||||
return this.AddNote(note)
|
||||
return this.AddNote(note, false)
|
||||
}
|
||||
return info.Note{}
|
||||
}
|
||||
@ -176,6 +282,49 @@ func (this *NoteService) AddNoteContent(noteContent info.NoteContent) info.NoteC
|
||||
return noteContent;
|
||||
}
|
||||
|
||||
// API, abstract, desc需要这里获取
|
||||
// 不需要
|
||||
/*
|
||||
func (this *NoteService) AddNoteAndContentApi(note info.Note, noteContent info.NoteContent, myUserId bson.ObjectId) info.Note {
|
||||
if(note.NoteId.Hex() == "") {
|
||||
noteId := bson.NewObjectId();
|
||||
note.NoteId = noteId;
|
||||
}
|
||||
note.CreatedTime = time.Now()
|
||||
note.UpdatedTime = note.CreatedTime
|
||||
note.IsTrash = false
|
||||
note.UpdatedUserId = note.UserId
|
||||
note.UrlTitle = GetUrTitle(note.UserId.Hex(), note.Title, "note")
|
||||
note.Usn = userService.IncrUsn(note.UserId.Hex())
|
||||
|
||||
// desc这里获取
|
||||
desc := SubStringHTMLToRaw(noteContent.Content, 50)
|
||||
note.Desc = desc;
|
||||
|
||||
// 设为blog
|
||||
notebookId := note.NotebookId.Hex()
|
||||
note.IsBlog = notebookService.IsBlog(notebookId)
|
||||
|
||||
if note.IsBlog {
|
||||
note.PublicTime = note.UpdatedTime
|
||||
}
|
||||
|
||||
db.Insert(db.Notes, note)
|
||||
|
||||
// tag1, 不需要了
|
||||
// tagService.AddTags(note.UserId.Hex(), note.Tags)
|
||||
|
||||
// recount notebooks' notes number
|
||||
notebookService.ReCountNotebookNumberNotes(notebookId)
|
||||
|
||||
// 这里, 添加到内容中
|
||||
abstract := SubStringHTML(noteContent.Content, 200, "")
|
||||
noteContent.Abstract = abstract
|
||||
this.AddNoteContent(noteContent)
|
||||
|
||||
return note
|
||||
}*/
|
||||
|
||||
// 添加笔记和内容
|
||||
// 这里使用 info.NoteAndContent 接收?
|
||||
func (this *NoteService) AddNoteAndContentForController(note info.Note, noteContent info.NoteContent, updatedUserId string) info.Note {
|
||||
@ -198,7 +347,24 @@ func (this *NoteService) AddNoteAndContent(note info.Note, noteContent info.Note
|
||||
if note.UserId != myUserId {
|
||||
note = this.AddSharedNote(note, myUserId)
|
||||
} else {
|
||||
note = this.AddNote(note)
|
||||
note = this.AddNote(note, false)
|
||||
}
|
||||
if note.NoteId != "" {
|
||||
this.AddNoteContent(noteContent)
|
||||
}
|
||||
return note
|
||||
}
|
||||
|
||||
func (this *NoteService) AddNoteAndContentApi(note info.Note, noteContent info.NoteContent, myUserId bson.ObjectId) info.Note {
|
||||
if(note.NoteId.Hex() == "") {
|
||||
noteId := bson.NewObjectId()
|
||||
note.NoteId = noteId
|
||||
}
|
||||
noteContent.NoteId = note.NoteId
|
||||
if note.UserId != myUserId {
|
||||
note = this.AddSharedNote(note, myUserId)
|
||||
} else {
|
||||
note = this.AddNote(note, true)
|
||||
}
|
||||
if note.NoteId != "" {
|
||||
this.AddNoteContent(noteContent)
|
||||
@ -207,19 +373,30 @@ func (this *NoteService) AddNoteAndContent(note info.Note, noteContent info.Note
|
||||
}
|
||||
|
||||
// 修改笔记
|
||||
func (this *NoteService) UpdateNote(userId, updatedUserId, noteId string, needUpdate bson.M) bool {
|
||||
// 这里没有判断usn
|
||||
func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bson.M, usn int) (bool, string, int) {
|
||||
// 是否存在
|
||||
note := this.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
return false, "notExists", 0
|
||||
}
|
||||
|
||||
userId := note.UserId.Hex()
|
||||
// updatedUserId 要修改userId的note, 此时需要判断是否有修改权限
|
||||
if userId != updatedUserId {
|
||||
if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) {
|
||||
Log("NO AUTH2")
|
||||
return false
|
||||
return false, "noAuth", 0
|
||||
} else {
|
||||
Log("HAS AUTH -----------")
|
||||
}
|
||||
}
|
||||
|
||||
if usn > 0 && note.Usn != usn {
|
||||
return false, "conflict", 0
|
||||
}
|
||||
|
||||
// 是否已自定义
|
||||
note := this.GetNoteById(noteId)
|
||||
if note.IsBlog && note.HasSelfDefined {
|
||||
delete(needUpdate, "ImgSrc")
|
||||
delete(needUpdate, "Desc")
|
||||
@ -227,8 +404,11 @@ func (this *NoteService) UpdateNote(userId, updatedUserId, noteId string, needUp
|
||||
|
||||
needUpdate["UpdatedUserId"] = bson.ObjectIdHex(updatedUserId);
|
||||
needUpdate["UpdatedTime"] = time.Now();
|
||||
afterUsn := userService.IncrUsn(userId);
|
||||
needUpdate["Usn"] = afterUsn
|
||||
|
||||
// 添加tag2
|
||||
// TODO 这个tag去掉, 添加tag另外添加, 不要这个
|
||||
if tags, ok := needUpdate["Tags"]; ok {
|
||||
tagService.AddTagsI(userId, tags)
|
||||
}
|
||||
@ -236,10 +416,55 @@ func (this *NoteService) UpdateNote(userId, updatedUserId, noteId string, needUp
|
||||
// 是否修改了isBlog
|
||||
// 也要修改noteContents的IsBlog
|
||||
if isBlog, ok := needUpdate["IsBlog"]; ok {
|
||||
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog})
|
||||
isBlog2 := isBlog.(bool)
|
||||
if note.IsBlog != isBlog2 {
|
||||
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog2})
|
||||
|
||||
// 重新发布成博客
|
||||
if !note.IsBlog {
|
||||
needUpdate["PublicTime"] = needUpdate["UpdatedTime"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, needUpdate)
|
||||
ok := db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, needUpdate)
|
||||
if !ok {
|
||||
return ok, "", 0
|
||||
}
|
||||
|
||||
// 重新获取之
|
||||
note = this.GetNoteById(noteId)
|
||||
|
||||
hasRecount := false
|
||||
|
||||
// 如果修改了notebookId, 则更新notebookId'count
|
||||
// 两方的notebook也要修改
|
||||
notebookIdI := needUpdate["NotebookId"]
|
||||
if notebookIdI != nil {
|
||||
notebookId := notebookIdI.(bson.ObjectId)
|
||||
if notebookId != "" {
|
||||
notebookService.ReCountNotebookNumberNotes(note.NotebookId.Hex())
|
||||
hasRecount = true
|
||||
notebookService.ReCountNotebookNumberNotes(notebookId.Hex())
|
||||
}
|
||||
}
|
||||
|
||||
// 不要多次更新, isTrash = false, = true都要重新统计
|
||||
if !hasRecount {
|
||||
if _, ok := needUpdate["IsTrash"]; ok {
|
||||
notebookService.ReCountNotebookNumberNotes(note.NotebookId.Hex())
|
||||
}
|
||||
}
|
||||
|
||||
return true, "", afterUsn
|
||||
}
|
||||
|
||||
// 附件修改, 增加noteIncr
|
||||
func (this *NoteService) IncrNoteUsn(noteId, userId string) int {
|
||||
afterUsn := userService.IncrUsn(userId)
|
||||
db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
|
||||
bson.M{"UpdatedTime": time.Now(), "Usn": afterUsn})
|
||||
return afterUsn
|
||||
}
|
||||
|
||||
// 这里要判断权限, 如果userId != updatedUserId, 那么需要判断权限
|
||||
@ -254,31 +479,50 @@ func (this *NoteService) UpdateNoteTitle(userId, updatedUserId, noteId, title st
|
||||
}
|
||||
|
||||
return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
|
||||
bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId), "Title": title, "UpdatedTime": time.Now()})
|
||||
bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId), "Title": title, "UpdatedTime": time.Now(), "Usn": userService.IncrUsn(userId)})
|
||||
}
|
||||
|
||||
// 修改笔记本内容
|
||||
// [ok] TODO perm未测
|
||||
func (this *NoteService) UpdateNoteContent(userId, updatedUserId, noteId, content, abstract string) bool {
|
||||
// hasBeforeUpdateNote 之前是否更新过note其它信息, 如果有更新, usn不用更新
|
||||
// TODO abstract这里生成
|
||||
func (this *NoteService) UpdateNoteContent(updatedUserId, noteId, content, abstract string, hasBeforeUpdateNote bool, usn int) (bool, string, int) {
|
||||
// 是否已自定义
|
||||
note := this.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
return false, "notExists", 0
|
||||
}
|
||||
userId := note.UserId.Hex()
|
||||
// updatedUserId 要修改userId的note, 此时需要判断是否有修改权限
|
||||
if userId != updatedUserId {
|
||||
if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) {
|
||||
Log("NO AUTH")
|
||||
return false
|
||||
return false, "noAuth", 0
|
||||
}
|
||||
}
|
||||
|
||||
// abstract重置
|
||||
data := bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId),
|
||||
"Content": content,
|
||||
"Abstract": abstract,
|
||||
"UpdatedTime": time.Now()}
|
||||
|
||||
// 是否已自定义
|
||||
note := this.GetNoteById(noteId)
|
||||
if note.IsBlog && note.HasSelfDefined {
|
||||
delete(data, "Abstract")
|
||||
}
|
||||
|
||||
// usn, 修改笔记不可能单独修改内容
|
||||
afterUsn := 0
|
||||
// 如果之前没有修改note其它信息, 那么usn++
|
||||
if !hasBeforeUpdateNote {
|
||||
// 需要验证
|
||||
if usn >= 0 && note.Usn != usn {
|
||||
return false, "conflict", 0
|
||||
}
|
||||
afterUsn = userService.IncrUsn(userId)
|
||||
db.UpdateByIdAndUserIdField(db.Notes, noteId, userId, "Usn", usn)
|
||||
}
|
||||
|
||||
if db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, data) {
|
||||
// 这里, 添加历史记录
|
||||
noteContentHistoryService.AddHistory(noteId, userId, info.EachHistory{UpdatedUserId: bson.ObjectIdHex(updatedUserId),
|
||||
@ -289,9 +533,9 @@ func (this *NoteService) UpdateNoteContent(userId, updatedUserId, noteId, conten
|
||||
// 更新笔记图片
|
||||
noteImageService.UpdateNoteImages(userId, noteId, note.ImgSrc, content)
|
||||
|
||||
return true
|
||||
return true, "", afterUsn
|
||||
}
|
||||
return false
|
||||
return false, "", 0
|
||||
}
|
||||
|
||||
// ?????
|
||||
@ -305,7 +549,7 @@ func (this *NoteService) updateNoteImages(noteId string, content string) bool {
|
||||
// 更新tags
|
||||
// [ok] [del]
|
||||
func (this *NoteService) UpdateTags(noteId string, userId string, tags []string) bool {
|
||||
return db.UpdateByIdAndUserIdField(db.Notes, noteId, userId, "Tags", tags)
|
||||
return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, bson.M{"Tags": tags, "Usn": userService.IncrUsn(userId)})
|
||||
}
|
||||
|
||||
func (this *NoteService) ToBlog(userId, noteId string, isBlog, isTop bool) bool {
|
||||
@ -323,6 +567,8 @@ func (this *NoteService) ToBlog(userId, noteId string, isBlog, isTop bool) bool
|
||||
} else {
|
||||
noteUpdate["HasSelfDefined"] = false
|
||||
}
|
||||
noteUpdate["Usn"] = userService.IncrUsn(userId)
|
||||
|
||||
ok := db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, noteUpdate)
|
||||
// 重新计算tags
|
||||
go (func() {
|
||||
@ -342,7 +588,9 @@ func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
|
||||
|
||||
re := db.UpdateByIdAndUserId(db.Notes, noteId, userId,
|
||||
bson.M{"$set": bson.M{"IsTrash": false,
|
||||
"NotebookId": bson.ObjectIdHex(notebookId)}})
|
||||
"NotebookId": bson.ObjectIdHex(notebookId),
|
||||
"Usn": userService.IncrUsn(userId),
|
||||
}})
|
||||
|
||||
if re {
|
||||
// 更新blog状态
|
||||
@ -364,13 +612,14 @@ func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
|
||||
// 如果自己的blog状态是true, 不用改变,
|
||||
// 否则, 如果notebookId的blog是true, 则改为true之
|
||||
// 返回blog状态
|
||||
// move, copy时用
|
||||
func (this *NoteService) updateToNotebookBlog(noteId, notebookId, userId string) bool {
|
||||
if this.IsBlog(noteId) {
|
||||
return true
|
||||
}
|
||||
if notebookService.IsBlog(notebookId) {
|
||||
db.UpdateByIdAndUserId(db.Notes, noteId, userId,
|
||||
bson.M{"$set": bson.M{"IsBlog": true}})
|
||||
bson.M{"$set": bson.M{"IsBlog": true, "PublicTime": time.Now()}}) // life
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -567,7 +816,6 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
//------------
|
||||
// 统计
|
||||
func (this *NoteService) CountNote(userId string) int {
|
||||
@ -583,4 +831,43 @@ func (this *NoteService) CountBlog(userId string) int {
|
||||
q["UserId"] = bson.ObjectIdHex(userId)
|
||||
}
|
||||
return db.Count(db.Notes, q)
|
||||
}
|
||||
}
|
||||
|
||||
// 通过标签来查询
|
||||
func (this *NoteService) CountNoteByTag(userId string, tag string) int {
|
||||
if tag == "" {
|
||||
return 0
|
||||
}
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId),
|
||||
// "IsTrash": false,
|
||||
"IsDeleted": false,
|
||||
"Tags": bson.M{"$in": []string{tag}}}
|
||||
return db.Count(db.Notes, query)
|
||||
}
|
||||
|
||||
// 删除tag
|
||||
// 返回所有note的Usn
|
||||
func (this *NoteService) UpdateNoteToDeleteTag(userId string, targetTag string) map[string]int {
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId),
|
||||
"Tags": bson.M{"$in": []string{targetTag}}}
|
||||
notes := []info.Note{}
|
||||
db.ListByQ(db.Notes, query, ¬es)
|
||||
ret := map[string]int{}
|
||||
for _, note := range notes {
|
||||
tags := note.Tags
|
||||
if tags == nil {
|
||||
continue
|
||||
}
|
||||
for i, tag := range tags {
|
||||
if tag == targetTag {
|
||||
tags = tags
|
||||
tags = append(tags[:i], tags[i+1:]...)
|
||||
break;
|
||||
}
|
||||
}
|
||||
usn := userService.IncrUsn(userId)
|
||||
db.UpdateByQMap(db.Notes, bson.M{"_id": note.NoteId}, bson.M{"Usn": usn, "Tags": tags})
|
||||
ret[note.NoteId.Hex()] = usn
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
@ -111,12 +111,24 @@ func (this *NotebookService) GetNotebookByUserIdAndUrlTitle(userId, notebookIdOr
|
||||
return notebook
|
||||
}
|
||||
|
||||
// 同步的方法
|
||||
func (this *NotebookService) GeSyncNotebooks(userId string, afterUsn, maxEntry int) ([]info.Notebook) {
|
||||
notebooks := []info.Notebook{}
|
||||
q := db.Notebooks.Find(bson.M{"UserId": bson.ObjectIdHex(userId), "Usn": bson.M{"$gt": afterUsn}});
|
||||
q.Sort("Usn").Limit(maxEntry).All(¬ebooks)
|
||||
return notebooks
|
||||
}
|
||||
|
||||
// 得到用户下所有的notebook
|
||||
// 排序好之后返回
|
||||
// [ok]
|
||||
func (this *NotebookService) GetNotebooks(userId string) info.SubNotebooks {
|
||||
userNotebooks := []info.Notebook{}
|
||||
db.Notebooks.Find(bson.M{"UserId": bson.ObjectIdHex(userId)}).All(&userNotebooks)
|
||||
orQ := []bson.M{
|
||||
bson.M{"IsDeleted": false},
|
||||
bson.M{"IsDeleted": bson.M{"$exists": false}},
|
||||
}
|
||||
db.Notebooks.Find(bson.M{"UserId": bson.ObjectIdHex(userId), "$or": orQ}).All(&userNotebooks)
|
||||
|
||||
if len(userNotebooks) == 0 {
|
||||
return nil
|
||||
@ -141,14 +153,46 @@ func (this *NotebookService) GetNotebooksByNotebookIds(notebookIds []bson.Object
|
||||
|
||||
// 添加
|
||||
// [ok]
|
||||
func (this *NotebookService) AddNotebook(notebook info.Notebook) bool {
|
||||
func (this *NotebookService) AddNotebook(notebook info.Notebook) (bool, info.Notebook) {
|
||||
notebook.UrlTitle = GetUrTitle(notebook.UserId.Hex(), notebook.Title, "notebook")
|
||||
notebook.Usn = userService.IncrUsn(notebook.UserId.Hex())
|
||||
now := time.Now()
|
||||
notebook.CreatedTime = now
|
||||
notebook.UpdatedTime = now
|
||||
err := db.Notebooks.Insert(notebook)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return false, notebook
|
||||
}
|
||||
return true
|
||||
return true, notebook
|
||||
}
|
||||
// 更新笔记, api
|
||||
func (this *NotebookService) UpdateNotebookApi(userId, notebookId, title, parentNotebookId string, seq, usn int) (bool, string, info.Notebook) {
|
||||
if notebookId == "" {
|
||||
return false, "notebookIdNotExists", info.Notebook{}
|
||||
}
|
||||
|
||||
// 先判断usn是否和数据库的一样, 如果不一样, 则冲突, 不保存
|
||||
notebook := this.GetNotebookById(notebookId)
|
||||
// 不存在
|
||||
if notebook.NotebookId == "" {
|
||||
return false, "notExists", notebook
|
||||
} else if notebook.Usn != usn {
|
||||
return false, "conflict", notebook
|
||||
}
|
||||
notebook.Usn = userService.IncrUsn(userId);
|
||||
notebook.Title = title;
|
||||
|
||||
updates := bson.M{"Title": title, "Usn": notebook.Usn, "Seq": seq, "UpdatedTime": time.Now()};
|
||||
if(parentNotebookId != "" && bson.IsObjectIdHex(parentNotebookId)) {
|
||||
updates["ParentNotebookId"] = bson.ObjectIdHex(parentNotebookId);
|
||||
} else {
|
||||
updates["ParentNotebookId"] = "";
|
||||
}
|
||||
ok := db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, updates);
|
||||
if ok {
|
||||
return ok, "", this.GetNotebookById(notebookId)
|
||||
}
|
||||
return false, "", notebook
|
||||
}
|
||||
|
||||
// 判断是否是blog
|
||||
@ -174,19 +218,22 @@ func (this *NotebookService) UpdateNotebook(notebook info.Notebook) bool {
|
||||
// 更新笔记本标题
|
||||
// [ok]
|
||||
func (this *NotebookService) UpdateNotebookTitle(notebookId, userId, title string) bool {
|
||||
return db.UpdateByIdAndUserIdField(db.Notebooks, notebookId, userId, "Title", title)
|
||||
usn := userService.IncrUsn(userId)
|
||||
return db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, bson.M{"Title": title, "Usn": usn})
|
||||
}
|
||||
|
||||
// 更新notebook
|
||||
func (this *NotebookService) UpdateNotebook(userId, notebookId string, needUpdate bson.M) bool {
|
||||
needUpdate["UpdatedTime"] = time.Now();
|
||||
needUpdate["Usn"] = userService.IncrUsn(userId)
|
||||
return db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, needUpdate)
|
||||
}
|
||||
|
||||
// ToBlog or Not
|
||||
func (this *NotebookService) ToBlog(userId, notebookId string, isBlog bool) (bool) {
|
||||
updates := bson.M{"IsBlog": isBlog, "Usn": userService.IncrUsn(userId)}
|
||||
// 笔记本
|
||||
db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, bson.M{"IsBlog": isBlog})
|
||||
db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, updates)
|
||||
|
||||
// 更新笔记
|
||||
q := bson.M{"UserId": bson.ObjectIdHex(userId),
|
||||
@ -197,6 +244,8 @@ func (this *NotebookService) ToBlog(userId, notebookId string, isBlog bool) (boo
|
||||
} else {
|
||||
data["HasSelfDefined"] = false
|
||||
}
|
||||
// usn
|
||||
data["Usn"] = userService.IncrUsn(userId)
|
||||
db.UpdateByQMap(db.Notes, q, data)
|
||||
|
||||
// noteContents也更新, 这个就麻烦了, noteContents表没有NotebookId
|
||||
@ -227,7 +276,10 @@ func (this *NotebookService) DeleteNotebook(userId, notebookId string) (bool, st
|
||||
if db.Count(db.Notes, bson.M{"NotebookId": bson.ObjectIdHex(notebookId),
|
||||
"UserId": bson.ObjectIdHex(userId),
|
||||
"IsTrash": false}) == 0 { // 不包含trash
|
||||
return db.DeleteByIdAndUserId(db.Notebooks, notebookId, userId), ""
|
||||
// 不是真删除 1/20, 为了同步笔记本
|
||||
ok := db.UpdateByQMap(db.Notebooks, bson.M{"_id": bson.ObjectIdHex(notebookId)}, bson.M{"IsDeleted": true, "Usn": userService.IncrUsn(userId)})
|
||||
return ok, ""
|
||||
// return db.DeleteByIdAndUserId(db.Notebooks, notebookId, userId), ""
|
||||
}
|
||||
return false, "笔记本下有笔记"
|
||||
} else {
|
||||
@ -235,6 +287,18 @@ func (this *NotebookService) DeleteNotebook(userId, notebookId string) (bool, st
|
||||
}
|
||||
}
|
||||
|
||||
// API调用, 删除笔记本, 不作笔记控制
|
||||
func (this *NotebookService) DeleteNotebookForce(userId, notebookId string, usn int) (bool, string) {
|
||||
notebook := this.GetNotebookById(notebookId)
|
||||
// 不存在
|
||||
if notebook.NotebookId == "" {
|
||||
return false, "notExists"
|
||||
} else if notebook.Usn != usn {
|
||||
return false, "conflict"
|
||||
}
|
||||
return db.DeleteByIdAndUserId(db.Notebooks, notebookId, userId), ""
|
||||
}
|
||||
|
||||
// 排序
|
||||
// 传入 notebookId => Seq
|
||||
// 为什么要传入userId, 防止修改其它用户的信息 (恶意)
|
||||
@ -245,7 +309,7 @@ func (this *NotebookService) SortNotebooks(userId string, notebookId2Seqs map[st
|
||||
}
|
||||
|
||||
for notebookId, seq := range notebookId2Seqs {
|
||||
if !db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(notebookId), bson.ObjectIdHex(userId), "Seq", seq) {
|
||||
if !db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, bson.M{"Seq": seq, "Usn": userService.IncrUsn(userId)}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -253,15 +317,14 @@ func (this *NotebookService) SortNotebooks(userId string, notebookId2Seqs map[st
|
||||
return true
|
||||
}
|
||||
|
||||
// 排序和设置父
|
||||
func (this *NotebookService) DragNotebooks(userId string, curNotebookId string, parentNotebookId string, siblings []string) bool {
|
||||
userIdO := bson.ObjectIdHex(userId)
|
||||
|
||||
ok := false
|
||||
// 如果没parentNotebookId, 则parentNotebookId设空
|
||||
if(parentNotebookId == "") {
|
||||
ok = db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(curNotebookId), userIdO, "ParentNotebookId", "");
|
||||
ok = db.UpdateByIdAndUserIdMap(db.Notebooks, curNotebookId, userId, bson.M{"ParentNotebookId": "", "Usn": userService.IncrUsn(userId)});
|
||||
} else {
|
||||
ok = db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(curNotebookId), userIdO, "ParentNotebookId", bson.ObjectIdHex(parentNotebookId));
|
||||
ok = db.UpdateByIdAndUserIdMap(db.Notebooks, curNotebookId, userId, bson.M{"ParentNotebookId": bson.ObjectIdHex(parentNotebookId), "Usn": userService.IncrUsn(userId)});
|
||||
}
|
||||
|
||||
if !ok {
|
||||
@ -270,7 +333,7 @@ func (this *NotebookService) DragNotebooks(userId string, curNotebookId string,
|
||||
|
||||
// 排序
|
||||
for seq, notebookId := range siblings {
|
||||
if !db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(notebookId), userIdO, "Seq", seq) {
|
||||
if !db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, bson.M{"Seq": seq, "Usn": userService.IncrUsn(userId)}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -283,7 +346,7 @@ func (this *NotebookService) DragNotebooks(userId string, curNotebookId string,
|
||||
// trashService: DeleteNote (recove不用, 都统一在MoveNote里了)
|
||||
func (this *NotebookService) ReCountNotebookNumberNotes(notebookId string) bool {
|
||||
notebookIdO := bson.ObjectIdHex(notebookId)
|
||||
count := db.Count(db.Notes, bson.M{"NotebookId": notebookIdO, "IsTrash": false})
|
||||
count := db.Count(db.Notes, bson.M{"NotebookId": notebookIdO, "IsTrash": false, "IsDeleted": false})
|
||||
Log(count)
|
||||
Log(notebookId)
|
||||
return db.UpdateByQField(db.Notebooks, bson.M{"_id": notebookIdO}, "NumberNotes", count)
|
||||
|
@ -69,3 +69,20 @@ func (this *SessionService) SetCaptcha(sessionId, captcha string) bool {
|
||||
Log(ok)
|
||||
return ok
|
||||
}
|
||||
|
||||
//-----------
|
||||
// API
|
||||
func (this *SessionService) GetUserId(sessionId string) string {
|
||||
session := this.Get(sessionId)
|
||||
// 更新updateTime, 避免过期
|
||||
db.UpdateByQMap(db.Sessions, bson.M{"SessionId": sessionId},
|
||||
bson.M{"UpdatedTime": time.Now()})
|
||||
return session.UserId
|
||||
}
|
||||
|
||||
// 登录成功后设置userId
|
||||
func (this *SessionService) SetUserId(sessionId, userId string) bool {
|
||||
this.Get(sessionId)
|
||||
ok := this.Update(sessionId, "UserId", userId)
|
||||
return ok
|
||||
}
|
||||
|
@ -294,7 +294,11 @@ func (this *ShareService) AddShareNotebook(notebookId string, perm int, userId,
|
||||
if toUserId == "" {
|
||||
return false, "无该用户", ""
|
||||
}
|
||||
return this.AddShareNotebookToUserId(notebookId, perm, userId, toUserId)
|
||||
}
|
||||
|
||||
// 第三方注册时没有email
|
||||
func (this *ShareService) AddShareNotebookToUserId(notebookId string, perm int, userId, toUserId string) (bool, string, string) {
|
||||
// 添加一条记录说明两者存在关系
|
||||
this.AddHasShareNote(userId, toUserId);
|
||||
|
||||
@ -327,7 +331,11 @@ func (this *ShareService) AddShareNote(noteId string, perm int, userId, email st
|
||||
if toUserId == "" {
|
||||
return false, "无该用户", ""
|
||||
}
|
||||
|
||||
return this.AddShareNoteToUserId(noteId, perm, userId, toUserId)
|
||||
}
|
||||
|
||||
// 第三方测试没有userId
|
||||
func (this *ShareService) AddShareNoteToUserId(noteId string, perm int, userId, toUserId string) (bool, string, string) {
|
||||
// 添加一条记录说明两者存在关系
|
||||
this.AddHasShareNote(userId, toUserId);
|
||||
|
||||
|
@ -3,9 +3,9 @@ package service
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "time"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -14,12 +14,14 @@ import (
|
||||
type TagService struct {
|
||||
}
|
||||
|
||||
/*
|
||||
func (this *TagService) GetTags(userId string) []string {
|
||||
tag := info.Tag{}
|
||||
db.Get(db.Tags, userId, &tag)
|
||||
LogJ(tag)
|
||||
return tag.Tags
|
||||
}
|
||||
*/
|
||||
|
||||
func (this *TagService) AddTagsI(userId string, tags interface{}) bool {
|
||||
if ts, ok2 := tags.([]string); ok2 {
|
||||
@ -36,4 +38,100 @@ func (this *TagService) AddTags(userId string, tags []string) bool {
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// v2
|
||||
// 第二版标签, 单独一张表, 每一个tag一条记录
|
||||
|
||||
// 添加或更新标签, 先查下是否存在, 不存在则添加, 存在则更新
|
||||
// 都要统计下tag的note数
|
||||
// 什么时候调用? 笔记添加Tag, 删除Tag时
|
||||
// 删除note时, 都可以调用
|
||||
// 万能
|
||||
func (this *TagService) AddOrUpdateTag(userId string, tag string) info.NoteTag {
|
||||
userIdO := bson.ObjectIdHex(userId)
|
||||
noteTag := info.NoteTag{}
|
||||
db.GetByQ(db.NoteTags, bson.M{"UserId": userIdO, "Tag": tag}, ¬eTag)
|
||||
|
||||
// 存在, 则更新之
|
||||
if noteTag.TagId != "" {
|
||||
// 统计note数
|
||||
count := noteService.CountNoteByTag(userId, tag)
|
||||
noteTag.Count = count
|
||||
noteTag.UpdatedTime = time.Now()
|
||||
// noteTag.Usn = userService.IncrUsn(userId), 更新count而已
|
||||
db.UpdateByIdAndUserId(db.NoteTags, noteTag.TagId.Hex(), userId, noteTag)
|
||||
return noteTag
|
||||
}
|
||||
|
||||
// 不存在, 则创建之
|
||||
noteTag.TagId = bson.NewObjectId()
|
||||
noteTag.Count = 1
|
||||
noteTag.Tag = tag
|
||||
noteTag.UserId = bson.ObjectIdHex(userId)
|
||||
noteTag.CreatedTime = time.Now()
|
||||
noteTag.UpdatedTime = noteTag.CreatedTime
|
||||
noteTag.Usn = userService.IncrUsn(userId)
|
||||
noteTag.IsDeleted = false
|
||||
db.Insert(db.NoteTags, noteTag)
|
||||
|
||||
return noteTag
|
||||
}
|
||||
|
||||
// 得到标签, 按更新时间来排序
|
||||
func (this *TagService) GetTags(userId string) []info.NoteTag {
|
||||
tags := []info.NoteTag{}
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsDeleted": false}
|
||||
q := db.NoteTags.Find(query);
|
||||
sortFieldR := "-UpdatedTime"
|
||||
q.Sort(sortFieldR).All(&tags)
|
||||
return tags
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
// 也删除所有的笔记含该标签的
|
||||
// 返回noteId => usn
|
||||
func (this *TagService) DeleteTag(userId string, tag string) map[string]int {
|
||||
usn := userService.IncrUsn(userId)
|
||||
if db.UpdateByQMap(db.NoteTags, bson.M{"UserId": bson.ObjectIdHex(userId), "Tag": tag}, bson.M{"Usn": usn, "IsDeleted": true}) {
|
||||
return noteService.UpdateNoteToDeleteTag(userId, tag);
|
||||
}
|
||||
return map[string]int{}
|
||||
}
|
||||
|
||||
// 删除标签, 供API调用
|
||||
func (this *TagService) DeleteTagApi(userId string, tag string, usn int) (ok bool, msg string, toUsn int) {
|
||||
noteTag := info.NoteTag{}
|
||||
db.GetByQ(db.NoteTags, bson.M{"UserId": bson.ObjectIdHex(userId), "Tag": tag}, ¬eTag)
|
||||
|
||||
if noteTag.TagId == "" {
|
||||
return false, "notExists", 0
|
||||
}
|
||||
if noteTag.Usn > usn {
|
||||
return false, "conflict", 0
|
||||
}
|
||||
toUsn = userService.IncrUsn(userId)
|
||||
if db.UpdateByQMap(db.NoteTags, bson.M{"UserId": bson.ObjectIdHex(userId), "Tag": tag}, bson.M{"Usn": usn, "IsDeleted": true}) {
|
||||
return true, "", toUsn
|
||||
}
|
||||
return false, "", 0
|
||||
}
|
||||
|
||||
// 重新统计标签的count
|
||||
func (this *TagService) reCountTagCount(userId string, tags []string) {
|
||||
if tags == nil {
|
||||
return
|
||||
}
|
||||
for _, tag := range tags {
|
||||
this.AddOrUpdateTag(userId, tag);
|
||||
}
|
||||
}
|
||||
|
||||
// 同步用
|
||||
func (this *TagService) GeSyncTags(userId string, afterUsn, maxEntry int) ([]info.NoteTag) {
|
||||
noteTags := []info.NoteTag{}
|
||||
q := db.NoteTags.Find(bson.M{"UserId": bson.ObjectIdHex(userId), "Usn": bson.M{"$gt": afterUsn}});
|
||||
q.Sort("Usn").Limit(maxEntry).All(¬eTags)
|
||||
return noteTags
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func (this *TrashService) DeleteNote(noteId, userId string) bool {
|
||||
// 首先删除其共享
|
||||
if shareService.DeleteShareNoteAll(noteId, userId) {
|
||||
// 更新note isTrash = true
|
||||
if db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true}}) {
|
||||
if db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true, "Usn": userService.IncrUsn(userId)}}) {
|
||||
// recount notebooks' notes number
|
||||
notebookIdO := noteService.GetNotebookId(noteId)
|
||||
notebookId := notebookIdO.Hex()
|
||||
@ -44,7 +44,7 @@ func (this *TrashService) DeleteNote(noteId, userId string) bool {
|
||||
func (this *TrashService) DeleteSharedNote(noteId, userId, myUserId string) bool {
|
||||
note := noteService.GetNote(noteId, userId)
|
||||
if shareService.HasUpdatePerm(userId, myUserId, noteId) && note.CreatedUserId.Hex() == myUserId {
|
||||
return db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true}})
|
||||
return db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true, "Usn": userService.IncrUsn(userId)}})
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -53,23 +53,63 @@ func (this *TrashService) DeleteSharedNote(noteId, userId, myUserId string) bool
|
||||
func (this *TrashService) recoverNote(noteId, notebookId, userId string) bool {
|
||||
re := db.UpdateByIdAndUserId(db.Notes, noteId, userId,
|
||||
bson.M{"$set": bson.M{"IsTrash": false,
|
||||
"Usn": userService.IncrUsn(userId),
|
||||
"NotebookId": bson.ObjectIdHex(notebookId)}})
|
||||
return re;
|
||||
}
|
||||
|
||||
// 删除trash
|
||||
func (this *TrashService) DeleteTrash(noteId, userId string) bool {
|
||||
note := noteService.GetNote(noteId, userId);
|
||||
if note.NoteId == "" {
|
||||
return false
|
||||
}
|
||||
// delete note's attachs
|
||||
ok := attachService.DeleteAllAttachs(noteId, userId)
|
||||
|
||||
|
||||
// 设置删除位
|
||||
ok = db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
|
||||
bson.M{"IsDeleted": true,
|
||||
"Usn": userService.IncrUsn(userId)})
|
||||
// delete note
|
||||
ok = db.DeleteByIdAndUserId(db.Notes, noteId, userId)
|
||||
// ok = db.DeleteByIdAndUserId(db.Notes, noteId, userId)
|
||||
|
||||
// delete content
|
||||
ok = db.DeleteByIdAndUserId(db.NoteContents, noteId, userId)
|
||||
|
||||
// 重新统计tag's count
|
||||
// TODO 这里会改变tag's Usn
|
||||
tagService.reCountTagCount(userId, note.Tags)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (this *TrashService) DeleteTrashApi(noteId, userId string, usn int) (bool, string, int) {
|
||||
note := noteService.GetNote(noteId, userId)
|
||||
|
||||
if note.NoteId == "" || note.IsDeleted {
|
||||
return false, "notExists", 0
|
||||
}
|
||||
|
||||
if note.Usn != usn {
|
||||
return false, "conflict", 0
|
||||
}
|
||||
|
||||
// delete note's attachs
|
||||
ok := attachService.DeleteAllAttachs(noteId, userId)
|
||||
|
||||
// 设置删除位
|
||||
afterUsn := userService.IncrUsn(userId)
|
||||
ok = db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
|
||||
bson.M{"IsDeleted": true,
|
||||
"Usn": afterUsn})
|
||||
|
||||
// delete content
|
||||
ok = db.DeleteByIdAndUserId(db.NoteContents, noteId, userId)
|
||||
|
||||
return ok, "", afterUsn
|
||||
}
|
||||
|
||||
// 列出note, 排序规则, 还有分页
|
||||
// CreatedTime, UpdatedTime, title 来排序
|
||||
func (this *TrashService) ListNotes(userId string,
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "time"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ func (this *UpgradeService) UpgradeBlog() bool {
|
||||
*/
|
||||
func (this *UpgradeService) UpgradeBetaToBeta2(userId string) (ok bool, msg string) {
|
||||
if configService.GetGlobalStringConfig("UpgradeBetaToBeta2") != "" {
|
||||
return false, "已升级"
|
||||
return false, "Leanote have been upgraded"
|
||||
}
|
||||
|
||||
// 1. aboutMe -> page
|
||||
@ -102,3 +102,81 @@ func (this *UpgradeService) UpgradeBetaToBeta2(userId string) (ok bool, msg stri
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Usn设置
|
||||
// 客户端 api
|
||||
|
||||
func (this *UpgradeService) moveTag() {
|
||||
usnI := 1
|
||||
tags := []info.Tag{}
|
||||
db.ListByQ(db.Tags, bson.M{}, &tags)
|
||||
for _, eachTag := range tags {
|
||||
tagTitles := eachTag.Tags
|
||||
now := time.Now()
|
||||
if tagTitles != nil && len(tagTitles) > 0 {
|
||||
for _, tagTitle := range tagTitles {
|
||||
noteTag := info.NoteTag{}
|
||||
noteTag.TagId = bson.NewObjectId()
|
||||
noteTag.Count = 1
|
||||
noteTag.Tag = tagTitle
|
||||
noteTag.UserId = eachTag.UserId
|
||||
noteTag.CreatedTime = now
|
||||
noteTag.UpdatedTime = now
|
||||
noteTag.Usn = usnI
|
||||
noteTag.IsDeleted = false
|
||||
db.Insert(db.NoteTags, noteTag)
|
||||
usnI++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *UpgradeService) setNotebookUsn() {
|
||||
usnI := 1
|
||||
notebooks := []info.Notebook{}
|
||||
db.ListByQWithFields(db.Notebooks, bson.M{}, []string{"UserId"}, ¬ebooks)
|
||||
|
||||
for _, notebook := range notebooks {
|
||||
db.UpdateByQField(db.Notebooks, bson.M{"_id": notebook.NotebookId}, "Usn", usnI)
|
||||
usnI++
|
||||
}
|
||||
}
|
||||
|
||||
func (this *UpgradeService) setNoteUsn() {
|
||||
usnI := 1
|
||||
notes := []info.Note{}
|
||||
db.ListByQWithFields(db.Notes, bson.M{}, []string{"UserId"}, ¬es)
|
||||
|
||||
for _, note := range notes {
|
||||
db.UpdateByQField(db.Notes, bson.M{"_id": note.NoteId}, "Usn", usnI)
|
||||
usnI++
|
||||
}
|
||||
}
|
||||
|
||||
// 升级为Api, beta.4
|
||||
func (this *UpgradeService) Api(userId string) (ok bool, msg string) {
|
||||
if configService.GetGlobalStringConfig("UpgradeBetaToBeta4") != "" {
|
||||
return false, "Leanote have been upgraded"
|
||||
}
|
||||
|
||||
// user
|
||||
db.UpdateByQField(db.Users, bson.M{}, "Usn", 200000)
|
||||
|
||||
// notebook
|
||||
db.UpdateByQField(db.Notebooks, bson.M{}, "IsDeleted", false)
|
||||
this.setNotebookUsn();
|
||||
|
||||
// note
|
||||
// 1-N
|
||||
db.UpdateByQField(db.Notes, bson.M{}, "IsDeleted", false)
|
||||
this.setNoteUsn();
|
||||
|
||||
// tag
|
||||
// 1-N
|
||||
/// tag, 要重新插入, 将之前的Tag表迁移到NoteTag中
|
||||
this.moveTag();
|
||||
|
||||
configService.UpdateGlobalStringConfig(userId, "UpgradeBetaToBeta4", "1")
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
@ -12,6 +12,27 @@ import (
|
||||
type UserService struct {
|
||||
}
|
||||
|
||||
// 自增Usn
|
||||
// 每次notebook,note添加, 修改, 删除, 都要修改
|
||||
func (this *UserService) IncrUsn(userId string) int {
|
||||
user := info.User{}
|
||||
query := bson.M{"_id": bson.ObjectIdHex(userId)}
|
||||
db.GetByQWithFields(db.Users, query, []string{"Usn"}, &user)
|
||||
usn := user.Usn
|
||||
usn += 1
|
||||
Log("inc Usn")
|
||||
db.UpdateByQField(db.Users, query, "Usn", usn)
|
||||
return usn
|
||||
// return db.Update(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, bson.M{"$inc": bson.M{"ReadNum": 1}})
|
||||
}
|
||||
|
||||
func (this *UserService) GetUsn(userId string) int {
|
||||
user := info.User{}
|
||||
query := bson.M{"_id": bson.ObjectIdHex(userId)}
|
||||
db.GetByQWithFields(db.Users, query, []string{"Usn"}, &user)
|
||||
return user.Usn
|
||||
}
|
||||
|
||||
// 添加用户
|
||||
func (this *UserService) AddUser(user info.User) bool {
|
||||
if user.UserId == "" {
|
||||
@ -98,6 +119,10 @@ func (this *UserService) GetUserInfo(userId string) info.User {
|
||||
func (this *UserService) GetUserInfoByEmail(email string) info.User {
|
||||
user := info.User{}
|
||||
db.GetByQ(db.Users, bson.M{"Email": email}, &user)
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
// Logo路径问题, 有些有http: 有些没有
|
||||
>>>>>>> dev-life
|
||||
this.setUserLogo(&user)
|
||||
return user
|
||||
}
|
||||
@ -106,6 +131,10 @@ func (this *UserService) GetUserInfoByUsername(username string) info.User {
|
||||
user := info.User{}
|
||||
username = strings.ToLower(username)
|
||||
db.GetByQ(db.Users, bson.M{"Username": username}, &user)
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
// Logo路径问题, 有些有http: 有些没有
|
||||
>>>>>>> dev-life
|
||||
this.setUserLogo(&user)
|
||||
return user
|
||||
}
|
||||
|
@ -229,7 +229,12 @@
|
||||
<li>
|
||||
<a href="/admin/t?t=upgrade/beta2">
|
||||
<span>
|
||||
v1.0-beta to v1.0-beta2
|
||||
v1.0-beta to v1.0-beta.2
|
||||
</span>
|
||||
</a>
|
||||
<a href="/admin/t?t=upgrade/beta3">
|
||||
<span>
|
||||
v1.0-beta.2/3 to v1.0-beta.4
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Upgrade v1.0-beta to v1.0-beta2</h3></div>
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Upgrade v1.0-beta to v1.0-beta.2</h3></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Upgrade</button>
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Upgrade to v1.0-beta.2</button>
|
||||
</footer>
|
||||
</section>
|
||||
</form>
|
||||
@ -31,14 +31,16 @@ $(function() {
|
||||
$("#submit").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminUpgrade/UpgradeBetaToBeta2", {}, function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
art.confirm("Are you sure to upgrade to v1.0-beta.2", function() {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminUpgrade/UpgradeBetaToBeta2", {}, function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
46
app/views/admin/upgrade/beta3.html
Normal file
46
app/views/admin/upgrade/beta3.html
Normal file
@ -0,0 +1,46 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Upgrade v1.0-beta.2/3 to v1.0-beta.4</h3></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-6">
|
||||
<form id="add_user_form">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
Current Version: <span class="label label-success">leanote v{{.version}}</span>
|
||||
<ul>
|
||||
<li>Api support (Enable Leanote Desktop App to connect server))</li>
|
||||
<li>Update tags</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Upgrade to v1.0-beta.4</button>
|
||||
</footer>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
<script>
|
||||
$(function() {
|
||||
$("#submit").click(function(e) {
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
art.confirm("Are you sure to upgrade to v1.0-beta.4 ? Please don't try to upgrade twice.", function() {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminUpgrade/UpgradeBeta3ToBeta4", {}, function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
@ -713,7 +713,7 @@ function log(o) {
|
||||
|
||||
<!-- 插入图片 -->
|
||||
<div class="modal fade modal-insert-image">
|
||||
<div class="modal-dialog" style="width: 840px">
|
||||
<div class="modal-dialog" style="width: 840px;max-width:100%;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
@ -928,4 +928,4 @@ window.require = {
|
||||
<script src="/js/require.js"></script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
Reference in New Issue
Block a user