From 6f4ba8313ca9419316c76a8e5c46eac6381948d1 Mon Sep 17 00:00:00 2001 From: iiuazz Date: Fri, 19 Sep 2014 17:18:53 +0800 Subject: [PATCH 1/7] for safety image v1.0 #14 --- .gitignore | 2 +- app/controllers/FileController.go | 45 ++++-- app/controllers/NoteController.go | 1 - app/controllers/init.go | 5 +- app/db/Mgo.go | 4 + app/info/FileInfo.go | 8 +- app/info/NoteImage.go | 12 ++ app/info/NoteInfo.go | 14 +- app/lea/File.go | 15 ++ app/lea/Util.go | 12 ++ app/service/FileService.go | 129 +++++++++++++++++- app/service/NoteImageService.go | 69 ++++++++++ app/service/NoteService.go | 28 +++- app/service/ShareService.go | 4 +- app/service/init.go | 4 + app/views/Note/note-dev.html | 2 + public/js/app/note.js | 9 ++ public/js/app/share.js | 4 +- public/tinymce/plugins/leaui_image/plugin.js | 98 +++++++------ .../leaui_image/public/js/for_editor.js | 5 +- .../plugins/leaui_image/public/js/main.js | 13 +- .../plugins/leaui_image/public/js/main.min.js | 2 +- 22 files changed, 402 insertions(+), 83 deletions(-) create mode 100644 app/info/NoteImage.go create mode 100644 app/service/NoteImageService.go diff --git a/.gitignore b/.gitignore index f7308ef..348dea1 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ app/tmp/main.go .settings .project public/config.codekit - +files diff --git a/app/controllers/FileController.go b/app/controllers/FileController.go index 62a8a35..82fe1ab 100644 --- a/app/controllers/FileController.go +++ b/app/controllers/FileController.go @@ -3,11 +3,13 @@ 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" "io/ioutil" "os" "strconv" + "strings" ) // 首页 @@ -16,6 +18,7 @@ type File struct { } // 上传图片 editor +// 过时 func (c File) UploadImage(renderHtml string) revel.Result { if renderHtml == "" { renderHtml = "file/image.html" @@ -31,6 +34,7 @@ func (c File) UploadImage(renderHtml string) revel.Result { } // 上传的是博客logo +// TODO logo不要设置权限, 另外的目录 func (c File) UploadBlogLogo() revel.Result { return c.UploadImage("file/blog_logo.html"); } @@ -38,28 +42,25 @@ func (c File) UploadBlogLogo() revel.Result { // 拖拉上传, pasteImage func (c File) UploadImageJson(renderHtml, from string) revel.Result { re := c.uploadImage(from, ""); - re.Id = siteUrl + re.Id -// re.Id = re.Id return c.RenderJson(re) } // leaui image plugin func (c File) UploadImageLeaui(albumId string) revel.Result { re := c.uploadImage("", albumId); - re.Id = siteUrl + re.Id -// re.Id = re.Id return c.RenderJson(re) } // 上传图片, 公用方法 func (c File) uploadImage(from, albumId string) (re info.Re) { var fileUrlPath = "" + var fileId = "" var resultCode = 0 // 1表示正常 var resultMsg = "内部错误" // 错误信息 var Ok = false defer func() { - re.Id = fileUrlPath + re.Id = fileId re.Code = resultCode re.Msg = resultMsg re.Ok = Ok @@ -71,11 +72,10 @@ func (c File) uploadImage(from, albumId string) (re info.Re) { } defer file.Close() // 生成上传路径 - fileUrlPath = "/upload/" + c.GetUserId() + "/images" - dir := revel.BasePath + "/public/" + fileUrlPath + fileUrlPath = "files/" + c.GetUserId() + "/images" + dir := revel.BasePath + "/" + fileUrlPath err = os.MkdirAll(dir, 0755) if err != nil { - Log(err) return re } // 生成新的文件名 @@ -126,8 +126,12 @@ func (c File) uploadImage(from, albumId string) (re info.Re) { Path: fileUrlPath, Size: filesize} + id := bson.NewObjectId(); + fileInfo.FileId = id + fileId = id.Hex() Ok = fileService.AddImage(fileInfo, albumId, c.GetUserId()) + fileInfo.Path = ""; // 不要返回 re.Item = fileInfo return re @@ -197,4 +201,27 @@ func (c File) UpgradeLeauiImage() revel.Result { re.Msg = msg return c.RenderJson(re) -} \ No newline at end of file +} + +//----------- + +// 输出image +// 权限判断 +func (c File) OutputImage(noteId, 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 +} + +// 协作时复制图片到owner +func (c File) CopyImage(userId, fileId, toUserId string) revel.Result { + re := info.NewRe() + + re.Ok, re.Id = fileService.CopyImage(userId, fileId, toUserId) + + return c.RenderJson(re) +} diff --git a/app/controllers/NoteController.go b/app/controllers/NoteController.go index ac8558c..17b48c5 100644 --- a/app/controllers/NoteController.go +++ b/app/controllers/NoteController.go @@ -117,7 +117,6 @@ type NoteOrContent struct { // 这里不能用json, 要用post func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result { // 新添加note - LogJ(noteOrContent) if noteOrContent.IsNew { userId := c.GetObjectUserId(); myUserId := userId diff --git a/app/controllers/init.go b/app/controllers/init.go index 5736b19..9e99661 100644 --- a/app/controllers/init.go +++ b/app/controllers/init.go @@ -56,12 +56,13 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru "User": map[string]bool{"UpdateEmail": true, "ActiveEmail":true, }, - "oauth": map[string]bool{"githubCallback": true}, + "Oauth": map[string]bool{"GithubCallback": true}, + "File": map[string]bool{"OutputImage": true, "OutputFile": true}, } func needValidate(controller, method string) bool { // 在里面 if v, ok := commonUrl[controller]; ok { - // 不在commonUrl里 + // 在commonUrl里 if _, ok2 := v[method]; ok2 { return false } diff --git a/app/db/Mgo.go b/app/db/Mgo.go index 7a330df..e68ca3a 100644 --- a/app/db/Mgo.go +++ b/app/db/Mgo.go @@ -37,6 +37,8 @@ var Suggestions *mgo.Collection var Albums *mgo.Collection var Files *mgo.Collection +var NoteImages *mgo.Collection + // 初始化时连接数据库 func Init() { var url string @@ -104,6 +106,8 @@ func Init() { // Album & file Albums = Session.DB(dbname).C("albums") Files = Session.DB(dbname).C("files") + + NoteImages = Session.DB(dbname).C("note_images") } func init() { diff --git a/app/info/FileInfo.go b/app/info/FileInfo.go index 199d53c..66a7995 100644 --- a/app/info/FileInfo.go +++ b/app/info/FileInfo.go @@ -11,9 +11,11 @@ type File struct { AlbumId bson.ObjectId `bson:"AlbumId"` Name string `Name` // file name Title string `Title` // file name or user defined for search - Size int64 `Size` // file size (byte) - Type string `Type` // file type, such as image/jpg - Path string `Path` // the file path, based on /upload + Size int64 `Size` // file size (byte) + Type string `Type` // file type, "" = image, "doc" = word + Path string `Path` // the file path IsDefaultAlbum bool `IsDefaultAlbum` CreatedTime time.Time `CreatedTime` + + FromFileId bson.ObjectId `bson:"FromFileId,omitempty"` // copy from fileId, for collaboration } diff --git a/app/info/NoteImage.go b/app/info/NoteImage.go new file mode 100644 index 0000000..38643cf --- /dev/null +++ b/app/info/NoteImage.go @@ -0,0 +1,12 @@ +package info + +import ( + "gopkg.in/mgo.v2/bson" +) + +// 笔记内部图片 +type NoteImage struct { + NoteImageId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键 + NoteId bson.ObjectId `bson:"NoteId"` // 笔记 + ImageId bson.ObjectId `bson:"ImageId"` // 图片fileId +} \ No newline at end of file diff --git a/app/info/NoteInfo.go b/app/info/NoteInfo.go index 0b3b513..103e2df 100644 --- a/app/info/NoteInfo.go +++ b/app/info/NoteInfo.go @@ -24,6 +24,8 @@ type Note struct { IsMarkdown bool `IsMarkdown` // 是否是markdown笔记, 默认是false + AttachIds []string `FileIds,omitempty` // 2014/9/18, attachments + CreatedTime time.Time `CreatedTime` UpdatedTime time.Time `UpdatedTime` UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了 @@ -54,11 +56,11 @@ type NoteAndContent struct { // 每一个历史记录对象 type EachHistory struct { UpdatedUserId bson.ObjectId `UpdatedUserId` - UpdatedTime time.Time `UpdatedTime` - Content string `Content` + UpdatedTime time.Time `UpdatedTime` + Content string `Content` } type NoteContentHistory struct { - NoteId bson.ObjectId `bson:"_id,omitempty"` - UserId bson.ObjectId `bson:"UserId"` // 所属者 - Histories []EachHistory `Histories` -} \ No newline at end of file + NoteId bson.ObjectId `bson:"_id,omitempty"` + UserId bson.ObjectId `bson:"UserId"` // 所属者 + Histories []EachHistory `Histories` +} diff --git a/app/lea/File.go b/app/lea/File.go index a1f4dff..503f165 100644 --- a/app/lea/File.go +++ b/app/lea/File.go @@ -4,6 +4,7 @@ import ( "strings" "path/filepath" "os" + "io" ) // 分离文件名与扩展名(包含.) @@ -62,4 +63,18 @@ func ListDir(dir string) []string { } names, _ := f.Readdirnames(0) return names +} + +func CopyFile(srcName, dstName string) (written int64, err error) { + src, err := os.Open(srcName) + if err != nil { + return + } + defer src.Close() + dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + return + } + defer dst.Close() + return io.Copy(dst, src) } \ No newline at end of file diff --git a/app/lea/Util.go b/app/lea/Util.go index 1685802..3d28047 100644 --- a/app/lea/Util.go +++ b/app/lea/Util.go @@ -266,4 +266,16 @@ func RandomPwd(num int) string { } return str +} + +func InArray(arr []string, str string) bool { + if arr == nil { + return false + } + for _, v := range arr { + if v == str { + return true + } + } + return false } \ No newline at end of file diff --git a/app/service/FileService.go b/app/service/FileService.go index 7d8a7bd..defb377 100644 --- a/app/service/FileService.go +++ b/app/service/FileService.go @@ -1,7 +1,7 @@ package service import ( -// . "github.com/leanote/leanote/app/lea" + . "github.com/leanote/leanote/app/lea" "github.com/revel/revel" "github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/db" @@ -35,7 +35,7 @@ func (this *FileService) ListImagesWithPage(userId, albumId, key string, pageNum skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, "CreatedTime", false) files := []info.File{} - q := bson.M{"UserId": bson.ObjectIdHex(userId)} + q := bson.M{"UserId": bson.ObjectIdHex(userId), "Type": ""} // life if albumId != "" { q["AlbumId"] = bson.ObjectIdHex(albumId); } else { @@ -89,6 +89,7 @@ func (this *FileService) DeleteImage(userId, fileId string) (bool, string) { if(file.FileId != "") { if db.DeleteByIdAndUserId(db.Files, fileId, userId) { // delete image + // TODO err := os.Remove(revel.BasePath + "/public/" + file.Path) if err == nil { return true, "" @@ -103,4 +104,126 @@ func (this *FileService) DeleteImage(userId, fileId string) (bool, string) { // update image title func (this *FileService) UpdateImage(userId, fileId, title string) bool { return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title) -} \ No newline at end of file +} + +// 获取文件路径 +// 要判断是否具有权限 +// userId是否具有fileId的访问权限 +func (this *FileService) GetFile(userId, fileId string) string { + if userId == "" || fileId == "" { + return "" + } + + file := info.File{} + db.Get(db.Files, fileId, &file) + path := file.Path + if path == "" { + return "" + } + + // 1. 判断权限 + + // 是否是我的文件 + if file.UserId.Hex() == userId { + return path + } + + // 得到使用过该fileId的所有笔记NoteId + // 这些笔记是否有public的, 若有则ok + // 这些笔记(笔记本)是否有共享给我的, 若有则ok + + noteIds := noteImageService.GetNoteIds(fileId) + if noteIds != nil && len(noteIds) > 0 { + // 这些笔记是否有public的 + if db.Has(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}, "IsBlog": true}) { + return path + } + + // 若有共享给我的笔记? + if db.Has(db.ShareNotes, bson.M{"ToUserId": bson.ObjectIdHex(userId), "NoteId": bson.M{"$in": noteIds}}) { + return path + } + + // 笔记本是否共享给我? + // 通过笔记得到笔记本 + notes := []info.Note{} + db.ListByQWithFields(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}}, []string{"NotebookId"}, ¬es) + if notes != nil && len(notes) > 0 { + notebookIds := make([]bson.ObjectId, len(notes)) + for i := 0; i < len(notes); i++ { + notebookIds[i] = notes[i].NotebookId + } + + if db.Has(db.ShareNotebooks, bson.M{"ToUserId": bson.ObjectIdHex(userId), "NotebookId": bson.M{"$in": notebookIds}}) { + return path + } + } + } + + // 可能是刚复制到owner上, 但内容又没有保存, 所以没有note->imageId的映射, 此时看是否有fromFileId + if file.FromFileId != "" { + fromFile := info.File{} + db.Get2(db.Files, file.FromFileId, &fromFile) + if fromFile.UserId.Hex() == userId { + return fromFile.Path + } + } + + return "" +} + +// 复制图片 +func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, string) { + // 是否已经复制过了 + file2 := info.File{} + db.GetByQ(db.Files, bson.M{"UserId": bson.ObjectIdHex(toUserId), "FromFileId": bson.ObjectIdHex(fileId)}, &file2) + if file2.FileId != "" { + return true, file2.FileId.Hex(); + } + + // 复制之 + + file := info.File{} + db.GetByIdAndUserId(db.Files, fileId, userId, &file) + + if file.FileId == "" || file.UserId.Hex() != userId { + return false, "" + } + Log(file) + + _, ext := SplitFilename(file.Name) + newFilename := NewGuid() + ext + + dir := "files/" + toUserId + "/images" + filePath := dir + "/" + newFilename + err := os.MkdirAll(dir, 0755) + if err != nil { + return false, "" + } + + _, err = CopyFile(revel.BasePath + "/" + file.Path, revel.BasePath + "/" + filePath) + if err != nil { + Log(err) + return false, "" + } + + fileInfo := info.File{Name: newFilename, + Title: file.Title, + Path: filePath, + Size: file.Size, + FromFileId: file.FileId} + id := bson.NewObjectId(); + fileInfo.FileId = id + fileId = id.Hex() + Ok := this.AddImage(fileInfo, "", toUserId) + + if Ok { + return Ok, id.Hex() + } + return false, "" +} + +// 是否是我的文件 +func (this *FileService) IsMyFile(userId, fileId string) bool { + return db.Has(db.Files, bson.M{"UserId": bson.ObjectIdHex(userId), "_id": bson.ObjectIdHex(fileId)}) +} diff --git a/app/service/NoteImageService.go b/app/service/NoteImageService.go new file mode 100644 index 0000000..2840791 --- /dev/null +++ b/app/service/NoteImageService.go @@ -0,0 +1,69 @@ +package service + +import ( + "github.com/leanote/leanote/app/info" + "github.com/leanote/leanote/app/db" + . "github.com/leanote/leanote/app/lea" + "gopkg.in/mgo.v2/bson" + "regexp" +// "time" +) + +type NoteImageService struct { +} + +// 通过id, userId得到noteIds +func (this *NoteImageService) GetNoteIds(imageId string) ([]bson.ObjectId) { + noteImages := []info.NoteImage{} + db.ListByQWithFields(db.NoteImages, bson.M{"ImageId": bson.ObjectIdHex(imageId)}, []string{"NoteId"}, ¬eImages) + + if noteImages != nil && len(noteImages) > 0 { + noteIds := make([]bson.ObjectId, len(noteImages)) + cnt := len(noteImages) + for i := 0; i < cnt; i++ { + noteIds[i] = noteImages[i].NoteId + } + return noteIds + } + + return nil +} + +// 解析内容中的图片, 建立图片与note的关系 +// +// 图片必须是我的, 不然不添加 +func (this *NoteImageService) UpdateNoteImages(userId, noteId, content string) bool { + reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})") + find := reg.FindAllStringSubmatch(content, -1) // 查找所有的 + + // 删除旧的 + db.DeleteAll(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(noteId)}) + + Log("--------ii--") + + // 添加新的 + 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 _, ok := hasAdded[fileId]; !ok { + Log(fileId) + // 判断是否是我的文件 + if fileService.IsMyFile(userId, fileId) { + Log("?????") + Log("<><><>") + noteImage.ImageId = bson.ObjectIdHex(fileId) + db.Insert(db.NoteImages, noteImage) + } + hasAdded[fileId] = true + } + } + } + } + + return true +} \ No newline at end of file diff --git a/app/service/NoteService.go b/app/service/NoteService.go index f1341fd..dd3ce33 100644 --- a/app/service/NoteService.go +++ b/app/service/NoteService.go @@ -17,6 +17,12 @@ func (this *NoteService) GetNote(noteId, userId string) (note info.Note) { db.GetByIdAndUserId(db.Notes, noteId, userId, ¬e) return } +// fileService调用 +func (this *NoteService) GetNoteById(noteId string) (note info.Note) { + note = info.Note{} + db.Get(db.Notes, noteId, ¬e) + return +} // 得到blog, blogService用 // 不要传userId, 因为是公开的 func (this *NoteService) GetBlogNote(noteId string) (note info.Note) { @@ -148,6 +154,10 @@ func (this *NoteService) AddNoteContent(noteContent info.NoteContent) info.NoteC noteContent.UpdatedTime = noteContent.CreatedTime noteContent.UpdatedUserId = noteContent.UserId db.Insert(db.NoteContents, noteContent) + + // 更新笔记图片 + noteImageService.UpdateNoteImages(noteContent.UserId.Hex(), noteContent.NoteId.Hex(), noteContent.Content) + return noteContent; } @@ -237,11 +247,23 @@ func (this *NoteService) UpdateNoteContent(userId, updatedUserId, noteId, conten Content: content, UpdatedTime: time.Now(), }) + + // 更新笔记图片 + noteImageService.UpdateNoteImages(userId, noteId, content) + return true } return false } +// ????? +// 这种方式太恶心, 改动很大 +// 通过content修改笔记的imageIds列表 +// src="http://localhost:9000/file/outputImage?fileId=541ae75499c37b6b79000005¬eId=541ae63c19807a4bb9000000" +func (this *NoteService) updateNoteImages(noteId string, content string) bool { + return true +} + // 更新tags // [ok] [del] func (this *NoteService) UpdateTags(noteId string, userId string, tags []string) bool { @@ -396,7 +418,6 @@ func (this *NoteService) searchNoteFromContent(notes []info.Note, userId, key st for i, note := range notes { noteIds[i] = note.NoteId } - LogJ(noteIds) noteContents := []info.NoteContent{} query := bson.M{"_id": bson.M{"$nin": noteIds}, "UserId": bson.ObjectIdHex(userId), "Content": bson.M{"$regex": bson.RegEx{".*?" + key + ".*", "i"}}} if isBlog { @@ -419,9 +440,6 @@ func (this *NoteService) searchNoteFromContent(notes []info.Note, userId, key st noteIds2[i] = content.NoteId } -// Log(" content search ") -// Log(lenContent) - // 得到notes notes2 := this.ListNotesByNoteIds(noteIds2) @@ -446,8 +464,6 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb // 总记录数 count, _ = q.Count() - Log(count) - q.Sort(sortFieldR). Skip(skipNum). Limit(pageSize). diff --git a/app/service/ShareService.go b/app/service/ShareService.go index 8e80e55..f13a648 100644 --- a/app/service/ShareService.go +++ b/app/service/ShareService.go @@ -334,7 +334,7 @@ func (this *ShareService) AddHasShareNote(userId, toUserId string) bool { } // userId是否被共享了noteId -func (this *ShareService) hasSharedNote(noteId, myUserId string) bool { +func (this *ShareService) HasSharedNote(noteId, myUserId string) bool { return db.Has(db.ShareNotes, bson.M{"ToUserId": bson.ObjectIdHex(myUserId), "NoteId": bson.ObjectIdHex(noteId)}) } // noteId的notebook是否共享了给我 @@ -355,7 +355,7 @@ func (this *ShareService) GetShareNoteContent(noteId, myUserId, sharedUserId str noteContent = info.NoteContent{} // 是否单独共享了该notebook // 或者, 其notebook共享了我 - if this.hasSharedNote(noteId, myUserId) || this.hasSharedNotebook(noteId, myUserId, sharedUserId) { + if this.HasSharedNote(noteId, myUserId) || this.hasSharedNotebook(noteId, myUserId, sharedUserId) { db.Get(db.NoteContents, noteId, ¬eContent) } else { } diff --git a/app/service/init.go b/app/service/init.go index cd020c4..985e938 100644 --- a/app/service/init.go +++ b/app/service/init.go @@ -17,6 +17,8 @@ var userService *UserService var tagService *TagService var blogService *BlogService var tokenService *TokenService +var noteImageService *NoteImageService +var fileService *FileService func init() { notebookService = &NotebookService{} @@ -28,4 +30,6 @@ func init() { tagService = &TagService{} blogService = &BlogService{} tokenService = &TokenService{} + fileService = &FileService{} + noteImageService = &NoteImageService{} } \ No newline at end of file diff --git a/app/views/Note/note-dev.html b/app/views/Note/note-dev.html index d65a11c..520221f 100644 --- a/app/views/Note/note-dev.html +++ b/app/views/Note/note-dev.html @@ -988,11 +988,13 @@ initSlimScroll(); + diff --git a/public/js/app/note.js b/public/js/app/note.js index 19f2d62..c891305 100644 --- a/public/js/app/note.js +++ b/public/js/app/note.js @@ -71,6 +71,15 @@ Note.setNoteCache = function(content, clear) { } } +// 得到当前的笔记 +Note.getCurNote = function() { + var self = this; + if(self.curNoteId == "") { + return null; + } + return self.cache[self.curNoteId]; +} + // 每当有notebookId相应的note改变时都要重新清空之 // 并设置该notebookId有值 Note.clearCacheByNotebookId = function(notebookId) { diff --git a/public/js/app/share.js b/public/js/app/share.js index cd34b6d..366ac2a 100644 --- a/public/js/app/share.js +++ b/public/js/app/share.js @@ -93,8 +93,8 @@ Share.renderShareNotebooks = function(sharedUserInfos, shareNotebooks) { return; } - if(!shareNotebooks || typeof shareNotebooks != "object" || shareNotebooks.length < 0) { - return; + if(!shareNotebooks || typeof shareNotebooks != "object" || shareNotebooks.length < 0) { + shareNotebooks = {}; } var $shareNotebooks = $("#shareNotebooks"); diff --git a/public/tinymce/plugins/leaui_image/plugin.js b/public/tinymce/plugins/leaui_image/plugin.js index 45d95ea..d4cbbb2 100644 --- a/public/tinymce/plugins/leaui_image/plugin.js +++ b/public/tinymce/plugins/leaui_image/plugin.js @@ -88,7 +88,7 @@ tinymce.PluginManager.add('leaui_image', function(editor, url) { d.height = img.getAttribute("data-height"); d.title = img.getAttribute("data-title"); - datas.push(d); + datas.push(d); } }; @@ -104,49 +104,63 @@ tinymce.PluginManager.add('leaui_image', function(editor, url) { } data.src = trueSrc; - /* - var width = "", height="", title=""; - if(data.width) { - width = 'width="' + data.width +'" '; - } - if(data.height) { - height = 'height="' + data.height +'" '; - } - if(data.title) { - title = 'title="' + data.title +'" '; - } - var attrs = width + height + title; - editor.insertContent(''); - */ - - // 这里, 如果图片宽度过大, 这里设置成500px - var back = (function(data2, i) { - var d = {}; - var imgElm; - // 先显示loading... - d.id = '__mcenew' + i; - d.src = "http://leanote.com/images/loading-24.gif"; - imgElm = dom.createHTML('img', d); - editor.insertContent(imgElm); - imgElm = dom.get(d.id); - log(imgElm) - - return function(wh) { - if(wh && wh.width) { - if(wh.width > 600) { - wh.width = 600; - } - data2.width = wh.width; - } - dom.setAttrib(imgElm, 'src', data2.src); - dom.setAttrib(imgElm, 'width', data2.width); - dom.setAttrib(imgElm, 'title', data2.title); + var renderImage = function(data) { + // 这里, 如果图片宽度过大, 这里设置成500px + var back = (function(data2, i) { + var d = {}; + var imgElm; + // 先显示loading... + d.id = '__mcenew' + i; + d.src = "http://leanote.com/images/loading-24.gif"; + imgElm = dom.createHTML('img', d); + editor.insertContent(imgElm); + imgElm = dom.get(d.id); - dom.setAttrib(imgElm, 'id', null); + return function(wh) { + if(wh && wh.width) { + if(wh.width > 600) { + wh.width = 600; + } + data2.width = wh.width; + } + dom.setAttrib(imgElm, 'src', data2.src); + dom.setAttrib(imgElm, 'width', data2.width); + dom.setAttrib(imgElm, 'title', data2.title); + + dom.setAttrib(imgElm, 'id', null); + } + })(data, i); + getImageSize(data.src, back); + } + + // outputImage?fileId=123232323 + var fileId = ""; + fileIds = trueSrc.split("fileId=") + if(fileIds.length == 2 && fileIds[1].length == "53aecf8a8a039a43c8036282".length) { + fileId = fileIds[1]; + } + if(fileId) { + // 得到fileId, 如果这个笔记不是我的, 那么肯定是协作的笔记, 那么需要将图片copy给原note owner + var curNote = Note.getCurNote(); + if(curNote && curNote.UserId != UserInfo.UserId) { + (function(data) { + ajaxPost("/file/copyImage", {userId: UserInfo.UserId, fileId: fileId, toUserId: curNote.UserId}, function(re) { + if(reIsOk(re) && re.Id) { + var urlPrefix = window.location.protocol + "//" + window.location.host; + data.src = urlPrefix + "/file/outputImage?fileId=" + re.Id; + } + renderImage(data); + }); + })(data); + } else { + renderImage(data); } - })(data, i); - getImageSize(data.src, back); - } + } else { + renderImage(data); + } + + } // end for + this.parent().parent().close(); } }, diff --git a/public/tinymce/plugins/leaui_image/public/js/for_editor.js b/public/tinymce/plugins/leaui_image/public/js/for_editor.js index d3ecd67..0cbeed6 100644 --- a/public/tinymce/plugins/leaui_image/public/js/for_editor.js +++ b/public/tinymce/plugins/leaui_image/public/js/for_editor.js @@ -1,7 +1,7 @@ // for editor. // drag image to editor // Copyright leaui - +var urlPrefix = window.location.protocol + "//" + window.location.host; define('leaui_image', ['jquery.ui.widget', 'fileupload'], function(){ var editor = tinymce.activeEditor; var dom = editor.dom; @@ -103,7 +103,8 @@ define('leaui_image', ['jquery.ui.widget', 'fileupload'], function(){ done: function(e, data) { if (data.result.Ok == true) { data.context.remove(); - var data2 = {src: data.result.Id} + // life + var data2 = {src: urlPrefix + "/file/outputImage?fileId=" + data.result.Id + "¬eId=" + Note.curNoteId} insertImage(data2); } else { data.context.empty(); diff --git a/public/tinymce/plugins/leaui_image/public/js/main.js b/public/tinymce/plugins/leaui_image/public/js/main.js index 94a60d5..86109c1 100644 --- a/public/tinymce/plugins/leaui_image/public/js/main.js +++ b/public/tinymce/plugins/leaui_image/public/js/main.js @@ -260,13 +260,19 @@ var o = { for(var i in datas){ var each = datas[i]; var classes = ""; - var src = urlPrefix + each.Path; + // life edit + // 之前的 + if(each.Path != "" && each.Path.substr(0, 7) == "/upload") { + var src = urlPrefix + each.Path; + } else { + var src = urlPrefix + "/file/outputImage?fileId=" + each.FileId; + } // log(src); if(selectedMap[src]) { classes = 'class="selected"'; } html += '
  • '; - html += ''; + html += ''; // html += '
    '; html += '
    ' + each.Title + '
    '; html += "
  • "; @@ -348,10 +354,11 @@ var o = { return false; } + // life 为了图片安全 if(typeof $li == "object") { var src = $li.find("img").attr('src'); } else { - src = $li; + src = urlPrefix + "/file/outputImage?fileId=" + $li; } this.selectedImages.push(src); this.reRenderSelectedImages(false, src); diff --git a/public/tinymce/plugins/leaui_image/public/js/main.min.js b/public/tinymce/plugins/leaui_image/public/js/main.min.js index 4fe6431..9462448 100644 --- a/public/tinymce/plugins/leaui_image/public/js/main.min.js +++ b/public/tinymce/plugins/leaui_image/public/js/main.min.js @@ -1 +1 @@ -function log(e){console.log(e)}function retIsOk(e){return e&&"object"==typeof e&&1==e.status?!0:!1}function getImageSize(e,t){function a(e,a){i.parentNode.removeChild(i),t({width:e,height:a})}var i=document.createElement("img");i.onload=function(){a(i.clientWidth,i.clientHeight)},i.onerror=function(){a()},i.src=e;var s=i.style;s.visibility="hidden",s.position="fixed",s.bottom=s.left=0,s.width=s.height="auto",document.body.appendChild(i)}var o={maxSelected:G.maxSelected,selectedZoneO:$("#preview"),previewO:$("#preview"),selectedImages:[],imageAttrs:{},pageNum:1,pagination:function(e){var t=this;$(".pagination").pagination(e,{items_per_page:G.perPageItems,callback:function(e){t.pageNum=e+1,t.renderImages($("#albumsForList").val(),t.pageNum,!1)}})},showMsg:function(e){$("#msg").html(e).css("display","inline"),setTimeout(function(){$("#msg").fadeOut()},2e3)},pageAddAlbum:function(e){var t='";$("#albumsForUpload").append(t).val(e.id),$("#albumsForList").append(t)},renderAlbums:function(){var e=this;$.get("server/index.php?action=album:getAlbums",function(t){if(t){var a="";for(var i in t){var s=t[i],r='";a+=r}$("#albumsForUpload").html(a),$("#albumsForList").html(a);var l=$("#albumsForList").val();e.renderImages(l,1,!0)}})},imageMaskO:$("#imageMask"),noImagesO:$("#noImages"),loadingO:$("#loading"),loadingStart:function(){this.imageMaskO.is(":hidden")&&this.imageMaskO.css("opacity",.8).show(),this.noImagesO.hide(),this.loadingO.show()},loadingEnd:function(){this.imageMaskO.hide()},noImages:function(){this.imageMaskO.show().css("opacity",1),this.noImagesO.show(),this.loadingO.hide()},renderImages:function(e,t,a){var i=this;t||(t=1),i.loadingStart(),$.get("server/index.php?action=file:getImages",{album_id:e,page:t},function(e){if(!e||!e.count)return void i.noImages();i.loadingEnd();var t=e.datas,s={};for(var r in i.selectedImages){var l=i.selectedImages[r];s[l]=!0}var n="";for(var r in t){var o=t[r],d="",l=G.imageSrcPrefix+"/"+o.file_path;s[l]&&(d='class="selected"'),n+="
  • ",n+='',n+='
    ',n+="
  • "}$("#imageList").html(n),a&&i.pagination(e.count),$("#imageList img").lazyload()})},initSelectedZones:function(){var e=this;num=this.maxSelected,e.previewO.html("");for(var t=1;num>=t;++t)e.previewO.append("
  • "+t+"
  • ")},reRenderSelectedImages:function(e,t){for(var a=this,i=this.selectedZoneO.find("li"),s=this.selectedImages.length-1,r=0;rs)l.html(r+1);else{src=this.selectedImages[r];var n=a.imageAttrs[src],o="";n&&(n.width&&(o+=' data-width="'+n.width+'"'),n.height&&(o+=' data-height="'+n.height+'"'),n.title&&(o+=' data-title="'+n.title+'"')),l.html("
    ')}e?l.removeClass("selected"):t==src&&l.click()}},removeSelectedImage:function(e){var t=this,a=e.find("img").attr("src");for(var i in this.selectedImages)this.selectedImages[i]==a&&this.selectedImages.splice(i,1);this.reRenderSelectedImages(!0),t.clearAttrs()},addSelectedImage:function(e){if(this.maxSelected<=this.selectedImages.length)return!1;if("object"==typeof e)var t=e.find("img").attr("src");else t=e;return this.selectedImages.push(t),this.reRenderSelectedImages(!1,t),!0},initDataFromTinymce:function(){var e=this,t=top.LEAUI_DATAS,a="";if(t&&t.length>0){for(var i in t){var s=t[i];a=s.src,e.selectedImages.push(s.src),e.imageAttrs[s.src]=s}e.reRenderSelectedImages(!1,a)}},init:function(){var e=this;$("#addAlbumBtn").click(function(){var t=$("#albumName").val();return t?void $.get("server/index.php?action=album:addAlbum",{album_name:t},function(a){retIsOk(a)&&($("#albumName").val(""),e.showMsg("Add Sucess!"),a.album_name=t,e.pageAddAlbum(a))}):void $("#albumName").focus()}),$("#deleteAlbumBtn").click(function(){var t=$("#albumsForUpload").val();$.get("server/index.php?action=album:deleteAlbum",{album_id:t},function(a){1==a?(e.showMsg("Delete Sucess!"),$("#albumsForUpload option[value='"+t+"']").remove(),$("#albumsForList").val()==t&&(e.needRefresh=!0),$("#albumsForList option[value='"+t+"']").remove()):alert("This album has images, please delete it's images at first.")})}),$("#albumsForList").change(function(){var t=$(this).val();e.renderImages(t,1,!0)}),$("#imageList").on("click","li",function(){$(this).hasClass("selected")?($(this).removeClass("selected"),e.removeSelectedImage($(this))):e.addSelectedImage($(this))&&$(this).addClass("selected")}),$("#imageList").on("click",".del",function(t){var a=this;if(t.stopPropagation(),confirm("Are you sure to delete this image ?")){var i=$(this).data("id");$.get("server/index.php?action=file:deleteImage",{file_id:i},function(t){if(t){var i=$(a).closest("li");i.hasClass("selected")&&e.removeSelectedImage(i),$(a).closest("li").remove()}})}}),$("#preview").on("click",".del",function(t){t.stopPropagation();var a=$(this).closest("li"),i=a.find("img").attr("src");e.removeSelectedImage(a),$("#imageList img").each(function(){var e=$(this).attr("src");i==e&&$(this).parent().parent().removeClass("selected")})}),$("#goAddImageBtn").click(function(){$("#albumsForUpload").val($("#albumsForList").val()),$("#myTab li:eq(1) a").tab("show")}),$("#myTab a").click(function(t){t.preventDefault(),$(this).tab("show"),e.needRefresh&&"#images"==$(this).attr("href")&&(setTimeout(function(){var t=$("#albumsForList").val();e.renderImages(t,e.pageNum,!0)},200),e.needRefresh=!1)}),$("#refresh").click(function(){var t=$("#albumsForList").val();e.renderImages(t,e.pageNum,!1)}),$("#addImageUrlBtn").click(function(t){t.preventDefault();var a=$.trim($("#imageUrl").val());return a?void getImageSize(a,function(t){return t.width&&t.height?($("#msgForUrl").hide(),$("#imageUrl").val(""),void e.addSelectedImage(a)):void $("#msgForUrl").show()}):void $("#imageUrl").focus()}),$("#preview").on("click","li",function(){$(this).hasClass("selected")||$(this).find("img").length&&($("#preview li").removeClass("selected"),$(this).addClass("selected"),e.initAttr($(this)))}),$("#attrTitle, #attrWidth, #attrHeight").on("blur",function(){e.modifyAttr($(this))}),$("#attrConstrain").on("click",function(){e.modifyAttr($(this))}),e.initSelectedZones(),e.initDataFromTinymce(),e.renderAlbums(),e.initUploader()},curSrc:"",curLi:null,attrTitleO:$("#attrTitle"),attrWidthO:$("#attrWidth"),attrHeightO:$("#attrHeight"),attrConstrainO:$("#attrConstrain"),clearAttrs:function(){var e=this;e.attrTitleO.val("").attr("disabled",!0),e.attrHeightO.val("").attr("disabled",!0),e.attrWidthO.val("").attr("disabled",!0),e.attrConstrainO.prop("checked",!1).attr("disabled",!0)},scale:function(e){var t=this,a=t.attrConstrainO.is(":checked"),i=+t.attrWidthO.val(),s=+t.attrHeightO.val();if(!isNaN(i)&&!isNaN(s)){var r=t.getCurAttrs(),l=r.preWidth||r.width,n=r.preHeight||r.height;a&&l&&n&&(e?(s=parseInt(i/l*n),t.attrHeightO.val(s)):(i=parseInt(s/n*l),t.attrWidthO.val(i)));var o={width:i,height:s};return o}},getCurAttrs:function(){var e=this;return e.imageAttrs[e.curSrc]},setCurDataAttrs:function(e){var t=this,a=t.curLi.find("img");a.attr("data-width",e.width),a.attr("data-height",e.height),a.attr("data-title",e.title),t.imageAttrs[t.curSrc]=e},modifyAttr:function(e){var t=this,a=e.attr("id"),i=e.val(),s=t.getCurAttrs();if(s){switch(a){case"attrConstrain":i=0,e.is(":checked")&&(i=1),s.constrain=i;break;case"attrTitle":s.title=i;break;case"attrWidth":$.extend(s,t.scale(!0));break;case"attrHeight":$.extend(s,t.scale(!1))}t.setCurDataAttrs(s)}},initAttr:function(e){function t(e){e=e||{},a.attrTitleO.val(e.title).attr("disabled",!1),a.attrWidthO.val(e.width).attr("disabled",!1),a.attrHeightO.val(e.height).attr("disabled",!1),a.attrConstrainO.attr("disabled",!1),e.constrain?a.attrConstrainO.prop("checked",!0):a.attrConstrainO.prop("checked",!1),a.setCurDataAttrs(e)}var a=this;"object"!=typeof e&&(e=$("#preview").find('img[src="'+e+'"]').parent());var i=e.find("img").attr("src");a.curSrc=i,a.curLi=e;var s=a.imageAttrs[i];s=s||{},s&&s.width&&s.height?t(s):getImageSize(i,function(e){return e.title=s.title||"",e.constrain=1,e.preWidth=e.width,e.preHeight=e.height,i!=a.curSrc?(a.imageAttrs[i]=e,void a.setCurDataAttrs(s)):void t(e)})},needRefresh:!1,uploadRefreshImageList:function(){var e=this,t=$("#albumsForList").val();t==$("#albumsForUpload").val()&&(e.needRefresh=!0)},initUploader:function(){function e(e){return"number"!=typeof e?"":e>=1e9?(e/1e9).toFixed(2)+" GB":e>=1e6?(e/1e6).toFixed(2)+" MB":(e/1e3).toFixed(2)+" KB"}var t=this,a=$("#upload ul");$("#drop a").click(function(){$(this).parent().find("input").click()}),$("#upload").fileupload({dataType:"json",acceptFileTypes:/(\.|\/)(gif|jpg|jpeg|png|jpe)$/i,maxFileSize:21e4,dropZone:$("#drop"),formData:function(e){return[{name:"album_id",value:$("#albumsForUpload").val()}]},add:function(t,i){var s=$('
  • ');s.find("div").append(i.files[0].name+" ["+e(i.files[0].size)+"]"),i.context=s.appendTo(a);var r=i.submit()},done:function(a,i){if(1==i.result.status)i.context.remove(),t.addSelectedImage(G.imageSrcPrefix+"/"+i.result.file_path),t.uploadRefreshImageList();else{i.context.empty();var s=$('
  • ');s.find("div").append("Error: "+i.files[0].name+" ["+e(i.files[0].size)+"] "+i.result.msg),i.context.append(s)}$("#upload-msg").scrollTop(1e3)},fail:function(t,a){a.context.empty();var i=$('
  • ');i.find("div").append("Error: "+a.files[0].name+" ["+e(a.files[0].size)+"] "+a.errorThrown),a.context.append(i),$("#upload-msg").scrollTop(1e3)}}),$(document).on("drop dragover",function(e){e.preventDefault()})}};$(function(){o.init()}); \ No newline at end of file +function log(e){console.log(e)}function retIsOk(e){return e&&"object"==typeof e&&1==e.status?!0:!1}function getImageSize(e,t){function a(e,a){i.parentNode.removeChild(i),t({width:e,height:a})}var i=document.createElement("img");i.onload=function(){a(i.clientWidth,i.clientHeight)},i.onerror=function(){a()},i.src=e;var s=i.style;s.visibility="hidden",s.position="fixed",s.bottom=s.left=0,s.width=s.height="auto",document.body.appendChild(i)}var o={maxSelected:G.maxSelected,selectedZoneO:$("#preview"),previewO:$("#preview"),selectedImages:[],imageAttrs:{},pageNum:1,pagination:function(e){var t=this;$(".pagination").pagination(e,{items_per_page:G.perPageItems,callback:function(e){t.pageNum=e+1,t.renderImages($("#albumsForList").val(),t.pageNum,!1)}})},showMsg:function(e){$("#msg").html(e).css("display","inline"),setTimeout(function(){$("#msg").fadeOut()},2e3)},pageAddAlbum:function(e){var t='";$("#albumsForUpload").append(t).val(e.id),$("#albumsForList").append(t)},renderAlbums:function(){var e=this;$.get("server/index.php?action=album:getAlbums",function(t){if(t){var a="";for(var i in t){var s=t[i],r='";a+=r}$("#albumsForUpload").html(a),$("#albumsForList").html(a);var l=$("#albumsForList").val();e.renderImages(l,1,!0)}})},imageMaskO:$("#imageMask"),noImagesO:$("#noImages"),loadingO:$("#loading"),loadingStart:function(){this.imageMaskO.is(":hidden")&&this.imageMaskO.css("opacity",.8).show(),this.noImagesO.hide(),this.loadingO.show()},loadingEnd:function(){this.imageMaskO.hide()},noImages:function(){this.imageMaskO.show().css("opacity",1),this.noImagesO.show(),this.loadingO.hide()},renderImages:function(e,t,a){var i=this;t||(t=1),i.loadingStart(),$.get("server/index.php?action=file:getImages",{album_id:e,page:t},function(e){if(!e||!e.count)return void i.noImages();i.loadingEnd();var t=e.datas,s={};for(var r in i.selectedImages){var l=i.selectedImages[r];s[l]=!0}var n="";for(var r in t){var o=t[r],d="",l=G.imageSrcPrefix+"/"+o.file_path;s[l]&&(d='class="selected"'),n+="
  • ",n+='',n+='
    ',n+="
  • "}$("#imageList").html(n),a&&i.pagination(e.count),$("#imageList img").lazyload()})},initSelectedZones:function(){var e=this;num=this.maxSelected,e.previewO.html("");for(var t=1;num>=t;++t)e.previewO.append("
  • "+t+"
  • ")},reRenderSelectedImages:function(e,t){for(var a=this,i=this.selectedZoneO.find("li"),s=this.selectedImages.length-1,r=0;rs)l.html(r+1);else{src=this.selectedImages[r];var n=a.imageAttrs[src],o="";n&&(n.width&&(o+=' data-width="'+n.width+'"'),n.height&&(o+=' data-height="'+n.height+'"'),n.title&&(o+=' data-title="'+n.title+'"')),l.html("
    ')}e?l.removeClass("selected"):t==src&&l.click()}},removeSelectedImage:function(e){var t=this,a=e.find("img").attr("src");for(var i in this.selectedImages)this.selectedImages[i]==a&&this.selectedImages.splice(i,1);this.reRenderSelectedImages(!0),t.clearAttrs()},addSelectedImage:function(e){if(this.maxSelected<=this.selectedImages.length)return!1;if("object"==typeof e)var t=e.find("img").attr("src");else t=e;return this.selectedImages.push(t),this.reRenderSelectedImages(!1,t),!0},initDataFromTinymce:function(){var e=this,t=top.LEAUI_DATAS,a="";if(t&&t.length>0){for(var i in t){var s=t[i];a=s.src,e.selectedImages.push(s.src),e.imageAttrs[s.src]=s}e.reRenderSelectedImages(!1,a)}},init:function(){var e=this;$("#addAlbumBtn").click(function(){var t=$("#albumName").val();return t?void $.get("server/index.php?action=album:addAlbum",{album_name:t},function(a){retIsOk(a)&&($("#albumName").val(""),e.showMsg("Add Sucess!"),a.album_name=t,e.pageAddAlbum(a))}):void $("#albumName").focus()}),$("#deleteAlbumBtn").click(function(){var t=$("#albumsForUpload").val();$.get("server/index.php?action=album:deleteAlbum",{album_id:t},function(a){1==a?(e.showMsg("Delete Sucess!"),$("#albumsForUpload option[value='"+t+"']").remove(),$("#albumsForList").val()==t&&(e.needRefresh=!0),$("#albumsForList option[value='"+t+"']").remove()):alert("This album has images, please delete it's images at first.")})}),$("#albumsForList").change(function(){var t=$(this).val();e.renderImages(t,1,!0)}),$("#imageList").on("click","li",function(){$(this).hasClass("selected")?($(this).removeClass("selected"),e.removeSelectedImage($(this))):e.addSelectedImage($(this))&&$(this).addClass("selected")}),$("#imageList").on("click",".del",function(t){var a=this;if(t.stopPropagation(),confirm("Are you sure to delete this image ?")){var i=$(this).data("id");$.get("server/index.php?action=file:deleteImage",{file_id:i},function(t){if(t){var i=$(a).closest("li");i.hasClass("selected")&&e.removeSelectedImage(i),$(a).closest("li").remove()}})}}),$("#preview").on("click",".del",function(t){t.stopPropagation();var a=$(this).closest("li"),i=a.find("img").attr("src");e.removeSelectedImage(a),$("#imageList img").each(function(){var e=$(this).attr("src");i==e&&$(this).parent().parent().removeClass("selected")})}),$("#goAddImageBtn").click(function(){$("#albumsForUpload").val($("#albumsForList").val()),$("#myTab li:eq(1) a").tab("show")}),$("#myTab a").click(function(t){t.preventDefault(),$(this).tab("show"),e.needRefresh&&"#images"==$(this).attr("href")&&(setTimeout(function(){var t=$("#albumsForList").val();e.renderImages(t,e.pageNum,!0)},200),e.needRefresh=!1)}),$("#refresh").click(function(){var t=$("#albumsForList").val();e.renderImages(t,e.pageNum,!1)}),$("#addImageUrlBtn").click(function(t){t.preventDefault();var a=$.trim($("#imageUrl").val());return a?void getImageSize(a,function(t){return t.width&&t.height?($("#msgForUrl").hide(),$("#imageUrl").val(""),void e.addSelectedImage(a)):void $("#msgForUrl").show()}):void $("#imageUrl").focus()}),$("#preview").on("click","li",function(){$(this).hasClass("selected")||$(this).find("img").length&&($("#preview li").removeClass("selected"),$(this).addClass("selected"),e.initAttr($(this)))}),$("#attrTitle, #attrWidth, #attrHeight").on("blur",function(){e.modifyAttr($(this))}),$("#attrConstrain").on("click",function(){e.modifyAttr($(this))}),e.initSelectedZones(),e.initDataFromTinymce(),e.renderAlbums(),e.initUploader()},curSrc:"",curLi:null,attrTitleO:$("#attrTitle"),attrWidthO:$("#attrWidth"),attrHeightO:$("#attrHeight"),attrConstrainO:$("#attrConstrain"),clearAttrs:function(){var e=this;e.attrTitleO.val("").attr("disabled",!0),e.attrHeightO.val("").attr("disabled",!0),e.attrWidthO.val("").attr("disabled",!0),e.attrConstrainO.prop("checked",!1).attr("disabled",!0)},scale:function(e){var t=this,a=t.attrConstrainO.is(":checked"),i=+t.attrWidthO.val(),s=+t.attrHeightO.val();if(!isNaN(i)&&!isNaN(s)){var r=t.getCurAttrs(),l=r.preWidth||r.width,n=r.preHeight||r.height;a&&l&&n&&(e?(s=parseInt(i/l*n),t.attrHeightO.val(s)):(i=parseInt(s/n*l),t.attrWidthO.val(i)));var o={width:i,height:s};return o}},getCurAttrs:function(){var e=this;return e.imageAttrs[e.curSrc]},setCurDataAttrs:function(e){var t=this,a=t.curLi.find("img");a.attr("data-width",e.width),a.attr("data-height",e.height),a.attr("data-title",e.title),t.imageAttrs[t.curSrc]=e},modifyAttr:function(e){var t=this,a=e.attr("id"),i=e.val(),s=t.getCurAttrs();if(s){switch(a){case"attrConstrain":i=0,e.is(":checked")&&(i=1),s.constrain=i;break;case"attrTitle":s.title=i;break;case"attrWidth":$.extend(s,t.scale(!0));break;case"attrHeight":$.extend(s,t.scale(!1))}t.setCurDataAttrs(s)}},initAttr:function(e){function t(e){e=e||{},a.attrTitleO.val(e.title).attr("disabled",!1),a.attrWidthO.val(e.width).attr("disabled",!1),a.attrHeightO.val(e.height).attr("disabled",!1),a.attrConstrainO.attr("disabled",!1),e.constrain?a.attrConstrainO.prop("checked",!0):a.attrConstrainO.prop("checked",!1),a.setCurDataAttrs(e)}var a=this;"object"!=typeof e&&(e=$("#preview").find('img[src="'+e+'"]').parent());var i=e.find("img").attr("src");a.curSrc=i,a.curLi=e;var s=a.imageAttrs[i];s=s||{},s&&s.width&&s.height?t(s):getImageSize(i,function(e){return e.title=s.title||"",e.constrain=1,e.preWidth=e.width,e.preHeight=e.height,i!=a.curSrc?(a.imageAttrs[i]=e,void a.setCurDataAttrs(s)):void t(e)})},needRefresh:!1,uploadRefreshImageList:function(){var e=this,t=$("#albumsForList").val();t==$("#albumsForUpload").val()&&(e.needRefresh=!0)},initUploader:function(){function e(e){return"number"!=typeof e?"":e>=1e9?(e/1e9).toFixed(2)+" GB":e>=1e6?(e/1e6).toFixed(2)+" MB":(e/1e3).toFixed(2)+" KB"}var t=this,a=$("#upload ul");$("#drop a").click(function(){$(this).parent().find("input").click()}),$("#upload").fileupload({dataType:"json",acceptFileTypes:/(\.|\/)(gif|jpg|jpeg|png|jpe)$/i,maxFileSize:21e4,dropZone:$("#drop"),formData:function(e){return[{name:"album_id",value:$("#albumsForUpload").val()}]},add:function(t,i){var s=$('
  • ');s.find("div").append(i.files[0].name+" ["+e(i.files[0].size)+"]"),i.context=s.appendTo(a);var r=i.submit()},done:function(a,i){if(1==i.result.status)i.context.remove(),t.addSelectedImage(G.imageSrcPrefix+"/"+i.result.file_path),t.uploadRefreshImageList();else{i.context.empty();var s=$('
  • ');s.find("div").append("Error: "+i.files[0].name+" ["+e(i.files[0].size)+"] "+i.result.msg),i.context.append(s)}$("#upload-msg").scrollTop(1e3)},fail:function(t,a){a.context.empty();var i=$('
  • ');i.find("div").append("Error: "+a.files[0].name+" ["+e(a.files[0].size)+"] "+a.errorThrown),a.context.append(i),$("#upload-msg").scrollTop(1e3)}}),$(document).on("drop dragover",function(e){e.preventDefault()})}};$(function(){o.init()}); From 9f89a3c717659f8f51ac122211aef5b22c3217b0 Mon Sep 17 00:00:00 2001 From: life Date: Fri, 19 Sep 2014 17:29:24 +0800 Subject: [PATCH 2/7] #14 safety image --- .../leaui_image/public/js/for_editor.js | 83 +++++++++++++------ 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/public/tinymce/plugins/leaui_image/public/js/for_editor.js b/public/tinymce/plugins/leaui_image/public/js/for_editor.js index 0cbeed6..bd1846f 100644 --- a/public/tinymce/plugins/leaui_image/public/js/for_editor.js +++ b/public/tinymce/plugins/leaui_image/public/js/for_editor.js @@ -35,33 +35,62 @@ define('leaui_image', ['jquery.ui.widget', 'fileupload'], function(){ } var i = 1; - function insertImage(data2) { - // 这里, 如果图片宽度过大, 这里设置成500px - var d = {}; - var imgElm; - // 先显示loading... - d.id = '__mcenew' + (i++); - d.src = "http://leanote.com/images/loading-24.gif"; - imgElm = dom.createHTML('img', d); - editor.insertContent(imgElm); - imgElm = dom.get(d.id); - - function callback (wh) { - if(wh && wh.width) { - if(wh.width > 600) { - wh.width = 600; - } - data2.width = wh.width; - } - dom.setAttrib(imgElm, 'src', data2.src); - dom.setAttrib(imgElm, 'width', data2.width); - if(data2.title) { - dom.setAttrib(imgElm, 'title', data2.title); - } + function insertImage(data) { + var renderImage = function(data2) { + // 这里, 如果图片宽度过大, 这里设置成500px + var d = {}; + var imgElm; + // 先显示loading... + d.id = '__mcenew' + (i++); + d.src = "http://leanote.com/images/loading-24.gif"; + imgElm = dom.createHTML('img', d); + editor.insertContent(imgElm); + imgElm = dom.get(d.id); - dom.setAttrib(imgElm, 'id', null); - }; - getImageSize(data2.src, callback); + function callback (wh) { + if(wh && wh.width) { + if(wh.width > 600) { + wh.width = 600; + } + data2.width = wh.width; + } + dom.setAttrib(imgElm, 'src', data2.src); + dom.setAttrib(imgElm, 'width', data2.width); + if(data2.title) { + dom.setAttrib(imgElm, 'title', data2.title); + } + + dom.setAttrib(imgElm, 'id', null); + }; + getImageSize(data.src, callback); + } + + //------------- + // outputImage?fileId=123232323 + var fileId = ""; + fileIds = data.src.split("fileId=") + if(fileIds.length == 2 && fileIds[1].length == "53aecf8a8a039a43c8036282".length) { + fileId = fileIds[1]; + } + if(fileId) { + // 得到fileId, 如果这个笔记不是我的, 那么肯定是协作的笔记, 那么需要将图片copy给原note owner + var curNote = Note.getCurNote(); + if(curNote && curNote.UserId != UserInfo.UserId) { + (function(data) { + ajaxPost("/file/copyImage", {userId: UserInfo.UserId, fileId: fileId, toUserId: curNote.UserId}, function(re) { + if(reIsOk(re) && re.Id) { + var urlPrefix = window.location.protocol + "//" + window.location.host; + data.src = urlPrefix + "/file/outputImage?fileId=" + re.Id; + } + renderImage(data); + }); + })(data); + } else { + renderImage(data); + } + } else { + renderImage(data); + } } var initUploader = function() { @@ -104,7 +133,7 @@ define('leaui_image', ['jquery.ui.widget', 'fileupload'], function(){ if (data.result.Ok == true) { data.context.remove(); // life - var data2 = {src: urlPrefix + "/file/outputImage?fileId=" + data.result.Id + "¬eId=" + Note.curNoteId} + var data2 = {src: urlPrefix + "/file/outputImage?fileId=" + data.result.Id} insertImage(data2); } else { data.context.empty(); From 81c2254cfb621f05cbd40e30cc4428b7094248fa Mon Sep 17 00:00:00 2001 From: life Date: Sat, 20 Sep 2014 15:20:36 +0800 Subject: [PATCH 3/7] copy shared note must copy images --- app/service/FileService.go | 4 +-- app/service/NoteImageService.go | 43 +++++++++++++++++++++++++++++---- app/service/NoteService.go | 18 +++++++++++--- app/service/ShareService.go | 14 ++++++----- 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/app/service/FileService.go b/app/service/FileService.go index defb377..37bd4f9 100644 --- a/app/service/FileService.go +++ b/app/service/FileService.go @@ -110,7 +110,7 @@ func (this *FileService) UpdateImage(userId, fileId, title string) bool { // 要判断是否具有权限 // userId是否具有fileId的访问权限 func (this *FileService) GetFile(userId, fileId string) string { - if userId == "" || fileId == "" { + if fileId == "" { return "" } @@ -124,7 +124,7 @@ func (this *FileService) GetFile(userId, fileId string) string { // 1. 判断权限 // 是否是我的文件 - if file.UserId.Hex() == userId { + if userId != "" && file.UserId.Hex() == userId { return path } diff --git a/app/service/NoteImageService.go b/app/service/NoteImageService.go index 2840791..11dcf6d 100644 --- a/app/service/NoteImageService.go +++ b/app/service/NoteImageService.go @@ -39,8 +39,6 @@ func (this *NoteImageService) UpdateNoteImages(userId, noteId, content string) b // 删除旧的 db.DeleteAll(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(noteId)}) - Log("--------ii--") - // 添加新的 var fileId string noteImage := info.NoteImage{NoteId: bson.ObjectIdHex(noteId)} @@ -54,8 +52,6 @@ func (this *NoteImageService) UpdateNoteImages(userId, noteId, content string) b Log(fileId) // 判断是否是我的文件 if fileService.IsMyFile(userId, fileId) { - Log("?????") - Log("<><><>") noteImage.ImageId = bson.ObjectIdHex(fileId) db.Insert(db.NoteImages, noteImage) } @@ -66,4 +62,41 @@ func (this *NoteImageService) UpdateNoteImages(userId, noteId, content string) b } return true -} \ No newline at end of file +} + +// 复制图片, 把note的图片都copy给我, 且修改noteContent图片路径 +func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId, content, toUserId string) string { + // 得到fromNoteId的noteImages, 如果为空, 则直接返回content + noteImages := []info.NoteImage{} + db.ListByQWithFields(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(fromNoteId)}, []string{"ImageId"}, ¬eImages) + + if len(noteImages) == 0 { + return content; + } + + // + // 把fileId=1232替换成新的 + replaceMap := map[string]string{} + for _, noteImage := range noteImages { + imageId := noteImage.ImageId.Hex() + ok, newImageId := fileService.CopyImage(fromUserId, imageId, toUserId) + if ok { + replaceMap[imageId] = newImageId + } + } + + if len(replaceMap) > 0 { + // 替换之 + reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})") + content = reg.ReplaceAllStringFunc(content, func(each string) string { + // each=outputImage?fileId=541bd2f599c37b4f3r000003 + fileId := each[len(each)-24:] // 得到后24位, 也即id + if replaceFileId, ok := replaceMap[fileId]; ok { + return "outputImage?fileId=" + replaceFileId + } + return each + }); + } + + return content; +} diff --git a/app/service/NoteService.go b/app/service/NoteService.go index dd3ce33..8f25aad 100644 --- a/app/service/NoteService.go +++ b/app/service/NoteService.go @@ -338,9 +338,12 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note { } // 复制别人的共享笔记给我 -// TODO 判断是否共享了给我 +// 将别人可用的图片转为我的图片, 复制图片 func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId string) info.Note { - if notebookService.IsMyNotebook(notebookId, myUserId) { + Log(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) + // 判断是否共享了给我 + if notebookService.IsMyNotebook(notebookId, myUserId) && + (shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) { note := this.GetNote(noteId, fromUserId) if note.NoteId == "" { return info.Note{} @@ -354,10 +357,15 @@ func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId note.IsTop = false note.IsBlog = false // 别人的可能是blog + note.ImgSrc = "" // 为什么清空, 因为图片需要复制, 先清空 + // content noteContent.NoteId = note.NoteId noteContent.UserId = note.UserId + // 复制图片, 把note的图片都copy给我, 且修改noteContent图片路径 + noteContent.Content = noteImageService.CopyNoteImages(noteId, fromUserId, note.NoteId.Hex(), noteContent.Content, myUserId) + // 添加之 note = this.AddNoteAndContent(note, noteContent, note.UserId); @@ -375,8 +383,10 @@ func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId // shareService call // [ok] func (this *NoteService) GetNotebookId(noteId string) bson.ObjectId { - note := &info.Note{} - db.Get(db.Notes, noteId, note) + note := info.Note{} + // db.Get(db.Notes, noteId, ¬e) + // LogJ(note) + db.GetByQWithFields(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, []string{"NotebookId"}, ¬e) return note.NotebookId } diff --git a/app/service/ShareService.go b/app/service/ShareService.go index f13a648..1e13c35 100644 --- a/app/service/ShareService.go +++ b/app/service/ShareService.go @@ -3,7 +3,7 @@ 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" "sort" @@ -338,10 +338,12 @@ func (this *ShareService) HasSharedNote(noteId, myUserId string) bool { return db.Has(db.ShareNotes, bson.M{"ToUserId": bson.ObjectIdHex(myUserId), "NoteId": bson.ObjectIdHex(noteId)}) } // noteId的notebook是否共享了给我 -func (this *ShareService) hasSharedNotebook(noteId, myUserId, sharedUserId string) bool { - note := noteService.GetNote(noteId, sharedUserId) - if note.NoteId != "" { - return db.Has(db.ShareNotebooks, bson.M{"NotebookId": note.NotebookId, +func (this *ShareService) HasSharedNotebook(noteId, myUserId, sharedUserId string) bool { + notebookId := noteService.GetNotebookId(noteId) + Log(noteId) + Log(notebookId) + if notebookId != "" { + return db.Has(db.ShareNotebooks, bson.M{"NotebookId": notebookId, "UserId": bson.ObjectIdHex(sharedUserId), "ToUserId": bson.ObjectIdHex(myUserId), }) @@ -355,7 +357,7 @@ func (this *ShareService) GetShareNoteContent(noteId, myUserId, sharedUserId str noteContent = info.NoteContent{} // 是否单独共享了该notebook // 或者, 其notebook共享了我 - if this.HasSharedNote(noteId, myUserId) || this.hasSharedNotebook(noteId, myUserId, sharedUserId) { + if this.HasSharedNote(noteId, myUserId) || this.HasSharedNotebook(noteId, myUserId, sharedUserId) { db.Get(db.NoteContents, noteId, ¬eContent) } else { } From ab242b10f21c97d728de1b11523b52457378e3d1 Mon Sep 17 00:00:00 2001 From: life Date: Sun, 21 Sep 2014 00:29:46 +0800 Subject: [PATCH 4/7] paste image when edit/add shared note --- app/controllers/FileController.go | 32 ++++++++++++++++--- .../plugins/paste/classes/Clipboard.js | 11 ++++--- public/tinymce/plugins/paste/plugin.js | 11 ++++--- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/app/controllers/FileController.go b/app/controllers/FileController.go index 82fe1ab..3d172b6 100644 --- a/app/controllers/FileController.go +++ b/app/controllers/FileController.go @@ -18,7 +18,7 @@ type File struct { } // 上传图片 editor -// 过时 +// 过时 已弃用! func (c File) UploadImage(renderHtml string) revel.Result { if renderHtml == "" { renderHtml = "file/image.html" @@ -39,12 +39,36 @@ func (c File) UploadBlogLogo() revel.Result { return c.UploadImage("file/blog_logo.html"); } -// 拖拉上传, pasteImage -func (c File) UploadImageJson(renderHtml, from string) revel.Result { +// 弃用 +func (c File) UploadImageJson(from, noteId string) revel.Result { re := c.uploadImage(from, ""); return c.RenderJson(re) } +// 拖拉上传, pasteImage +// noteId 是为了判断是否是协作的note, 如果是则需要复制一份到note owner中 +func (c File) PasteImage(noteId string) revel.Result { + re := c.uploadImage("pasteImage", ""); + + userId := c.GetUserId() + note := noteService.GetNoteById(noteId) + if note.UserId != "" { + noteUserId := note.UserId.Hex() + if noteUserId != userId { + // 是否是有权限协作的 + if shareService.HasUpdatePerm(noteUserId, userId, noteId) { + // 复制图片之, 图片复制给noteUserId + _, re.Id = fileService.CopyImage(userId, re.Id, noteUserId) + } else { + // 怎么可能在这个笔记下paste图片呢? + // 正常情况下不会 + } + } + } + + return c.RenderJson(re) +} + // leaui image plugin func (c File) UploadImageLeaui(albumId string) revel.Result { re := c.uploadImage("", albumId); @@ -60,7 +84,7 @@ func (c File) uploadImage(from, albumId string) (re info.Re) { var Ok = false defer func() { - re.Id = fileId + re.Id = fileId // 只是id, 没有其它信息 re.Code = resultCode re.Msg = resultMsg re.Ok = Ok diff --git a/public/tinymce/plugins/paste/classes/Clipboard.js b/public/tinymce/plugins/paste/classes/Clipboard.js index bfb5064..2a50326 100644 --- a/public/tinymce/plugins/paste/classes/Clipboard.js +++ b/public/tinymce/plugins/paste/classes/Clipboard.js @@ -287,6 +287,7 @@ define("tinymce/pasteplugin/Clipboard", [ var c = new FormData; c.append("from", "pasteImage"); c.append("file", blob); + c.append("noteId", Note.curNoteId); // life // var d; // d = $.ajaxSettings.xhr(); // d.withCredentials = i;var d = {}; @@ -296,10 +297,10 @@ define("tinymce/pasteplugin/Clipboard", [ var dom = editor.dom; var d = {}; d.id = '__mcenew'; - d.src = "http://leanote.com/images/loading-24.gif"; + d.src = "http://leanote.com/images/loading-24.gif"; // 写死了 editor.insertContent(dom.createHTML('img', d)); var imgElm = dom.get('__mcenew'); - $.ajax({url: "/file/uploadImageJson", contentType:false, processData:false , data: c, type: "POST"} + $.ajax({url: "/file/pasteImage", contentType:false, processData:false , data: c, type: "POST"} ).done(function(re) { if(!re || typeof re != "object" || !re.Ok) { // 删除 @@ -307,7 +308,9 @@ define("tinymce/pasteplugin/Clipboard", [ return; } // 这里, 如果图片宽度过大, 这里设置成500px - getImageSize(re.Id, function(wh) { + var urlPrefix = window.location.protocol + "//" + window.location.host; + var src = urlPrefix + "/file/outputImage?fileId=" + re.Id; + getImageSize(src, function(wh) { // life 4/25 if(wh && wh.width) { if(wh.width > 600) { @@ -316,7 +319,7 @@ define("tinymce/pasteplugin/Clipboard", [ d.width = wh.width; dom.setAttrib(imgElm, 'width', d.width); } - dom.setAttrib(imgElm, 'src', re.Id); + dom.setAttrib(imgElm, 'src', src); }); dom.setAttrib(imgElm, 'id', null); }); diff --git a/public/tinymce/plugins/paste/plugin.js b/public/tinymce/plugins/paste/plugin.js index 83f5b81..9ffab7a 100644 --- a/public/tinymce/plugins/paste/plugin.js +++ b/public/tinymce/plugins/paste/plugin.js @@ -472,6 +472,7 @@ define("tinymce/pasteplugin/Clipboard", [ var c = new FormData; c.append("from", "pasteImage"); c.append("file", blob); + c.append("noteId", Note.curNoteId); // var d; // d = $.ajaxSettings.xhr(); // d.withCredentials = i;var d = {}; @@ -481,10 +482,10 @@ define("tinymce/pasteplugin/Clipboard", [ var dom = editor.dom; var d = {}; d.id = '__mcenew'; - d.src = "http://leanote.com/images/loading-24.gif"; + d.src = "http://leanote.com/images/loading-24.gif"; // 写死了 editor.insertContent(dom.createHTML('img', d)); var imgElm = dom.get('__mcenew'); - $.ajax({url: "/file/uploadImageJson", contentType:false, processData:false , data: c, type: "POST"} + $.ajax({url: "/file/pasteImage", contentType:false, processData:false , data: c, type: "POST"} ).done(function(re) { if(!re || typeof re != "object" || !re.Ok) { // 删除 @@ -492,7 +493,9 @@ define("tinymce/pasteplugin/Clipboard", [ return; } // 这里, 如果图片宽度过大, 这里设置成500px - getImageSize(re.Id, function(wh) { + var urlPrefix = window.location.protocol + "//" + window.location.host; + var src = urlPrefix + "/file/outputImage?fileId=" + re.Id; + getImageSize(src, function(wh) { // life 4/25 if(wh && wh.width) { if(wh.width > 600) { @@ -501,7 +504,7 @@ define("tinymce/pasteplugin/Clipboard", [ d.width = wh.width; dom.setAttrib(imgElm, 'width', d.width); } - dom.setAttrib(imgElm, 'src', re.Id); + dom.setAttrib(imgElm, 'src', src); }); dom.setAttrib(imgElm, 'id', null); }); From c556ab59b5d014d6d82226fe534ee5d22fcae5f9 Mon Sep 17 00:00:00 2001 From: life Date: Sun, 21 Sep 2014 22:05:04 +0800 Subject: [PATCH 5/7] attachment feature #10 --- app/controllers/AttachController.go | 212 ++++++++++++++++++ app/controllers/FileController.go | 17 +- app/controllers/init.go | 1 + app/db/Mgo.go | 2 + app/info/AttachInfo.go | 21 ++ app/info/NoteInfo.go | 2 +- app/service/AttachService.go | 175 +++++++++++++++ app/service/FileService.go | 1 - app/service/NoteService.go | 3 + app/service/ShareService.go | 73 +++++- app/service/TrashService.go | 10 +- app/service/init.go | 2 + app/views/Note/note-dev.html | 46 +++- messages/msg.en | 3 + messages/msg.zh | 3 + public/css/theme/basic.less | 104 ++++++++- public/css/theme/default.css | 132 ++++++++--- public/css/theme/default.less | 39 ---- public/css/theme/simple.css | 2 +- public/css/theme/simple.less | 37 --- public/css/theme/writting-overwrite.css | 2 +- public/css/theme/writting.css | 2 +- public/js/app/attachment_upload.js | 134 +++++++++++ public/js/app/note.js | 210 +++++++++++++++++ public/js/common.js | 5 + .../editor/pagedown/Markdown.Editor.js | 94 ++++++++ 26 files changed, 1197 insertions(+), 135 deletions(-) create mode 100644 app/controllers/AttachController.go create mode 100644 app/info/AttachInfo.go create mode 100644 app/service/AttachService.go create mode 100644 public/js/app/attachment_upload.js diff --git a/app/controllers/AttachController.go b/app/controllers/AttachController.go new file mode 100644 index 0000000..d238c98 --- /dev/null +++ b/app/controllers/AttachController.go @@ -0,0 +1,212 @@ +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" + "io/ioutil" + "os" + "strings" + "time" + "io" + "archive/tar" + "compress/gzip" +) + +// 附件 +type Attach struct { + BaseController +} + +// 上传附件 +func (c Attach) UploadAttach(noteId string) revel.Result { + re := c.uploadAttach(noteId) + return c.RenderJson(re) +} +func (c Attach) uploadAttach(noteId string) (re info.Re) { + var fileId = "" + var resultMsg = "error" // 错误信息 + var Ok = false + var fileInfo info.Attach + + re = info.NewRe() + + defer func() { + re.Id = fileId // 只是id, 没有其它信息 + re.Msg = resultMsg + re.Ok = Ok + re.Item = fileInfo + }() + + // 判断是否有权限为笔记添加附件 + if !shareService.HasUpdateNotePerm(noteId, c.GetUserId()) { + return re + } + + file, handel, err := c.Request.FormFile("file") + if err != nil { + return re + } + defer file.Close() + + data, err := ioutil.ReadAll(file) + if err != nil { + return re + } + // > 5M? + if(len(data) > 5 * 1024 * 1024) { + resultMsg = "File is bigger than 5M" + return re + } + + // 生成上传路径 + filePath := "files/" + c.GetUserId() + "/attachs" + dir := revel.BasePath + "/" + filePath + err = os.MkdirAll(dir, 0755) + if err != nil { + return re + } + // 生成新的文件名 + filename := handel.Filename + _, ext := SplitFilename(filename) // .doc + filename = NewGuid() + ext + toPath := dir + "/" + filename; + err = ioutil.WriteFile(toPath, data, 0777) + if err != nil { + return re + } + + // add File to db + fileType := "" + if ext != "" { + fileType = strings.ToLower(ext[1:]) + } + filesize := GetFilesize(toPath) + fileInfo = info.Attach{Name: filename, + Title: handel.Filename, + NoteId: bson.ObjectIdHex(noteId), + UploadUserId: c.GetObjectUserId(), + Path: filePath + "/" + filename, + Type: fileType, + Size: filesize} + + id := bson.NewObjectId(); + fileInfo.AttachId = id + fileId = id.Hex() + Ok = attachService.AddAttach(fileInfo) + + fileInfo.Path = ""; // 不要返回 + resultMsg = "success" + + return re +} + +// 删除附件 +func (c Attach) DeleteAttach(attachId string) revel.Result { + re := info.NewRe() + re.Ok, re.Msg = attachService.DeleteAttach(attachId, c.GetUserId()) + return c.RenderJson(re) +} + +// get all attachs by noteId +func (c Attach) GetAttachs(noteId string) revel.Result { + re := info.NewRe() + re.Ok = true + re.List = attachService.ListAttachs(noteId, c.GetUserId()) + return c.RenderJson(re) +} + +// 下载附件 +// 权限判断 +func (c Attach) Download(attachId string) revel.Result { + attach := attachService.GetAttach(attachId, c.GetUserId()); // 得到路径 + path := attach.Path + if path == "" { + return c.RenderText("") + } + fn := revel.BasePath + "/" + strings.TrimLeft(path, "/") + file, _ := os.Open(fn) + return c.RenderBinary(file, attach.Title, revel.Attachment, time.Now()) // revel.Attachment + // return c.RenderFile(file, revel.Attachment) // revel.Attachment +} + +func (c Attach) DownloadAll(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 +} + + diff --git a/app/controllers/FileController.go b/app/controllers/FileController.go index 3d172b6..91cdb6b 100644 --- a/app/controllers/FileController.go +++ b/app/controllers/FileController.go @@ -17,7 +17,6 @@ type File struct { BaseController } -// 上传图片 editor // 过时 已弃用! func (c File) UploadImage(renderHtml string) revel.Result { if renderHtml == "" { @@ -33,18 +32,19 @@ func (c File) UploadImage(renderHtml string) revel.Result { return c.RenderTemplate(renderHtml) } +// 已弃用 +func (c File) UploadImageJson(from, noteId string) revel.Result { + re := c.uploadImage(from, ""); + return c.RenderJson(re) +} + + // 上传的是博客logo // TODO logo不要设置权限, 另外的目录 func (c File) UploadBlogLogo() revel.Result { return c.UploadImage("file/blog_logo.html"); } -// 弃用 -func (c File) UploadImageJson(from, noteId string) revel.Result { - re := c.uploadImage(from, ""); - return c.RenderJson(re) -} - // 拖拉上传, pasteImage // noteId 是为了判断是否是协作的note, 如果是则需要复制一份到note owner中 func (c File) PasteImage(noteId string) revel.Result { @@ -69,13 +69,14 @@ func (c File) PasteImage(noteId string) revel.Result { return c.RenderJson(re) } -// leaui image plugin +// leaui image plugin upload image func (c File) UploadImageLeaui(albumId string) revel.Result { re := c.uploadImage("", albumId); return c.RenderJson(re) } // 上传图片, 公用方法 +// upload image common func func (c File) uploadImage(from, albumId string) (re info.Re) { var fileUrlPath = "" var fileId = "" diff --git a/app/controllers/init.go b/app/controllers/init.go index 9e99661..56fb798 100644 --- a/app/controllers/init.go +++ b/app/controllers/init.go @@ -25,6 +25,7 @@ var suggestionService *service.SuggestionService var albumService *service.AlbumService var fileService *service.FileService +var attachService *service.AttachService var pageSize = 1000 var defaultSortField = "UpdatedTime" diff --git a/app/db/Mgo.go b/app/db/Mgo.go index e68ca3a..6f91f8b 100644 --- a/app/db/Mgo.go +++ b/app/db/Mgo.go @@ -36,6 +36,7 @@ var Suggestions *mgo.Collection // Album & file(image) var Albums *mgo.Collection var Files *mgo.Collection +var Attachs *mgo.Collection var NoteImages *mgo.Collection @@ -106,6 +107,7 @@ func Init() { // Album & file Albums = Session.DB(dbname).C("albums") Files = Session.DB(dbname).C("files") + Attachs = Session.DB(dbname).C("attachs") NoteImages = Session.DB(dbname).C("note_images") } diff --git a/app/info/AttachInfo.go b/app/info/AttachInfo.go new file mode 100644 index 0000000..4dc39ef --- /dev/null +++ b/app/info/AttachInfo.go @@ -0,0 +1,21 @@ +package info + +import ( + "gopkg.in/mgo.v2/bson" + "time" +) + +// Attach belongs to note +type Attach struct { + AttachId bson.ObjectId `bson:"_id,omitempty"` // + NoteId bson.ObjectId `bson:"NoteId"` // + UploadUserId bson.ObjectId `bson:"UploadUserId"` // 可以不是note owner, 协作者userId + Name string `Name` // file name, md5, such as 13232312.doc + Title string `Title` // raw file name + Size int64 `Size` // file size (byte) + Type string `Type` // file type, "doc" = word + Path string `Path` // the file path such as: files/userId/attachs/adfadf.doc + CreatedTime time.Time `CreatedTime` + + // FromFileId bson.ObjectId `bson:"FromFileId,omitempty"` // copy from fileId, for collaboration +} diff --git a/app/info/NoteInfo.go b/app/info/NoteInfo.go index 103e2df..9ba45fd 100644 --- a/app/info/NoteInfo.go +++ b/app/info/NoteInfo.go @@ -24,7 +24,7 @@ type Note struct { IsMarkdown bool `IsMarkdown` // 是否是markdown笔记, 默认是false - AttachIds []string `FileIds,omitempty` // 2014/9/18, attachments + AttachNum int `AttachNum` // 2014/9/21, attachments num CreatedTime time.Time `CreatedTime` UpdatedTime time.Time `UpdatedTime` diff --git a/app/service/AttachService.go b/app/service/AttachService.go new file mode 100644 index 0000000..521558d --- /dev/null +++ b/app/service/AttachService.go @@ -0,0 +1,175 @@ +package service + +import ( + . "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" +) + +type AttachService struct { +} + +// add attach +func (this *AttachService) AddAttach(attach info.Attach) bool { + attach.CreatedTime = time.Now() + ok := db.Insert(db.Attachs, attach) + + if ok { + // 更新笔记的attachs num + this.updateNoteAttachNum(attach.NoteId, 1) + } + + return ok +} + +// 更新笔记的附件个数 +// addNum 1或-1 +func (this *AttachService) updateNoteAttachNum(noteId bson.ObjectId, addNum int) bool { + num := db.Count(db.Attachs, bson.M{"NoteId": noteId}) + /* + note := info.Note{} + note = noteService.GetNoteById(noteId.Hex()) + note.AttachNum += addNum + if note.AttachNum < 0 { + note.AttachNum = 0 + } + Log(note.AttachNum) + */ + return db.UpdateByQField(db.Notes, bson.M{"_id": noteId}, "AttachNum", num) +} + +// list attachs +func (this *AttachService) ListAttachs(noteId, userId string) []info.Attach { + attachs := []info.Attach{} + // 判断是否有权限为笔记添加附件 + if !shareService.HasUpdateNotePerm(noteId, userId) { + return attachs + } + + db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs) + + return attachs +} + +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) + if note.UserId.Hex() == userId { + attachs := []info.Attach{} + db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs) + for _, attach := range attachs { + attach.Path = strings.TrimLeft(attach.Path, "/") + os.Remove(revel.BasePath + "/" + attach.Path) + } + return true + } + + return false +} + +// delete attach +func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string) { + attach := info.Attach{} + db.Get(db.Attachs, attachId, &attach) + + if(attach.AttachId != "") { + // 判断是否有权限为笔记添加附件 + if !shareService.HasUpdateNotePerm(attach.NoteId.Hex(), userId) { + return false, "No Perm" + } + + if db.Delete(db.Attachs, bson.M{"_id": bson.ObjectIdHex(attachId)}) { + this.updateNoteAttachNum(attach.NoteId, -1) + attach.Path = strings.TrimLeft(attach.Path, "/") + err := os.Remove(revel.BasePath + "/" + attach.Path) + if err == nil { + return true, "delete file error" + } + return false, "delete file error" + } + return false, "db error" + } + return false, "no such item" +} + +// 获取文件路径 +// 要判断是否具有权限 +// userId是否具有attach的访问权限 +func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attach) { + if attachId == "" { + return + } + + attach = info.Attach{} + db.Get(db.Attachs, attachId, &attach) + path := attach.Path + if path == "" { + return + } + + note := noteService.GetNoteById(attach.NoteId.Hex()) + + // 判断权限 + + // 笔记是否是公开的 + if note.IsBlog { + return + } + + // 笔记是否是我的 + if note.UserId.Hex() == userId { + return + } + + // 我是否有权限查看或协作 + if shareService.HasReadNotePerm(attach.NoteId.Hex(), userId) { + return + } + + attach = info.Attach{} + return +} + +// 复制笔记时需要复制附件 +// noteService调用, 权限已判断 +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) + if err != nil { + return false + } + _, err = CopyFile(revel.BasePath + "/" + attach.Path, revel.BasePath + "/" + filePath) + if err != nil { + return false + } + attach.Name = newFilename + attach.Path = filePath + + this.AddAttach(attach) + } + + return true +} \ No newline at end of file diff --git a/app/service/FileService.go b/app/service/FileService.go index 37bd4f9..8a8cfc3 100644 --- a/app/service/FileService.go +++ b/app/service/FileService.go @@ -189,7 +189,6 @@ func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, strin if file.FileId == "" || file.UserId.Hex() != userId { return false, "" } - Log(file) _, ext := SplitFilename(file.Name) newFilename := NewGuid() + ext diff --git a/app/service/NoteService.go b/app/service/NoteService.go index 8f25aad..7fc2504 100644 --- a/app/service/NoteService.go +++ b/app/service/NoteService.go @@ -366,6 +366,9 @@ func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId // 复制图片, 把note的图片都copy给我, 且修改noteContent图片路径 noteContent.Content = noteImageService.CopyNoteImages(noteId, fromUserId, note.NoteId.Hex(), noteContent.Content, myUserId) + // 复制附件 + attachService.CopyAttachs(noteId, note.NoteId.Hex(), myUserId) + // 添加之 note = this.AddNoteAndContent(note, noteContent, note.UserId); diff --git a/app/service/ShareService.go b/app/service/ShareService.go index 1e13c35..ebad5b0 100644 --- a/app/service/ShareService.go +++ b/app/service/ShareService.go @@ -3,7 +3,7 @@ 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" "sort" @@ -286,6 +286,28 @@ func (this *ShareService) AddShareNote(noteId string, perm int, userId, email st return db.Insert(db.ShareNotes, shareNote), "", toUserId } +// updatedUserId是否有查看userId noteId的权限? +func (this *ShareService) HasReadPerm(userId, updatedUserId, noteId string) bool { + if !db.Has(db.ShareNotes, + bson.M{"UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(updatedUserId), "NoteId": bson.ObjectIdHex(noteId)}) { + // noteId的notebookId是否被共享了? + notebookId := noteService.GetNotebookId(noteId) + if notebookId.Hex() == "" { + return false + } + + // 判断notebook是否被共享 + if !db.Has(db.ShareNotebooks, + bson.M{"UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(updatedUserId), "NotebookId": notebookId}) { + return false + } else { + return true + } + } else { + return true + } +} + // updatedUserId是否有修改userId noteId的权限? func (this *ShareService) HasUpdatePerm(userId, updatedUserId, noteId string) bool { // 1. noteId是否被共享了? @@ -340,8 +362,6 @@ func (this *ShareService) HasSharedNote(noteId, myUserId string) bool { // noteId的notebook是否共享了给我 func (this *ShareService) HasSharedNotebook(noteId, myUserId, sharedUserId string) bool { notebookId := noteService.GetNotebookId(noteId) - Log(noteId) - Log(notebookId) if notebookId != "" { return db.Has(db.ShareNotebooks, bson.M{"NotebookId": notebookId, "UserId": bson.ObjectIdHex(sharedUserId), @@ -509,4 +529,51 @@ func (this *ShareService) DeleteUserShareNoteAndNotebook(userId, toUserId string db.DeleteAll(db.HasShareNotes, query); return true +} + +// 用户userId是否有修改noteId的权限 +func (this *ShareService) HasUpdateNotePerm(noteId, userId string) bool { + if noteId == "" || userId == "" { + return false; + } + note := noteService.GetNoteById(noteId) + LogJ(note); + if note.UserId != "" { + noteUserId := note.UserId.Hex() + if noteUserId != userId { + // 是否是有权限协作的 + if this.HasUpdatePerm(noteUserId, userId, noteId) { + return true + } else { + return false; + } + } else { + return true + } + } else { + return false; + } +} + +// 用户userId是否有修改noteId的权限 +func (this *ShareService) HasReadNotePerm(noteId, userId string) bool { + if noteId == "" || userId == "" { + return false; + } + note := noteService.GetNoteById(noteId) + if note.UserId != "" { + noteUserId := note.UserId.Hex() + if noteUserId != userId { + // 是否是有权限协作的 + if this.HasReadPerm(noteUserId, userId, noteId) { + return true + } else { + return false; + } + } else { + return true + } + } else { + return false; + } } \ No newline at end of file diff --git a/app/service/TrashService.go b/app/service/TrashService.go index 23b972a..014e6ac 100644 --- a/app/service/TrashService.go +++ b/app/service/TrashService.go @@ -53,7 +53,15 @@ func (this *TrashService) recoverNote(noteId, notebookId, userId string) bool { // 删除trash func (this *TrashService) DeleteTrash(noteId, userId string) bool { - return db.DeleteByIdAndUserId(db.Notes, noteId, userId) + // delete note's attachs + ok := attachService.DeleteAllAttachs(noteId, userId) + + // delete note + ok = db.DeleteByIdAndUserId(db.Notes, noteId, userId) + // delete content + ok = db.DeleteByIdAndUserId(db.NoteContents, noteId, userId) + + return ok } // 列出note, 排序规则, 还有分页 diff --git a/app/service/init.go b/app/service/init.go index 985e938..d8bf943 100644 --- a/app/service/init.go +++ b/app/service/init.go @@ -19,6 +19,7 @@ var blogService *BlogService var tokenService *TokenService var noteImageService *NoteImageService var fileService *FileService +var attachService *AttachService func init() { notebookService = &NotebookService{} @@ -31,5 +32,6 @@ func init() { blogService = &BlogService{} tokenService = &TokenService{} fileService = &FileService{} + attachService = &AttachService{} noteImageService = &NoteImageService{} } \ No newline at end of file diff --git a/app/views/Note/note-dev.html b/app/views/Note/note-dev.html index 520221f..171a4a1 100644 --- a/app/views/Note/note-dev.html +++ b/app/views/Note/note-dev.html @@ -353,6 +353,7 @@ function log(o) { Sort --> + @@ -466,6 +468,38 @@ function log(o) {