This commit is contained in:
life committed 2014-05-07 13:06:24 +08:00
1 parent fac05a7b6c
commit 476ade10e7
1085 files changed
+259628

No files matched your search

+130
View File
@@ -0,0 +1,130 @@
package controllers
import (
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
)
// 用户登录/注销/找回密码
type Auth struct {
BaseController
}
//--------
// 登录
func (c Auth) Login(email string) revel.Result {
c.RenderArgs["title"] = c.Message("login")
c.RenderArgs["subTitle"] = c.Message("login")
c.RenderArgs["email"] = email
if c.Has("demo") {
c.RenderArgs["demo"] = true
c.RenderArgs["email"] = "demo@leanote.com"
}
return c.RenderTemplate("home/login.html")
}
func (c Auth) DoLogin(email, pwd string) revel.Result {
userInfo := authService.Login(email, pwd)
if userInfo.Email != "" {
c.SetSession(userInfo)
// 必须要redirect, 不然用户刷新会重复提交登录信息
// return c.Redirect("/")
return c.RenderJson(info.Re{Ok: true})
}
// return c.RenderTemplate("login.html")
return c.RenderJson(info.Re{Ok: false, Msg: c.Message("wrongUsernameOrPassword")})
}
// 注销
func (c Auth) Logout() revel.Result {
c.ClearSession()
return c.Redirect("login")
}
// 体验一下
func (c Auth) Demo() revel.Result {
c.DoLogin("demo@leanote.com", "demo@leanote.com")
return c.Redirect("/note")
}
//--------
// 注册
func (c Auth) Register() revel.Result {
c.RenderArgs["title"] = c.Message("register")
c.RenderArgs["subTitle"] = c.Message("register")
return c.RenderTemplate("home/register.html")
}
func (c Auth) DoRegister(email, pwd string) revel.Result {
re := info.NewRe();
if email == "" {
re.Msg = c.Message("inputEmail")
return c.RenderJson(re)
} else if !IsEmail(email) {
re.Msg = c.Message("wrongEmail")
return c.RenderJson(re)
}
// 密码
if pwd == "" {
re.Msg = c.Message("inputPassword")
return c.RenderJson(re)
} else if len(pwd) < 6 {
re.Msg = c.Message("wrongPassword")
return c.RenderJson(re)
}
// 注册
re.Ok, re.Msg = authService.Register(email, pwd)
// 注册成功, 则立即登录之
if re.Ok {
c.DoLogin(email, pwd)
}
return c.RenderJson(re)
}
//--------
// 找回密码
func (c Auth) FindPassword() revel.Result {
c.RenderArgs["title"] = c.Message("findPassword")
c.RenderArgs["subTitle"] = c.Message("findPassword")
return c.RenderTemplate("home/find_password.html")
}
func (c Auth) DoFindPassword(email string) revel.Result {
pwdService.FindPwd(email)
re := info.NewRe()
re.Ok = true
return c.RenderJson(re)
}
// 点击链接后, 先验证之
func (c Auth) FindPassword2(token string) revel.Result {
c.RenderArgs["title"] = c.Message("findPassword")
c.RenderArgs["subTitle"] = c.Message("findPassword")
if token == "" {
return c.RenderTemplate("find_password2_timeout.html")
}
ok, _, findPwd := tokenService.VerifyToken(token, info.TokenPwd);
if !ok {
return c.RenderTemplate("home/find_password2_timeout.html")
}
c.RenderArgs["findPwd"] = findPwd
c.RenderArgs["title"] = c.Message("updatePassword")
c.RenderArgs["subTitle"] = c.Message("updatePassword")
return c.RenderTemplate("home/find_password2.html")
}
// 找回密码修改密码
func (c Auth) FindPasswordUpdate(token, pwd string) revel.Result {
re := info.NewRe();
re.Ok, re.Msg = IsGoodPwd(pwd)
if !re.Ok {
return c.RenderJson(re)
}
// 修改之
re.Ok, re.Msg = pwdService.UpdatePwd(token, pwd)
return c.RenderJson(re)
}
+174
View File
@@ -0,0 +1,174 @@
package controllers
import (
"github.com/revel/revel"
"labix.org/v2/mgo/bson"
"encoding/json"
"github.com/leanote/leanote/app/info"
// "io/ioutil"
// "fmt"
"math"
"strconv"
"strings"
)
// 公用Controller, 其它Controller继承它
type BaseController struct {
*revel.Controller
}
func (c BaseController) GetUserId() string {
if userId, ok := c.Session["UserId"]; ok {
return userId
}
return ""
}
// 是否已登录
func (c BaseController) HasLogined() bool {
return c.GetUserId() != ""
}
func (c BaseController) GetObjectUserId() bson.ObjectId {
userId := c.GetUserId()
if userId != "" {
return bson.ObjectIdHex(userId)
}
return ""
}
func (c BaseController) GetEmail() string {
if email, ok := c.Session["Email"]; ok {
return email
}
return ""
}
func (c BaseController) GetUsername() string {
if email, ok := c.Session["Username"]; ok {
return email
}
return ""
}
// 得到用户信息
func (c BaseController) GetUserInfo() info.User {
if userId, ok := c.Session["UserId"]; ok && userId != "" {
notebookWidth, _ := strconv.Atoi(c.Session["NotebookWidth"])
noteListWidth, _ := strconv.Atoi(c.Session["NoteListWidth"])
user := info.User{UserId: bson.ObjectIdHex(userId),
Email: c.Session["Email"],
Username: c.Session["Username"],
UsernameRaw: c.Session["UsernameRaw"],
Theme: c.Session["Theme"],
NotebookWidth: notebookWidth,
NoteListWidth: noteListWidth,
}
if c.Session["Verified"] == "1" {
user.Verified = true
}
if c.Session["LeftIsMin"] == "1" {
user.LeftIsMin = true
}
return user
}
return info.User{}
}
func (c BaseController) SetSession(userInfo info.User) {
if userInfo.UserId.Hex() != "" {
c.Session["UserId"] = userInfo.UserId.Hex()
c.Session["Email"] = userInfo.Email
c.Session["Username"] = userInfo.Username
c.Session["UsernameRaw"] = userInfo.UsernameRaw
c.Session["Theme"] = userInfo.Theme
c.Session["NotebookWidth"] = strconv.Itoa(userInfo.NotebookWidth)
c.Session["NoteListWidth"] = strconv.Itoa(userInfo.NoteListWidth)
if userInfo.Verified {
c.Session["Verified"] = "1"
} else {
c.Session["Verified"] = "0"
}
if userInfo.LeftIsMin {
c.Session["LeftIsMin"] = "1"
} else {
c.Session["LeftIsMin"] = "0"
}
}
}
func (c BaseController) ClearSession() {
delete(c.Session, "UserId")
delete(c.Session, "Email")
delete(c.Session, "Username")
delete(c.Session, "theme")
}
// 修改session
func (c BaseController) UpdateSession(key, value string) {
c.Session[key] = value
}
// 返回json
func (c BaseController) Json(i interface{}) string {
// b, _ := json.MarshalIndent(i, "", " ")
b, _ := json.Marshal(i)
return string(b)
}
// 得到第几页
func (c BaseController) GetPage() int {
page := 0
c.Params.Bind(&page, "page")
if page == 0 {
return 1
}
return page
}
// 判断是否含有某参数
func (c BaseController) Has(key string) bool {
if _, ok := c.Params.Values[key]; ok {
return true;
}
return false;
}
/*
func (c Blog) GetPage(page, count int, list interface{}) info.Page {
return info.Page{page, int(math.Ceil(float64(count)/float64(page))), list}
}
*/
func (c BaseController) GetTotalPage(page, count int) int {
return int(math.Ceil(float64(count)/float64(page)))
}
//-------------
func (c BaseController) E404() revel.Result {
c.RenderArgs["title"] = "404";
return c.NotFound("", nil)
}
// 设置本地
func (c BaseController) SetLocale() {
locale := string(c.Request.Locale) // zh-CN
lang := locale
if strings.Contains(locale, "-") {
pos := strings.Index(locale, "-")
lang = locale[0:pos]
}
if lang != "zh" && lang != "en" {
lang = "en";
}
c.RenderArgs["locale"] = lang;
}
// 设置userInfo
func (c BaseController) SetUserInfo() {
userInfo := c.GetUserInfo()
c.RenderArgs["userInfo"] = userInfo
}
+235
View File
@@ -0,0 +1,235 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"labix.org/v2/mgo/bson"
// . "leanote/app/lea"
"github.com/leanote/leanote/app/info"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "math"
// "os"
// "path"
)
type Blog struct {
BaseController
}
//---------------------------
// 后台 note<->blog
// 设置/取消Blog; 置顶
func (c Blog) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
if isTop {
isBlog = true
}
if !isBlog {
isTop = false
}
noteUpdate := bson.M{"IsBlog": isBlog, "IsTop": isTop}
re := noteService.UpdateNote(c.GetUserId(), c.GetUserId(),
noteId, noteUpdate)
return c.RenderJson(re)
}
// 设置notebook <-> blog
func (c Blog) SetNotebook2Blog(notebookId string, isBlog bool) revel.Result {
noteUpdate := bson.M{"IsBlog": isBlog}
re := notebookService.UpdateNotebook(c.GetUserId(),
notebookId, noteUpdate)
return c.RenderJson(re)
}
//-----------------------------
// 前台
// 默认是admin用户的博客
// 列表
// 这里还需要得到其它博客配置信息...
// 配置信息可以放在users表中, 或添加一个user_options表(用户配置表)
var blogPageSize = 5
var searchBlogPageSize = 30
func (c Blog) Index(userId string, notebookId string) revel.Result {
if userId == "" {
userId = leanoteUserId
}
// userId可能是 username, email
userInfo := userService.GetUserInfoByAny(userId)
if userInfo.UserId == "" {
return c.E404()
}
userId = userInfo.UserId.Hex()
c.isMe(userId)
c.RenderArgs["userInfo"] = userInfo
// 得到博客设置信息
userBlog := blogService.GetUserBlog(userId)
c.RenderArgs["userBlog"] = userBlog
var notebook info.Notebook
if notebookId != "" {
notebook = notebookService.GetNotebook(notebookId, userId)
if !notebook.IsBlog {
return c.E404()
}
c.RenderArgs["title"] = userBlog.Title + " - 分类: " + notebook.Title
} else {
c.RenderArgs["title"] = userBlog.Title
}
// 分页的话, 需要分页信息, totalPage, curPage
page := c.GetPage()
count, blogs := blogService.ListBlogs(userId, notebookId, page, blogPageSize, "UpdatedTime", false)
c.RenderArgs["blogs"] = blogs
c.RenderArgs["page"] = page
c.RenderArgs["pageSize"] = blogPageSize
c.RenderArgs["count"] = count
// 当前notebook
c.RenderArgs["notebookId"] = notebookId
c.RenderArgs["notebook"] = notebook
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
if notebookId == "" {
c.RenderArgs["index"] = true
}
c.getRecentBlogs(userId)
return c.RenderTemplate("blog/index.html")
}
// 详情
func (c Blog) View(noteId string) revel.Result {
blog := blogService.GetBlog(noteId)
c.RenderArgs["blog"] = blog
userInfo := userService.GetUserInfo(blog.UserId.Hex())
c.RenderArgs["userInfo"] = userInfo
c.RenderArgs["title"] = blog.Title + " - " + userInfo.Email
userId := userInfo.UserId.Hex()
c.isMe(userId)
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
// 得到博客设置信息
c.RenderArgs["userBlog"] = blogService.GetUserBlog(userId)
c.getRecentBlogs(userId)
return c.RenderTemplate("blog/view.html")
}
// 搜索
func (c Blog) SearchBlog(userId, key string) revel.Result {
c.RenderArgs["title"] = "搜索 " + key
c.RenderArgs["key"] = key
userInfo := userService.GetUserInfoByAny(userId)
c.RenderArgs["userInfo"] = userInfo
userId = userInfo.UserId.Hex()
page := c.GetPage()
_, blogs := blogService.SearchBlog(key, userId, page, searchBlogPageSize, "UpdatedTime", false)
c.RenderArgs["blogs"] = blogs
c.RenderArgs["key"] = key
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
// 得到博客设置信息
c.RenderArgs["userBlog"] = blogService.GetUserBlog(userId)
c.getRecentBlogs(userId)
c.isMe(userId)
return c.RenderTemplate("blog/search.html")
}
// 博客设置
func (c Blog) Set() revel.Result {
userId := c.GetUserId()
userInfo := userService.GetUserInfo(userId)
c.RenderArgs["userInfo"] = userInfo
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
// 得到博客设置信息
c.RenderArgs["userBlog"] = blogService.GetUserBlog(userId)
c.RenderArgs["title"] = "博客设置"
c.RenderArgs["isMe"] = true
c.RenderArgs["set"] = true
c.getRecentBlogs(userId)
return c.RenderTemplate("blog/set.html")
}
func (c Blog) SetUserBlogBase(userBlog info.UserBlogBase) revel.Result {
re := info.NewRe()
re.Ok = blogService.UpdateUserBlogBase(c.GetUserId(), userBlog)
return c.RenderJson(re)
}
func (c Blog) SetUserBlogComment(userBlog info.UserBlogComment) revel.Result {
re := info.NewRe()
re.Ok = blogService.UpdateUserBlogComment(c.GetUserId(), userBlog)
return c.RenderJson(re)
}
func (c Blog) SetUserBlogStyle(userBlog info.UserBlogStyle) revel.Result {
re := info.NewRe()
re.Ok = blogService.UpdateUserBlogStyle(c.GetUserId(), userBlog)
return c.RenderJson(re)
}
// userId可能是其它的
func (c Blog) AboutMe(userId string) revel.Result {
userInfo := userService.GetUserInfoByAny(userId)
if userInfo.UserId == "" {
return c.E404()
}
userId = userInfo.UserId.Hex()
c.RenderArgs["userInfo"] = userInfo
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
c.RenderArgs["userBlog"] = blogService.GetUserBlog(userId)
c.RenderArgs["aboutMe"] = true
c.RenderArgs["title"] = "关于我"
c.isMe(userId)
c.getRecentBlogs(userId)
return c.RenderTemplate("blog/about_me.html")
}
// 当前的博客是否是我的
func (c Blog) isMe(userId string) {
c.RenderArgs["isMe"] = userId == c.GetUserId()
}
// 优化, 这里不要得到count
func (c Blog) getRecentBlogs(userId string) {
_, c.RenderArgs["recentBlogs"] = blogService.ListBlogs(userId, "", 1, 5, "UpdatedTime", false)
}
// 可以不要, 因为注册的时候已经把username设为email了
func (c Blog) setRenderUserInfo(userInfo info.User) {
if userInfo.Username == "" {
userInfo.Username = userInfo.Email
}
c.RenderArgs["userInfo"] = userInfo
}
+105
View File
@@ -0,0 +1,105 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
"io/ioutil"
"os"
)
// 首页
type File struct {
BaseController
}
// 上传图片 editor
func (c File) UploadImage(renderHtml string) revel.Result {
if renderHtml == "" {
renderHtml = "file/image.html"
}
re := c.uploadImage();
c.RenderArgs["fileUrlPath"] = "http://leanote.com" + re.Id
c.RenderArgs["resultCode"] = re.Code
c.RenderArgs["resultMsg"] = re.Msg
return c.RenderTemplate(renderHtml)
}
// 上传的是博客logo
func (c File) UploadBlogLogo() revel.Result {
return c.UploadImage("file/blog_logo.html");
}
// 拖拉上传
func (c File) UploadImageJson(renderHtml string) revel.Result {
re := c.uploadImage();
re.Id = "http://leanote.com" + re.Id
// re.Id = re.Id
return c.RenderJson(re)
}
// 上传图片, 公用方法
func (c File) uploadImage() (re info.Re) {
var fileUrlPath = ""
var resultCode = 0 // 1表示正常
var resultMsg = "内部错误" // 错误信息
defer func() {
re.Id = fileUrlPath
re.Code = resultCode
re.Msg = resultMsg
}()
file, handel, err := c.Request.FormFile("file")
if err != nil {
return re
}
defer file.Close()
// 生成上传路径
fileUrlPath = "/upload/" + c.GetUserId() + "/images"
dir := revel.BasePath + "/public/" + fileUrlPath
err = os.MkdirAll(dir, 0755)
if err != nil {
Log(err)
return re
}
// 生成新的文件名
filename := handel.Filename
_, ext := SplitFilename(filename)
if(ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg") {
resultMsg = "不是图片"
return re
}
filename = NewGuid() + ext
data, err := ioutil.ReadAll(file)
if err != nil {
LogJ(err)
return re
}
// > 2M?
if(len(data) > 5 * 1024 * 1024) {
resultCode = 0
resultMsg = "图片大于2M"
return re
}
toPath := dir + "/" + filename;
err = ioutil.WriteFile(toPath, data, 0777)
if err != nil {
LogJ(err)
return re
}
// 改变成gif图片
_, toPathGif := TransToGif(toPath, 0, true)
fileUrlPath += "/" + GetFilename(toPathGif)
resultCode = 1
resultMsg = "上传成功!"
return re
}
+35
View File
@@ -0,0 +1,35 @@
package controllers
import (
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
)
// 首页
type Index struct {
BaseController
}
// leanote展示页, 没有登录的, 或已登录明确要进该页的
func (c Index) Index() revel.Result {
c.SetUserInfo();
c.RenderArgs["title"] = "leanote";
c.SetLocale()
return c.RenderTemplate("home/index.html");
}
// 建议
func (c Index) Suggestion(addr, suggestion string) revel.Result {
re := info.NewRe()
re.Ok = suggestionService.AddSuggestion(info.Suggestion{Addr: addr, UserId: c.GetObjectUserId(), Suggestion: suggestion})
// 发给我
go func() {
SendToLeanote("建议", "建议", "UserId: " + c.GetUserId() + " <br /> Suggestions: " + suggestion)
}();
return c.RenderJson(re)
}
+42
View File
@@ -0,0 +1,42 @@
package controllers
import (
"github.com/revel/revel"
// "github.com/leanote/leanote/app/info"
)
// 首页
type Mobile struct {
BaseController
}
// leanote展示页, 没有登录的, 或已登录明确要进该页的
func (c Mobile) Index() revel.Result {
c.SetLocale()
userInfo := c.GetUserInfo()
userId := userInfo.UserId.Hex()
// 没有登录
if userId == "" {
return c.RenderTemplate("mobile/login.html")
}
// 已登录了, 那么得到所有信息
notebooks := notebookService.GetNotebooks(userId)
shareNotebooks, sharedUserInfos := shareService.GetShareNotebooks(userId)
c.RenderArgs["userInfo"] = userInfo
c.RenderArgs["userInfoJson"] = c.Json(userInfo)
c.RenderArgs["notebooks"] = c.Json(notebooks)
c.RenderArgs["shareNotebooks"] = c.Json(shareNotebooks)
c.RenderArgs["sharedUserInfos"] = c.Json(sharedUserInfos)
c.RenderArgs["tagsJson"] = c.Json(tagService.GetTags(c.GetUserId()))
return c.RenderTemplate("mobile/index.html");
}
func (c Mobile) Logout() revel.Result {
c.ClearSession()
return c.RenderTemplate("mobile/login.html");
}
@@ -0,0 +1,16 @@
package controllers
import (
"github.com/revel/revel"
)
type NoteContentHistory struct {
BaseController
}
// 得到list
func (c NoteContentHistory) ListHistories(noteId string) revel.Result {
histories := noteContentHistoryService.ListHistories(noteId, c.GetUserId())
return c.RenderJson(histories)
}
+245
View File
@@ -0,0 +1,245 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"labix.org/v2/mgo/bson"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/lea/html2image"
"github.com/leanote/leanote/app/info"
// "os"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
)
type Note struct {
BaseController
}
// 笔记首页, 判断是否已登录
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
// 否则, 转向登录页面
func (c Note) Index() revel.Result {
c.SetLocale()
userInfo := c.GetUserInfo()
userId := userInfo.UserId.Hex()
// 没有登录
if userId == "" {
return c.Redirect("/login")
}
// 已登录了, 那么得到所有信息
notebooks := notebookService.GetNotebooks(userId)
shareNotebooks, sharedUserInfos := shareService.GetShareNotebooks(userId)
// 还需要按时间排序(DESC)得到notes
notes := []info.Note{}
noteContent := info.NoteContent{}
if len(notebooks) > 0 {
// _, notes = noteService.ListNotes(c.GetUserId(), "", false, c.GetPage(), pageSize, defaultSortField, false, false);
// 变成最新
_, notes = noteService.ListNotes(c.GetUserId(), "", false, c.GetPage(), 50, defaultSortField, false, false);
if len(notes) > 0 {
noteContent = noteService.GetNoteContent(notes[0].NoteId.Hex(), userId)
}
}
// 当然, 还需要得到第一个notes的content
//...
c.RenderArgs["userInfo"] = userInfo
c.RenderArgs["userInfoJson"] = c.Json(userInfo)
c.RenderArgs["notebooks"] = c.Json(notebooks)
c.RenderArgs["shareNotebooks"] = c.Json(shareNotebooks)
c.RenderArgs["sharedUserInfos"] = c.Json(sharedUserInfos)
c.RenderArgs["notes"] = c.Json(notes)
c.RenderArgs["noteContentJson"] = c.Json(noteContent)
c.RenderArgs["noteContent"] = noteContent.Content
c.RenderArgs["tagsJson"] = c.Json(tagService.GetTags(c.GetUserId()))
if isDev, _ := revel.Config.Bool("mode.dev"); isDev {
return c.RenderTemplate("note/note-dev.html")
} else {
return c.RenderTemplate("note/note.html")
}
}
// 首页, 判断是否已登录
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
// 否则, 转向登录页面
func (c Note) ListNotes(notebookId string) revel.Result {
_, notes := noteService.ListNotes(c.GetUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false);
return c.RenderJson(notes)
}
// 得到trash
func (c Note) ListTrashNotes() revel.Result {
_, notes := noteService.ListNotes(c.GetUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false);
return c.RenderJson(notes)
}
// 得到内容
func (c Note) GetNoteContent(noteId string) revel.Result {
noteContent := noteService.GetNoteContent(noteId, c.GetUserId())
return c.RenderJson(noteContent)
}
// 更新note或content
// 肯定会传userId(谁的), NoteId
// 会传Title, Content, Tags, 一种或几种
type NoteOrContent struct {
NotebookId string
NoteId string
UserId string
Title string
Desc string
ImgSrc string
Tags []string
Content string
Abstract string
IsNew bool
IsMarkdown bool
FromUserId string // 为共享而新建
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
}
// 这里不能用json, 要用post
func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
// 新添加note
LogJ(noteOrContent)
if noteOrContent.IsNew {
userId := c.GetObjectUserId();
myUserId := userId
// 为共享新建?
if noteOrContent.FromUserId != "" {
userId = bson.ObjectIdHex(noteOrContent.FromUserId)
}
note := info.Note{UserId: userId,
NoteId: bson.ObjectIdHex(noteOrContent.NoteId),
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
Title: noteOrContent.Title,
Tags: noteOrContent.Tags,
Desc: noteOrContent.Desc,
ImgSrc: noteOrContent.ImgSrc,
IsBlog: noteOrContent.IsBlog,
IsMarkdown: noteOrContent.IsMarkdown,
};
noteContent := info.NoteContent{NoteId: note.NoteId,
UserId: userId,
IsBlog: note.IsBlog,
Content: noteOrContent.Content,
Abstract: noteOrContent.Abstract};
note = noteService.AddNoteAndContent(note, noteContent, myUserId)
return c.RenderJson(note)
}
noteUpdate := bson.M{}
needUpdateNote := false
// 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("Tags[]") {
needUpdateNote = true
noteUpdate["Tags"] = noteOrContent.Tags;
}
if needUpdateNote {
noteService.UpdateNote(noteOrContent.UserId, c.GetUserId(),
noteOrContent.NoteId, noteUpdate)
}
//-------------
if c.Has("Content") {
noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(),
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract)
}
return c.RenderJson(true)
}
// 删除note/ 删除别人共享给我的笔记
// userId 是note.UserId
func (c Note) DeleteNote(noteId, userId string, isShared bool) revel.Result {
if(!isShared) {
return c.RenderJson(trashService.DeleteNote(noteId, c.GetUserId()));
}
return c.RenderJson(trashService.DeleteSharedNote(noteId, userId, c.GetUserId()));
}
// 删除trash
func (c Note) DeleteTrash(noteId string) revel.Result {
return c.RenderJson(trashService.DeleteTrash(noteId, c.GetUserId()));
}
// 移动note
func (c Note) MoveNote(noteId, notebookId string) revel.Result {
return c.RenderJson(noteService.MoveNote(noteId, notebookId, c.GetUserId()));
}
// 复制note
func (c Note) CopyNote(noteId, notebookId string) revel.Result {
return c.RenderJson(noteService.CopyNote(noteId, notebookId, c.GetUserId()));
}
// 复制别人共享的笔记给我
func (c Note) CopySharedNote(noteId, notebookId, fromUserId string) revel.Result {
return c.RenderJson(noteService.CopySharedNote(noteId, notebookId, fromUserId, c.GetUserId()));
}
//------------
// search
// 通过title搜索
func (c Note) SearchNote(key string) revel.Result {
_, blogs := noteService.SearchNote(key, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false, false)
return c.RenderJson(blogs)
}
// 通过tags搜索
func (c Note) SearchNoteByTags(tags []string) revel.Result {
_, blogs := noteService.SearchNoteByTags(tags, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false)
return c.RenderJson(blogs)
}
//-----------------
// html2image
func (c Note) Html2Image(noteId string) revel.Result {
re := info.NewRe()
userId := c.GetUserId()
note := noteService.GetNote(noteId, userId)
if note.NoteId == "" {
return c.RenderJson(re)
}
content := noteService.GetNoteContent(noteId, userId)
// path 判断是否需要重新生成之
fileUrlPath := "/upload/" + userId + "/images/weibo"
dir := revel.BasePath + "/public/" + fileUrlPath
if !ClearDir(dir) {
return c.RenderJson(re)
}
filename := note.NoteId.Hex() + ".png";
path := dir + "/" + filename
// 生成之
html2image.ToImage(userId, c.GetUsername(), noteId, note.Title, content.Content, path)
re.Ok = true
re.Id = fileUrlPath + "/" + filename
return c.RenderJson(re)
}
+57
View File
@@ -0,0 +1,57 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"github.com/leanote/leanote/app/info"
"labix.org/v2/mgo/bson"
// . "github.com/leanote/leanote/app/lea"
// "io/ioutil"
)
type Notebook struct {
BaseController
}
// 得到笔记本
// 该用户下的
func (c Notebook) GetNotebooks() {
}
func (c Notebook) Index(notebook info.Notebook, i int, name string) revel.Result {
return c.RenderJson(notebook)
}
// 得到用户的所有笔记本
func (c Notebook) getNotebooks() revel.Result {
re := notebookService.GetNotebooks(c.GetUserId())
return c.RenderJson(re)
}
func (c Notebook) DeleteNotebook(notebookId string) revel.Result {
re, msg := notebookService.DeleteNotebook(c.GetUserId(), notebookId)
return c.RenderJson(info.Re{Ok: re, Msg: msg})
}
// 添加notebook
func (c Notebook) AddNotebook(notebookId, title string) revel.Result {
notebook := info.Notebook{NotebookId: bson.ObjectIdHex(notebookId),
Title: title,
Seq: -1,
UserId: c.GetObjectUserId()}
re := notebookService.AddNotebook(notebook)
if(re) {
return c.RenderJson(notebook)
} else {
return c.RenderJson(false)
}
}
// 修改标题
func (c Notebook) UpdateNotebookTitle(notebookId, title string) revel.Result {
return c.RenderJson(notebookService.UpdateNotebookTitle(notebookId, c.GetUserId(), title))
}
// 排序
func (c Notebook) SortNotebooks(notebookId2Seqs map[string]int) revel.Result {
return c.RenderJson(notebookService.SortNotebooks(c.GetUserId(), notebookId2Seqs))
}
+70
View File
@@ -0,0 +1,70 @@
package controllers
import (
"code.google.com/p/goauth2/oauth"
"github.com/revel/revel"
"github.com/leanote/leanote/app/lea/netutil"
. "github.com/leanote/leanote/app/lea"
"encoding/json"
"fmt"
)
type Oauth struct {
BaseController
}
var oauthCfg = &oauth.Config{
ClientId: "3790fbf1fc14bc6c5d85",
ClientSecret: "e9dadfe601c7caa6df9b33db3e7539945c60dfa2",
AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token",
RedirectURL: "http://leanote.com/oauth/githubCallback",
Scope: "user",
}
// 用户允许后, github返回到leanote
// 通过code得到token
// https://github.com/login/oauth/authorize?access_type=&approval_prompt=&client_id=3790fbf1fc14bc6c5d85&redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Foauth2callback&response_type=code&scope=user&state=
func (c Oauth) GithubCallback(code string) revel.Result {
t := &oauth.Transport{Config: oauthCfg}
// Exchange the received code for a token
tok, err := t.Exchange(code)
token := tok.AccessToken
if err != nil || token == "" {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
// 得到用户信息
profileInfoURL := "https://api.github.com/user"
url := fmt.Sprintf("%s?access_token=%s", profileInfoURL, token)
content, err2 := netutil.GetContent(url)
if err2 != nil {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
// 转成map
profileInfo := map[string]interface{}{}
Log(string(content))
err2 = json.Unmarshal(content, &profileInfo)
if err2 != nil {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
usernameI := profileInfo["login"]
username, _ := usernameI.(string)
userId := username
// 注册
isExists, userInfo := authService.ThirdRegister("github", userId, username)
c.RenderArgs["isExists"] = isExists
c.RenderArgs["userInfo"] = userInfo
// 登录之
c.SetSession(userInfo)
return c.Redirect("/note")
}
+142
View File
@@ -0,0 +1,142 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
// "labix.org/v2/mgo/bson"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
)
type Share struct {
BaseController
}
// 添加共享note
func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Result {
status := make(map[string]info.Re, len(emails))
// 自己不能给自己添加共享
myEmail := c.GetEmail();
for _, email := range emails {
if email == "" {
continue;
}
if(myEmail != email) {
ok, msg, userId := shareService.AddShareNote(noteId, perm, c.GetUserId(), email);
status[email] = info.Re{Ok: ok, Msg: msg, Id: userId}
} else {
status[email] = info.Re{Ok: false, Msg: "不能分享给自己"}
}
}
return c.RenderJson(status)
}
// 添加共享notebook
func (c Share) AddShareNotebook(notebookId string, emails []string, perm int) revel.Result {
status := make(map[string]info.Re, len(emails))
// 自己不能给自己添加共享
myEmail := c.GetEmail();
for _, email := range emails {
if email == "" {
continue;
}
if(myEmail != email) {
ok, msg, userId := shareService.AddShareNotebook(notebookId, perm, c.GetUserId(), email);
status[email] = info.Re{Ok: ok, Msg: msg, Id: userId}
} else {
status[email] = info.Re{Ok: false, Msg: "不能分享给自己"}
}
}
return c.RenderJson(status)
}
// 得到notes
// userId 该userId分享给我的
func (c Share) ListShareNotes(notebookId, userId string) revel.Result {
// 表示是默认笔记本, 不是某个特定notebook的共享
var notes []info.ShareNoteWithPerm
if notebookId == "" {
notes = shareService.ListShareNotes(c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false);
return c.RenderJson(notes)
} else {
// 有notebookId的
return c.RenderJson(shareService.ListShareNotesByNotebookId(notebookId, c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false));
}
}
// 得到内容
// sharedUserId 是谁的笔记
func (c Share) GetShareNoteContent(noteId, sharedUserId string) revel.Result {
noteContent := shareService.GetShareNoteContent(noteId, c.GetUserId(), sharedUserId)
return c.RenderJson(noteContent)
}
// 查看note的分享信息
// 分享给了哪些用户和权限
// ShareNotes表 userId = me, noteId = ...
// 还要查看该note的notebookId分享的信息
func (c Share) ListNoteShareUserInfo(noteId string) revel.Result {
note := noteService.GetNote(noteId, c.GetUserId())
noteShareUserInfos := shareService.ListNoteShareUserInfo(noteId, c.GetUserId())
c.RenderArgs["noteOrNotebookShareUserInfos"] = noteShareUserInfos
c.RenderArgs["isNote"] = true
c.RenderArgs["noteOrNotebookId"] = note.NoteId.Hex();
c.RenderArgs["title"] = note.Title
return c.RenderTemplate("share/note_notebook_share_user_infos.html")
}
func (c Share) ListNotebookShareUserInfo(notebookId string) revel.Result {
notebook := notebookService.GetNotebook(notebookId, c.GetUserId())
notebookShareUserInfos := shareService.ListNotebookShareUserInfo(notebookId, c.GetUserId())
c.RenderArgs["noteOrNotebookShareUserInfos"] = notebookShareUserInfos
c.RenderArgs["isNote"] = false
c.RenderArgs["noteOrNotebookId"] = notebook.NotebookId.Hex();
c.RenderArgs["title"] = notebook.Title
return c.RenderTemplate("share/note_notebook_share_user_infos.html")
}
//------------
// 改变share note 权限
func (c Share) UpdateShareNotePerm(noteId string, perm int, toUserId string) revel.Result {
return c.RenderJson(shareService.UpdateShareNotePerm(noteId, perm, c.GetUserId(), toUserId));
}
// 改变share notebook 权限
func (c Share) UpdateShareNotebookPerm(notebookId string, perm int, toUserId string) revel.Result {
return c.RenderJson(shareService.UpdateShareNotebookPerm(notebookId, perm, c.GetUserId(), toUserId));
}
//---------------
// 删除share note
func (c Share) DeleteShareNote(noteId string, toUserId string) revel.Result {
return c.RenderJson(shareService.DeleteShareNote(noteId, c.GetUserId(), toUserId));
}
// 删除share notebook
func (c Share) DeleteShareNotebook(notebookId string, toUserId string) revel.Result {
return c.RenderJson(shareService.DeleteShareNotebook(notebookId, c.GetUserId(), toUserId));
}
// 删除share note, 被共享方删除
func (c Share) DeleteShareNoteBySharedUser(noteId string, fromUserId string) revel.Result {
return c.RenderJson(shareService.DeleteShareNote(noteId, fromUserId, c.GetUserId()));
}
// 删除share notebook, 被共享方删除
func (c Share) DeleteShareNotebookBySharedUser(notebookId string, fromUserId string) revel.Result {
return c.RenderJson(shareService.DeleteShareNotebook(notebookId, fromUserId, c.GetUserId()));
}
// 删除fromUserId分享给我的所有note, notebook
func (c Share) DeleteUserShareNoteAndNotebook(fromUserId string) revel.Result {
return c.RenderJson(shareService.DeleteUserShareNoteAndNotebook(fromUserId, c.GetUserId()));
}
+196
View File
@@ -0,0 +1,196 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
// "labix.org/v2/mgo/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 User struct {
BaseController
}
// 修改用户名, 需要重置session
func (c User) UpdateUsername(username string) revel.Result {
re := info.NewRe();
// 判断是否满足最基本的, 4位, 不含特殊字符, 大小写无关. email大小写无关
if len(username) < 4 {
re.Ok = false
re.Msg = "至少4位"
return c.RenderJson(re);
}
if !IsUsername(username) {
re.Ok = false
re.Msg = "不能包含特殊字符"
return c.RenderJson(re);
}
re.Ok, re.Msg = userService.UpdateUsername(c.GetUserId(), username)
if(re.Ok) {
c.UpdateSession("Username", username)
}
return c.RenderJson(re);
}
// 修改密码
func (c User) UpdatePwd(oldPwd, pwd string) revel.Result {
re := info.NewRe();
if oldPwd == "" {
re.Msg = "旧密码错误"
return c.RenderJson(re);
}
re.Ok, re.Msg = IsGoodPwd(pwd)
if !re.Ok {
return c.RenderJson(re);
}
re.Ok, re.Msg = userService.UpdatePwd(c.GetUserId(), oldPwd, pwd)
return c.RenderJson(re);
}
// 更新主题
func (c User) UpdateTheme(theme string) revel.Result {
re := info.NewRe();
re.Ok = userService.UpdateTheme(c.GetUserId(), theme)
if re.Ok {
c.UpdateSession("Theme", theme)
}
return c.RenderJson(re);
}
// 发送邀请链接
func (c User) SendRegisterEmail(content, toEmail string) revel.Result {
re := info.NewRe()
if content == "" || !IsEmail(toEmail) {
return c.RenderJson(re);
}
// 发送邮件
var userInfo = c.GetUserInfo();
url := "http://leanote.com/register?from=" + userInfo.Username
body := fmt.Sprintf("点击链接注册leanote: <a href='%v'>%v</a>. ", url, url);
body = content + "<br />" + body
re.Ok = SendEmail(toEmail, userInfo.Username + "邀请您注册leanote", "邀请注册", body)
return c.RenderJson(re);
}
//---------------------------
// 重新发送激活邮件
func (c User) ReSendActiveEmail() revel.Result {
re := info.NewRe()
re.Ok = userService.RegisterSendActiveEmail(c.GetUserId(), c.GetEmail())
return c.RenderJson(re)
}
// 修改Email发送激活邮箱
func (c User) UpdateEmailSendActiveEmail(email string) revel.Result {
re := info.NewRe()
re.Ok, re.Msg = userService.UpdateEmailSendActiveEmail(c.GetUserId(), email)
return c.RenderJson(re)
}
// 通过点击链接
// 修改邮箱
func (c User) UpdateEmail(token string) revel.Result {
c.SetUserInfo();
ok, msg, email := userService.UpdateEmail(token)
c.RenderArgs["title"] = "验证邮箱"
c.RenderArgs["ok"] = ok
c.RenderArgs["msg"] = msg
c.RenderArgs["email"] = email
// 修改session
if ok {
c.UpdateSession("Email", email)
}
return c.RenderTemplate("user/update_email.html")
}
// 注册后激活邮箱
func (c User) ActiveEmail(token string) revel.Result {
c.SetUserInfo();
ok, msg, email := userService.ActiveEmail(token)
// 需要修改session
if ok {
c.UpdateSession("Verified", "1");
}
c.RenderArgs["title"] = "验证邮箱"
c.RenderArgs["ok"] = ok
c.RenderArgs["msg"] = msg
c.RenderArgs["email"] = email
return c.RenderTemplate("user/active_email.html")
}
//------------
// 第三方账号添加leanote账号
func (c User) AddAccount(email, pwd string) revel.Result {
re := info.NewRe()
if email == "" {
re.Msg = "请输入邮箱"
return c.RenderJson(re)
} else if !IsEmail(email) {
re.Msg = "请输入正确的邮箱"
return c.RenderJson(re)
}
// 密码
if pwd == "" {
re.Msg = "请输入密码"
return c.RenderJson(re)
} else if len(pwd) < 6 {
re.Msg = "密码长度至少6位"
return c.RenderJson(re)
}
re.Ok, re.Msg = userService.ThirdAddUser(c.GetUserId(), email, pwd)
if re.Ok {
c.UpdateSession("Email", email);
}
return c.RenderJson(re)
}
//-----------------
// 用户偏爱
func (c User) UpdateColumnWidth(notebookWidth, noteListWidth int) revel.Result {
re := info.NewRe()
re.Ok = userService.UpdateColumnWidth(c.GetUserId(), notebookWidth, noteListWidth)
if re.Ok {
c.UpdateSession("NotebookWidth", strconv.Itoa(notebookWidth));
c.UpdateSession("NoteListWidth", strconv.Itoa(noteListWidth));
}
return c.RenderJson(re)
}
func (c User) UpdateLeftIsMin(leftIsMin bool) revel.Result {
re := info.NewRe()
re.Ok = userService.UpdateLeftIsMin(c.GetUserId(), leftIsMin)
if re.Ok {
if leftIsMin {
c.UpdateSession("LeftIsMin", "1");
} else {
c.UpdateSession("LeftIsMin", "0");
}
}
return c.RenderJson(re)
}
+124
View File
@@ -0,0 +1,124 @@
package controllers
import (
"github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/info"
"github.com/revel/revel"
"strings"
)
// 该文件初始化所有service方法
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 pageSize = 1000
var defaultSortField = "UpdatedTime"
var leanoteUserId = "52d26b4e99c37b609a000001"
// 拦截器
// 不需要拦截的url
// Index 除了Note之外都不需要
var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": true,
"Login": true,
"DoLogin": true,
"Logout": true,
"Register": true,
"DoRegister": true,
"FindPasswword": true,
"DoFindPassword": true,
"FindPassword2": true,
"FindPasswordUpdate": true,
"Suggestion": true,
},
"Blog": map[string]bool{"Index": true,
"View": true,
"AboutMe": true,
"SearchBlog": true,
},
// 用户的激活与修改邮箱都不需要登录, 通过链接地址
"User": map[string]bool{"UpdateEmail": true,
"ActiveEmail":true,
},
"oauth": map[string]bool{"githubCallback": 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;
}
}
func AuthInterceptor(c *revel.Controller) revel.Result {
// 全部变成首字大写
var controller = strings.Title(c.Name)
var method = strings.Title(c.MethodName)
// 是否需要验证?
if !needValidate(controller, method) {
return nil
}
// 验证是否已登录
if userId, ok := c.Session["UserId"]; ok && userId != "" {
return nil // 已登录
}
// 没有登录, 判断是否是ajax操作
if c.Request.Header.Get("X-Requested-With") == "XMLHttpRequest" {
re := info.NewRe()
re.Msg = "NOTLOGIN"
return c.RenderJson(re)
}
return c.Redirect("/login")
}
func init() {
// interceptor
// revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Index{}) // Index.Note自己校验
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Notebook{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Note{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Share{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &User{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &File{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Blog{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &NoteContentHistory{})
// service
userService = &service.UserService{}
noteService = &service.NoteService{}
trashService = &service.TrashService{}
notebookService = &service.NotebookService{}
noteContentHistoryService = &service.NoteContentHistoryService{}
authService = &service.AuthService{}
shareService = &service.ShareService{}
blogService = &service.BlogService{}
tagService = &service.TagService{}
pwdService = &service.PwdService{}
tokenService = &service.TokenService{}
suggestionService = &service.SuggestionService{}
revel.OnAppStart(func() {
leanoteUserId, _ = revel.Config.String("adminUsername")
})
}
+276
View File
@@ -0,0 +1,276 @@
package db
import (
"fmt"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"github.com/revel/revel"
)
// Init mgo and the common DAO
// 数据连接
var Session *mgo.Session
// 各个表的Collection对象
var Notebooks *mgo.Collection
var Notes *mgo.Collection
var NoteContents *mgo.Collection
var NoteContentHistories *mgo.Collection
var ShareNotes *mgo.Collection
var ShareNotebooks *mgo.Collection
var HasShareNotes *mgo.Collection
var Blogs *mgo.Collection
var Users *mgo.Collection
var Tags *mgo.Collection
var TagNotes *mgo.Collection
var UserBlogs *mgo.Collection
var Tokens *mgo.Collection
var Suggestions *mgo.Collection
// 初始化时连接数据库
func Init() {
var url string
var ok bool
config := revel.Config;
url, ok = config.String("db.url")
dbname, _ := config.String("db.dbname")
if !ok {
host, _ := revel.Config.String("db.host")
port, _ := revel.Config.String("db.port")
username, _ := revel.Config.String("db.username")
password, _ := revel.Config.String("db.password")
url = "mongodb://" + username + ":" + password + "@" + host + ":" + port + "/" + dbname
}
// [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options]
// mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
var err error
Session, err = mgo.Dial(url)
if err != nil {
panic(err)
}
// Optional. Switch the session to a monotonic behavior.
Session.SetMode(mgo.Monotonic, true)
// notebook
Notebooks = Session.DB(dbname).C("notebooks")
// notes
Notes = Session.DB(dbname).C("notes")
// noteContents
NoteContents = Session.DB(dbname).C("note_contents")
NoteContentHistories = Session.DB(dbname).C("note_content_histories")
// share
ShareNotes = Session.DB(dbname).C("share_notes")
ShareNotebooks = Session.DB(dbname).C("share_notebooks")
HasShareNotes = Session.DB(dbname).C("has_share_notes")
// user
Users = Session.DB(dbname).C("users")
// blog
Blogs = Session.DB(dbname).C("blogs")
// tag
Tags = Session.DB(dbname).C("tags")
TagNotes = Session.DB(dbname).C("tag_notes")
// blog
UserBlogs = Session.DB(dbname).C("user_blogs")
// find password
Tokens = Session.DB(dbname).C("tokens")
//
Suggestions = Session.DB(dbname).C("suggestions")
}
func init() {
revel.OnAppStart(func() {
Init()
})
}
func close() {
Session.Close()
}
// common DAO
// 公用方法
//----------------------
func Insert(collection *mgo.Collection, i interface{}) bool {
err := collection.Insert(i)
return Err(err)
}
//----------------------
// 适合一条记录全部更新
func Update(collection *mgo.Collection, query interface{}, i interface{}) bool {
err := collection.Update(query, i)
return Err(err)
}
func Upsert(collection *mgo.Collection, query interface{}, i interface{}) bool {
_, err := collection.Upsert(query, i)
return Err(err)
}
func UpdateAll(collection *mgo.Collection, query interface{}, i interface{}) bool {
_, err := collection.UpdateAll(query, i)
return Err(err)
}
func UpdateByIdAndUserId(collection *mgo.Collection, id, userId string, i interface{}) bool {
err := collection.Update(GetIdAndUserIdQ(id, userId), i)
return Err(err)
}
func UpdateByIdAndUserId2(collection *mgo.Collection, id, userId bson.ObjectId, i interface{}) bool {
err := collection.Update(GetIdAndUserIdBsonQ(id, userId), i)
return Err(err)
}
func UpdateByIdAndUserIdField(collection *mgo.Collection, id, userId, field string, value interface{}) bool {
return UpdateByIdAndUserId(collection, id, userId, bson.M{"$set": bson.M{field:value}})
}
func UpdateByIdAndUserIdMap(collection *mgo.Collection, id, userId string, v bson.M) bool {
return UpdateByIdAndUserId(collection, id, userId, bson.M{"$set": v})
}
func UpdateByIdAndUserIdField2(collection *mgo.Collection, id, userId bson.ObjectId, field string, value interface{}) bool {
return UpdateByIdAndUserId2(collection, id, userId, bson.M{"$set": bson.M{field:value}})
}
func UpdateByIdAndUserIdMap2(collection *mgo.Collection, id, userId bson.ObjectId, v bson.M) bool {
return UpdateByIdAndUserId2(collection, id, userId, bson.M{"$set": v})
}
//
func UpdateByQField(collection *mgo.Collection, q interface{}, field string, value interface{}) bool {
_, err := collection.UpdateAll(q, bson.M{"$set": bson.M{field: value}})
return Err(err)
}
// 查询条件和值
func UpdateByQMap(collection *mgo.Collection, q interface{}, v interface{}) bool {
_, err := collection.UpdateAll(q, bson.M{"$set": v})
return Err(err)
}
//------------------------
// 删除一条
func Delete(collection *mgo.Collection, q interface{}) bool {
err := collection.Remove(q)
return Err(err)
}
func DeleteByIdAndUserId(collection *mgo.Collection, id, userId string) bool {
err := collection.Remove(GetIdAndUserIdQ(id, userId))
return Err(err)
}
func DeleteByIdAndUserId2(collection *mgo.Collection, id, userId bson.ObjectId) bool {
err := collection.Remove(GetIdAndUserIdBsonQ(id, userId))
return Err(err)
}
// 删除所有
func DeleteAllByIdAndUserId(collection *mgo.Collection, id, userId string) bool {
_, err := collection.RemoveAll(GetIdAndUserIdQ(id, userId))
return Err(err)
}
func DeleteAllByIdAndUserId2(collection *mgo.Collection, id, userId bson.ObjectId) bool {
_, err := collection.RemoveAll(GetIdAndUserIdBsonQ(id, userId))
return Err(err)
}
func DeleteAll(collection *mgo.Collection, q interface{}) bool {
_, err := collection.RemoveAll(q)
return Err(err)
}
//-------------------------
func Get(collection *mgo.Collection, id string, i interface{}) {
collection.FindId(bson.ObjectIdHex(id)).One(i)
}
func Get2(collection *mgo.Collection, id bson.ObjectId, i interface{}) {
collection.FindId(id).One(i)
}
func GetByQ(collection *mgo.Collection, q interface{}, i interface{}) {
collection.Find(q).One(i)
}
func ListByQ(collection *mgo.Collection, q interface{}, i interface{}) {
collection.Find(q).All(i)
}
func ListByQLimit(collection *mgo.Collection, q interface{}, i interface{}, limit int) {
collection.Find(q).Limit(limit).All(i)
}
// 查询某些字段, q是查询条件, fields是字段名列表
func GetByQWithFields(collection *mgo.Collection, q bson.M, fields []string, i interface{}) {
selector := make(bson.M, len(fields))
for _, field := range fields {
selector[field] = true
}
collection.Find(q).Select(selector).One(i)
}
// 查询某些字段, q是查询条件, fields是字段名列表
func ListByQWithFields(collection *mgo.Collection, q bson.M, fields []string, i interface{}) {
selector := make(bson.M, len(fields))
for _, field := range fields {
selector[field] = true
}
collection.Find(q).Select(selector).All(i)
}
func GetByIdAndUserId(collection *mgo.Collection, id, userId string, i interface{}) {
collection.Find(GetIdAndUserIdQ(id, userId)).One(i)
}
func GetByIdAndUserId2(collection *mgo.Collection, id, userId bson.ObjectId, i interface{}) {
collection.Find(GetIdAndUserIdBsonQ(id, userId)).One(i)
}
//----------------------
func Count(collection *mgo.Collection, q interface{}) int {
cnt, err := collection.Find(q).Count()
if err != nil {
Err(err)
}
return cnt
}
func Has(collection *mgo.Collection, q interface{}) bool {
if Count(collection, q) > 0 {
return true
}
return false
}
//-----------------
// 得到主键和userId的复合查询条件
func GetIdAndUserIdQ(id, userId string) bson.M {
return bson.M{"_id": bson.ObjectIdHex(id), "UserId": bson.ObjectIdHex(userId)}
}
func GetIdAndUserIdBsonQ(id, userId bson.ObjectId) bson.M {
return bson.M{"_id": id, "UserId": userId}
}
// DB处理错误
func Err(err error) bool {
if err != nil {
fmt.Println(err)
// 删除时, 查找
if err.Error() == "not found" {
return true;
}
return false
}
return true
}
+72
View File
@@ -0,0 +1,72 @@
package main
import (
"fmt"
"os"
"bufio"
"strings"
"encoding/json"
)
// convert revel msg to js msg
var msgBasePath = "/Users/life/Documents/Go/package/src/leanote/messages/"
var targetBasePath = "/Users/life/Documents/Go/package/src/leanote/public/js/i18n/"
func parse(filename string) {
file, err := os.Open(msgBasePath + filename)
reader := bufio.NewReader(file)
msg := map[string]string{}
if err != nil {
fmt.Println(err)
return
}
for true {
line, _, err := reader.ReadLine()
if err != nil {
break
}
if len(line) == 0 {
continue
}
// 对每一行进行处理
if line[0] == '#' || line[1] == '#' {
continue;
}
lineStr := string(line)
// 找到第一个=位置
pos := strings.Index(lineStr, "=")
if pos < 0 {
continue;
}
key := string(line[0:pos])
value := string(line[pos+1:])
// fmt.Println(lineStr)
// fmt.Println(value)
msg[key] = value
}
// JSON
b, _ := json.Marshal(msg)
str := string(b)
fmt.Println(str);
targetName := targetBasePath + filename + ".js"
file2, err2 := os.OpenFile(targetName, os.O_RDWR|os.O_CREATE, 0644)
if err2 != nil {
file2, err2 = os.Create(targetName)
}
file2.WriteString("var MSG = " + str + ";")
}
// 生成js的i18n文件
func main() {
parse("msg.en")
parse("msg.zh")
}
+43
View File
@@ -0,0 +1,43 @@
package info
import (
"labix.org/v2/mgo/bson"
)
// 只为blog, 不为note
type BlogItem struct {
Note
Content string // 可能是content的一部分, 截取. 点击more后就是整个信息了
HasMore bool // 是否是否还有
}
type UserBlogBase struct {
Logo string `Logo`
Title string `Title` // 标题
SubTitle string `SubTitle` // 副标题
AboutMe string `AboutMe` // 关于我
}
type UserBlogComment struct {
CanComment bool `CanComment` // 是否可以评论
DisqusId string `DisqusId`
}
type UserBlogStyle struct {
Style string `Style` // 风格
}
// 每个用户一份博客设置信息
type UserBlog struct {
UserId bson.ObjectId `bson:"_id"` // 谁的
Logo string `Logo`
Title string `Title` // 标题
SubTitle string `SubTitle` // 副标题
AboutMe string `AboutMe` // 关于我
CanComment bool `CanComment` // 是否可以评论
DisqusId string `DisqusId`
Style string `Style` // 风格
}
+64
View File
@@ -0,0 +1,64 @@
package info
import (
"labix.org/v2/mgo/bson"
"time"
)
// 只存笔记基本信息
// 内容不存放
type Note struct {
NoteId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
UserId bson.ObjectId `bson:"UserId"` // 谁的
CreatedUserId bson.ObjectId `bson:"CreatedUserId,omitempty"` // 谁创建的(UserId != CreatedUserId, 是因为共享). 只是共享才有, 默认为空, 不存 必须要加omitempty
NotebookId bson.ObjectId `bson:"NotebookId"`
Title string `Title` // 标题
Desc string `Desc` // 描述, 非html
ImgSrc string `ImgSrc` // 图片, 第一张缩略图地址
Tags []string `Tags,omitempty`
IsTrash bool `IsTrash` // 是否是trash, 默认是false
IsBlog bool `IsBlog,omitempty` // 是否设置成了blog 2013/12/29 新加
IsTop bool `IsTop,omitempty` // blog是否置顶
IsMarkdown bool `IsMarkdown` // 是否是markdown笔记, 默认是false
CreatedTime time.Time `CreatedTime`
UpdatedTime time.Time `UpdatedTime`
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了
}
// 内容
type NoteContent struct {
NoteId bson.ObjectId `bson:"_id,omitempty"`
UserId bson.ObjectId `bson:"UserId"`
IsBlog bool `IsBlog,omitempty` // 为了搜索博客
Content string `Content`
Abstract string `Abstract` // 摘要, 有html标签, 比content短, 在博客展示需要, 不放在notes表中
CreatedTime time.Time `CreatedTime`
UpdatedTime time.Time `UpdatedTime`
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了
}
// 基本信息和内容在一起
type NoteAndContent struct {
Note
NoteContent
}
// 历史记录
// 每一个历史记录对象
type EachHistory struct {
UpdatedUserId bson.ObjectId `UpdatedUserId`
UpdatedTime time.Time `UpdatedTime`
Content string `Content`
}
type NoteContentHistory struct {
NoteId bson.ObjectId `bson:"_id,omitempty"`
UserId bson.ObjectId `bson:"UserId"` // 所属者
Histories []EachHistory `Histories`
}
+56
View File
@@ -0,0 +1,56 @@
package info
import (
"labix.org/v2/mgo/bson"
"time"
)
// 在数据库中每个
// 修改字段必须要在NotebookService中修改ParseAndSortNotebooks(没有匿名字段), 以后重构
type Notebook 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` // 标题
NumberNotes int `NumberNotes` // 笔记数
IsTrash bool `IsTrash,omitempty` // 是否是trash, 默认是false
IsBlog bool `IsBlog,omitempty` // 是否是Blog 2013/12/29 新加
CreatedTime time.Time `CreatedTime,omitempty`
UpdatedTime time.Time `UpdatedTime,omitempty`
}
// 仅仅是为了返回前台
type SubNotebooks []Notebooks
type Notebooks struct {
Notebook
Subs SubNotebooks // 子notebook 在数据库中是没有的
}
// SubNotebook sort
func (this SubNotebooks) Len() int {
return len(this)
}
func (this SubNotebooks) Less(i, j int) bool {
return this[i].Seq < this[j].Seq
}
func (this SubNotebooks) Swap(i, j int) {
this[i], this[j] = this[j], this[i]
}
/*
修改方案, 因为要共享notebook的问题, 所以还是每个notebook一条记录
{
notebookId,
title,
seq,
parentNoteBookId, // 上级
userId
}
得到所有该用户的notebook, 然后组装成tree返回之
更新顺序
添加notebook
更新notebook
删除notebook
*/
+1
View File
@@ -0,0 +1 @@
All the mgo model and common struct
+18
View File
@@ -0,0 +1,18 @@
package info
import (
)
// controller ajax返回
type Re struct {
Ok bool
Code int
Msg string
Id string
List interface{}
Item interface{}
}
func NewRe() Re {
return Re{Ok: false}
}
+166
View File
@@ -0,0 +1,166 @@
package info
import (
"labix.org/v2/mgo/bson"
"time"
)
// 共享笔记本
/*
{
userId, 谁的共享笔记本
shared: {
userId1: { // 哪个用户共享的
seq:
"defaultNotebook": {noteId:{Seq:33}, noteId2:{}} // 默认笔记本, 里面是全部的笔记
"notebookIds":{notebookId:{}, notebookId2:{}}} // 其它笔记本
},
userId2: {
}
}
}
*/
// 以后可能含有其它信息
type EachSharedNote struct {
Seq int
}
type EachSharedNotebook struct {
Seq int
}
// 每一个用户共享给的note, notebook
type EachSharedNotebookAndNotes struct {
Seq int // 共享给谁的所在序号
DefaultNotebook map[string]EachSharedNote // noteId => 共享的note
Notebooks map[string]EachSharedNotebook // notebookId => 共享的notebook
}
type SharedNotebookAndNotes struct {
UserId bson.ObjectId `bson:"_id"`
Shared map[string]EachSharedNotebookAndNotes // userId =>
}
/*
{
UserId
Notes: {noteId => [userId1, userId2], noteId2: []}
Notebooks: {notebookId => [], notebookId2 => []}
}
*/
// 用户正在共享的notebook, note
type SharingNotebookAndNotes struct {
UserId bson.ObjectId `bson:"_id"`
Notes map[string][]string // noteId => []string{userId1, userId2}
Notebooks map[string][]string // notebookId => []string{userId1, userId2}
}
// 以上以后再用, 现不用
//----------------------------------------
/*
每一个sharing一条记录, 这样更好操作
类似Notebook的
{
_id
userId
toUserId
noteId
notebookId
seq
}
*/
type ShareNotebook struct {
ShareNotebookId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
UserId bson.ObjectId `bson:"UserId"`
ToUserId bson.ObjectId `bson:"ToUserId"`
NotebookId bson.ObjectId `bson:"NotebookId"`
Seq int `bson:"Seq"` // 排序
Perm int `bson:"Perm"` // 权限, 其下所有notes 0只读, 1可修改
CreatedTime time.Time `CreatedTime,omitempty`
// IsDefault bool `IsDefault` // 是否是默认共享notebook, perm seq=-9999999, NotebookId=null
}
/*
[
ShareNotebooks,
Subs: [ShareNotebooks, ]
ShareNotebooks
]
*/
type SubShareNotebooks []ShareNotebooks
type ShareNotebooks struct {
Notebook
ShareNotebook
Subs SubShareNotebooks
// Notebook与ShareNotebook公用的不能生成json
Seq int
NotebookId bson.ObjectId
IsDefault bool // 是否是默认笔记本
}
// SubShareNotebook sort
func (this SubShareNotebooks) Len() int {
return len(this)
}
func (this SubShareNotebooks) Less(i, j int) bool {
return this[i].ShareNotebook.Seq < this[j].ShareNotebook.Seq
}
func (this SubShareNotebooks) Swap(i, j int) {
this[i], this[j] = this[j], this[i]
}
// 以用户进行分组
// type ShareNotebooksByUsers []ShareNotebooksByUser
type ShareNotebooksByUser map[string][]ShareNotebooks
/*
type ShareNotebooksByUser struct {
// User
UserId bson.ObjectId
ShareNotebooks []ShareNotebooks // SubShareNotebooks 一样的, 不过用[]更容易理解
}
*/
//----------------------------------
// 唯一: userId-ToUserId-NoteId
// use leanote
// db.leanote.share_notes.ensureIndex({"UserId":1,"ToUserId":1, "NoteId": 1},{"unique":true})
type ShareNote struct {
ShareNoteId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
UserId bson.ObjectId `bson:"UserId"`
ToUserId bson.ObjectId `bson:"ToUserId"`
NoteId bson.ObjectId `bson:"NoteId"`
Perm int `bson:"Perm"` // 权限, 0只读, 1可修改
CreatedTime time.Time `CreatedTime`
}
// 谁共享给了谁note
// 共享了note, notebook都要加!
// 唯一: UserId-ToUserId
// db.leanote.has_share_notes.ensureIndex({"UserId":1,"ToUserId":1},{"unique":true})
type HasShareNote struct {
HasShareNotebookId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
UserId bson.ObjectId `bson:"UserId"`
ToUserId bson.ObjectId `bson:"ToUserId"`
Seq int `bson:"Seq"` // 以后还可以用户排序
}
// 将note与权限结合起来
type ShareNoteWithPerm struct {
Note
Perm int
}
// 查看共享状态, 用户的信息
type ShareUserInfo struct {
ToUserId bson.ObjectId `bson:"UserId"`
Email string
Perm int // note或其notebook的权限
NotebookHasShared bool // 是否其notebook共享了
}
+13
View File
@@ -0,0 +1,13 @@
package info
import (
"labix.org/v2/mgo/bson"
)
// 建议
type Suggestion struct {
Id bson.ObjectId `bson:"_id"`
UserId bson.ObjectId `UserId`
Addr string `Addr`
Suggestion string `Suggestion`
}
+19
View File
@@ -0,0 +1,19 @@
package info
import (
"labix.org/v2/mgo/bson"
)
// 这里主要是为了统计每个tag的note数目
type TagNote struct {
TagId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
UserId bson.ObjectId `bson:"UserId"`
Tag string `Title` // 标题
NoteNum int `NoteNum` // note数目
}
// 每个用户一条记录, 存储用户的所有tags
type Tag struct {
UserId bson.ObjectId `bson:"_id"`
Tags []string `Tags`
}
+32
View File
@@ -0,0 +1,32 @@
package info
import (
"labix.org/v2/mgo/bson"
"time"
)
// 随机token
// 验证邮箱
// 找回密码
// token type
const (
TokenPwd = iota
TokenActiveEmail
TokenUpdateEmail
)
// 过期时间
const (
PwdOverHours = 2.0
ActiveEmailOverHours = 48.0
UpdateEmailOverHours = 2.0
)
type Token struct {
UserId bson.ObjectId `bson:"_id"`
Email string `Email`
Token string `Token`
Type int `Type`
CreatedTime time.Time `CreatedTime`
}
+35
View File
@@ -0,0 +1,35 @@
package info
import (
"labix.org/v2/mgo/bson"
"time"
)
// 第三方类型
const (
ThirdGithub = iota
ThirdQQ
)
type User struct {
UserId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
Email string `Email` // 全是小写
Verified bool `Verified` // Email是否已验证过?
Username string `Username` // 不区分大小写, 全是小写
UsernameRaw string `UsernameRaw` // 可能有大小写
Pwd string `bson:"Pwd" json:"-"`
CreatedTime time.Time `CreatedTime`
// 主题
Theme string `Theme`
// 用户配置
NotebookWidth int `NotebookWidth` // 笔记本宽度
NoteListWidth int `NoteListWidth` // 笔记列表宽度
LeftIsMin bool `LeftIsMin` // 左侧是否是隐藏的, 默认是打开的
// 这里 第三方登录
ThirdUserId string `ThirdUserId` // 用户Id, 在第三方中唯一可识别
ThirdUsername string `ThirdUsername` // 第三方中username, 为了显示
ThirdType int `ThirdType` // 第三方类型
}
+12
View File
@@ -0,0 +1,12 @@
package info
import (
)
// 分页数据
type Page struct {
CurPage int // 当前页码
TotalPage int // 总页
List interface{}
}
+118
View File
@@ -0,0 +1,118 @@
package app
import (
"github.com/revel/revel"
. "github.com/leanote/leanote/app/lea"
_ "github.com/leanote/leanote/app/lea/binder"
"reflect"
"fmt"
"html/template"
"math"
"strconv"
"time"
)
func init() {
// Filters is the default set of global filters.
revel.Filters = []revel.Filter{
revel.PanicFilter, // Recover from panics and display an error page instead.
revel.RouterFilter, // Use the routing table to select the right Action
// AuthFilter, // Invoke the action.
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params.
revel.SessionFilter, // Restore and write the session cookie.
// session.SessionFilter, // leanote memcache session life
revel.FlashFilter, // Restore and write the flash cookie.
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
revel.I18nFilter, // Resolve the requested language
revel.InterceptorFilter, // Run interceptors around the action.
revel.CompressFilter, // Compress the result.
revel.ActionInvoker, // Invoke the action.
}
revel.TemplateFuncs["raw"] = func(str string) template.HTML {
return template.HTML(str)
}
revel.TemplateFuncs["add"] = func(i int) template.HTML {
i = i + 1;
return template.HTML(fmt.Sprintf("%v", i))
}
revel.TemplateFuncs["concat"] = func(s1, s2 string) template.HTML {
return template.HTML(s1 + s2)
}
revel.TemplateFuncs["datetime"] = func(t time.Time) template.HTML {
return template.HTML(t.Format("2006-01-02 15:04:05"))
}
// interface是否有该字段
revel.TemplateFuncs["has"] = func(i interface{}, key string) bool {
t := reflect.TypeOf(i)
_, ok := t.FieldByName(key)
return ok
}
// tags
revel.TemplateFuncs["blogTags"] = func(tags []string) template.HTML {
if tags == nil || len(tags) == 0 {
return ""
}
tagMap := map[string]string{"red": "红色", "yellow": "黄色", "blue": "蓝色", "green": "绝色"}
tagStr := ""
lenTags := len(tags)
for i, tag := range tags {
if text, ok := tagMap[tag]; ok {
tagStr += text
} else {
tagStr += tag
}
if i != lenTags - 1 {
tagStr += ","
}
}
return template.HTML(tagStr)
}
// pagination
revel.TemplateFuncs["page"] = func(userId, notebookId string, page, pageSize, count int) template.HTML {
if count == 0 {
return "";
}
totalPage := int(math.Ceil(float64(count)/float64(pageSize)))
preClass := ""
prePage := page - 1
if prePage == 0 {
prePage = 1
}
nextClass := ""
nextPage := page + 1
var preUrl, nextUrl string
urlBase := "/blog/" + userId
if notebookId != "" {
urlBase += "/" + notebookId
}
preUrl = urlBase + "?page=" + strconv.Itoa(prePage)
nextUrl = urlBase + "?page=" + strconv.Itoa(nextPage)
// 没有上一页了
if page == 1 {
preClass = "disabled"
preUrl = "#"
}
// 没有下一页了
if totalPage <= page {
nextClass = "disabled"
nextUrl = "#"
}
return template.HTML("<li class='" + preClass + "'><a href='" + preUrl + "'>Previous</a></li> <li class='" + nextClass + "'><a href='" + nextUrl + "'>Next</a></li>")
}
// init Email
revel.OnAppStart(func() {
InitEmail()
})
}
+15
View File
@@ -0,0 +1,15 @@
package lea
import (
"encoding/json"
"github.com/revel/revel"
)
func Log(i interface{}) {
revel.INFO.Println(i)
}
func LogJ(i interface{}) {
b, _ := json.MarshalIndent(i, "", " ")
revel.INFO.Println(string(b))
}
+89
View File
@@ -0,0 +1,89 @@
package lea
import (
"net/smtp"
"strings"
"github.com/revel/revel"
)
// 发送邮件
var host = "smtp.ym.163.com"
var port = "25"
var username = "noreply@leanote.com"
var password = "---"
func InitEmail() {
config := revel.Config;
host, _ = config.String("email.host")
port, _ = config.String("email.port")
username, _ = config.String("email.username")
password, _ = config.String("email.password")
}
var bodyTpl = `
<html>
<body>
<div style="width: 800px; margin:auto; border-radius:5px; border: 1px solid #ccc; padding: 20px;">
<div>
<div>
<div style="float:left; height: 40px;">
<a href="http://leanote.com" style="font-size: 24px">leanote</a>
</div>
<div style="float:left; height:40px; line-height:16px;">
&nbsp;&nbsp;| &nbsp;<span style="font-size:24px">$title</span>
</div>
<div style="clear:both"></div>
</div>
</div>
<hr style="border:none;border-top: 1px solid #ccc"/>
<div style="margin-top: 20px; font-size: 14px;">
$body
</div>
<div id="leanoteFooter" style="margin-top: 30px; border-top: 1px solid #ccc">
<style>
#leanoteFooter {
color: #666;
font-size: 12px;
}
#leanoteFooter a {
color: #666;
font-size: 12px;
}
</style>
<a href="http://leanote.com">leanote</a>, your own cloud note
</div>
</div>
</body>
</html>
`
func SendEmail(to, subject, title, body string) bool {
hp := strings.Split(host, ":")
auth := smtp.PlainAuth("", username, password, hp[0])
var content_type string
mailtype := "html"
if mailtype == "html" {
content_type = "Content-Type: text/"+ mailtype + "; charset=UTF-8"
} else{
content_type = "Content-Type: text/plain" + "; charset=UTF-8"
}
// 登录之
body = strings.Replace(bodyTpl, "$body", body, 1)
body = strings.Replace(body, "$title", title, 1)
msg := []byte("To: " + to + "\r\nFrom: " + username + "<"+ username +">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body)
send_to := strings.Split(to, ";")
err := smtp.SendMail(host+":"+port, auth, username, send_to, msg)
if err != nil {
return false
}
return true
}
func SendToLeanote(subject, title, body string) {
to := "leanote@leanote.com"
SendEmail(to, subject, title, body);
}
+45
View File
@@ -0,0 +1,45 @@
package lea
import (
"strings"
"path/filepath"
"os"
)
// 分离文件名与扩展名(包含.)
func SplitFilename(filename string) (baseName, ext string) {
baseName = filename
// 找到最后一个'.'
ext = SubstringByte(filename, strings.LastIndex(filename, "."))
baseName = strings.TrimRight(filename, ext)
ext = strings.ToLower(ext)
return;
}
// 转换文件的格式
// toExt包含.
func TransferExt(path string, toExt string) string {
dir := filepath.Dir(path) + "/" // 文件路径
name := filepath.Base(path) // 文件名 a.jpg
// 获取文件名与路径
baseName, _ := SplitFilename(name)
return dir + baseName + toExt
}
func GetFilename(path string) string {
return filepath.Base(path)
}
// 清空dir下所有的文件和文件夹
// RemoveAll会清空本文件夹, 所以还要创建之
func ClearDir(dir string) bool {
err := os.RemoveAll(dir)
if err != nil {
return false
}
err = os.MkdirAll(dir, 0777)
if err != nil {
return false
}
return true
}
+106
View File
@@ -0,0 +1,106 @@
package lea
import (
"github.com/Terry-Mao/paint"
"github.com/Terry-Mao/paint/wand"
"fmt"
"os"
)
// 传源路径, 在该路径下写入另一个gif
// maxWidth 最大宽度, == 0表示不改变宽度
// 成功后删除
func TransToGif(path string, maxWidth uint, afterDelete bool) (ok bool, transPath string) {
ok = false
transPath = path
wand.Genesis()
defer wand.Terminus()
w := wand.NewMagickWand()
defer w.Destroy()
if err := w.ReadImage(path); err != nil {
fmt.Println(err);
return;
}
width := w.ImageWidth()
height := w.ImageHeight()
if maxWidth != 0 {
if width > maxWidth {
// 等比缩放
height = height * maxWidth/width
width = maxWidth
}
}
w.SetImageFormat("GIF");
if err := paint.Thumbnail(w, width, height); err != nil {
fmt.Println(err);
return;
}
// 判断是否是gif图片, 是就不用转换了
baseName, ext := SplitFilename(path)
var toPath string
if(ext == ".gif") {
toPath = baseName + "_2" + ext
} else {
toPath = TransferExt(path, ".gif");
}
if err := w.WriteImage(toPath); err != nil {
fmt.Println(err);
return;
}
if afterDelete {
os.Remove(path)
}
ok = true
transPath = toPath
return
}
// 缩小到
// 未用
func Reset(path string, maxWidth uint) (ok bool, transPath string){
wand.Genesis()
defer wand.Terminus()
w := wand.NewMagickWand()
defer w.Destroy()
if err := w.ReadImage(path); err != nil {
fmt.Println(err);
return;
}
width := w.ImageWidth()
height := w.ImageHeight()
if maxWidth != 0 {
if width > maxWidth {
// 等比缩放
height = height * maxWidth/width
width = maxWidth
}
}
if err := paint.Thumbnail(w, width, height); err != nil {
fmt.Println(err);
return;
}
toPath := TransferExt(path, ".gif");
if err := w.WriteImage(toPath); err != nil {
fmt.Println(err);
return;
}
ok = true
transPath = toPath
return
}
+1
View File
@@ -0,0 +1 @@
The leanote util package
+269
View File
@@ -0,0 +1,269 @@
package lea
import (
"fmt"
"regexp"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"io"
"labix.org/v2/mgo/bson"
"time"
math_rand "math/rand"
)
// 字符串
// md5
func Md5(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
// Guid
func NewGuid() string {
b := make([]byte, 48)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
}
return Md5(base64.URLEncoding.EncodeToString(b))
}
// 后面加个str生成之, 更有保障, 确保唯一
func NewGuidWith(str string) string {
b := make([]byte, 48)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
}
return Md5(base64.URLEncoding.EncodeToString([]byte(string(b) + str)))
}
// 以byte来截取
func SubstringByte(str string, start int) string {
return substr(str, start, len(str)-start, false)
}
func Substring(str string, start int) string {
return substr(str, start, len(str)-start, true)
}
func Substr(str string, start, length int) string {
return substr(str, start, length, true)
}
func substr(str string, start, length int, isRune bool) string {
rs := []rune(str)
rs2 := []byte(str)
rl := len(rs)
if !isRune {
rl = len(rs2)
}
end := 0
if start < 0 {
start = rl - 1 + start
}
end = start + length
if start > end {
start, end = end, start
}
if start < 0 {
start = 0
}
if start > rl {
start = rl
}
if end < 0 {
end = 0
}
if end > rl {
end = rl
}
if isRune {
return string(rs[start:end])
}
return string(rs2[start:end])
}
// 截取含html的字符串
/*
<div>
abcdef
<p>xxxxxxxxxxx</p>
<img src="" />
<div>
abcdfef
<p>aksdfjsld
dfsadf
--------------- 这里截断
</p>
<p>xxxxxxxxxxxxxxx</p>
abcdef
</div>
</div>
那么只要在最后的html中添加</p></div></div>补全结束即可
思路:
1. 找到截断的位置
2. 去掉成对的标签
3. 去掉不要结束的标签
4. 补全标签
*/
func ReplaceAll(oldStr, pattern, newStr string) string {
p, _ := regexp.Compile(pattern)
s := p.ReplaceAll([]byte(oldStr), []byte(newStr))
return string(s)
}
func SubStringHTML(param string, length int, end string) string {
// 先取出<pre></pre>占位..
result := ""
// 1
n := 0
var temp rune // 中文问题, 用rune来解决
isCode := false //是不是HTML代码
isHTML := false //是不是HTML特殊字符,如&nbsp;
rStr := []rune(param)
for i := 0; i < len(rStr); i++ {
temp = rStr[i]
if temp == '<' {
isCode = true
} else if temp == '&' {
isHTML = true
} else if temp == '>' && isCode {
n = n - 1
isCode = false
} else if temp == ';' && isHTML {
isHTML = false
}
if !isCode && !isHTML {
n = n + 1
}
result += string(temp)
if n >= length {
break
}
}
result += end
// 取出所有标签
tempResult := ReplaceAll(result, "(>)[^<>]*(<?)", "$1$2") // 把标签中间的所有内容都去掉了
// 2. 去掉不需要结素标记的HTML标记
tempResult = ReplaceAll(tempResult, "</?(embed|AREA|BASE|BASEFONT|BR|COL|COLGROUP|DD|DT|FRAME|HEAD|HR|IMG|INPUT|ISINDEX|LI|LINK|META|OPTION|PARAM|area|wbr|br|col|colgroup|dd|dt|frame|hr|img|input|isindex|link|meta|param)[^<>]*/?>", "")
// 把<div class=xxx的class=xxx去掉
tempResult = ReplaceAll(tempResult, "<(/?[a-zA-Z]+)[^<>]*>", "<$1>")
// 3 只能用正则,+stack来去有结束的
// golang的正则暂不支持back reference, 以后可以用它来去掉重复的标签
p, _ := regexp.Compile("<(/?[a-zA-Z]+)[^<>]*>") // 得到所有的<div>, </div>...
strs := p.FindAllString(tempResult, -1)
// fmt.Println(strs)
stack := make([]string, len(strs))
stackP := -1
for _, each := range strs {
if stackP >= 0 {
// 匹配
if stack[stackP][1:] == each[2:] {
stackP--
continue
}
}
stackP++
stack[stackP] = each
}
// 补全tag
if stackP != -1 {
fmt.Println(stack[0 : stackP+1])
for _, each := range stack[0 : stackP+1] {
if each[1] != '/' {
result += "</" + each[1:]
}
}
}
return result
}
// 是否是合格的密码
func IsGoodPwd(pwd string) (bool, string) {
if pwd == "" {
return false, "密码不能为空"
}
if len(pwd) < 6 {
return false, "密码至少6位"
}
return true, ""
}
// 是否是email
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)
return ok
}
// 是否只包含数字, 字母 -, _
func IsUsername(username string) bool {
if username == "" {
return false;
}
ok, _ := regexp.MatchString(`[^0-9a-zA-Z_\-]`, username)
return !ok
}
// 是否是ObjectId
func IsObjectId(id string) (ok bool) {
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
// 证明有错误发生
if err := recover(); err != nil {
ok = false
} else {
ok = true
}
}()
bson.ObjectIdHex(id)
return
}
// 随机密码
// num 几位
func RandomPwd(num int) string {
chars := make([]byte, 62)
j := 0
for i := 48; i <= 57; i++ {
chars[j] = byte(i)
j++
}
for i := 65; i <= 90; i++ {
chars[j] = byte(i)
j++
}
for i := 97; i <= 122; i++ {
chars[j] = byte(i)
j++
}
j--;
str := ""
math_rand.Seed(time.Now().UnixNano())
for i := 0; i < num; i++ {
x := math_rand.Intn(j)
str += string(chars[x])
}
return str
}
+120
View File
@@ -0,0 +1,120 @@
package binder
import (
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/controllers"
"fmt"
"reflect"
"strings"
)
// leanote binder struct
// rewrite revel struct binder
// not need the struct name as prefix,
// eg:
// type Note struct {Name}
// func (c Controller) List(note Note) revel.Result {}
// in revel you must pass the note.Name as key, now you just pass Name
// for test
// map[string][string]
var MSSBinder = revel.Binder{
Bind: func(params *revel.Params, name string, typ reflect.Type) reflect.Value {
var (
result = reflect.MakeMap(typ)
keyType = typ.Key()
valueType = typ.Elem()
)
for paramName, values := range params.Values {
key := paramName // [len(name)+1 : len(paramName)-1]
// 每一个值 values[0]
result.SetMapIndex(revel.BindValue(key, keyType), revel.BindValue(values[0], valueType))
}
return result
},
Unbind: func(output map[string]string, name string, val interface{}) {
mapValue := reflect.ValueOf(val)
for _, key := range mapValue.MapKeys() {
revel.Unbind(output, fmt.Sprintf("%v", key.Interface()),
mapValue.MapIndex(key).Interface())
}
},
}
// struct需要., a.b = "life"
// a: contoller是形参名
// 修改, 默认就是a.b, 传b
func nextKey(key string) string {
fieldLen := strings.IndexAny(key, ".[")
if fieldLen == -1 {
return key
}
return key[:fieldLen]
}
var leanoteStructBinder = revel.Binder{
Bind: func(params *revel.Params, name string, typ reflect.Type) reflect.Value {
result := reflect.New(typ).Elem()
fieldValues := make(map[string]reflect.Value)
for key, _ := range params.Values {
// 这里, 如果没有点, 默认就是a.
// life
var suffix string
var noPrefix = false
if !strings.HasPrefix(key, name + ".") {
noPrefix = true
suffix = key
// continue
} else {
// Get the name of the struct property.
// Strip off the prefix. e.g. foo.bar.baz => bar.baz
suffix = key[len(name)+1:]
}
fieldName := nextKey(suffix) // e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar"
fieldLen := len(fieldName)
if _, ok := fieldValues[fieldName]; !ok {
// Time to bind this field. Get it and make sure we can set it.
fieldValue := result.FieldByName(fieldName)
if !fieldValue.IsValid() {
continue
}
if !fieldValue.CanSet() {
continue
}
var boundVal reflect.Value
// 没有name前缀
if(noPrefix) {
boundVal = revel.Bind(params, key[:fieldLen], fieldValue.Type())
} else {
boundVal = revel.Bind(params, key[:len(name)+1+fieldLen], fieldValue.Type())
}
fieldValue.Set(boundVal)
fieldValues[fieldName] = boundVal
}
}
return result
},
Unbind: func(output map[string]string, name string, iface interface{}) {
val := reflect.ValueOf(iface)
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
structField := typ.Field(i)
fieldValue := val.Field(i)
// PkgPath is specified to be empty exactly for exported fields.
if structField.PkgPath == "" {
revel.Unbind(output, fmt.Sprintf("%s.%s", name, structField.Name), fieldValue.Interface())
}
}
},
}
func init() {
revel.TypeBinders[reflect.TypeOf(info.UserBlogBase{})] = leanoteStructBinder
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(controllers.NoteOrContent{})] = leanoteStructBinder
}
+762
View File
@@ -0,0 +1,762 @@
package html2image
import (
"leanote/app/lea"
"leanote/app/lea/netutil"
"bufio"
"code.google.com/p/draw2d/draw2d"
// "fmt"
"image"
"image/color"
"image/png"
"image/gif"
"image/jpeg"
"os"
// "strings"
// "time"
"github.com/revel/revel"
"code.google.com/p/go.net/html"
"strings"
"strconv"
)
type Html2Image struct {
image *image.RGBA
gc *draw2d.ImageGraphicContext
// 试探
gc2 *draw2d.ImageGraphicContext
width float64 // 图片宽度
height float64
painWidth float64 // 画布宽度
startX float64
x float64
y float64
isFirstP bool // 是否是第一个段落?
// 换行和段落的高度
brY float64
pY float64
// 字体
normalFontFamily draw2d.FontData
boldFontFamily draw2d.FontData
// preTag 之前的标签
preTag *html.Node
}
func NewHtml2Image() *Html2Image {
h := &Html2Image{}
h.width = 440
h.height = 10000 // 最开始设为很大, 不然加不了图片, 会影响速度
i, gc := h.InitGc(h.width, h.height)
h.gc = gc;
h.image = i
// 试探
_, h.gc2 = h.InitGc(h.width, 100)
h.startX = 10
// 最初位置
h.x = h.startX
h.y = 80
h.isFirstP = true
h.normalFontFamily = draw2d.FontData{"xihei", 4, draw2d.FontStyleNormal};
h.boldFontFamily = draw2d.FontData{"heiti", 5, draw2d.FontStyleNormal};
h.SetNormalFont()
return h
}
func (this *Html2Image) InitGc(w, h float64) (* image.RGBA, *draw2d.ImageGraphicContext) {
i := image.NewRGBA(image.Rect(0, 0, int(w), int(h)))
gc := draw2d.NewGraphicContext(i)
gc.SetStrokeColor(image.Black)
gc.SetFillColor(image.White)
// fill the background
// gc.Clear()
draw2d.SetFontFolder(revel.BasePath + "/public/fonts/weibo")
draw2d.Rect(gc, 0, 0, w, h) // 设置背景
gc.FillStroke()
gc.SetFillColor(image.Black)
// 这个很耗时
// gc.Translate(0, 0)
return i, gc
}
func (this *Html2Image) SaveToPngFile(filePath string) bool {
// m := this.image;
m := this.image.SubImage(image.Rect(0, 0, int(this.width), int(this.y + 20)))
// 需要截断之
f, err := os.Create(filePath)
if err != nil {
return false
}
defer f.Close()
b := bufio.NewWriter(f)
err = png.Encode(b, m)
if err != nil {
return false
}
err = b.Flush()
if err != nil {
return false
}
return true
}
// 字体大小
func (this *Html2Image) SetSmallFont() {
this.gc.SetFontData(this.normalFontFamily)
this.gc2.SetFontData(this.normalFontFamily)
this.gc.SetFillColor(color.NRGBA{60, 60, 60, 255})
this.gc.SetFontSize(12)
this.gc2.SetFontSize(12)
this.brY = 16
this.pY = 30
this.painWidth = this.width - 10
}
func (this *Html2Image) SetNormalFont() {
this.gc.SetFillColor(image.Black)
this.gc.SetFontData(this.normalFontFamily)
this.gc2.SetFontData(this.normalFontFamily)
this.gc.SetFontSize(14)
this.gc2.SetFontSize(14)
this.brY = 20
this.pY = 30
this.painWidth = this.width - 10
}
func (this *Html2Image) SetAColor() {
this.gc.SetFillColor(color.NRGBA{66, 139, 202, 255})
}
// 标题
func (this *Html2Image) SetTitleFont() {
this.gc.SetFillColor(image.Black)
this.gc.SetFontData(this.boldFontFamily)
this.gc2.SetFontData(this.boldFontFamily)
this.gc.SetFontSize(24)
this.gc2.SetFontSize(24)
this.brY = 30
this.pY = 60
this.painWidth = this.width - 100
}
// h1, h2...
// h1
func (this *Html2Image) SetHeadFont(h string) {
this.gc.SetFillColor(image.Black)
this.gc.SetFontData(this.boldFontFamily)
this.gc2.SetFontData(this.boldFontFamily)
this.painWidth = this.width - 50
if h == "h1" {
this.gc.SetFontSize(20)
this.gc2.SetFontSize(20)
this.brY = 30
this.pY = 60
} else if h == "h2" {
this.gc.SetFontSize(16)
this.gc2.SetFontSize(16)
this.brY = 25
this.pY = 50
} else if h == "h3" || h == "h4" {
this.gc.SetFontSize(14)
this.gc2.SetFontSize(14)
this.brY = 20
this.pY = 40
}
}
// 新的段落
func (this *Html2Image) NewP() {
// if !this.isFirstP {
this.x = this.startX;
this.y += this.pY;
// } else {
// this.isFirstP = false
// }
}
// 新一行
func (this *Html2Image) NewBr() {
this.x = this.startX;
this.y += this.brY;
}
// 是否超出了
// 1 个汉字 17.875
// 1 个字母最多 17.15625
func (this *Html2Image) IsOver(r []rune) bool {
// fmt.Println(string(r))
// 还有多宽
// 就算全拿汉字来说
// 这里是优化,速度有提升
/*
if this.painWidth - this.x > 17.875 * float64(len(r)) {
return false
}
*/
// println(text)
width2 := this.gc2.FillStringAt(string(r), 0, 0)
// 以下的方法可以极大节约时间
// a, b, c, d := this.gc2.GetStringBounds(string(r))
// width2 := c - a + 2
// fmt.Println(width2)
// fmt.Println(c - a)
// 小于, 那么需要->大 到第一个不合适的位置
if width2 + this.x <= this.painWidth {
return false
}
return true
}
// 是否是字母
func (this *Html2Image) isAlpha(word rune) bool {
if (word >= 'a' && word <= 'z') || (word >= 'A' && word <= 'Z') {
return true
}
return false
}
// 是否是标点, 是标点就包含进来
func (this *Html2Image) includePunctuation(r []rune, end int) int {
if len(r) == end {
return end
}
c := r[end]
if c == ',' || c == '.' || c == '?' || c == ':' || c == ';' || c == '!' || c == ',' || c == '。' || c == '?' || c == ':' || c == ';' || c == '!' {
return end + 1
}
return end;
}
// 插入文本
// 要判断是否太长了, 太长了就截断
func (this *Html2Image) InsertText(text string, needTest bool, prefix string) {
if needTest && this.painWidth - this.x < 2 {
// 另起一行
this.NewBr()
this.InsertText(text, true, prefix)
return;
}
r := []rune(text)
// 试探吧, 可能需要截取
if !needTest || !this.IsOver(r) {
// 不用截
// 可能有\n
width := this.gc.FillStringAt(prefix + text, this.x, this.y)
this.x += width + 1
} else {
// 刚开始加10个字, 之后一个一个来
// 一个汉字, 或一个单词加
wordStart := false
wordStartPos := 0
maxRI := len(r) - 1
for i, word := range r { // i 是0, 1, 2, 3...
// i是byte的位置, 一个汉字占3位
// 是字母
if this.isAlpha(word) {
if !wordStart {
wordStart = true
wordStartPos = i
}
} else if(word == '\n' || word == '\r') {
// 是否是\n
i = this.includePunctuation(r, i)
this.InsertText(string(r[0:i]), false, prefix)
this.NewBr()
// 之后的
if maxRI != i {
this.InsertText(string(r[i+1:]), true, prefix)
}
return;
} else {
// 单词没结束不计算
wordStart = false
if i > 0 {
// 这里计算是否超出了, 包含自己在内
if this.IsOver(r[0:i+1]) {
// 那么...回退前一个
end := i
// 如果上一个是单词, 那么整个单词都不要, 取单词开头
if this.isAlpha(r[i-1]) {
end = wordStartPos
// 这一行全是这个单词, 不太现实, 但有可能, 只能截断了
if end == 0 {
end = i
}
}
// 这一段写上
// println("------>" + string(r[0:end]))
// 这里, 判断后面一个是否是标点符号
end = this.includePunctuation(r, end)
this.InsertText(string(r[0:end]), false, prefix)
this.NewBr()
// 之后的
this.InsertText(string(r[end:]), true, prefix)
return;
} else {
// 没超出, 不用计算, 但出要看是否是结尾了
// 怎么可能会出现这种情况呢?, 第一步就试了
/*
if i+1 == len(text) {
println("不可能")
}
*/
}
}
}
} // for
// !!
// 如果是 go get code.google.com/p/graphics-go/graphics 最后是字母, 怎么办?
if wordStart {
// 这里计算是否超出了, 包含自己在内
end := maxRI + 1
// 如果上一个是单词, 那么整个单词都不要, 取单词开头
if this.isAlpha(r[maxRI]) {
end = wordStartPos
// 这一行全是这个单词, 不太现实, 但有可能, 只能截断了
if end == 0 {
end = maxRI + 1
}
}
// 这一段写上
// println("-e----->" + string(r[0:end]))
// 这里, 判断后面一个是否是标点符号
end = this.includePunctuation(r, end)
this.InsertText(string(r[0:end]), false, prefix)
this.NewBr()
// 之后的
this.InsertText(string(r[end:]), true, prefix)
return;
}
}
}
// 设置页脚, url文章链接
func (this *Html2Image) SetBottom(username, url string) {
// 画一条线
this.NewBr()
this.gc.MoveTo(this.x, this.y)
this.gc.LineTo(this.painWidth, this.y)
this.gc.SetStrokeColor(color.NRGBA{200, 0, 0, 255})
this.gc.SetLineWidth(2)
this.gc.FillStroke()
this.SetSmallFont()
// 左侧写字
this.NewP()
this.InsertText("本文来自 " + username + " 的leanote笔记", true, " ")
this.NewBr()
this.InsertText("个人博客: ", false, " ")
siteUrl, _ := revel.Config.String("site.url")
if siteUrl == "" {
siteUrl = "http://leanote.com"
}
this.InsertA(siteUrl + "/blog/" + username, false)
this.setLogo()
/*
this.painWidth = this.width - 100
this.NewP()
this.InsertText("leanote, 不一样的笔记.", false, " ")
this.NewBr()
this.InsertText("在这里你可以管理自己的知识", false, " ")
this.NewBr()
this.InsertText("将知识分享给好友, 与好友一起协作知识", false, " ")
this.NewBr()
this.InsertText("并且还可以将笔记设为博客公开", false, " ")
this.InsertText(". 赶紧加入吧! leanote.com", false, "")
// Logo
*/
}
func (this *Html2Image) setImage(path string, x, y float64) {
f1, err := os.Open(path)
if err != nil {
return;
panic(err)
}
var m1 image.Image
_, ext := lea.SplitFilename(path)
if ext == ".png" {
m1, err = png.Decode(f1)
} else if ext == ".gif" {
m1, err = gif.Decode(f1)
} else if ext == ".jpg" {
m1, err = jpeg.Decode(f1)
}
if err != nil {
return
panic(err)
}
this.gc.Translate(x, y)
this.gc.DrawImage(m1)
this.gc.Translate(-x, -y)
}
// 画leanote logo
func (this *Html2Image) setLogo() {
// 右上角的logo
path := revel.BasePath + "/public/images/leanote/logo-20-a-6.png"
println(path)
this.setImage(path, 320, 10)
/*
// 右下角设置Logo
path = revel.BasePath + "/public/images/leanote/logo-60-a-6.png"
this.setImage(path, 320, this.y - 75)
*/
}
// 插入链接
func (this *Html2Image) InsertA(text string, isNormal bool) {
if text == "" {
return
}
this.SetAColor()
this.InsertText(text, true, "")
// 还原
if isNormal {
this.SetNormalFont()
} else {
this.SetSmallFont()
}
}
// 文章标题
func (this *Html2Image) InsertTitle(title string) {
oldX := this.x
oldY := this.y - 35
// 插入之
this.SetTitleFont()
this.InsertText(title, true, " ")
// 还原字体大小
this.SetNormalFont()
this.NewBr()
this.gc.MoveTo(oldX, oldY)
this.gc.LineTo(this.x, this.y - 10)
this.gc.SetStrokeColor(color.NRGBA{200, 0, 0, 255})
this.gc.SetLineWidth(5)
this.gc.FillStroke()
}
// 插入h1, h2, ... h4
func (this *Html2Image) InsertHead(n *html.Node) {
this.SetHeadFont(n.Data); // h1, h2...
this.NewP()
// 把标题内容全都拿出
var text = ""
for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == html.ElementNode && (c.Data == "p" || c.Data == "br"){
text += " "
} else {
text += strings.TrimRight(c.Data, "\n")
}
}
this.InsertText(text, true, "")
this.SetNormalFont()
}
// 插入代码
func (this *Html2Image) InsertCode(n *html.Node) {
this.NewP()
oldX := this.x
oldY := this.y - 20
for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == html.ElementNode && (c.Data == "p" || c.Data == "br"){
this.NewBr()
} else {
this.InsertText(strings.TrimRight(c.Data, "\n"), true, " ")
}
}
this.NewBr()
this.gc.MoveTo(oldX, oldY)
this.gc.LineTo(this.x, this.y - 20)
this.gc.SetStrokeColor(color.NRGBA{0, 200, 0, 255})
this.gc.SetLineWidth(2)
this.gc.FillStroke()
}
// 插入图片
// 这个path应该是url,
// http://abc.com/a.gif 需要先下载
// 或 /upload/a.gif
func (this *Html2Image) InsertImage(path string, needTrans bool, width uint) {
if path == "" {
return;
}
// 是url, 那么取网络图片之
var ok bool
if strings.HasPrefix(path, "http") || strings.HasPrefix(path, "//") {
path, ok = netutil.WriteUrl(path, "/tmp")
if !ok || path == ""{
return
}
} else {
path = revel.BasePath + "/public/" + path
}
// 需要转换, logo不需要转换
if(needTrans) {
painWidth := uint(this.painWidth - 10)
if width > 0 && painWidth > width {
painWidth = width
}
ok, path = lea.TransToGif(path, painWidth, false)
if !ok || path == "" {
return;
}
}
f1, err := os.Open(path)
if err != nil {
return;
panic(err)
}
var m1 image.Image
_, ext := lea.SplitFilename(path)
if ext == ".png" {
m1, err = png.Decode(f1)
} else {
m1, err = gif.Decode(f1)
}
if err != nil {
return
panic(err)
}
// 如果之前是p, 那么不要有<br>
if this.preTag.Data != "p" {
this.NewBr()
}
this.gc.Translate(this.x, this.y)
this.gc.DrawImage(m1)
// 还原
this.gc.Translate(-this.x, -this.y) // 这个有用些
this.y += float64(m1.Bounds().Dy()) - 20
this.NewP()
os.Remove(path)
// 如果图片是文章第一个的话, 之后的需要p
this.isFirstP = false
}
// 内容主体
func (this *Html2Image) InsertBody(htmlStr string) (ok bool) {
reader := bufio.NewReader(strings.NewReader(htmlStr))
doc, err := html.Parse(reader)
if err != nil {
return;
}
var f func(*html.Node, *html.Node, string)
f = func(n *html.Node, p *html.Node, prefix string) {
// if p != nil {
// fmt.Println("Parent Data: " + p.Data)
// }
defer func() {
if n.Type == html.ElementNode {
this.preTag = n
}
}()
// 标签
if n.Type == html.ElementNode {
if n.Data == "p" {
this.NewP()
// 遍历之后的
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c, n, prefix)
}
return;
}
// 也是一个段落, 只是要缩进
if n.Data == "ul" || n.Data == "ol" {
this.NewP()
// 遍历之后的
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c, n, "")
}
return;
}
if n.Data == "li" {
// 遍历之后的
// 是否需要前缀
needPrefix := true // 第一个肯定要
for c := n.FirstChild; c != nil; c = c.NextSibling {
if needPrefix {
f(c, n, " ")
needPrefix = false
} else {
f(c, n, "")
}
if c.Type == html.ElementNode {
if c.Data == "br" || c.Data == "p" {
needPrefix = true
} else {
needPrefix = false
}
}
}
// 输完一行后再换行
this.NewBr()
return;
}
// 标题
if n.Data == "h1" || n.Data == "h2" || n.Data == "h3" || n.Data == "h4" {
this.InsertHead(n)
return;
}
if n.Data == "pre" {
// 把之后的全拿过来
this.InsertCode(n)
return;
}
// 图片
// 得到src
if n.Data == "img" {
src := ""
width := 0
for _, attr := range n.Attr {
if attr.Key == "src" {
src = attr.Val
} else if attr.Key == "width" {
width, _ = strconv.Atoi(attr.Val)
}
}
if src != "" {
this.InsertImage(src, true, uint(width))
}
return;
}
// 链接
// 如果链接里只有文本, 那么单独处理, 如果还有其它的, 不作链接处理
if n.Data == "a" {
if n.FirstChild == n.LastChild {
this.InsertA(n.FirstChild.Data, true)
return;
}
}
// 空行
if n.Data == "br"/* || n.Data == "div"*/ {
this.NewBr()
}
}
// 是文本, 输出之
if n.Type == html.TextNode {
data := strings.TrimSpace(n.Data);
// <p>xx<br/>xxx</p> 这些空白也是TextNode <p>
if data != "" {
this.InsertText(prefix + data, true, "")
}
return;
}
// 其余的
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c, n, prefix)
}
return;
}
f(doc, nil, "")
return true
}
// 主函数
func ToImage(uid, username, noteId, title, htmlStr, toPath string) (ok bool) {
h := NewHtml2Image()
// 标题
h.InsertTitle(title)
// 主体
ok = h.InsertBody(htmlStr)
if(!ok) {
return
}
// 页眉与页脚
h.SetBottom(username, "")
// 保存成png图片
ok = h.SaveToPngFile(toPath)
return
}
func TestFillString() {
h := NewHtml2Image()
str := `一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace,很喜欢那里的氛围,希望国内其他地方也能有更多这类空间供创客发挥。
假如你有一个比较成型的想法,想在硬件领域做点事情,核心团队也基本组好,硬件软件交互基本都有专人了`
// h.IsOver("W")
h.InsertText("go get code.google.com/p/graphics\n-go/graphics", true, "")
// h.InsertText("usr/bin/install: 无法创建一般文件'/usr/local/jpeg6/include/jconfig.", true)
// h.InsertImage("/Users/life/Desktop/share.png")
// h.NewP()
h.InsertText(str, true, "")
// h.InsertImage("/Users/life/Desktop/share.png")
h.SaveToPngFile("/Users/life/Desktop/TestPath3.png")
}
+26
View File
@@ -0,0 +1,26 @@
package memcache
import (
"github.com/robfig/gomemcache/memcache"
"encoding/json"
)
func Set(key string, value map[string]string, expiration int32) {
// 把value转成byte
bytes, _ := json.Marshal(value)
if expiration == -1 {
expiration = 30 * 24 * 60 * 60 // 30天
}
client.Set(&memcache.Item{Key: key, Value: bytes, Expiration: expiration})
}
func Get(key string) map[string]string {
item, err := client.Get(key)
if err != nil {
return nil
}
m := map[string]string{}
json.Unmarshal(item.Value, &m)
return m
}
+11
View File
@@ -0,0 +1,11 @@
package memcache
import (
"github.com/robfig/gomemcache/memcache"
)
var client *memcache.Client
func init() {
// client = memcache.New("localhost:11211")
}
+93
View File
@@ -0,0 +1,93 @@
package netutil
import (
"strings"
"os"
// "path/filepath"
"net/http"
"io/ioutil"
. "leanote/app/lea"
)
// net的util
// toPath 文件保存的目录
// 默认是/tmp
// 返回文件的完整目录
func WriteUrl(url string, toPath string) (path string, ok bool) {
if url == "" {
return;
}
content, err := GetContent(url)
if err != nil {
return;
}
// a.html?a=a11&xxx
url = trimQueryParams(url)
_, ext := SplitFilename(url)
if toPath == "" {
toPath = "/tmp"
}
// dir := filepath.Dir(toPath)
newFilename := NewGuid() + ext
fullPath := toPath + "/" + newFilename
/*
if err := os.MkdirAll(dir, 0777); err != nil {
return
}
*/
// 写到文件中
file, err := os.Create(fullPath)
defer file.Close()
if err != nil {
return
}
file.Write(content)
path = fullPath
ok = true
return
}
// 得到内容
func GetContent(url string) (content []byte, err error) {
var resp *http.Response
resp, err = http.Get(url)
if(resp != nil && resp.Body != nil) {
defer resp.Body.Close()
} else {
}
if resp == nil || resp.Body == nil || err != nil || resp.StatusCode != http.StatusOK {
return
}
var buf []byte
buf, err = ioutil.ReadAll(resp.Body)
if(err != nil) {
return
}
content = buf;
err = nil
return
}
// 将url ?, #后面的字符串去掉
func trimQueryParams(url string) string {
pos := strings.Index(url, "?");
if pos != -1 {
url = Substr(url, 0, pos);
}
pos = strings.Index(url, "#");
if pos != -1 {
url = Substr(url, 0, pos);
}
pos = strings.Index(url, "!");
if pos != -1 {
url = Substr(url, 0, pos);
}
return url;
}
+31
View File
@@ -0,0 +1,31 @@
package session
import (
"github.com/robfig/revel"
"leanote/app/lea/memcache"
// . "leanote/app/lea"
)
// 使用filter
// 很巧妙就使用了memcache来处理session
// revel的session(cookie)只存sessionId, 其它信息存在memcache中
func SessionFilter(c *revel.Controller, fc []revel.Filter) {
sessionId := c.Session.Id()
// 从memcache中得到cache, 赋给session
cache := revel.Session(memcache.Get(sessionId))
if cache == nil {
cache = revel.Session{}
cache.Id()
}
c.Session = cache
fc[0](c, fc[1:])
// 再把session保存之
memcache.Set(sessionId, c.Session, -1)
// 只留下sessionId
c.Session = revel.Session{revel.SESSION_ID_KEY: sessionId}
}
+1
View File
@@ -0,0 +1 @@
Compress and combine js files
+141
View File
@@ -0,0 +1,141 @@
package main
import (
"fmt"
"os"
"os/exec"
"io/ioutil"
"strings"
)
/*
用golang exec 总是说找不到uglifyjs命令, 需要全部路径
而且node, npm要在/usr/bin下, 已建ln
*/
/*
<script src="js/jquery-1.9.0.min.js"></script>
<!-- 以后将所有的js压缩合并成一个文件 -->
<script src="js/jquery-cookie.js"></script>
<script src="js/bootstrap.js"></script>
<script type="text/javascript" src="tinymce/tinymce.js"></script>
<script src="js/common.js"></script>
<script src="js/app/note.js"></script>
<script src="js/app/tag.js"></script>
<script src="js/app/notebook.js"></script>
<script src="js/app/share.js"></script>
<script src="js/object_id.js"></script>
<script type="text/javascript" src="js/ZeroClipboard/ZeroClipboard.js"></script>
*/
//var jss = []string{"js/jquery-cookie", "js/bootstrap"}
var jss = []string{"js/jquery-cookie", "js/bootstrap",
"js/common", "js/app/note", "js/app/tag", "js/app/notebook", "js/app/share",
"js/object_id", "js/ZeroClipboard/ZeroClipboard"}
var base = "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/public/"
var cmdPath = "/usr/local/bin/uglifyjs"
func cmdError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "The command failed to perform: %s (Command: %s, Arguments: %s)", err, "", "")
} else {
fmt.Println("OK")
}
}
// filename没有扩展名
func compressJs(filename string) {
source := base + filename + ".js"
to := base + filename + "-min.js"
cmd := exec.Command(cmdPath, source, "-o", to)
_, err := cmd.CombinedOutput()
cmdError(err)
}
func combineJs() {
// 生成一个总文件
cmd := exec.Command("rm", base + "js/all.js")
_, err := cmd.CombinedOutput()
cmdError(err)
for _, js := range jss {
to := base + js + "-min.js"
compressJs(js)
// 每个压缩后的文件放入之
cmd2 := exec.Command("/bin/sh", "-c", "cat " + to + " >> " + base + "js/all.js")
_, err := cmd2.CombinedOutput()
cmdError(err)
cmd2 = exec.Command("/bin/sh", "-c", "cat \n >> " + base + "js/all.js")
_, err = cmd2.CombinedOutput()
cmdError(err)
}
}
// 改note-dev->note
func dev() {
// 即替换note.js->note-min.js
m := map[string]string{"note.js": "note-min.js",
"app.js": "app-min.js",
"page.js": "page-min.js",
"common.js": "common-min.js",
"notebook.js": "notebook-min.js",
"share.js": "share-min.js",
"tag.js": "tag-min.js",
"jquery.contextmenu.js": "jquery.contextmenu-min.js",
"editor/editor.js": "editor/editor-min.js",
"/public/mdeditor/editor/scrollLink.js": "/public/mdeditor/editor/scrollLink-min.js",
"console.log(o);": "",
}
path := "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/src/views/note/note-dev.html"
target := "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/src/views/note/note.html"
bs, _ := ioutil.ReadFile(path)
content := string(bs)
print(content)
for key, value := range m {
content = strings.Replace(content, key, value, -1)
}
ioutil.WriteFile(target, []byte(content), os.ModeAppend)
}
// 压缩js成一块
func tinymce() {
// cmdStr := "node_modules/jake/bin/cli.js minify bundle[themes:modern,plugins:table,paste,advlist,autolink,link,image,lists,charmap,hr,searchreplace,visualblocks,visualchars,code,nav,tabfocus,contextmenu,directionality,codemirror,codesyntax,textcolor,fullpage]"
// cmd := exec.Command("/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master/node_modules/jake/bin/cli.js", "minify", "bundle[themes:modern,plugins:table,paste,advlist,autolink,link,image,lists,charmap,hr,searchreplace,visualblocks,visualchars,code,nav,tabfocus,contextmenu,directionality,codemirror,codesyntax,textcolor,fullpage]")
cmd := exec.Command("/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master/node_modules/jake/bin/cli.js", "minify")
cmd.Dir = "/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master"
_, err := cmd.CombinedOutput()
cmdError(err)
}
func main() {
dev();
// 其它零散的需要压缩的js
otherJss := []string{"tinymce/tinymce", "js/app/page", "js/contextmenu/jquery.contextmenu",
"mdeditor/editor/scrollLink",
"mdeditor/editor/editor",
"mdeditor/editor/jquery.waitforimages",
"mdeditor/editor/pagedown/local/Markdown.local.zh",
"mdeditor/editor/pagedown/Markdown.Editor",
"mdeditor/editor/pagedown/Markdown.Sanitizer",
"mdeditor/editor/pagedown/Markdown.Converter",
"mdeditor/editor/Markdown.Extra",
"mdeditor/editor/underscore",
"mdeditor/editor/mathJax",
"js/jQuery-slimScroll-1.3.0/jquery.slimscroll",
}
for _, js := range otherJss {
compressJs(js)
}
// 先压缩后合并
combineJs()
// 压缩tinymce
tinymce()
}
+812
View File
@@ -0,0 +1,812 @@
// GENERATED CODE - DO NOT EDIT
package routes
import "github.com/revel/revel"
type tBaseController struct {}
var BaseController tBaseController
func (_ tBaseController) E404(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("BaseController.E404", args).Url
}
type tTestRunner struct {}
var TestRunner tTestRunner
func (_ tTestRunner) Index(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("TestRunner.Index", args).Url
}
func (_ tTestRunner) Run(
suite string,
test string,
) string {
args := make(map[string]string)
revel.Unbind(args, "suite", suite)
revel.Unbind(args, "test", test)
return revel.MainRouter.Reverse("TestRunner.Run", args).Url
}
func (_ tTestRunner) List(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("TestRunner.List", args).Url
}
type tStatic struct {}
var Static tStatic
func (_ tStatic) Serve(
prefix string,
filepath string,
) string {
args := make(map[string]string)
revel.Unbind(args, "prefix", prefix)
revel.Unbind(args, "filepath", filepath)
return revel.MainRouter.Reverse("Static.Serve", args).Url
}
func (_ tStatic) ServeModule(
moduleName string,
prefix string,
filepath string,
) string {
args := make(map[string]string)
revel.Unbind(args, "moduleName", moduleName)
revel.Unbind(args, "prefix", prefix)
revel.Unbind(args, "filepath", filepath)
return revel.MainRouter.Reverse("Static.ServeModule", args).Url
}
type tFile struct {}
var File tFile
func (_ tFile) UploadImage(
renderHtml string,
) string {
args := make(map[string]string)
revel.Unbind(args, "renderHtml", renderHtml)
return revel.MainRouter.Reverse("File.UploadImage", args).Url
}
func (_ tFile) UploadBlogLogo(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("File.UploadBlogLogo", args).Url
}
func (_ tFile) UploadImageJson(
renderHtml string,
) string {
args := make(map[string]string)
revel.Unbind(args, "renderHtml", renderHtml)
return revel.MainRouter.Reverse("File.UploadImageJson", args).Url
}
type tNotebook struct {}
var Notebook tNotebook
func (_ tNotebook) Index(
notebook interface{},
i int,
name string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebook", notebook)
revel.Unbind(args, "i", i)
revel.Unbind(args, "name", name)
return revel.MainRouter.Reverse("Notebook.Index", args).Url
}
func (_ tNotebook) DeleteNotebook(
notebookId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
return revel.MainRouter.Reverse("Notebook.DeleteNotebook", args).Url
}
func (_ tNotebook) AddNotebook(
notebookId string,
title string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "title", title)
return revel.MainRouter.Reverse("Notebook.AddNotebook", args).Url
}
func (_ tNotebook) UpdateNotebookTitle(
notebookId string,
title string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "title", title)
return revel.MainRouter.Reverse("Notebook.UpdateNotebookTitle", args).Url
}
type tNoteContentHistory struct {}
var NoteContentHistory tNoteContentHistory
func (_ tNoteContentHistory) ListHistories(
noteId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
return revel.MainRouter.Reverse("NoteContentHistory.ListHistories", args).Url
}
type tNote struct {}
var Note tNote
func (_ tNote) Index(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Note.Index", args).Url
}
func (_ tNote) ListNotes(
notebookId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
return revel.MainRouter.Reverse("Note.ListNotes", args).Url
}
func (_ tNote) ListTrashNotes(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Note.ListTrashNotes", args).Url
}
func (_ tNote) GetNoteContent(
noteId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
return revel.MainRouter.Reverse("Note.GetNoteContent", args).Url
}
func (_ tNote) UpdateNoteOrContent(
noteOrContent controllers.NoteOrContent,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteOrContent", noteOrContent)
return revel.MainRouter.Reverse("Note.UpdateNoteOrContent", args).Url
}
func (_ tNote) DeleteNote(
noteId string,
userId string,
isShared bool,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "userId", userId)
revel.Unbind(args, "isShared", isShared)
return revel.MainRouter.Reverse("Note.DeleteNote", args).Url
}
func (_ tNote) DeleteTrash(
noteId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
return revel.MainRouter.Reverse("Note.DeleteTrash", args).Url
}
func (_ tNote) MoveNote(
noteId string,
notebookId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "notebookId", notebookId)
return revel.MainRouter.Reverse("Note.MoveNote", args).Url
}
func (_ tNote) CopyNote(
noteId string,
notebookId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "notebookId", notebookId)
return revel.MainRouter.Reverse("Note.CopyNote", args).Url
}
func (_ tNote) CopySharedNote(
noteId string,
notebookId string,
fromUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "fromUserId", fromUserId)
return revel.MainRouter.Reverse("Note.CopySharedNote", args).Url
}
func (_ tNote) SearchNote(
key string,
) string {
args := make(map[string]string)
revel.Unbind(args, "key", key)
return revel.MainRouter.Reverse("Note.SearchNote", args).Url
}
func (_ tNote) SearchNoteByTags(
tags []string,
) string {
args := make(map[string]string)
revel.Unbind(args, "tags", tags)
return revel.MainRouter.Reverse("Note.SearchNoteByTags", args).Url
}
func (_ tNote) Html2Image(
noteId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
return revel.MainRouter.Reverse("Note.Html2Image", args).Url
}
type tShare struct {}
var Share tShare
func (_ tShare) AddShareNote(
noteId string,
emails []string,
perm int,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "emails", emails)
revel.Unbind(args, "perm", perm)
return revel.MainRouter.Reverse("Share.AddShareNote", args).Url
}
func (_ tShare) AddShareNotebook(
notebookId string,
emails []string,
perm int,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "emails", emails)
revel.Unbind(args, "perm", perm)
return revel.MainRouter.Reverse("Share.AddShareNotebook", args).Url
}
func (_ tShare) ListShareNotes(
notebookId string,
userId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "userId", userId)
return revel.MainRouter.Reverse("Share.ListShareNotes", args).Url
}
func (_ tShare) GetShareNoteContent(
noteId string,
sharedUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "sharedUserId", sharedUserId)
return revel.MainRouter.Reverse("Share.GetShareNoteContent", args).Url
}
func (_ tShare) ListNoteShareUserInfo(
noteId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
return revel.MainRouter.Reverse("Share.ListNoteShareUserInfo", args).Url
}
func (_ tShare) ListNotebookShareUserInfo(
notebookId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
return revel.MainRouter.Reverse("Share.ListNotebookShareUserInfo", args).Url
}
func (_ tShare) UpdateShareNotePerm(
noteId string,
perm int,
toUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "perm", perm)
revel.Unbind(args, "toUserId", toUserId)
return revel.MainRouter.Reverse("Share.UpdateShareNotePerm", args).Url
}
func (_ tShare) UpdateShareNotebookPerm(
notebookId string,
perm int,
toUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "perm", perm)
revel.Unbind(args, "toUserId", toUserId)
return revel.MainRouter.Reverse("Share.UpdateShareNotebookPerm", args).Url
}
func (_ tShare) DeleteShareNote(
noteId string,
toUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "toUserId", toUserId)
return revel.MainRouter.Reverse("Share.DeleteShareNote", args).Url
}
func (_ tShare) DeleteShareNotebook(
notebookId string,
toUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "toUserId", toUserId)
return revel.MainRouter.Reverse("Share.DeleteShareNotebook", args).Url
}
func (_ tShare) DeleteShareNoteBySharedUser(
noteId string,
fromUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "fromUserId", fromUserId)
return revel.MainRouter.Reverse("Share.DeleteShareNoteBySharedUser", args).Url
}
func (_ tShare) DeleteShareNotebookBySharedUser(
notebookId string,
fromUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "fromUserId", fromUserId)
return revel.MainRouter.Reverse("Share.DeleteShareNotebookBySharedUser", args).Url
}
func (_ tShare) DeleteUserShareNoteAndNotebook(
fromUserId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "fromUserId", fromUserId)
return revel.MainRouter.Reverse("Share.DeleteUserShareNoteAndNotebook", args).Url
}
type tUser struct {}
var User tUser
func (_ tUser) UpdateUsername(
username string,
) string {
args := make(map[string]string)
revel.Unbind(args, "username", username)
return revel.MainRouter.Reverse("User.UpdateUsername", args).Url
}
func (_ tUser) UpdatePwd(
oldPwd string,
pwd string,
) string {
args := make(map[string]string)
revel.Unbind(args, "oldPwd", oldPwd)
revel.Unbind(args, "pwd", pwd)
return revel.MainRouter.Reverse("User.UpdatePwd", args).Url
}
func (_ tUser) UpdateTheme(
theme string,
) string {
args := make(map[string]string)
revel.Unbind(args, "theme", theme)
return revel.MainRouter.Reverse("User.UpdateTheme", args).Url
}
func (_ tUser) SendRegisterEmail(
content string,
toEmail string,
) string {
args := make(map[string]string)
revel.Unbind(args, "content", content)
revel.Unbind(args, "toEmail", toEmail)
return revel.MainRouter.Reverse("User.SendRegisterEmail", args).Url
}
func (_ tUser) ReSendActiveEmail(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("User.ReSendActiveEmail", args).Url
}
func (_ tUser) UpdateEmailSendActiveEmail(
email string,
) string {
args := make(map[string]string)
revel.Unbind(args, "email", email)
return revel.MainRouter.Reverse("User.UpdateEmailSendActiveEmail", args).Url
}
func (_ tUser) UpdateEmail(
token string,
) string {
args := make(map[string]string)
revel.Unbind(args, "token", token)
return revel.MainRouter.Reverse("User.UpdateEmail", args).Url
}
func (_ tUser) ActiveEmail(
token string,
) string {
args := make(map[string]string)
revel.Unbind(args, "token", token)
return revel.MainRouter.Reverse("User.ActiveEmail", args).Url
}
func (_ tUser) AddAccount(
email string,
pwd string,
) string {
args := make(map[string]string)
revel.Unbind(args, "email", email)
revel.Unbind(args, "pwd", pwd)
return revel.MainRouter.Reverse("User.AddAccount", args).Url
}
func (_ tUser) UpdateColumnWidth(
notebookWidth int,
noteListWidth int,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookWidth", notebookWidth)
revel.Unbind(args, "noteListWidth", noteListWidth)
return revel.MainRouter.Reverse("User.UpdateColumnWidth", args).Url
}
func (_ tUser) UpdateLeftIsMin(
leftIsMin bool,
) string {
args := make(map[string]string)
revel.Unbind(args, "leftIsMin", leftIsMin)
return revel.MainRouter.Reverse("User.UpdateLeftIsMin", args).Url
}
type tAuth struct {}
var Auth tAuth
func (_ tAuth) Login(
email string,
) string {
args := make(map[string]string)
revel.Unbind(args, "email", email)
return revel.MainRouter.Reverse("Auth.Login", args).Url
}
func (_ tAuth) DoLogin(
email string,
pwd string,
) string {
args := make(map[string]string)
revel.Unbind(args, "email", email)
revel.Unbind(args, "pwd", pwd)
return revel.MainRouter.Reverse("Auth.DoLogin", args).Url
}
func (_ tAuth) Logout(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Auth.Logout", args).Url
}
func (_ tAuth) Demo(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Auth.Demo", args).Url
}
func (_ tAuth) Register(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Auth.Register", args).Url
}
func (_ tAuth) DoRegister(
email string,
pwd string,
) string {
args := make(map[string]string)
revel.Unbind(args, "email", email)
revel.Unbind(args, "pwd", pwd)
return revel.MainRouter.Reverse("Auth.DoRegister", args).Url
}
func (_ tAuth) FindPassword(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Auth.FindPassword", args).Url
}
func (_ tAuth) DoFindPassword(
email string,
) string {
args := make(map[string]string)
revel.Unbind(args, "email", email)
return revel.MainRouter.Reverse("Auth.DoFindPassword", args).Url
}
func (_ tAuth) FindPassword2(
token string,
) string {
args := make(map[string]string)
revel.Unbind(args, "token", token)
return revel.MainRouter.Reverse("Auth.FindPassword2", args).Url
}
func (_ tAuth) FindPasswordUpdate(
token string,
pwd string,
) string {
args := make(map[string]string)
revel.Unbind(args, "token", token)
revel.Unbind(args, "pwd", pwd)
return revel.MainRouter.Reverse("Auth.FindPasswordUpdate", args).Url
}
type tBlog struct {}
var Blog tBlog
func (_ tBlog) SetNote2Blog(
noteId string,
isBlog bool,
isTop bool,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
revel.Unbind(args, "isBlog", isBlog)
revel.Unbind(args, "isTop", isTop)
return revel.MainRouter.Reverse("Blog.SetNote2Blog", args).Url
}
func (_ tBlog) SetNotebook2Blog(
notebookId string,
isBlog bool,
) string {
args := make(map[string]string)
revel.Unbind(args, "notebookId", notebookId)
revel.Unbind(args, "isBlog", isBlog)
return revel.MainRouter.Reverse("Blog.SetNotebook2Blog", args).Url
}
func (_ tBlog) Index(
userId string,
notebookId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "userId", userId)
revel.Unbind(args, "notebookId", notebookId)
return revel.MainRouter.Reverse("Blog.Index", args).Url
}
func (_ tBlog) View(
noteId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "noteId", noteId)
return revel.MainRouter.Reverse("Blog.View", args).Url
}
func (_ tBlog) SearchBlog(
userId string,
key string,
) string {
args := make(map[string]string)
revel.Unbind(args, "userId", userId)
revel.Unbind(args, "key", key)
return revel.MainRouter.Reverse("Blog.SearchBlog", args).Url
}
func (_ tBlog) Set(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Blog.Set", args).Url
}
func (_ tBlog) SetUserBlogBase(
userBlog interface{},
) string {
args := make(map[string]string)
revel.Unbind(args, "userBlog", userBlog)
return revel.MainRouter.Reverse("Blog.SetUserBlogBase", args).Url
}
func (_ tBlog) SetUserBlogComment(
userBlog interface{},
) string {
args := make(map[string]string)
revel.Unbind(args, "userBlog", userBlog)
return revel.MainRouter.Reverse("Blog.SetUserBlogComment", args).Url
}
func (_ tBlog) SetUserBlogStyle(
userBlog interface{},
) string {
args := make(map[string]string)
revel.Unbind(args, "userBlog", userBlog)
return revel.MainRouter.Reverse("Blog.SetUserBlogStyle", args).Url
}
func (_ tBlog) AboutMe(
userId string,
) string {
args := make(map[string]string)
revel.Unbind(args, "userId", userId)
return revel.MainRouter.Reverse("Blog.AboutMe", args).Url
}
type tIndex struct {}
var Index tIndex
func (_ tIndex) Index(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Index.Index", args).Url
}
func (_ tIndex) Suggestion(
addr string,
suggestion string,
) string {
args := make(map[string]string)
revel.Unbind(args, "addr", addr)
revel.Unbind(args, "suggestion", suggestion)
return revel.MainRouter.Reverse("Index.Suggestion", args).Url
}
type tMobile struct {}
var Mobile tMobile
func (_ tMobile) Index(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Mobile.Index", args).Url
}
func (_ tMobile) Logout(
) string {
args := make(map[string]string)
return revel.MainRouter.Reverse("Mobile.Logout", args).Url
}
type tOauth struct {}
var Oauth tOauth
func (_ tOauth) GithubCallback(
code string,
) string {
args := make(map[string]string)
revel.Unbind(args, "code", code)
return revel.MainRouter.Reverse("Oauth.GithubCallback", args).Url
}
+113
View File
@@ -0,0 +1,113 @@
package service
import (
"labix.org/v2/mgo/bson"
// "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"fmt"
)
// 登录与权限
type AuthService struct {
}
// pwd已md5了
func (this *AuthService) Login(emailOrUsername, pwd string) info.User {
return userService.LoginGetUserInfo(emailOrUsername, Md5(pwd))
}
// 注册
/*
注册 leanote@leanote.com userId = "5368c1aa99c37b029d000001"
添加 在博客上添加一篇欢迎note, note1 5368c1b919807a6f95000000
将nk1(只读), nk2(可写) 分享给该用户
将note1 复制到用户的生活nk上
*/
// 1. 添加用户
// 2. 将leanote共享给我
// [ok]
func (this *AuthService) Register(email, pwd string) (bool, string) {
// 用户是否已存在
if userService.IsExistsUser(email) {
return false, email + " 已被注册"
}
user := info.User{UserId: bson.NewObjectId(), Email: email, Username: email, Pwd: Md5(pwd)}
return this.register(user)
}
func (this *AuthService) register(user info.User) (bool, string) {
if userService.AddUser(user) {
// 添加笔记本, 生活, 学习, 工作
notebook := info.Notebook{
Seq: -1,
UserId: user.UserId}
title2Id := map[string]bson.ObjectId{"life": bson.NewObjectId(), "study": bson.NewObjectId(), "work": bson.NewObjectId()}
for title, objectId := range title2Id {
notebook.Title = title
notebook.NotebookId = objectId
notebook.UserId = user.UserId
notebookService.AddNotebook(notebook);
}
email := user.Email
// 添加leanote -> 该用户的共享
leanoteUserId := "5368c1aa99c37b029d000001";
nk1 := "5368c1aa99c37b029d000002" // leanote
welcomeNoteId := "5368c1b919807a6f95000000" // 欢迎来到leanote
shareService.AddShareNotebook(nk1, 0, leanoteUserId, email);
shareService.AddShareNote(welcomeNoteId, 0, leanoteUserId, email);
// 将welcome copy给我
note := noteService.CopySharedNote(welcomeNoteId, title2Id["life"].Hex(), leanoteUserId, user.UserId.Hex());
// 公开为博客
noteUpdate := bson.M{"IsBlog": true}
noteService.UpdateNote(user.UserId.Hex(), user.UserId.Hex(), note.NoteId.Hex(), noteUpdate)
//---------------
// 添加一条userBlog
blogService.UpdateUserBlog(info.UserBlog{UserId: user.UserId,
Title: user.Username + " 's Blog",
SubTitle: "love leanote!",
AboutMe: "Hello, I am (^_^)",
})
}
return true, ""
}
//--------------
// 第三方注册
// 第三方得到用户名, 可能需要多次判断
func (this *AuthService) getUsername(thirdType, thirdUsername string) (username string) {
username = thirdType + "-" + thirdUsername
i := 1
for ;; {
if !userService.IsExistsUserByUsername(username) {
return
}
username = fmt.Sprintf("%v%v", username, i)
}
}
func (this *AuthService) ThirdRegister(thirdType, thirdUserId, thirdUsername string) (exists bool, userInfo info.User) {
userInfo = userService.GetUserInfoByThirdUserId(thirdUserId)
if userInfo.UserId != "" {
exists = true
return
}
username := this.getUsername(thirdType, thirdUsername)
userInfo = info.User{UserId: bson.NewObjectId(),
Username: username,
ThirdUserId: thirdUserId,
ThirdUsername: thirdUsername,
}
_, _ = this.register(userInfo)
return
}
+155
View File
@@ -0,0 +1,155 @@
package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
// "time"
// "sort"
)
// blog
/*
note, notebook都可设为blog
关键是, 怎么得到blog列表? 还要分页
??? 不用新建, 直接使用notes表, 添加IsBlog字段. 新建表 blogs {NoteId, UserId, CreatedTime, IsTop(置顶)}, NoteId, UserId 为unique!!
// 设置一个note为blog
添加到blogs中
// 设置/取消notebook为blog
创建一个note时, 如果其notebookId已设为blog, 那么添加该note到blog中.
设置一个notebook为blog时, 将其下所有的note添加到blogs里 -> 更新其IsBlog为true
取消一个notebook不为blog时, 删除其下的所有note -> 更新其IsBlog为false
*/
type BlogService struct {
}
// 得到某博客具体信息
func (this *BlogService) GetBlog(noteId string) (blog info.BlogItem) {
note := noteService.GetBlogNote(noteId)
if note.NoteId == "" || !note.IsBlog {
return
}
// 内容
noteContent := noteService.GetNoteContent(note.NoteId.Hex(), note.UserId.Hex())
// 组装成blogItem
blog = info.BlogItem{note, noteContent.Content, false}
return
}
// 得到用户共享的notebooks
func (this *BlogService) ListBlogNotebooks(userId string) []info.Notebook {
notebooks := []info.Notebook{}
db.ListByQ(db.Notebooks, bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true}, &notebooks)
return notebooks
}
// 博客列表
// userId 表示谁的blog
func (this *BlogService) ListBlogs(userId, notebookId string, page, pageSize int, sortField string, isAsc bool) (int, []info.BlogItem) {
count, notes := noteService.ListNotes(userId, notebookId, false, page, pageSize, sortField, isAsc, true);
if(notes == nil || len(notes) == 0) {
return 0, nil
}
// 得到content, 并且每个都要substring
noteIds := make([]bson.ObjectId, len(notes))
for i, note := range notes {
noteIds[i] = note.NoteId
}
// 直接得到noteContents表的abstract
// 这里可能是乱序的
noteContents := noteService.ListNoteAbstractsByNoteIds(noteIds) // 返回[info.NoteContent]
noteContentsMap := make(map[bson.ObjectId]info.NoteContent, len(noteContents))
for _, noteContent := range noteContents {
noteContentsMap[noteContent.NoteId] = noteContent
}
// 组装成blogItem
// 按照notes的顺序
blogs := make([]info.BlogItem, len(noteIds))
for i, note := range notes {
hasMore := true
var content string
if noteContent, ok := noteContentsMap[note.NoteId]; ok {
content = noteContent.Abstract
}
blogs[i] = info.BlogItem{note, content, hasMore}
}
return count, blogs
}
func (this *BlogService) SearchBlog(key, userId string, page, pageSize int, sortField string, isAsc bool) (int, []info.BlogItem) {
count, notes := noteService.SearchNote(key, userId, page, pageSize, sortField, isAsc, true);
if(notes == nil || len(notes) == 0) {
return 0, nil
}
// 得到content, 并且每个都要substring
noteIds := make([]bson.ObjectId, len(notes))
for i, note := range notes {
noteIds[i] = note.NoteId
}
// 直接得到noteContents表的abstract
// 这里可能是乱序的
noteContents := noteService.ListNoteAbstractsByNoteIds(noteIds) // 返回[info.NoteContent]
noteContentsMap := make(map[bson.ObjectId]info.NoteContent, len(noteContents))
for _, noteContent := range noteContents {
noteContentsMap[noteContent.NoteId] = noteContent
}
// 组装成blogItem
// 按照notes的顺序
blogs := make([]info.BlogItem, len(noteIds))
for i, note := range notes {
hasMore := true
var content string
if noteContent, ok := noteContentsMap[note.NoteId]; ok {
content = noteContent.Abstract
}
blogs[i] = info.BlogItem{note, content, hasMore}
}
return count, blogs
}
//------------------------
// 博客设置
func (this *BlogService) GetUserBlog(userId string) info.UserBlog {
userBlog := info.UserBlog{}
db.Get(db.UserBlogs, userId, &userBlog)
if userBlog.Title == "" {
userInfo := userService.GetUserInfo(userId)
userBlog.Title = userInfo.Username + " 的博客"
}
return userBlog
}
// 修改之
func (this *BlogService) UpdateUserBlog(userBlog info.UserBlog) bool {
return db.Upsert(db.UserBlogs, bson.M{"_id": userBlog.UserId}, userBlog)
}
// 修改之UserBlogBase
func (this *BlogService) UpdateUserBlogBase(userId string, userBlog info.UserBlogBase) bool {
return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
}
func (this *BlogService) UpdateUserBlogComment(userId string, userBlog info.UserBlogComment) bool {
return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
}
func (this *BlogService) UpdateUserBlogStyle(userId string, userBlog info.UserBlogStyle) bool {
return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
}
+64
View File
@@ -0,0 +1,64 @@
package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
// "time"
)
// 历史记录
type NoteContentHistoryService struct {
}
// 每个历史记录最大值
var maxSize = 10
// 新建一个note, 不需要添加历史记录
// 添加历史
func (this *NoteContentHistoryService) AddHistory(noteId, userId string, eachHistory info.EachHistory) {
// 检查是否是空
if eachHistory.Content == "" {
return
}
// 先查是否存在历史记录, 没有则添加之
history := info.NoteContentHistory{}
db.GetByIdAndUserId(db.NoteContentHistories, noteId, userId, &history)
if history.NoteId == "" {
this.newHistory(noteId, userId, eachHistory)
} else {
// 判断是否超出 maxSize, 如果超出则pop最后一个, 再push之, 不用那么麻烦, 直接update吧, 虽然影响性能
// TODO
l := len(history.Histories)
if l >= maxSize {
history.Histories = history.Histories[l-maxSize:]
}
newHistory := []info.EachHistory{eachHistory}
newHistory = append(newHistory, history.Histories...) // 在开头加了, 最近的在最前
history.Histories = newHistory
// 更新之
db.UpdateByIdAndUserId(db.NoteContentHistories, noteId, userId, history)
}
return
}
// 新建历史
func (this *NoteContentHistoryService) newHistory(noteId, userId string, eachHistory info.EachHistory) {
history := info.NoteContentHistory{NoteId: bson.ObjectIdHex(noteId),
UserId: bson.ObjectIdHex(userId),
Histories: []info.EachHistory{eachHistory},
}
// 保存之
db.Insert(db.NoteContentHistories, history)
}
// 列表展示
func (this *NoteContentHistoryService) ListHistories(noteId, userId string) []info.EachHistory {
histories := info.NoteContentHistory{}
db.GetByIdAndUserId(db.NoteContentHistories, noteId, userId, &histories)
return histories.Histories
}
+449
View File
@@ -0,0 +1,449 @@
package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
. "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"time"
)
type NoteService struct {
}
// 通过id, userId得到note
func (this *NoteService) GetNote(noteId, userId string) (note info.Note) {
note = info.Note{}
db.GetByIdAndUserId(db.Notes, noteId, userId, &note)
return
}
// 得到blog, blogService用
// 不要传userId, 因为是公开的
func (this *NoteService) GetBlogNote(noteId string) (note info.Note) {
note = info.Note{}
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsBlog": true, "IsTrash": false}, &note)
return
}
// 通过id, userId得到noteContent
func (this *NoteService) GetNoteContent(noteContentId, userId string) (noteContent info.NoteContent) {
noteContent = info.NoteContent{}
db.GetByIdAndUserId(db.NoteContents, noteContentId, userId, &noteContent)
return
}
// 列出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}
if isBlog {
query["IsBlog"] = true
}
if notebookId != "" {
query["NotebookId"] = bson.ObjectIdHex(notebookId)
}
q := db.Notes.Find(query);
// 总记录数
count, _ = q.Count()
q.Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).
All(&notes)
return
}
// 通过noteIds来查询
// ShareService调用
func (this *NoteService) ListNotesByNoteIdsWithPageSort(noteIds []bson.ObjectId, userId string,
pageNumber, pageSize int, sortField string, isAsc bool, isBlog bool) (notes []info.Note) {
skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, sortField, isAsc)
notes = []info.Note{}
// 不是trash
db.Notes.
Find(bson.M{"_id": bson.M{"$in": noteIds}, "IsTrash": false}).
Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).
All(&notes)
return
}
// shareService调用
func (this *NoteService) ListNotesByNoteIds(noteIds []bson.ObjectId) (notes []info.Note) {
notes = []info.Note{}
db.Notes.
Find(bson.M{"_id": bson.M{"$in": noteIds}}).
All(&notes)
return
}
// blog需要
func (this *NoteService) ListNoteContentsByNoteIds(noteIds []bson.ObjectId) (notes []info.NoteContent) {
notes = []info.NoteContent{}
db.NoteContents.
Find(bson.M{"_id": bson.M{"$in": noteIds}}).
All(&notes)
return
}
// 只得到abstract, 不需要content
func (this *NoteService) ListNoteAbstractsByNoteIds(noteIds []bson.ObjectId) (notes []info.NoteContent) {
notes = []info.NoteContent{}
db.ListByQWithFields(db.NoteContents, bson.M{"_id": bson.M{"$in": noteIds}}, []string{"_id", "Abstract"}, &notes)
return
}
// 添加笔记
// 首先要判断Notebook是否是Blog, 是的话设为blog
// [ok]
func (this *NoteService) AddNote(note info.Note) 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
// 设为blog
note.IsBlog = notebookService.IsBlog(note.NotebookId.Hex())
db.Insert(db.Notes, note)
// tag1
tagService.AddTags(note.UserId.Hex(), note.Tags)
return note
}
// 添加共享d笔记
func (this *NoteService) AddSharedNote(note info.Note, myUserId bson.ObjectId) info.Note {
// 判断我是否有权限添加
if shareService.HasUpdateNotebookPerm(note.UserId.Hex(), myUserId.Hex(), note.NotebookId.Hex()) {
note.CreatedUserId = myUserId // 是我给共享我的人创建的
return this.AddNote(note)
}
return info.Note{}
}
// 添加笔记本内容
// [ok]
func (this *NoteService) AddNoteContent(noteContent info.NoteContent) info.NoteContent {
noteContent.CreatedTime = time.Now()
noteContent.UpdatedTime = noteContent.CreatedTime
noteContent.UpdatedUserId = noteContent.UserId
db.Insert(db.NoteContents, noteContent)
return noteContent;
}
// 添加笔记和内容
// 这里使用 info.NoteAndContent 接收?
func (this *NoteService) AddNoteAndContent(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)
}
if note.NoteId != "" {
this.AddNoteContent(noteContent)
}
return note
}
// 修改笔记
// [ok] TODO perm还没测
func (this *NoteService) UpdateNote(userId, updatedUserId, noteId string, needUpdate bson.M) bool {
// updatedUserId 要修改userId的note, 此时需要判断是否有修改权限
if userId != updatedUserId {
if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) {
Log("NO AUTH")
return false
} else {
Log("HAS AUTH -----------")
}
}
needUpdate["UpdatedUserId"] = bson.ObjectIdHex(updatedUserId);
needUpdate["UpdatedTime"] = time.Now();
// 添加tag2
if tags, ok := needUpdate["Tags"]; ok {
tagService.AddTagsI(userId, tags)
}
// 是否修改了isBlog
// 也要修改noteContents的IsBlog
if isBlog, ok := needUpdate["IsBlog"]; ok {
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog})
}
return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, needUpdate)
}
// 这里要判断权限, 如果userId != updatedUserId, 那么需要判断权限
// [ok] TODO perm还没测 [del]
func (this *NoteService) UpdateNoteTitle(userId, updatedUserId, noteId, title string) bool {
// updatedUserId 要修改userId的note, 此时需要判断是否有修改权限
if userId != updatedUserId {
if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) {
println("NO AUTH")
return false
}
}
return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId), "Title": title, "UpdatedTime": time.Now()})
}
// 修改笔记本内容
// [ok] TODO perm未测
func (this *NoteService) UpdateNoteContent(userId, updatedUserId, noteId, content, abstract string) bool {
// updatedUserId 要修改userId的note, 此时需要判断是否有修改权限
if userId != updatedUserId {
if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) {
Log("NO AUTH")
return false
}
}
if db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId,
bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId),
"Content": content,
"Abstract": abstract,
"UpdatedTime": time.Now()}) {
// 这里, 添加历史记录
noteContentHistoryService.AddHistory(noteId, userId, info.EachHistory{UpdatedUserId: bson.ObjectIdHex(updatedUserId),
Content: content,
UpdatedTime: time.Now(),
})
return true
}
return false
}
// 更新tags
// [ok] [del]
func (this *NoteService) UpdateTags(noteId string, userId string, tags []string) bool {
return db.UpdateByIdAndUserIdField(db.Notes, noteId, userId, "Tags", tags)
}
// 移动note
// trash, 正常的都可以用
// 1. 要检查下notebookId是否是自己的
// 2. 要判断之前是否是blog, 如果不是, 那么notebook是否是blog?
func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
if notebookService.IsMyNotebook(notebookId, userId) {
re := db.UpdateByIdAndUserId(db.Notes, noteId, userId,
bson.M{"$set": bson.M{"IsTrash": false,
"NotebookId": bson.ObjectIdHex(notebookId)}})
if re {
// 更新blog状态
this.updateToNotebookBlog(noteId, notebookId, userId)
}
return this.GetNote(noteId, userId);
}
return info.Note{}
}
// 如果自己的blog状态是true, 不用改变,
// 否则, 如果notebookId的blog是true, 则改为true之
// 返回blog状态
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}})
return true
}
return false
}
// 判断是否是blog
func (this *NoteService) IsBlog(noteId string) bool {
note := info.Note{}
db.GetByQWithFields(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, []string{"IsBlog"}, &note);
return note.IsBlog
}
// 复制note
// 正常的可以用
// 先查, 再新建
// 要检查下notebookId是否是自己的
func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
if notebookService.IsMyNotebook(notebookId, userId) {
note := this.GetNote(noteId, userId)
noteContent := this.GetNoteContent(noteId, userId)
// 重新生成noteId
note.NoteId = bson.NewObjectId();
note.NotebookId = bson.ObjectIdHex(notebookId)
noteContent.NoteId = note.NoteId
this.AddNoteAndContent(note, noteContent, note.UserId);
// 更新blog状态
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId)
note.IsBlog = isBlog
return note
}
return info.Note{}
}
// 复制别人的共享笔记给我
// TODO 判断是否共享了给我
func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId string) info.Note {
if notebookService.IsMyNotebook(notebookId, myUserId) {
note := this.GetNote(noteId, fromUserId)
if note.NoteId == "" {
return info.Note{}
}
noteContent := this.GetNoteContent(noteId, fromUserId)
// 重新生成noteId
note.NoteId = bson.NewObjectId();
note.NotebookId = bson.ObjectIdHex(notebookId)
note.UserId = bson.ObjectIdHex(myUserId)
note.IsTop = false
note.IsBlog = false // 别人的可能是blog
// content
noteContent.NoteId = note.NoteId
noteContent.UserId = note.UserId
// 添加之
note = this.AddNoteAndContent(note, noteContent, note.UserId);
// 更新blog状态
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, myUserId)
note.IsBlog = isBlog
return note
}
return info.Note{}
}
// 通过noteId得到notebookId
// shareService call
// [ok]
func (this *NoteService) GetNotebookId(noteId string) bson.ObjectId {
note := &info.Note{}
db.Get(db.Notes, noteId, note)
return note.NotebookId
}
//------------------
// 搜索Note
func (this *NoteService) SearchNote(key, userId string, pageNumber, pageSize int, sortField string, isAsc, 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": false,
"Title": bson.M{"$regex": bson.RegEx{".*?" + key + ".*", "i"}},
}
if isBlog {
query["IsBlog"] = true
}
q := db.Notes.Find(query);
// 总记录数
count, _ = q.Count()
q.Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).
All(&notes)
// 如果 < pageSize 那么搜索content, 且id不在这些id之间的
if len(notes) < pageSize {
notes = this.searchNoteFromContent(notes, userId, key, pageSize, sortFieldR, isBlog)
}
return
}
// 搜索noteContents, 补集pageSize个
func (this *NoteService) searchNoteFromContent(notes []info.Note, userId, key string, pageSize int, sortField string, isBlog bool) []info.Note {
var remain = pageSize - len(notes)
noteIds := make([]bson.ObjectId, len(notes))
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 {
query["IsBlog"] = true
}
db.NoteContents.
Find(query).
Sort(sortField).
Limit(remain).
Select(bson.M{"_id": true}).
All(&noteContents)
var lenContent = len(noteContents)
if(lenContent == 0) {
return notes
}
// 收集ids
noteIds2 := make([]bson.ObjectId, lenContent)
for i, content := range noteContents {
noteIds2[i] = content.NoteId
}
// Log(" content search ")
// Log(lenContent)
// 得到notes
notes2 := this.ListNotesByNoteIds(noteIds2)
// 合并之
notes = append(notes, notes2...)
return notes
}
//----------------
// tag搜索
func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumber, pageSize int, sortField string, isAsc 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": false,
"Tags": bson.M{"$all": tags}}
q := db.Notes.Find(query);
// 总记录数
count, _ = q.Count()
Log(count)
q.Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).
All(&notes)
return
}
+211
View File
@@ -0,0 +1,211 @@
package service
import (
// "fmt"
"labix.org/v2/mgo/bson"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea"
"sort"
"time"
)
// 笔记本
type NotebookService struct {
}
// 排序
func sortSubNotebooks(eachNotebooks info.SubNotebooks) info.SubNotebooks {
// 遍历子, 则子往上进行排序
for _, eachNotebook := range eachNotebooks {
if eachNotebook.Subs != nil && len(eachNotebook.Subs) > 0 {
eachNotebook.Subs = sortSubNotebooks(eachNotebook.Subs)
}
}
// 子排完了, 本层排
sort.Sort(&eachNotebooks)
return eachNotebooks
}
// 整理(成有关系)并排序
// GetNotebooks()调用
// ShareService调用
func ParseAndSortNotebooks(userNotebooks []info.Notebook, noParentDelete, needSort bool) info.SubNotebooks {
// 整理成info.Notebooks
// 第一遍, 建map
// notebookId => info.Notebooks
userNotebooksMap := make(map[bson.ObjectId]*info.Notebooks, len(userNotebooks))
for _, each := range userNotebooks {
newNotebooks := info.Notebooks{Subs: info.SubNotebooks{}}
newNotebooks.NotebookId = each.NotebookId
newNotebooks.Title = each.Title
newNotebooks.Seq = each.Seq
newNotebooks.UserId = each.UserId
newNotebooks.ParentNotebookId = each.ParentNotebookId
newNotebooks.NumberNotes = each.NumberNotes
newNotebooks.IsTrash = each.IsTrash
newNotebooks.IsBlog = each.IsBlog
userNotebooksMap[each.NotebookId] = &newNotebooks
}
// 第二遍, 追加到父下
// nilObjectId := bson.ObjectIdHex("")
for id, each := range userNotebooksMap {
// 如果有父, 那么追加到父下, 并剪掉当前, 那么最后就只有根的元素
if each.ParentNotebookId.Hex() != "" {
if userNotebooksMap[each.ParentNotebookId] != nil {
userNotebooksMap[each.ParentNotebookId].Subs = append(userNotebooksMap[each.ParentNotebookId].Subs, *each)
// 并剪掉
delete(userNotebooksMap, id)
} else if noParentDelete {
// 没有父, 且设置了要删除
delete(userNotebooksMap, id)
}
}
}
// 第三遍, 得到所有根
final := make(info.SubNotebooks, len(userNotebooksMap))
i := 0
for _, each := range userNotebooksMap {
final[i] = *each
i++
}
// 最后排序
if needSort {
return sortSubNotebooks(final)
}
return final
}
// 得到某notebook
func (this *NotebookService) GetNotebook(notebookId, userId string) info.Notebook {
notebook := info.Notebook{}
db.GetByIdAndUserId(db.Notebooks, notebookId, userId, &notebook)
return notebook
}
// 得到用户下所有的notebook
// 排序好之后返回
// [ok]
func (this *NotebookService) GetNotebooks(userId string) info.SubNotebooks {
userNotebooks := []info.Notebook{}
db.Notebooks.Find(bson.M{"UserId": bson.ObjectIdHex(userId)}).All(&userNotebooks)
if len(userNotebooks) == 0 {
return nil
}
return ParseAndSortNotebooks(userNotebooks, true, true)
}
// share调用, 不需要删除没有父的notebook
// 不需要排序, 因为会重新排序
// 通过notebookIds得到notebooks, 并转成层次有序
func (this *NotebookService) GetNotebooksByNotebookIds(notebookIds []bson.ObjectId) info.SubNotebooks {
userNotebooks := []info.Notebook{}
db.Notebooks.Find(bson.M{"_id": bson.M{"$in": notebookIds}}).All(&userNotebooks)
if len(userNotebooks) == 0 {
return nil
}
return ParseAndSortNotebooks(userNotebooks, false, false)
}
// 添加
// [ok]
func (this *NotebookService) AddNotebook(notebook info.Notebook) bool {
err := db.Notebooks.Insert(notebook)
if err != nil {
panic(err)
} else {
}
return true
}
// 判断是否是blog
func (this *NotebookService) IsBlog(notebookId string) bool {
notebook := info.Notebook{}
db.GetByQWithFields(db.Notebooks, bson.M{"_id": bson.ObjectIdHex(notebookId)}, []string{"IsBlog"}, &notebook);
return notebook.IsBlog
}
// 判断是否是我的notebook
func (this *NotebookService) IsMyNotebook(notebookId, userId string) bool {
return db.Has(db.Notebooks, bson.M{"_id": bson.ObjectIdHex(notebookId), "UserId": bson.ObjectIdHex(userId)})
}
// 更新笔记本信息
// 太广, 不用
/*
func (this *NotebookService) UpdateNotebook(notebook info.Notebook) bool {
return db.UpdateByIdAndUserId2(db.Notebooks, notebook.NotebookId, notebook.UserId, notebook)
}
*/
// 更新笔记本标题
// [ok]
func (this *NotebookService) UpdateNotebookTitle(notebookId, userId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Notebooks, notebookId, userId, "Title", title)
}
// 更新notebook
func (this *NotebookService) UpdateNotebook(userId, notebookId string, needUpdate bson.M) bool {
needUpdate["UpdatedTime"] = time.Now();
// 如果有IsBlog之类的, 需要特殊处理
if isBlog, ok := needUpdate["IsBlog"]; ok {
// 设为blog/取消
if is, ok2 := isBlog.(bool); ok2 {
q := bson.M{"UserId": bson.ObjectIdHex(userId),
"NotebookId": bson.ObjectIdHex(notebookId)}
db.UpdateByQMap(db.Notes, q, bson.M{"IsBlog": is})
// noteContents也更新, 这个就麻烦了, noteContents表没有NotebookId
// 先查该notebook下所有notes, 得到id
notes := []info.Note{}
db.ListByQWithFields(db.Notes, q, []string{"_id"}, &notes)
if len(notes) > 0 {
noteIds := make([]bson.ObjectId, len(notes))
for i, each := range notes {
noteIds[i] = each.NoteId
}
db.UpdateByQMap(db.NoteContents, bson.M{"_id": bson.M{"$in": noteIds}}, bson.M{"IsBlog": isBlog})
}
}
}
return db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, needUpdate)
}
// 先查看该notebookId下是否有notes, 没有则删除
func (this *NotebookService) DeleteNotebook(userId, notebookId string) (bool, string) {
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), ""
}
return false, "笔记本下有笔记"
}
// 排序
// 传入 notebookId => Seq
// 为什么要传入userId, 防止修改其它用户的信息 (恶意)
// [ok]
func (this *NotebookService) SortNotebooks(userId string, notebookId2Seqs map[string]int) bool {
if len(notebookId2Seqs) == 0 {
return false
}
for notebookId, seq := range notebookId2Seqs {
if !db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(notebookId), bson.ObjectIdHex(userId), "Seq", seq) {
return false
}
}
return true
}
+63
View File
@@ -0,0 +1,63 @@
package service
import (
"labix.org/v2/mgo/bson"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"fmt"
)
// 找回密码
// 修改密码
var overHours = 2.0 // 小时后过期
type PwdService struct {
}
// 1. 找回密码, 通过email找用户,
// 用户存在, 生成code
func (this *PwdService) FindPwd(email string) (ok bool, msg string) {
ok = false
userId := userService.GetUserId(email)
if userId == "" {
msg = "用户不存在"
return
}
token := tokenService.NewToken(userId, email, info.TokenPwd)
if token == "" {
return false, "db error"
}
// 发送邮件
url := "http://leanote.com/findPassword/" + token
body := fmt.Sprintf("请点击链接修改密码: <a href='%v'>%v</a>. %v小时后过期.", url, url, int(overHours));
if !SendEmail(email, "leanote-找回密码", "找回密码", body) {
return false, "邮箱发送失败"
}
ok = true
return
}
// 修改密码
// 先验证
func (this *PwdService) UpdatePwd(token, pwd string) (bool, string) {
var tokenInfo info.Token
var ok bool
var msg string
// 先验证
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenPwd); !ok {
return ok, msg
}
// 修改密码之
ok = db.UpdateByQField(db.Users, bson.M{"_id": tokenInfo.UserId}, "Pwd", Md5(pwd))
// 删除token
tokenService.DeleteToken(tokenInfo.UserId.Hex(), info.TokenPwd)
return ok, ""
}
+1
View File
@@ -0,0 +1 @@
All services. Called by controller
+510
View File
@@ -0,0 +1,510 @@
package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"time"
"sort"
)
// 共享Notebook, Note服务
type ShareService struct {
}
//-----------------
// 总体来说, 这个方法比较麻烦, 速度未知. 以后按以下方案来缓存用户基础数据
// 以后建个用户的基本数据表, 放所有notebook, sharedNotebook的缓存!!
// 每更新一次则启动一个goroutine异步更新
// 共享只能共享本notebook下的, 如果其子也要共享, 必须设置其子!!!
// 那么, 父, 子在shareNotebooks表中都会有记录
// 得到用户的所有*被*共享的Notebook
// 1 得到别人共享给我的所有notebooks
// 2 按parent进行层次化
// 3 每个层按seq进行排序
// 4 按用户分组
// [ok]
func (this *ShareService) GetShareNotebooks(userId string) (info.ShareNotebooksByUser, []info.User) {
//-------------
// 查询HasShareNote表得到所有其它用户信息
hasShareNotes := []info.HasShareNote{}
db.ListByQ(db.HasShareNotes, bson.M{"ToUserId": bson.ObjectIdHex(userId)}, &hasShareNotes);
userIds := make([]bson.ObjectId, len(hasShareNotes))
for i, each := range hasShareNotes {
userIds[i] = each.UserId
}
userInfos := userService.GetUserInfosOrderBySeq(userIds);
//--------------------
// 得到他们共享给我的notebooks
shareNotebooks := []info.ShareNotebook{}
db.ShareNotebooks.Find(bson.M{"ToUserId": bson.ObjectIdHex(userId)}).All(&shareNotebooks)
if len(shareNotebooks) == 0 {
return nil, userInfos
}
shareNotebooksLen := len(shareNotebooks)
// 找到了所有的notbookId, 那么找notebook表得到其详细信息
notebookIds := make([]bson.ObjectId, shareNotebooksLen)
shareNotebooksMap := make(map[bson.ObjectId]info.ShareNotebook, shareNotebooksLen)
for i, each := range shareNotebooks {
// 默认的是没有notebookId的
notebookIds[i] = each.NotebookId
shareNotebooksMap[each.NotebookId] = each
}
// 1, 2
subNotebooks := notebookService.GetNotebooksByNotebookIds(notebookIds)
// 填充其它信息变成SubShareNotebooks
subShareNotebooks := this.parseToSubShareNotebooks(&subNotebooks, &shareNotebooksMap)
// 3 按用户进行分组成ShareNotebooksByUser
shareNotebooksByUsersMap := map[bson.ObjectId] []info.ShareNotebooks{}
// 先建立userId => []
for _, eachSub := range subShareNotebooks {
userId := eachSub.Notebook.UserId
if _, ok := shareNotebooksByUsersMap[userId]; ok {
shareNotebooksByUsersMap[userId] = append(shareNotebooksByUsersMap[userId], eachSub)
} else {
shareNotebooksByUsersMap[userId] = []info.ShareNotebooks{eachSub}
}
}
shareNotebooksByUser := info.ShareNotebooksByUser{};
for userId, eachShareNotebooks := range shareNotebooksByUsersMap {
// 4, 按用户排序
shareNotebooksByUser[userId.Hex()] = sortSubShareNotebooks(eachShareNotebooks)
}
return shareNotebooksByUser, userInfos
}
// 排序
func sortSubShareNotebooks(eachNotebooks info.SubShareNotebooks) info.SubShareNotebooks {
// 遍历子, 则子往上进行排序
for _, eachNotebook := range eachNotebooks {
if eachNotebook.Subs != nil && len(eachNotebook.Subs) > 0 {
eachNotebook.Subs = sortSubShareNotebooks(eachNotebook.Subs)
}
}
// 子排完了, 本层排
sort.Sort(&eachNotebooks)
return eachNotebooks
}
// 将普通的notebooks添加perm及shareNotebook信息
func (this *ShareService) parseToSubShareNotebooks(subNotebooks *info.SubNotebooks, shareNotebooksMap *map[bson.ObjectId]info.ShareNotebook) info.SubShareNotebooks {
subShareNotebooks := info.SubShareNotebooks{}
for _, each := range *subNotebooks {
shareNotebooks := info.ShareNotebooks{}
shareNotebooks.Notebook = each.Notebook // 基本信息有了
shareNotebooks.ShareNotebook = (*shareNotebooksMap)[each.NotebookId] // perm有了
// 公用的, 单独赋值
shareNotebooks.Seq = shareNotebooks.ShareNotebook.Seq
shareNotebooks.NotebookId = shareNotebooks.ShareNotebook.NotebookId
// 还有其子, 递归解析之
if each.Subs != nil && len(each.Subs) > 0 {
shareNotebooks.Subs = this.parseToSubShareNotebooks(&each.Subs, shareNotebooksMap)
}
subShareNotebooks = append(subShareNotebooks, shareNotebooks)
}
return subShareNotebooks
}
//-------------
// 得到共享笔记本下的notes
func (this *ShareService) ListShareNotesByNotebookId(notebookId, myUserId, sharedUserId string,
page, pageSize int, sortField string, isAsc bool) ([]info.ShareNoteWithPerm) {
// 1 首先判断是否真的sharedUserId 共享了 notebookId 给 myUserId
shareNotebook := info.ShareNotebook{}
db.GetByQ(db.ShareNotebooks, bson.M{"NotebookId": bson.ObjectIdHex(notebookId),
"UserId": bson.ObjectIdHex(sharedUserId), "ToUserId": bson.ObjectIdHex(myUserId)}, &shareNotebook)
if shareNotebook.NotebookId == "" {
return nil
}
perm := shareNotebook.Perm
// 2 得到该notebook下分页显示所有的notes
_, notes := noteService.ListNotes(sharedUserId, notebookId, false, page, pageSize, sortField, isAsc, false);
// 3 添加权限信息
// 3.1 如果该notebook自己有其它权限信息, 比如1, 那么覆盖notebook的权限信息
noteIds := make([]bson.ObjectId, len(notes))
for i, note := range notes {
noteIds[i] = note.NoteId
}
notePerms := this.getNotesPerm(noteIds, myUserId, sharedUserId);
// 3.2 组合
notesWithPerm := make([]info.ShareNoteWithPerm, len(notes))
for i, each := range notes {
thisPerm := perm;
if selfPerm, ok := notePerms[each.NoteId]; ok {
thisPerm = selfPerm
}
notesWithPerm[i] = info.ShareNoteWithPerm{each, thisPerm}
}
return notesWithPerm
}
// 得到note的perm信息
func (this *ShareService) getNotesPerm(noteIds []bson.ObjectId, myUserId, sharedUserId string) map[bson.ObjectId]int {
shareNotes := []info.ShareNote{}
db.ListByQ(db.ShareNotes, bson.M{"NoteId": bson.M{"$in": noteIds}, "UserId": bson.ObjectIdHex(sharedUserId), "ToUserId": bson.ObjectIdHex(myUserId)}, &shareNotes)
notesPerm := make(map[bson.ObjectId]int, len(shareNotes))
for _, each := range shareNotes {
notesPerm[each.NoteId] = each.Perm
}
return notesPerm
}
// 得到默认的单个的notes 共享集
// 如果真要支持排序, 这里得到所有共享的notes, 到noteService方再sort和limit
// 可以这样! 到时将零散的共享noteId放在用户基本数据中
// 这里不好排序
func (this *ShareService) ListShareNotes(myUserId, sharedUserId string,
pageNumber, pageSize int, sortField string, isAsc bool) ([]info.ShareNoteWithPerm) {
skipNum, _ := parsePageAndSort(pageNumber, pageSize, sortField, isAsc)
shareNotes := []info.ShareNote{}
db.ShareNotes.
Find(bson.M{"UserId": bson.ObjectIdHex(sharedUserId), "ToUserId": bson.ObjectIdHex(myUserId)}).
// Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).
All(&shareNotes)
if len(shareNotes) == 0 {
return nil
}
noteIds := make([]bson.ObjectId, len(shareNotes))
for i, each := range shareNotes {
noteIds[i] = each.NoteId
}
notes := noteService.ListNotesByNoteIds(noteIds)
notesMap := make(map[bson.ObjectId]info.Note, len(notes))
for _, each := range notes {
notesMap[each.NoteId] = each
}
// 将shareNotes与notes结合起来
notesWithPerm := make([]info.ShareNoteWithPerm, len(shareNotes))
for i, each := range shareNotes {
notesWithPerm[i] = info.ShareNoteWithPerm{notesMap[each.NoteId], each.Perm}
}
return notesWithPerm
}
func (this *ShareService) notes2NotesWithPerm(notes []info.Note) {
}
// 添加一个notebook共享
// [ok]
func (this *ShareService) AddShareNotebook1(shareNotebook info.ShareNotebook) bool {
// 添加一条记录说明两者存在关系
this.AddHasShareNote(shareNotebook.UserId.Hex(), shareNotebook.ToUserId.Hex());
shareNotebook.CreatedTime = time.Now()
return db.Insert(db.ShareNotebooks, shareNotebook)
}
// 添加共享笔记本
func (this *ShareService) AddShareNotebook(notebookId string, perm int, userId, email string) (bool, string, string) {
// 通过email得到被共享的userId
toUserId := userService.GetUserId(email)
if toUserId == "" {
return false, "无该用户", ""
}
// 添加一条记录说明两者存在关系
this.AddHasShareNote(userId, toUserId);
// 先删除之
db.Delete(db.ShareNotebooks, bson.M{"NotebookId": bson.ObjectIdHex(notebookId),
"UserId": bson.ObjectIdHex(userId),
"ToUserId": bson.ObjectIdHex(toUserId),
});
shareNotebook := info.ShareNotebook{NotebookId: bson.ObjectIdHex(notebookId),
UserId: bson.ObjectIdHex(userId),
ToUserId: bson.ObjectIdHex(toUserId),
Perm: perm,
CreatedTime: time.Now(),
}
return db.Insert(db.ShareNotebooks, shareNotebook), "", toUserId
}
// 添加一个note共享
// [ok]
/*
func (this *ShareService) AddShareNote(shareNote info.ShareNote) bool {
shareNote.CreatedTime = time.Now()
return db.Insert(db.ShareNotes, shareNote)
}
*/
func (this *ShareService) AddShareNote(noteId string, perm int, userId, email string) (bool, string, string) {
// 通过email得到被共享的userId
toUserId := userService.GetUserId(email)
if toUserId == "" {
return false, "无该用户", ""
}
// 添加一条记录说明两者存在关系
this.AddHasShareNote(userId, toUserId);
// 先删除之
db.Delete(db.ShareNotes, bson.M{"NoteId": bson.ObjectIdHex(noteId),
"UserId": bson.ObjectIdHex(userId),
"ToUserId": bson.ObjectIdHex(toUserId),
});
shareNote := info.ShareNote{NoteId: bson.ObjectIdHex(noteId),
UserId: bson.ObjectIdHex(userId),
ToUserId: bson.ObjectIdHex(toUserId),
Perm: perm,
CreatedTime: time.Now(),
}
return db.Insert(db.ShareNotes, shareNote), "", toUserId
}
// updatedUserId是否有修改userId noteId的权限?
func (this *ShareService) HasUpdatePerm(userId, updatedUserId, noteId string) bool {
// 1. noteId是否被共享了?
// 得到该note share的信息
/*
UserId bson.ObjectId `bson:"UserId"`
ToUserId bson.ObjectId `bson:"ToUserId"`
NoteId bson.ObjectId `bson:"NoteId"`
Perm int `bson:"Perm"` // 权限, 0只读, 1可修改
*/
if !db.Has(db.ShareNotes,
bson.M{"UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(updatedUserId), "NoteId": bson.ObjectIdHex(noteId), "Perm": 1}) {
// 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, "Perm": 1}) {
return false
} else {
return true
}
} else {
return true
}
}
// updatedUserId是否有修改userId notebookId的权限?
func (this *ShareService) HasUpdateNotebookPerm(userId, updatedUserId, notebookId string) bool {
// 判断notebook是否被共享
if !db.Has(db.ShareNotebooks,
bson.M{"UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(updatedUserId), "NotebookId": bson.ObjectIdHex(notebookId), "Perm": 1}) {
return false
} else {
return true
}
}
// 共享note, notebook时使用
func (this *ShareService) AddHasShareNote(userId, toUserId string) bool {
db.Insert(db.HasShareNotes, info.HasShareNote{UserId: bson.ObjectIdHex(userId), ToUserId: bson.ObjectIdHex(toUserId)})
return true;
}
// userId是否被共享了noteId
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,
"UserId": bson.ObjectIdHex(sharedUserId),
"ToUserId": bson.ObjectIdHex(myUserId),
})
}
return false
}
// 得到共享的笔记内容
// 首先要判断这个note是否我被共享了
func (this *ShareService) GetShareNoteContent(noteId, myUserId, sharedUserId string) (noteContent info.NoteContent) {
noteContent = info.NoteContent{}
// 是否单独共享了该notebook
// 或者, 其notebook共享了我
if this.hasSharedNote(noteId, myUserId) || this.hasSharedNotebook(noteId, myUserId, sharedUserId) {
db.Get(db.NoteContents, noteId, &noteContent)
} else {
}
return
}
// 查看note的分享信息
// 分享给了哪些用户和权限
// ShareNotes表 userId = me, noteId = ...
// 还要查看该note的notebookId分享的信息
func (this *ShareService) ListNoteShareUserInfo(noteId, userId string) []info.ShareUserInfo {
// 得到shareNote信息, 得到所有的ToUserId
shareNotes := []info.ShareNote{}
db.ListByQLimit(db.ShareNotes, bson.M{"NoteId": bson.ObjectIdHex(noteId), "UserId": bson.ObjectIdHex(userId)}, &shareNotes, 100)
if len(shareNotes) == 0 {
return nil
}
shareNotesMap := make(map[bson.ObjectId]info.ShareNote, len(shareNotes))
for _, each := range shareNotes {
shareNotesMap[each.ToUserId] = each
}
toUserIds := make([]bson.ObjectId, len(shareNotes))
for i, eachShareNote := range shareNotes {
toUserIds[i] = eachShareNote.ToUserId
}
note := noteService.GetNote(noteId, userId)
if note.NoteId == "" {
return nil
}
// 查看其notebook的shareNotebooks信息
shareNotebooks := []info.ShareNotebook{}
db.ListByQ(db.ShareNotebooks,
bson.M{"NotebookId": note.NotebookId, "UserId": bson.ObjectIdHex(userId), "ToUserId": bson.M{"$in": toUserIds}},
&shareNotebooks)
shareNotebooksMap := make(map[bson.ObjectId]info.ShareNotebook, len(shareNotebooks))
for _, each := range shareNotebooks {
shareNotebooksMap[each.ToUserId] = each
}
// 得到用户信息
userInfos := userService.ListUserInfosByUserIds(toUserIds)
if len(userInfos) == 0 {
return nil
}
shareUserInfos := make([]info.ShareUserInfo, len(userInfos))
for i, userInfo := range userInfos {
_, hasNotebook := shareNotebooksMap[userInfo.UserId]
shareUserInfos[i] = info.ShareUserInfo{ToUserId: userInfo.UserId,
Email: userInfo.Email,
Perm: shareNotesMap[userInfo.UserId].Perm,
NotebookHasShared: hasNotebook,
}
}
return shareUserInfos
}
// 得到notebook的share信息
// TODO 这里必须要分页, 最多取100个用户; 限制
func (this *ShareService) ListNotebookShareUserInfo(notebookId, userId string) []info.ShareUserInfo {
// notebook的shareNotebooks信息
shareNotebooks := []info.ShareNotebook{}
db.ListByQLimit(db.ShareNotebooks,
bson.M{"NotebookId": bson.ObjectIdHex(notebookId), "UserId": bson.ObjectIdHex(userId)},
&shareNotebooks, 100)
if len(shareNotebooks) == 0 {
return nil
}
// 得到用户信息
toUserIds := make([]bson.ObjectId, len(shareNotebooks))
for i, each := range shareNotebooks {
toUserIds[i] = each.ToUserId
}
userInfos := userService.ListUserInfosByUserIds(toUserIds)
if len(userInfos) == 0 {
return nil
}
shareNotebooksMap := make(map[bson.ObjectId]info.ShareNotebook, len(shareNotebooks))
for _, each := range shareNotebooks {
shareNotebooksMap[each.ToUserId] = each
}
shareUserInfos := make([]info.ShareUserInfo, len(userInfos))
for i, userInfo := range userInfos {
shareUserInfos[i] = info.ShareUserInfo{ToUserId: userInfo.UserId,
Email: userInfo.Email,
Perm: shareNotebooksMap[userInfo.UserId].Perm,
}
}
return shareUserInfos
}
//----------------
// 改变note share权限
func (this *ShareService) UpdateShareNotePerm(noteId string, perm int, userId, toUserId string) bool {
return db.UpdateByQField(db.ShareNotes,
bson.M{"NoteId": bson.ObjectIdHex(noteId), "UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(toUserId)},
"Perm",
perm,
)
}
func (this *ShareService) UpdateShareNotebookPerm(notebookId string, perm int, userId, toUserId string) bool {
return db.UpdateByQField(db.ShareNotebooks,
bson.M{"NotebookId": bson.ObjectIdHex(notebookId), "UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(toUserId)},
"Perm",
perm,
)
}
//---------------
// 删除share note
func (this *ShareService) DeleteShareNote(noteId string, userId, toUserId string) bool {
return db.DeleteAll(db.ShareNotes,
bson.M{"NoteId": bson.ObjectIdHex(noteId), "UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(toUserId)})
}
// 删除笔记时要删除该noteId的所有...
func (this *ShareService) DeleteShareNoteAll(noteId string, userId string) bool {
return db.DeleteAll(db.ShareNotes,
bson.M{"NoteId": bson.ObjectIdHex(noteId), "UserId": bson.ObjectIdHex(userId)})
}
// 删除share notebook
func (this *ShareService) DeleteShareNotebook(notebookId string, userId, toUserId string) bool {
return db.DeleteAll(db.ShareNotebooks,
bson.M{"NotebookId": bson.ObjectIdHex(notebookId), "UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(toUserId)})
}
// 删除userId分享给toUserId的所有
func (this *ShareService) DeleteUserShareNoteAndNotebook(userId, toUserId string) bool {
query := bson.M{"UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(toUserId)}
db.DeleteAll(db.ShareNotebooks, query);
db.DeleteAll(db.ShareNotes, query);
db.DeleteAll(db.HasShareNotes, query);
return true
}
+21
View File
@@ -0,0 +1,21 @@
package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
// "time"
// "sort"
)
type SuggestionService struct {
}
// 得到某博客具体信息
func (this *SuggestionService) AddSuggestion(suggestion info.Suggestion) bool {
if suggestion.Id == "" {
suggestion.Id = bson.NewObjectId()
}
return db.Insert(db.Suggestions, suggestion)
}
+39
View File
@@ -0,0 +1,39 @@
package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
. "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
// "time"
)
/*
每添加,更新note时, 都将tag添加到tags表中
*/
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 {
return this.AddTags(userId, ts)
}
return false
}
func (this *TagService) AddTags(userId string, tags []string) bool {
for _, tag := range tags {
if !db.Upsert(db.Tags,
bson.M{"_id": bson.ObjectIdHex(userId)},
bson.M{"$addToSet": bson.M{"Tags": tag}}) {
return false
}
}
return true
}
+72
View File
@@ -0,0 +1,72 @@
package service
import (
"labix.org/v2/mgo/bson"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"time"
)
// token
// 找回密码
// 修改密码
type TokenService struct {
}
// 生成token
func (this *TokenService) NewToken(userId string, email string, tokenType int) string {
token := info.Token{UserId: bson.ObjectIdHex(userId), Token: NewGuidWith(email), CreatedTime: time.Now(), Email: email, Type: tokenType}
if db.Upsert(db.Tokens, bson.M{"_id": token.UserId}, token) {
return token.Token
}
return ""
}
// 删除token
func (this *TokenService) DeleteToken(userId string, tokenType int) bool {
return db.Delete(db.Tokens, bson.M{"_id": bson.ObjectIdHex(userId), "Type": tokenType})
}
func (this *TokenService) GetOverHours(tokenType int) float64 {
if tokenType == info.TokenPwd {
return info.PwdOverHours
} else if tokenType == info.TokenUpdateEmail {
return info.UpdateEmailOverHours
} else {
return info.ActiveEmailOverHours
}
}
// 验证token, 是否存在, 过时?
func (this *TokenService) VerifyToken(token string, tokenType int) (ok bool, msg string, tokenInfo info.Token) {
overHours = this.GetOverHours(tokenType)
ok = false
if token == "" {
msg = "不存在"
return
}
db.GetByQ(db.Tokens, bson.M{"Token": token}, &tokenInfo)
if tokenInfo.UserId == "" {
msg = "不存在"
return
}
// 验证是否过时
now := time.Now()
duration := now.Sub(tokenInfo.CreatedTime)
if duration.Hours() > overHours {
msg = "过期"
return
}
ok = true
return
}
+65
View File
@@ -0,0 +1,65 @@
package service
import (
"labix.org/v2/mgo/bson"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
)
// 回收站
// 可以移到noteSerice中
// 不能删除notebook, 如果其下有notes!
// 这样回收站里只有note
// 删除笔记后(或删除笔记本)后入回收站
// 把note, notebook设个标记即可!
// 已经在trash里的notebook, note不能是共享!, 所以要删除共享
type TrashService struct {
}
//---------------------
// 删除note
// 应该放在回收站里
// 有trashService
func (this *TrashService) DeleteNote(noteId, userId string) bool {
// 首先删除其共享
if shareService.DeleteShareNoteAll(noteId, userId) {
// 更新note isTrash = true
return db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true}})
}
return false
}
// 删除别人共享给我的笔记
// 先判断我是否有权限, 笔记是否是我创建的
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 false
}
// recover
func (this *TrashService) recoverNote(noteId, notebookId, userId string) bool {
re := db.UpdateByIdAndUserId(db.Notes, noteId, userId,
bson.M{"$set": bson.M{"IsTrash": false,
"NotebookId": bson.ObjectIdHex(notebookId)}})
return re;
}
// 删除trash
func (this *TrashService) DeleteTrash(noteId, userId string) bool {
return db.DeleteByIdAndUserId(db.Notes, noteId, userId)
}
// 列出note, 排序规则, 还有分页
// CreatedTime, UpdatedTime, title 来排序
func (this *TrashService) ListNotes(userId string,
pageNumber, pageSize int, sortField string, isAsc bool) (notes []info.Note) {
_, notes = noteService.ListNotes(userId, "", true, pageNumber, pageSize, sortField, isAsc, false)
return
}
+294
View File
@@ -0,0 +1,294 @@
package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
. "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"time"
"strings"
"fmt"
)
type UserService struct {
}
// 添加用户
func (this *UserService) AddUser(user info.User) bool {
if user.UserId == "" {
user.UserId = bson.NewObjectId()
}
user.CreatedTime = time.Now()
if user.Email != "" {
user.Email = strings.ToLower(user.Email)
// 发送验证邮箱
go func() {
this.RegisterSendActiveEmail(user.UserId.Hex(), user.Email)
}();
}
return db.Insert(db.Users, user)
}
// 通过email得到userId
func (this *UserService) GetUserId(email string) string {
email = strings.ToLower(email)
user := info.User{}
db.GetByQ(db.Users, bson.M{"Email": email}, &user)
return user.UserId.Hex()
}
// 是否存在该用户 email
func (this *UserService) IsExistsUser(email string) bool {
if this.GetUserId(email) == "" {
return false
}
return true
}
// 是否存在该用户 username
func (this *UserService) IsExistsUserByUsername(username string) bool {
return db.Count(db.Users, bson.M{"Username": username}) >= 1
}
// 得到用户信息, userId, username, email
func (this *UserService) GetUserInfoByAny(idEmailUsername string) info.User {
if IsObjectId(idEmailUsername) {
return this.GetUserInfo(idEmailUsername)
}
if strings.Contains(idEmailUsername, "@") {
return this.GetUserInfoByEmail(idEmailUsername)
}
return this.GetUserInfoByUsername(idEmailUsername)
}
// 得到用户信息 userId
func (this *UserService) GetUserInfo(userId string) info.User {
user := info.User{}
db.Get(db.Users, userId, &user)
return user
}
// 得到用户信息 email
func (this *UserService) GetUserInfoByEmail(email string) info.User {
user := info.User{}
db.GetByQ(db.Users, bson.M{"Email": email}, &user)
return user
}
// 得到用户信息 username
func (this *UserService) GetUserInfoByUsername(username string) info.User {
user := info.User{}
db.GetByQ(db.Users, bson.M{"Username": username}, &user)
return user
}
func (this *UserService) GetUserInfoByThirdUserId(thirdUserId string) info.User {
user := info.User{}
db.GetByQ(db.Users, bson.M{"ThirdUserId": thirdUserId}, &user)
return user
}
func (this *UserService) ListUserInfosByUserIds(userIds []bson.ObjectId) []info.User {
users := []info.User{}
db.ListByQ(db.Users, bson.M{"_id": bson.M{"$in": userIds}}, &users)
return users
}
// 通过ids得到users, 按id的顺序组织users
func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.User {
users := []info.User{}
db.ListByQ(db.Users, bson.M{"_id": bson.M{"$in": userIds}}, &users);
usersMap := map[bson.ObjectId]info.User{}
for _, user := range users {
usersMap[user.UserId] = user
}
users2 := []info.User{};
for _, userId := range userIds {
if user, ok := usersMap[userId]; ok {
users2 = append(users2, user)
}
}
return users2
}
// 使用email(username), pwd得到用户信息
func (this *UserService) LoginGetUserInfo(emailOrUsername, md5Pwd string) info.User {
emailOrUsername = strings.ToLower(emailOrUsername)
user := info.User{}
if strings.Contains(emailOrUsername, "@") {
db.GetByQ(db.Users, bson.M{"Email": emailOrUsername, "Pwd": md5Pwd}, &user)
} else {
db.GetByQ(db.Users, bson.M{"Username": emailOrUsername, "Pwd": md5Pwd}, &user)
}
return user
}
// 更新username
func (this *UserService) UpdateUsername(userId, username string) (bool, string) {
if userId == "" || username == "" {
return false, "用户已存在"
}
usernameRaw := username // 原先的, 可能是同一个, 但有大小写
username = strings.ToLower(username)
// 先判断是否存在
userIdO := bson.ObjectIdHex(userId)
if db.Has(db.Users, bson.M{"Username": username, "_id": bson.M{"$ne": userIdO}}) {
return false, "用户已存在"
}
ok := db.UpdateByQMap(db.Users, bson.M{"_id": userIdO}, bson.M{"Username": username, "UsernameRaw": usernameRaw})
return ok, ""
}
//----------------------
// 已经登录了的用户修改密码
func (this *UserService) UpdatePwd(userId, oldPwd, pwd string) (bool, string) {
userInfo := this.GetUserInfo(userId)
if userInfo.Pwd != Md5(oldPwd) {
return false, "旧密码错误"
}
ok := db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", Md5(pwd))
return ok, ""
}
// 修改主题
func (this *UserService) UpdateTheme(userId, theme string) (bool) {
ok := db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Theme", theme)
return ok
}
//---------------
// 修改email
// 发送激活邮件
// AddUser调用
// 可以使用一个goroutine
func (this *UserService) RegisterSendActiveEmail(userId string, email string) bool {
token := tokenService.NewToken(userId, email, info.TokenActiveEmail)
if token == "" {
return false
}
// 发送邮件
url := "http://leanote.com/user/activeEmail?token=" + token
body := fmt.Sprintf("请点击链接验证邮箱: <a href='%v'>%v</a>. %v小时后过期.", url, url, tokenService.GetOverHours(info.TokenActiveEmail));
if !SendEmail(email, "leanote-验证邮箱", "验证邮箱", body) {
return false
}
// 发送给我 life@leanote.com
SendEmail("life@leanote.com", "新增用户", "新增用户", "用户名" + email);
return true
}
// 修改邮箱
func (this *UserService) UpdateEmailSendActiveEmail(userId, email string) (ok bool, msg string) {
// 先验证该email是否被注册了
if userService.IsExistsUser(email) {
ok = false
msg = "该邮箱已注册"
return
}
token := tokenService.NewToken(userId, email, info.TokenUpdateEmail)
if token == "" {
return
}
// 发送邮件
url := "http://115.28.133.226/user/updateEmail?token=" + token
body := "邮箱验证后您的登录邮箱为: <b>" + email + "</b><br />";
body += fmt.Sprintf("请点击链接验证邮箱: <a href='%v'>%v</a>. %v小时后过期.", url, url, tokenService.GetOverHours(info.TokenUpdateEmail));
if !SendEmail(email, "leanote-验证邮箱", "验证邮箱", body) {
msg = "发送失败"
return
}
ok = true
return
}
// 注册后验证邮箱
func (this *UserService) ActiveEmail(token string) (ok bool, msg, email string) {
tokenInfo := info.Token{}
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenActiveEmail); ok {
// 修改之后的邮箱
email = tokenInfo.Email
userInfo := this.GetUserInfoByEmail(email)
if userInfo.UserId == "" {
ok = false
msg = "不存在该用户"
return
}
// 修改之, 并将verified = true
ok = db.UpdateByQMap(db.Users, bson.M{"_id": userInfo.UserId}, bson.M{"Verified": true})
return
}
ok = false
msg = "该链接已过期"
return
}
// 修改邮箱
// 在此之前, 验证token是否过期
// 验证email是否有人注册了
func (this *UserService) UpdateEmail(token string) (ok bool, msg, email string) {
tokenInfo := info.Token{}
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenUpdateEmail); ok {
// 修改之后的邮箱
email = tokenInfo.Email
// 先验证该email是否被注册了
if userService.IsExistsUser(email) {
ok = false
msg = "该邮箱已注册"
return
}
// 修改之, 并将verified = true
ok = db.UpdateByQMap(db.Users, bson.M{"_id": tokenInfo.UserId}, bson.M{"Email": email, "Verified": true})
return
}
ok = false
msg = "该链接已过期"
return
}
//---------
// 第三方添加账号
func (this *UserService) ThirdAddUser(userId, email, pwd string) (ok bool, msg string) {
// 判断email是否存在
if this.IsExistsUser(email) {
msg = "该用户已存在"
return
}
ok = db.UpdateByQMap(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, bson.M{"Email": email, "Pwd": Md5(pwd)})
return
}
//------------
// 偏好设置
// 宽度
func (this *UserService)UpdateColumnWidth(userId string, notebookWidth, noteListWidth int) bool {
return db.UpdateByQMap(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, bson.M{"NotebookWidth": notebookWidth, "NoteListWidth": noteListWidth})
}
// 左侧是否隐藏
func (this *UserService)UpdateLeftIsMin(userId string, leftIsMin bool) bool {
return db.UpdateByQMap(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, bson.M{"LeftIsMin": leftIsMin})
}
+20
View File
@@ -0,0 +1,20 @@
package service
import (
)
// service 通用方法
// 分页, 排序处理
func parsePageAndSort(pageNumber, pageSize int, sortField string, isAsc bool) (skipNum int, sortFieldR string) {
skipNum = (pageNumber - 1) * pageSize
if sortField == "" {
sortField = "UpdatedTime"
}
if !isAsc {
sortFieldR = "-" + sortField
} else {
sortFieldR = sortField
}
return
}
+31
View File
@@ -0,0 +1,31 @@
package service
import (
)
// init service, for share service bettween services
// 初始化, 实例service
// 为了共享service
var notebookService *NotebookService
var noteService *NoteService
var noteContentHistoryService *NoteContentHistoryService
var trashService *TrashService
var shareService *ShareService
var userService *UserService
var tagService *TagService
var blogService *BlogService
var tokenService *TokenService
func init() {
notebookService = &NotebookService{}
noteService = &NoteService{}
noteContentHistoryService = &NoteContentHistoryService{}
trashService = &TrashService{}
shareService = &ShareService{}
userService = &UserService{}
tagService = &TagService{}
blogService = &BlogService{}
tokenService = &TokenService{}
}
+229
View File
@@ -0,0 +1,229 @@
package main
import (
"github.com/robfig/revel"
"github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
// "github.com/leanote/leanote/app/lea/memcache"
// "github.com/leanote/leanote/app/lea/netutil"
"github.com/leanote/leanote/app/lea/html2image"
"time"
"fmt"
"labix.org/v2/mgo/bson"
// "labix.org/v2/mgo"
// "encoding/json"
// "strings"
)
var userId = "5295d4c95b1dd58edb4a7f4f"
var userIdO = bson.ObjectIdHex(userId)
var userId3 = "52b43ae4cfeeae33ef073b2b"
var userId3O = bson.ObjectIdHex(userId3)
var toUserId = "52b4376bcfeeae33ef073b21"
var toUserIdO = bson.ObjectIdHex(toUserId)
var notebookId = "52b2d051ea3ba3d3fb35910c"
var notebookIdO = bson.ObjectIdHex(notebookId)
var notebookId2O = bson.ObjectIdHex("52b2d051ea3ba3d3fb35910b")
var noteId = "52b2dd34ea3ba3d3fb35910d"
var noteIdO = bson.ObjectIdHex(noteId)
func testNoteService() {
println("testNoteService")
noteService := &service.NoteService{}
note := noteService.GetBlogNote("535f9e6b19807a4c8d000000")
LogJ(note)
/*
_, notes := noteService.SearchNote("go", "52d3e8ac99c37b7f0d000001", 1, 30, "", false, true);
LogJ(notes)
*/
return;
/*
noteService.AddNote(info.Note{UserId: userIdO,
NotebookId: notebookIdO,
Title: "life you", Tags: []string{"red", "yellow"}})
*/
noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
NoteId: bson.ObjectIdHex("52b4531dcfeeae33ef073b33"),
Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.UpdateNoteContent(userId, userId, "52b2dd34ea3ba3d3fb35910d", "life2---------")
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testNotebookService() {
service := &service.NotebookService{}
Log(service.IsBlog("52ccb959bcbf21610d000001"))
return;
notebooks := service.GetNotebooks(userId3)
LogJ(notebooks)
// b, _ := json.MarshalIndent(notebooks, "", " ")
// fmt.Println(string(b))
// service.UpdateNotebookTitle("52b2cf9eea3ba3d3fb359108", userId, "JS")
// service.SortNotebooks(userId, map[string]int{"52b2d051ea3ba3d3fb35910c": 4, "52b2d051ea3ba3d3fb35910b": 3})
/*
service.AddNotebook(info.Notebook{UserId: userId3O,
ParentNotebookId: bson.ObjectIdHex("52b43c1fcfeeae33ef073b2e"),
Title: "Mac-life",
Seq: 0})
*/
println("xxx")
}
func testShareService() {
service := &service.ShareService{}
// service.AddShareNote("52bd127dbcbf216d0b000000", 1, "5295d4c95b1dd58edb4a7f4f", "c@a.com")
LogJ(service.ListNoteShareUserInfo("52cd11a1bcbf215680000000", "5295d4c95b1dd58edb4a7f4f"))
return;
/*
service.AddShareNotebook(info.ShareNotebook{UserId: userId3O, ToUserId: userIdO,
NotebookId: bson.ObjectIdHex("52b43c38cfeeae33ef073b30")})
*/
notebooks,_ := service.GetShareNotebooks(userId)
LogJ(notebooks)
// noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
// NoteId: bson.ObjectIdHex("52b2dd34ea3ba3d3fb35910d"), Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testAuthService() {
userService := &service.UserService{}
LogJ(userService.GetUserInfo("52d26b4e99c37b609a000001"))
// userService.AddUser(info.User{UserId: bson.ObjectIdHex("52d26b4e99c37b609a000001"), Email: "leanote@leanote.com", Pwd:"abc"})
return;
authService := &service.AuthService{}
authService.Register("f@a.com", "abc")
// fmt.Println(authService.LogonGetUserInfo("a@a.com", "abc"))
// noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
// NoteId: bson.ObjectIdHex("52b2dd34ea3ba3d3fb35910d"), Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testTagService() {
service := &service.TagService{}
// service.AddTags("5295d4c95b1dd58edb4a7f4f", []string{"life", "blue", "yellow"})
// service.AddTags("5295d4c95b1dd58edb4a7f4f", []string{"what", "can", "make"})
LogJ(service.GetTags("5295d4c95b1dd58edb4a7f4f"))
}
func testHtml2Image() {
start := time.Now()
Log("start...")
// TestFillString()
html2image.ToImage("uid", "username", "noteId", "开发一款属于自己的编程语言,开发一款属于自己的编程语言听起来是不是很酷?", `
<div class="each-post">
<p>
一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace,很喜欢那里的氛围,希望国内其他地方也能有更多这类空间供创客发挥。
假如你有一个比较成型的想法,想在硬件领域做点事情,核心团队也基本组好,硬件软件交互基本都有专人了。</p>
<p><a>这时候你的首要目标</a>,就是找个地方,按照你的计划,尽早做出一个可用的原型。Techspace可能是一个合适的地方。
一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace,很喜欢那里的氛围,希望国内其他地方也能有更多这类空间供创客发挥。
深圳 Techspace 位于工业区,园区内有奥迪、BMW 等企业的厂房,在大门口我停下来问保安,M10 栋在哪里?保安答,去 Techspace?最里面靠右手那栋。惊叹于保安的机智,我也在想,莫非有许多朋友都慕名来到这巨大园区寻访 Techspace?
穿过一片工业区里的高楼和各种建筑材料
</p>
<!--
<pre class="">cd jpeg-9a/<br>./configure --enable-shared --enable-static
make<br>make --- install</pre>
<p>
life you can, !!@kk
</p>
-->
</div>
`, "/Users/life/Desktop/a.png")
fmt.Printf("time cost %v\n", time.Now().Sub(start))
}
func main() {
revel.BasePath = "/Users/life/Documents/Go/package/src/leanote"
// a, b := SplitFilename("http://ab/c/a.gif#??")
// println(a)
// println(b)
// path, ok := netutil.WriteUrl("http://a.36krcnd.com/photo/2014/9bd1a07c0973d79ca05ad13c3c2e16b8.png!slider", "/tmp")
// println(path)
// testHtml2Image();
// println(IsObjectId("52d26b4e99c37b609a000001"))
// b := `请点击链接验证邮箱: <a href="">http://leanote.com/user/activeEmail?token=d8ca086cce5550a6227f9dc84dbac09d</a>. 48小时后过期.`
// SendEmail("lifephp@gmail.com", "leanote-验证邮箱", "验证邮", b)
//_, err := mgo.Dial("mongodb://leanote:nKFAkxKnWkEQy8Vv2LlM@115.28.133.226:27017/leanote")
// testNotebookService();
testNoteService();
// testShareService()
// testAuthService()
// testTagService();
/*
filename := "你好59.26.png"
ext := SubstringByte(filename, strings.LastIndex(filename, "."))
ext = strings.ToLower(ext)
print(ext)
52d26ab199c37b5f80000001
Log(bson.NewObjectId())
Log(bson.NewObjectId())
Log(bson.NewObjectId())
*/
// Log(TransferExt("/你好a/b/a.gif", ".jpg"))
// TransToGif("/Users/life/Desktop/a2.png", 0, false)
// Log(IsUsername("xx**x"))
/*
memcache.Set("xx", map[string]string{"A":"you"}, 0)
a := memcache.Get("xx")
Log(a)
*/
}
+751
View File
@@ -0,0 +1,751 @@
// GENERATED CODE - DO NOT EDIT
package main
import (
"flag"
"reflect"
"github.com/revel/revel"
_ "github.com/leanote/leanote/app"
controllers "github.com/leanote/leanote/app/controllers"
_ "github.com/leanote/leanote/app/db"
info "github.com/leanote/leanote/app/info"
_ "github.com/leanote/leanote/app/lea/binder"
_ "github.com/leanote/leanote/app/lea/memcache"
_ "github.com/leanote/leanote/app/service"
controllers1 "github.com/revel/revel/modules/static/app/controllers"
_ "github.com/revel/revel/modules/testrunner/app"
controllers0 "github.com/revel/revel/modules/testrunner/app/controllers"
)
var (
runMode *string = flag.String("runMode", "", "Run mode.")
port *int = flag.Int("port", 0, "By default, read from app.conf")
importPath *string = flag.String("importPath", "", "Go Import Path for the app.")
srcPath *string = flag.String("srcPath", "", "Path to the source root.")
// So compiler won't complain if the generated code doesn't reference reflect package...
_ = reflect.Invalid
)
func main() {
flag.Parse()
revel.Init(*runMode, *importPath, *srcPath)
revel.INFO.Println("Running revel server")
revel.RegisterController((*controllers.BaseController)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "E404",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers0.TestRunner)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "Index",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
46: []string{
"testSuites",
},
},
},
&revel.MethodType{
Name: "Run",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "suite", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "test", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
69: []string{
"error",
},
},
},
&revel.MethodType{
Name: "List",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers1.Static)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "Serve",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "prefix", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "filepath", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "ServeModule",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "moduleName", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "prefix", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "filepath", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.File)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "UploadImage",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "renderHtml", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UploadBlogLogo",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UploadImageJson",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "renderHtml", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.Notebook)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "Index",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebook", Type: reflect.TypeOf((*info.Notebook)(nil)) },
&revel.MethodArg{Name: "i", Type: reflect.TypeOf((*int)(nil)) },
&revel.MethodArg{Name: "name", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DeleteNotebook",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "AddNotebook",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "title", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateNotebookTitle",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "title", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.NoteContentHistory)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "ListHistories",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.Note)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "Index",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "ListNotes",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "ListTrashNotes",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "GetNoteContent",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateNoteOrContent",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteOrContent", Type: reflect.TypeOf((*controllers.NoteOrContent)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DeleteNote",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "userId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "isShared", Type: reflect.TypeOf((*bool)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DeleteTrash",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "MoveNote",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "CopyNote",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "CopySharedNote",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "fromUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "SearchNote",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "key", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "SearchNoteByTags",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "tags", Type: reflect.TypeOf((*[]string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "Html2Image",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.Share)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "AddShareNote",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "emails", Type: reflect.TypeOf((*[]string)(nil)) },
&revel.MethodArg{Name: "perm", Type: reflect.TypeOf((*int)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "AddShareNotebook",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "emails", Type: reflect.TypeOf((*[]string)(nil)) },
&revel.MethodArg{Name: "perm", Type: reflect.TypeOf((*int)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "ListShareNotes",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "userId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "GetShareNoteContent",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "sharedUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "ListNoteShareUserInfo",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "ListNotebookShareUserInfo",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateShareNotePerm",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "perm", Type: reflect.TypeOf((*int)(nil)) },
&revel.MethodArg{Name: "toUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateShareNotebookPerm",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "perm", Type: reflect.TypeOf((*int)(nil)) },
&revel.MethodArg{Name: "toUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DeleteShareNote",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "toUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DeleteShareNotebook",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "toUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DeleteShareNoteBySharedUser",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "fromUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DeleteShareNotebookBySharedUser",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "fromUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DeleteUserShareNoteAndNotebook",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "fromUserId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.User)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "UpdateUsername",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "username", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdatePwd",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "oldPwd", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "pwd", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateTheme",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "theme", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "SendRegisterEmail",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "content", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "toEmail", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "ReSendActiveEmail",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateEmailSendActiveEmail",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "email", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateEmail",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "token", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "ActiveEmail",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "token", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "AddAccount",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "email", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "pwd", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateColumnWidth",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookWidth", Type: reflect.TypeOf((*int)(nil)) },
&revel.MethodArg{Name: "noteListWidth", Type: reflect.TypeOf((*int)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "UpdateLeftIsMin",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "leftIsMin", Type: reflect.TypeOf((*bool)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.Auth)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "Login",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "email", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DoLogin",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "email", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "pwd", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "Logout",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "Demo",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "Register",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DoRegister",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "email", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "pwd", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "FindPassword",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "DoFindPassword",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "email", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "FindPassword2",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "token", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "FindPasswordUpdate",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "token", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "pwd", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.Blog)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "SetNote2Blog",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "isBlog", Type: reflect.TypeOf((*bool)(nil)) },
&revel.MethodArg{Name: "isTop", Type: reflect.TypeOf((*bool)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "SetNotebook2Blog",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "isBlog", Type: reflect.TypeOf((*bool)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "Index",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "userId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "notebookId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "View",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "noteId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "SearchBlog",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "userId", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "key", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "Set",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "SetUserBlogBase",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "userBlog", Type: reflect.TypeOf((*info.UserBlogBase)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "SetUserBlogComment",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "userBlog", Type: reflect.TypeOf((*info.UserBlogComment)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "SetUserBlogStyle",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "userBlog", Type: reflect.TypeOf((*info.UserBlogStyle)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "AboutMe",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "userId", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.Index)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "Index",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "Suggestion",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "addr", Type: reflect.TypeOf((*string)(nil)) },
&revel.MethodArg{Name: "suggestion", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.Mobile)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "Index",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
&revel.MethodType{
Name: "Logout",
Args: []*revel.MethodArg{
},
RenderArgNames: map[int][]string{
},
},
})
revel.RegisterController((*controllers.Oauth)(nil),
[]*revel.MethodType{
&revel.MethodType{
Name: "GithubCallback",
Args: []*revel.MethodArg{
&revel.MethodArg{Name: "code", Type: reflect.TypeOf((*string)(nil)) },
},
RenderArgNames: map[int][]string{
},
},
})
revel.DefaultValidationKeys = map[string]map[int]string{
}
revel.TestSuites = []interface{}{
}
revel.Run(*port)
}
+23
View File
@@ -0,0 +1,23 @@
{{set . "title" "Home"}}
{{template "header.html" .}}
<header class="hero-unit" style="background-color:#A9F16C">
<div class="container">
<div class="row">
<div class="hero-text">
<h1>It works!</h1>
<p></p>
</div>
</div>
</div>
</header>
<div class="container">
<div class="row">
<div class="span6">
{{template "flash.html" .}}
</div>
</div>
</div>
{{template "footer.html" .}}
+21
View File
@@ -0,0 +1,21 @@
{{template "Blog/header.html" .}}
<div id="postsContainer">
<div id="posts">
<div class="each-post">
<div class="title">
关于我
</div>
<div class="created-time">
</div>
<div class="desc">
{{.userBlog.AboutMe | raw}}
</div>
<!-- comment -->
{{template "blog/comment.html" .}}
</div>
</div>
</div>
{{template "Blog/footer.html" .}}
+18
View File
@@ -0,0 +1,18 @@
{{if .userBlog.CanComment}}
<div id="disqus_thread"></div>
<!-- comment -->
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = '{{.userBlog.DisqusId}}'; // required: replace example with your forum shortname
var disqus_identifier = '{{.userBlog.UserId.Hex}}/{{.blog.NoteId.Hex}}/{{.blog.Title}}'; // 博客链接
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
{{end}}
+62
View File
@@ -0,0 +1,62 @@
<div id="footerContainer">
{{$userId := .userBlog.UserId.Hex}}
<div class="container" id="footer">
<div class="col-md-4">
<h3>导航</h3>
<ul>
<li><a href="/blog/{{$userId}}">首页</a></li>
{{range .notebooks}}
<li>
<a href="/blog/{{$userId}}/{{.NotebookId.Hex}}">{{.Title}}</a>
</li>
{{end}}
<li><a href="/blog/aboutMe/{{$userId}}">关于我</a></li>
</ul>
</div>
<div class="col-md-4">
<h3>最近发表</h3>
<ul>
{{range .recentBlogs}}
<li title="{{.Title}}"><a href="/blog/view/{{.NoteId.Hex}}/">{{.Title}}</a></li>
{{end}}
</ul>
</div>
<div class="col-md-4">
<h3>快速链接</h3>
<ul>
<li><a href="/note">我的笔记</a></li>
<li><a href="/login">登录</a></li>
<li><a href="http://leanote.com" target="_blank">leanote</a></li>
<li><a href="https://github.com/leanote/leanote" target="_blank">leanote github</a></li>
</ul>
</div>
</div>
</div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap-min.js"></script>
<script>
$(function() {
/*
$("#searchInput").click(function() {
$("#search").width("130px");
$("#searchInput").width("100px");
});
$("#searchInput").blur(function() {
$("#search").width(0);
$("#searchInput").width(0);
});
*/
});
// 搜索
function search(e) {
var key = $("#searchInput").val();
if(!key) {
location.href = "/blog/" + UserInfo.Username;
} else {
var tpl = '<form action="/blog/searchBlog/' + UserInfo.Username +'" method="get">';
tpl += '<input name="key" value="' + key + '" />';
tpl += "</form";
$(tpl).submit();
}
}␍</script>
+88
View File
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note!">
<meta name="author" content="leanote">
<title>{{.title}}</title>
<!-- Bootstrap core CSS -->
<link href="/css/bootstrap.css" rel="stylesheet">
<link href="/css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet">
<link id="styleLink" href="/css/blog/{{if .userBlog.Style}}{{.userBlog.Style}}{{else}}blog_default{{end}}.css" rel="stylesheet">
<script>
function log(o) {
}
</script>
<style>
</style>
</head>
<body>
<div id="headerAndNav" >
<div id="headerContainer" class="container">
<!-- 头部可放博客名, 导航 -->
<div id="header">
{{$userId := .userBlog.UserId.Hex}} <!-- 可不要了 -->
{{$username := .userInfo.Username}}
<h1>
<a href="/blog/{{$username}}" id="logo">
{{if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" title="{{.userBlog.Title}}"/>
{{else}}
{{.userBlog.Title | raw}}
{{end}}
</a>
</h1>
<div id="blogDesc">
{{.userBlog.SubTitle | raw}}
</div>
</div>
</div>
<!-- Static navbar -->
<div class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
{{$navNotebookId := .notebookId}}
<li class="{{if .index}}active{{end}}"><a href="/blog/{{$username}}">首页</a></li>
{{range .notebooks}}
{{$notebookId := .NotebookId.Hex}}
<li class="{{if eq $navNotebookId $notebookId}}active{{else}}{{end}}">
<a href="/blog/{{$username}}/{{$notebookId}}"
>{{.Title}}</a>
</li>
{{end}}
<li class="{{if .aboutMe}}active{{end}}"><a href="/blog/aboutMe/{{$username}}">关于我</a></li>
{{if .isMe}}
<li class="{{if .set}}active{{end}}"><a href="/blog/set" >博客设置</a></li>
<li><a href="/note" >{{msg . "myNote"}}</a></li>
{{end}}
</ul>
<form class="navbar-form navbar-right" id="search" onsubmit="search(event);return false;">
<div class="input-group">
<span class="input-group-addon" id="searchIcon" onclick="search(event);"><i class="fa fa-search"></i></span>
<input type="text" placeholder="search" id="searchInput" class="form-control" value="{{.key}}">
</div>
</form>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<script>
var UserInfo = {UserId: "{{$userId}}", Email: "{{.userInfo.Email}}", Username: "{{.userInfo.Username}}"};
</script>
+71
View File
@@ -0,0 +1,71 @@
<link href="/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
<script src="/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script>
$("pre").addClass("prettyprint linenums");
prettyPrint();
</script>
<!--
<script type="text/javascript" src="/js/syntaxhighlighter/scripts/shCore.js"></script>
<link href="/js/syntaxhighlighter/styles/shCore.css" rel="stylesheet" type="text/css" />
<link href="/js/syntaxhighlighter/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
    $(function () {
var pathPrefix = "/js/syntaxhighlighter/scripts/";
        var map = {
"brush: jscript": "shBrushJScript.js",
         "brush: c#": "shBrushCSharp.js",
        "brush: css": "shBrushCss.js",
        "brush: cpp": "shBrushCpp.js",
        "brush: java": "shBrushJava.js",
        "brush: xml": "shBrushXml.js",
        "brush: php": "shBrushPhp.js",
        "brush: perl": "shBrushPerl.js",
        "brush: python": "shBrushPython.js",
        "brush: ruby": "shBrushRuby.js",
        "brush: sql": "shBrushSql.js"}
        var needLoadCount = 0;
        var alreadyLoadCount = 0;
$('pre[class]').each(function() {
var brush = $(this).attr("class");
var js = map[brush]
if (js) {
                needLoadCount++;
map[brush] = null; // 不要重复加载
loadJS(pathPrefix + js, function () {
                    alreadyLoadCount++;
                    if (alreadyLoadCount == needLoadCount) {
//setTimeout(function() {
                        SyntaxHighlighter.highlight();
//}, 500);
                    }
                });
}
});
    });
function loadJS(url, callback) {
$.getScript(url, function() {
callback();
});
}
 
/*
function loadJS(url, callback, charset) {
    var script = document.createElement('script');
    script.onload = script.onreadystatechange = function () {
        if (script && script.readyState && /^(?!(?:loaded|complete)$)/.test(script.readyState)) return;
        script.onload = script.onreadystatechange = null;
        script.src = '';
        script.parentNode.removeChild(script);
        script = null;
        if (callback) callback();
    };
    script.charset = charset || document.charset || document.characterSet;
    script.src = url;
    try { document.getElementsByTagName("head")[0].appendChild(script); } catch (e) { }
}
*/
</script>
-->
+44
View File
@@ -0,0 +1,44 @@
{{template "Blog/header.html" .}}
<div id="postsContainer">
<div class="container">
{{if .notebookId}}
<h2>分类 {{.notebook.Title}}</h2>
{{end}}
</div>
<div id="posts">
{{range .blogs}}
<div class="each-post">
<div class="title">
<a href="/blog/view/{{.NoteId.Hex}}" title="全文">
{{.Title}}
</a>
</div>
<div class="created-time">
<i class="fa fa-bookmark-o" style="color: #666"></i>
{{if .Tags}}
{{blogTags .Tags}}
{{else}}
无
{{end}}
|
<i class="fa fa-calendar" style="color: #666"></i> 更新 {{.UpdatedTime | datetime}} |
<i class="fa fa-calendar" style="color: #666"></i> 创建 {{.CreatedTime | datetime}}
</div>
<div class="desc">
{{.Content | raw}}
</div>
<a class="more" href="/blog/view/{{.NoteId.Hex}}" title="更多">More...</a>
</div>
{{end}}
<!-- 分页 -->
<ul class="pager">
{{page .userInfo.Username .notebookId .page .pageSize .count}}
</ul>
</div>
</div>
{{template "Blog/footer.html" .}}
{{template "Blog/highlight.html"}}
</body>
</html>
+45
View File
@@ -0,0 +1,45 @@
{{template "Blog/header.html" .}}
<div id="postsContainer">
<div class="container">
<h2>搜索 {{.key}} </h2>
</div>
<div id="posts">
{{range .blogs}}
<div class="each-post">
<div class="title">
<a href="/blog/view/{{.NoteId.Hex}}" title="全文">
{{.Title}}
</a>
</div>
<div class="created-time">
<i class="fa fa-bookmark-o" style="color: #666"></i>
{{if .Tags}}
{{blogTags .Tags}}
{{else}}
无
{{end}}
|
<i class="fa fa-calendar" style="color: #666"></i> 更新 {{.UpdatedTime | datetime}} |
<i class="fa fa-calendar" style="color: #666"></i> 创建 {{.CreatedTime | datetime}}
</div>
<div class="desc">
{{.Content | raw}}
</div>
<a class="more" href="/blog/view/{{.NoteId.Hex}}" title="更多">More...</a>
</div>
{{end}}
{{if not .blogs }}
<div class="each-post">
无
</div>
{{end}}
</div>
</div>
{{template "Blog/footer.html" .}}
{{template "Blog/highlight.html"}}
</body>
</html>
+301
View File
@@ -0,0 +1,301 @@
{{template "Blog/header.html" .}}
<!-- -->
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css"
type="text/css">
<style>
.tab-pane {
padding-top: 10px;
}
</style>
<div id="postsContainer">
<div id="posts">
<ul id="myTab" class="nav nav-tabs">
<li class="active"><a href="#baseInfo" data-toggle="tab">基本设置</a></li>
<li class=""><a href="#commentInfo" data-toggle="tab">评论设置</a></li>
<li class=""><a href="#styleInfo" data-toggle="tab">主题设置</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane" id="styleInfo">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="Style" class="col-sm-2 control-label">主题</label>
<div class="col-sm-10">
<label><input type="radio" name="Style"
value="blog_default"
{{if not .userBlog.Style}}
checked="checked"
{{else}}
{{if eq .userBlog.Style "blog_default"}}
checked="checked"
{{end}}
{{end}}>
默认 </label>
<label><input type="radio" name="Style"
value="blog_daqi"
{{if eq .userBlog.Style "blog_daqi"}}checked="checked"{{end}}>
大气</label>
<label><input type="radio" name="Style"
value="blog_left_fixed"
{{if eq .userBlog.Style "blog_left_fixed"}}checked="checked"{{end}}>
左侧导航固定</label>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-success">保存</button>
<span class="msg"></span>
</div>
</div>
</div>
</form>
</div>
<div class="tab-pane" id="commentInfo">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="subTitle" class="col-sm-2 control-label">开启评论</label>
<div class="col-sm-10">
<input type="checkbox" id="CanComment" name="CanComment"
{{if .userBlog.CanComment}}checked="checked"{{end}} >
<br />
leanote采用 <a href="http://disqus.com" target="_blank">Disqus</a>
作为评论系统
<div id="disqusSet">
<label for="DisqusId">Disqus Id</label> <input type="text"
class="form-control" style="display: inline; width: 50%"
id="DisqusId" name="DisqusId"
value="{{if .userBlog.DisqusId}}{{.userBlog.DisqusId}}{{else}}leanote{{end}}">
<br /> (请填写您申请的Disqus唯一url前缀, 建议您申请Disqus帐号, 这样可以自己管理评论.
或使用leanote的默认Disqus Id. <a target="_blank"
href="/blog/view/52db8463e01c530ef8000001">需要帮助?</a>)
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-success">保存</button>
<span class="msg"></span>
</div>
</div>
</div>
</form>
</div>
<div class="tab-pane active" id="baseInfo">
<div class="form-horizontal" role="form" id="userBlogForm">
<div class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<div class="alert alert-success" id="msg"
style="display: none; margin-bottom: 3px;"></div>
</div>
</div>
<div class="form-group">
<label for="title" class="col-sm-2 control-label">博客名称</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="Title" name="Title"
placeholder="比如: leanote的博客"
value="{{if .userBlog.Title}}{{.userBlog.Title}}{{else}}{{.userInfo.Email}} 的博客{{end}}">
</div>
</div>
<div class="form-group">
<label for="logo" class="col-sm-2 control-label">博客Logo</label>
<div class="col-sm-10">
<input type="hidden" name="Logo" id="Logo"
value="{{.userBlog.Logo}}" />
<form id="formLogo" action="/file/uploadBlogLogo" method="post"
enctype="multipart/form-data" target="logoTarget"
onsubmit="inProgress()">
<input type="file" class="form-control" id="logo2" name="file"
onChange='$("#formLogo").submit();' /> 上传logo将显示logo(替代博客标题)
<div id="logoImg"
{{if .userBlog.Logo }}
{{else}}
style="display: none"{{end}}
>
<img src="{{.userBlog.Logo}}" style="height: 40px" /> <a
href="#" id="deleteLogo">删除</a>
</div>
</form>
<iframe id="logoTarget" name="logoTarget" src="#"
style="display: none"></iframe>
</div>
</div>
<div class="form-group">
<label for="subTitle" class="col-sm-2 control-label">博客描述</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="SubTitle"
name="SubTitle" value="{{.userBlog.SubTitle}}"
placeholder="比如: leanote, 不只是笔记">
</div>
</div>
<div class="form-group">
<label for="aboutMe" class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<div id="popularToolbar"></div>
</div>
</div>
<div class="form-group">
<label for="aboutMe" class="col-sm-2 control-label">关于我</label>
<div class="col-sm-10">
<textarea id="AboutMe" name="AboutMe">{{.userBlog.AboutMe}}</textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-success">保存</button>
<span class="msg"></span>
</div>
</div>
</div>
</div>
</div>
</div>
{{template "Blog/footer.html" .}}
<script src="/js/common-min.js"></script>
<script type="text/javascript" src="/tinymce/tinymce.min.js"></script>
<script>
$(function() {
tinymce.init({
selector : "#AboutMe",
content_css : [ "/css/bootstrap.css", "/css/editor.css" ],
skin : "custom",
language : "zh_CN",
height : 300,
width : "100%",
plugins : [
"advlist autolink link image lists charmap hr ",
"searchreplace visualblocks visualchars code tabfocus",
"table contextmenu directionality textcolor paste fullpage textcolor" ],
toolbar1 : "formatselect fontselect fontsizeselect | forecolor backcolor | bold italic underline strikethrough | image | bullist numlist | alignleft aligncenter alignright alignjustify",
toolbar2 : "outdent indent blockquote | link unlink | table | hr removeformat | subscript superscript | visualchars visualblocks | searchreplace | code",
menubar : false,
toolbar_items_size : 'small',
statusbar : false,
font_formats : "Arial=arial,helvetica,sans-serif;"
+ "Arial Black=arial black,avant garde;"
+ "Times New Roman=times new roman,times;"
+ "Courier New=courier new,courier;"
+ "Tahoma=tahoma,arial,helvetica,sans-serif;"
+ "Verdana=verdana,geneva;" + "宋体=SimSun;"
+ "新宋体=NSimSun;" + "黑体=SimHei;"
+ "微软雅黑=Microsoft YaHei",
block_formats : "Header 1=h1;Header 2=h2;Header 3=h3; Header 4=h4;Pre=pre;Paragraph=p"
});
$("#deleteLogo").click(function() {
$("#Logo").val("");
$("#logoImg").hide();
});
$("#CanComment").click(function() {
if ($(this).is(":checked")) {
$("#disqusSet").show();
} else {
$("#disqusSet").hide();
}
});
$("#CanComment").trigger("click").trigger("click"); // 恶心的想法
// 基本设置
$("#baseInfo .btn-success").click(function() {
var data = {
Title : $("#Title").val(),
SubTitle : $("#SubTitle").val(),
Logo : $("#Logo").val(),
AboutMe : getEditorContent()
}
post("/blog/setUserBlogBase", data, function(ret) {
showMsg2($("#baseInfo .msg"), "保存成功", 2000);
$("#blogDesc").html(data.SubTitle);
$("#logo").html(data.Title);
if(data.Logo) {
$("#logo").html(t('<img src="?" />', data.Logo));
}
}, this);
});
// 评论
$("#commentInfo .btn-success").click(function(e) {
e.preventDefault();
var data = {
CanComment : $("#CanComment").is(":checked"),
DisqusId : $("#DisqusId").val(),
}
post("/blog/setUserBlogComment", data, function(ret) {
showMsg2($("#commentInfo .msg"), "保存成功", 2000);
}, this);
});
// 主题
$("#styleInfo .btn-success").click(function(e) {
e.preventDefault();
var data = {
Style : $("input[name='Style']:checked").val()
}
post("/blog/setUserBlogStyle", data, function(ret) {
showMsg2($("#styleInfo .msg"), "保存成功", 2000);
}, this);
});
$("input[name='Style']").click(function() {
$("#styleLink").attr("href", "/css/blog/" + $(this).val() + ".css");
});
});
function inProgress() {
}
// filename, result, resultCode
function uploadFinish(ret) {
if (ret) {
if (ret.resultCode == '1') {
$("#logoImg img").attr("src", ret.filename).parent().show();
$("#Logo").val(ret.filename);
return;
}
alert(ret.result);
return;
}
// 上传出错
alert("上传出错");
}
function submit() {
var tpl = '<form action="/blog/setBlog" method="post"><input name="Title" value="?" />';
tpl += '<input name="SubTitle" value="?" />';
tpl += '<input name="Logo" value="?" />';
tpl += '<input name="CanComment" value="?" />';
tpl += '<input name="DisqusId" value="?" />';
tpl += '<input name="Style" value="?" />';
tpl += '<textarea name="AboutMe">?</textarea>';
tpl += "</form";
var data = {
Title : $("#Title").val(),
SubTitle : $("#SubTitle").val(),
Logo : $("#Logo").val(),
AboutMe : getEditorContent(),
CanComment : $("#CanComment").is(":checked"),
DisqusId : $("#DisqusId").val(),
Style : $("input[name='Style']:checked").val()
}
if (!data.DisqusId) {
data.DisqusId = "leanote";
}
$(t(tpl, data.Title, data.SubTitle, data.Logo,
data.CanComment, data.DisqusId, data.Style,
data.AboutMe)).submit();
}
</script>
</div>
</body>
</html>
+139
View File
@@ -0,0 +1,139 @@
{{template "Blog/header.html" .}}
<!-- -->
<div id="postsContainer">
<div id="posts">
<div class="each-post">
<div class="title">
{{.blog.Title}}
</div>
<div class="created-time">
<i class="fa fa-bookmark-o" style="color: #666"></i>
{{if .blog.Tags}}
{{blogTags .blog.Tags}}
{{else}}
无
{{end}}
|
<i class="fa fa-calendar" style="color: #666"></i> 更新 {{.blog.UpdatedTime | datetime}} |
<i class="fa fa-calendar" style="color: #666"></i> 创建 {{.blog.CreatedTime | datetime}}
</div>
<div class="desc" id="content">
{{if .blog.IsMarkdown }}
<div id="markdownContent" style="display: none">
<!-- 用textarea装html, 防止得到的值失真 -->
<textarea>
{{.blog.Content | raw}}
</textarea>
</div>
<div style="padding: 20px; text-align: center">
<img src="/images/loading-32.gif" />
</div>
{{else}}
{{.blog.Content | raw}}
{{end}}
</div>
<!-- comment -->
{{template "blog/comment.html" .}}
</div>
</div>
</div>
{{template "Blog/footer.html" .}}
{{template "Blog/highlight.html"}}
<!-- Nav -->
<style>
#blogNav {
display: none;
background-color: #fff;
opacity: 0.7;
position: fixed;
z-index: 10;
padding: 3px;
border-radius: 3px;
}
#blogNavContent {
overflow-y: auto;
max-height: 250px;
display: none;
}
#blogNavNav {
cursor: pointer;
}
#blogNav a {
color: #666;
}
#blogNav:hover {
opacity: 0.9;
}
#blogNav a:hover {
color: #0fb264;
}
#blogNav ul {
padding-left: 20px;
}
#blogNav ul .nav-h1 {
}
#blogNav ul .nav-h2 {
margin-left: 20px;
}
#blogNav ul .nav-h3 {
margin-left: 30px;
}
#blogNav ul .nav-h4 {
margin-left: 40px;
}
#blogNav ul .nav-h5 {
margin-left: 50px;
}
</style>
<div id="blogNav">
<div id="blogNavNav">
<i class="fa fa-align-justify" title="文档导航"></i>
<span>{{msg . "nav"}}</span>
</div>
<div id="blogNavContent" style="max-width: 200px">
</div>
</div>
<script src="/public/js/app/blog/nav.js"></script>
{{if .blog.IsMarkdown }}
<script src="/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Converter.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Sanitizer.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Editor.js"></script>
<script src="/public/mdeditor/editor/pagedown/local/Markdown.local.zh.js"></script>
<script src="/public/mdeditor/editor/Markdown.Extra.js"></script>
<!--mathjax-->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]], processEscapes: true }, messageStyle: "none"});
</script>
<script src="/public/mdeditor/editor/mathJax.js"></script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
var content = $.trim($("#markdownContent textarea").val());
// $("#markdownContent").html("");
var converter = Markdown.getSanitizingConverter();
Markdown.Extra.init(converter, {extensions: ["tables", "fenced_code_gfm", "def_list"]});
var html = converter.makeHtml(content);
$("#content").html(html);
$("pre").addClass("prettyprint linenums");
prettyPrint();
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"wmd-preview"]);
initNav();
</script>
{{else}}
<script>
$(function() {
initNav();
});
</script>
{{end}}
</body>
</html>
+72
View File
@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<title>404</title>
</head>
<body>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, Sans;
background: #EEEEEE;
}
.block {
padding: 20px;
border-bottom: 1px solid #aaa;
}
#header h1 {
font-weight: normal;
font-size: 28px;
margin: 0;
}
#more {
color: #666;
font-size: 80%;
border: none;
}
#header {
background: #FFFFCC;
}
#header p {
color: #333;
}
#routes {
background: #f6f6f6;
}
#routes h2 {
font-weight: normal;
font-size: 18px;
margin: 0 0 10px 0;
}
#routes ol {
}
#routes li {
font-size: 14px;
font-family: monospace;
color: #333;
}
</style>
<div id="header" class="block">
{{with .Error}}
<h1>
{{.Title}}
</h1>
<p>
{{.Description}}
</p>
{{end}}
</div>
<div id="routes" class="block">
<h2>These routes have been tried, in this order :</h2>
<ol>
{{range .Router.Routes}}
<li>{{pad .Method 10}}{{pad .Path 50}}{{.Action}}</li>
{{end}}
</ol>
</div>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
{{if eq .RunMode "dev"}}
{{template "errors/404-dev.html" .}}
{{else}}
{{template "home/header_box.html" .}}
<style>
html, body {
height: 100%;
}
</style>
<section id="box">
<div>
<h1>error-404</h1>
<form class="form-inline" id="boxForm">
This page cann't found.
<br />
<a href="javascript:history.go(-1)">Back</a>
</form>
</div>
</section>
</body>
</html>
{{end}}
+118
View File
@@ -0,0 +1,118 @@
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, Sans;
background: #EEEEEE;
}
.block {
padding: 20px;
border-bottom: 1px solid #aaa;
}
#header h1 {
font-weight: normal;
font-size: 28px;
margin: 0;
}
#more {
color: #666;
font-size: 80%;
border: none;
}
#header {
background: #fcd2da;
}
#header p {
color: #333;
}
#source {
background: #f6f6f6;
}
#source h2 {
font-weight: normal;
font-size: 18px;
margin: 0 0 10px 0;
}
#source .lineNumber {
float: left;
display: block;
width: 40px;
text-align: right;
margin-right: 10px;
font-size: 14px;
font-family: monospace;
background: #333;
color: #fff;
}
#source .line {
clear: both;
color: #333;
margin-bottom: 1px;
}
#source pre {
font-size: 14px;
margin: 0;
overflow-x: hidden;
}
#source .error {
color: #c00 !important;
}
#source .error .lineNumber {
background: #c00;
}
#source a {
text-decoration: none;
}
#source a:hover * {
cursor: pointer !important;
}
#source a:hover pre {
background: #FAFFCF !important;
}
#source em {
font-style: normal;
text-decoration: underline;
font-weight: bold;
}
#source strong {
font-style: normal;
font-weight: bold;
}
</style>
{{with .Error}}
<div id="header" class="block">
<h1>
{{.Title}}
</h1>
<p>
{{if .SourceType}}
The {{.SourceType}} <strong>{{.Path}}</strong> does not compile: <strong>{{.Description}}</strong>
{{else}}
{{.Description}}
{{end}}
</p>
</div>
{{if .Path}}
<div id="source" class="block">
<h2>In {{.Path}}
{{if .Line}}
(around {{if .Line}}line {{.Line}}{{end}}{{if .Column}} column {{.Column}}{{end}})
{{end}}
</h2>
{{range .ContextSource}}
<div class="line {{if .IsError}}error{{end}}">
<span class="lineNumber">{{.Line}}:</span>
<pre>{{.Source}}</pre>
</div>
{{end}}
</div>
{{end}}
{{if .MetaError}}
<div id="source" class="block">
<h2>Additionally, an error occurred while handling this error.</h2>
<div class="line error">
{{.MetaError}}
</div>
</div>
{{end}}
{{end}}
+18
View File
@@ -0,0 +1,18 @@
{{if eq .RunMode "dev"}}
{{template "errors/500-dev.html" .}}
{{else}}
{{template "home/header_box.html" .}}
<section id="box">
<div>
<h1>error-500</h1>
<form class="form-inline" id="boxForm">
Sorry, we got a error.
<br />
<a href="javascript:history.go(-1)">Back</a>
</form>
</div>
</section>
</body>
</html>
{{end}}
+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>图片上传结果</title>
<script language="javascript" type="text/javascript">
window.parent.window.uploadFinish({
filename:'{{.fileUrlPath}}',
result: '{{.resultMsg}}',
resultCode: '{{.resultCode}}'
});
</script>
</head>
</html>
+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
window.parent.window.leanoteImagesDialog.uploadFinish({
filename:'{{.fileUrlPath}}',
result: '{{.resultMsg}}',
resultCode: '{{.resultCode}}'
});
</script>
</head>
</html>
+83
View File
@@ -0,0 +1,83 @@
{{template "home/header_box.html" .}}
<style>
</style>
<section id="box">
<div>
<h1>leanote | {{msg . "findPassword"}}</h1>
<form class="form-inline" id="boxForm">
<div class="alert alert-danger" id="loginMsg"></div>
<table style="width: 100%">
<tr>
<td style="width: 110px"><label for="email">{{msg . "email"}}</label>
<input type="text" class="form-control" id="email" name="email" value="{{.email}}"> </td>
</tr>
<tr>
<td>
<button id="loginBtn" class="btn btn-success" style="width: 100%">{{msg . "findPassword"}}</button>
</td>
</tr>
</table>
</form>
<div id="quickLinks">
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</div>
</div>
</section>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script src="/js/common.js"></script>
<script>
$(function() {
$("#email").focus();
if($("#email").val()) {
$("#pwd").focus();
}
function showMsg(msg, id) {
$("#loginMsg").html(msg).show();
if(id) {
$("#" + id).focus();
}
}
function hideMsg() {
$("#loginMsg").hide();
}
$("#loginBtn").click(function(e){
e.preventDefault();
var email = $("#email").val();
if(!email) {
showMsg("{{msg . "inputEmail"}}", "email");
return;
} else {
var myreg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/;
if(!myreg.test(email)) {
showMsg("{{msg . "wrongEmail"}}", "email");
return;
}
}
$("#loginBtn").html("{{msg . "ing"}}...").addClass("disabled");
// hideMsg();
$.post("/doFindPassword", {email: email}, function(e) {
$("#loginBtn").html("{{msg . "findPassword"}}").removeClass("disabled");
if(e.Ok) {
var msg = "{{msg . "findPasswordSendEmailOver"}}";
var loginAddress = getEmailLoginAddress(email);
if(loginAddress) {
msg += ' <a target="_blank" href="' + loginAddress + '">{{msg . "checkEmai"}}</a>';
}
$("#loginMsg").html(msg).show().removeClass("alert-danger").addClass("alert-success");
} else {
showMsg(e.Msg, "email");
}
});
});
});
</script>
</body>
</html>
+99
View File
@@ -0,0 +1,99 @@
{{template "home/header_box.html" .}}
<section id="box">
<div>
<h1>leanote | {{msg . "updatePassword"}}</h1>
<form class="form-inline" id="boxForm">
<div class="form-group">
</div>
<div class="alert alert-danger" id="loginMsg"> </div>
<table style="width: 100%">
<tr>
<td style="width: 350px"><label for="email">{{msg . "email"}}</label>
<br />
{{.findPwd.Email}}
</td>
</tr>
<tr>
<td>
<label for="pwd">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
{{msg . "passwordTips"}}
</td>
</tr>
<tr>
<td>
<label for="pwd2">{{msg . "password2"}}</label>
<input type="password" class="form-control" id="pwd2" name="pwd2">
</td>
</tr>
<tr>
<td>
<button id="loginBtn" class="btn btn-success" style="width: 100%">{{msg . "updatePassword"}}</button>
</td>
</tr>
</table>
</form>
</div>
</section>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
$(function() {
$("#pwd").focus();
function showMsg(msg, id) {
$("#loginMsg").html(msg).show();
if(id) {
$("#" + id).focus();
}
}
function hideMsg() {
$("#loginMsg").hide();
}
$("#loginBtn").click(function(e){
e.preventDefault();
var pwd = $("#pwd").val();
var pwd2 = $("#pwd2").val();
if(!pwd) {
showMsg("{{msg . "inputPassword"}}", "pwd");
return;
} else {
if(pwd.length < 6) {
showMsg("{{msg . "notGoodPassword"}}", "pwd");
return;
}
}
if(!pwd2) {
showMsg("{{msg . "inputPassword2"}}", "pwd2");
return;
} else {
if(pwd != pwd2) {
showMsg("{{msg . "confirmPassword"}}", "pwd2");
return;
}
}
$("#loginBtn").html("{{msg . "ing"}}...").addClass("disabled");
// hideMsg();
$.post("/findPasswordUpdate", {pwd: pwd, token: "{{.findPwd.Token}}"}, function(e) {
$("#loginBtn").html("{{msg . "updatePassword"}}").removeClass("disabled");
if(e.Ok) {
$("#loginBtn").html("{{msg . "updatePasswordSuccessRedirectToLogin"}}");
location.href = "/login?email={{.findPwd.Email}}";
} else {
showMsg(e.Msg, "pwd");
}
});
});
});
</script>
</body>
</html>
@@ -0,0 +1,22 @@
{{template "home/header_box.html" .}}
<section id="box">
<div>
<h1>leanote {{msg . "findPassword"}} - {{msg . "findPasswordTimeout"}}</h1>
<form class="form-inline" id="boxForm">
<div class="alert alert-danger" id="loginMsg" style="display: block">
{{msg . "findPasswordTimeout"}}, <a href="/findPassword">{{msg . "reFindPassword"}}</a>
</div>
</form>
<div id="quickLinks">
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</div>
</div>
</section>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
</body>
</html>
+22
View File
@@ -0,0 +1,22 @@
<div id="footer">
<div class="container">
<div class="row">
<div class="col-md-6">
<i class="fa fa-envelope-o fa-3x icon-muted"></i>
<h2>Contact</h2>
<a href="http://weibo.com/leanotecom" target="_blank"><i class="fa fa-weibo"></i></a>
<br />
leanote@leanote.com
<div id="beian">
<a href="http://www.miibeian.gov.cn" target="_blank">沪ICP备14006011号</a>
</div>
</div>
<div class="col-md-6">
<i class="fa fa-globe fa-3x icon-muted"></i>
<h2>Join Us</h2>
<a href="https://github.com/leanote/leanote">github leanote</a>
</div>
</div>
</div>
</div>
+53
View File
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note!">
<meta name="author" content="leanote">
<title>{{.title}}</title>
<link href="/css/bootstrap.css" rel="stylesheet">
<link href="/css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/css/index.css" rel="stylesheet">
<style>
</style>
<script>
function log(o) {
}
</script>
</head>
<body>
<div id="headerContainer" style="background-color:#fff" class="navbar-fixed-top">
<div class="container" style="clearfix" id="header">
<div class="pull-left">
<h1>
<a href="/index">
<img src="/images/logo.png" id="" style="height: 40px" title="leanote, Your own cloud note!"/>
</a>
</h1>
</div>
<div class="pull-right" id="loginBtns">
{{if .userInfo.Email}}
{{msg . "hi"}}, {{.userInfo.Username}}
<a href="/note">{{msg . "myNote"}}</a>
<a href="/logout">{{msg . "logout"}}</a>
{{else}}
<a href="/login">{{msg . "login"}}</a>
<a href="/register" class="btn-register">{{msg . "register"}}</a>
{{end}}
</div>
<ul id="blogNav" class="pull-right">
<li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<li><a href="/index#aboutLeanote" target="#aboutLeanote" class="smooth-scroll">{{msg . "aboutLeanote"}}</a> </li>
<li><a id="leanoteBlog" href="http://leanote.com/blog/leanote" target="_blank" class="">{{msg . "leanoteBlog"}}</a></li>
</ul>
</div>
</div>
+21
View File
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note!">
<meta name="author" content="leanote">
<title>{{.title}}</title>
<link href="/css/bootstrap.css" rel="stylesheet">
<link href="/css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/css/index.css" rel="stylesheet">
<style>
html, body {
height: 100%;
}
</style>
</head>
<body>
+86
View File
@@ -0,0 +1,86 @@
{{template "home/header.html" .}}
<style>
</style>
<section>
<div class="header">
<h2>leanote, Your own cloud note!</h2>
<p>Knowledge, Sharing, Cooperation, Blog... all just in leanote</p>
<div>
<a class="btn btn-primary" href="https://github.com/leanote/leanote">Fork leanote from Github</a>
&nbsp;
&nbsp;
<a class="btn btn-default" href="/demo">Try it</a>
&nbsp;
&nbsp;
OR
&nbsp;
&nbsp;
<a class="btn btn-default" href="/register">{{msg . "register"}}</a>
</div>
</div>
<div class="preview" style="position: relative;">
<div>
<div class="img-header">
<img src="/images/home/mac-btns.png"/>
</div>
<img src="/images/home/preview2.png" style="width: 750px;" />
</div>
<div class="mobile">
<div class="mobile-header">
<img src="/images/home/mac-dot.png" />
</div>
<img class="mobile-image" src="/images/home/mobile.png" />
</div>
</div>
</section>
<div class="container" id="aboutLeanote">
<h2>{{msg . "aboutLeanote"}}</h2>
<div class="row">
<div class="col-md-3">
<h3>{{msg . "knowledge"}}</h3>
<p>{{msg . "knowledgeInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "share"}}</h3>
<p>{{msg . "shareInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "cooperation"}}</h3>
<p>{{msg . "cooperationInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div>
</div>
</div>
{{template "home/footer.html"}}
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
$(function() {
/*
var u = navigator.userAgent;
var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1;
if(isMobile || $("body").width() < 600) {
location.href = "/mobile/index";
}
*/
// 平滑滚动
$(".smooth-scroll").click(function(e) {
e.preventDefault();
var t = $(this).attr("target");
var targetOffset = $(t).offset().top - 80;
$('html,body').animate({scrollTop: targetOffset}, 300);
});
});
</script>
</body>
</html>
+103
View File
@@ -0,0 +1,103 @@
{{template "home/header_box.html" .}}
<section id="box">
<div>
<a class="back" href="javascript:history.go(-1);" tabindex="-1">←Back</a>
</div>
<div>
<h1>leanote | {{msg . "login"}}</h1>
<form class="form-inline" id="boxForm" >
<div class="alert alert-danger" id="loginMsg"></div>
<table>
<tr>
<td>
<label for="email">{{msg . "usernameOrEmail"}}</label>
<input type="text" class="form-control" id="email" name="email" value="{{.email}}">
</td>
</tr>
<tr>
<td>
<label for="pwd">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
</td>
</tr>
<tr>
<td>
<button id="loginBtn" class="btn btn-success" style="width: 100%">{{msg . "login"}}</button>
<br />
<a href="/findPassword">{{msg . "forgetPassword"}}</a>
<br />
<a href="/register">{{msg . "register"}}</a> {{msg . "or"}}
<a href="/demo">{{msg . "try"}}</a>
<div style="border-top: 1px dashed #666;margin:2px 0">
{{msg . "3th"}}: <a id="github">github<i class="fa fa-github"></i></a>
<span id="thirdLoginLoading" style="display: none"> <img src="/images/loading-a-20-2.gif" />正在登录...</span>
</div>
</td>
</tr>
</table>
</form>
<div id="quickLinks">
<a href="/index">{{msg . "home"}}</a>
</div>
</div>
</section>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
$(function() {
$("#email").focus();
if($("#email").val()) {
$("#pwd").focus();
}
function showMsg(msg, id) {
$("#loginMsg").html(msg).show();
if(id) {
$("#" + id).focus();
}
}
function hideMsg() {
$("#loginMsg").hide();
}
$("#loginBtn").click(function(e){
e.preventDefault();
var email = $("#email").val();
var pwd = $("#pwd").val();
if(!email) {
showMsg("{{msg . "inputUsername"}}", "email");
return;
}
if(!pwd) {
showMsg("{{msg . "inputPassword"}}", "pwd");
return;
} else {
if(pwd.length < 6) {
showMsg("{{msg . "wrongPassword"}}", "pwd");
return;
}
}
$("#loginBtn").html("{{msg . "logining"}}...").addClass("disabled");
// hideMsg();
$.post("/doLogin", {email: email, pwd: pwd}, function(e) {
$("#loginBtn").html("{{msg . "login"}}").removeClass("disabled");
if(e.Ok) {
$("#loginBtn").html("{{msg . "loginSuccess"}}...");
location.href = '/note';
} else {
showMsg(e.Msg, "pwd");
}
});
});
// github
$("#github").click(function() {
$("#thirdLoginLoading").show();
location.href="https://github.com/login/oauth/authorize?access_type=&approval_prompt=&client_id=3790fbf1fc14bc6c5d85&redirect_uri=http%3A%2F%2Fleanote.com%2Foauth%2FgithubCallback&response_type=code&scope=user&state=";
});
});
</script>
</body>
</html>
+22
View File
@@ -0,0 +1,22 @@
<div class="modal fade bs-modal-sm" id="leanoteDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="modalTitle">操作</h4>
</div>
<div class="modal-body">
..................
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
+115
View File
@@ -0,0 +1,115 @@
{{template "home/header_box.html" .}}
<section id="box">
<div>
<a class="back" href="javascript:history.go(-1);" tabindex="-1">←Back</a>
</div>
<div>
<h1>leanote | {{msg . "register"}}</h1>
<form class="form-inline" id="boxForm">
<div class="alert alert-danger" id="loginMsg"></div>
<table>
<tr>
<td>
<label for="email">{{msg . "email"}}</label>
<input type="text" class="form-control" id="email" name="email">
</td>
</tr>
<tr>
<td>
<label for="pwd">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
{{msg . "passwordTips"}}
</td>
</tr>
<tr>
<td>
<label for="pwd2">{{msg . "password2"}}</label>
<input type="password" class="form-control" id="pwd2" name="pwd2">
</td>
</tr>
<tr>
<td>
<button id="registerBtn" class="btn btn-success" style="width: 100%">{{msg . "register"}}</button>
</td>
</tr>
</table>
</form>
<div id="quickLinks">
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</div>
</div>
</section>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
$(function() {
$("#email").focus();
function showMsg(msg, id) {
$("#loginMsg").html(msg).show();
if(id) {
$("#" + id).focus();
}
}
function hideMsg() {
$("#loginMsg").hide();
}
$("#registerBtn").click(function(e){
e.preventDefault();
var email = $("#email").val();
var pwd = $("#pwd").val();
var pwd2 = $("#pwd2").val();
if(!email) {
showMsg("{{msg . "inputEmail"}}", "email");
return;
} else {
var myreg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[0-9a-zA-Z]{2,3}$/;
if(!myreg.test(email)) {
showMsg("{{msg . "wrongEmail"}}", "email");
return;
}
}
if(!pwd) {
showMsg("{{msg . "inputPassword"}}", "pwd");
return;
} else {
if(pwd.length < 6) {
showMsg("{{msg . "notGoodPassword"}}", "pwd");
return;
}
}
if(!pwd2) {
showMsg("{{msg . "inputPassword2"}}", "pwd2");
return;
} else {
if(pwd != pwd2) {
showMsg("{{msg . "confirmPassword"}}", "pwd2");
return;
}
}
$("#registerBtn").html("{{msg . "ing"}}...").addClass("disabled");
// hideMsg();
$.post("/doRegister", {email: email, pwd: pwd}, function(e) {
$("#registerBtn").html("{{msg . "register"}}").removeClass("disabled");
if(e.Ok) {
$("#registerBtn").html("{{msg . "registerSuccessAndRdirectToNote"}}");
location.href = '/note';
} else {
showMsg(e.Msg, "email");
}
});
});
});
</script>
</body>
</html>
+178
View File
@@ -0,0 +1,178 @@
<!DOCTYPE HTML>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>leanote</title>
<link href="/public/css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/public/mobile/css/base.css" rel="stylesheet" type="text/css">
<link href="/public/css/bootstrap.css" rel="stylesheet" />
<link href="/public/mobile/css/leanote.css" rel="stylesheet" type="text/css">
<script>
function log(o) {
// console.log(o);
}
</script>
</head>
<body>
<div id="preloader">
<div id="status">
<p class="center-text">
</p>
</div>
</div>
<div class="all-elements" style="height: 100%">
<div id="sidebar" class="page-sidebar sidebar-content-background">
<div class="page-sidebar-scroll">
<div>
<div class="sidebar-section">
<a href="#" class="sidebar-close"></a>
<em>leanote</em>
</div>
<div class="sidebar-decoration"></div>
<div id="userInfo">
{{.userInfo.Username}}
</div>
<div class="sidebar-decoration"></div>
<div class="sidebar-navigation">
<div class="nav-item">
<a href="#" class="nav-newest">最新笔记</a>
</div>
<div class="nav-item">
<a href="#" class="nav-myNotebooks">我的笔记本</a>
</div>
<div class="nav-item">
<a href="#" class="nav-myTags">我的标签</a>
</div>
</div>
<div class="sidebar-decoration"></div>
<div class="sidebar-navigation">
<div class="nav-item">
<a href="#" class="nav-logout">退出</a>
</div>
</div>
</div>
</div>
</div>
<div id="content" class="page-content" style="position: absolute;background-color: #fff;width: 100%;">
<!-- notebooks -->
<div id="notebooks">
<!-- 头部 -->
<div class="content-controls clearfix">
<div class="pull-left">
<i class="fa fa-angle-left back"></i>
</div>
<div class="pull-left">
<span class="g-title">
我的笔记本
</span>
</div>
<!-- 其它按钮 -->
<div class="pull-right btns">
</div>
</div>
<div class="each-container">
<div class="each-content" style="bottom: 0">
<ul id="notebookList">
</ul>
</div>
<!--
<div class="each-footer">
添加笔记本
</div>
-->
</div>
</div>
<!-- note -->
<div id="notes">
<!-- 头部 -->
<div class="content-controls clearfix">
<div class="pull-left">
<i class="fa fa-angle-left back"></i>
</div>
<div class="pull-left">
<span class="g-title">
XX的笔记
</span>
</div>
<!-- 其它按钮 -->
<div class="pull-right btns">
</div>
</div>
<div class="each-container">
<div class="each-content" style="bottom:0">
<div id="noteItemList">
</div>
</div>
<!--
<div class="each-footer">
添加笔记
</div>
-->
</div>
</div>
<!-- view -->
<div id="view">
<div class="content-controls clearfix">
<div class="pull-left">
<i class="fa fa-angle-left back"></i>
</div>
<div class="pull-left">
<span class="g-title">
笔记标题
</span>
</div>
<!-- 其它按钮 -->
<div class="pull-right btns">
<i class="fa fa-pencil" id="editBtn"></i>
</div>
</div>
<div class="each-container">
<div class="each-content" style="bottom:0">
<img id="loading" style="width: 30px; margin: auto; margin-top: 100px; height: 30px; display: none" src="/public/mobile/images/status.gif" />
<div id="noteContent"></div>
</div>
<!--
<div class="each-footer">
</div>
-->
</div>
</div>
</div>
</div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/public/mobile/js/snap.js"></script>
<script src="/public/js/i18n/msg.{{.locale}}.js"></script>
<script src="/public/mobile/js/app/mobile-common.js"></script>
<script>
var UserInfo = json({{.userInfoJson}});
var notebooks = json({{.notebooks}});
</script>
<!--
// var shareNotebooks = json({{.shareNotebooks}});
// var sharedUserInfos = json({{.sharedUserInfos}});
// var tagsJson = json({{.tagsJson}});
-->
<script src="/public/mobile/js/app/mobile-notebook.js"></script>
<script src="/public/mobile/js/app/mobile-tag.js"></script>
<script src="/public/mobile/js/app/mobile-note.js"></script>
<script src="/public/mobile/js/app/mobile-page.js"></script>
<script>
Notebook.RenderNotebooks(notebooks);
// Share.RenderShareNotebooks(sharedUserInfos, shareNotebooks);
// Tag.renderTagNav(tagsJson);
</script>
</body>
</html>
+109
View File
@@ -0,0 +1,109 @@
<!DOCTYPE HTML>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>leanote login</title>
<link href="/public/css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/public/mobile/styles/style.css" rel="stylesheet" type="text/css">
<link href="/public/mobile/styles/colors/default.css" rel="stylesheet" type="text/css" id="pagestyle">
<link href="/public/mobile/styles/framework.css" rel="stylesheet" type="text/css">
<link href="/public/css/bootstrap.css" rel="stylesheet" />
<link href="/public/mobile/styles/life.css" rel="stylesheet" type="text/css">
<style>
.alert {
padding: 10px;
margin-bottom: 10px;
border-radius: 3px;
}
.form-control {
border-radius: 0;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-transition: nonoe;
transition: none;
}
</style>
</head>
<body>
<div id="preloader">
<div id="status">
<p class="center-text">
loading...
</p>
</div>
</div>
<div style="text-align: center">
<img src="/public/mobile/images/blog01.jpg" style="width: 100%"/>
<form id="loginForm" style="width: 90%; margin: auto;">
<div class="form-group">
<h2 style="margin-top:10px">leanote 登录</h2>
</div>
<div class="alert alert-danger" id="loginMsg" style="display: none"></div>
<div class="form-group">
<input class="form-control" id="email" name="email" placeholder="用户名或Email" value="">
</div>
<div class="form-group">
<input type="password" class="form-control" id="pwd" name="pwd" placeholder="密码">
</div>
<button id="loginBtn" class="btn btn-success" style="width: 100%">登录</button>
</form>
</div>
<script type="text/javascript" src="/public/mobile/scripts/jquery.js"></script>
<script>
$(function() {
$("#status").fadeOut(); // will first fade out the loading animation
$("#preloader").delay(350).fadeOut("slow"); // will fade out the white DIV that covers the website.
function showMsg(msg, id) {
$("#loginMsg").html(msg).show();
if(id) {
$("#" + id).focus();
}
}
function hideMsg() {
$("#loginMsg").hide();
}
$("#loginBtn").click(function(e){
e.preventDefault();
var email = $("#email").val();
var pwd = $("#pwd").val();
if(!email) {
showMsg("{{msg . "inputUsername"}}", "email");
return;
}
if(!pwd) {
showMsg("{{msg . "inputPassword"}}", "pwd");
return;
} else {
if(pwd.length < 6) {
showMsg("{{msg . "wrongPassword"}}", "pwd");
return;
}
}
$("#loginBtn").html("{{msg . "logining"}}...").addClass("disabled");
// hideMsg();
$.post("/doLogin", {email: email, pwd: pwd}, function(e) {
$("#loginBtn").html("{{msg . "login"}}").removeClass("disabled");
if(e.Ok) {
$("#loginBtn").html("{{msg . "loginSuccess"}}...");
location.href = '/mobile/index';
} else {
showMsg(e.Msg, "pwd");
}
});
});
});
</script>
</body>
</html>
+29
View File
@@ -0,0 +1,29 @@
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="modalTitle">历史记录</h4>
</div>
<div class="modal-body">
<div id="shareMsg" class="alert alert-danger" style="display: none; margin: 5px 0 0 0;"></div>
<table class="table table-hover" id="shareNotebookTable">
<tbody>
{{range $i, $v := .histories}}
<tr>
<td>
{{raw $v.Content}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
+953
View File
@@ -0,0 +1,953 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="life">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note">
<title>leanote, {{msg . "moto"}}</title>
<link href="css/bootstrap.css" rel="stylesheet" />
<!-- 先加载, 没有样式, 宽度不定 -->
<link rel="stylesheet" href="tinymce/skins/custom/skin.min.css" type="text/css" />
<!-- leanote css -->
<link rel="stylesheet" href="css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css" type="text/css" id="themeLink" />
<link href="css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" />
<script>
function log(o) {
console.log(o);
}
</script>
</head>
<body>
<div id="page">
<div id="pageInner">
<!-- 头部 -->
<!-- <div id="headerContainer"> -->
<div id="header">
<!-- <div id="headerLeft" class="pull-left">
</div>
-->
<div id="logo" class="pull-left">
<span>lea</span>note
<!-- <img style="height: 45px;" src="/images/logo.png" title="leanote, 不一样的笔记" alt="leanote">
-->
</div>
<div id="switcher" class="pull-left">
<i class="fa fa-align-justify" id="leftSwitcher" title="{{msg . "leftHidden"}}"></i>
<span id="leftSwitcher2" title="{{msg . "leftShow"}}">lea</span>
</div>
<!-- search -->
<div class="pull-left">
<form class="navbar-form form-inline col-lg-2 hidden-xs" id="searchNote">
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
</form>
</div>
<!-- 全局按钮 -->
<div class="pull-left" style="line-height: 40px; margin-top: 10px;">
<!-- 新建笔记 -->
<div id="newMyNote">
<a id="newNoteBtn" title="{{msg . "newNote"}}">
<i class="fa fa-file-o"></i>
{{msg . "newNote"}}
</a>
|
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
Markdown
</a>
- <span id="curNotebookForNewNote" notebookId=""></span>
<div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle"
id="dropdownMenu2" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2" style="width: 180px;left: -5px;" id="notebookNavForNewNote">
</ul>
</div>
</div>
<!-- 只为新建别人的笔记 -->
<div id="newSharedNote" style="display: none">
<a id="newSharedNoteBtn">
<i class="fa fa-file-o"></i>
{{msg . "newNote"}}
</a>
|
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
Markdown
</a>
- <span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle"
id="dropdownMenu2" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2" style="width: 180px;left: -5px;" id="notebookNavForNewSharedNote">
</ul>
</div>
</div>
</div>
<div class="pull-left" style="line-height: 40px; margin-top: 10px; margin-left: 10px;" id="leanoteMsg">
<span id="msg" style="display: inline-block;"></span>
<img id="loading" style="visibility: hidden" src="/images/loading-a-20.gif" />
{{if eq .userInfo.Email "demo@leanote.com"}}
<span>
{{msg . "demoRegister"}}
</span>
{{end}}
</div>
<!--
<div class="pull-left alert-warning" style="line-height: 20px; margin-top: 10px; margin-left: 0px; display: none" id="verifyMsg">
您还没有验证邮箱, 验证邮件已发送至 {{.userInfo.Email}}.
<br />
<a class=".nowToActive">现在去验证</a> <a id="reSendActiveEmail">重新发送</a> <a id="wrongEmail">邮箱填错了?</a>
</div>
-->
<div class="pull-right" style="margin: 0 10px" id="myProfile">
<div class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" style="line-height: 60px;">
<!--
<img src="images/avatar.png" style="height: 40px; border: 1px solid #ccc" />
-->
<span class="username">
{{if .userInfo.UsernameRaw}}
{{.userInfo.UsernameRaw}}
{{else}}
{{.userInfo.Username}}
{{end}}
</span>
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu li-a" role="menu">
<li role="presentation" id="setInfo">
<i class="fa fa-info"></i>
{{msg . "accountSetting"}}
</li>
<li role="presentation" id="setTheme">
<i class="fa fa-sun-o"></i>
{{msg . "themeSetting"}}
</li>
<!--
<li role="presentation" id="yourSuggestions">
<i class="fa fa-comment-o"></i>
{{msg . "yourSuggestions"}}
</li>
-->
<li role="presentation" onclick="logout()">
<i class="fa fa-sign-out"></i>
{{msg . "logout"}}
</li>
</ul>
</div>
</div>
<div class="pull-right" style="margin: 0 10px" id="topNav">
<a target="_blank" href="/blog/{{.userInfo.Username}}">
{{msg . "myBlog"}}
</a>
</div>
<!--
<ul id="setting" class="pull-right">
<li class="dropdown messages">
<a class="dropdown-toggle" data-toggle="dropdown">
<span aria-hidden="true" class="icon se7en-flag"></span>
<div class="sr-only">Messages</div>
<p class="counter">3</p>
</a>
<ul class="dropdown-menu">
<li><a href="#"> c@c.com 共享了笔记给你 是否接受? </a></li>
<li><a href="#"> d@c.com 共享了笔记给你 是否接受? </a></li>
</ul>
</li>
<li class="dropdown settings">
<a class="dropdown-toggle" data-toggle="dropdown">
<span aria-hidden="true" class="icon se7en-gear"></span>
<div class="sr-only">Settings</div>
</a>
<ul class="dropdown-menu">
<li><a class="settings-link blue"><span></span>Blue</a></li>
<li><a class="settings-link green"><span></span>Green</a></li>
<li><a class="settings-link orange"><span></span>Orange</a>
</li>
<li><a class="settings-link magenta"><span></span>Magenta</a>
</li>
<li><a class="settings-link gray"><span></span>Gray</a></li>
</ul>
</li>
</ul>
-->
<div style="clear: both"></div>
</div>
<div id="mainContainer" class="clearfix">
<div id="mainMask">
<img src="/images/loading-30.gif" />
<br />
<br />
loading...
</div>
<div id="leftNotebook">
<div id="notebook">
<div class="folderNote opened" id="myNotebooks">
<div class="folderHeader clearfix">
<i class="fa fa-book pull-left fa-left"></i>
<span class="pull-left">
{{msg . "notebook"}}
</span>
<div class="pull-right" id="addNotebookPlus" title="{{msg . "addNotebook"}}">
<i class="fa fa-plus" title="{{msg . "addNotebook"}}"></i>
</div>
</div>
<ul class="folderBody" id="notebookList">
</ul>
</div>
<div class="folderNote closed" id="myTag">
<div class="folderHeader">
<i class="fa fa-bookmark fa-left"></i>
<span>
{{msg . "tag"}}
</span>
</div>
<ul class="folderBody" id="tagNav">
<li><a> <span class="label label-red">{{msg . "red"}}</span></a></li>
<li><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li>
<li><a> <span class="label label-yellow">{{msg . "yellow"}}</span></a></li>
<li><a> <span class="label label-green">{{msg . "green"}}</span></a></li>
</ul>
</div>
<div class="folderNote closed" id="">
<div class="folderHeader">
<i class="fa fa-user fa-left"></i>
<span>
共享
</span>
</div>
<ul class="folderBody" id="shareNotebooks">
</ul>
</div>
</div>
<!-- 缩小版 -->
<div id="notebookMin">
<!-- 这里隐藏, 不要 -->
<div target="#notebookList" title="{{msg . "notebook"}}" class="minContainer">
<i class="fa fa-book"></i>
<ul class="dropdown-menu" id="minNotebookList">
</ul>
</div>
<div target="#tagNav" title="{{msg . "myTag"}}" class="minContainer">
<i class="fa fa-bookmark"></i>
<ul class="dropdown-menu" id="minTagNav">
</ul>
</div>
<div id="minShareNotebooks">
<!--
<div class="minContainer" target="" title="">
<i class="fa fa-user"></i>
<ul class="dropdown-menu">
</ul>
</div>
-->
</div>
</div>
<!--
底下隐藏/展开
<div id="notebookBottom">
<i class="fa fa-angle-left" id="leftSwitcher"></i>
</div>
-->
</div>
<div class="noteSplit" id="notebookSplitter"></div>
<div id="noteAndEditor">
<div id="noteList">
<div class="clearfix" id="notesAndSort" style="position: relative">
<div class="pull-left">
<!-- 我的笔记本 -->
<div class="dropdown" id="myNotebookNavForListNav">
<a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">
<span id="curNotebookForListNote">{{msg . "all"}}</span>
<i class="fa fa-angle-down"></i>
</a>
<!-- 如果notebook过多, 则会产生滚动 -->
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu1" style="left: 5px;" id="notebookNavForListNote">
</ul>
</div>
<!-- 共享的笔记本 -->
<div class="dropdown" id="sharedNotebookNavForListNav" style="display: none">
<a class="ios7-a dropdown-toggle" id="dropdownMenu2" data-toggle="dropdown">
<span id="curSharedNotebookForListNote">{{msg . "all"}}</span>
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu2" style="left: 5px;" id="sharedNotebookNavForListNote">
</ul>
</div>
</div>
<div class="pull-left" id="tagSearch" style="line-height: 38px">
</div>
<div id="sortType">
<div class="dropdown">
<a class="ios7-a dropdown-toggle" id="dropdownMenu1"
data-toggle="dropdown">
<!--
<i class="fa fa-sort-alpha-asc">
</i>Sort <i class="fa fa-angle-down"></i>
-->
</a>
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu1"
style="right: 3px; ! important; left: -100px; min-width: 100px;">
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">创建日期</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">修改日期</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">标题</a></li>
<li role="presentation" class="divider"></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">Separated </a></li>
</ul>
</div>
</div>
</div>
<!-- 笔记列表 -->
<!-- wrap 为了slimScroll -->
<div id="noteItemListWrap" style="position: absolute; left: 0; right: 0; top: 41px; bottom: 3px">
<div id="noteItemList">
</div>
</div>
</div>
<div class="noteSplit" id="noteSplitter"></div>
<div id="note">
<!-- 只读模式 -->
<div id="noteRead">
<div id="noteReadContainer">
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix">
<div class="pull-left">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
</div>
<!-- 修改时间 -->
<div class="pull-left" style="margin-left: 10px;">
<i class="fa fa-calendar"></i>{{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
</div>
<!-- 修改时间 -->
<div class="pull-left" style="margin-left: 10px;">
<i class="fa fa-calendar"></i>{{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
</div>
<div id="noteReadContent">
</div>
</div>
</div>
<!-- 遮罩, 为了resize3Columns用 -->
<div id="noteMask"
style="position: absolute; top: 0px; bottom: 0px; right: 0; left: 10px; z-index: -1"></div>
<div id="editorMask">
该笔记本下空空如也...何不
<br />
<div id="editorMaskBtns">
<br />
<a class="note">新建笔记</a>
<a class="markdown">新建Markdown笔记</a>
</div>
<div id="editorMaskBtnsEmpty">
Sorry, 这里不能添加笔记的.
</div>
</div>
<div id="noteTop">
<!-- 左侧放tag, 右侧放按钮, 比如save, info, move, delete -->
<div id="tool" class="clearfix">
<!-- tag可以参考 mac的文件tag, 预定义一些颜色, 用户也可以自己输入-->
<!--
<div class="pull-left" id="noteTitleDiv">
<input name="noteTitle" id="noteTitle" value="" placeholder="无标题">
</div>
-->
<div class="pull-left" id="tag">
<div id="tags" style="display: inline-block; line-height: 25px;">
</div>
<div class="dropdown" style="display: inline-block"
id="tagDropdown">
<i class="fa fa-bookmark-o"></i>
<a
class="metro-a dropdown-toggle" data-toggle="dropdown"
id="addTagTrigger" style="cursor: text; padding-left: 0">
{{msg . "clickAddTag"}}
</a>
<input type="text" id="addTagInput" />
<ul class="dropdown-menu" role="menu" id="tagColor">
<li role="presentation"><span class="label label-red">{{msg . "red"}}</span>
</li>
<li role="presentation"><span class="label label-blue">{{msg . "blue"}}</span>
</li>
<li role="presentation"><span class="label label-yellow">{{msg . "yellow"}}</span></li>
<li role="presentation"><span class="label label-green">{{msg . "green"}}</span></li>
</ul>
</div>
</div>
<ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s"
data-toggle="dropdown">{{msg . "save"}}</a></li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown">{{msg . "editorTips"}}</a></li>
<li><a class="ios7-a " id="contentHistory"
data-toggle="dropdown">{{msg . "history"}}</a></li>
</ul>
</div>
<div id="noteTitleDiv">
<input name="noteTitle" id="noteTitle" value="" placeholder="{{msg . "unTitled"}}">
</div>
</div>
<div id="editor">
<!-- 编辑器 -->
<div id="mceToolbar" style="">
<div id="popularToolbar"
style="position: absolute; right: 30px; left: 0"></div>
<a
style="dispaly: block; position: absolute; cursor: pointer; right: 10px; padding: 3px 10px"
id="moreBtn"> <i class="fa fa-angle-down"></i>
</a>
</div>
<div id="leanoteNav">
<h1>
<i class="fa fa-align-justify" title="文档导航"></i>
<span>{{msg . "nav"}}</span>
</h1>
<div id="leanoteNavContent">
</div>
</div>
<!-- 由此可以算高度 -->
<div id="editorContent" name="editorContent">
{{.noteContent}}
</div>
</div>
<div id="mdEditor">
<div id="wmd-button-bar" id="mdBar"></div>
<!-- 为了scroll -->
<div class="clearfix" id="mdEditorPreview">
<div id="left-column" class="pull-left">
<div id="wmd-panel-editor" class="wmd-panel-editor">
<textarea class="wmd-input theme" id="wmd-input" spellcheck="false"></textarea>
</div>
</div>
<div id="right-column" class="pull-right">
<div id="wmd-panel-preview" class="wmd-panel-preview preview-container">
<div id="wmd-preview" class="wmd-preview"></div>
</div>
</div>
</div>
<textarea id="md-section-helper"></textarea>
</div>
<!-- mdEditor -->
<!-- Hidden Popup Modal -->
<div class="modal fade bs-modal-sm" id="editorDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="editorDialog-title">操作</h4>
</div>
<div class="modal-body">
<p></p>
<div class="input-group">
<span class="input-group-addon">
<i></i>
</span>
<input type="text" class="form-control" placeholder="">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="editorDialog-confirm">确认</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>
</div>
<!-- 弹出框 模板 -->
<div class="modal fade bs-modal-sm" id="leanoteDialogRemote" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
</div>
<div class="modal fade bs-modal-sm" id="leanoteDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="modalTitle">操作</h4>
</div>
<div class="modal-body">
内容区
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal fade bs-modal-sm" id="sendRegisterEmailDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">邀请好友</h4>
</div>
<div class="modal-body">
<form role="form">
<div class="form-group">
<div id="registerEmailMsg" class="alert alert-warning" style="display: none">
</div>
<input type="hidden" id="toEmail"/>
<label for="emailContent">邮件内容</label>
<textarea class="form-control" id="emailContent">Hi, 我是李铁, leanote非常好用, 快来注册吧.</textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" id="registerEmailBtn">发送</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal fade bs-modal-sm" id="setThemeDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">主题设置</h4>
</div>
<div class="modal-body">
<style>
#themeForm td {
padding: 5px;
text-align: center;
}
#themeForm img {
border: 1px solid #eee;
padding: 2px;
}
</style>
<table id="themeForm">
<tr>
<td>
<label>
<img src="/images/slider/all.gif" height="100px">
<br />
<input type="radio" name="theme" value="default"> {{msg . "default"}}
</label>
</td>
<td>
<label>
<img src="/images/slider/all-simple.gif" height="100px">
<br />
<input type="radio" name="theme" value="simple"> {{msg . "simple"}}
</label>
</td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- 编辑器提示 -->
<div class="modal fade bs-modal-sm" id="tipsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "editorTips"}}</h4>
</div>
<div class="modal-body">
{{msg . "editorTipsInfo"}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- 图片上传 -->
<div class="modal fade bs-modal-sm" id="imageDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content" style="height: 460px;" >
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "uploadImage"}}</h4>
</div>
<div class="modal-body">
<iframe style="" height="360" src="" scrolling="no" frameBorder="0" width="99%"></iframe>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- 您的建议
<div class="modal fade bs-modal-sm" id="suggestionsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "yourSuggestions"}}</h4>
</div>
<div class="modal-body">
<div style="margin-bottom:20px;">
{{msg . "suggestionsInfo"}}<br />
</div>
<div class="alert alert-warning" id="suggestionMsg" style="display: none"> </div>
<textarea style="width: 100%; height: 100px" id="suggestionTextarea" placeholder="{{msg . "suggestions"}}"></textarea>
</div>
<div class="modal-footer">
<button id="suggestionBtn" class="btn btn-success" >{{msg . "submit"}}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div>
</div>
</div>
-->
<!-- modal 静态区域 -->
<div class="display: hidden">
<!-- 长微博 -->
<div id="html2ImageDialog">
<div class="modal-body">
<div style="max-height: 300px; padding: 5px 0; text-align: center; overflow: scroll;" class="weibo">
<span class="text">
leanote正在努力为你生成长微博...
</span>
<br />
<img src="/images/loading-24.gif"/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "cancel"}}</button>
<button type="button" class="btn btn-success sendWeiboBtn disabled">{{msg . "send"}}</button>
</div>
</div>
<!-- 长微博 -->
<div id="reSendActiveEmailDialog">
<div class="modal-body">
<div style="max-height: 300px; padding: 5px 0; text-align: center; overflow: scroll;" class="weibo">
<span class="text">
<img src="/images/loading-24.gif"/>
正在发送邮件到{{.userInfo.Email}}...
</span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-success viewEmailBtn disabled">查看邮件</button>
</div>
</div>
<!-- 帐户设置 -->
<div id="dialogSetInfo">
<div class="modal-body">
<ul class="nav nav-tabs" id="myTabs">
<li><a href="#baseInfo" data-toggle="tab">{{msg . "basicInfo"}}</a></li>
<li><a href="#emailInfo" data-toggle="tab">{{msg . "updateEmail"}}</a></li>
<li><a href="#updatePwd" data-toggle="tab">{{msg . "updatePassword"}}</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="baseInfo">
<form>
<table>
<tr>
<td>
<label for="username">用户名设置</label>
<div class="alert alert-danger" id="usernameMsg" style="display: none"></div>
<input type="text" class="form-control" id="username">
你的邮箱是 {{.userInfo.Email}}, 可以再设置一个唯一的用户名.
<br />
用户名至少4位, 不可含特殊字符.
</td>
</tr>
<tr>
<td>
<button id="usernameBtn" class="btn btn-success">提交</button>
</td>
</tr>
</table>
</form>
</div>
<div class="tab-pane" id="emailInfo">
<form>
<table>
<tr>
<td>
当前邮箱为: <span id="curEmail">{{.userInfo.Email}}</span>
{{if .userInfo.Verified}}
已验证
{{else}}
未验证
<a class="raw nowToActive">现在去验证</a>
<a class="raw reSendActiveEmail">重新发送</a>
{{end}}
<br />
<label for="email">修改邮箱</label>
<div class="alert alert-danger" id="emailMsg" style="display: none"></div>
<input type="text" class="form-control" id="email">
邮箱修改后, 验证之后才有效, 验证之后新的邮箱地址将会作为登录帐号使用.
</td>
</tr>
<tr>
<td>
<button id="emailBtn" class="btn btn-success">发送验证邮箱</button>
</td>
</tr>
</table>
</form>
</div>
<div class="tab-pane" id="updatePwd">
<form>
<table style="width: 80%">
<tr>
<td>
<div class="alert alert-danger" id="pwdMsg" style="display: none"></div>
</td>
</tr>
<tr>
<td style="width: 80px">
<label for="pwd">{{msg . "oldPassword"}}</label>
<input type="password" class="form-control" id="oldPwd" name="oldPwd">
</td>
</tr>
<tr>
<td>
<label for="pwd">{{msg . "newPassword"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
</td>
</tr>
<tr>
<td>
<label for="pwd2">{{msg . "password2"}}</label>
<input type="password" class="form-control" id="pwd2" name="pwd2">
</td>
</tr>
<tr>
<td>
<button id="pwdBtn" class="btn btn-success" style="width: 100%">{{msg . "submit"}}</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
<!-- 账号设置, 是通过third 登录进来的 -->
<div id="thirdDialogSetInfo">
<div class="modal-body">
<ul class="nav nav-tabs" id="thirdMyTabs">
<li><a href="#accountInfo" data-toggle="tab">创建帐号</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="accountInfo">
您现在使用的是第三方帐号登录leanote, 您也可以注册leanote帐号登录, 赶紧注册一个吧.
<br />
注册成功后仍可以使用第三方帐号登录leanote并管理您现有的笔记.
<form>
<div class="alert alert-danger" id="thirdAccountMsg" style="display: none"></div>
<table style="width: 100%">
<tr>
<td>
<label for="thirdEmail">邮箱</label>
</td>
<td>
<input type="text" class="form-control" id="thirdEmail">
</td>
</tr>
<tr>
<td>
<label for="thirdPwd">密码</label>
</td>
<td>
<input type="password" class="form-control" id="thirdPwd">
</td>
<td>
密码至少6位
</td>
</tr>
<tr>
<td>
<label for="thirdPwd2">重复密码</label>
</td>
<td>
<input type="password" class="form-control" id="thirdPwd2">
</td>
</tr>
<tr>
<td></td>
<td>
<button id="accountBtn" class="btn btn-success" style="width: 100%">提交</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
<div class="hide" id="copyDiv"></div>
</div>
</div>
</div>
<script src="js/jquery-1.9.0.min.js"></script>
<script src="js/i18n/msg.{{.locale}}.js"></script>
<script src="js/common.js"></script>
<script>
var UrlPrefix = "http://leanote.com"; // 为了发weibo
var UserInfo = json({{.userInfoJson}});
var notebooks = json({{.notebooks}});
var shareNotebooks = json({{.shareNotebooks}});
var sharedUserInfos = json({{.sharedUserInfos}});
var notes = json({{.notes}});
var noteContentJson = json({{.noteContentJson}});
var tagsJson = json({{.tagsJson}});
</script>
<!-- 渲染view -->
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
<script src="js/app/page.js"></script>
<script src="tinymce/tinymce.min.js"></script>
<script src="js/jquery-cookie-min.js"></script>
<script src="js/bootstrap-min.js"></script>
<script src="js/app/note.js"></script>
<script src="js/app/tag.js"></script>
<script src="js/app/notebook.js"></script>
<script src="js/app/share.js"></script>
<script src="js/object_id-min.js"></script>
<script src="js/ZeroClipboard/ZeroClipboard-min.js"></script>
<script>
Notebook.renderNotebooks(notebooks);
Share.renderShareNotebooks(sharedUserInfos, shareNotebooks);
Note.renderNotes(notes);
if(!isEmpty(notes)) {
Note.changeNote(notes[0].NoteId);
}
Note.setNoteCache(noteContentJson);
Note.renderNoteContent(noteContentJson)
Tag.renderTagNav(tagsJson);
</script>
<!-- mdEditor -->
<link href="/public/mdeditor/editor/editor.css" rel="stylesheet">
<script src="/public/mdeditor/editor/pagedown/Markdown.Converter-min.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Sanitizer-min.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Editor-min.js"></script>
<script src="/public/mdeditor/editor/pagedown/local/Markdown.local.zh-min.js"></script>
<script src="/public/mdeditor/editor/Markdown.Extra-min.js"></script>
<script src="/public/mdeditor/editor/underscore-min.js"></script>
<script src="/public/mdeditor/editor/scrollLink.js"></script>
<!--mathjax-->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]], processEscapes: true }, messageStyle: "none"});
</script>
<script src="/public/mdeditor/editor/mathJax-min.js"></script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script src="/public/mdeditor/editor/jquery.waitforimages-min.js"></script>
<script src="/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script src="/public/mdeditor/editor/editor.js"></script>
<!-- mdEditor end -->
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
<script src="/js/contextmenu/jquery.contextmenu.js"></script>
</body>
</html>
+953
View File
@@ -0,0 +1,953 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="life">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note">
<title>leanote, {{msg . "moto"}}</title>
<link href="css/bootstrap.css" rel="stylesheet" />
<!-- 先加载, 没有样式, 宽度不定 -->
<link rel="stylesheet" href="tinymce/skins/custom/skin.min.css" type="text/css" />
<!-- leanote css -->
<link rel="stylesheet" href="css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css" type="text/css" id="themeLink" />
<link href="css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" />
<script>
function log(o) {
}
</script>
</head>
<body>
<div id="page">
<div id="pageInner">
<!-- 头部 -->
<!-- <div id="headerContainer"> -->
<div id="header">
<!-- <div id="headerLeft" class="pull-left">
</div>
-->
<div id="logo" class="pull-left">
<span>lea</span>note
<!-- <img style="height: 45px;" src="/images/logo.png" title="leanote, 不一样的笔记" alt="leanote">
-->
</div>
<div id="switcher" class="pull-left">
<i class="fa fa-align-justify" id="leftSwitcher" title="{{msg . "leftHidden"}}"></i>
<span id="leftSwitcher2" title="{{msg . "leftShow"}}">lea</span>
</div>
<!-- search -->
<div class="pull-left">
<form class="navbar-form form-inline col-lg-2 hidden-xs" id="searchNote">
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
</form>
</div>
<!-- 全局按钮 -->
<div class="pull-left" style="line-height: 40px; margin-top: 10px;">
<!-- 新建笔记 -->
<div id="newMyNote">
<a id="newNoteBtn" title="{{msg . "newNote"}}">
<i class="fa fa-file-o"></i>
{{msg . "newNote"}}
</a>
|
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
Markdown
</a>
- <span id="curNotebookForNewNote" notebookId=""></span>
<div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle"
id="dropdownMenu2" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2" style="width: 180px;left: -5px;" id="notebookNavForNewNote">
</ul>
</div>
</div>
<!-- 只为新建别人的笔记 -->
<div id="newSharedNote" style="display: none">
<a id="newSharedNoteBtn">
<i class="fa fa-file-o"></i>
{{msg . "newNote"}}
</a>
|
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
Markdown
</a>
- <span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle"
id="dropdownMenu2" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2" style="width: 180px;left: -5px;" id="notebookNavForNewSharedNote">
</ul>
</div>
</div>
</div>
<div class="pull-left" style="line-height: 40px; margin-top: 10px; margin-left: 10px;" id="leanoteMsg">
<span id="msg" style="display: inline-block;"></span>
<img id="loading" style="visibility: hidden" src="/images/loading-a-20.gif" />
{{if eq .userInfo.Email "demo@leanote.com"}}
<span>
{{msg . "demoRegister"}}
</span>
{{end}}
</div>
<!--
<div class="pull-left alert-warning" style="line-height: 20px; margin-top: 10px; margin-left: 0px; display: none" id="verifyMsg">
您还没有验证邮箱, 验证邮件已发送至 {{.userInfo.Email}}.
<br />
<a class=".nowToActive">现在去验证</a> <a id="reSendActiveEmail">重新发送</a> <a id="wrongEmail">邮箱填错了?</a>
</div>
-->
<div class="pull-right" style="margin: 0 10px" id="myProfile">
<div class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" style="line-height: 60px;">
<!--
<img src="images/avatar.png" style="height: 40px; border: 1px solid #ccc" />
-->
<span class="username">
{{if .userInfo.UsernameRaw}}
{{.userInfo.UsernameRaw}}
{{else}}
{{.userInfo.Username}}
{{end}}
</span>
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu li-a" role="menu">
<li role="presentation" id="setInfo">
<i class="fa fa-info"></i>
{{msg . "accountSetting"}}
</li>
<li role="presentation" id="setTheme">
<i class="fa fa-sun-o"></i>
{{msg . "themeSetting"}}
</li>
<!--
<li role="presentation" id="yourSuggestions">
<i class="fa fa-comment-o"></i>
{{msg . "yourSuggestions"}}
</li>
-->
<li role="presentation" onclick="logout()">
<i class="fa fa-sign-out"></i>
{{msg . "logout"}}
</li>
</ul>
</div>
</div>
<div class="pull-right" style="margin: 0 10px" id="topNav">
<a target="_blank" href="/blog/{{.userInfo.Username}}">
{{msg . "myBlog"}}
</a>
</div>
<!--
<ul id="setting" class="pull-right">
<li class="dropdown messages">
<a class="dropdown-toggle" data-toggle="dropdown">
<span aria-hidden="true" class="icon se7en-flag"></span>
<div class="sr-only">Messages</div>
<p class="counter">3</p>
</a>
<ul class="dropdown-menu">
<li><a href="#"> c@c.com 共享了笔记给你 是否接受? </a></li>
<li><a href="#"> d@c.com 共享了笔记给你 是否接受? </a></li>
</ul>
</li>
<li class="dropdown settings">
<a class="dropdown-toggle" data-toggle="dropdown">
<span aria-hidden="true" class="icon se7en-gear"></span>
<div class="sr-only">Settings</div>
</a>
<ul class="dropdown-menu">
<li><a class="settings-link blue"><span></span>Blue</a></li>
<li><a class="settings-link green"><span></span>Green</a></li>
<li><a class="settings-link orange"><span></span>Orange</a>
</li>
<li><a class="settings-link magenta"><span></span>Magenta</a>
</li>
<li><a class="settings-link gray"><span></span>Gray</a></li>
</ul>
</li>
</ul>
-->
<div style="clear: both"></div>
</div>
<div id="mainContainer" class="clearfix">
<div id="mainMask">
<img src="/images/loading-30.gif" />
<br />
<br />
loading...
</div>
<div id="leftNotebook">
<div id="notebook">
<div class="folderNote opened" id="myNotebooks">
<div class="folderHeader clearfix">
<i class="fa fa-book pull-left fa-left"></i>
<span class="pull-left">
{{msg . "notebook"}}
</span>
<div class="pull-right" id="addNotebookPlus" title="{{msg . "addNotebook"}}">
<i class="fa fa-plus" title="{{msg . "addNotebook"}}"></i>
</div>
</div>
<ul class="folderBody" id="notebookList">
</ul>
</div>
<div class="folderNote closed" id="myTag">
<div class="folderHeader">
<i class="fa fa-bookmark fa-left"></i>
<span>
{{msg . "tag"}}
</span>
</div>
<ul class="folderBody" id="tagNav">
<li><a> <span class="label label-red">{{msg . "red"}}</span></a></li>
<li><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li>
<li><a> <span class="label label-yellow">{{msg . "yellow"}}</span></a></li>
<li><a> <span class="label label-green">{{msg . "green"}}</span></a></li>
</ul>
</div>
<div class="folderNote closed" id="">
<div class="folderHeader">
<i class="fa fa-user fa-left"></i>
<span>
共享
</span>
</div>
<ul class="folderBody" id="shareNotebooks">
</ul>
</div>
</div>
<!-- 缩小版 -->
<div id="notebookMin">
<!-- 这里隐藏, 不要 -->
<div target="#notebookList" title="{{msg . "notebook"}}" class="minContainer">
<i class="fa fa-book"></i>
<ul class="dropdown-menu" id="minNotebookList">
</ul>
</div>
<div target="#tagNav" title="{{msg . "myTag"}}" class="minContainer">
<i class="fa fa-bookmark"></i>
<ul class="dropdown-menu" id="minTagNav">
</ul>
</div>
<div id="minShareNotebooks">
<!--
<div class="minContainer" target="" title="">
<i class="fa fa-user"></i>
<ul class="dropdown-menu">
</ul>
</div>
-->
</div>
</div>
<!--
底下隐藏/展开
<div id="notebookBottom">
<i class="fa fa-angle-left" id="leftSwitcher"></i>
</div>
-->
</div>
<div class="noteSplit" id="notebookSplitter"></div>
<div id="noteAndEditor">
<div id="noteList">
<div class="clearfix" id="notesAndSort" style="position: relative">
<div class="pull-left">
<!-- 我的笔记本 -->
<div class="dropdown" id="myNotebookNavForListNav">
<a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">
<span id="curNotebookForListNote">{{msg . "all"}}</span>
<i class="fa fa-angle-down"></i>
</a>
<!-- 如果notebook过多, 则会产生滚动 -->
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu1" style="left: 5px;" id="notebookNavForListNote">
</ul>
</div>
<!-- 共享的笔记本 -->
<div class="dropdown" id="sharedNotebookNavForListNav" style="display: none">
<a class="ios7-a dropdown-toggle" id="dropdownMenu2" data-toggle="dropdown">
<span id="curSharedNotebookForListNote">{{msg . "all"}}</span>
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu2" style="left: 5px;" id="sharedNotebookNavForListNote">
</ul>
</div>
</div>
<div class="pull-left" id="tagSearch" style="line-height: 38px">
</div>
<div id="sortType">
<div class="dropdown">
<a class="ios7-a dropdown-toggle" id="dropdownMenu1"
data-toggle="dropdown">
<!--
<i class="fa fa-sort-alpha-asc">
</i>Sort <i class="fa fa-angle-down"></i>
-->
</a>
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu1"
style="right: 3px; ! important; left: -100px; min-width: 100px;">
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">创建日期</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">修改日期</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">标题</a></li>
<li role="presentation" class="divider"></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">Separated </a></li>
</ul>
</div>
</div>
</div>
<!-- 笔记列表 -->
<!-- wrap 为了slimScroll -->
<div id="noteItemListWrap" style="position: absolute; left: 0; right: 0; top: 41px; bottom: 3px">
<div id="noteItemList">
</div>
</div>
</div>
<div class="noteSplit" id="noteSplitter"></div>
<div id="note">
<!-- 只读模式 -->
<div id="noteRead">
<div id="noteReadContainer">
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix">
<div class="pull-left">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
</div>
<!-- 修改时间 -->
<div class="pull-left" style="margin-left: 10px;">
<i class="fa fa-calendar"></i>{{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
</div>
<!-- 修改时间 -->
<div class="pull-left" style="margin-left: 10px;">
<i class="fa fa-calendar"></i>{{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
</div>
<div id="noteReadContent">
</div>
</div>
</div>
<!-- 遮罩, 为了resize3Columns用 -->
<div id="noteMask"
style="position: absolute; top: 0px; bottom: 0px; right: 0; left: 10px; z-index: -1"></div>
<div id="editorMask">
该笔记本下空空如也...何不
<br />
<div id="editorMaskBtns">
<br />
<a class="note">新建笔记</a>
<a class="markdown">新建Markdown笔记</a>
</div>
<div id="editorMaskBtnsEmpty">
Sorry, 这里不能添加笔记的.
</div>
</div>
<div id="noteTop">
<!-- 左侧放tag, 右侧放按钮, 比如save, info, move, delete -->
<div id="tool" class="clearfix">
<!-- tag可以参考 mac的文件tag, 预定义一些颜色, 用户也可以自己输入-->
<!--
<div class="pull-left" id="noteTitleDiv">
<input name="noteTitle" id="noteTitle" value="" placeholder="无标题">
</div>
-->
<div class="pull-left" id="tag">
<div id="tags" style="display: inline-block; line-height: 25px;">
</div>
<div class="dropdown" style="display: inline-block"
id="tagDropdown">
<i class="fa fa-bookmark-o"></i>
<a
class="metro-a dropdown-toggle" data-toggle="dropdown"
id="addTagTrigger" style="cursor: text; padding-left: 0">
{{msg . "clickAddTag"}}
</a>
<input type="text" id="addTagInput" />
<ul class="dropdown-menu" role="menu" id="tagColor">
<li role="presentation"><span class="label label-red">{{msg . "red"}}</span>
</li>
<li role="presentation"><span class="label label-blue">{{msg . "blue"}}</span>
</li>
<li role="presentation"><span class="label label-yellow">{{msg . "yellow"}}</span></li>
<li role="presentation"><span class="label label-green">{{msg . "green"}}</span></li>
</ul>
</div>
</div>
<ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s"
data-toggle="dropdown">{{msg . "save"}}</a></li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown">{{msg . "editorTips"}}</a></li>
<li><a class="ios7-a " id="contentHistory"
data-toggle="dropdown">{{msg . "history"}}</a></li>
</ul>
</div>
<div id="noteTitleDiv">
<input name="noteTitle" id="noteTitle" value="" placeholder="{{msg . "unTitled"}}">
</div>
</div>
<div id="editor">
<!-- 编辑器 -->
<div id="mceToolbar" style="">
<div id="popularToolbar"
style="position: absolute; right: 30px; left: 0"></div>
<a
style="dispaly: block; position: absolute; cursor: pointer; right: 10px; padding: 3px 10px"
id="moreBtn"> <i class="fa fa-angle-down"></i>
</a>
</div>
<div id="leanoteNav">
<h1>
<i class="fa fa-align-justify" title="文档导航"></i>
<span>{{msg . "nav"}}</span>
</h1>
<div id="leanoteNavContent">
</div>
</div>
<!-- 由此可以算高度 -->
<div id="editorContent" name="editorContent">
{{.noteContent}}
</div>
</div>
<div id="mdEditor">
<div id="wmd-button-bar" id="mdBar"></div>
<!-- 为了scroll -->
<div class="clearfix" id="mdEditorPreview">
<div id="left-column" class="pull-left">
<div id="wmd-panel-editor" class="wmd-panel-editor">
<textarea class="wmd-input theme" id="wmd-input" spellcheck="false"></textarea>
</div>
</div>
<div id="right-column" class="pull-right">
<div id="wmd-panel-preview" class="wmd-panel-preview preview-container">
<div id="wmd-preview" class="wmd-preview"></div>
</div>
</div>
</div>
<textarea id="md-section-helper"></textarea>
</div>
<!-- mdEditor -->
<!-- Hidden Popup Modal -->
<div class="modal fade bs-modal-sm" id="editorDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="editorDialog-title">操作</h4>
</div>
<div class="modal-body">
<p></p>
<div class="input-group">
<span class="input-group-addon">
<i></i>
</span>
<input type="text" class="form-control" placeholder="">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="editorDialog-confirm">确认</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>
</div>
<!-- 弹出框 模板 -->
<div class="modal fade bs-modal-sm" id="leanoteDialogRemote" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
</div>
<div class="modal fade bs-modal-sm" id="leanoteDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="modalTitle">操作</h4>
</div>
<div class="modal-body">
内容区
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal fade bs-modal-sm" id="sendRegisterEmailDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">邀请好友</h4>
</div>
<div class="modal-body">
<form role="form">
<div class="form-group">
<div id="registerEmailMsg" class="alert alert-warning" style="display: none">
</div>
<input type="hidden" id="toEmail"/>
<label for="emailContent">邮件内容</label>
<textarea class="form-control" id="emailContent">Hi, 我是李铁, leanote非常好用, 快来注册吧.</textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" id="registerEmailBtn">发送</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal fade bs-modal-sm" id="setThemeDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">主题设置</h4>
</div>
<div class="modal-body">
<style>
#themeForm td {
padding: 5px;
text-align: center;
}
#themeForm img {
border: 1px solid #eee;
padding: 2px;
}
</style>
<table id="themeForm">
<tr>
<td>
<label>
<img src="/images/slider/all.gif" height="100px">
<br />
<input type="radio" name="theme" value="default"> {{msg . "default"}}
</label>
</td>
<td>
<label>
<img src="/images/slider/all-simple.gif" height="100px">
<br />
<input type="radio" name="theme" value="simple"> {{msg . "simple"}}
</label>
</td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- 编辑器提示 -->
<div class="modal fade bs-modal-sm" id="tipsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "editorTips"}}</h4>
</div>
<div class="modal-body">
{{msg . "editorTipsInfo"}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- 图片上传 -->
<div class="modal fade bs-modal-sm" id="imageDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content" style="height: 460px;" >
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "uploadImage"}}</h4>
</div>
<div class="modal-body">
<iframe style="" height="360" src="" scrolling="no" frameBorder="0" width="99%"></iframe>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- 您的建议
<div class="modal fade bs-modal-sm" id="suggestionsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "yourSuggestions"}}</h4>
</div>
<div class="modal-body">
<div style="margin-bottom:20px;">
{{msg . "suggestionsInfo"}}<br />
</div>
<div class="alert alert-warning" id="suggestionMsg" style="display: none"> </div>
<textarea style="width: 100%; height: 100px" id="suggestionTextarea" placeholder="{{msg . "suggestions"}}"></textarea>
</div>
<div class="modal-footer">
<button id="suggestionBtn" class="btn btn-success" >{{msg . "submit"}}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div>
</div>
</div>
-->
<!-- modal 静态区域 -->
<div class="display: hidden">
<!-- 长微博 -->
<div id="html2ImageDialog">
<div class="modal-body">
<div style="max-height: 300px; padding: 5px 0; text-align: center; overflow: scroll;" class="weibo">
<span class="text">
leanote正在努力为你生成长微博...
</span>
<br />
<img src="/images/loading-24.gif"/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "cancel"}}</button>
<button type="button" class="btn btn-success sendWeiboBtn disabled">{{msg . "send"}}</button>
</div>
</div>
<!-- 长微博 -->
<div id="reSendActiveEmailDialog">
<div class="modal-body">
<div style="max-height: 300px; padding: 5px 0; text-align: center; overflow: scroll;" class="weibo">
<span class="text">
<img src="/images/loading-24.gif"/>
正在发送邮件到{{.userInfo.Email}}...
</span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-success viewEmailBtn disabled">查看邮件</button>
</div>
</div>
<!-- 帐户设置 -->
<div id="dialogSetInfo">
<div class="modal-body">
<ul class="nav nav-tabs" id="myTabs">
<li><a href="#baseInfo" data-toggle="tab">{{msg . "basicInfo"}}</a></li>
<li><a href="#emailInfo" data-toggle="tab">{{msg . "updateEmail"}}</a></li>
<li><a href="#updatePwd" data-toggle="tab">{{msg . "updatePassword"}}</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="baseInfo">
<form>
<table>
<tr>
<td>
<label for="username">用户名设置</label>
<div class="alert alert-danger" id="usernameMsg" style="display: none"></div>
<input type="text" class="form-control" id="username">
你的邮箱是 {{.userInfo.Email}}, 可以再设置一个唯一的用户名.
<br />
用户名至少4位, 不可含特殊字符.
</td>
</tr>
<tr>
<td>
<button id="usernameBtn" class="btn btn-success">提交</button>
</td>
</tr>
</table>
</form>
</div>
<div class="tab-pane" id="emailInfo">
<form>
<table>
<tr>
<td>
当前邮箱为: <span id="curEmail">{{.userInfo.Email}}</span>
{{if .userInfo.Verified}}
已验证
{{else}}
未验证
<a class="raw nowToActive">现在去验证</a>
<a class="raw reSendActiveEmail">重新发送</a>
{{end}}
<br />
<label for="email">修改邮箱</label>
<div class="alert alert-danger" id="emailMsg" style="display: none"></div>
<input type="text" class="form-control" id="email">
邮箱修改后, 验证之后才有效, 验证之后新的邮箱地址将会作为登录帐号使用.
</td>
</tr>
<tr>
<td>
<button id="emailBtn" class="btn btn-success">发送验证邮箱</button>
</td>
</tr>
</table>
</form>
</div>
<div class="tab-pane" id="updatePwd">
<form>
<table style="width: 80%">
<tr>
<td>
<div class="alert alert-danger" id="pwdMsg" style="display: none"></div>
</td>
</tr>
<tr>
<td style="width: 80px">
<label for="pwd">{{msg . "oldPassword"}}</label>
<input type="password" class="form-control" id="oldPwd" name="oldPwd">
</td>
</tr>
<tr>
<td>
<label for="pwd">{{msg . "newPassword"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
</td>
</tr>
<tr>
<td>
<label for="pwd2">{{msg . "password2"}}</label>
<input type="password" class="form-control" id="pwd2" name="pwd2">
</td>
</tr>
<tr>
<td>
<button id="pwdBtn" class="btn btn-success" style="width: 100%">{{msg . "submit"}}</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
<!-- 账号设置, 是通过third 登录进来的 -->
<div id="thirdDialogSetInfo">
<div class="modal-body">
<ul class="nav nav-tabs" id="thirdMyTabs">
<li><a href="#accountInfo" data-toggle="tab">创建帐号</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="accountInfo">
您现在使用的是第三方帐号登录leanote, 您也可以注册leanote帐号登录, 赶紧注册一个吧.
<br />
注册成功后仍可以使用第三方帐号登录leanote并管理您现有的笔记.
<form>
<div class="alert alert-danger" id="thirdAccountMsg" style="display: none"></div>
<table style="width: 100%">
<tr>
<td>
<label for="thirdEmail">邮箱</label>
</td>
<td>
<input type="text" class="form-control" id="thirdEmail">
</td>
</tr>
<tr>
<td>
<label for="thirdPwd">密码</label>
</td>
<td>
<input type="password" class="form-control" id="thirdPwd">
</td>
<td>
密码至少6位
</td>
</tr>
<tr>
<td>
<label for="thirdPwd2">重复密码</label>
</td>
<td>
<input type="password" class="form-control" id="thirdPwd2">
</td>
</tr>
<tr>
<td></td>
<td>
<button id="accountBtn" class="btn btn-success" style="width: 100%">提交</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
<div class="hide" id="copyDiv"></div>
</div>
</div>
</div>
<script src="js/jquery-1.9.0.min.js"></script>
<script src="js/i18n/msg.{{.locale}}.js"></script>
<script src="js/common-min.js"></script>
<script>
var UrlPrefix = "http://leanote.com"; // 为了发weibo
var UserInfo = json({{.userInfoJson}});
var notebooks = json({{.notebooks}});
var shareNotebooks = json({{.shareNotebooks}});
var sharedUserInfos = json({{.sharedUserInfos}});
var notes = json({{.notes}});
var noteContentJson = json({{.noteContentJson}});
var tagsJson = json({{.tagsJson}});
</script>
<!-- 渲染view -->
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
<script src="js/app/page-min.js"></script>
<script src="tinymce/tinymce.min.js"></script>
<script src="js/jquery-cookie-min.js"></script>
<script src="js/bootstrap-min.js"></script>
<script src="js/app/note-min.js"></script>
<script src="js/app/tag-min.js"></script>
<script src="js/app/notebook-min.js"></script>
<script src="js/app/share-min.js"></script>
<script src="js/object_id-min.js"></script>
<script src="js/ZeroClipboard/ZeroClipboard-min.js"></script>
<script>
Notebook.renderNotebooks(notebooks);
Share.renderShareNotebooks(sharedUserInfos, shareNotebooks);
Note.renderNotes(notes);
if(!isEmpty(notes)) {
Note.changeNote(notes[0].NoteId);
}
Note.setNoteCache(noteContentJson);
Note.renderNoteContent(noteContentJson)
Tag.renderTagNav(tagsJson);
</script>
<!-- mdEditor -->
<link href="/public/mdeditor/editor/editor.css" rel="stylesheet">
<script src="/public/mdeditor/editor/pagedown/Markdown.Converter-min.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Sanitizer-min.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Editor-min.js"></script>
<script src="/public/mdeditor/editor/pagedown/local/Markdown.local.zh-min.js"></script>
<script src="/public/mdeditor/editor/Markdown.Extra-min.js"></script>
<script src="/public/mdeditor/editor/underscore-min.js"></script>
<script src="/public/mdeditor/editor/scrollLink-min.js"></script>
<!--mathjax-->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]], processEscapes: true }, messageStyle: "none"});
</script>
<script src="/public/mdeditor/editor/mathJax-min.js"></script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script src="/public/mdeditor/editor/jquery.waitforimages-min.js"></script>
<script src="/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script src="/public/mdeditor/editor/editor-min.js"></script>
<!-- mdEditor end -->
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
<script src="/js/contextmenu/jquery.contextmenu-min.js"></script>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
{{set . "title" "Home"}}
{{template "header.html" .}}
<header class="hero-unit" style="background-color:red">
<div class="container">
<div class="row">
<div class="hero-text">
<h1>Hello, life</h1>
<p></p>
</div>
</div>
</div>
</header>
<div class="container">
<div class="row">
<div class="span6">
{{template "flash.html" .}}
</div>
</div>
</div>
{{template "footer.html" .}}
+18
View File
@@ -0,0 +1,18 @@
{{template "home/header_box.html" .}}
<section id="box">
<div>
<h1>
leanote | we got a error
</h1>
<form class="form-inline" id="boxForm">
<p>
Sorry, we can't get your infomation.
<br />
Please <a href="/login">Sign in</a> Or <a href="/register?email={{.email}}">Sign up</a>
</p>
</form>
</div>
</section>
</body>
</html>
@@ -0,0 +1,72 @@
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="modalTitle">分享 <b>{{.title}}</b></h4>
</div>
{{$noteOrNotebookId := .noteOrNotebookId}}
<div class="modal-body">
<button class="btn btn-default" id="addShareNotebookBtn">添加分享</button>
<div id="shareMsg" class="alert alert-danger" style="display: none; margin: 5px 0 0 0;"></div>
<table class="table table-hover" id="shareNotebookTable">
<thead>
<tr>
<th>#</th>
<th>好友邮箱</th>
<th>权限</th>
<th>删除分享</th>
</tr>
</thead>
<tbody>
<tr id="tr1">
<td>#</td>
<td>
<input id="friendsEmail" type="text" class="form-control" style="width: 200px" placeholder="好友邮箱">
</td>
<td>
<label for="readPerm1"><input type="radio" name="perm1" checked="checked" value="0" id="readPerm1"> 只读</label>
<label for="writePerm1"><input type="radio" name="perm1" value="1" id="writePerm1"> 可编辑</label>
</td>
<td>
<button class="btn btn-success" onclick="addShareNoteOrNotebook(1)">分享</button>
<button class="btn btn-warning" onclick="deleteShareNoteOrNotebook(1)">删除</button>
</td>
</tr>
{{range $i, $v := .noteOrNotebookShareUserInfos}}
{{$toUserId := $v.ToUserId.Hex}}
<tr>
<td>{{add $i}}</td>
<td>{{$v.Email}}</td>
<td>
{{if eq $v.Perm 0}}
<a href="#" noteOrNotebookId="{{$noteOrNotebookId}}" perm="{{$v.Perm}}" toUserId="{{$toUserId}}" title="点击改变权限" class="btn btn-default change-perm">只读</a>
{{else}}
<a href="#" noteOrNotebookId="{{$noteOrNotebookId}}" perm="{{$v.Perm}}" toUserId="{{$toUserId}}" title="点击改变权限" class="btn btn-default change-perm">可编辑</a>
{{end}}
</td>
<td>
<a href="#" noteOrNotebookId="{{$noteOrNotebookId}}" toUserId="{{$toUserId}}" class="btn btn-warning delete-share">删除</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
Share.dialogIsNote = {{.isNote}};
Share.dialogNoteOrNotebookId = '{{.noteOrNotebookId}}';
$(function() {
setTimeout(function() {
$("#tr1 #friendsEmail").focus();
}, 500);
});
</script>
@@ -0,0 +1,84 @@
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="modalTitle">{{.note.Title}} 的分享状态</h4>
</div>
{{$noteId := .note.NoteId.Hex}}
<div class="modal-body">
{{if .noteShareUserInfos }}
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Email</th>
<th>权限</th>
<th>删除分享</th>
</tr>
</thead>
<tbody>
{{range $i, $v := .noteShareUserInfos}}
{{$toUserId := $v.ToUserId.Hex}}
<tr>
<td>{{add $i}}</td>
<td>{{$v.Email}}</td>
<td>
{{if eq $v.Perm 0}}
<a href="#" noteId="{{$noteId}}" perm="{{$v.Perm}}" toUserId="{{$toUserId}}" title="点击改变权限" class="btn btn-default change-perm">只读</a>
{{else}}
<a href="#" noteId="{{$noteId}}" perm="{{$v.Perm}}" toUserId="{{$toUserId}}" title="点击改变权限" class="btn btn-default change-perm">可编辑</a>
{{end}}
</td>
<td>
<a href="#" noteId="{{$noteId}}" toUserId="{{$toUserId}}" class="btn btn-warning delete-share">删除</a>
</td>
</tr>
{{end}}
</tbody>
</table>
{{else}}
无分享信息
{{end}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
$(function() {
$(".change-perm").click(function() {
var self = this;
var perm = $(this).attr("perm");
var noteId = $(this).attr("noteId");
var toUserId = $(this).attr("toUserId");
var toHtml = "可编辑";
var toPerm = "1";
if(perm == "1") {
toHtml = "只读";
toPerm = "0";
}
ajaxGet("/share/UpdateShareNotePerm", {noteId: noteId, perm: toPerm, toUserId: toUserId}, function(ret) {
if(ret) {
$(self).html(toHtml);
$(self).attr("perm", toPerm);
}
});
});
$(".delete-share").click(function() {
var self = this;
var noteId = $(this).attr("noteId");
var toUserId = $(this).attr("toUserId");
ajaxGet("/share/DeleteShareNote", {noteId: noteId, toUserId: toUserId}, function(ret) {
if(ret) {
$(self).parent().parent().remove();
}
});
});
});
</script>
+25
View File
@@ -0,0 +1,25 @@
{{template "header.html" .}}
<div id="postsContainer">
<div id="posts">
<form class="form-inline" id="registerForm" style="width: 400px; margin: auto;">
<h1 style="margin-top:10px">
验证邮箱 -
{{if .ok}}成功{{else}}失败{{end}}
</h1>
您的邮箱 {{.email}} 验证
{{if .ok}}成功{{else}}失败{{end}}
{{if .msg}}<br />{{.msg}}{{end}}
<br />
<a href="/">回到我的笔记</a>
</form>
</div>
</div>
{{template "footer.html"}}
<script>
</script>
</body>
</html>
+30
View File
@@ -0,0 +1,30 @@
{{template "header.html" .}}
<div id="postsContainer">
<div id="posts">
<form class="form-inline" id="registerForm" style="width: 400px; margin: auto;">
<h1 style="margin-top:10px">
验证邮箱 -
{{if .ok}}成功{{else}}失败{{end}}
</h1>
您的邮箱 {{.email}} 验证
{{if .ok}}成功{{else}}失败{{end}}
{{if .ok}}
<br />
您的新登录邮箱为 {{.email}}
{{end}}
{{if .msg}}<br />{{.msg}}{{end}}
<br />
<a href="/">回到我的笔记</a>
</form>
</div>
</div>
{{template "footer.html"}}
<script>
</script>
</body>
</html>
+76
View File
@@ -0,0 +1,76 @@
#------------------------
# leanote config
#------------------------
http.port=80
site.url=http://leanote.com
# the administrator username
adminUsername=admin
# mongdb
db.host=localhost
db.port=27017
db.dbname=leanote # required
db.username=root
db.password=root123
# or you can set the mongdb url
# mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
db.url=mongodb://root:root123@localhost:27017/leanote
# email
email.host=
email.port=25
email.username=
email.password=
#--------------------------------
# revel config
# for dev
#--------------------------------
app.name=leanote
app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y
http.addr=
http.ssl=false
cookie.httponly=false
cookie.prefix=LEANOTE
cookie.secure=false
format.date=01/02/2006
format.datetime=01/02/2006 15:04
results.chunked=false
log.trace.prefix = "TRACE "
log.info.prefix = "INFO "
log.warn.prefix = "WARN "
log.error.prefix = "ERROR "
# The default language of this application.
i18n.default_language=en
module.static=github.com/revel/revel/modules/static
[dev]
mode.dev=true
results.pretty=true
watch=true
module.testrunner = github.com/revel/revel/modules/testrunner
log.trace.output = stderr
log.info.output = stderr
log.warn.output = stderr
log.error.output = stderr
[prod]
mode.dev=false
results.pretty=false
watch=false
module.testrunner =
log.trace.output = off
log.info.output = off
log.warn.output = %(app.name)s.log
log.error.output = %(app.name)s.log
+55
View File
@@ -0,0 +1,55 @@
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
module:testrunner
# Home is My Blog
Get / Blog.Index
GET /note Note.index
# leanote home
GET /index Index.index
GET /login Auth.Login
POST /doLogin Auth.doLogin
GET /logout Auth.Logout
Get /demo Auth.Demo
Get /register Auth.Register
POST /doRegister Auth.DoRegister
GET /findPassword/:token Auth.FindPassword2
GET /findPassword Auth.FindPassword
POST /doFindPassword Auth.DoFindPassword
POST /findPasswordUpdate Auth.FindPasswordUpdate
# mobile
GET /mobile Mobile.Index
# blog
# /blog/view/:noteId必须要放在/blog/:userId...之前
GET /blog/searchBlog/:userId Blog.SearchBlog
GET /blog/set Blog.Set
GET /blog/aboutMe/:userId Blog.AboutMe
POST /blog/setUserBlogBase Blog.SetUserBlogBase
POST /blog/setUserBlogComment Blog.SetUserBlogComment
POST /blog/setUserBlogStyle Blog.SetUserBlogStyle
GET /blog/view/:noteId Blog.View
GET /blog/:userId Blog.Index
GET /blog/:userId/:notebookId Blog.Index
GET /blog Blog.Index
# Map static resources from the /app/public folder to the /public path
GET /favicon.ico Static.Serve("public/images/favicon.ico")
GET /public/*filepath Static.Serve("public")
GET /js/*filepath Static.Serve("public/js")
GET /images/*filepath Static.Serve("public/images")
GET /img/*filepath Static.Serve("public/img")
GET /css/*filepath Static.Serve("public/css")
GET /fonts/*filepath Static.Serve("public/fonts")
GET /tinymce/*filepath Static.Serve("public/tinymce")
#GET /plugins/*filepath Static.Serve("public/tinymce/plugins")
GET /upload/*filepath Static.Serve("public/upload")
# common
* /:controller/:action :controller.:action
+120
View File
@@ -0,0 +1,120 @@
# leanote
moto=Not just a note!
# 首页
forgetPassword = Forget password?
or=or
try=Try it
3th=Third-party accounts
usernameOrEmail=Username or email
password=Password
home=Home
aboutLeanote=About leanote
suggestions=Suggestions
yourSuggestions=Suggestions
leanoteBlog=Blog
knowledge=Knowledge
knowledgeInfo=Use leanote as a note, manage your knowledge in leanote.
share=Share
shareInfo=Share your knowledge to your friends in leanote.
cooperation=Cooperation
cooperationInfo=Collaborate with friends to improve your knowledge.
blog=Blog
blogInfo=You can public your knowledge and leanote is your blog!
suggestionsInfo=help us to improve our service.
yourContact=Your contact
emailOrOthers=Email or other contact way
hi=Hi
welcomeUseLeanote=Welcome!
myNote=Enter to my note
curUser=Email
# form
submit=submit
register=Sign up
login=Sign in
password2=Confirm your password
email=Email
inputUsername=Username(email) is required
inputEmail=Email is required
wrongEmail=Wrong email
wrongUsernameOrPassword=Wrong username or password
inputPassword=Password is required
wrongPassword=Wrong password
logining=Sign in
loginSuccess=login success
ing=processing
# 注册
registerSuccessAndRdirectToNote=register success, now redirect to my note...
# 找回密码
passwordTips=The length is at least 6
findPassword=Find password
findPasswordSendEmailOver=We have already send the find password link to your email, please check out your email
checkEmai=Check email
findPasswordTimeout=time out
reFindPassword=find password again
updatePassword=Update password
updatePasswordSuccessRedirectToLogin=update password success and redirect to login page...
inputPassword2=Please input your password again
confirmPassword=Please confirm your password
notGoodPassword=Tt's not a good password, the length is at least 6
# 笔记主页
myBlog=Blog
history=Histories
save=Save
editorTips=Editor Tips
editorTipsInfo=<h4>1. Short Cuts</h4>ctrl+shift+c Toggle code <br /> ctrl+shift+i Insert/edit image <h4>2. shift+enter Get out of current block</h4> eg. <img src="/images/outofcode.png" style="width: 90px"/> in this situation you can use shift+enter to get out of current code block.
newNote=New Note
newMarkdown=New Markdown Note
clickAddTag=Click to add Tag
notebook=Notebook
myNotebook=My Notebook
addNotebook=Add Notebook
all=Newest
trash=Trash
unTitled=UnTitled
defaultShare=Default sharing
leftHidden=Hidden slide bar
leftShow=Show slide bar
nav=Note Nav
update=Update
create=Create
demoRegister=您现在使用的是体验帐号, <a href="/register">立即注册?</a>
close=Close
cancel=Cancel
send=Send
# 标签
tag=Tag
myTag=My Tag
red=red
yellow=yellow
blue=blue
green=green
# 设置
accountSetting=Account
themeSetting=Theme
logout=Logout
basicInfo=Basic
updateEmail=Update Email
usernameSetting=Update Username
oldPassword=Old Password
newPassword=New Password
default=Default
simple=Simple
# tinymce
uploadImage=Upload Image
# error
notFound=This page cann't found.
a=a
+123
View File
@@ -0,0 +1,123 @@
# leanote
moto=不一样的笔记!
# 首页
forgetPassword = 忘记密码?
or=或
try=体验一下
3th=第三方登录
usernameOrEmail=用户名或Email
password=密码
home=主页
aboutLeanote=关于leanote
suggestions=建议
yourSuggestions=帮助完善leanote
leanoteBlog=官方博客
knowledge=知识
knowledgeInfo=leanote是一个笔记, 你可以用它来管理自己的知识.
share=分享
shareInfo=你也可以将知识分享给你的好友.
cooperation=协作
cooperationInfo=分享给好友的同时也可以让你的好友和你一起来完善它.
blog=博客
blogInfo=将笔记公开, 让知识传播的更远!
suggestionsInfo=帮助我们完善leanote
yourContact=您的联系方式
emailOrOthers=Email或其它联系方式
hi=Hi
welcomeUseLeanote=欢迎使用leanote
myNote=进入我的笔记
curUser=当前登录帐户
# form
submit=提交
login=登录
register=注册
password2=确认密码
email=Email
inputUsername=请输入用户名或Email
inputEmail=请输入Email
wrongEmail=Email格式有误
wrongUsernameOrPassword=用户名或密码有误
inputPassword=请输入密码
wrongPassword=密码有误
logining=正在登录
loginSuccess=登录成功, 正在跳转
ing=正在处理
# 注册
registerSuccessAndRdirectToNote=注册成功, 正在转至我的笔记...
# 找回密码
passwordTips=密码至少6位
findPassword=找回密码
findPasswordSendEmailOver=已经将修改密码的链接发送到您的邮箱, 请查收邮件.
checkEmai=查收邮箱
updatePassword=修改密码
findPasswordTimeout=链接已过期
reFindPassword=重新找回密码
updatePassword=修改密码
updatePasswordSuccessRedirectToLogin=修改成功, 正在跳转到登录页
inputPassword2=请再次输入密码
confirmPassword=两次密码输入不一致
notGoodPassword=密码至少6位
# 笔记主页
myBlog=我的博客
history=历史记录
save=保存
editorTips=帮助
editorTipsInfo=<h4>1. 快捷键</h4>ctrl+shift+c 代码块切换 <br /> ctrl+shift+i 插入/修改图片<h4>2. shift+enter 跳出当前区域</h4>比如在代码块中<img src="/images/outofcode.png" style="width: 90px"/>按shift+enter可跳出当前代码块.
newNote=新建笔记
newMarkdown=新建Markdown笔记
clickAddTag=点击添加标签
notebook=笔记本
myNotebook=我的笔记本
addNotebook=添加笔记本
all=最新
trash=废纸篓
unTitled=无标题
defaultShare=默认共享
leftHidden=隐藏左侧
leftShow=展开左侧
nav=文档导航
update=更新
create=创建
demoRegister=您现在使用的是体验帐号, <a href="/register">立即注册?</a>
close=关闭
cancel=取消
send=发送
# 标签
tag=标签
myTag=我的标签
red=红色
yellow=黄色
blue=蓝色
green=绿色
# 设置
accountSetting=帐户设置
themeSetting=主题设置
logout=退出
basicInfo=基本信息
updateEmail=修改Email
usernameSetting=用户名设置
oldPassword=旧密码
newPassword=新密码
default=默认
simple=简约
# tinymce
uploadImage=上传图片
# error
notFound=该页面不存在
# 必须要加这个, 奇怪
[CN]
Loaded 100 of 1085 files, more files were not shown because too many files have changed in this diff. Show more