v1.0 beta init
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "strconv"
|
||||
)
|
||||
|
||||
// 用户登录/注销/找回密码
|
||||
@@ -14,48 +15,88 @@ type Auth struct {
|
||||
|
||||
//--------
|
||||
// 登录
|
||||
func (c Auth) Login(email string) revel.Result {
|
||||
func (c Auth) Login(email, from string) revel.Result {
|
||||
c.RenderArgs["title"] = c.Message("login")
|
||||
c.RenderArgs["subTitle"] = c.Message("login")
|
||||
c.RenderArgs["email"] = email
|
||||
c.RenderArgs["from"] = from
|
||||
c.RenderArgs["openRegister"] = openRegister
|
||||
|
||||
sessionId := c.Session.Id()
|
||||
if sessionService.LoginTimesIsOver(sessionId) {
|
||||
c.RenderArgs["needCaptcha"] = true
|
||||
}
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
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 {
|
||||
|
||||
// 为了demo和register
|
||||
func (c Auth) doLogin(email, pwd string) revel.Result {
|
||||
sessionId := c.Session.Id()
|
||||
var msg = ""
|
||||
|
||||
userInfo := authService.Login(email, pwd)
|
||||
if userInfo.Email != "" {
|
||||
c.SetSession(userInfo)
|
||||
// 必须要redirect, 不然用户刷新会重复提交登录信息
|
||||
// return c.Redirect("/")
|
||||
configService.InitUserConfigs(userInfo.UserId.Hex())
|
||||
sessionService.ClearLoginTimes(sessionId)
|
||||
return c.RenderJson(info.Re{Ok: true})
|
||||
} else {
|
||||
// 登录错误, 则错误次数++
|
||||
msg = "wrongUsernameOrPassword"
|
||||
}
|
||||
// return c.RenderTemplate("login.html")
|
||||
return c.RenderJson(info.Re{Ok: false, Msg: c.Message("wrongUsernameOrPassword")})
|
||||
|
||||
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId) , Msg: c.Message(msg)})
|
||||
}
|
||||
func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result {
|
||||
sessionId := c.Session.Id()
|
||||
var msg = ""
|
||||
|
||||
// > 5次需要验证码, 直到登录成功
|
||||
if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha {
|
||||
msg = "captchaError"
|
||||
} else {
|
||||
userInfo := authService.Login(email, pwd)
|
||||
if userInfo.Email != "" {
|
||||
c.SetSession(userInfo)
|
||||
sessionService.ClearLoginTimes(sessionId)
|
||||
return c.RenderJson(info.Re{Ok: true})
|
||||
} else {
|
||||
// 登录错误, 则错误次数++
|
||||
msg = "wrongUsernameOrPassword"
|
||||
sessionService.IncrLoginTimes(sessionId)
|
||||
}
|
||||
}
|
||||
|
||||
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId) , Msg: c.Message(msg)})
|
||||
}
|
||||
// 注销
|
||||
func (c Auth) Logout() revel.Result {
|
||||
sessionId := c.Session.Id()
|
||||
sessionService.Clear(sessionId)
|
||||
c.ClearSession()
|
||||
return c.Redirect("/login")
|
||||
}
|
||||
|
||||
// 体验一下
|
||||
func (c Auth) Demo() revel.Result {
|
||||
c.DoLogin("demo@leanote.com", "demo@leanote.com")
|
||||
c.doLogin(configService.GetGlobalStringConfig("demoUsername"), configService.GetGlobalStringConfig("demoPassword"))
|
||||
return c.Redirect("/note")
|
||||
}
|
||||
|
||||
//--------
|
||||
// 注册
|
||||
func (c Auth) Register() revel.Result {
|
||||
func (c Auth) Register(from string) revel.Result {
|
||||
if !openRegister {
|
||||
return c.Redirect("/index")
|
||||
}
|
||||
c.SetLocale()
|
||||
c.RenderArgs["from"] = from
|
||||
|
||||
c.RenderArgs["title"] = c.Message("register")
|
||||
c.RenderArgs["subTitle"] = c.Message("register")
|
||||
@@ -68,21 +109,11 @@ 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 re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderRe(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)
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
// 注册
|
||||
@@ -90,10 +121,10 @@ func (c Auth) DoRegister(email, pwd string) revel.Result {
|
||||
|
||||
// 注册成功, 则立即登录之
|
||||
if re.Ok {
|
||||
c.DoLogin(email, pwd)
|
||||
c.doLogin(email, pwd)
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
//--------
|
||||
@@ -130,13 +161,12 @@ func (c Auth) FindPassword2(token string) revel.Result {
|
||||
// 找回密码修改密码
|
||||
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)
|
||||
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
// 修改之
|
||||
re.Ok, re.Msg = pwdService.UpdatePwd(token, pwd)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// 公用Controller, 其它Controller继承它
|
||||
@@ -54,15 +56,21 @@ func (c BaseController) GetUsername() string {
|
||||
// 得到用户信息
|
||||
func (c BaseController) GetUserInfo() info.User {
|
||||
if userId, ok := c.Session["UserId"]; ok && userId != "" {
|
||||
return userService.GetUserInfo(userId);
|
||||
/*
|
||||
notebookWidth, _ := strconv.Atoi(c.Session["NotebookWidth"])
|
||||
noteListWidth, _ := strconv.Atoi(c.Session["NoteListWidth"])
|
||||
mdEditorWidth, _ := strconv.Atoi(c.Session["MdEditorWidth"])
|
||||
LogJ(c.Session)
|
||||
user := info.User{UserId: bson.ObjectIdHex(userId),
|
||||
Email: c.Session["Email"],
|
||||
Logo: c.Session["Logo"],
|
||||
Username: c.Session["Username"],
|
||||
UsernameRaw: c.Session["UsernameRaw"],
|
||||
Theme: c.Session["Theme"],
|
||||
NotebookWidth: notebookWidth,
|
||||
NoteListWidth: noteListWidth,
|
||||
MdEditorWidth: mdEditorWidth,
|
||||
}
|
||||
if c.Session["Verified"] == "1" {
|
||||
user.Verified = true
|
||||
@@ -71,10 +79,19 @@ func (c BaseController) GetUserInfo() info.User {
|
||||
user.LeftIsMin = true
|
||||
}
|
||||
return user
|
||||
*/
|
||||
}
|
||||
return info.User{}
|
||||
}
|
||||
|
||||
// 这里的session都是cookie中的, 与数据库session无关
|
||||
func (c BaseController) GetSession(key string) string {
|
||||
v, ok := c.Session[key]
|
||||
if !ok {
|
||||
v = ""
|
||||
}
|
||||
return v
|
||||
}
|
||||
func (c BaseController) SetSession(userInfo info.User) {
|
||||
if userInfo.UserId.Hex() != "" {
|
||||
c.Session["UserId"] = userInfo.UserId.Hex()
|
||||
@@ -82,6 +99,7 @@ func (c BaseController) SetSession(userInfo info.User) {
|
||||
c.Session["Username"] = userInfo.Username
|
||||
c.Session["UsernameRaw"] = userInfo.UsernameRaw
|
||||
c.Session["Theme"] = userInfo.Theme
|
||||
c.Session["Logo"] = userInfo.Logo
|
||||
|
||||
c.Session["NotebookWidth"] = strconv.Itoa(userInfo.NotebookWidth)
|
||||
c.Session["NoteListWidth"] = strconv.Itoa(userInfo.NoteListWidth)
|
||||
@@ -165,6 +183,12 @@ func (c BaseController) SetLocale() string {
|
||||
lang = "en";
|
||||
}
|
||||
c.RenderArgs["locale"] = lang;
|
||||
c.RenderArgs["siteUrl"] = siteUrl;
|
||||
|
||||
c.RenderArgs["blogUrl"] = configService.GetBlogUrl()
|
||||
c.RenderArgs["leaUrl"] = configService.GetLeaUrl()
|
||||
c.RenderArgs["noteUrl"] = configService.GetNoteUrl()
|
||||
|
||||
return lang
|
||||
}
|
||||
|
||||
@@ -172,4 +196,47 @@ func (c BaseController) SetLocale() string {
|
||||
func (c BaseController) SetUserInfo() {
|
||||
userInfo := c.GetUserInfo()
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
}
|
||||
|
||||
// life
|
||||
// 返回解析后的字符串, 只是为了解析模板得到字符串
|
||||
func (c BaseController) RenderTemplateStr(templatePath string) string {
|
||||
// Get the Template.
|
||||
// 返回 GoTemplate{tmpl, loader}
|
||||
template, err := revel.MainTemplateLoader.Template(templatePath)
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
tpl := &revel.RenderTemplateResult{
|
||||
Template: template,
|
||||
RenderArgs: c.RenderArgs, // 把args给它
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
tpl.Template.Render(&buffer, c.RenderArgs)
|
||||
return buffer.String();
|
||||
}
|
||||
|
||||
// json, result
|
||||
// 为了msg
|
||||
// msg-v1-v2-v3
|
||||
func (c BaseController) RenderRe(re info.Re) revel.Result {
|
||||
if re.Msg != "" {
|
||||
if(strings.Contains(re.Msg, "-")) {
|
||||
msgAndValues := strings.Split(re.Msg, "-")
|
||||
if len(msgAndValues) == 2 {
|
||||
re.Msg = c.Message(msgAndValues[0], msgAndValues[1])
|
||||
} else {
|
||||
others := msgAndValues[0:]
|
||||
a := make([]interface{}, len(others))
|
||||
for i, v := range others {
|
||||
a[i] = v
|
||||
}
|
||||
re.Msg = c.Message(msgAndValues[0], a...)
|
||||
}
|
||||
} else {
|
||||
re.Msg = c.Message(re.Msg)
|
||||
}
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
@@ -17,142 +19,283 @@ 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)
|
||||
}
|
||||
|
||||
//-----------------------------
|
||||
// 前台
|
||||
|
||||
// 域名, 没用
|
||||
func (c Blog) domain() (ok bool, userBlog info.UserBlog) {
|
||||
return
|
||||
}
|
||||
|
||||
// 各种地址设置
|
||||
func (c Blog) setUrl(userBlog info.UserBlog, userInfo info.User) {
|
||||
// 主页 http://leanote.com/blog/life or http://blog.leanote.com/life or http:// xxxx.leanote.com or aa.com
|
||||
var indexUrl, viewUrl, searchUrl, cateUrl, aboutMeUrl, staticUrl string
|
||||
host := c.Request.Request.Host
|
||||
staticUrl = configService.GetUserUrl(strings.Split(host, ":")[0])
|
||||
// staticUrl == host, 为保证同源!!! 只有host, http://leanote.com, http://blog/leanote.com
|
||||
// life.leanote.com, lealife.com
|
||||
if userBlog.Domain != "" && configService.AllowCustomDomain() {
|
||||
// ok
|
||||
indexUrl = configService.GetUserUrl(userBlog.Domain)
|
||||
cateUrl = indexUrl + "/cate" // /xxxxx
|
||||
viewUrl = indexUrl + "/view" // /xxxxx
|
||||
searchUrl = indexUrl + "/search" // /xxxxx
|
||||
aboutMeUrl = indexUrl + "/aboutMe"
|
||||
} else if userBlog.SubDomain != "" {
|
||||
indexUrl = configService.GetUserSubUrl(userBlog.SubDomain)
|
||||
cateUrl = indexUrl + "/cate" // /xxxxx
|
||||
viewUrl = indexUrl + "/view" // /xxxxx
|
||||
searchUrl = indexUrl + "/search" // /xxxxx
|
||||
aboutMeUrl = indexUrl + "/aboutMe"
|
||||
} else {
|
||||
// ok
|
||||
blogUrl := configService.GetBlogUrl()
|
||||
userIdOrEmail := ""
|
||||
if userInfo.Username != "" {
|
||||
userIdOrEmail = userInfo.Username
|
||||
} else if userInfo.Email != "" {
|
||||
userIdOrEmail = userInfo.Email
|
||||
} else {
|
||||
userIdOrEmail = userInfo.UserId.Hex()
|
||||
}
|
||||
indexUrl = blogUrl + "/" + userIdOrEmail
|
||||
cateUrl = blogUrl + "/cate" // /notebookId
|
||||
viewUrl = blogUrl + "/view" // /xxxxx
|
||||
searchUrl = blogUrl + "/search/" + userIdOrEmail // /xxxxx
|
||||
aboutMeUrl = blogUrl + "/aboutMe/" + userIdOrEmail
|
||||
}
|
||||
|
||||
// 分类
|
||||
// 搜索
|
||||
// 查看
|
||||
c.RenderArgs["indexUrl"] = indexUrl
|
||||
c.RenderArgs["cateUrl"] = cateUrl
|
||||
c.RenderArgs["viewUrl"] = viewUrl
|
||||
c.RenderArgs["searchUrl"] = searchUrl
|
||||
c.RenderArgs["aboutMeUrl"] = aboutMeUrl
|
||||
c.RenderArgs["staticUrl"] = staticUrl
|
||||
}
|
||||
|
||||
// 公共
|
||||
func (c Blog) blogCommon(userId string, userBlog info.UserBlog, userInfo info.User) (ok bool) {
|
||||
if userInfo.UserId == "" {
|
||||
userInfo = userService.GetUserInfoByAny(userId)
|
||||
if userInfo.UserId == "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
|
||||
// 分类导航
|
||||
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
|
||||
// 最新笔记
|
||||
c.getRecentBlogs(userId)
|
||||
// 语言, url地址
|
||||
c.SetLocale();
|
||||
c.RenderArgs["isMe"] = userId == c.GetUserId()
|
||||
|
||||
// 得到博客设置信息
|
||||
if userBlog.UserId == "" {
|
||||
userBlog = blogService.GetUserBlog(userId)
|
||||
}
|
||||
c.RenderArgs["userBlog"] = userBlog
|
||||
|
||||
c.setUrl(userBlog, userInfo)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 跨域判断是否是我的博客
|
||||
func (c Blog) IsMe(userId string) revel.Result {
|
||||
var js = ""
|
||||
if c.GetUserId() == userId {
|
||||
js = "$('.is-me').removeClass('hide');"
|
||||
}
|
||||
return c.RenderText(js);
|
||||
}
|
||||
|
||||
// 进入某个用户的博客
|
||||
var blogPageSize = 5
|
||||
var searchBlogPageSize = 30
|
||||
func (c Blog) Index(userId string, notebookId string) revel.Result {
|
||||
// 用户id为空, 转至博客平台
|
||||
if userId == "" {
|
||||
userId = leanoteUserId;
|
||||
|
||||
// 分类 /cate/xxxxxxxx?notebookId=1212
|
||||
func (c Blog) Cate(notebookId string) revel.Result {
|
||||
if notebookId == "" {
|
||||
return c.E404()
|
||||
}
|
||||
// 自定义域名
|
||||
hasDomain, userBlog := c.domain()
|
||||
userId := ""
|
||||
if hasDomain {
|
||||
userId = userBlog.UserId.Hex()
|
||||
}
|
||||
|
||||
// userId可能是 username, email
|
||||
userInfo := userService.GetUserInfoByAny(userId)
|
||||
if userInfo.UserId == "" {
|
||||
var notebook info.Notebook
|
||||
notebook = notebookService.GetNotebookById(notebookId)
|
||||
if !notebook.IsBlog {
|
||||
return c.E404()
|
||||
}
|
||||
if userId != "" && userId != notebook.UserId.Hex() {
|
||||
return c.E404()
|
||||
}
|
||||
userId = notebook.UserId.Hex()
|
||||
|
||||
if !c.blogCommon(userId, userBlog, info.User{}) {
|
||||
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)
|
||||
count, blogs := blogService.ListBlogs(userId, notebookId, page, blogPageSize, "PublicTime", false)
|
||||
|
||||
c.RenderArgs["notebookId"] = notebookId
|
||||
c.RenderArgs["notebook"] = notebook
|
||||
c.RenderArgs["title"] = c.Message("blogClass") + " - " + notebook.Title
|
||||
c.RenderArgs["blogs"] = blogs
|
||||
c.RenderArgs["page"] = page
|
||||
c.RenderArgs["pageSize"] = blogPageSize
|
||||
c.RenderArgs["count"] = count
|
||||
|
||||
return c.RenderTemplate("blog/index.html")
|
||||
}
|
||||
|
||||
// 显示分类的最近博客, json
|
||||
func (c Blog) ListCateLatest(notebookId string) revel.Result {
|
||||
if notebookId == "" {
|
||||
return c.E404()
|
||||
}
|
||||
// 自定义域名
|
||||
hasDomain, userBlog := c.domain()
|
||||
userId := ""
|
||||
if hasDomain {
|
||||
userId = userBlog.UserId.Hex()
|
||||
}
|
||||
|
||||
var notebook info.Notebook
|
||||
notebook = notebookService.GetNotebookById(notebookId)
|
||||
if !notebook.IsBlog {
|
||||
return c.E404()
|
||||
}
|
||||
if userId != "" && userId != notebook.UserId.Hex() {
|
||||
return c.E404()
|
||||
}
|
||||
userId = notebook.UserId.Hex()
|
||||
|
||||
if !c.blogCommon(userId, userBlog, info.User{}) {
|
||||
return c.E404()
|
||||
}
|
||||
|
||||
// 分页的话, 需要分页信息, totalPage, curPage
|
||||
page := 1
|
||||
_, blogs := blogService.ListBlogs(userId, notebookId, page, 5, "PublicTime", false)
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.List = blogs
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c Blog) Index(userIdOrEmail string) revel.Result {
|
||||
// 自定义域名
|
||||
hasDomain, userBlog := c.domain()
|
||||
userId := ""
|
||||
if hasDomain {
|
||||
userId = userBlog.UserId.Hex()
|
||||
}
|
||||
|
||||
// 用户id为空, 转至博客平台
|
||||
if userIdOrEmail == "" {
|
||||
userIdOrEmail = leanoteUserId;
|
||||
}
|
||||
var userInfo info.User
|
||||
if userId != "" {
|
||||
userInfo = userService.GetUserInfoByAny(userId)
|
||||
} else {
|
||||
userInfo = userService.GetUserInfoByAny(userIdOrEmail)
|
||||
}
|
||||
userId = userInfo.UserId.Hex()
|
||||
|
||||
if !c.blogCommon(userId, userBlog, userInfo) {
|
||||
return c.E404()
|
||||
}
|
||||
|
||||
// 分页的话, 需要分页信息, totalPage, curPage
|
||||
page := c.GetPage()
|
||||
count, blogs := blogService.ListBlogs(userId, "", page, blogPageSize, "PublicTime", 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)
|
||||
c.RenderArgs["index"] = true
|
||||
c.RenderArgs["notebookId"] = ""
|
||||
c.RenderArgs["title"] = userBlog.Title
|
||||
|
||||
return c.RenderTemplate("blog/index.html")
|
||||
}
|
||||
|
||||
// 详情
|
||||
func (c Blog) View(noteId string) revel.Result {
|
||||
// 自定义域名
|
||||
hasDomain, userBlog := c.domain()
|
||||
userId := ""
|
||||
if hasDomain {
|
||||
userId = userBlog.UserId.Hex()
|
||||
}
|
||||
|
||||
blog := blogService.GetBlog(noteId)
|
||||
c.RenderArgs["blog"] = blog
|
||||
|
||||
userInfo := userService.GetUserInfo(blog.UserId.Hex())
|
||||
if userId != "" && userInfo.UserId.Hex() != userId {
|
||||
return c.E404()
|
||||
}
|
||||
c.RenderArgs["blog"] = blog
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.RenderArgs["title"] = blog.Title + " - " + userInfo.Username
|
||||
|
||||
c.RenderArgs["title"] = blog.Title + " - " + userInfo.Email
|
||||
userId = userInfo.UserId.Hex()
|
||||
c.blogCommon(userId, userBlog, info.User{})
|
||||
|
||||
userId := userInfo.UserId.Hex()
|
||||
c.isMe(userId)
|
||||
|
||||
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
|
||||
|
||||
// 得到博客设置信息
|
||||
c.RenderArgs["userBlog"] = blogService.GetUserBlog(userId)
|
||||
|
||||
c.getRecentBlogs(userId)
|
||||
// 得到访问者id
|
||||
visitUserId := c.GetUserId()
|
||||
if(visitUserId != "") {
|
||||
visitUserInfo := userService.GetUserInfo(visitUserId)
|
||||
c.RenderArgs["visitUserInfoJson"] = c.Json(visitUserInfo)
|
||||
c.RenderArgs["visitUserInfo"] = visitUserInfo
|
||||
} else {
|
||||
c.RenderArgs["visitUserInfoJson"] = "{}";
|
||||
}
|
||||
|
||||
return c.RenderTemplate("blog/view.html")
|
||||
}
|
||||
|
||||
// 搜索
|
||||
func (c Blog) SearchBlog(userId, key string) revel.Result {
|
||||
c.RenderArgs["title"] = "搜索 " + key
|
||||
func (c Blog) Search(userIdOrEmail, key string) revel.Result {
|
||||
// 自定义域名
|
||||
hasDomain, userBlog := c.domain()
|
||||
userId := ""
|
||||
if hasDomain {
|
||||
userId = userBlog.UserId.Hex()
|
||||
}
|
||||
|
||||
c.RenderArgs["title"] = c.Message("search") + " - " + key
|
||||
c.RenderArgs["key"] = key
|
||||
|
||||
userInfo := userService.GetUserInfoByAny(userId)
|
||||
var userInfo info.User
|
||||
if userId != "" {
|
||||
userInfo = userService.GetUserInfoByAny(userId)
|
||||
} else {
|
||||
userInfo = userService.GetUserInfoByAny(userIdOrEmail)
|
||||
}
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
|
||||
userId = userInfo.UserId.Hex()
|
||||
c.blogCommon(userId, userBlog, userInfo)
|
||||
|
||||
page := c.GetPage()
|
||||
_, blogs := blogService.SearchBlog(key, userId, page, searchBlogPageSize, "UpdatedTime", false)
|
||||
_, blogs := blogService.SearchBlog(key, userId, page, searchBlogPageSize, "PublicTime", 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")
|
||||
}
|
||||
|
||||
@@ -162,17 +305,15 @@ func (c Blog) Set() revel.Result {
|
||||
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["title"] = c.Message("blogSet")
|
||||
c.RenderArgs["isMe"] = true
|
||||
c.RenderArgs["set"] = true
|
||||
|
||||
c.getRecentBlogs(userId)
|
||||
c.RenderArgs["allowCustomDomain"] = configService.GetGlobalStringConfig("allowCustomDomain")
|
||||
|
||||
c.SetLocale();
|
||||
userBlog := blogService.GetUserBlog(userId)
|
||||
c.blogCommon(userId, userBlog, info.User{})
|
||||
|
||||
return c.RenderTemplate("blog/set.html")
|
||||
}
|
||||
@@ -194,37 +335,38 @@ func (c Blog) SetUserBlogStyle(userBlog info.UserBlogStyle) revel.Result {
|
||||
}
|
||||
|
||||
// userId可能是其它的
|
||||
func (c Blog) AboutMe(userId string) revel.Result {
|
||||
userInfo := userService.GetUserInfoByAny(userId)
|
||||
func (c Blog) AboutMe(userIdOrEmail string) revel.Result {
|
||||
// 自定义域名
|
||||
hasDomain, userBlog := c.domain()
|
||||
userId := ""
|
||||
if hasDomain {
|
||||
userId = userBlog.UserId.Hex()
|
||||
}
|
||||
|
||||
var userInfo info.User
|
||||
if userId != "" {
|
||||
userInfo = userService.GetUserInfoByAny(userId)
|
||||
} else {
|
||||
userInfo = userService.GetUserInfoByAny(userIdOrEmail)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
c.RenderArgs["title"] = c.Message("aboutMe")
|
||||
c.blogCommon(userId, userBlog, info.User{})
|
||||
|
||||
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)
|
||||
_, c.RenderArgs["recentBlogs"] = blogService.ListBlogs(userId, "", 1, 5, "PublicTime", false)
|
||||
}
|
||||
|
||||
// 可以不要, 因为注册的时候已经把username设为email了
|
||||
@@ -233,4 +375,130 @@ func (c Blog) setRenderUserInfo(userInfo info.User) {
|
||||
userInfo.Username = userInfo.Email
|
||||
}
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// 后台 note<->blog
|
||||
|
||||
// 设置/取消Blog; 置顶
|
||||
func (c Blog) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
|
||||
noteUpdate := bson.M{}
|
||||
if isTop {
|
||||
isBlog = true
|
||||
}
|
||||
if !isBlog {
|
||||
isTop = false
|
||||
}
|
||||
noteUpdate["IsBlog"] = isBlog
|
||||
noteUpdate["IsTop"] = isTop
|
||||
if isBlog {
|
||||
noteUpdate["PublicTime"] = time.Now()
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
//----------------
|
||||
// 社交, 点赞, 评论
|
||||
|
||||
// 我是否点过赞?
|
||||
// 所有点赞的用户列表
|
||||
// 各个评论中是否我也点过赞?
|
||||
func (c Blog) GetLike(noteId string) revel.Result {
|
||||
userId := c.GetUserId()
|
||||
|
||||
// 我也点过?
|
||||
isILikeIt := blogService.IsILikeIt(noteId, userId)
|
||||
// 点赞用户列表
|
||||
likedUsers, hasMoreLikedUser := blogService.ListLikedUsers(noteId, false)
|
||||
|
||||
result := map[string]interface{}{}
|
||||
result["isILikeIt"] = isILikeIt
|
||||
result["likedUsers"] = likedUsers
|
||||
result["hasMoreLikedUser"] = hasMoreLikedUser
|
||||
|
||||
return c.RenderJson(result)
|
||||
}
|
||||
func (c Blog) GetLikeAndComments(noteId string) revel.Result {
|
||||
userId := c.GetUserId()
|
||||
|
||||
// 我也点过?
|
||||
isILikeIt := blogService.IsILikeIt(noteId, userId)
|
||||
// 点赞用户列表
|
||||
likedUsers, hasMoreLikedUser := blogService.ListLikedUsers(noteId, false)
|
||||
// 评论
|
||||
page := c.GetPage()
|
||||
pageInfo, comments, commentUserInfo := blogService.ListComments(userId, noteId, page, 15)
|
||||
|
||||
result := map[string]interface{}{}
|
||||
result["isILikeIt"] = isILikeIt
|
||||
result["likedUsers"] = likedUsers
|
||||
result["hasMoreLikedUser"] = hasMoreLikedUser
|
||||
result["pageInfo"] = pageInfo
|
||||
result["comments"] = comments
|
||||
result["commentUserInfo"] = commentUserInfo
|
||||
|
||||
return c.RenderJson(result)
|
||||
}
|
||||
|
||||
func (c Blog) IncReadNum(noteId string) revel.Result {
|
||||
blogService.IncReadNum(noteId)
|
||||
return nil
|
||||
}
|
||||
// 点赞
|
||||
func (c Blog) LikeBlog(noteId string) revel.Result {
|
||||
userId := c.GetUserId()
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Item = blogService.LikeBlog(noteId, userId)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c Blog) ListLikes(noteId string) revel.Result {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Blog) ListComments(noteId string) revel.Result {
|
||||
// 评论
|
||||
userId := c.GetUserId()
|
||||
page := c.GetPage()
|
||||
pageInfo, comments, commentUserInfo := blogService.ListComments(userId, noteId, page, 15)
|
||||
|
||||
result := map[string]interface{}{}
|
||||
result["pageInfo"] = pageInfo
|
||||
result["comments"] = comments
|
||||
result["commentUserInfo"] = commentUserInfo
|
||||
|
||||
return c.RenderJson(result)
|
||||
}
|
||||
func (c Blog) DeleteComment(noteId, commentId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.DeleteComment(noteId, commentId, c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c Blog) Comment(noteId, content, toCommentId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Item = blogService.Comment(noteId, toCommentId, c.GetUserId(), content);
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c Blog) LikeComment(commentId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
ok, isILikeIt, num := blogService.LikeComment(commentId, c.GetUserId())
|
||||
re.Ok = ok
|
||||
re.Item = bson.M{"IsILikeIt": isILikeIt, "Num": num}
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c Blog) Report(noteId, commentId, reason string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.Report(noteId, commentId, reason, c.GetUserId());
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
43
app/controllers/CaptchaController.go
Normal file
43
app/controllers/CaptchaController.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/lea/captcha"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
// "strconv"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// 验证码服务
|
||||
type Captcha struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
type Ca string
|
||||
func (r Ca) Apply(req *revel.Request, resp *revel.Response) {
|
||||
resp.WriteHeader(http.StatusOK, "image/png")
|
||||
}
|
||||
|
||||
func (c Captcha) Get() revel.Result {
|
||||
c.Response.ContentType = "image/png"
|
||||
image, str := captcha.Fetch()
|
||||
image.WriteTo(c.Response.Out)
|
||||
|
||||
sessionId := c.Session["_ID"]
|
||||
// LogJ(c.Session)
|
||||
// Log("------")
|
||||
// Log(str)
|
||||
// Log(sessionId)
|
||||
Log("..")
|
||||
sessionService.SetCaptcha(sessionId, str)
|
||||
|
||||
return c.Render()
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
// "encoding/json"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/lea/netutil"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -22,7 +23,7 @@ type File struct {
|
||||
func (c File) UploadBlogLogo() revel.Result {
|
||||
re := c.uploadImage("logo", "");
|
||||
|
||||
c.RenderArgs["fileUrlPath"] = siteUrl + "/" + re.Id
|
||||
c.RenderArgs["fileUrlPath"] = re.Id
|
||||
c.RenderArgs["resultCode"] = re.Code
|
||||
c.RenderArgs["resultMsg"] = re.Msg
|
||||
|
||||
@@ -53,6 +54,24 @@ func (c File) PasteImage(noteId string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 头像设置
|
||||
func (c File) UploadAvatar() revel.Result {
|
||||
re := c.uploadImage("logo", "");
|
||||
|
||||
c.RenderArgs["fileUrlPath"] = re.Id
|
||||
c.RenderArgs["resultCode"] = re.Code
|
||||
c.RenderArgs["resultMsg"] = re.Msg
|
||||
|
||||
if re.Ok {
|
||||
re.Ok = userService.UpdateAvatar(c.GetUserId(), re.Id)
|
||||
if re.Ok {
|
||||
c.UpdateSession("Logo", re.Id);
|
||||
}
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// leaui image plugin upload image
|
||||
func (c File) UploadImageLeaui(albumId string) revel.Result {
|
||||
re := c.uploadImage("", albumId);
|
||||
@@ -243,6 +262,38 @@ func (c File) CopyImage(userId, fileId, toUserId string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 复制外网的图片, 成公共图片 放在/upload下
|
||||
func (c File) CopyHttpImage(src string) revel.Result {
|
||||
re := info.NewRe()
|
||||
fileUrlPath := "upload/" + c.GetUserId() + "/images"
|
||||
dir := revel.BasePath + "/public/" + fileUrlPath
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
filesize, filename, _, ok := netutil.WriteUrl(src, dir)
|
||||
|
||||
if !ok {
|
||||
re.Msg = "copy error"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// File
|
||||
fileInfo := info.File{Name: filename,
|
||||
Title: filename,
|
||||
Path: fileUrlPath + "/" + filename,
|
||||
Size: filesize}
|
||||
|
||||
id := bson.NewObjectId();
|
||||
fileInfo.FileId = id
|
||||
|
||||
re.Id = id.Hex()
|
||||
re.Item = fileInfo.Path
|
||||
re.Ok = fileService.AddImage(fileInfo, "", c.GetUserId())
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
//------------
|
||||
// 过时 已弃用!
|
||||
func (c File) UploadImage(renderHtml string) revel.Result {
|
||||
|
||||
@@ -3,7 +3,7 @@ package controllers
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
)
|
||||
|
||||
// 首页
|
||||
@@ -29,7 +29,7 @@ func (c Index) Suggestion(addr, suggestion string) revel.Result {
|
||||
|
||||
// 发给我
|
||||
go func() {
|
||||
SendToLeanote("建议", "建议", "UserId: " + c.GetUserId() + " <br /> Suggestions: " + suggestion)
|
||||
emailService.SendEmail("leanote@leanote.com", "建议", "UserId: " + c.GetUserId() + " <br /> Suggestions: " + suggestion)
|
||||
}();
|
||||
|
||||
return c.RenderJson(re)
|
||||
|
||||
@@ -5,12 +5,13 @@ import (
|
||||
// "encoding/json"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/lea/html2image"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// "os"
|
||||
"os/exec"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "bytes"
|
||||
// "os"
|
||||
)
|
||||
|
||||
type Note struct {
|
||||
@@ -52,6 +53,8 @@ func (c Note) Index() revel.Result {
|
||||
// 当然, 还需要得到第一个notes的content
|
||||
//...
|
||||
|
||||
adminUsername, _ := revel.Config.String("adminUsername")
|
||||
c.RenderArgs["isAdmin"] = adminUsername == userInfo.Username
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.RenderArgs["userInfoJson"] = c.Json(userInfo)
|
||||
c.RenderArgs["notebooks"] = c.Json(notebooks)
|
||||
@@ -221,31 +224,131 @@ func (c Note) SearchNoteByTags(tags []string) revel.Result {
|
||||
return c.RenderJson(blogs)
|
||||
}
|
||||
|
||||
//-----------------
|
||||
//------------------
|
||||
// html2image
|
||||
// 判断是否有权限生成
|
||||
// 博客也可以调用
|
||||
// 这是脚本调用, 没有cookie, 不执行权限控制, 通过传来的appKey判断
|
||||
func (c Note) ToImage(noteId, appKey string) revel.Result {
|
||||
// 虽然传了cookie但是这里还是不能得到userId, 所以还是通过appKey来验证之
|
||||
appKeyTrue, _ := revel.Config.String("app.secret")
|
||||
if appKeyTrue != appKey {
|
||||
return c.RenderText("")
|
||||
}
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
return c.RenderText("")
|
||||
}
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
noteUserId := note.UserId.Hex()
|
||||
content := noteService.GetNoteContent(noteId, noteUserId)
|
||||
userInfo := userService.GetUserInfo(noteUserId);
|
||||
|
||||
c.RenderArgs["blog"] = note
|
||||
c.RenderArgs["content"] = content.Content
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
userBlog := blogService.GetUserBlog(noteUserId)
|
||||
c.RenderArgs["userBlog"] = userBlog
|
||||
|
||||
return c.RenderTemplate("html2Image/index.html")
|
||||
}
|
||||
|
||||
func (c Note) Html2Image(noteId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
userId := c.GetUserId()
|
||||
note := noteService.GetNote(noteId, userId)
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
re.Msg = "No Note"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
content := noteService.GetNoteContent(noteId, userId)
|
||||
|
||||
noteUserId := note.UserId.Hex()
|
||||
// 是否有权限
|
||||
if noteUserId != userId {
|
||||
// 是否是有权限协作的
|
||||
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
|
||||
re.Msg = "No Perm"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
// path 判断是否需要重新生成之
|
||||
fileUrlPath := "/upload/" + userId + "/images/weibo"
|
||||
fileUrlPath := "/upload/" + noteUserId + "/images/weibo"
|
||||
dir := revel.BasePath + "/public/" + fileUrlPath
|
||||
if !ClearDir(dir) {
|
||||
re.Msg = "No Dir"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
filename := note.NoteId.Hex() + ".png";
|
||||
path := dir + "/" + filename
|
||||
|
||||
// 生成之
|
||||
html2image.ToImage(userId, c.GetUsername(), noteId, note.Title, content.Content, path)
|
||||
// cookie
|
||||
cookieName := revel.CookiePrefix + "_SESSION"
|
||||
cookie, err := c.Request.Cookie(cookieName)
|
||||
cookieStr := cookie.String()
|
||||
cookieValue := ""
|
||||
if err == nil && len(cookieStr) > len(cookieName) {
|
||||
cookieValue = cookieStr[len(cookieName)+1:]
|
||||
}
|
||||
|
||||
re.Ok = true
|
||||
re.Id = fileUrlPath + "/" + filename
|
||||
appKey, _ := revel.Config.String("app.secret")
|
||||
cookieDomain, _ := revel.Config.String("cookie.domain")
|
||||
// 生成之
|
||||
url := siteUrl + "/note/toImage?noteId=" + noteId + "&appKey=" + appKey;
|
||||
// /Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/b.js
|
||||
binPath := configService.GetGlobalStringConfig("toImageBinPath")
|
||||
if binPath == "" {
|
||||
return c.RenderJson(re);
|
||||
}
|
||||
cc := binPath + " \"" + url + "\" \"" + path + "\" \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||
Log(cc);
|
||||
b, err := cmd.Output()
|
||||
if err == nil {
|
||||
re.Ok = true
|
||||
re.Id = fileUrlPath + "/" + filename
|
||||
} else {
|
||||
re.Msg = string(b)
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
|
||||
/*
|
||||
// 这里速度慢, 生成不完全(图片和内容都不全)
|
||||
content := noteService.GetNoteContent(noteId, noteUserId)
|
||||
userInfo := userService.GetUserInfo(noteUserId);
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
c.RenderArgs["blog"] = note
|
||||
c.RenderArgs["content"] = content.Content
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
userBlog := blogService.GetUserBlog(noteUserId)
|
||||
c.RenderArgs["userBlog"] = userBlog
|
||||
|
||||
html := c.RenderTemplateStr("html2Image/index.html") // Result类型的
|
||||
contentFile := dir + "/html";
|
||||
fout, err := os.Create(contentFile)
|
||||
if err != nil {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
fout.WriteString(html);
|
||||
fout.Close()
|
||||
|
||||
cc := "/Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/c.js \"" + contentFile + "\" \"" + path + "\""
|
||||
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||
b, err := cmd.Output()
|
||||
if err == nil {
|
||||
re.Ok = true
|
||||
re.Id = fileUrlPath + "/" + filename
|
||||
} else {
|
||||
Log(string(b))
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
"fmt"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
@@ -19,43 +19,48 @@ type User struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (c User) Account(tab int) revel.Result {
|
||||
userInfo := c.GetUserInfo()
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.RenderArgs["tab"] = tab
|
||||
c.SetLocale()
|
||||
return c.RenderTemplate("user/account.html")
|
||||
}
|
||||
|
||||
// 修改用户名, 需要重置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(c.GetUsername() == "demo") {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
if !IsUsername(username) {
|
||||
re.Ok = false
|
||||
re.Msg = "不能包含特殊字符"
|
||||
return c.RenderJson(re);
|
||||
|
||||
if re.Ok, re.Msg = Vd("username", username); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = userService.UpdateUsername(c.GetUserId(), username)
|
||||
if(re.Ok) {
|
||||
c.UpdateSession("Username", username)
|
||||
}
|
||||
return c.RenderJson(re);
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
func (c User) UpdatePwd(oldPwd, pwd string) revel.Result {
|
||||
re := info.NewRe();
|
||||
if oldPwd == "" {
|
||||
re.Msg = "旧密码错误"
|
||||
return c.RenderJson(re);
|
||||
if(c.GetUsername() == "demo") {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = IsGoodPwd(pwd)
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re);
|
||||
if re.Ok, re.Msg = Vd("password", oldPwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = userService.UpdatePwd(c.GetUserId(), oldPwd, pwd)
|
||||
return c.RenderJson(re);
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
// 更新主题
|
||||
@@ -75,14 +80,7 @@ func (c User) SendRegisterEmail(content, toEmail string) revel.Result {
|
||||
return c.RenderJson(re);
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
var userInfo = c.GetUserInfo();
|
||||
siteUrl, _ := revel.Config.String("site.url")
|
||||
url := siteUrl + "/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)
|
||||
|
||||
re.Ok = emailService.SendInviteEmail(c.GetUserInfo(), toEmail, content)
|
||||
return c.RenderJson(re);
|
||||
}
|
||||
|
||||
@@ -91,15 +89,23 @@ func (c User) SendRegisterEmail(content, toEmail string) revel.Result {
|
||||
// 重新发送激活邮件
|
||||
func (c User) ReSendActiveEmail() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = userService.RegisterSendActiveEmail(c.GetUserId(), c.GetEmail())
|
||||
re.Ok = emailService.RegisterSendActiveEmail(c.GetUserInfo(), 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)
|
||||
if(c.GetUsername() == "demo") {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re);
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = emailService.UpdateEmailSendActiveEmail(c.GetUserInfo(), email)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
// 通过点击链接
|
||||
@@ -145,22 +151,12 @@ func (c User) ActiveEmail(token string) revel.Result {
|
||||
// 第三方账号添加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)
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = userService.ThirdAddUser(c.GetUserId(), email, pwd)
|
||||
@@ -169,17 +165,20 @@ func (c User) AddAccount(email, pwd string) revel.Result {
|
||||
c.UpdateSession("Email", email);
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
//-----------------
|
||||
// 用户偏爱
|
||||
func (c User) UpdateColumnWidth(notebookWidth, noteListWidth int) revel.Result {
|
||||
func (c User) UpdateColumnWidth(notebookWidth, noteListWidth, mdEditorWidth int) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = userService.UpdateColumnWidth(c.GetUserId(), notebookWidth, noteListWidth)
|
||||
re.Ok = userService.UpdateColumnWidth(c.GetUserId(), notebookWidth, noteListWidth, mdEditorWidth)
|
||||
if re.Ok {
|
||||
c.UpdateSession("NotebookWidth", strconv.Itoa(notebookWidth));
|
||||
c.UpdateSession("NoteListWidth", strconv.Itoa(noteListWidth));
|
||||
c.UpdateSession("NoteListWidth", strconv.Itoa(noteListWidth));
|
||||
c.UpdateSession("MdEditorWidth", strconv.Itoa(mdEditorWidth));
|
||||
|
||||
LogJ(c.Session)
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -48,4 +48,12 @@ func (c AdminBaseController) getSorter(sorterField string, isAsc bool, okSorter
|
||||
}
|
||||
c.RenderArgs["sorter"] = sorter
|
||||
return sorterField, isAsc;
|
||||
}
|
||||
|
||||
func (c AdminBaseController) updateConfig(keys []string) {
|
||||
userId := c.GetUserId()
|
||||
for _, key := range keys {
|
||||
v := c.Params.Values.Get(key)
|
||||
configService.UpdateGlobalStringConfig(userId, key, v)
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,22 @@ func (c Admin) Index() revel.Result {
|
||||
c.RenderArgs["title"] = "leanote"
|
||||
c.SetLocale()
|
||||
|
||||
c.RenderArgs["countUser"] = userService.CountUser()
|
||||
c.RenderArgs["countNote"] = noteService.CountNote()
|
||||
c.RenderArgs["countBlog"] = noteService.CountBlog()
|
||||
|
||||
return c.RenderTemplate("admin/index.html");
|
||||
}
|
||||
|
||||
// 模板
|
||||
func (c Admin) T(t string) revel.Result {
|
||||
c.RenderArgs["str"] = configService.GlobalStringConfigs
|
||||
c.RenderArgs["arr"] = configService.GlobalArrayConfigs
|
||||
c.RenderArgs["map"] = configService.GlobalMapConfigs
|
||||
c.RenderArgs["arrMap"] = configService.GlobalArrMapConfigs
|
||||
return c.RenderTemplate("admin/" + t + ".html")
|
||||
}
|
||||
|
||||
func (c Admin) GetView(view string) revel.Result {
|
||||
return c.RenderTemplate("admin/" + view);
|
||||
}
|
||||
114
app/controllers/admin/AdminData.go
Normal file
114
app/controllers/admin/AdminData.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"os"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 数据管理, 备份和恢复
|
||||
|
||||
type AdminData struct {
|
||||
AdminBaseController
|
||||
}
|
||||
|
||||
func (c AdminData) Index() revel.Result {
|
||||
backups := configService.GetGlobalArrMapConfig("backups")
|
||||
// 逆序之
|
||||
backups2 := make([]map[string]string, len(backups))
|
||||
j := 0
|
||||
for i := len(backups)-1; i >= 0; i-- {
|
||||
backups2[j] = backups[i]
|
||||
j++
|
||||
}
|
||||
c.RenderArgs["backups"] = backups2
|
||||
return c.RenderTemplate("admin/data/index.html");
|
||||
}
|
||||
|
||||
func (c AdminData) Backup() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = configService.Backup("")
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 还原
|
||||
func (c AdminData) Restore(createdTime string) revel.Result {
|
||||
re := info.Re{}
|
||||
re.Ok, re.Msg = configService.Restore(createdTime)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c AdminData) Delete(createdTime string) revel.Result {
|
||||
re := info.Re{}
|
||||
re.Ok, re.Msg = configService.DeleteBackup(createdTime)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c AdminData) UpdateRemark(createdTime, remark string) revel.Result {
|
||||
re := info.Re{}
|
||||
re.Ok, re.Msg = configService.UpdateBackupRemark(createdTime, remark)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c AdminData) Download(createdTime string) revel.Result {
|
||||
backup, ok := configService.GetBackup(createdTime)
|
||||
if !ok {
|
||||
return c.RenderText("")
|
||||
}
|
||||
|
||||
dbname, _ := revel.Config.String("db.dbname")
|
||||
path := backup["path"] + "/" + dbname
|
||||
allFiles := ListDir(path)
|
||||
|
||||
filename := "backup_" + dbname + "_" + backup["createdTime"] + ".tar.gz"
|
||||
|
||||
// file write
|
||||
fw, err := os.Create(revel.BasePath + "/files/" + filename)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
|
||||
// gzip write
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
|
||||
// tar write
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
// 遍历文件列表
|
||||
for _, file := range allFiles {
|
||||
fn := path + "/" + file
|
||||
fr, err := os.Open(fn)
|
||||
fileInfo, _ := fr.Stat()
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// 信息头
|
||||
h := new(tar.Header)
|
||||
h.Name = file
|
||||
h.Size = fileInfo.Size()
|
||||
h.Mode = int64(fileInfo.Mode())
|
||||
h.ModTime = fileInfo.ModTime()
|
||||
|
||||
// 写信息头
|
||||
err = tw.WriteHeader(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 写文件
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} // for
|
||||
|
||||
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachm
|
||||
}
|
||||
233
app/controllers/admin/AdminEmailController.go
Normal file
233
app/controllers/admin/AdminEmailController.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"strings"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// admin 首页
|
||||
|
||||
type AdminEmail struct {
|
||||
AdminBaseController
|
||||
}
|
||||
|
||||
// email配置
|
||||
func (c AdminEmail) Email() revel.Result {
|
||||
return nil
|
||||
}
|
||||
|
||||
// blog标签设置
|
||||
func (c AdminEmail) Blog() revel.Result {
|
||||
recommendTags := configService.GetGlobalArrayConfig("recommendTags")
|
||||
newTags := configService.GetGlobalArrayConfig("newTags")
|
||||
c.RenderArgs["recommendTags"] = strings.Join(recommendTags, ",")
|
||||
c.RenderArgs["newTags"] = strings.Join(newTags, ",")
|
||||
return c.RenderTemplate("admin/setting/blog.html");
|
||||
}
|
||||
func (c AdminEmail) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// demo
|
||||
// blog标签设置
|
||||
func (c AdminEmail) Demo() revel.Result {
|
||||
c.RenderArgs["demoUsername"] = configService.GetGlobalStringConfig("demoUsername")
|
||||
c.RenderArgs["demoPassword"] = configService.GetGlobalStringConfig("demoPassword")
|
||||
return c.RenderTemplate("admin/setting/demo.html");
|
||||
}
|
||||
func (c AdminEmail) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
userInfo := authService.Login(demoUsername, demoPassword)
|
||||
if userInfo.UserId == "" {
|
||||
re.Msg = "The User is Not Exists";
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUserId", userInfo.UserId.Hex())
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUsername", demoUsername)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoPassword", demoPassword)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// ToImage
|
||||
// 长微博的bin路径phantomJs
|
||||
func (c AdminEmail) ToImage() revel.Result {
|
||||
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
|
||||
return c.RenderTemplate("admin/setting/toImage.html");
|
||||
}
|
||||
func (c AdminEmail) DoToImage(toImageBinPath string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "toImageBinPath", toImageBinPath)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c AdminEmail) Set(emailHost, emailPort, emailUsername, emailPassword string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailHost", emailHost)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailPort", emailPort)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailUsername", emailUsername)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailPassword", emailPassword)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c AdminEmail) Template() revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
keys := []string{"emailTemplateHeader", "emailTemplateFooter",
|
||||
"emailTemplateRegisterSubject",
|
||||
"emailTemplateRegister",
|
||||
"emailTemplateFindPasswordSubject",
|
||||
"emailTemplateFindPassword",
|
||||
"emailTemplateUpdateEmailSubject",
|
||||
"emailTemplateUpdateEmail",
|
||||
"emailTemplateInviteSubject",
|
||||
"emailTemplateInvite",
|
||||
"emailTemplateCommentSubject",
|
||||
"emailTemplateComment",
|
||||
}
|
||||
|
||||
userId := c.GetUserId()
|
||||
for _, key := range keys {
|
||||
v := c.Params.Values.Get(key)
|
||||
if v != "" {
|
||||
ok, msg := emailService.ValidTpl(v)
|
||||
if !ok {
|
||||
re.Ok = false
|
||||
re.Msg = "Error key: " + key + "<br />" + msg
|
||||
return c.RenderJson(re)
|
||||
} else {
|
||||
configService.UpdateGlobalStringConfig(userId, key, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 发送Email
|
||||
func (c AdminEmail) SendEmailToEmails(sendEmails, latestEmailSubject, latestEmailBody string, verified, saveAsOldEmail bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
c.updateConfig([]string{"sendEmails", "latestEmailSubject", "latestEmailBody"})
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if saveAsOldEmail {
|
||||
oldEmails := configService.GetGlobalMapConfig("oldEmails")
|
||||
oldEmails[latestEmailSubject] = latestEmailBody
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails);
|
||||
}
|
||||
|
||||
sendEmails = strings.Replace(sendEmails, "\r", "", -1)
|
||||
emails := strings.Split(sendEmails, "\n")
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToEmails(emails, latestEmailSubject, latestEmailBody);
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 发送Email
|
||||
func (c AdminEmail) SendToUsers2(emails, latestEmailSubject, latestEmailBody string, verified, saveAsOldEmail bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
c.updateConfig([]string{"sendEmails", "latestEmailSubject", "latestEmailBody"})
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if saveAsOldEmail {
|
||||
oldEmails := configService.GetGlobalMapConfig("oldEmails")
|
||||
oldEmails[latestEmailSubject] = latestEmailBody
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails);
|
||||
}
|
||||
|
||||
emails = strings.Replace(emails, "\r", "", -1)
|
||||
emailsArr := strings.Split(emails, "\n")
|
||||
|
||||
users := userService.ListUserInfosByEmails(emailsArr)
|
||||
LogJ(emailsArr)
|
||||
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToUsers(users, latestEmailSubject, latestEmailBody);
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// send Email dialog
|
||||
func (c AdminEmail) SendEmailDialog(emails string) revel.Result{
|
||||
emailsArr := strings.Split(emails, ",")
|
||||
emailsNl := strings.Join(emailsArr, "\n")
|
||||
|
||||
c.RenderArgs["emailsNl"] = emailsNl
|
||||
c.RenderArgs["str"] = configService.GlobalStringConfigs
|
||||
c.RenderArgs["map"] = configService.GlobalMapConfigs
|
||||
|
||||
return c.RenderTemplate("admin/email/emailDialog.html");
|
||||
}
|
||||
|
||||
func (c AdminEmail) SendToUsers(userFilterEmail, userFilterWhiteList, userFilterBlackList, latestEmailSubject, latestEmailBody string, verified, saveAsOldEmail bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
c.updateConfig([]string{"userFilterEmail", "userFilterWhiteList", "userFilterBlackList", "latestEmailSubject", "latestEmailBody"})
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if saveAsOldEmail {
|
||||
oldEmails := configService.GetGlobalMapConfig("oldEmails")
|
||||
oldEmails[latestEmailSubject] = latestEmailBody
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails);
|
||||
}
|
||||
|
||||
users := userService.GetAllUserByFilter(userFilterEmail, userFilterWhiteList, userFilterBlackList, verified)
|
||||
|
||||
if(users == nil || len(users) == 0) {
|
||||
re.Ok = false
|
||||
re.Msg = "no users"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToUsers(users, latestEmailSubject, latestEmailBody);
|
||||
if(!re.Ok) {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
re.Ok = true
|
||||
re.Msg = "users:" + strconv.Itoa(len(users))
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 删除emails
|
||||
func (c AdminEmail) DeleteEmails(ids string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = emailService.DeleteEmails(strings.Split(ids, ","))
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c AdminEmail) List(sorter, keywords string) revel.Result {
|
||||
pageNumber := c.GetPage()
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"email", "ok", "subject", "createdTime"});
|
||||
pageInfo, emails := emailService.ListEmailLogs(pageNumber, userPageSize, sorterField, isAsc, keywords);
|
||||
c.RenderArgs["pageInfo"] = pageInfo
|
||||
c.RenderArgs["emails"] = emails
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
return c.RenderTemplate("admin/email/list.html");
|
||||
}
|
||||
@@ -29,12 +29,22 @@ func (c AdminSetting) Blog() revel.Result {
|
||||
func (c AdminSetting) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
re.Ok = configService.UpdateUserArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
|
||||
re.Ok = configService.UpdateUserArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 共享设置
|
||||
func (c AdminSetting) ShareNote(registerSharedUserId string,
|
||||
registerSharedNotebookPerms, registerSharedNotePerms []int,
|
||||
registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds []string) revel.Result {
|
||||
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = configService.UpdateShareNoteConfig(registerSharedUserId, registerSharedNotebookPerms, registerSharedNotePerms, registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds);
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// demo
|
||||
// blog标签设置
|
||||
func (c AdminSetting) Demo() revel.Result {
|
||||
@@ -51,12 +61,53 @@ func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoUserId", userInfo.UserId.Hex())
|
||||
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoUsername", demoUsername)
|
||||
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoPassword", demoPassword)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUserId", userInfo.UserId.Hex())
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUsername", demoUsername)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoPassword", demoPassword)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// ToImage
|
||||
// 长微博的bin路径phantomJs
|
||||
func (c AdminSetting) ToImage() revel.Result {
|
||||
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
|
||||
return c.RenderTemplate("admin/setting/toImage.html");
|
||||
}
|
||||
func (c AdminSetting) DoToImage(toImageBinPath string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "toImageBinPath", toImageBinPath)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// SubDomain
|
||||
func (c AdminSetting) SubDomain() revel.Result {
|
||||
c.RenderArgs["str"] = configService.GlobalStringConfigs
|
||||
c.RenderArgs["arr"] = configService.GlobalArrayConfigs
|
||||
|
||||
c.RenderArgs["noteSubDomain"] = configService.GetGlobalStringConfig("noteSubDomain")
|
||||
c.RenderArgs["blogSubDomain"] = configService.GetGlobalStringConfig("blogSubDomain")
|
||||
c.RenderArgs["leaSubDomain"] = configService.GetGlobalStringConfig("leaSubDomain")
|
||||
|
||||
return c.RenderTemplate("admin/setting/subDomain.html");
|
||||
}
|
||||
func (c AdminSetting) DoSubDomain(noteSubDomain, blogSubDomain, leaSubDomain, blackSubDomains, allowCustomDomain, blackCustomDomains string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "noteSubDomain", noteSubDomain)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "blogSubDomain", blogSubDomain)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "leaSubDomain", leaSubDomain)
|
||||
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "allowCustomDomain", allowCustomDomain)
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "blackSubDomains", strings.Split(blackSubDomains, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "blackCustomDomains", strings.Split(blackCustomDomains, ","))
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c AdminSetting) Mongodb(mongodumpPath, mongorestorePath string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "mongodumpPath", mongodumpPath)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "mongorestorePath", mongorestorePath)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
18
app/controllers/admin/AdminUpgradeController.go
Normal file
18
app/controllers/admin/AdminUpgradeController.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
)
|
||||
|
||||
// Upgrade controller
|
||||
type AdminUpgrade struct {
|
||||
AdminBaseController
|
||||
}
|
||||
|
||||
func (c AdminUpgrade) UpgradeBlog() revel.Result {
|
||||
upgradeService.UpgradeBlog()
|
||||
return nil;
|
||||
}
|
||||
@@ -25,6 +25,8 @@ var noteImageService *service.NoteImageService
|
||||
var fileService *service.FileService
|
||||
var attachService *service.AttachService
|
||||
var configService *service.ConfigService
|
||||
var emailService *service.EmailService
|
||||
var upgradeService *service.UpgradeService
|
||||
|
||||
var adminUsername = "admin"
|
||||
// 拦截器
|
||||
@@ -115,12 +117,18 @@ func InitService() {
|
||||
suggestionService = service.SuggestionS
|
||||
authService = service.AuthS
|
||||
configService = service.ConfigS
|
||||
emailService = service.EmailS
|
||||
upgradeService = service.UpgradeS
|
||||
}
|
||||
|
||||
func init() {
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Admin{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminSetting{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminUser{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminBlog{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminEmail{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminUpgrade{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminData{})
|
||||
revel.OnAppStart(func() {
|
||||
adminUsername, _ = revel.Config.String("adminUsername")
|
||||
})
|
||||
|
||||
@@ -25,6 +25,8 @@ var noteImageService *service.NoteImageService
|
||||
var fileService *service.FileService
|
||||
var attachService *service.AttachService
|
||||
var configService *service.ConfigService
|
||||
var emailService *service.EmailService
|
||||
var sessionService *service.SessionService
|
||||
|
||||
var pageSize = 1000
|
||||
var defaultSortField = "UpdatedTime"
|
||||
@@ -47,10 +49,15 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
|
||||
"FindPasswordUpdate": true,
|
||||
"Suggestion": true,
|
||||
},
|
||||
"Note": map[string]bool{"ToImage": true},
|
||||
"Blog": map[string]bool{"Index": true,
|
||||
"View": true,
|
||||
"AboutMe": true,
|
||||
"SearchBlog": true,
|
||||
"Cate": true,
|
||||
"Search": true,
|
||||
"GetLikeAndComments": true,
|
||||
"IncReadNum": true,
|
||||
"ListComments": true,
|
||||
},
|
||||
// 用户的激活与修改邮箱都不需要登录, 通过链接地址
|
||||
"User": map[string]bool{"UpdateEmail": true,
|
||||
@@ -118,6 +125,8 @@ func InitService() {
|
||||
suggestionService = service.SuggestionS
|
||||
authService = service.AuthS
|
||||
configService = service.ConfigS
|
||||
emailService = service.EmailS
|
||||
sessionService = service.SessionS
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -40,6 +40,15 @@ var Attachs *mgo.Collection
|
||||
|
||||
var NoteImages *mgo.Collection
|
||||
var Configs *mgo.Collection
|
||||
var EmailLogs *mgo.Collection
|
||||
|
||||
// blog
|
||||
var BlogLikes *mgo.Collection
|
||||
var BlogComments *mgo.Collection
|
||||
var Reports *mgo.Collection
|
||||
|
||||
// session
|
||||
var Sessions *mgo.Collection
|
||||
|
||||
// 初始化时连接数据库
|
||||
func Init() {
|
||||
@@ -113,12 +122,17 @@ func Init() {
|
||||
NoteImages = Session.DB(dbname).C("note_images")
|
||||
|
||||
Configs = Session.DB(dbname).C("configs")
|
||||
}
|
||||
|
||||
func init() {
|
||||
revel.OnAppStart(func() {
|
||||
Init()
|
||||
})
|
||||
EmailLogs = Session.DB(dbname).C("email_logs")
|
||||
|
||||
// 社交
|
||||
BlogLikes = Session.DB(dbname).C("blog_likes")
|
||||
BlogComments = Session.DB(dbname).C("blog_comments")
|
||||
|
||||
// 举报
|
||||
Reports = Session.DB(dbname).C("reports")
|
||||
|
||||
// session
|
||||
Sessions = Session.DB(dbname).C("sessions")
|
||||
}
|
||||
|
||||
func close() {
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
|
||||
// convert revel msg to js msg
|
||||
|
||||
var msgBasePath = "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/messages/"
|
||||
var targetBasePath = "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/public/js/i18n/"
|
||||
var msgBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/messages/"
|
||||
var targetBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/public/js/i18n/"
|
||||
func parse(filename string) {
|
||||
file, err := os.Open(msgBasePath + filename)
|
||||
reader := bufio.NewReader(file)
|
||||
@@ -62,11 +62,28 @@ func parse(filename string) {
|
||||
if err2 != nil {
|
||||
file2, err2 = os.Create(targetName)
|
||||
}
|
||||
file2.WriteString("var MSG = " + str + ";")
|
||||
file2.WriteString("var MSG = " + str + ";" + `
|
||||
function getMsg(key, data) {
|
||||
var msg = MSG[key]
|
||||
if(msg) {
|
||||
if(data) {
|
||||
if(!isArray(data)) {
|
||||
data = [data];
|
||||
}
|
||||
for(var i = 0; i < data.length; ++i) {
|
||||
msg = msg.replace("%s", data[i]);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
return key;
|
||||
}`)
|
||||
}
|
||||
|
||||
// 生成js的i18n文件
|
||||
func main() {
|
||||
parse("msg.en")
|
||||
parse("msg.zh")
|
||||
parse("blog.zh")
|
||||
parse("blog.en")
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package info
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 只为blog, 不为note
|
||||
@@ -10,35 +11,76 @@ type BlogItem struct {
|
||||
Note
|
||||
Content string // 可能是content的一部分, 截取. 点击more后就是整个信息了
|
||||
HasMore bool // 是否是否还有
|
||||
User User // 用户信息
|
||||
User User // 用户信息
|
||||
}
|
||||
|
||||
type UserBlogBase struct {
|
||||
Logo string `Logo`
|
||||
Title string `Title` // 标题
|
||||
SubTitle string `SubTitle` // 副标题
|
||||
AboutMe string `AboutMe` // 关于我
|
||||
Logo string `Logo`
|
||||
Title string `Title` // 标题
|
||||
SubTitle string `SubTitle` // 副标题
|
||||
AboutMe string `AboutMe` // 关于我
|
||||
}
|
||||
|
||||
type UserBlogComment struct {
|
||||
CanComment bool `CanComment` // 是否可以评论
|
||||
DisqusId string `DisqusId`
|
||||
CanComment bool `CanComment` // 是否可以评论
|
||||
CommentType string `CommentType` // default 或 disqus
|
||||
DisqusId string `DisqusId`
|
||||
}
|
||||
|
||||
type UserBlogStyle struct {
|
||||
Style string `Style` // 风格
|
||||
Style string `Style` // 风格
|
||||
Css string `Css` // 自定义css
|
||||
}
|
||||
|
||||
// 每个用户一份博客设置信息
|
||||
type UserBlog struct {
|
||||
UserId bson.ObjectId `bson:"_id"` // 谁的
|
||||
Logo string `Logo`
|
||||
Title string `Title` // 标题
|
||||
SubTitle string `SubTitle` // 副标题
|
||||
AboutMe string `AboutMe` // 关于我
|
||||
UserId bson.ObjectId `bson:"_id"` // 谁的
|
||||
Logo string `Logo`
|
||||
Title string `Title` // 标题
|
||||
SubTitle string `SubTitle` // 副标题
|
||||
AboutMe string `AboutMe` // 关于我
|
||||
|
||||
CanComment bool `CanComment` // 是否可以评论
|
||||
|
||||
CanComment bool `CanComment` // 是否可以评论
|
||||
DisqusId string `DisqusId`
|
||||
|
||||
Style string `Style` // 风格
|
||||
}
|
||||
CommentType string `CommentType` // default 或 disqus
|
||||
DisqusId string `DisqusId`
|
||||
|
||||
Style string `Style` // 风格
|
||||
Css string `Css` // 自定义css
|
||||
|
||||
SubDomain string `SubDomain` // 二级域名
|
||||
Domain string `Domain` // 自定义域名
|
||||
}
|
||||
|
||||
//------------------------
|
||||
// 社交功能, 点赞, 分享, 评论
|
||||
|
||||
// 点赞记录
|
||||
type BlogLike struct {
|
||||
LikeId bson.ObjectId `bson:"_id"`
|
||||
NoteId bson.ObjectId `NoteId`
|
||||
UserId bson.ObjectId `UserId`
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
}
|
||||
|
||||
// 评论
|
||||
type BlogComment struct {
|
||||
CommentId bson.ObjectId `bson:"_id"`
|
||||
NoteId bson.ObjectId `NoteId`
|
||||
|
||||
UserId bson.ObjectId `UserId` // UserId回复ToUserId
|
||||
Content string `Content` // 评论内容
|
||||
|
||||
ToCommentId bson.ObjectId `ToCommendId,omitempty` // 对某条评论进行回复
|
||||
ToUserId bson.ObjectId `ToUserId,omitempty` // 为空表示直接评论, 不回空表示回复某人
|
||||
|
||||
LikeNum int `LikeNum` // 点赞次数, 评论也可以点赞
|
||||
LikeUserIds []string `LikeUserIds` // 点赞的用户ids
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
}
|
||||
|
||||
type BlogCommentPublic struct {
|
||||
BlogComment
|
||||
IsILikeIt bool
|
||||
}
|
||||
|
||||
@@ -5,11 +5,21 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 配置
|
||||
// 用户配置高于全局配置
|
||||
// 配置, 每一个配置一行记录
|
||||
type Config struct {
|
||||
UserId bson.ObjectId `bson:"_id"`
|
||||
StringConfigs map[string]string `StringConfigs` // key => value
|
||||
ArrayConfigs map[string][]string `ArrayConfigs` // key => []value
|
||||
UpdatedTime time.Time `UpdatedTime`
|
||||
ConfigId bson.ObjectId `bson:"_id"`
|
||||
UserId bson.ObjectId `UserId`
|
||||
Key string `Key`
|
||||
ValueStr string `ValueStr,omitempty` // "1"
|
||||
ValueArr []string `ValueArr,omitempty` // ["1","b","c"]
|
||||
ValueMap map[string]string `ValueMap,omitempty` // {"a":"bb", "CC":"xx"}
|
||||
ValueArrMap []map[string]string `ValueArrMap,omitempty` // [{"a":"B"}, {}, {}]
|
||||
IsArr bool `IsArr` // 是否是数组
|
||||
IsMap bool `IsMap` // 是否是Map
|
||||
IsArrMap bool `IsArrMap` // 是否是数组Map
|
||||
|
||||
// StringConfigs map[string]string `StringConfigs` // key => value
|
||||
// ArrayConfigs map[string][]string `ArrayConfigs` // key => []value
|
||||
|
||||
UpdatedTime time.Time `UpdatedTime`
|
||||
}
|
||||
|
||||
19
app/info/EmailLogInfo.go
Normal file
19
app/info/EmailLogInfo.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 发送邮件
|
||||
type EmailLog struct {
|
||||
LogId bson.ObjectId `bson:"_id"`
|
||||
|
||||
Email string `Email` // 发送者
|
||||
Subject string `Subject` // 主题
|
||||
Body string `Body` // 内容
|
||||
Msg string `Msg` // 发送失败信息
|
||||
Ok bool `Ok` // 发送是否成功
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
}
|
||||
@@ -15,21 +15,28 @@ type Note struct {
|
||||
Title string `Title` // 标题
|
||||
Desc string `Desc` // 描述, 非html
|
||||
|
||||
ImgSrc string `ImgSrc` // 图片, 第一张缩略图地址
|
||||
Tags []string `Tags,omitempty`
|
||||
|
||||
IsTrash bool `IsTrash` // 是否是trash, 默认是false
|
||||
ImgSrc string `ImgSrc` // 图片, 第一张缩略图地址
|
||||
Tags []string `Tags,omitempty`
|
||||
|
||||
IsBlog bool `IsBlog,omitempty` // 是否设置成了blog 2013/12/29 新加
|
||||
IsTrash bool `IsTrash` // 是否是trash, 默认是false
|
||||
|
||||
IsBlog bool `IsBlog,omitempty` // 是否设置成了blog 2013/12/29 新加
|
||||
IsRecommend bool `IsRecommend,omitempty` // 是否为推荐博客 2014/9/24新加
|
||||
IsTop bool `IsTop,omitempty` // blog是否置顶
|
||||
IsTop bool `IsTop,omitempty` // blog是否置顶
|
||||
|
||||
// 2014/9/28 添加评论社交功能
|
||||
ReadNum int `ReadNum,omitempty` // 阅读次数 2014/9/28
|
||||
LikeNum int `LikeNum,omitempty` // 点赞次数 2014/9/28
|
||||
CommentNum int `CommentNum,omitempty` // 评论次数 2014/9/28
|
||||
|
||||
IsMarkdown bool `IsMarkdown` // 是否是markdown笔记, 默认是false
|
||||
|
||||
AttachNum int `AttachNum` // 2014/9/21, attachments num
|
||||
AttachNum int `AttachNum` // 2014/9/21, attachments num
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
UpdatedTime time.Time `UpdatedTime`
|
||||
RecommendTime time.Time `RecommendTime,omitempty` // 推荐时间
|
||||
PublicTime time.Time `PublicTime,omitempty` // 发表时间, 公开为博客则设置
|
||||
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了
|
||||
}
|
||||
|
||||
|
||||
19
app/info/ReportInfo.go
Normal file
19
app/info/ReportInfo.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 举报
|
||||
type Report struct {
|
||||
ReportId bson.ObjectId `bson:"_id"`
|
||||
NoteId bson.ObjectId `NoteId`
|
||||
|
||||
UserId bson.ObjectId `UserId` // UserId回复ToUserId
|
||||
Reason string `Reason` // 评论内容
|
||||
|
||||
CommentId bson.ObjectId `CommendId,omitempty` // 对某条评论进行回复
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
}
|
||||
19
app/info/SessionInfo.go
Normal file
19
app/info/SessionInfo.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
// http://docs.mongodb.org/manual/tutorial/expire-data/
|
||||
type Session struct {
|
||||
Id bson.ObjectId `bson:"_id,omitempty"` // 没有意义
|
||||
|
||||
SessionId string `bson:"SessionId"` // SessionId
|
||||
|
||||
LoginTimes int `LoginTimes` // 登录错误时间
|
||||
Captcha string `Captcha` // 验证码
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
UpdatedTime time.Time `UpdatedTime` // 更新时间, expire这个时间会自动清空
|
||||
}
|
||||
@@ -27,6 +27,7 @@ type User struct {
|
||||
// 用户配置
|
||||
NotebookWidth int `NotebookWidth` // 笔记本宽度
|
||||
NoteListWidth int `NoteListWidth` // 笔记列表宽度
|
||||
MdEditorWidth int `MdEditorWidth` // markdown 左侧编辑器宽度
|
||||
LeftIsMin bool `LeftIsMin` // 左侧是否是隐藏的, 默认是打开的
|
||||
|
||||
// 这里 第三方登录
|
||||
|
||||
81
app/init.go
81
app/init.go
@@ -4,9 +4,13 @@ import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
"github.com/leanote/leanote/app/controllers/admin"
|
||||
_ "github.com/leanote/leanote/app/lea/binder"
|
||||
"github.com/leanote/leanote/app/lea/session"
|
||||
"github.com/leanote/leanote/app/lea/memcache"
|
||||
"github.com/leanote/leanote/app/lea/route"
|
||||
"reflect"
|
||||
"fmt"
|
||||
"html/template"
|
||||
@@ -14,20 +18,24 @@ import (
|
||||
"strings"
|
||||
"strconv"
|
||||
"time"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
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.
|
||||
RouterFilter,
|
||||
route.RouterFilter,
|
||||
// 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.
|
||||
// revel.SessionFilter, // Restore and write the session cookie.
|
||||
|
||||
// session.SessionFilter, // leanote memcache session life
|
||||
// 使用SessionFilter标准版从cookie中得到sessionID, 然后通过MssessionFilter从Memcache中得到
|
||||
// session, 之后MSessionFilter将session只存sessionID然后返回给SessionFilter返回到web
|
||||
session.SessionFilter, // leanote session
|
||||
// session.MSessionFilter, // leanote memcache session
|
||||
|
||||
revel.FlashFilter, // Restore and write the flash cookie.
|
||||
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
|
||||
@@ -48,12 +56,34 @@ func init() {
|
||||
i = i - 1;
|
||||
return i
|
||||
}
|
||||
revel.TemplateFuncs["join"] = func(arr []string) template.HTML {
|
||||
if arr == nil {
|
||||
return template.HTML("")
|
||||
}
|
||||
return template.HTML(strings.Join(arr, ","))
|
||||
}
|
||||
revel.TemplateFuncs["concat"] = func(s1, s2 string) template.HTML {
|
||||
return template.HTML(s1 + s2)
|
||||
}
|
||||
revel.TemplateFuncs["concatStr"] = func(strs ...string) string {
|
||||
str := ""
|
||||
for _, s := range strs {
|
||||
str += s
|
||||
}
|
||||
return str
|
||||
}
|
||||
revel.TemplateFuncs["json"] = func(i interface{}) string {
|
||||
b, _ := json.Marshal(i)
|
||||
return string(b)
|
||||
}
|
||||
revel.TemplateFuncs["datetime"] = func(t time.Time) template.HTML {
|
||||
return template.HTML(t.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
revel.TemplateFuncs["unixDatetime"] = func(unixSec string) template.HTML {
|
||||
sec, _ := strconv.Atoi(unixSec)
|
||||
t := time.Unix(int64(sec), 0)
|
||||
return template.HTML(t.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// interface是否有该字段
|
||||
revel.TemplateFuncs["has"] = func(i interface{}, key string) bool {
|
||||
@@ -63,6 +93,26 @@ func init() {
|
||||
}
|
||||
|
||||
// tags
|
||||
revel.TemplateFuncs["blogTags"] = func(renderArgs map[string]interface{}, tags []string) template.HTML {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
tagStr := ""
|
||||
lenTags := len(tags)
|
||||
for i, tag := range tags {
|
||||
str := revel.Message(locale, tag)
|
||||
if strings.HasPrefix(str, "???") {
|
||||
str = tag
|
||||
}
|
||||
tagStr += str
|
||||
if i != lenTags - 1 {
|
||||
tagStr += ","
|
||||
}
|
||||
}
|
||||
return template.HTML(tagStr)
|
||||
}
|
||||
/*
|
||||
revel.TemplateFuncs["blogTags"] = func(tags []string) template.HTML {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return ""
|
||||
@@ -83,7 +133,7 @@ func init() {
|
||||
}
|
||||
return template.HTML(tagStr)
|
||||
}
|
||||
|
||||
*/
|
||||
revel.TemplateFuncs["li"] = func(a string) string {
|
||||
Log(a)
|
||||
Log("life==")
|
||||
@@ -130,6 +180,15 @@ func init() {
|
||||
return ""
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/14226416/go-lang-templates-always-quotes-a-string-and-removes-comments
|
||||
revel.TemplateFuncs["rawMsg"] = func(renderArgs map[string]interface{}, message string, args ...interface{}) template.JS {
|
||||
str, ok := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return template.JS(revel.Message(str, message, args...))
|
||||
}
|
||||
|
||||
// 为后台管理sorter th使用
|
||||
// 必须要返回HTMLAttr, 返回html, golang 会执行安全检查返回ZgotmplZ
|
||||
// sorterI 可能是nil, 所以用interfalce{}来接收
|
||||
@@ -155,7 +214,7 @@ func init() {
|
||||
}
|
||||
|
||||
// pagination
|
||||
revel.TemplateFuncs["page"] = func(userId, notebookId string, page, pageSize, count int) template.HTML {
|
||||
revel.TemplateFuncs["page"] = func(urlBase string, page, pageSize, count int) template.HTML {
|
||||
if count == 0 {
|
||||
return "";
|
||||
}
|
||||
@@ -170,11 +229,6 @@ func init() {
|
||||
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)
|
||||
|
||||
@@ -238,8 +292,13 @@ func init() {
|
||||
|
||||
// init Email
|
||||
revel.OnAppStart(func() {
|
||||
// 数据库
|
||||
db.Init()
|
||||
// email配置
|
||||
InitEmail()
|
||||
|
||||
InitVd()
|
||||
memcache.InitMemcache() // session服务
|
||||
// 其它service
|
||||
service.InitService()
|
||||
controllers.InitService()
|
||||
admin.InitService()
|
||||
|
||||
@@ -22,7 +22,7 @@ func InitEmail() {
|
||||
var bodyTpl = `
|
||||
<html>
|
||||
<body>
|
||||
<div style="width: 800px; margin:auto; border-radius:5px; border: 1px solid #ccc; padding: 20px;">
|
||||
<div style="width: 600px; margin:auto; border-radius:5px; border: 1px solid #ccc; padding: 20px;">
|
||||
<div>
|
||||
<div>
|
||||
<div style="float:left; height: 40px;">
|
||||
@@ -56,7 +56,7 @@ var bodyTpl = `
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
func SendEmail(to, subject, title, body string) bool {
|
||||
func SendEmailOld(to, subject, body string) bool {
|
||||
hp := strings.Split(host, ":")
|
||||
auth := smtp.PlainAuth("", username, password, hp[0])
|
||||
|
||||
@@ -69,9 +69,8 @@ func SendEmail(to, subject, title, body string) bool {
|
||||
content_type = "Content-Type: text/plain" + "; charset=UTF-8"
|
||||
}
|
||||
|
||||
// 登录之
|
||||
body = strings.Replace(bodyTpl, "$body", body, 1)
|
||||
body = strings.Replace(body, "$title", title, 1)
|
||||
//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, ";")
|
||||
@@ -84,7 +83,7 @@ func SendEmail(to, subject, title, body string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func SendToLeanote(subject, title, body string) {
|
||||
func SendToLeanoteOld(subject, title, body string) {
|
||||
to := "leanote@leanote.com"
|
||||
SendEmail(to, subject, title, body);
|
||||
SendEmailOld(to, subject, body);
|
||||
}
|
||||
@@ -77,4 +77,14 @@ func CopyFile(srcName, dstName string) (written int64, err error) {
|
||||
}
|
||||
defer dst.Close()
|
||||
return io.Copy(dst, src)
|
||||
}
|
||||
|
||||
func IsDirExists(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return os.IsExist(err)
|
||||
}else{
|
||||
return fi.IsDir()
|
||||
}
|
||||
return false
|
||||
}
|
||||
145
app/lea/Vd.go
Normal file
145
app/lea/Vd.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package lea
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// 验证
|
||||
|
||||
var rulesStr = `{
|
||||
"username": [
|
||||
{"rule": "required", "msg": "inputUsername"},
|
||||
{"rule": "noSpecialChars", "msg": "noSpecialChars"},
|
||||
{"rule": "minLength", "data": "4", "msg": "minLength", "msgData": "4"}
|
||||
],
|
||||
"email": [
|
||||
{"rule": "required", "msg": "inputEmail"},
|
||||
{"rule": "email", "msg": "errorEmail"}
|
||||
],
|
||||
"password": [
|
||||
{"rule": "required", "msg": "inputPassword"},
|
||||
{"rule": "password", "msg": "errorPassword"}
|
||||
],
|
||||
"subDomain": [
|
||||
{"rule": "subDomain", "msg": "errorSubDomain"}
|
||||
],
|
||||
"domain": [
|
||||
{"rule": "domain", "msg": "errorDomain"}
|
||||
]
|
||||
}
|
||||
`
|
||||
var rulesMap map[string][]map[string]string
|
||||
|
||||
var rules = map[string]func(string, map[string]string)(bool, string) {
|
||||
"required": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
},
|
||||
"minLength": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
data := rule["data"]
|
||||
dataI, _ := strconv.Atoi(data)
|
||||
ok = len(value) >= dataI
|
||||
return
|
||||
},
|
||||
|
||||
"password": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
ok = len(value) >= 6
|
||||
return
|
||||
},
|
||||
"email": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
ok = IsEmail(value)
|
||||
return
|
||||
},
|
||||
"noSpecialChars": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
ok = IsUsername(value)
|
||||
return
|
||||
},
|
||||
// www.baidu.com
|
||||
//
|
||||
"domain": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
if value == "" {
|
||||
ok = true
|
||||
return // 可为空
|
||||
}
|
||||
ok2, _ := regexp.MatchString(`[^0-9a-zA-Z_\.\-]`, value)
|
||||
ok = !ok2
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
},
|
||||
// abcd
|
||||
"subDomain": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
if value == "" {
|
||||
ok = true
|
||||
return // 可为空
|
||||
}
|
||||
if len(value) < 4 {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
ok2, _ := regexp.MatchString(`[^0-9a-zA-Z_\-]`, value)
|
||||
ok = !ok2
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func InitVd() {
|
||||
json.Unmarshal([]byte(rulesStr), &rulesMap)
|
||||
LogJ(rulesMap)
|
||||
}
|
||||
|
||||
// 验证
|
||||
// Vd("username", "life")
|
||||
|
||||
func Vd(name, value string) (ok bool, msg string) {
|
||||
rs, _ := rulesMap[name]
|
||||
|
||||
for _, rule := range rs {
|
||||
ruleFunc, _ := rules[rule["rule"]]
|
||||
if ok2, msg2 := ruleFunc(value, rule); !ok2 {
|
||||
ok = false
|
||||
if msg2 != "" {
|
||||
msg = msg2
|
||||
} else {
|
||||
msg = rule["msg"]
|
||||
}
|
||||
msgData := rule["msgData"]
|
||||
if msgData != "" {
|
||||
msg += "-" + msgData
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func Vds(m map[string]string) (ok bool, msg string) {
|
||||
for name, value := range m {
|
||||
ok, msg = Vd(name, value)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
399
app/lea/captcha/Captcha.go
Normal file
399
app/lea/captcha/Captcha.go
Normal file
@@ -0,0 +1,399 @@
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"math/rand"
|
||||
crand "crypto/rand"
|
||||
"time"
|
||||
"strconv"
|
||||
)
|
||||
const (
|
||||
stdWidth = 100
|
||||
stdHeight = 40
|
||||
maxSkew = 2
|
||||
)
|
||||
|
||||
const (
|
||||
fontWidth = 5
|
||||
fontHeight = 8
|
||||
blackChar = 1
|
||||
)
|
||||
|
||||
var font = [][]byte{
|
||||
{ // 0
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 1
|
||||
0, 0, 1, 0, 0,
|
||||
0, 1, 1, 0, 0,
|
||||
1, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
},
|
||||
{ // 2
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 1,
|
||||
0, 1, 1, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
},
|
||||
{ // 3
|
||||
1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 4
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
},
|
||||
{ // 5
|
||||
1, 1, 1, 1, 1,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 6
|
||||
0, 0, 1, 1, 1,
|
||||
0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 7
|
||||
1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
},
|
||||
{ // 8
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 9
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 1, 0, 0, 1,
|
||||
0, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
*image.NRGBA
|
||||
color *color.NRGBA
|
||||
width int //a digit width
|
||||
height int //a digit height
|
||||
dotsize int
|
||||
}
|
||||
func init(){
|
||||
rand.Seed(int64(time.Second))
|
||||
}
|
||||
|
||||
func NewImage(digits []byte, width, height int) *Image {
|
||||
img := new(Image)
|
||||
r := image.Rect(img.width, img.height, stdWidth, stdHeight)
|
||||
img.NRGBA = image.NewNRGBA(r)
|
||||
|
||||
img.color = &color.NRGBA{
|
||||
uint8(rand.Intn(129)),
|
||||
uint8(rand.Intn(129)),
|
||||
uint8(rand.Intn(129)),
|
||||
0xFF,
|
||||
}
|
||||
// Draw background (10 random circles of random brightness)
|
||||
img.calculateSizes(width, height, len(digits))
|
||||
img.fillWithCircles(10, img.dotsize)
|
||||
|
||||
maxx := width - (img.width+img.dotsize)*len(digits) - img.dotsize
|
||||
maxy := height - img.height - img.dotsize*2
|
||||
|
||||
x := rnd(img.dotsize*2, maxx)
|
||||
y := rnd(img.dotsize*2, maxy)
|
||||
|
||||
// Draw digits.
|
||||
for _, n := range digits {
|
||||
img.drawDigit(font[n], x, y)
|
||||
x += img.width + img.dotsize
|
||||
}
|
||||
|
||||
// Draw strike-through line.
|
||||
// 中间线不要
|
||||
//img.strikeThrough()
|
||||
|
||||
return img
|
||||
}
|
||||
|
||||
func (img *Image) WriteTo(w io.Writer) (int64, error) {
|
||||
return 0, png.Encode(w, img)
|
||||
}
|
||||
|
||||
func (img *Image) calculateSizes(width, height, ncount int) {
|
||||
|
||||
// Goal: fit all digits inside the image.
|
||||
var border int
|
||||
if width > height {
|
||||
border = height / 5
|
||||
} else {
|
||||
border = width / 5
|
||||
}
|
||||
// Convert everything to floats for calculations.
|
||||
w := float64(width - border*2) //268
|
||||
h := float64(height - border*2) //48
|
||||
// fw takes into account 1-dot spacing between digits.
|
||||
|
||||
fw := float64(fontWidth) + 1 //6
|
||||
|
||||
fh := float64(fontHeight) //8
|
||||
nc := float64(ncount) //7
|
||||
|
||||
// Calculate the width of a single digit taking into account only the
|
||||
// width of the image.
|
||||
nw := w / nc //38
|
||||
// Calculate the height of a digit from this width.
|
||||
nh := nw * fh / fw //51
|
||||
|
||||
// Digit too high?
|
||||
|
||||
if nh > h {
|
||||
// Fit digits based on height.
|
||||
nh = h //nh = 44
|
||||
nw = fw / fh * nh
|
||||
}
|
||||
// Calculate dot size.
|
||||
img.dotsize = int(nh / fh)
|
||||
// Save everything, making the actual width smaller by 1 dot to account
|
||||
// for spacing between digits.
|
||||
img.width = int(nw)
|
||||
img.height = int(nh) - img.dotsize
|
||||
}
|
||||
|
||||
func (img *Image) fillWithCircles(n, maxradius int) {
|
||||
color := img.color
|
||||
maxx := img.Bounds().Max.X
|
||||
maxy := img.Bounds().Max.Y
|
||||
for i := 0; i < n; i++ {
|
||||
setRandomBrightness(color, 255)
|
||||
r := rnd(1, maxradius)
|
||||
img.drawCircle(color, rnd(r, maxx-r), rnd(r, maxy-r), r)
|
||||
}
|
||||
}
|
||||
|
||||
func (img *Image) drawHorizLine(color color.Color, fromX, toX, y int) {
|
||||
for x := fromX; x <= toX; x++ {
|
||||
img.Set(x, y, color)
|
||||
}
|
||||
}
|
||||
|
||||
func (img *Image) drawCircle(color color.Color, x, y, radius int) {
|
||||
f := 1 - radius
|
||||
dfx := 1
|
||||
dfy := -2 * radius
|
||||
xx := 0
|
||||
yy := radius
|
||||
|
||||
img.Set(x, y+radius, color)
|
||||
img.Set(x, y-radius, color)
|
||||
img.drawHorizLine(color, x-radius, x+radius, y)
|
||||
|
||||
for xx < yy {
|
||||
if f >= 0 {
|
||||
yy--
|
||||
dfy += 2
|
||||
f += dfy
|
||||
}
|
||||
xx++
|
||||
dfx += 2
|
||||
f += dfx
|
||||
img.drawHorizLine(color, x-xx, x+xx, y+yy)
|
||||
img.drawHorizLine(color, x-xx, x+xx, y-yy)
|
||||
img.drawHorizLine(color, x-yy, x+yy, y+xx)
|
||||
img.drawHorizLine(color, x-yy, x+yy, y-xx)
|
||||
}
|
||||
}
|
||||
|
||||
func (img *Image) strikeThrough() {
|
||||
r := 0
|
||||
maxx := img.Bounds().Max.X
|
||||
maxy := img.Bounds().Max.Y
|
||||
y := rnd(maxy/3, maxy-maxy/3)
|
||||
for x := 0; x < maxx; x += r {
|
||||
r = rnd(1, img.dotsize/3)
|
||||
y += rnd(-img.dotsize/2, img.dotsize/2)
|
||||
if y <= 0 || y >= maxy {
|
||||
y = rnd(maxy/3, maxy-maxy/3)
|
||||
}
|
||||
img.drawCircle(img.color, x, y, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (img *Image) drawDigit(digit []byte, x, y int) {
|
||||
skf := rand.Float64() * float64(rnd(-maxSkew, maxSkew))
|
||||
xs := float64(x)
|
||||
minr := img.dotsize / 2 // minumum radius
|
||||
maxr := img.dotsize/2 + img.dotsize/4 // maximum radius
|
||||
y += rnd(-minr, minr)
|
||||
for yy := 0; yy < fontHeight; yy++ {
|
||||
for xx := 0; xx < fontWidth; xx++ {
|
||||
if digit[yy*fontWidth+xx] != blackChar {
|
||||
continue
|
||||
}
|
||||
// Introduce random variations.
|
||||
or := rnd(minr, maxr)
|
||||
ox := x + (xx * img.dotsize) + rnd(0, or/2)
|
||||
oy := y + (yy * img.dotsize) + rnd(0, or/2)
|
||||
|
||||
img.drawCircle(img.color, ox, oy, or)
|
||||
}
|
||||
xs += skf
|
||||
x = int(xs)
|
||||
}
|
||||
}
|
||||
|
||||
func setRandomBrightness(c *color.NRGBA, max uint8) {
|
||||
minc := min3(c.R, c.G, c.B)
|
||||
maxc := max3(c.R, c.G, c.B)
|
||||
if maxc > max {
|
||||
return
|
||||
}
|
||||
n := rand.Intn(int(max-maxc)) - int(minc)
|
||||
c.R = uint8(int(c.R) + n)
|
||||
c.G = uint8(int(c.G) + n)
|
||||
c.B = uint8(int(c.B) + n)
|
||||
}
|
||||
|
||||
func min3(x, y, z uint8) (o uint8) {
|
||||
o = x
|
||||
if y < o {
|
||||
o = y
|
||||
}
|
||||
if z < o {
|
||||
o = z
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func max3(x, y, z uint8) (o uint8) {
|
||||
o = x
|
||||
if y > o {
|
||||
o = y
|
||||
}
|
||||
if z > o {
|
||||
o = z
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// rnd returns a random number in range [from, to].
|
||||
func rnd(from, to int) int {
|
||||
//println(to+1-from)
|
||||
return rand.Intn(to+1-from) + from
|
||||
}
|
||||
|
||||
const (
|
||||
// Standard length of uniuri string to achive ~95 bits of entropy.
|
||||
StdLen = 16
|
||||
// Length of uniurl string to achive ~119 bits of entropy, closest
|
||||
// to what can be losslessly converted to UUIDv4 (122 bits).
|
||||
UUIDLen = 20
|
||||
)
|
||||
|
||||
// Standard characters allowed in uniuri string.
|
||||
var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
||||
|
||||
// New returns a new random string of the standard length, consisting of
|
||||
// standard characters.
|
||||
func New() string {
|
||||
return NewLenChars(StdLen, StdChars)
|
||||
}
|
||||
|
||||
// NewLen returns a new random string of the provided length, consisting of
|
||||
// standard characters.
|
||||
func NewLen(length int) string {
|
||||
return NewLenChars(length, StdChars)
|
||||
}
|
||||
|
||||
// NewLenChars returns a new random string of the provided length, consisting
|
||||
// of the provided byte slice of allowed characters (maximum 256).
|
||||
func NewLenChars(length int, chars []byte) string {
|
||||
b := make([]byte, length)
|
||||
r := make([]byte, length+(length/4)) // storage for random bytes.
|
||||
clen := byte(len(chars))
|
||||
maxrb := byte(256 - (256 % len(chars)))
|
||||
i := 0
|
||||
for {
|
||||
if _, err := io.ReadFull(crand.Reader, r); err != nil {
|
||||
panic("error reading from random source: " + err.Error())
|
||||
}
|
||||
for _, c := range r {
|
||||
if c >= maxrb {
|
||||
// Skip this number to avoid modulo bias.
|
||||
continue
|
||||
}
|
||||
b[i] = chars[c%clen]
|
||||
i++
|
||||
if i == length {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func Fetch() (*Image, string) {
|
||||
d := make([]byte, 4)
|
||||
s := NewLen(4)
|
||||
ss := ""
|
||||
d = []byte(s)
|
||||
for v := range d {
|
||||
d[v] %= 10
|
||||
ss += strconv.FormatInt(int64(d[v]), 32)
|
||||
}
|
||||
return NewImage(d, 100, 40), ss
|
||||
}
|
||||
10
app/lea/html2image/ToImage.go
Normal file
10
app/lea/html2image/ToImage.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package html2image
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
)
|
||||
|
||||
func Html2Image(userInfo info.User, note info.Note, content, toPath string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -3,9 +3,20 @@ package memcache
|
||||
import (
|
||||
"github.com/robfig/gomemcache/memcache"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func Set(key string, value map[string]string, expiration int32) {
|
||||
var client *memcache.Client
|
||||
|
||||
// onAppStart后调用
|
||||
func InitMemcache() {
|
||||
client = memcache.New("localhost:11211")
|
||||
}
|
||||
|
||||
//------------
|
||||
// map
|
||||
|
||||
func SetMap(key string, value map[string]string, expiration int32) {
|
||||
// 把value转成byte
|
||||
bytes, _ := json.Marshal(value)
|
||||
if expiration == -1 {
|
||||
@@ -14,7 +25,7 @@ func Set(key string, value map[string]string, expiration int32) {
|
||||
client.Set(&memcache.Item{Key: key, Value: bytes, Expiration: expiration})
|
||||
}
|
||||
|
||||
func Get(key string) map[string]string {
|
||||
func GetMap(key string) map[string]string {
|
||||
item, err := client.Get(key)
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -23,4 +34,33 @@ func Get(key string) map[string]string {
|
||||
m := map[string]string{}
|
||||
json.Unmarshal(item.Value, &m)
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
//------------
|
||||
// string
|
||||
func GetString(key string) string {
|
||||
item, err := client.Get(key)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(item.Value)
|
||||
}
|
||||
func SetString(key string, value string, expiration int32) {
|
||||
if expiration == -1 {
|
||||
expiration = 30 * 24 * 60 * 60 // 30天
|
||||
}
|
||||
client.Set(&memcache.Item{Key: key, Value: []byte(value), Expiration: expiration})
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// int, 是通过转成string来存的
|
||||
|
||||
func GetInt(key string) int {
|
||||
str := GetString(key)
|
||||
i, _ := strconv.Atoi(str)
|
||||
return i
|
||||
}
|
||||
func SetInt(key string, value int, expiration int32) {
|
||||
str := strconv.Itoa(value)
|
||||
SetString(key, str, expiration)
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package memcache
|
||||
|
||||
import (
|
||||
"github.com/robfig/gomemcache/memcache"
|
||||
)
|
||||
|
||||
var client *memcache.Client
|
||||
|
||||
func init() {
|
||||
// client = memcache.New("localhost:11211")
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// toPath 文件保存的目录
|
||||
// 默认是/tmp
|
||||
// 返回文件的完整目录
|
||||
func WriteUrl(url string, toPath string) (path string, ok bool) {
|
||||
func WriteUrl(url string, toPath string) (length int64, newFilename, path string, ok bool) {
|
||||
if url == "" {
|
||||
return;
|
||||
}
|
||||
@@ -22,6 +22,8 @@ func WriteUrl(url string, toPath string) (path string, ok bool) {
|
||||
return;
|
||||
}
|
||||
|
||||
length = int64(len(content))
|
||||
|
||||
// a.html?a=a11&xxx
|
||||
url = trimQueryParams(url)
|
||||
_, ext := SplitFilename(url)
|
||||
@@ -29,13 +31,8 @@ func WriteUrl(url string, toPath string) (path string, ok bool) {
|
||||
toPath = "/tmp"
|
||||
}
|
||||
// dir := filepath.Dir(toPath)
|
||||
newFilename := NewGuid() + ext
|
||||
newFilename = NewGuid() + ext
|
||||
fullPath := toPath + "/" + newFilename
|
||||
/*
|
||||
if err := os.MkdirAll(dir, 0777); err != nil {
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// 写到文件中
|
||||
file, err := os.Create(fullPath)
|
||||
@@ -54,6 +51,7 @@ func WriteUrl(url string, toPath string) (path string, ok bool) {
|
||||
func GetContent(url string) (content []byte, err error) {
|
||||
var resp *http.Response
|
||||
resp, err = http.Get(url)
|
||||
Log(err)
|
||||
if(resp != nil && resp.Body != nil) {
|
||||
defer resp.Body.Close()
|
||||
} else {
|
||||
@@ -65,6 +63,7 @@ func GetContent(url string) (content []byte, err error) {
|
||||
var buf []byte
|
||||
buf, err = ioutil.ReadAll(resp.Body)
|
||||
if(err != nil) {
|
||||
Log(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package lea
|
||||
package route
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "github.com/leanote/leanote/app/service"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// overwite revel RouterFilter
|
||||
// /api/user/Info => ApiUser.Info()
|
||||
var staticPrefix = []string{"/public", "/favicon.ico", "/css", "/js", "/images", "/tinymce", "/upload", "/fonts"}
|
||||
func RouterFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
// 补全controller部分
|
||||
path := c.Request.Request.URL.Path
|
||||
|
||||
// Figure out the Controller/Action
|
||||
var route *revel.RouteMatch = revel.MainRouter.Route(c.Request.Request)
|
||||
if route == nil {
|
||||
@@ -24,12 +30,25 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
|
||||
//----------
|
||||
// life start
|
||||
path := c.Request.Request.URL.Path
|
||||
// Log(c.Request.Request.URL.Host)
|
||||
if strings.HasPrefix(path, "/api") || strings.HasPrefix(path, "api") {
|
||||
route.ControllerName = "Api" + route.ControllerName
|
||||
/*
|
||||
type URL struct {
|
||||
Scheme string
|
||||
Opaque string // encoded opaque data
|
||||
User *Userinfo // username and password information
|
||||
Host string // host or host:port
|
||||
Path string
|
||||
RawQuery string // encoded query values, without '?'
|
||||
Fragment string // fragment for references, without '#'
|
||||
}
|
||||
*/
|
||||
if route.ControllerName != "Static" {
|
||||
// api设置
|
||||
// leanote.com/api/user/get => ApiUser::Get
|
||||
if strings.HasPrefix(path, "/api/") || strings.HasPrefix(path, "api/") {
|
||||
route.ControllerName = "Api" + route.ControllerName
|
||||
}
|
||||
// end
|
||||
}
|
||||
// end
|
||||
|
||||
// Set the action.
|
||||
if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
|
||||
38
app/lea/session/MSession.go
Normal file
38
app/lea/session/MSession.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/lea/memcache"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
)
|
||||
|
||||
// 使用filter
|
||||
// 很巧妙就使用了memcache来处理session
|
||||
// revel的session(cookie)只存sessionId, 其它信息存在memcache中
|
||||
|
||||
func MSessionFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
sessionId := c.Session.Id()
|
||||
|
||||
// 从memcache中得到cache, 赋给session
|
||||
cache := revel.Session(memcache.GetMap(sessionId))
|
||||
|
||||
Log("memcache")
|
||||
LogJ(cache)
|
||||
if cache == nil {
|
||||
cache = revel.Session{}
|
||||
cache.Id()
|
||||
}
|
||||
c.Session = cache
|
||||
|
||||
// Make session vars available in templates as {{.session.xyz}}
|
||||
c.RenderArgs["session"] = c.Session
|
||||
|
||||
fc[0](c, fc[1:])
|
||||
|
||||
// 再把session保存之
|
||||
LogJ(c.Session)
|
||||
memcache.SetMap(sessionId, c.Session, -1)
|
||||
|
||||
// 只留下sessionId
|
||||
c.Session = revel.Session{revel.SESSION_ID_KEY: sessionId}
|
||||
}
|
||||
@@ -1,31 +1,208 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"github.com/robfig/revel"
|
||||
"github.com/leanote/leanote/app/lea/memcache"
|
||||
// . "leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 使用filter
|
||||
// 很巧妙就使用了memcache来处理session
|
||||
// revel的session(cookie)只存sessionId, 其它信息存在memcache中
|
||||
// 主要修改revel的cookie, 设置Domain
|
||||
// 为了使sub domain共享cookie
|
||||
// cookie.domain = leanote.com
|
||||
|
||||
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()
|
||||
// A signed cookie (and thus limited to 4kb in size).
|
||||
// Restriction: Keys may not have a colon in them.
|
||||
type Session map[string]string
|
||||
|
||||
const (
|
||||
SESSION_ID_KEY = "_ID"
|
||||
TIMESTAMP_KEY = "_TS"
|
||||
)
|
||||
|
||||
// expireAfterDuration is the time to live, in seconds, of a session cookie.
|
||||
// It may be specified in config as "session.expires". Values greater than 0
|
||||
// set a persistent cookie with a time to live as specified, and the value 0
|
||||
// sets a session cookie.
|
||||
var expireAfterDuration time.Duration
|
||||
var cookieDomain = "" // life
|
||||
func init() {
|
||||
// Set expireAfterDuration, default to 30 days if no value in config
|
||||
revel.OnAppStart(func() {
|
||||
var err error
|
||||
if expiresString, ok := revel.Config.String("session.expires"); !ok {
|
||||
expireAfterDuration = 30 * 24 * time.Hour
|
||||
} else if expiresString == "session" {
|
||||
expireAfterDuration = 0
|
||||
} else if expireAfterDuration, err = time.ParseDuration(expiresString); err != nil {
|
||||
panic(fmt.Errorf("session.expires invalid: %s", err))
|
||||
}
|
||||
|
||||
cookieDomain, _ = revel.Config.String("cookie.domain")
|
||||
})
|
||||
}
|
||||
|
||||
// Id retrieves from the cookie or creates a time-based UUID identifying this
|
||||
// session.
|
||||
func (s Session) Id() string {
|
||||
if sessionIdStr, ok := s[SESSION_ID_KEY]; ok {
|
||||
return sessionIdStr
|
||||
}
|
||||
c.Session = cache
|
||||
|
||||
buffer := make([]byte, 32)
|
||||
if _, err := rand.Read(buffer); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
s[SESSION_ID_KEY] = hex.EncodeToString(buffer)
|
||||
return s[SESSION_ID_KEY]
|
||||
}
|
||||
|
||||
// getExpiration return a time.Time with the session's expiration date.
|
||||
// If previous session has set to "session", remain it
|
||||
func (s Session) getExpiration() time.Time {
|
||||
if expireAfterDuration == 0 || s[TIMESTAMP_KEY] == "session" {
|
||||
// Expire after closing browser
|
||||
return time.Time{}
|
||||
}
|
||||
return time.Now().Add(expireAfterDuration)
|
||||
}
|
||||
|
||||
// cookie returns an http.Cookie containing the signed session.
|
||||
func (s Session) cookie() *http.Cookie {
|
||||
var sessionValue string
|
||||
ts := s.getExpiration()
|
||||
s[TIMESTAMP_KEY] = getSessionExpirationCookie(ts)
|
||||
for key, value := range s {
|
||||
if strings.ContainsAny(key, ":\x00") {
|
||||
panic("Session keys may not have colons or null bytes")
|
||||
}
|
||||
if strings.Contains(value, "\x00") {
|
||||
panic("Session values may not have null bytes")
|
||||
}
|
||||
sessionValue += "\x00" + key + ":" + value + "\x00"
|
||||
}
|
||||
|
||||
sessionData := url.QueryEscape(sessionValue)
|
||||
cookie := http.Cookie{
|
||||
Name: revel.CookiePrefix + "_SESSION",
|
||||
Value: revel.Sign(sessionData) + "-" + sessionData,
|
||||
Path: "/",
|
||||
HttpOnly: revel.CookieHttpOnly,
|
||||
Secure: revel.CookieSecure,
|
||||
Expires: ts.UTC(),
|
||||
}
|
||||
|
||||
if cookieDomain != "" {
|
||||
cookie.Domain = cookieDomain
|
||||
}
|
||||
|
||||
return &cookie
|
||||
}
|
||||
|
||||
// sessionTimeoutExpiredOrMissing returns a boolean of whether the session
|
||||
// cookie is either not present or present but beyond its time to live; i.e.,
|
||||
// whether there is not a valid session.
|
||||
func sessionTimeoutExpiredOrMissing(session Session) bool {
|
||||
if exp, present := session[TIMESTAMP_KEY]; !present {
|
||||
return true
|
||||
} else if exp == "session" {
|
||||
return false
|
||||
} else if expInt, _ := strconv.Atoi(exp); int64(expInt) < time.Now().Unix() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getSessionFromCookie returns a Session struct pulled from the signed
|
||||
// session cookie.
|
||||
func getSessionFromCookie(cookie *http.Cookie) Session {
|
||||
session := make(Session)
|
||||
|
||||
// Separate the data from the signature.
|
||||
hyphen := strings.Index(cookie.Value, "-")
|
||||
if hyphen == -1 || hyphen >= len(cookie.Value)-1 {
|
||||
return session
|
||||
}
|
||||
sig, data := cookie.Value[:hyphen], cookie.Value[hyphen+1:]
|
||||
|
||||
// Verify the signature.
|
||||
if !revel.Verify(data, sig) {
|
||||
revel.INFO.Println("Session cookie signature failed")
|
||||
return session
|
||||
}
|
||||
|
||||
revel.ParseKeyValueCookie(data, func(key, val string) {
|
||||
session[key] = val
|
||||
})
|
||||
|
||||
if sessionTimeoutExpiredOrMissing(session) {
|
||||
session = make(Session)
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
// SessionFilter is a Revel Filter that retrieves and sets the session cookie.
|
||||
// Within Revel, it is available as a Session attribute on Controller instances.
|
||||
// The name of the Session cookie is set as CookiePrefix + "_SESSION".
|
||||
func SessionFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
session := restoreSession(c.Request.Request)
|
||||
// c.Session, 重新生成一个revel.Session给controller!!!
|
||||
// Log("sessoin--------")
|
||||
// LogJ(session)
|
||||
revelSession := revel.Session(session) // 强制转换 还是同一个对象, 但有个问题, 这样Session.Id()方法是用revel的了
|
||||
c.Session = revelSession
|
||||
// 生成sessionId
|
||||
c.Session.Id()
|
||||
sessionWasEmpty := len(c.Session) == 0
|
||||
|
||||
// Make session vars available in templates as {{.session.xyz}}
|
||||
c.RenderArgs["session"] = c.Session
|
||||
|
||||
fc[0](c, fc[1:])
|
||||
|
||||
// 再把session保存之
|
||||
memcache.Set(sessionId, c.Session, -1)
|
||||
|
||||
// 只留下sessionId
|
||||
c.Session = revel.Session{revel.SESSION_ID_KEY: sessionId}
|
||||
|
||||
// Store the signed session if it could have changed.
|
||||
if len(c.Session) > 0 || !sessionWasEmpty {
|
||||
// 转换成lea.Session
|
||||
session = Session(c.Session)
|
||||
c.SetCookie(session.cookie())
|
||||
}
|
||||
}
|
||||
|
||||
// restoreSession returns either the current session, retrieved from the
|
||||
// session cookie, or a new session.
|
||||
func restoreSession(req *http.Request) Session {
|
||||
cookie, err := req.Cookie(revel.CookiePrefix + "_SESSION")
|
||||
if err != nil {
|
||||
return make(Session)
|
||||
} else {
|
||||
return getSessionFromCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// getSessionExpirationCookie retrieves the cookie's time to live as a
|
||||
// string of either the number of seconds, for a persistent cookie, or
|
||||
// "session".
|
||||
func getSessionExpirationCookie(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return "session"
|
||||
}
|
||||
return strconv.FormatInt(t.Unix(), 10)
|
||||
}
|
||||
|
||||
// SetNoExpiration sets session to expire when browser session ends
|
||||
func (s Session) SetNoExpiration() {
|
||||
s[TIMESTAMP_KEY] = "session"
|
||||
}
|
||||
|
||||
// SetDefaultExpiration sets session to expire after default duration
|
||||
func (s Session) SetDefaultExpiration() {
|
||||
delete(s, TIMESTAMP_KEY)
|
||||
}
|
||||
@@ -39,6 +39,7 @@ var cmdPath = "/usr/local/bin/uglifyjs"
|
||||
|
||||
func cmdError(err error) {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Fprintf(os.Stderr, "The command failed to perform: %s (Command: %s, Arguments: %s)", err, "", "")
|
||||
} else {
|
||||
fmt.Println("OK")
|
||||
@@ -63,6 +64,7 @@ func combineJs() {
|
||||
|
||||
for _, js := range jss {
|
||||
to := base + js + "-min.js"
|
||||
fmt.Println(to)
|
||||
compressJs(js)
|
||||
|
||||
// 每个压缩后的文件放入之
|
||||
@@ -85,6 +87,7 @@ func dev() {
|
||||
"notebook.js": "notebook-min.js",
|
||||
"share.js": "share-min.js",
|
||||
"tag.js": "tag-min.js",
|
||||
"main.js": "main-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",
|
||||
@@ -108,7 +111,8 @@ func tinymce() {
|
||||
// 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()
|
||||
c, err := cmd.CombinedOutput()
|
||||
fmt.Println(string(c))
|
||||
cmdError(err)
|
||||
}
|
||||
|
||||
@@ -116,11 +120,12 @@ func main() {
|
||||
dev();
|
||||
|
||||
// 其它零散的需要压缩的js
|
||||
otherJss := []string{"tinymce/tinymce", "js/app/page", "js/contextmenu/jquery.contextmenu",
|
||||
otherJss := []string{"tinymce/tinymce", "js/main", "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/local/Markdown.local.en",
|
||||
"mdeditor/editor/pagedown/Markdown.Editor",
|
||||
"mdeditor/editor/pagedown/Markdown.Sanitizer",
|
||||
"mdeditor/editor/pagedown/Markdown.Converter",
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/revel/revel"
|
||||
// "github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// 登录与权限
|
||||
@@ -16,7 +17,8 @@ type AuthService struct {
|
||||
|
||||
// pwd已md5了
|
||||
func (this *AuthService) Login(emailOrUsername, pwd string) info.User {
|
||||
return userService.LoginGetUserInfo(emailOrUsername, Md5(pwd))
|
||||
userInfo := userService.LoginGetUserInfo(emailOrUsername, Md5(pwd))
|
||||
return userInfo
|
||||
}
|
||||
|
||||
// 注册
|
||||
@@ -56,20 +58,30 @@ func (this *AuthService) register(user info.User) (bool, string) {
|
||||
email := user.Email
|
||||
|
||||
// 添加leanote -> 该用户的共享
|
||||
leanoteUserId, _ := revel.Config.String("register.sharedUserId"); // "5368c1aa99c37b029d000001";
|
||||
nk1, _ := revel.Config.String("register.sharedUserShareNotebookId"); // 5368c1aa99c37b029d000002" // leanote
|
||||
welcomeNoteId, _ := revel.Config.String("register.welcomeNoteId") // "5368c1b919807a6f95000000" // 欢迎来到leanote
|
||||
|
||||
if leanoteUserId != "" && nk1 != "" && welcomeNoteId != "" {
|
||||
shareService.AddShareNotebook(nk1, 0, leanoteUserId, email);
|
||||
shareService.AddShareNote(welcomeNoteId, 0, leanoteUserId, email);
|
||||
registerSharedUserId := configService.GetGlobalStringConfig("registerSharedUserId")
|
||||
if(registerSharedUserId != "") {
|
||||
registerSharedNotebooks := configService.GetGlobalArrMapConfig("registerSharedNotebooks")
|
||||
registerSharedNotes := configService.GetGlobalArrMapConfig("registerSharedNotes")
|
||||
registerCopyNoteIds := configService.GetGlobalArrayConfig("registerCopyNoteIds")
|
||||
|
||||
// 将welcome copy给我
|
||||
note := noteService.CopySharedNote(welcomeNoteId, title2Id["life"].Hex(), leanoteUserId, user.UserId.Hex());
|
||||
// 添加共享笔记本
|
||||
for _, notebook := range registerSharedNotebooks {
|
||||
perm, _ := strconv.Atoi(notebook["perm"])
|
||||
shareService.AddShareNotebook(notebook["notebookId"], perm, registerSharedUserId, email);
|
||||
}
|
||||
|
||||
// 公开为博客
|
||||
noteUpdate := bson.M{"IsBlog": true}
|
||||
noteService.UpdateNote(user.UserId.Hex(), user.UserId.Hex(), note.NoteId.Hex(), noteUpdate)
|
||||
// 添加共享笔记
|
||||
for _, note := range registerSharedNotes {
|
||||
perm, _ := strconv.Atoi(note["perm"])
|
||||
shareService.AddShareNote(note["noteId"], perm, registerSharedUserId, email);
|
||||
}
|
||||
|
||||
// 复制笔记
|
||||
for _, noteId := range registerCopyNoteIds {
|
||||
note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex());
|
||||
noteUpdate := bson.M{"IsBlog": true}
|
||||
noteService.UpdateNote(user.UserId.Hex(), user.UserId.Hex(), note.NoteId.Hex(), noteUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
//---------------
|
||||
|
||||
@@ -3,10 +3,12 @@ package service
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "time"
|
||||
// "sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// blog
|
||||
@@ -207,15 +209,25 @@ func (this *BlogService) ListAllBlogs(tag string, keywords string, isRecommend b
|
||||
|
||||
//------------------------
|
||||
// 博客设置
|
||||
func (this *BlogService) fixUserBlog(userBlog *info.UserBlog) {
|
||||
/*
|
||||
if userBlog.Title == "" {
|
||||
userInfo := userService.GetUserInfo(userBlog)
|
||||
userBlog.Title = userInfo.Username + " 's Blog"
|
||||
}
|
||||
*/
|
||||
|
||||
// Logo路径问题, 有些有http: 有些没有
|
||||
Log(userBlog.Logo)
|
||||
if userBlog.Logo != "" && !strings.HasPrefix(userBlog.Logo, "http") {
|
||||
userBlog.Logo = strings.Trim(userBlog.Logo, "/")
|
||||
userBlog.Logo = siteUrl + "/" + userBlog.Logo
|
||||
}
|
||||
}
|
||||
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 + " 的博客"
|
||||
}
|
||||
|
||||
this.fixUserBlog(&userBlog)
|
||||
return userBlog
|
||||
}
|
||||
|
||||
@@ -225,7 +237,8 @@ func (this *BlogService) UpdateUserBlog(userBlog info.UserBlog) bool {
|
||||
}
|
||||
// 修改之UserBlogBase
|
||||
func (this *BlogService) UpdateUserBlogBase(userId string, userBlog info.UserBlogBase) bool {
|
||||
return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
|
||||
ok := db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
|
||||
return ok
|
||||
}
|
||||
func (this *BlogService) UpdateUserBlogComment(userId string, userBlog info.UserBlogComment) bool {
|
||||
return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
|
||||
@@ -234,10 +247,325 @@ func (this *BlogService) UpdateUserBlogStyle(userId string, userBlog info.UserBl
|
||||
return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
|
||||
}
|
||||
|
||||
//------------
|
||||
|
||||
//---------------------
|
||||
// 后台管理
|
||||
|
||||
// 推荐博客
|
||||
func (this *BlogService) SetRecommend(noteId string, isRecommend bool) bool {
|
||||
return db.UpdateByQField(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsBlog": true}, "IsRecommend", isRecommend)
|
||||
data := bson.M{"IsRecommend": isRecommend}
|
||||
if isRecommend {
|
||||
data["RecommendTime"] = time.Now()
|
||||
}
|
||||
return db.UpdateByQMap(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsBlog": true}, data)
|
||||
}
|
||||
|
||||
//----------------------
|
||||
// 博客社交, 评论
|
||||
|
||||
// 返回所有liked用户, bool是否还有
|
||||
func (this *BlogService) ListLikedUsers(noteId string, isAll bool) ([]info.User, bool) {
|
||||
// 默认前5
|
||||
pageSize := 5
|
||||
skipNum, sortFieldR := parsePageAndSort(1, pageSize, "CreatedTime", false)
|
||||
|
||||
likes := []info.BlogLike{}
|
||||
query := bson.M{"NoteId": bson.ObjectIdHex(noteId)}
|
||||
q := db.BlogLikes.Find(query);
|
||||
|
||||
// 总记录数
|
||||
count, _ := q.Count()
|
||||
if count == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if isAll {
|
||||
q.Sort(sortFieldR).Skip(skipNum).Limit(pageSize).All(&likes)
|
||||
} else {
|
||||
q.Sort(sortFieldR).All(&likes)
|
||||
}
|
||||
|
||||
// 得到所有userIds
|
||||
userIds := make([]bson.ObjectId, len(likes))
|
||||
for i, like := range likes {
|
||||
userIds[i] = like.UserId
|
||||
}
|
||||
// 得到用户信息
|
||||
userMap := userService.MapUserInfoAndBlogInfosByUserIds(userIds)
|
||||
|
||||
users := make([]info.User, len(likes));
|
||||
for i, like := range likes {
|
||||
users[i] = userMap[like.UserId]
|
||||
}
|
||||
|
||||
return users, count > pageSize
|
||||
}
|
||||
|
||||
func (this *BlogService) IsILikeIt(noteId, userId string) bool {
|
||||
if userId == "" {
|
||||
return false
|
||||
}
|
||||
if db.Has(db.BlogLikes, bson.M{"NoteId": bson.ObjectIdHex(noteId), "UserId": bson.ObjectIdHex(userId)}) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 阅读次数统计+1
|
||||
func (this *BlogService) IncReadNum(noteId string) bool {
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.IsBlog {
|
||||
return db.Update(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, bson.M{"$inc": bson.M{"ReadNum": 1}})
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 点赞
|
||||
// retun ok , isLike
|
||||
func (this *BlogService) LikeBlog(noteId, userId string) (ok bool, isLike bool) {
|
||||
ok = false
|
||||
isLike = false
|
||||
if noteId == "" || userId == "" {
|
||||
return
|
||||
}
|
||||
// 判断是否点过赞, 如果点过那么取消点赞
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if !note.IsBlog /*|| note.UserId.Hex() == userId */{
|
||||
return
|
||||
}
|
||||
|
||||
noteIdO := bson.ObjectIdHex(noteId)
|
||||
userIdO := bson.ObjectIdHex(userId)
|
||||
var n int
|
||||
if !db.Has(db.BlogLikes, bson.M{"NoteId": noteIdO, "UserId": userIdO}) {
|
||||
n = 1
|
||||
// 添加之
|
||||
db.Insert(db.BlogLikes, info.BlogLike{LikeId: bson.NewObjectId(), NoteId: noteIdO, UserId: userIdO, CreatedTime: time.Now()})
|
||||
isLike = true
|
||||
} else {
|
||||
// 已点过, 那么删除之
|
||||
n = -1
|
||||
db.Delete(db.BlogLikes, bson.M{"NoteId": noteIdO, "UserId": userIdO})
|
||||
isLike = false
|
||||
}
|
||||
ok = db.Update(db.Notes, bson.M{"_id": noteIdO}, bson.M{"$inc": bson.M{"LikeNum": n}})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 评论
|
||||
// 在noteId博客下userId 给toUserId评论content
|
||||
// commentId可为空(针对某条评论评论)
|
||||
func (this *BlogService) Comment(noteId, toCommentId, userId, content string) (bool, info.BlogComment) {
|
||||
var comment info.BlogComment
|
||||
if content == "" {
|
||||
return false, comment
|
||||
}
|
||||
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if !note.IsBlog {
|
||||
return false, comment
|
||||
}
|
||||
|
||||
comment = info.BlogComment{CommentId: bson.NewObjectId(),
|
||||
NoteId: bson.ObjectIdHex(noteId),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
Content: content,
|
||||
CreatedTime: time.Now(),
|
||||
}
|
||||
var comment2 = info.BlogComment{}
|
||||
if toCommentId != "" {
|
||||
comment2 = info.BlogComment{}
|
||||
db.Get(db.BlogComments, toCommentId, &comment2)
|
||||
if comment2.CommentId != "" {
|
||||
comment.ToCommentId = comment2.CommentId
|
||||
comment.ToUserId = comment2.UserId
|
||||
}
|
||||
} else {
|
||||
// comment.ToUserId = note.UserId
|
||||
}
|
||||
ok := db.Insert(db.BlogComments, comment)
|
||||
if ok {
|
||||
// 评论+1
|
||||
db.Update(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, bson.M{"$inc": bson.M{"CommentNum": 1}})
|
||||
}
|
||||
|
||||
if userId != note.UserId.Hex() || toCommentId != "" {
|
||||
go func() {
|
||||
this.sendEmail(note, comment2, userId, content);
|
||||
}()
|
||||
}
|
||||
|
||||
return ok, comment
|
||||
}
|
||||
|
||||
// 发送email
|
||||
func (this *BlogService) sendEmail(note info.Note, comment info.BlogComment, userId, content string) {
|
||||
emailService.SendCommentEmail(note, comment, userId, content);
|
||||
/*
|
||||
toUserId := note.UserId.Hex()
|
||||
// title := "评论提醒"
|
||||
|
||||
// 表示回复回复的内容, 那么发送给之前回复的
|
||||
if comment.CommentId != "" {
|
||||
toUserId = comment.UserId.Hex()
|
||||
}
|
||||
toUserInfo := userService.GetUserInfo(toUserId)
|
||||
sendUserInfo := userService.GetUserInfo(userId)
|
||||
|
||||
subject := note.Title + " 收到 " + sendUserInfo.Username + " 的评论";
|
||||
if comment.CommentId != "" {
|
||||
subject = "您在 " + note.Title + " 发表的评论收到 " + sendUserInfo.Username;
|
||||
if userId == note.UserId.Hex() {
|
||||
subject += "(作者)";
|
||||
}
|
||||
subject += " 的评论";
|
||||
}
|
||||
|
||||
body := "{header}<b>评论内容</b>: <br /><blockquote>" + content + "</blockquote>";
|
||||
href := "http://"+ configService.GetBlogDomain() + "/view/" + note.NoteId.Hex()
|
||||
body += "<br /><b>博客链接</b>: <a href='" + href + "'>" + href + "</a>{footer}";
|
||||
|
||||
emailService.SendEmail(toUserInfo.Email, subject, body)
|
||||
*/
|
||||
}
|
||||
|
||||
// 作者(或管理员)可以删除所有评论
|
||||
// 自己可以删除评论
|
||||
func (this *BlogService) DeleteComment(noteId, commentId, userId string) bool {
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if !note.IsBlog {
|
||||
return false
|
||||
}
|
||||
|
||||
comment := info.BlogComment{}
|
||||
db.Get(db.BlogComments, commentId, &comment)
|
||||
|
||||
if comment.CommentId == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if userId == adminUserId || note.UserId.Hex() == userId || comment.UserId.Hex() == userId {
|
||||
if db.Delete(db.BlogComments, bson.M{"_id": bson.ObjectIdHex(commentId)}) {
|
||||
// 评论-1
|
||||
db.Update(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, bson.M{"$inc": bson.M{"CommentNum": -1}})
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 点赞/取消赞
|
||||
func (this *BlogService) LikeComment(commentId, userId string) (ok bool, isILike bool, num int) {
|
||||
ok = false
|
||||
isILike = false
|
||||
num = 0
|
||||
comment := info.BlogComment{}
|
||||
|
||||
db.Get(db.BlogComments, commentId, &comment)
|
||||
|
||||
var n int
|
||||
if comment.LikeUserIds != nil && len(comment.LikeUserIds) > 0 && InArray(comment.LikeUserIds, userId) {
|
||||
n = -1
|
||||
// 从点赞名单删除
|
||||
db.Update(db.BlogComments, bson.M{"_id": bson.ObjectIdHex(commentId)},
|
||||
bson.M{"$pull": bson.M{"LikeUserIds": userId}})
|
||||
isILike = false
|
||||
} else {
|
||||
n = 1
|
||||
// 添加之
|
||||
db.Update(db.BlogComments, bson.M{"_id": bson.ObjectIdHex(commentId)},
|
||||
bson.M{"$push": bson.M{"LikeUserIds": userId}})
|
||||
isILike = true
|
||||
}
|
||||
|
||||
if comment.LikeUserIds == nil {
|
||||
num = 0
|
||||
} else {
|
||||
num = len(comment.LikeUserIds) + n
|
||||
}
|
||||
|
||||
ok = db.Update(db.BlogComments, bson.M{"_id": bson.ObjectIdHex(commentId)},
|
||||
bson.M{"$set": bson.M{"LikeNum": num}})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 评论列表
|
||||
// userId主要是显示userId是否点过某评论的赞
|
||||
// 还要获取用户信息
|
||||
func (this *BlogService) ListComments(userId, noteId string, page, pageSize int) (info.Page, []info.BlogCommentPublic, map[string]info.User) {
|
||||
pageInfo := info.Page{CurPage: page}
|
||||
|
||||
comments2 := []info.BlogComment{}
|
||||
|
||||
skipNum, sortFieldR := parsePageAndSort(page, pageSize, "CreatedTime", false)
|
||||
|
||||
query := bson.M{"NoteId": bson.ObjectIdHex(noteId)}
|
||||
q := db.BlogComments.Find(query);
|
||||
|
||||
// 总记录数
|
||||
count, _ := q.Count()
|
||||
q.Sort(sortFieldR).Skip(skipNum).Limit(pageSize).All(&comments2)
|
||||
|
||||
if(len(comments2) == 0) {
|
||||
return pageInfo, nil, nil
|
||||
}
|
||||
|
||||
comments := make([]info.BlogCommentPublic, len(comments2))
|
||||
// 我是否点过赞呢?
|
||||
for i, comment := range comments2 {
|
||||
comments[i].BlogComment = comment
|
||||
if comment.LikeNum > 0 && comment.LikeUserIds != nil && len(comment.LikeUserIds) > 0 && InArray(comment.LikeUserIds, userId) {
|
||||
comments[i].IsILikeIt = true
|
||||
}
|
||||
}
|
||||
|
||||
note := noteService.GetNoteById(noteId);
|
||||
|
||||
// 得到用户信息
|
||||
userIdsMap := map[bson.ObjectId]bool{note.UserId: true}
|
||||
for _, comment := range comments {
|
||||
userIdsMap[comment.UserId] = true
|
||||
if comment.ToUserId != "" { // 可能为空
|
||||
userIdsMap[comment.ToUserId] = true
|
||||
}
|
||||
}
|
||||
userIds := make([]bson.ObjectId, len(userIdsMap))
|
||||
i := 0
|
||||
for userId, _ := range userIdsMap {
|
||||
userIds[i] = userId
|
||||
i++
|
||||
}
|
||||
|
||||
// 得到用户信息
|
||||
userMap := userService.MapUserInfoByUserIds(userIds)
|
||||
userMap2 := make(map[string]info.User, len(userMap))
|
||||
for userId, v := range userMap {
|
||||
userMap2[userId.Hex()] = v
|
||||
}
|
||||
|
||||
pageInfo = info.NewPage(page, pageSize, count, nil)
|
||||
|
||||
return pageInfo, comments, userMap2
|
||||
}
|
||||
|
||||
// 举报
|
||||
func (this *BlogService) Report(noteId, commentId, reason, userId string) (bool) {
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if !note.IsBlog {
|
||||
return false
|
||||
}
|
||||
|
||||
report := info.Report{ReportId: bson.NewObjectId(),
|
||||
NoteId: bson.ObjectIdHex(noteId),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
Reason: reason,
|
||||
CreatedTime: time.Now(),
|
||||
}
|
||||
if commentId != "" {
|
||||
report.CommentId = bson.ObjectIdHex(commentId)
|
||||
}
|
||||
return db.Insert(db.Reports, report)
|
||||
}
|
||||
@@ -2,40 +2,38 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"github.com/revel/revel"
|
||||
"time"
|
||||
"os"
|
||||
"os/exec"
|
||||
"fmt"
|
||||
"strings"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// 配置服务
|
||||
// 只是全局的, 用户的配置没有
|
||||
type ConfigService struct {
|
||||
// 全局的
|
||||
GlobalAllConfigs map[string]interface{}
|
||||
GlobalStringConfigs map[string]string
|
||||
GlobalArrayConfigs map[string][]string
|
||||
|
||||
// 两种配置, 用户自己的
|
||||
UserStringConfigs map[string]string
|
||||
UserArrayConfigs map[string][]string
|
||||
|
||||
// 合并之后的
|
||||
AllStringConfigs map[string]string
|
||||
AllArrayConfigs map[string][]string
|
||||
GlobalMapConfigs map[string]map[string]string
|
||||
GlobalArrMapConfigs map[string][]map[string]string
|
||||
}
|
||||
|
||||
var adminUserId = ""
|
||||
|
||||
// appStart时 将全局的配置从数据库中得到作为全局
|
||||
func (this *ConfigService) InitGlobalConfigs() bool {
|
||||
this.GlobalAllConfigs = map[string]interface{}{}
|
||||
this.GlobalStringConfigs = map[string]string{}
|
||||
this.GlobalArrayConfigs = map[string][]string{}
|
||||
|
||||
this.UserStringConfigs = map[string]string{}
|
||||
this.UserArrayConfigs = map[string][]string{}
|
||||
|
||||
this.AllStringConfigs = map[string]string{}
|
||||
this.AllArrayConfigs = map[string][]string{}
|
||||
this.GlobalMapConfigs = map[string]map[string]string{}
|
||||
this.GlobalArrMapConfigs = map[string][]map[string]string{}
|
||||
|
||||
adminUsername, _ := revel.Config.String("adminUsername")
|
||||
if adminUsername == "" {
|
||||
@@ -48,84 +46,95 @@ func (this *ConfigService) InitGlobalConfigs() bool {
|
||||
}
|
||||
adminUserId = userInfo.UserId.Hex()
|
||||
|
||||
configs := info.Config{}
|
||||
db.Get2(db.Configs, userInfo.UserId, &configs)
|
||||
configs := []info.Config{}
|
||||
db.ListByQ(db.Configs, bson.M{"UserId": userInfo.UserId}, &configs)
|
||||
|
||||
if configs.UserId == "" {
|
||||
db.Insert(db.Configs, info.Config{UserId: userInfo.UserId, StringConfigs: map[string]string{}, ArrayConfigs: map[string][]string{}})
|
||||
}
|
||||
|
||||
this.GlobalStringConfigs = configs.StringConfigs;
|
||||
this.GlobalArrayConfigs = configs.ArrayConfigs;
|
||||
|
||||
// 复制到所有配置上
|
||||
for key, value := range this.GlobalStringConfigs {
|
||||
this.AllStringConfigs[key] = value
|
||||
}
|
||||
for key, value := range this.GlobalArrayConfigs {
|
||||
this.AllArrayConfigs[key] = value
|
||||
for _, config := range configs {
|
||||
if config.IsArr {
|
||||
this.GlobalArrayConfigs[config.Key] = config.ValueArr
|
||||
this.GlobalAllConfigs[config.Key] = config.ValueArr
|
||||
} else if config.IsMap {
|
||||
this.GlobalMapConfigs[config.Key] = config.ValueMap
|
||||
this.GlobalAllConfigs[config.Key] = config.ValueMap
|
||||
} else if config.IsArrMap {
|
||||
this.GlobalArrMapConfigs[config.Key] = config.ValueArrMap
|
||||
this.GlobalAllConfigs[config.Key] = config.ValueArrMap
|
||||
} else {
|
||||
this.GlobalStringConfigs[config.Key] = config.ValueStr
|
||||
this.GlobalAllConfigs[config.Key] = config.ValueStr
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 用户登录后获取用户自定义的配置, 并将所有的配置都用上
|
||||
func (this *ConfigService) InitUserConfigs(userId string) bool {
|
||||
configs := info.Config{}
|
||||
db.Get(db.Configs, userId, &configs)
|
||||
|
||||
if configs.UserId == "" {
|
||||
db.Insert(db.Configs, info.Config{UserId: bson.ObjectIdHex(userId), StringConfigs: map[string]string{}, ArrayConfigs: map[string][]string{}})
|
||||
// 通用方法
|
||||
func (this *ConfigService) updateGlobalConfig(userId, key string, value interface{}, isArr, isMap, isArrMap bool) bool {
|
||||
// 判断是否存在
|
||||
if _, ok := this.GlobalAllConfigs[key]; !ok {
|
||||
// 需要添加
|
||||
config := info.Config{ConfigId: bson.NewObjectId(),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
Key: key,
|
||||
IsArr: isArr,
|
||||
IsMap: isMap,
|
||||
IsArrMap: isArrMap,
|
||||
UpdatedTime: time.Now(),
|
||||
}
|
||||
if(isArr) {
|
||||
v, _ := value.([]string)
|
||||
config.ValueArr = v
|
||||
this.GlobalArrayConfigs[key] = v
|
||||
} else if isMap {
|
||||
v, _ := value.(map[string]string)
|
||||
config.ValueMap = v
|
||||
this.GlobalMapConfigs[key] = v
|
||||
} else if isArrMap {
|
||||
v, _ := value.([]map[string]string)
|
||||
config.ValueArrMap = v
|
||||
this.GlobalArrMapConfigs[key] = v
|
||||
} else {
|
||||
v, _ := value.(string)
|
||||
config.ValueStr = v
|
||||
this.GlobalStringConfigs[key] = v
|
||||
}
|
||||
return db.Insert(db.Configs, config)
|
||||
} else {
|
||||
i := bson.M{"UpdatedTime": time.Now()}
|
||||
this.GlobalAllConfigs[key] = value
|
||||
if(isArr) {
|
||||
v, _ := value.([]string)
|
||||
i["ValueArr"] = v
|
||||
this.GlobalArrayConfigs[key] = v
|
||||
} else if isMap {
|
||||
v, _ := value.(map[string]string)
|
||||
i["ValueMap"] = v
|
||||
this.GlobalMapConfigs[key] = v
|
||||
} else if isArrMap {
|
||||
v, _ := value.([]map[string]string)
|
||||
i["ValueArrMap"] = v
|
||||
this.GlobalArrMapConfigs[key] = v
|
||||
} else {
|
||||
v, _ := value.(string)
|
||||
i["ValueStr"] = v
|
||||
this.GlobalStringConfigs[key] = v
|
||||
}
|
||||
return db.UpdateByQMap(db.Configs, bson.M{"UserId": bson.ObjectIdHex(userId), "Key": key}, i)
|
||||
}
|
||||
|
||||
this.UserStringConfigs = configs.StringConfigs;
|
||||
this.UserArrayConfigs = configs.ArrayConfigs;
|
||||
|
||||
// 合并配置
|
||||
for key, value := range this.UserStringConfigs {
|
||||
this.AllStringConfigs[key] = value
|
||||
}
|
||||
for key, value := range this.UserArrayConfigs {
|
||||
this.AllArrayConfigs[key] = value
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
func (this *ConfigService) GetStringConfig(key string) string {
|
||||
return this.AllStringConfigs[key]
|
||||
}
|
||||
func (this *ConfigService) GetArrayConfig(key string) []string {
|
||||
arr := this.AllArrayConfigs[key]
|
||||
if arr == nil {
|
||||
return []string{}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// 更新用户配置
|
||||
func (this *ConfigService) UpdateUserStringConfig(userId, key string, value string) bool {
|
||||
this.UserStringConfigs[key] = value
|
||||
this.AllStringConfigs[key] = value
|
||||
if userId == adminUserId {
|
||||
this.GlobalStringConfigs[key] = value
|
||||
}
|
||||
|
||||
// 保存到数据库中
|
||||
return db.UpdateByQMap(db.Configs, bson.M{"_id": bson.ObjectIdHex(userId)},
|
||||
bson.M{"StringConfigs": this.UserStringConfigs, "UpdatedTime": time.Now()})
|
||||
func (this *ConfigService) UpdateGlobalStringConfig(userId, key string, value string) bool {
|
||||
return this.updateGlobalConfig(userId, key, value, false, false, false)
|
||||
}
|
||||
func (this *ConfigService) UpdateUserArrayConfig(userId, key string, value []string) bool {
|
||||
this.UserArrayConfigs[key] = value
|
||||
this.AllArrayConfigs[key] = value
|
||||
if userId == adminUserId {
|
||||
this.GlobalArrayConfigs[key] = value
|
||||
}
|
||||
|
||||
// 保存到数据库中
|
||||
return db.UpdateByQMap(db.Configs, bson.M{"_id": bson.ObjectIdHex(userId)},
|
||||
bson.M{"ArrayConfigs": this.UserArrayConfigs, "UpdatedTime": time.Now()})
|
||||
func (this *ConfigService) UpdateGlobalArrayConfig(userId, key string, value []string) bool {
|
||||
return this.updateGlobalConfig(userId, key, value, true, false, false)
|
||||
}
|
||||
func (this *ConfigService) UpdateGlobalMapConfig(userId, key string, value map[string]string) bool {
|
||||
return this.updateGlobalConfig(userId, key, value, false, true, false)
|
||||
}
|
||||
func (this *ConfigService) UpdateGlobalArrMapConfig(userId, key string, value []map[string]string) bool {
|
||||
return this.updateGlobalConfig(userId, key, value, false, false, true)
|
||||
}
|
||||
|
||||
// 获取全局配置, 博客平台使用
|
||||
@@ -138,4 +147,391 @@ func (this *ConfigService) GetGlobalArrayConfig(key string) []string {
|
||||
return []string{}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
}
|
||||
func (this *ConfigService) GetGlobalMapConfig(key string) map[string]string {
|
||||
m := this.GlobalMapConfigs[key]
|
||||
if m == nil {
|
||||
return map[string]string{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
func (this *ConfigService) GetGlobalArrMapConfig(key string) []map[string]string {
|
||||
m := this.GlobalArrMapConfigs[key]
|
||||
if m == nil {
|
||||
return []map[string]string{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
//-------
|
||||
// 修改共享笔记的配置
|
||||
func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
registerSharedNotebookPerms, registerSharedNotePerms []int,
|
||||
registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds []string) (ok bool, msg string) {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprint(err)
|
||||
}
|
||||
}();
|
||||
|
||||
// 用户是否存在?
|
||||
if registerSharedUserId == "" {
|
||||
ok = true
|
||||
msg = "share userId is blank, So it share nothing to register"
|
||||
this.UpdateGlobalStringConfig(adminUserId, "registerSharedUserId", "")
|
||||
return
|
||||
} else {
|
||||
user := userService.GetUserInfo(registerSharedUserId)
|
||||
if user.UserId == "" {
|
||||
ok = false
|
||||
msg = "no such user: " + registerSharedUserId
|
||||
return
|
||||
} else {
|
||||
this.UpdateGlobalStringConfig(adminUserId, "registerSharedUserId", registerSharedUserId)
|
||||
}
|
||||
}
|
||||
|
||||
notebooks := []map[string]string{}
|
||||
// 共享笔记本
|
||||
if len(registerSharedNotebookIds) > 0 {
|
||||
for i := 0; i < len(registerSharedNotebookIds); i++ {
|
||||
// 判断笔记本是否存在
|
||||
notebookId := registerSharedNotebookIds[i]
|
||||
if notebookId == "" {
|
||||
continue
|
||||
}
|
||||
notebook := notebookService.GetNotebook(notebookId, registerSharedUserId)
|
||||
if notebook.NotebookId == "" {
|
||||
ok = false
|
||||
msg = "The user has no such notebook: " + notebookId
|
||||
return
|
||||
} else {
|
||||
perm := "0";
|
||||
if registerSharedNotebookPerms[i] == 1 {
|
||||
perm = "1"
|
||||
}
|
||||
notebooks = append(notebooks, map[string]string{"notebookId": notebookId, "perm": perm})
|
||||
}
|
||||
}
|
||||
}
|
||||
this.UpdateGlobalArrMapConfig(adminUserId, "registerSharedNotebooks", notebooks)
|
||||
|
||||
notes := []map[string]string{}
|
||||
// 共享笔记
|
||||
if len(registerSharedNoteIds) > 0 {
|
||||
for i := 0; i < len(registerSharedNoteIds); i++ {
|
||||
// 判断笔记本是否存在
|
||||
noteId := registerSharedNoteIds[i]
|
||||
if noteId == "" {
|
||||
continue
|
||||
}
|
||||
note := noteService.GetNote(noteId, registerSharedUserId)
|
||||
if note.NoteId == "" {
|
||||
ok = false
|
||||
msg = "The user has no such note: " + noteId
|
||||
return
|
||||
} else {
|
||||
perm := "0";
|
||||
if registerSharedNotePerms[i] == 1 {
|
||||
perm = "1"
|
||||
}
|
||||
notes = append(notes, map[string]string{"noteId": noteId, "perm": perm})
|
||||
}
|
||||
}
|
||||
}
|
||||
this.UpdateGlobalArrMapConfig(adminUserId, "registerSharedNotes", notes)
|
||||
|
||||
// 复制
|
||||
noteIds := []string{}
|
||||
if len(registerCopyNoteIds) > 0 {
|
||||
for i := 0; i < len(registerCopyNoteIds); i++ {
|
||||
// 判断笔记本是否存在
|
||||
noteId := registerCopyNoteIds[i]
|
||||
if noteId == "" {
|
||||
continue
|
||||
}
|
||||
note := noteService.GetNote(noteId, registerSharedUserId)
|
||||
if note.NoteId == "" {
|
||||
ok = false
|
||||
msg = "The user has no such note: " + noteId
|
||||
return
|
||||
} else {
|
||||
noteIds = append(noteIds, noteId)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.UpdateGlobalArrayConfig(adminUserId, "registerCopyNoteIds", noteIds)
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// 添加备份
|
||||
func (this *ConfigService) AddBackup(path, remark string) bool {
|
||||
backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
|
||||
n := time.Now().Unix()
|
||||
nstr := fmt.Sprintf("%v", n)
|
||||
backups = append(backups, map[string]string{"createdTime": nstr, "path": path, "remark": remark})
|
||||
return this.UpdateGlobalArrMapConfig(adminUserId, "backups", backups)
|
||||
}
|
||||
|
||||
func (this *ConfigService) getBackupDirname() string {
|
||||
n := time.Now()
|
||||
y, m, d := n.Date()
|
||||
return strconv.Itoa(y) + "_" + m.String() + "_" + strconv.Itoa(d) + "_" + fmt.Sprintf("%v", n.Unix())
|
||||
}
|
||||
func (this *ConfigService) Backup(remark string) (ok bool, msg string) {
|
||||
binPath := configService.GetGlobalStringConfig("mongodumpPath")
|
||||
config := revel.Config;
|
||||
dbname, _ := config.String("db.dbname")
|
||||
host, _ := revel.Config.String("db.host")
|
||||
port, _ := revel.Config.String("db.port")
|
||||
username, _ := revel.Config.String("db.username")
|
||||
password, _ := revel.Config.String("db.password")
|
||||
// mongodump -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
|
||||
binPath = binPath + " -h " + host + " -d " + dbname + " -port " + port
|
||||
if username != "" {
|
||||
binPath += " -u " + username + " -p " + password
|
||||
}
|
||||
// 保存的路径
|
||||
dir := revel.BasePath + "/backup/" + this.getBackupDirname()
|
||||
binPath += " -o " + dir
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command("/bin/sh", "-c", binPath)
|
||||
Log(binPath);
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
ok = false
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
return
|
||||
}
|
||||
ok = configService.AddBackup(dir, remark)
|
||||
return ok, msg
|
||||
}
|
||||
// 还原
|
||||
func (this *ConfigService) Restore(createdTime string) (ok bool, msg string) {
|
||||
backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
|
||||
var i int
|
||||
var backup map[string]string
|
||||
for i, backup = range backups {
|
||||
if backup["createdTime"] == createdTime {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if i == len(backups) {
|
||||
return false, "Backup Not Found"
|
||||
}
|
||||
|
||||
// 先备份当前
|
||||
ok, msg = this.Backup("Auto backup when restore from " + backup["createdTime"] )
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// mongorestore -h localhost -d leanote --directoryperdb /home/user1/gopackage/src/github.com/leanote/leanote/mongodb_backup/leanote_install_data/
|
||||
binPath := configService.GetGlobalStringConfig("mongorestorePath")
|
||||
config := revel.Config;
|
||||
dbname, _ := config.String("db.dbname")
|
||||
host, _ := revel.Config.String("db.host")
|
||||
port, _ := revel.Config.String("db.port")
|
||||
username, _ := revel.Config.String("db.username")
|
||||
password, _ := revel.Config.String("db.password")
|
||||
// mongorestore -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
|
||||
binPath = binPath + " --drop -h " + host + " -d " + dbname + " -port " + port
|
||||
if username != "" {
|
||||
binPath += " -u " + username + " -p " + password
|
||||
}
|
||||
|
||||
path := backup["path"] + "/" + dbname
|
||||
// 判断路径是否存在
|
||||
if !IsDirExists(path) {
|
||||
return false, path + " Is Not Exists"
|
||||
}
|
||||
|
||||
binPath += " --directoryperdb " + path
|
||||
|
||||
cmd := exec.Command("/bin/sh", "-c", binPath)
|
||||
Log(binPath);
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
ok = false
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
return
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
func (this *ConfigService) DeleteBackup(createdTime string) (bool, string) {
|
||||
backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
|
||||
var i int
|
||||
var backup map[string]string
|
||||
for i, backup = range backups {
|
||||
if backup["createdTime"] == createdTime {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if i == len(backups) {
|
||||
return false, "Backup Not Found"
|
||||
}
|
||||
|
||||
// 删除文件夹之
|
||||
err := os.RemoveAll(backups[i]["path"])
|
||||
if err != nil {
|
||||
return false, fmt.Sprintf("%v", err)
|
||||
}
|
||||
|
||||
// 删除之
|
||||
backups = append(backups[0:i], backups[i+1:]...)
|
||||
|
||||
ok := this.UpdateGlobalArrMapConfig(adminUserId, "backups", backups)
|
||||
return ok, ""
|
||||
}
|
||||
|
||||
func (this *ConfigService) UpdateBackupRemark(createdTime, remark string) (bool, string) {
|
||||
backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
|
||||
var i int
|
||||
var backup map[string]string
|
||||
for i, backup = range backups {
|
||||
if backup["createdTime"] == createdTime {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if i == len(backups) {
|
||||
return false, "Backup Not Found"
|
||||
}
|
||||
backup["remark"] = remark;
|
||||
|
||||
ok := this.UpdateGlobalArrMapConfig(adminUserId, "backups", backups)
|
||||
return ok, ""
|
||||
}
|
||||
|
||||
// 得到备份
|
||||
func (this *ConfigService) GetBackup(createdTime string) (map[string]string, bool) {
|
||||
backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
|
||||
var i int
|
||||
var backup map[string]string
|
||||
for i, backup = range backups {
|
||||
if backup["createdTime"] == createdTime {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if i == len(backups) {
|
||||
return map[string]string{}, false
|
||||
}
|
||||
return backup, true
|
||||
}
|
||||
|
||||
//--------------
|
||||
// sub domain
|
||||
var defaultDomain string
|
||||
var schema = "http://"
|
||||
var port string
|
||||
|
||||
func init() {
|
||||
revel.OnAppStart(func() {
|
||||
port = strconv.Itoa(revel.HttpPort)
|
||||
if port != "80" {
|
||||
port = ":" + port
|
||||
} else {
|
||||
port = "";
|
||||
}
|
||||
|
||||
siteUrl, _ = revel.Config.String("site.url") // 已包含:9000, http, 去掉成 leanote.com
|
||||
if strings.HasPrefix(siteUrl, "http://") {
|
||||
defaultDomain = siteUrl[len("http://"):]
|
||||
} else if strings.HasPrefix(siteUrl, "https://") {
|
||||
defaultDomain = siteUrl[len("https://"):]
|
||||
schema = "https://"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
func (this *ConfigService) GetSchema() string {
|
||||
return schema;
|
||||
}
|
||||
// 默认
|
||||
func (this *ConfigService) GetDefaultDomain() string {
|
||||
return defaultDomain
|
||||
}
|
||||
// 包含http://
|
||||
func (this *ConfigService) GetDefaultUrl() string {
|
||||
return schema + defaultDomain
|
||||
}
|
||||
// note
|
||||
func (this *ConfigService) GetNoteDomain() string {
|
||||
subDomain := this.GetGlobalStringConfig("noteSubDomain");
|
||||
if subDomain != "" {
|
||||
return subDomain + port
|
||||
}
|
||||
return this.GetDefaultDomain() + "/note"
|
||||
}
|
||||
func (this *ConfigService) GetNoteUrl() string {
|
||||
return schema + this.GetNoteDomain();
|
||||
}
|
||||
|
||||
// blog
|
||||
func (this *ConfigService) GetBlogDomain() string {
|
||||
subDomain := this.GetGlobalStringConfig("blogSubDomain");
|
||||
if subDomain != "" {
|
||||
return subDomain + port
|
||||
}
|
||||
return this.GetDefaultDomain() + "/blog"
|
||||
}
|
||||
func (this *ConfigService) GetBlogUrl() string {
|
||||
return schema + this.GetBlogDomain();
|
||||
}
|
||||
// lea
|
||||
func (this *ConfigService) GetLeaDomain() string {
|
||||
subDomain := this.GetGlobalStringConfig("leaSubDomain");
|
||||
if subDomain != "" {
|
||||
return subDomain + port
|
||||
}
|
||||
return this.GetDefaultDomain() + "/lea"
|
||||
}
|
||||
func (this *ConfigService) GetLeaUrl() string {
|
||||
return schema + this.GetLeaDomain();
|
||||
}
|
||||
|
||||
func (this *ConfigService) GetUserUrl(domain string) string {
|
||||
return schema + domain + port
|
||||
}
|
||||
func (this *ConfigService) GetUserSubUrl(subDomain string) string {
|
||||
return schema + subDomain + "." + this.GetDefaultDomain()
|
||||
}
|
||||
|
||||
// 是否允许自定义域名
|
||||
func (this *ConfigService) AllowCustomDomain() bool {
|
||||
return configService.GetGlobalStringConfig("allowCustomDomain") != ""
|
||||
}
|
||||
// 是否是好的自定义域名
|
||||
func (this *ConfigService) IsGoodCustomDomain(domain string) bool {
|
||||
blacks := this.GetGlobalArrayConfig("blackCustomDomains")
|
||||
for _, black := range blacks {
|
||||
if strings.Contains(domain, black) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (this *ConfigService) IsGoodSubDomain(domain string) bool {
|
||||
blacks := this.GetGlobalArrayConfig("blackSubDomains")
|
||||
LogJ(blacks)
|
||||
for _, black := range blacks {
|
||||
if domain == black {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
474
app/service/EmailService.go
Normal file
474
app/service/EmailService.go
Normal file
@@ -0,0 +1,474 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
"strings"
|
||||
"net/smtp"
|
||||
"strconv"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// 发送邮件
|
||||
|
||||
type EmailService struct {
|
||||
tpls map[string]*template.Template
|
||||
}
|
||||
|
||||
func NewEmailService() (*EmailService) {
|
||||
return &EmailService{tpls: map[string]*template.Template{}}
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
var host = ""
|
||||
var emailPort = ""
|
||||
var username = ""
|
||||
var password = ""
|
||||
|
||||
func InitEmailFromDb() {
|
||||
host = configService.GetGlobalStringConfig("emailHost")
|
||||
emailPort = configService.GetGlobalStringConfig("emailPort")
|
||||
username = configService.GetGlobalStringConfig("emailUsername")
|
||||
password = configService.GetGlobalStringConfig("emailPassword")
|
||||
}
|
||||
|
||||
func (this *EmailService) SendEmail(to, subject, body string) (ok bool, e string) {
|
||||
InitEmailFromDb()
|
||||
|
||||
if host == "" || emailPort == "" || username == "" || password == "" {
|
||||
return
|
||||
}
|
||||
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"
|
||||
}
|
||||
|
||||
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+":"+emailPort, auth, username, send_to, msg)
|
||||
|
||||
if err != nil {
|
||||
e = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// AddUser调用
|
||||
// 可以使用一个goroutine
|
||||
func (this *EmailService) RegisterSendActiveEmail(userInfo info.User, email string) bool {
|
||||
token := tokenService.NewToken(userInfo.UserId.Hex(), email, info.TokenActiveEmail)
|
||||
if token == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateRegisterSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateRegister");
|
||||
|
||||
if(tpl == "") {
|
||||
return false
|
||||
}
|
||||
|
||||
tokenUrl := siteUrl + "/user/activeEmail?token=" + token
|
||||
// {siteUrl} {tokenUrl} {token} {tokenTimeout} {user.id} {user.email} {user.username}
|
||||
token2Value := map[string]interface{}{"siteUrl": siteUrl, "tokenUrl": tokenUrl, "token": token, "tokenTimeout": strconv.Itoa(int(tokenService.GetOverHours(info.TokenActiveEmail))),
|
||||
"user": map[string]interface{}{
|
||||
"userId": userInfo.UserId.Hex(),
|
||||
"email": userInfo.Email,
|
||||
"username": userInfo.Username,
|
||||
},
|
||||
}
|
||||
|
||||
var ok bool
|
||||
ok, _, subject, tpl = this.renderEmail(subject, tpl, token2Value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
ok, _ = this.SendEmail(email, subject, tpl)
|
||||
return ok
|
||||
}
|
||||
|
||||
// 修改邮箱
|
||||
func (this *EmailService) UpdateEmailSendActiveEmail(userInfo info.User, email string) (ok bool, msg string) {
|
||||
// 先验证该email是否被注册了
|
||||
if userService.IsExistsUser(email) {
|
||||
ok = false
|
||||
msg = "该邮箱已注册"
|
||||
return
|
||||
}
|
||||
|
||||
token := tokenService.NewToken(userInfo.UserId.Hex(), email, info.TokenUpdateEmail)
|
||||
|
||||
if token == "" {
|
||||
return
|
||||
}
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateUpdateEmailSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateUpdateEmail");
|
||||
|
||||
// 发送邮件
|
||||
tokenUrl := siteUrl + "/user/updateEmail?token=" + token
|
||||
// {siteUrl} {tokenUrl} {token} {tokenTimeout} {user.userId} {user.email} {user.username}
|
||||
token2Value := map[string]interface{}{"siteUrl": siteUrl, "tokenUrl": tokenUrl, "token": token, "tokenTimeout": strconv.Itoa(int(tokenService.GetOverHours(info.TokenActiveEmail))),
|
||||
"newEmail": email,
|
||||
"user": map[string]interface{}{
|
||||
"userId": userInfo.UserId.Hex(),
|
||||
"email": userInfo.Email,
|
||||
"username": userInfo.Username,
|
||||
},
|
||||
}
|
||||
|
||||
ok, msg, subject, tpl = this.renderEmail(subject, tpl, token2Value)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
ok, msg = this.SendEmail(email, subject, tpl)
|
||||
return
|
||||
}
|
||||
|
||||
func (this *EmailService) FindPwdSendEmail(token, email string) (ok bool, msg string) {
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateFindPasswordSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateFindPassword");
|
||||
|
||||
// 发送邮件
|
||||
tokenUrl := siteUrl + "/findPassword/" + token
|
||||
// {siteUrl} {tokenUrl} {token} {tokenTimeout} {user.id} {user.email} {user.username}
|
||||
token2Value := map[string]interface{}{"siteUrl": siteUrl, "tokenUrl": tokenUrl,
|
||||
"token": token, "tokenTimeout": strconv.Itoa(int(tokenService.GetOverHours(info.TokenActiveEmail)))}
|
||||
|
||||
ok, msg, subject, tpl = this.renderEmail(subject, tpl, token2Value)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// 发送邮件
|
||||
ok, msg = this.SendEmail(email, subject, tpl)
|
||||
return
|
||||
}
|
||||
|
||||
// 发送邀请链接
|
||||
func (this *EmailService) SendInviteEmail(userInfo info.User, email, content string) bool {
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateInviteSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateInvite");
|
||||
|
||||
token2Value := map[string]interface{}{"siteUrl": siteUrl,
|
||||
"registerUrl": siteUrl + "/register?from=" + userInfo.Username,
|
||||
"content": content,
|
||||
"user": map[string]interface{}{
|
||||
"username": userInfo.Username,
|
||||
"email": userInfo.Email,
|
||||
},
|
||||
}
|
||||
var ok bool
|
||||
ok, _, subject, tpl = this.renderEmail(subject, tpl, token2Value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// 发送邮件
|
||||
ok, _ = this.SendEmail(email, subject, tpl)
|
||||
return ok
|
||||
}
|
||||
|
||||
// 发送评论
|
||||
func (this *EmailService) SendCommentEmail(note info.Note, comment info.BlogComment, userId, content string) bool {
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateCommentSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateComment");
|
||||
|
||||
// title := "评论提醒"
|
||||
|
||||
/*
|
||||
toUserId := note.UserId.Hex()
|
||||
// title := "评论提醒"
|
||||
|
||||
// 表示回复回复的内容, 那么发送给之前回复的
|
||||
if comment.CommentId != "" {
|
||||
toUserId = comment.UserId.Hex()
|
||||
}
|
||||
toUserInfo := userService.GetUserInfo(toUserId)
|
||||
sendUserInfo := userService.GetUserInfo(userId)
|
||||
|
||||
subject := note.Title + " 收到 " + sendUserInfo.Username + " 的评论";
|
||||
if comment.CommentId != "" {
|
||||
subject = "您在 " + note.Title + " 发表的评论收到 " + sendUserInfo.Username;
|
||||
if userId == note.UserId.Hex() {
|
||||
subject += "(作者)";
|
||||
}
|
||||
subject += " 的评论";
|
||||
}
|
||||
*/
|
||||
|
||||
toUserId := note.UserId.Hex()
|
||||
// 表示回复回复的内容, 那么发送给之前回复的
|
||||
if comment.CommentId != "" {
|
||||
toUserId = comment.UserId.Hex()
|
||||
}
|
||||
toUserInfo := userService.GetUserInfo(toUserId) // 被评论者
|
||||
sendUserInfo := userService.GetUserInfo(userId) // 评论者
|
||||
|
||||
// {siteUrl} {blogUrl}
|
||||
// {blog.id} {blog.title} {blog.url}
|
||||
// {commentUser.userId} {commentUser.username} {commentUser.email}
|
||||
// {commentedUser.userId} {commentedUser.username} {commentedUser.email}
|
||||
token2Value := map[string]interface{}{"siteUrl": siteUrl, "blogUrl": configService.GetBlogUrl(),
|
||||
"blog": map[string]string{
|
||||
"id": note.NoteId.Hex(),
|
||||
"title": note.Title,
|
||||
"url": configService.GetBlogUrl() + "/view/" + note.NoteId.Hex(),
|
||||
},
|
||||
"commentContent": content,
|
||||
// 评论者信息
|
||||
"commentUser": map[string]interface{}{"userId": sendUserInfo.UserId.Hex(),
|
||||
"username": sendUserInfo.Username,
|
||||
"email": sendUserInfo.Email,
|
||||
"isBlogAuthor": userId == note.UserId.Hex(),
|
||||
},
|
||||
// 被评论者信息
|
||||
"commentedUser": map[string]interface{}{"userId": toUserId,
|
||||
"username": toUserInfo.Username,
|
||||
"email": toUserInfo.Email,
|
||||
"isBlogAuthor": toUserId == note.UserId.Hex(),
|
||||
},
|
||||
}
|
||||
|
||||
ok := false
|
||||
ok, _, subject, tpl = this.renderEmail(subject, tpl, token2Value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
ok, _ = this.SendEmail(toUserInfo.Email, subject, tpl)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
||||
// 验证模板是否正确
|
||||
func (this *EmailService) ValidTpl(str string) (ok bool, msg string){
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprint(err)
|
||||
}
|
||||
}();
|
||||
header := configService.GetGlobalStringConfig("emailTemplateHeader");
|
||||
footer := configService.GetGlobalStringConfig("emailTemplateFooter");
|
||||
str = strings.Replace(str, "{{header}}", header, -1)
|
||||
str = strings.Replace(str, "{{footer}}", footer, -1)
|
||||
_, err := template.New("tpl name").Parse(str)
|
||||
if err != nil {
|
||||
msg = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// ok, msg, subject, tpl
|
||||
func (this *EmailService) getTpl(str string) (ok bool, msg string, tpl *template.Template){
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprint(err)
|
||||
}
|
||||
}();
|
||||
|
||||
var err error
|
||||
var has bool
|
||||
|
||||
if tpl, has = this.tpls[str]; !has {
|
||||
tpl, err = template.New("tpl name").Parse(str)
|
||||
if err != nil {
|
||||
msg = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
this.tpls[str] = tpl
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// 通过subject, body和值得到内容
|
||||
func (this *EmailService) renderEmail(subject, body string, values map[string]interface{}) (ok bool, msg string, o string, b string) {
|
||||
ok = false
|
||||
msg = ""
|
||||
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprint(err) // 这里的err其实就是panic传入的内容,
|
||||
}
|
||||
}();
|
||||
|
||||
var tpl *template.Template
|
||||
|
||||
values["siteUrl"] = siteUrl;
|
||||
|
||||
// subject
|
||||
if subject != "" {
|
||||
ok, msg, tpl = this.getTpl(subject)
|
||||
if(!ok) {
|
||||
return
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
err := tpl.Execute(&buffer, values)
|
||||
if err != nil {
|
||||
msg = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
o = buffer.String()
|
||||
} else {
|
||||
o = ""
|
||||
}
|
||||
|
||||
// content
|
||||
header := configService.GetGlobalStringConfig("emailTemplateHeader");
|
||||
footer := configService.GetGlobalStringConfig("emailTemplateFooter");
|
||||
body = strings.Replace(body, "{{header}}", header, -1)
|
||||
body = strings.Replace(body, "{{footer}}", footer, -1)
|
||||
values["subject"] = o
|
||||
ok, msg, tpl = this.getTpl(body)
|
||||
if(!ok) {
|
||||
return
|
||||
}
|
||||
var buffer2 bytes.Buffer
|
||||
err := tpl.Execute(&buffer2, values)
|
||||
if err != nil {
|
||||
msg = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
b = buffer2.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 发送email给用户
|
||||
// 需要记录
|
||||
func (this *EmailService) SendEmailToUsers(users []info.User, subject, body string) (ok bool, msg string) {
|
||||
if(users == nil || len(users) == 0) {
|
||||
msg = "no users"
|
||||
return
|
||||
}
|
||||
|
||||
// 尝试renderHtml
|
||||
ok, msg, _, _ = this.renderEmail(subject, body, map[string]interface{}{})
|
||||
if(!ok) {
|
||||
Log(msg)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for _, user := range users {
|
||||
LogJ(user)
|
||||
m := map[string]interface{}{}
|
||||
m["userId"] = user.UserId.Hex()
|
||||
m["username"] = user.Username
|
||||
m["email"] = user.Email
|
||||
ok2, msg2, subject2, body2 := this.renderEmail(subject, body, m)
|
||||
ok = ok2
|
||||
msg = msg2
|
||||
if(ok2) {
|
||||
sendOk, msg := this.SendEmail(user.Email, subject2, body2);
|
||||
this.AddEmailLog(user.Email, subject, body, sendOk, msg) // 把模板记录下
|
||||
// 记录到Email Log
|
||||
if sendOk {
|
||||
// Log("ok " + user.Email)
|
||||
} else {
|
||||
// Log("no " + user.Email)
|
||||
}
|
||||
} else {
|
||||
// Log(msg);
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *EmailService) SendEmailToEmails(emails []string, subject, body string) (ok bool, msg string) {
|
||||
if(emails == nil || len(emails) == 0) {
|
||||
msg = "no emails"
|
||||
return
|
||||
}
|
||||
|
||||
// 尝试renderHtml
|
||||
ok, msg, _, _ = this.renderEmail(subject, body, map[string]interface{}{})
|
||||
if(!ok) {
|
||||
Log(msg)
|
||||
return
|
||||
}
|
||||
|
||||
// go func() {
|
||||
for _, email := range emails {
|
||||
if email == "" {
|
||||
continue
|
||||
}
|
||||
m := map[string]interface{}{}
|
||||
m["email"] = email
|
||||
ok, msg, subject, body = this.renderEmail(subject, body, m)
|
||||
if(ok) {
|
||||
sendOk, msg := this.SendEmail(email, subject, body);
|
||||
this.AddEmailLog(email, subject, body, sendOk, msg)
|
||||
// 记录到Email Log
|
||||
if sendOk {
|
||||
Log("ok " + email)
|
||||
} else {
|
||||
Log("no " + email)
|
||||
}
|
||||
} else {
|
||||
Log(msg);
|
||||
}
|
||||
}
|
||||
// }()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 添加邮件日志
|
||||
func (this *EmailService) AddEmailLog(email, subject, body string, ok bool, msg string) {
|
||||
log := info.EmailLog{LogId: bson.NewObjectId(), Email: email, Subject: subject, Body: body, Ok: ok, Msg: msg, CreatedTime: time.Now()}
|
||||
db.Insert(db.EmailLogs, log)
|
||||
}
|
||||
// 展示邮件日志
|
||||
|
||||
func (this *EmailService) DeleteEmails(ids []string) bool {
|
||||
idsO := make([]bson.ObjectId, len(ids))
|
||||
for i, id := range ids {
|
||||
idsO[i] = bson.ObjectIdHex(id)
|
||||
}
|
||||
db.DeleteAll(db.EmailLogs, bson.M{"_id": bson.M{"$in": idsO}})
|
||||
|
||||
return true
|
||||
}
|
||||
func (this *EmailService) ListEmailLogs(pageNumber, pageSize int, sortField string, isAsc bool, email string) (page info.Page, emailLogs []info.EmailLog) {
|
||||
emailLogs = []info.EmailLog{}
|
||||
skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, sortField, isAsc)
|
||||
query := bson.M{}
|
||||
if email != "" {
|
||||
query["Email"] = bson.M{"$regex": bson.RegEx{".*?" + email + ".*", "i"}}
|
||||
}
|
||||
q := db.EmailLogs.Find(query);
|
||||
// 总记录数
|
||||
count, _ := q.Count()
|
||||
// 列表
|
||||
q.Sort(sortFieldR).
|
||||
Skip(skipNum).
|
||||
Limit(pageSize).
|
||||
All(&emailLogs)
|
||||
page = info.NewPage(pageNumber, pageSize, count, nil)
|
||||
return
|
||||
}
|
||||
@@ -126,13 +126,21 @@ func (this *NoteService) AddNote(note info.Note) info.Note {
|
||||
note.UpdatedUserId = note.UserId
|
||||
|
||||
// 设为blog
|
||||
note.IsBlog = notebookService.IsBlog(note.NotebookId.Hex())
|
||||
notebookId := note.NotebookId.Hex()
|
||||
note.IsBlog = notebookService.IsBlog(notebookId)
|
||||
|
||||
if note.IsBlog {
|
||||
note.PublicTime = note.UpdatedTime
|
||||
}
|
||||
|
||||
db.Insert(db.Notes, note)
|
||||
|
||||
// tag1
|
||||
tagService.AddTags(note.UserId.Hex(), note.Tags)
|
||||
|
||||
// recount notebooks' notes number
|
||||
notebookService.ReCountNotebookNumberNotes(notebookId)
|
||||
|
||||
return note
|
||||
}
|
||||
|
||||
@@ -276,6 +284,9 @@ func (this *NoteService) UpdateTags(noteId string, userId string, tags []string)
|
||||
// 2. 要判断之前是否是blog, 如果不是, 那么notebook是否是blog?
|
||||
func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
|
||||
if notebookService.IsMyNotebook(notebookId, userId) {
|
||||
note := this.GetNote(noteId, userId)
|
||||
preNotebookId := note.NotebookId.Hex()
|
||||
|
||||
re := db.UpdateByIdAndUserId(db.Notes, noteId, userId,
|
||||
bson.M{"$set": bson.M{"IsTrash": false,
|
||||
"NotebookId": bson.ObjectIdHex(notebookId)}})
|
||||
@@ -283,6 +294,13 @@ func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
|
||||
if re {
|
||||
// 更新blog状态
|
||||
this.updateToNotebookBlog(noteId, notebookId, userId)
|
||||
|
||||
// recount notebooks' notes number
|
||||
notebookService.ReCountNotebookNumberNotes(notebookId)
|
||||
// 之前不是trash才统计, trash本不在统计中的
|
||||
if !note.IsTrash && preNotebookId != notebookId {
|
||||
notebookService.ReCountNotebookNumberNotes(preNotebookId)
|
||||
}
|
||||
}
|
||||
|
||||
return this.GetNote(noteId, userId);
|
||||
@@ -330,7 +348,11 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
|
||||
// 更新blog状态
|
||||
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId)
|
||||
|
||||
// recount
|
||||
notebookService.ReCountNotebookNumberNotes(notebookId)
|
||||
|
||||
note.IsBlog = isBlog
|
||||
|
||||
return note
|
||||
}
|
||||
|
||||
@@ -340,7 +362,7 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
|
||||
// 复制别人的共享笔记给我
|
||||
// 将别人可用的图片转为我的图片, 复制图片
|
||||
func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId string) info.Note {
|
||||
Log(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId))
|
||||
// Log(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId))
|
||||
// 判断是否共享了给我
|
||||
if notebookService.IsMyNotebook(notebookId, myUserId) &&
|
||||
(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) {
|
||||
@@ -375,6 +397,9 @@ func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId
|
||||
// 更新blog状态
|
||||
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, myUserId)
|
||||
|
||||
// recount
|
||||
notebookService.ReCountNotebookNumberNotes(notebookId)
|
||||
|
||||
note.IsBlog = isBlog
|
||||
return note
|
||||
}
|
||||
@@ -482,4 +507,13 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb
|
||||
Limit(pageSize).
|
||||
All(¬es)
|
||||
return
|
||||
}
|
||||
|
||||
//------------
|
||||
// 统计
|
||||
func (this *NoteService) CountNote() int {
|
||||
return db.Count(db.Notes, bson.M{"IsTrash": false})
|
||||
}
|
||||
func (this *NoteService) CountBlog() int {
|
||||
return db.Count(db.Notes, bson.M{"IsBlog": true, "IsTrash": false})
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
@@ -96,6 +96,11 @@ func (this *NotebookService) GetNotebook(notebookId, userId string) info.Noteboo
|
||||
db.GetByIdAndUserId(db.Notebooks, notebookId, userId, ¬ebook)
|
||||
return notebook
|
||||
}
|
||||
func (this *NotebookService) GetNotebookById(notebookId string) info.Notebook {
|
||||
notebook := info.Notebook{}
|
||||
db.Get(db.Notebooks, notebookId, ¬ebook)
|
||||
return notebook
|
||||
}
|
||||
|
||||
// 得到用户下所有的notebook
|
||||
// 排序好之后返回
|
||||
@@ -168,11 +173,15 @@ func (this *NotebookService) UpdateNotebook(userId, notebookId string, needUpdat
|
||||
|
||||
// 如果有IsBlog之类的, 需要特殊处理
|
||||
if isBlog, ok := needUpdate["IsBlog"]; ok {
|
||||
// 设为blog/取消
|
||||
// 设为blog/取消, 把它下面所有的note都设为isBlog
|
||||
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})
|
||||
data := bson.M{"IsBlog": is}
|
||||
if is {
|
||||
data["PublicTime"] = time.Now()
|
||||
}
|
||||
db.UpdateByQMap(db.Notes, q, data)
|
||||
|
||||
// noteContents也更新, 这个就麻烦了, noteContents表没有NotebookId
|
||||
// 先查该notebook下所有notes, 得到id
|
||||
@@ -248,4 +257,27 @@ func (this *NotebookService) DragNotebooks(userId string, curNotebookId string,
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 重新统计笔记本下的笔记数目
|
||||
// noteSevice: AddNote, CopyNote, CopySharedNote, MoveNote
|
||||
// trashService: DeleteNote (recove不用, 都统一在MoveNote里了)
|
||||
func (this *NotebookService) ReCountNotebookNumberNotes(notebookId string) bool {
|
||||
notebookIdO := bson.ObjectIdHex(notebookId)
|
||||
count := db.Count(db.Notes, bson.M{"NotebookId": notebookIdO, "IsTrash": false})
|
||||
Log(count)
|
||||
Log(notebookId)
|
||||
return db.UpdateByQField(db.Notebooks, bson.M{"_id": notebookIdO}, "NumberNotes", count)
|
||||
}
|
||||
|
||||
func (this *NotebookService) ReCountAll() {
|
||||
/*
|
||||
// 得到所有笔记本
|
||||
notebooks := []info.Notebook{}
|
||||
db.ListByQWithFields(db.Notebooks, bson.M{}, []string{"NotebookId"}, ¬ebooks)
|
||||
|
||||
for _, each := range notebooks {
|
||||
this.ReCountNotebookNumberNotes(each.NotebookId.Hex())
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package service
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 找回密码
|
||||
@@ -32,14 +30,7 @@ func (this *PwdService) FindPwd(email string) (ok bool, msg string) {
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
siteUrl, _ := revel.Config.String("site.url")
|
||||
url := siteUrl + "/findPassword/" + token
|
||||
body := fmt.Sprintf("请点击链接修改密码: <a href='%v'>%v</a>. %v小时后过期.", url, url, int(overHours));
|
||||
if !SendEmail(email, "leanote-找回密码", "找回密码", body) {
|
||||
return false, "邮箱发送失败"
|
||||
}
|
||||
|
||||
ok = true
|
||||
ok, msg = emailService.FindPwdSendEmail(token, email)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
71
app/service/SessionService.go
Normal file
71
app/service/SessionService.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
// "strings"
|
||||
)
|
||||
|
||||
// Session存储到mongodb中
|
||||
type SessionService struct {
|
||||
}
|
||||
|
||||
func (this *SessionService) Update(sessionId, key string, value interface{}) bool {
|
||||
return db.UpdateByQMap(db.Sessions, bson.M{"SessionId": sessionId},
|
||||
bson.M{key: value, "UpdatedTime": time.Now()})
|
||||
}
|
||||
// 注销时清空session
|
||||
func (this *SessionService) Clear(sessionId string) bool {
|
||||
return db.Delete(db.Sessions, bson.M{"SessionId": sessionId})
|
||||
}
|
||||
func (this *SessionService) Get(sessionId string) info.Session {
|
||||
session := info.Session{}
|
||||
db.GetByQ(db.Sessions, bson.M{"SessionId": sessionId}, &session)
|
||||
|
||||
// 如果没有session, 那么插入一条之
|
||||
if session.Id == "" {
|
||||
session.Id = bson.NewObjectId()
|
||||
session.SessionId = sessionId
|
||||
session.CreatedTime = time.Now()
|
||||
session.UpdatedTime = session.CreatedTime
|
||||
db.Insert(db.Sessions, session)
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
//------------------
|
||||
// 错误次数处理
|
||||
|
||||
// 登录错误时间是否已超过了
|
||||
func (this *SessionService) LoginTimesIsOver(sessionId string) bool {
|
||||
session := this.Get(sessionId)
|
||||
return session.LoginTimes > 5
|
||||
}
|
||||
// 登录成功后清空错误次数
|
||||
func (this *SessionService) ClearLoginTimes(sessionId string) bool {
|
||||
return this.Update(sessionId, "LoginTimes", 0)
|
||||
}
|
||||
// 增加错误次数
|
||||
func (this *SessionService) IncrLoginTimes(sessionId string) bool {
|
||||
session := this.Get(sessionId)
|
||||
return this.Update(sessionId, "LoginTimes", session.LoginTimes + 1)
|
||||
}
|
||||
|
||||
//----------
|
||||
// 验证码
|
||||
func (this *SessionService) GetCaptcha(sessionId string) string {
|
||||
session := this.Get(sessionId)
|
||||
return session.Captcha
|
||||
}
|
||||
func (this *SessionService) SetCaptcha(sessionId, captcha string) bool {
|
||||
this.Get(sessionId)
|
||||
Log(sessionId)
|
||||
Log(captcha)
|
||||
ok := this.Update(sessionId, "Captcha", captcha)
|
||||
Log(ok)
|
||||
return ok
|
||||
}
|
||||
@@ -28,7 +28,13 @@ 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}})
|
||||
if db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true}}) {
|
||||
// recount notebooks' notes number
|
||||
notebookIdO := noteService.GetNotebookId(noteId)
|
||||
notebookId := notebookIdO.Hex()
|
||||
notebookService.ReCountNotebookNumberNotes(notebookId)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
27
app/service/UpgradeService.go
Normal file
27
app/service/UpgradeService.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "time"
|
||||
)
|
||||
|
||||
|
||||
type UpgradeService struct {
|
||||
}
|
||||
|
||||
// 添加了PublicTime, RecommendTime
|
||||
func (this *UpgradeService) UpgradeBlog() bool {
|
||||
notes := []info.Note{}
|
||||
db.ListByQ(db.Notes, bson.M{"IsBlog": true}, ¬es)
|
||||
|
||||
// PublicTime, RecommendTime = UpdatedTime
|
||||
for _, note := range notes {
|
||||
db.UpdateByIdAndUserIdMap2(db.Notes, note.NoteId, note.UserId, bson.M{"PublicTime": note.UpdatedTime, "RecommendTime": note.UpdatedTime})
|
||||
Log(note.NoteId.Hex())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
}
|
||||
|
||||
|
||||
// 添加用户
|
||||
func (this *UserService) AddUser(user info.User) bool {
|
||||
if user.UserId == "" {
|
||||
@@ -27,7 +24,9 @@ func (this *UserService) AddUser(user info.User) bool {
|
||||
|
||||
// 发送验证邮箱
|
||||
go func() {
|
||||
this.RegisterSendActiveEmail(user.UserId.Hex(), user.Email)
|
||||
emailService.RegisterSendActiveEmail(user, user.Email)
|
||||
// 发送给我 life@leanote.com
|
||||
emailService.SendEmail("life@leanote.com", "新增用户", "{header}用户名" + user.Email + "{footer}");
|
||||
}();
|
||||
}
|
||||
|
||||
@@ -69,10 +68,23 @@ func (this *UserService) GetUserInfoByAny(idEmailUsername string) info.User {
|
||||
return this.GetUserInfoByUsername(idEmailUsername)
|
||||
}
|
||||
|
||||
func (this *UserService) setUserLogo(user *info.User) {
|
||||
// Logo路径问题, 有些有http: 有些没有
|
||||
if user.Logo == "" {
|
||||
user.Logo = "images/blog/default_avatar.png"
|
||||
}
|
||||
if user.Logo != "" && !strings.HasPrefix(user.Logo, "http") {
|
||||
user.Logo = strings.Trim(user.Logo, "/")
|
||||
user.Logo = siteUrl + "/" + user.Logo
|
||||
}
|
||||
}
|
||||
|
||||
// 得到用户信息 userId
|
||||
func (this *UserService) GetUserInfo(userId string) info.User {
|
||||
user := info.User{}
|
||||
db.Get(db.Users, userId, &user)
|
||||
// Logo路径问题, 有些有http: 有些没有
|
||||
this.setUserLogo(&user)
|
||||
return user
|
||||
}
|
||||
// 得到用户信息 email
|
||||
@@ -99,29 +111,27 @@ func (this *UserService) ListUserInfosByUserIds(userIds []bson.ObjectId) []info.
|
||||
db.ListByQ(db.Users, bson.M{"_id": bson.M{"$in": userIds}}, &users)
|
||||
return users
|
||||
}
|
||||
// 用户信息和博客设置信息
|
||||
func (this *UserService) MapUserInfoAndBlogInfosByUserIds(userIds []bson.ObjectId) map[bson.ObjectId]info.User {
|
||||
func (this *UserService) ListUserInfosByEmails(emails []string) []info.User {
|
||||
users := []info.User{}
|
||||
db.ListByQ(db.Users, bson.M{"Email": bson.M{"$in": emails}}, &users)
|
||||
return users
|
||||
}
|
||||
// 用户信息即可
|
||||
func (this *UserService) MapUserInfoByUserIds(userIds []bson.ObjectId) map[bson.ObjectId]info.User {
|
||||
users := []info.User{}
|
||||
db.ListByQ(db.Users, bson.M{"_id": bson.M{"$in": userIds}}, &users)
|
||||
|
||||
userBlogs := []info.UserBlog{}
|
||||
db.ListByQWithFields(db.UserBlogs, bson.M{"_id": bson.M{"$in": userIds}}, []string{"Logo"}, &userBlogs)
|
||||
|
||||
userBlogMap := make(map[bson.ObjectId]info.UserBlog, len(userBlogs))
|
||||
for _, user := range userBlogs {
|
||||
userBlogMap[user.UserId] = user
|
||||
}
|
||||
|
||||
userMap := make(map[bson.ObjectId]info.User, len(users))
|
||||
for _, user := range users {
|
||||
if userBlog, ok := userBlogMap[user.UserId]; ok {
|
||||
user.Logo = userBlog.Logo
|
||||
}
|
||||
this.setUserLogo(&user)
|
||||
userMap[user.UserId] = user
|
||||
}
|
||||
|
||||
return userMap
|
||||
}
|
||||
// 用户信息和博客设置信息
|
||||
func (this *UserService) MapUserInfoAndBlogInfosByUserIds(userIds []bson.ObjectId) map[bson.ObjectId]info.User {
|
||||
return this.MapUserInfoByUserIds(userIds)
|
||||
}
|
||||
|
||||
// 通过ids得到users, 按id的顺序组织users
|
||||
func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.User {
|
||||
@@ -174,6 +184,12 @@ func (this *UserService) UpdateUsername(userId, username string) (bool, string)
|
||||
return ok, ""
|
||||
}
|
||||
|
||||
// 修改头像
|
||||
func (this *UserService) UpdateAvatar(userId, avatarPath string) (bool) {
|
||||
userIdO := bson.ObjectIdHex(userId)
|
||||
return db.UpdateByQField(db.Users, bson.M{"_id": userIdO}, "Logo", avatarPath)
|
||||
}
|
||||
|
||||
//----------------------
|
||||
// 已经登录了的用户修改密码
|
||||
func (this *UserService) UpdatePwd(userId, oldPwd, pwd string) (bool, string) {
|
||||
@@ -194,59 +210,6 @@ func (this *UserService) UpdateTheme(userId, theme string) (bool) {
|
||||
//---------------
|
||||
// 修改email
|
||||
|
||||
// 发送激活邮件
|
||||
|
||||
// AddUser调用
|
||||
// 可以使用一个goroutine
|
||||
func (this *UserService) RegisterSendActiveEmail(userId string, email string) bool {
|
||||
token := tokenService.NewToken(userId, email, info.TokenActiveEmail)
|
||||
|
||||
if token == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
siteUrl, _ := revel.Config.String("site.url")
|
||||
url := siteUrl + "/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
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
siteUrl, _ := revel.Config.String("site.url")
|
||||
url := siteUrl + "/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{}
|
||||
@@ -308,13 +271,13 @@ func (this *UserService) ThirdAddUser(userId, email, pwd string) (ok bool, msg s
|
||||
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)UpdateColumnWidth(userId string, notebookWidth, noteListWidth, mdEditorWidth int) bool {
|
||||
return db.UpdateByQMap(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)},
|
||||
bson.M{"NotebookWidth": notebookWidth, "NoteListWidth": noteListWidth, "mdEditorWidth": mdEditorWidth})
|
||||
}
|
||||
// 左侧是否隐藏
|
||||
func (this *UserService)UpdateLeftIsMin(userId string, leftIsMin bool) bool {
|
||||
@@ -340,4 +303,48 @@ func (this *UserService) ListUsers(pageNumber, pageSize int, sortField string, i
|
||||
All(&users)
|
||||
page = info.NewPage(pageNumber, pageSize, count, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (this *UserService) GetAllUserByFilter(userFilterEmail, userFilterWhiteList, userFilterBlackList string, verified bool) []info.User {
|
||||
query := bson.M{}
|
||||
|
||||
if verified {
|
||||
query["Verified"] = true
|
||||
}
|
||||
|
||||
orQ := []bson.M{}
|
||||
if userFilterEmail != "" {
|
||||
orQ = append(orQ, bson.M{"Email": bson.M{"$regex": bson.RegEx{".*?" + userFilterEmail + ".*", "i"}}},
|
||||
bson.M{"Username": bson.M{"$regex": bson.RegEx{".*?" + userFilterEmail + ".*", "i"}}},
|
||||
)
|
||||
}
|
||||
if(userFilterWhiteList != "") {
|
||||
userFilterWhiteList = strings.Replace(userFilterWhiteList, "\r", "", -1)
|
||||
emails := strings.Split(userFilterWhiteList, "\n");
|
||||
orQ = append(orQ, bson.M{"Email": bson.M{"$in": emails}})
|
||||
}
|
||||
if len(orQ) > 0 {
|
||||
query["$or"] = orQ
|
||||
}
|
||||
|
||||
emailQ := bson.M{}
|
||||
if(userFilterBlackList != "") {
|
||||
userFilterWhiteList = strings.Replace(userFilterBlackList, "\r", "", -1)
|
||||
bEmails := strings.Split(userFilterBlackList, "\n");
|
||||
emailQ["$nin"] = bEmails
|
||||
query["Email"] = emailQ
|
||||
}
|
||||
|
||||
LogJ(query)
|
||||
users := []info.User{}
|
||||
q := db.Users.Find(query);
|
||||
q.All(&users)
|
||||
Log(len(users))
|
||||
|
||||
return users
|
||||
}
|
||||
|
||||
// 统计
|
||||
func (this *UserService) CountUser() int {
|
||||
return db.Count(db.Users, bson.M{})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
// init service, for share service bettween services
|
||||
@@ -24,7 +24,12 @@ var attachService, AttachS *AttachService
|
||||
var configService, ConfigS *ConfigService
|
||||
var PwdS *PwdService
|
||||
var SuggestionS *SuggestionService
|
||||
var emailService, EmailS *EmailService
|
||||
var AuthS *AuthService
|
||||
var UpgradeS *UpgradeService
|
||||
var SessionS, sessionService *SessionService
|
||||
|
||||
var siteUrl string
|
||||
|
||||
// onAppStart调用
|
||||
func InitService() {
|
||||
@@ -45,6 +50,9 @@ func InitService() {
|
||||
PwdS = &PwdService{}
|
||||
SuggestionS = &SuggestionService{}
|
||||
AuthS = &AuthService{}
|
||||
EmailS = NewEmailService()
|
||||
UpgradeS = &UpgradeService{}
|
||||
SessionS = &SessionService{}
|
||||
|
||||
notebookService = NotebookS
|
||||
noteService = NoteS
|
||||
@@ -60,4 +68,9 @@ func InitService() {
|
||||
albumService = AlbumS
|
||||
attachService = AttachS
|
||||
configService = ConfigS
|
||||
emailService = EmailS
|
||||
sessionService = SessionS
|
||||
|
||||
//
|
||||
siteUrl, _ = revel.Config.String("site.url")
|
||||
}
|
||||
@@ -189,7 +189,8 @@ func testLea() {
|
||||
|
||||
func main() {
|
||||
revel.BasePath = "/Users/life/Documents/Go/package/src/leanote"
|
||||
testLea();
|
||||
// testLea();
|
||||
|
||||
// a, b := SplitFilename("http://ab/c/a.gif#??")
|
||||
// println(a)
|
||||
// println(b)
|
||||
|
||||
@@ -4,22 +4,11 @@
|
||||
<section class="panel panel-default">
|
||||
<div class="row wrapper">
|
||||
<div class="col-sm-5 m-b-xs">
|
||||
<select class="input-sm form-control input-s-sm inline v-middle">
|
||||
<option value="0">
|
||||
Bulk action
|
||||
</option>
|
||||
<option value="1">
|
||||
Delete selected
|
||||
</option>
|
||||
<option value="2">
|
||||
Bulk edit
|
||||
</option>
|
||||
<option value="3">
|
||||
Export
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-default">
|
||||
Apply
|
||||
Action1
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default">
|
||||
Action2
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-sm-4 m-b-xs">
|
||||
@@ -62,16 +51,7 @@
|
||||
<i class="fa fa-sort"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
{{sorterTh $url "isRecommend" .sorter}}
|
||||
>
|
||||
isRecommend
|
||||
<span class="th-sort">
|
||||
<i class="fa fa-sort-down"></i>
|
||||
<i class="fa fa-sort-up"></i>
|
||||
<i class="fa fa-sort"></i>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th
|
||||
{{sorterTh $url "createdTime" .sorter}}
|
||||
>
|
||||
@@ -82,8 +62,6 @@
|
||||
<i class="fa fa-sort"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th width="30">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -100,21 +78,9 @@
|
||||
{{.User.Username}}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<button data-loading-text="..." class="btn btn-default change-recommend" data-id="{{.NoteId.Hex}}" data-recommend="{{if .IsRecommend}}1{{else}}0{{end}}">
|
||||
{{if .IsRecommend}}
|
||||
Y
|
||||
{{else}}
|
||||
N
|
||||
{{end}}
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
{{.CreatedTime|datetime}}
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" class="btn btn-default">Send Email</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
@@ -123,22 +89,11 @@
|
||||
<footer class="panel-footer">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 hidden-xs">
|
||||
<select class="input-sm form-control input-s-sm inline v-middle">
|
||||
<option value="0">
|
||||
Bulk action
|
||||
</option>
|
||||
<option value="1">
|
||||
Delete selected
|
||||
</option>
|
||||
<option value="2">
|
||||
Bulk edit
|
||||
</option>
|
||||
<option value="3">
|
||||
Export
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-default">
|
||||
Apply
|
||||
Action1
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default">
|
||||
Action2
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-sm-4 text-center">
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Blog</h3></div>
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Mongodb Tool Configuration</h3></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-6">
|
||||
<form id="add_user_form">
|
||||
<form id="data_form">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Recommend Tags</label>
|
||||
<input type="text" class="form-control" name="recommendTags" value="{{.recommendTags}}">
|
||||
Split by ','
|
||||
<label>mongodump path</label>
|
||||
<input type="text" class="form-control" name="mongodumpPath" value="{{.str.mongodumpPath}}" placeholder="">
|
||||
Please input the bin mongodump's absolute path
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>New Tags</label>
|
||||
<input type="text" class="form-control" name="newTags" value="{{.newTags}}">
|
||||
Split by ','
|
||||
<label>mongorestore path</label>
|
||||
<input type="text" class="form-control" name="mongorestorePath" value="{{.str.mongorestorePath}}" placeholder="">
|
||||
Please input the bin mongorestore's absolute path
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,14 +32,14 @@
|
||||
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
init_validator("#add_user_form");
|
||||
init_validator("#data_form");
|
||||
|
||||
$("#submit").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
if($("#add_user_form").valid()) {
|
||||
if($("#data_form").valid()) {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminSetting/doBlogTag", getFormJsonData("add_user_form"), function(ret){
|
||||
ajaxPost("/adminSetting/mongodb", getFormJsonData("data_form"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
115
app/views/Admin/Data/index.html
Normal file
115
app/views/Admin/Data/index.html
Normal file
@@ -0,0 +1,115 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Backup & Restore</h3></div>
|
||||
|
||||
<style>
|
||||
.break-all {
|
||||
word-break:break-all; /*支持IE,chrome,FF不支持*/
|
||||
word-wrap:break-word;/*支持IE,chrome,FF*/
|
||||
}
|
||||
</style>
|
||||
<section class="panel panel-default">
|
||||
|
||||
<div class="row wrapper">
|
||||
<div class="col-sm-5 m-b-xs">
|
||||
<button class="btn btn-primary backup-btn">Backup</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped b-t b-light">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="136px">
|
||||
Date
|
||||
</th>
|
||||
<th width="">
|
||||
Remark
|
||||
</th>
|
||||
<th>
|
||||
Path
|
||||
</th>
|
||||
<th width="170px">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $each := .backups}}
|
||||
<tr>
|
||||
<td>
|
||||
{{$each.createdTime|unixDatetime}}
|
||||
</td>
|
||||
<td>
|
||||
<textarea class="remark" data-id="{{$each.createdTime}}">{{$each.remark}}</textarea>
|
||||
</td>
|
||||
<td class="break-all">
|
||||
{{$each.path}}
|
||||
</td>
|
||||
<td>
|
||||
<button href="#" class="btn btn-sm btn-danger restore-btn" data-id="{{$each.createdTime}}">Restore</button>
|
||||
<a class="btn btn-sm btn-default download-attach" href="/adminData/download?createdTime={{$each.createdTime}}" target="_blank" title="Download" data-id=""><i class="fa fa-download"></i></a>
|
||||
<button class="btn btn-sm btn-warning delete-btn" title="Delete" data-id="{{$each.createdTime}}"><i class="fa fa-trash-o"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$(".backup-btn").click(function(){
|
||||
ajaxGet("/adminData/backup", {}, function(ret) {
|
||||
if(ret.Ok) {
|
||||
art.tips("Success");
|
||||
location.reload();
|
||||
} else {
|
||||
art.alert(ret.Msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
// 还原
|
||||
$(".restore-btn").click(function() {
|
||||
var createdTime = $(this).data("id");
|
||||
art.confirm("Are you sure? <br />Note. Leanote will do the following steps: <br />1)Backup the current database first. <br />2) And then delete the database. <br />3) Restore database from the selected version.", function() {
|
||||
ajaxGet("/adminData/restore", {createdTime: createdTime}, function(ret) {
|
||||
if(ret.Ok) {
|
||||
art.tips("Success");
|
||||
location.reload();
|
||||
} else {
|
||||
art.alert(ret.Msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$(".delete-btn").click(function() {
|
||||
var createdTime = $(this).data("id");
|
||||
art.confirm("Are you sure?", function() {
|
||||
ajaxGet("/adminData/delete", {createdTime: createdTime}, function(ret) {
|
||||
if(ret.Ok) {
|
||||
art.tips("Success");
|
||||
location.reload();
|
||||
} else {
|
||||
art.alert(ret.Msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$(".remark").change(function() {
|
||||
var createdTime = $(this).data("id");
|
||||
var remark = $(this).val();
|
||||
ajaxPost("/adminData/updateRemark", {createdTime: createdTime, remark: remark}, function(ret) {
|
||||
if(ret.Ok) {
|
||||
art.tips("Update Remark Success");
|
||||
} else {
|
||||
art.alert(ret.Msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
||||
76
app/views/Admin/Email/emailDialog.html
Normal file
76
app/views/Admin/Email/emailDialog.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<div class="row" style="width: 500px;
|
||||
height: 500px;
|
||||
overflow-y: scroll;">
|
||||
|
||||
<div class="col-sm-12">
|
||||
<form id="sendEmailForm">
|
||||
<section class="panel panel-default">
|
||||
<header class="panel-heading font-bold">Email</header>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Email List</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="emails">{{.emailsNl}}</textarea>
|
||||
input email line by line
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Select Old Email</label>
|
||||
<select class="form-control old-emails">
|
||||
<option value="">---Select---</option>
|
||||
{{range $subject, $body := .map.oldEmails}}
|
||||
<option>
|
||||
{{$subject}}
|
||||
</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" class="form-control" id="latestEmailSubject" name="latestEmailSubject" value="{{$.str.latestEmailSubject}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<textarea type="text" rows="10" id="latestEmailBody" class="form-control" name="latestEmailBody">{{$.str.latestEmailBody}}</textarea>
|
||||
</div>
|
||||
<label class="checkbox-inline"> <input type="checkbox" id="saveAsOldEmail" name="saveAsOldEmail" value="1"> Save As Old Email </label>
|
||||
</div>
|
||||
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submitEmail" class="btn btn-success btn-s-xs">Submit</button>
|
||||
</footer>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var oldEmails = eval("(" + {{json .map.oldEmails}} + ")");
|
||||
$(function() {
|
||||
$(".old-emails").change(function() {
|
||||
var subject = $(this).val();
|
||||
var body = oldEmails[subject];
|
||||
if(subject) {
|
||||
$("#latestEmailSubject").val(subject);
|
||||
$("#latestEmailBody").val(body);
|
||||
$("#saveAsOldEmail").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
$("#submitEmail").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminEmail/sendToUsers2", getFormJsonData("sendEmailForm"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
191
app/views/Admin/Email/list.html
Normal file
191
app/views/Admin/Email/list.html
Normal file
@@ -0,0 +1,191 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Email Logs</h3></div>
|
||||
|
||||
<section class="panel panel-default">
|
||||
<div class="row wrapper">
|
||||
<div class="col-sm-5 m-b-xs">
|
||||
<button class="btn btn-sm btn-default bulk-send">
|
||||
Send
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default bulk-delete">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-sm-4 m-b-xs">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="input-group search-group">
|
||||
<input type="text" class="input-sm form-control" placeholder="Email" id="keywords" value="{{.keywords}}" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-sm btn-default" type="button" data-url="/adminEmail/list">Search</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped b-t b-light">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="20">
|
||||
<input type="checkbox">
|
||||
</th>
|
||||
{{$url := urlConcat "/adminEmail/list" "keywords" .keywords}}
|
||||
<th
|
||||
{{sorterTh $url "email" .sorter}}
|
||||
>
|
||||
Email
|
||||
<span class="th-sort">
|
||||
<i class="fa fa-sort-down"></i>
|
||||
<i class="fa fa-sort-up"></i>
|
||||
<i class="fa fa-sort"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
{{sorterTh $url "subject" .sorter}}
|
||||
>
|
||||
Subject
|
||||
<span class="th-sort">
|
||||
<i class="fa fa-sort-down"></i>
|
||||
<i class="fa fa-sort-up"></i>
|
||||
<i class="fa fa-sort"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
{{sorterTh $url "ok" .sorter}}
|
||||
>
|
||||
Ok
|
||||
<span class="th-sort">
|
||||
<i class="fa fa-sort-down"></i>
|
||||
<i class="fa fa-sort-up"></i>
|
||||
<i class="fa fa-sort"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
{{sorterTh $url "msg" .sorter}}
|
||||
>
|
||||
Msg
|
||||
<span class="th-sort">
|
||||
<i class="fa fa-sort-down"></i>
|
||||
<i class="fa fa-sort-up"></i>
|
||||
<i class="fa fa-sort"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
{{sorterTh $url "createdTime" .sorter}}
|
||||
>
|
||||
Date
|
||||
<span class="th-sort">
|
||||
<i class="fa fa-sort-down"></i>
|
||||
<i class="fa fa-sort-up"></i>
|
||||
<i class="fa fa-sort"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .emails}}
|
||||
<tr id="tr_{{.LogId.Hex}}">
|
||||
<td>
|
||||
<input type="checkbox" class="ck" data-email="{{.Email}}" data-id="{{.LogId.Hex}}">
|
||||
</td>
|
||||
<td>
|
||||
{{.Email}}
|
||||
</td>
|
||||
<td>
|
||||
{{.Subject}}
|
||||
</td>
|
||||
<td>
|
||||
{{.Ok}}
|
||||
</td>
|
||||
<td>
|
||||
{{.Msg}}
|
||||
</td>
|
||||
<td>
|
||||
{{.CreatedTime|datetime}}
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" class="btn btn-default send-email" data-email="{{.Email}}">Send</a>
|
||||
<a href="#" class="btn btn-default delete-email" data-id="{{.LogId.Hex}}">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<footer class="panel-footer">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 hidden-xs">
|
||||
<button class="btn btn-sm btn-default bulk-send">
|
||||
Send
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default bulk-delete">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-sm-4 text-right text-center-xs">
|
||||
{{set . "url" (urlConcat "/adminEmail/list" "sorter" .sorter "keywords" .keywords)}}
|
||||
{{template "admin/user/page.html" .}}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$(".send-email").click(function() {
|
||||
openSendEmailDialog($(this).data("email"));
|
||||
});
|
||||
$(".bulk-send").click(function() {
|
||||
var emails = [];
|
||||
$(".ck:checked").each(function() {
|
||||
emails.push($(this).data("email"));
|
||||
});
|
||||
if(emails.length == 0) {
|
||||
art.alert("No user");
|
||||
return;
|
||||
}
|
||||
openSendEmailDialog(emails.join(","));
|
||||
});
|
||||
|
||||
function deleteEmails(ids) {
|
||||
if(!isArray(ids)) {
|
||||
ids = [ids];
|
||||
}
|
||||
ajaxPost("/adminEmail/deleteEmails", {ids: ids.join(",")}, function(ret) {
|
||||
if(ret.Ok) {
|
||||
if(ids.length > 8) {
|
||||
location.reload();
|
||||
}
|
||||
for(var i in ids) {
|
||||
$("#tr_" + ids[i]).remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(".delete-email").click(function() {
|
||||
var id = $(this).data('id');
|
||||
deleteEmails(id);
|
||||
});
|
||||
|
||||
$(".bulk-delete").click(function() {
|
||||
var ids = [];
|
||||
$(".ck:checked").each(function() {
|
||||
ids.push($(this).data("id"));
|
||||
});
|
||||
if(ids.length == 0) {
|
||||
art.alert("No email");
|
||||
return;
|
||||
}
|
||||
deleteEmails(ids);
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
||||
33
app/views/Admin/Email/page.html
Normal file
33
app/views/Admin/Email/page.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{{if gt .pageInfo.TotalPage 1}}
|
||||
<ul class="pagination pagination-sm m-t-none m-b-none">
|
||||
<li class="{{if eq $.pageInfo.CurPage 1}}disabled{{end}}" >
|
||||
<a href="{{if eq $.pageInfo.CurPage 1}}javascript:;{{else}}{{sub $.pageInfo.CurPage | urlConcat $.url "page" }}{{end}}">
|
||||
<i class="fa fa-chevron-left">
|
||||
</i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{{range $i := N 1 .pageInfo.TotalPage}}
|
||||
{{if eq $i $.pageInfo.CurPage}}
|
||||
<li class="active">
|
||||
<a href="javascript:;">
|
||||
{{$i}}
|
||||
</a>
|
||||
</li>
|
||||
{{else}}
|
||||
<li class="">
|
||||
<a href="{{urlConcat $.url "page" $i}}">
|
||||
{{$i}}
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<li class="{{if eq .pageInfo.CurPage .pageInfo.TotalPage}}disabled{{end}}" >
|
||||
<a href="{{if eq .pageInfo.CurPage .pageInfo.TotalPage}}javascript:;{{else}}{{add $.pageInfo.CurPage | urlConcat $.url "page" }}{{end}}">
|
||||
<i class="fa fa-chevron-right">
|
||||
</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{{end}}
|
||||
85
app/views/Admin/Email/send.html
Normal file
85
app/views/Admin/Email/send.html
Normal file
@@ -0,0 +1,85 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Send Email</h3></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12">
|
||||
<form id="formContainer">
|
||||
<section class="panel panel-default">
|
||||
<header class="panel-heading font-bold">Email</header>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Email List</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="sendEmails">{{.str.sendEmails}}</textarea>
|
||||
input email line by line
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Select Old Email</label>
|
||||
<select class="form-control old-emails">
|
||||
<option value="">---Select---</option>
|
||||
{{range $subject, $body := .map.oldEmails}}
|
||||
<option>
|
||||
{{$subject}}
|
||||
</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" class="form-control" id="latestEmailSubject" name="latestEmailSubject" value="{{$.str.latestEmailSubject}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<textarea type="text" rows="10" id="latestEmailBody" class="form-control" name="latestEmailBody">{{$.str.latestEmailBody}}</textarea>
|
||||
</div>
|
||||
<label class="checkbox-inline"> <input type="checkbox" id="saveAsOldEmail" name="saveAsOldEmail" value="1"> Save As Old Email </label>
|
||||
</div>
|
||||
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
|
||||
</footer>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
|
||||
<script>
|
||||
var oldEmails = eval("(" + {{json .map.oldEmails}} + ")");
|
||||
$(function() {
|
||||
init_validator("#formContainer");
|
||||
|
||||
$(".old-emails").change(function() {
|
||||
var subject = $(this).val();
|
||||
var body = oldEmails[subject];
|
||||
if(subject) {
|
||||
$("#latestEmailSubject").val(subject);
|
||||
$("#latestEmailBody").val(body);
|
||||
$("#saveAsOldEmail").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
$("#submit").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
if($("#formContainer").valid()) {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminEmail/sendEmailToEmails", getFormJsonData("formContainer"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
||||
105
app/views/Admin/Email/sendToUsers.html
Normal file
105
app/views/Admin/Email/sendToUsers.html
Normal file
@@ -0,0 +1,105 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Send Email to Users</h3></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12">
|
||||
<form id="formContainer">
|
||||
<section class="panel panel-default">
|
||||
<header class="panel-heading font-bold">User filter</header>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Search Email/Username</label>
|
||||
<input type="text" class="form-control" name="userFilterEmail" value="{{.str.userFilterEmail}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="checkbox-inline"> <input type="checkbox" name="verified" value="1"> Verfied </label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>White List</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="userFilterWhiteList">{{.str.userFilterWhiteList}}</textarea>
|
||||
input email line by line
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Black List</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="userFilterBlackList">{{.str.userFilterBlackList}}</textarea>
|
||||
input email line by line
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel panel-default">
|
||||
<header class="panel-heading font-bold">Email</header>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Select Old Email</label>
|
||||
<select class="form-control old-emails">
|
||||
<option value="">---Select---</option>
|
||||
{{range $subject, $body := .map.oldEmails}}
|
||||
<option>
|
||||
{{$subject}}
|
||||
</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" class="form-control" id="latestEmailSubject" name="latestEmailSubject" value="{{$.str.latestEmailSubject}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<textarea type="text" rows="10" id="latestEmailBody" class="form-control" name="latestEmailBody">{{$.str.latestEmailBody}}</textarea>
|
||||
</div>
|
||||
<label class="checkbox-inline"> <input type="checkbox" id="saveAsOldEmail" name="saveAsOldEmail" value="1"> Save As Old Email </label>
|
||||
</div>
|
||||
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
|
||||
</footer>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
|
||||
<script>
|
||||
var oldEmails = eval("(" + {{json .map.oldEmails}} + ")");
|
||||
$(function() {
|
||||
init_validator("#formContainer");
|
||||
|
||||
$(".old-emails").change(function() {
|
||||
var subject = $(this).val();
|
||||
var body = oldEmails[subject];
|
||||
if(subject) {
|
||||
$("#latestEmailSubject").val(subject);
|
||||
$("#latestEmailBody").val(body);
|
||||
$("#saveAsOldEmail").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
$("#submit").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
if($("#formContainer").valid()) {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminEmail/sendToUsers", getFormJsonData("formContainer"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
||||
62
app/views/Admin/Email/set.html
Normal file
62
app/views/Admin/Email/set.html
Normal file
@@ -0,0 +1,62 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Email Configuration</h3></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-6">
|
||||
<form id="add_user_form">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Host</label>
|
||||
<input type="text" class="form-control" name="emailHost" value="{{.str.emailHost}}" placeholder="eg. smtp.ym.163.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Port</label>
|
||||
<input type="text" class="form-control" name="emailPort" value="{{.str.emailPort}}" placeholder="eg. 25">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Username</label>
|
||||
<input type="text" class="form-control" name="emailUsername" value="{{.str.emailUsername}}" placeholder="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Password</label>
|
||||
<input type="text" class="form-control" name="emailPassword" value="{{.str.emailPassword}}" placeholder="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
|
||||
</footer>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
init_validator("#add_user_form");
|
||||
|
||||
$("#submit").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
if($("#add_user_form").valid()) {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminEmail/set", getFormJsonData("add_user_form"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
||||
325
app/views/Admin/Email/template.html
Normal file
325
app/views/Admin/Email/template.html
Normal file
@@ -0,0 +1,325 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Email Template</h3></div>
|
||||
|
||||
<style>
|
||||
.preview {
|
||||
overflow: auto;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
label {
|
||||
}
|
||||
</style>
|
||||
|
||||
<form id="add_user_form">
|
||||
<section class="panel panel-default">
|
||||
<header class="panel-heading bg-light">
|
||||
<ul class="nav nav-tabs nav-justified">
|
||||
<li class="active"><a href="#tab1" data-toggle="tab">Layout</a></li>
|
||||
<li class=""><a href="#tab2" data-toggle="tab">Register</a></li>
|
||||
<li class=""><a href="#tab3" data-toggle="tab">Update Email</a></li>
|
||||
<li ><a href="#tab4" data-toggle="tab">Find Passord</a></li>
|
||||
<li ><a href="#tab5" data-toggle="tab">Invite Register</a></li>
|
||||
<li ><a href="#tab6" data-toggle="tab">Blog Comment</a></li>
|
||||
|
||||
</ul>
|
||||
</header>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tab1">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<b>Layout</b>
|
||||
<div>
|
||||
Available tokens:
|
||||
<code>$.subject</code>
|
||||
<code>$.siteUrl</code>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Header</label>
|
||||
<textarea type="text" id="emailHeader" rows="10" class="form-control" name="emailTemplateHeader">{{.str.emailTemplateHeader}}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Footer</label>
|
||||
<textarea type="text" id="emailFooter" rows="10" class="form-control" name="emailTemplateFooter">{{.str.emailTemplateFooter}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tab2">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<b>Register Welcome And Email Validation:</b>
|
||||
<div>
|
||||
Available tokens:
|
||||
<code>header</code>
|
||||
<code>footer</code>
|
||||
<code>$.siteUrl</code>
|
||||
<code>$.tokenUrl</code>
|
||||
<code>$.token</code>
|
||||
<code>$.tokenTimeout</code>
|
||||
<code>$.user.userId</code>
|
||||
<code>$.user.email</code>
|
||||
<code>$.user.username</code>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" class="form-control" name="emailTemplateRegisterSubject" value="{{.str.emailTemplateRegisterSubject}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="emailTemplateRegister">{{.str.emailTemplateRegister}}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
Preview
|
||||
<div class="preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tab3">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<b>Update Email and Send Active Email</b>
|
||||
<div>
|
||||
Available tokens:
|
||||
<code>header</code>
|
||||
<code>footer</code>
|
||||
<code>$.siteUrl</code>
|
||||
<code>$.tokenUrl</code>
|
||||
<code>$.token</code>
|
||||
<code>$.tokenTimeout</code>
|
||||
<code>$.user.userId</code>
|
||||
<code>$.user.email</code>
|
||||
<code>$.user.username</code>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" class="form-control" name="emailTemplateUpdateEmailSubject" value="{{.str.emailTemplateUpdateEmailSubject}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="emailTemplateUpdateEmail">{{.str.emailTemplateUpdateEmail}}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Preview
|
||||
<div class="preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab4">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<b>Find Passord</b>
|
||||
<div>
|
||||
Available tokens:
|
||||
<code>header</code>
|
||||
<code>footer</code>
|
||||
<code>$.siteUrl</code>
|
||||
<code>$.tokenUrl</code>
|
||||
<code>$.token</code>
|
||||
<code>$.tokenTimeout</code>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" class="form-control" name="emailTemplateFindPasswordSubject" value="{{.str.emailTemplateFindPasswordSubject}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="emailTemplateFindPassword">{{.str.emailTemplateFindPassword}}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Preview
|
||||
<div class="preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab5">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<b>Invite Register</b>
|
||||
<div>
|
||||
Available tokens:
|
||||
<code>header</code>
|
||||
<code>footer</code>
|
||||
<code>$.siteUrl</code>
|
||||
<code>$.registerUrl</code>
|
||||
<code>$.user.username</code>
|
||||
<code>$.user.email</code>
|
||||
<code>$.content</code>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" class="form-control" name="emailTemplateInviteSubject" value="{{.str.emailTemplateInviteSubject}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="emailTemplateInvite">{{.str.emailTemplateInvite}}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Preview
|
||||
<div class="preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab6">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<b>Blog Comment</b>
|
||||
<div>
|
||||
Available tokens:
|
||||
<code>header</code>
|
||||
<code>footer</code>
|
||||
<code>$.siteUrl</code>
|
||||
<code>$.blogUrl</code>
|
||||
|
||||
<br />
|
||||
<code>$.commentContent</code>
|
||||
|
||||
<br />
|
||||
<code>$.blog.id</code>
|
||||
<code>$.blog.title</code>
|
||||
<code>$.blog.url</code>
|
||||
|
||||
<br />
|
||||
<code>$.commentUser.userId</code>
|
||||
<code>$.commentUser.username</code>
|
||||
<code>$.commentUser.email</code>
|
||||
<code>$.commentUser.isBlogAuthor</code>
|
||||
|
||||
<br />
|
||||
<code>$.commentedUser.userId</code>
|
||||
<code>$.commentedUser.username</code>
|
||||
<code>$.commentedUser.email</code>
|
||||
<code>$.commentedUser.isBlogAuthor</code>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Subject</label>
|
||||
<input type="text" class="form-control" name="emailTemplateCommentSubject" value="{{.str.emailTemplateCommentSubject}}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Body</label>
|
||||
<textarea type="text" rows="10" class="form-control" name="emailTemplateComment">{{.str.emailTemplateComment}}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Preview
|
||||
<div class="preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
|
||||
</footer>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
$("code").each(function() {
|
||||
var h = $(this).text();
|
||||
$(this).text("{" + "{" + h + "}" + "}");
|
||||
});
|
||||
|
||||
function previewEmail(t) {
|
||||
var $p = $(t).closest(".row");
|
||||
var tpl = $(t).val();
|
||||
var subject = $p.find("input").val() || "";
|
||||
var $preview = $p.find(".preview");
|
||||
|
||||
var header = $("#emailHeader").val();
|
||||
var footer = $("#emailFooter").val();
|
||||
|
||||
header = header.replace("{" + "{$.subject}" + "}", subject);
|
||||
tpl = tpl.replace("{" + "{header}" + "}", header);
|
||||
tpl = tpl.replace("{" + "{footer}" + "}", footer);
|
||||
|
||||
$preview.html(tpl);
|
||||
}
|
||||
|
||||
$("textarea").each(function() {
|
||||
previewEmail(this);
|
||||
});
|
||||
|
||||
$("textarea").keyup(function() {
|
||||
previewEmail(this);
|
||||
});
|
||||
|
||||
init_validator("#add_user_form");
|
||||
|
||||
$("#submit").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
if($("#add_user_form").valid()) {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminEmail/template", getFormJsonData("add_user_form"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
||||
@@ -9,11 +9,11 @@
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Demo Username</label>
|
||||
<input type="text" class="form-control" name="demoUsername" value="{{.demoUsername}}">
|
||||
<input type="text" class="form-control" name="demoUsername" value="{{.str.demoUsername}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Demo Password</label>
|
||||
<input type="text" class="form-control" name="demoPassword" value="{{.demoPassword}}">
|
||||
<input type="text" class="form-control" name="demoPassword" value="{{.str.demoPassword}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
173
app/views/Admin/Setting/shareNote.html
Normal file
173
app/views/Admin/Setting/shareNote.html
Normal file
@@ -0,0 +1,173 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Register Share Note</h3></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-6">
|
||||
<form id="formContainer">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Shared UserId</label>
|
||||
<input type="text" class="form-control" name="registerSharedUserId" value="{{.str.registerSharedUserId}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Shared Notebooks</label>
|
||||
<div class="clearfix" id="notebooks">
|
||||
{{range $notebook := .arrMap.registerSharedNotebooks}}
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" class="form-control" placeholder="notebookId" name="registerSharedNotebookIds[]"
|
||||
value="{{$notebook.notebookId}}"
|
||||
>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<select class="form-control" name="registerSharedNotebookPerms[]">
|
||||
<option value="0" {{if eq $notebook.perm "0"}}selected{{end}}>Read Only</option>
|
||||
<option value="1" {{if eq $notebook.perm "1"}}selected{{end}}>Writable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" class="form-control" placeholder="notebookId" name="registerSharedNotebookIds[]">
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<select class="form-control" name="registerSharedNotebookPerms[]">
|
||||
<option value="0">Read Only</option>
|
||||
<option value="1">Writable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" class="form-control" placeholder="notebookId" name="registerSharedNotebookIds[]">
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<select class="form-control" name="registerSharedNotebookPerms[]">
|
||||
<option value="0">Read Only</option>
|
||||
<option value="1">Writable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
The notebooks will shared to register user
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Shared Notes</label>
|
||||
<div class="clearfix" id="notebooks">
|
||||
{{range $note := .arrMap.registerSharedNotes}}
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" class="form-control" name="registerSharedNoteIds[]"
|
||||
value="{{$note.noteId}}"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<select class="form-control" name="registerSharedNotePerms[]">
|
||||
<option value="0" {{if eq $note.perm "0"}}selected{{end}}>Read Only</option>
|
||||
<option value="1" {{if eq $note.perm "1"}}selected{{end}}>Writable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" class="form-control" placeholder="noteId" name="registerSharedNoteIds[]">
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<select class="form-control" name="registerSharedNotePerms[]">
|
||||
<option value="0">Read Only</option>
|
||||
<option value="1">Writable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" class="form-control" placeholder="noteId" name="registerSharedNoteIds[]">
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<select class="form-control" name="registerSharedNotePerms[]">
|
||||
<option value="0">Read Only</option>
|
||||
<option value="1">Writable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
The notes will shared to register user
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Copy Notes</label>
|
||||
<div class="clearfix" id="notebooks">
|
||||
{{range $noteId := .arr.registerCopyNoteIds}}
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<input type="text" class="form-control" name="registerCopyNoteIds[]"
|
||||
value="{{$noteId}}"
|
||||
placeholder="noteId"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<input type="text" class="form-control" name="registerCopyNoteIds[]" placeholder="noteId"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<input type="text" class="form-control" name="registerCopyNoteIds[]" placeholder="noteId"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
The notes will copy to register user
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
|
||||
</footer>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
|
||||
<script>
|
||||
var a = "{{json .arrMap}}"
|
||||
|
||||
|
||||
$(function() {
|
||||
|
||||
init_validator("#formContainer");
|
||||
|
||||
$("#submit").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
if($("#formContainer").valid()) {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminSetting/shareNote", getFormJsonData("formContainer"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips(ret.Msg || "Success");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
||||
@@ -45,7 +45,7 @@ $(function() {
|
||||
ajaxPost("/auth/doRegister", getFormJsonData("add_user_form"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
art.alert(ret.Msg);
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">User</h3></div>
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Users</h3></div>
|
||||
|
||||
<section class="panel panel-default">
|
||||
<div class="row wrapper">
|
||||
<div class="col-sm-5 m-b-xs">
|
||||
<select class="input-sm form-control input-s-sm inline v-middle">
|
||||
<option value="0">
|
||||
<option value="">
|
||||
Bulk action
|
||||
</option>
|
||||
<option value="1">
|
||||
Delete selected
|
||||
</option>
|
||||
<option value="2">
|
||||
Bulk edit
|
||||
</option>
|
||||
<option value="3">
|
||||
Export
|
||||
Send Email
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-default">
|
||||
<button class="btn btn-sm btn-default bulk-btn">
|
||||
Apply
|
||||
</button>
|
||||
</div>
|
||||
@@ -90,7 +84,7 @@
|
||||
{{range .users}}
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" name="post[]" value="2">
|
||||
<input type="checkbox" class="ck" data-email="{{.Email}}" value="2">
|
||||
</td>
|
||||
<td>
|
||||
{{.Email}}
|
||||
@@ -103,15 +97,9 @@
|
||||
</td>
|
||||
<td>
|
||||
{{.CreatedTime|datetime}}
|
||||
<a href="#" class="active" data-toggle="class">
|
||||
<i class="fa fa-check text-success text-active">
|
||||
</i>
|
||||
<i class="fa fa-times text-danger text">
|
||||
</i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" class="btn btn-default">Send Email</a>
|
||||
<a href="#" class="btn btn-default send-email" data-email="{{.Email}}">Send Email</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
@@ -122,32 +110,21 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-4 hidden-xs">
|
||||
<select class="input-sm form-control input-s-sm inline v-middle">
|
||||
<option value="0">
|
||||
<option value="">
|
||||
Bulk action
|
||||
</option>
|
||||
<option value="1">
|
||||
Delete selected
|
||||
</option>
|
||||
<option value="2">
|
||||
Bulk edit
|
||||
</option>
|
||||
<option value="3">
|
||||
Export
|
||||
Send Email
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-default">
|
||||
<button class="btn btn-sm btn-default bulk-btn">
|
||||
Apply
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-sm-4 text-center">
|
||||
<small class="text-muted inline m-t-sm m-b-sm">
|
||||
showing 20-30 of 50 items
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-sm-4 text-right text-center-xs">
|
||||
|
||||
<div class="col-sm-8 text-right text-center-xs">
|
||||
{{set . "url" (urlConcat "/adminUser/index" "sorter" .sorter "keywords" .keywords)}}
|
||||
{{template "admin/user/page.html" .}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -157,6 +134,23 @@
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$(".send-email").click(function() {
|
||||
openSendEmailDialog($(this).data("email"));
|
||||
});
|
||||
$(".bulk-btn").click(function() {
|
||||
// email
|
||||
if($(this).prev().val() == "1") {
|
||||
var emails = [];
|
||||
$(".ck:checked").each(function() {
|
||||
emails.push($(this).data("email"));
|
||||
});
|
||||
if(emails.length == 0) {
|
||||
art.alert("No user");
|
||||
return;
|
||||
}
|
||||
openSendEmailDialog(emails.join(","));
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,32 +5,26 @@
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
<!-- Bootstrap -->
|
||||
<!-- App -->
|
||||
<script src="/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="/js/bootstrap.js"></script>
|
||||
<script src="/public/admin/js/artDialog/jquery.artDialog.js?skin=default"></script>
|
||||
<script src="/public/js/common.js"></script>
|
||||
<script src="/public/admin/js/admin.js"></script>
|
||||
<script>
|
||||
$(function(){
|
||||
var pathname = location.pathname;
|
||||
var arr = pathname.split("/");
|
||||
if(arr.length == 0){
|
||||
return;
|
||||
}
|
||||
var controller = "";
|
||||
var action = "";
|
||||
if(arr[0] == "") {
|
||||
arr = arr.slice(1);
|
||||
}
|
||||
controller = arr[0];
|
||||
if(arr.length > 1) {
|
||||
action = arr[1];
|
||||
}
|
||||
$("#nav > li").removeClass("active");
|
||||
$("#" + controller + "Nav").addClass("active");
|
||||
|
||||
$('a[href="' + pathname + '"]').parent().addClass("active");
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="/js/bootstrap.js"></script>
|
||||
<script src="/public/admin/js/artDialog/jquery.artDialog.js?skin=default"></script>
|
||||
<script src="/public/js/common.js"></script>
|
||||
<script src="/public/admin/js/admin.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
var pathname = location.pathname; // admin/t
|
||||
var search = location.search; // ?t=xxx, 如果有?page呢
|
||||
var fullPath = pathname;
|
||||
if(search.indexOf("?t=") >= 0) {
|
||||
var fullPath = pathname + search; // /admin/t?t=xxx
|
||||
}
|
||||
|
||||
$("#nav > li").removeClass("active");
|
||||
// 自己
|
||||
var $thisLi = $('#nav a[href^="' + fullPath + '"]').parent();
|
||||
$thisLi.addClass("active");
|
||||
// 父也active
|
||||
$thisLi.parent().parent().addClass('active');
|
||||
});
|
||||
</script>
|
||||
@@ -1,15 +1,15 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Workset</h3> <small>Welcome you!</small> </div>
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Dashboard</h3></div>
|
||||
<section class="panel panel-default">
|
||||
<div class="row m-l-none m-r-none bg-light lter">
|
||||
<div class="col-sm-6 col-md-3 padder-v b-r b-light">
|
||||
<span class="fa-stack fa-2x pull-left m-r-sm">
|
||||
<i class="fa fa-circle fa-stack-2x text-info"></i>
|
||||
<i class="fa fa-male fa-stack-1x text-white"></i>
|
||||
<i class="fa fa-users fa-stack-1x text-white"></i>
|
||||
</span>
|
||||
<a class="clear" href="#">
|
||||
<span class="h3 block m-t-xs"><strong>52,000</strong></span>
|
||||
<small class="text-muted text-uc">users</small>
|
||||
<a class="clear" href="/adminUser/index">
|
||||
<span class="h3 block m-t-xs"><strong>{{.countUser}}</strong></span>
|
||||
<small class="text-muted text-uc">Users</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3 padder-v b-r b-light lt">
|
||||
@@ -17,11 +17,21 @@
|
||||
<i class="fa fa-circle fa-stack-2x text-warning"></i>
|
||||
<i class="fa fa-file-o fa-stack-1x text-white"></i>
|
||||
</span>
|
||||
<a class="clear" href="#">
|
||||
<span class="h3 block m-t-xs"><strong>1312,000</strong></span>
|
||||
<small class="text-muted text-uc">notes</small>
|
||||
<a class="clear" href="javascript:;">
|
||||
<span class="h3 block m-t-xs"><strong>{{.countNote}}</strong></span>
|
||||
<small class="text-muted text-uc">Notes</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3 padder-v b-r b-light">
|
||||
<span class="fa-stack fa-2x pull-left m-r-sm">
|
||||
<i class="fa fa-circle fa-stack-2x text-info"></i>
|
||||
<i class="fa fa-bold fa-stack-1x text-white"></i>
|
||||
</span>
|
||||
<a class="clear" href="/adminBlog/index">
|
||||
<span class="h3 block m-t-xs"><strong>{{.countBlog}}</strong></span>
|
||||
<small class="text-muted text-uc">Blogs</small>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -30,51 +40,10 @@
|
||||
<h4 class="font-thin padder">
|
||||
Leanote Events
|
||||
</h4>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
Wellcome
|
||||
<a href="#" class="text-info">
|
||||
@Drew Wllon
|
||||
</a>
|
||||
and play this web application template, have fun1
|
||||
</p>
|
||||
<small class="block text-muted">
|
||||
<i class="fa fa-clock-o">
|
||||
</i>
|
||||
2 minuts ago
|
||||
</small>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
Morbi nec
|
||||
<a href="#" class="text-info">
|
||||
@Jonathan George
|
||||
</a>
|
||||
nunc condimentum ipsum dolor sit amet, consectetur
|
||||
</p>
|
||||
<small class="block text-muted">
|
||||
<i class="fa fa-clock-o">
|
||||
</i>
|
||||
1 hour ago
|
||||
</small>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<p>
|
||||
<a href="#" class="text-info">
|
||||
@Josh Long
|
||||
</a>
|
||||
Vestibulum ullamcorper sodales nisi nec adipiscing elit.
|
||||
</p>
|
||||
<small class="block text-muted">
|
||||
<i class="fa fa-clock-o">
|
||||
</i>
|
||||
2 hours ago
|
||||
</small>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-group" id="eventsList"></ul>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
<section class="panel panel-default">
|
||||
<form>
|
||||
<textarea class="form-control no-border" rows="3" placeholder="Suggestions to leanote"></textarea>
|
||||
@@ -84,23 +53,32 @@
|
||||
POST
|
||||
</button>
|
||||
<ul class="nav nav-pills nav-sm">
|
||||
<!--
|
||||
<li>
|
||||
<a href="#">
|
||||
<i class="fa fa-camera text-muted">
|
||||
</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<i class="fa fa-video-camera text-muted">
|
||||
</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
-->
|
||||
</footer>
|
||||
</section>
|
||||
-->
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
<script>
|
||||
$(function() {
|
||||
// leanote动态
|
||||
// http://leanote.com/blog/cate/5446753cfacfaa4f56000000
|
||||
var url = "http://localhost:9000/blog/listCateLatest/54269c83e5276724ac000000";
|
||||
function renderItem(item) {
|
||||
return '<li class="list-group-item"><p><a target="_blank" href="http://leanote.com/blog/view/' + item.NoteId + '">' + item.Title + '</a></p><small class="block text-muted"><i class="fa fa-clock-o"></i> ' + goNowToDatetime(item.PublicTime) + '</small></li>';
|
||||
}
|
||||
$.getJSON(url, function(data) {
|
||||
log(data);
|
||||
if(typeof data == "object" && data.Ok) {
|
||||
var list = data.List;
|
||||
var html = "";
|
||||
for(var i = 0; i < list.length; ++i) {
|
||||
var item = list[i];
|
||||
html += renderItem(item);
|
||||
}
|
||||
$("#eventsList").html(html);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{{template "admin/end.html" .}}
|
||||
@@ -1,8 +1,21 @@
|
||||
<nav class="nav-primary hidden-xs">
|
||||
<ul class="nav" id="nav">
|
||||
|
||||
<li class="active" id="adminUserNav">
|
||||
<a href="index.html">
|
||||
<li class="active">
|
||||
<a href="/admin/index">
|
||||
<i class="fa fa-dashboard icon">
|
||||
<b class="bg-success">
|
||||
</b>
|
||||
</i>
|
||||
<span>
|
||||
Dashboard
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="adminUserNav">
|
||||
<a href="#">
|
||||
<i class="fa fa-users icon">
|
||||
<b class="bg-danger">
|
||||
</b>
|
||||
@@ -22,7 +35,7 @@
|
||||
<li>
|
||||
<a href="/adminUser/index">
|
||||
<span>
|
||||
List
|
||||
Users
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -47,11 +60,65 @@
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li id="adminEmailNav">
|
||||
<a href="#layout">
|
||||
<i class="fa fa-envelope-o icon">
|
||||
<b class="bg-warning">
|
||||
</b>
|
||||
</i>
|
||||
<span class="pull-right">
|
||||
<i class="fa fa-angle-down text">
|
||||
</i>
|
||||
<i class="fa fa-angle-up text-active">
|
||||
</i>
|
||||
</span>
|
||||
<span>
|
||||
Email
|
||||
</span>
|
||||
</a>
|
||||
<ul class="nav lt">
|
||||
<li>
|
||||
<a href="/admin/t?t=email/set">
|
||||
<span>
|
||||
Configuration
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/t?t=email/template">
|
||||
<span>
|
||||
Template
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/t?t=email/sendToUsers">
|
||||
<span>
|
||||
Send Email to Users
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/t?t=email/send">
|
||||
<span>
|
||||
Send Email
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/adminEmail/list">
|
||||
<span>
|
||||
Email Logs
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li id="adminSettingNav">
|
||||
<a href="#layout">
|
||||
<i class="fa fa-cog icon">
|
||||
<b class="bg-warning">
|
||||
<b class="bg-info">
|
||||
</b>
|
||||
</i>
|
||||
<span class="pull-right">
|
||||
@@ -66,60 +133,56 @@
|
||||
</a>
|
||||
<ul class="nav lt">
|
||||
<li>
|
||||
<a href="layout-c.html">
|
||||
<span>
|
||||
Register
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="layout-c.html">
|
||||
<span>
|
||||
Login
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="layout-r.html">
|
||||
<span>
|
||||
Email
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="layout-h.html">
|
||||
<span>
|
||||
Share
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/adminSetting/blog">
|
||||
<span>
|
||||
Blog
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/adminSetting/demo">
|
||||
<a href="/admin/t?t=setting/demo">
|
||||
<span>
|
||||
Demo User
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/admin/t?t=setting/shareNote">
|
||||
<span>
|
||||
Register Share Note
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="#layout">
|
||||
<a href="#">
|
||||
<i class="fa fa-columns icon">
|
||||
<b class="bg-warning">
|
||||
<b class="bg-success">
|
||||
</b>
|
||||
</i>
|
||||
<span class="pull-right">
|
||||
<i class="fa fa-angle-down text">
|
||||
</i>
|
||||
<i class="fa fa-angle-up text-active">
|
||||
</i>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
Others
|
||||
Data
|
||||
</span>
|
||||
</a>
|
||||
<ul class="nav lt">
|
||||
<li>
|
||||
<a href="/admin/t?t=data/configuration">
|
||||
<span>
|
||||
Mongodb Tool Configuration
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/adminData/index">
|
||||
<span>
|
||||
Backup & Restore
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -26,18 +26,28 @@
|
||||
|
||||
<ul class="nav navbar-nav navbar-right m-n hidden-xs nav-user">
|
||||
<li class="hidden-xs">
|
||||
<a href="/index" class="dk">
|
||||
Index
|
||||
<a href="http://leanote.com" class="dk" target="_blank">
|
||||
Leanote Home
|
||||
</a>
|
||||
</li>
|
||||
<li class="hidden-xs">
|
||||
<a href="/note" class="dk">
|
||||
<a href="https://github.com/leanote/leanote" class="dk" target="_blank">
|
||||
Leanote Github
|
||||
</a>
|
||||
</li>
|
||||
<li class="hidden-xs">
|
||||
<a href="http://lea.leanote.com" class="dk" target="_blank">
|
||||
lea++
|
||||
</a>
|
||||
</li>
|
||||
<li class="hidden-xs">
|
||||
<a href="/note" class="dk" target="_blank">
|
||||
My Note
|
||||
</a>
|
||||
</li>
|
||||
<li class="hidden-xs">
|
||||
<a href="/blog/admin" class="dk">
|
||||
Blog
|
||||
<a href="/blog/admin" class="dk" target="_blank">
|
||||
My Blog
|
||||
</a>
|
||||
</li>
|
||||
<li class="hidden-xs">
|
||||
@@ -52,6 +62,7 @@
|
||||
<!-- .aside -->
|
||||
<aside class="bg-light lter b-r aside-md hidden-print hidden-xs" id="nav">
|
||||
<section class="vbox">
|
||||
<!--
|
||||
<header class="header bg-primary lter text-center clearfix">
|
||||
<div class="btn-group">
|
||||
<div class="hidden-nav-xs">
|
||||
@@ -61,6 +72,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
-->
|
||||
|
||||
<section class="w-f scrollable">
|
||||
<div class="slim-scroll" data-height="auto" data-disable-fade-out="true"
|
||||
data-distance="0" data-size="5px" data-color="#333333">
|
||||
@@ -69,14 +82,17 @@
|
||||
<!-- / nav -->
|
||||
</div>
|
||||
</section>
|
||||
<footer class="footer lt hidden-xs b-t b-light">
|
||||
<footer class="footer lt hidden-xs b-t b-light" style="min-height: initial;
|
||||
padding: 10px 15px;text-align:center;">
|
||||
<a href="http://leanote.com" target="_blank">leanote</a> © 2014
|
||||
<!--
|
||||
<a href="#nav" data-toggle="class:nav-xs" class="pull-right btn btn-sm btn-default btn-icon">
|
||||
<i class="fa fa-angle-left text">
|
||||
</i>
|
||||
<i class="fa fa-angle-right text-active">
|
||||
</i>
|
||||
</a>
|
||||
|
||||
-->
|
||||
</footer>
|
||||
</section>
|
||||
</aside>
|
||||
@@ -84,7 +100,7 @@
|
||||
<section id="content">
|
||||
<section class="vbox">
|
||||
<section class="scrollable padder">
|
||||
<!-- 导航 -->
|
||||
<!-- 导航
|
||||
<ul class="breadcrumb no-border no-radius b-b b-light pull-in">
|
||||
<li>
|
||||
<a href="index.html">
|
||||
@@ -102,5 +118,6 @@
|
||||
Components
|
||||
</li>
|
||||
</ul>
|
||||
-->
|
||||
<!-- 主要内容区 -->
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
</div>
|
||||
<div class="desc">
|
||||
{{.userBlog.AboutMe | raw}}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- comment -->
|
||||
{{template "blog/comment.html" .}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,190 @@
|
||||
{{if .userBlog.CanComment}}
|
||||
<!-- 赞 -->
|
||||
<div class="entry-controls clearfix">
|
||||
<div class="vote-section-wrapper clearfix">
|
||||
<button class="btn btn-default btn-zan" id="likeBtn"><i class="fa fa-thumbs-o-up"></i> <span id="likeNum">{{.blog.LikeNum}}</span> {{msg . "like"}}</button>
|
||||
<span class="control-item read-counts"><i class="fa fa-eye"></i> {{if .blog.ReadNum}}{{.blog.ReadNum}}{{else}}1{{end}} {{msg . "viewers"}}</span>
|
||||
</div>
|
||||
<div class="right-section">
|
||||
<div id="weixinQRCode"></div>
|
||||
<!-- google+
|
||||
<g:plusone size=”medium”></g:plusone>
|
||||
-->
|
||||
<button class="btn btn-share btn-default btn-weibo"><i class="fa fa-weibo"></i> {{msg . "sinaWeibo"}}</button>
|
||||
<button class="btn btn-share btn-default btn-weixin"><i class="fa fa-wechat"></i> {{msg . "weixin"}}</button>
|
||||
<div class="dropdown" style="display: inline-block; cursor: pointer; padding: 5px 10px;">
|
||||
<!-- open -->
|
||||
<div class="dropdown-toggle" data-hover="dropdown" data-toggle="dropdown">
|
||||
<i class="fa fa-share-square-o"></i>
|
||||
{{msg . "moreShare"}}
|
||||
</div>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="#" class="btn-share tencent-weibo"><i class="fa fa-tencent-weibo"></i> {{msg . "tencentWeibo"}}</a></li>
|
||||
<li><a href="#" class="btn-share qq"><i class="fa fa-qq"></i> {{msg . "qqZone"}}</a></li>
|
||||
<li><a href="#" class="btn-share renren"><i class="fa fa-renren"></i> {{msg . "renren"}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 举报 -->
|
||||
{{if eq .locale "zh"}}
|
||||
<div style="display: inline-block">
|
||||
<a id="reportBtn"><i class="fa fa-flag-o"></i> {{msg . "report"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="voters clearfix" id="likers">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/x-jsrender" id="tLikers">
|
||||
[[for users]]
|
||||
<a id="liker_[[:UserId]]" href="[[:~root.blogUrl]]/[[:Username]]" target="_blank" class="voter">
|
||||
[[if Logo]]
|
||||
<img alt="avatar" class="avatar-small" src="[[:Logo]]">
|
||||
[[else]]
|
||||
<img alt="avatar" class="avatar-small" src="/images/blog/default_avatar.png">
|
||||
[[/if]]
|
||||
</a>
|
||||
[[/for]]
|
||||
</script>
|
||||
{{if and .userBlog.CanComment (not (eq .userBlog.CommentType "disqus"))}}
|
||||
|
||||
<script type="text/x-jsrender" id="tComments">
|
||||
[[for comments]]
|
||||
<li class="comment-item">
|
||||
<!-- 头像 -->
|
||||
<a ui-hovercard="" target="_blank" class="avatar-link" title="[[:UserInfo.Username]]" href="[[:~root.blogUrl]]/[[:UserInfo.Username]]">
|
||||
<img class="avatar" src="[[:UserInfo.Logo]]">
|
||||
</a>
|
||||
<!-- 评论 -->
|
||||
<div class="comment-body">
|
||||
<div class="comment-hd">
|
||||
<a href="[[:~root.blogUrl]]/[[:UserInfo.Username]]" target="_blank" >[[:UserInfo.Username]]</a>
|
||||
[[if IsAuthorComment]]
|
||||
<span>({{msg . "author"}})</span>
|
||||
[[/if]]
|
||||
|
||||
<!-- 回复其它人 -->
|
||||
[[if ToUserInfo]]
|
||||
<span class="in-reply-to">
|
||||
{{rawMsg . "reply"}}
|
||||
<a href="[[:~root.blogUrl]]/[[:ToUserInfo.Username]]">[[:ToUserInfo.Username]]</a>
|
||||
</span>
|
||||
[[if ToUserIsAuthor]]
|
||||
<span>({{msg . "author"}})</span>
|
||||
[[/if]]
|
||||
[[/if]]
|
||||
</div>
|
||||
<div class="comment-content ng-binding" ng-bind-html="comment.content">
|
||||
[[html:Content]]
|
||||
</div>
|
||||
<div class="comment-ft clearfix" data-comment-id="[[:CommentId]]" >
|
||||
<span title="" ui-time="" class="date">[[:PublishDate]] </span>
|
||||
<span class="like-num [[if !LikeNum]]hide[[/if]]" title="[[:LikeNum]] {{rawMsg . "like"}}"><span class="like-num-i">[[:LikeNum]]</span> {{rawMsg . "like"}}</span></span>
|
||||
|
||||
[[if ~root.visitUserInfo.UserId]]
|
||||
[[if IsMyNote && !IsMyComment]]
|
||||
<a href="javascript:;" class="comment-trash op-link "><i class="fa fa-trash"></i> {{rawMsg . "delete"}}</a>
|
||||
[[/if]]
|
||||
[[if !IsMyComment]]
|
||||
<a href="javascript:;" class="comment-reply op-link ">
|
||||
<i class="fa fa-reply"></i>
|
||||
{{rawMsg . "reply"}}
|
||||
</a>
|
||||
<a href="javascript:;" class="comment-like op-link"><i class="fa fa-thumbs-o-up"></i> <span class="like-text">[[if IsILikeIt]]{{rawMsg . "unlike"}}[[else]]{{rawMsg . "like"}}[[/if]]</span></a>
|
||||
{{if eq .locale "zh"}}
|
||||
<a href="javascript:;" class="comment-report op-link "><i class="fa fa-flag-o"></i> {{rawMsg . "report"}}</a>
|
||||
{{end}}
|
||||
[[else]]
|
||||
<a href="javascript:;" class="comment-trash op-link "><i class="fa fa-trash"></i> {{rawMsg . "delete"}}</a>
|
||||
[[/if]]
|
||||
[[/if]]
|
||||
</div>
|
||||
|
||||
<!-- 回复该评论 -->
|
||||
[[if ~root.visitUserInfo.UserId]]
|
||||
<form class="comment-form comment-box-ft">
|
||||
<div class="clearfix">
|
||||
<div class="avatar-wrap">
|
||||
<img class="avatar" src="[[:~root.visitUserInfo.Logo]]">
|
||||
</div>
|
||||
<div class="editor-wrap">
|
||||
<textarea class="editable" id="commentContent" name="commentContent" placeholder="{{rawMsg . "reply"}}"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="command clearfix" style="display: block;">
|
||||
<button class="reply-comment-btn save btn btn-primary" data-comment-id="[[:CommentId]]">{{rawMsg . "comment"}}</button>
|
||||
<a class="cancel reply-cancel btn-link">{{rawMsg . "cancel"}}</a>
|
||||
</div>
|
||||
</form>
|
||||
[[/if]]
|
||||
</div>
|
||||
</li>
|
||||
[[/for]]
|
||||
</script>
|
||||
|
||||
<!-- 评论 -->
|
||||
<div class="comment-box hide">
|
||||
{{if .visitUserInfo.UserId}}
|
||||
<form class="comment-form comment-box-ft" id="commentForm">
|
||||
<div class="clearfix">
|
||||
<div class="avatar-wrap">
|
||||
<img class="avatar" src="{{.visitUserInfo.Logo}}">
|
||||
</div>
|
||||
<div class="editor-wrap">
|
||||
<textarea class="editable" id="commentContent" name="commentContent" placeholder="{{msg . "comment"}}" style="height: 100px;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="command clearfix" style="display: block;">
|
||||
<button id="commentBtn" class="reply-comment-btn save btn btn-primary">{{msg . "comment"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
{{else}}
|
||||
<div class="needLogin">
|
||||
<a onclick="goLogin()">{{msg . "signIn"}}</a>, {{msg . "submitComment"}}.
|
||||
<br />
|
||||
没有帐号? <a onclick="goRegister()">{{msg . "signUp"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="box-header">
|
||||
<span class="counter">
|
||||
<i class="icon icon-comment"></i><span id="commentNum">{{.blog.CommentNum}}</span> {{msg . "comments"}}
|
||||
</span>
|
||||
</div>
|
||||
<ul id="comments">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="moreComments">
|
||||
<div class="hide comments-more">
|
||||
<a>More...</a>
|
||||
</div>
|
||||
<div class="comments-loading">
|
||||
<img src="/images/loading-32.gif" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if eq .locale "zh"}}
|
||||
<div id="reportMsg" class="hide">
|
||||
<form class="report-form" name="reportForm">
|
||||
<ul class="options clearfix">
|
||||
<li><label><input required="" value="{{msg . "reportReason1"}}" name="reason" type="radio">{{msg . "reportReason1"}}</label></li>
|
||||
<li><label><input required="" value="{{msg . "reportReason2"}}" name="reason" type="radio">{{msg . "reportReason2"}}</label></li>
|
||||
<li><label><input required="" value="{{msg . "reportReason3"}}" name="reason" type="radio">{{msg . "reportReason3"}}</label></li>
|
||||
<li><label><input required="" value="{{msg . "reportReason4"}}" name="reason" type="radio">{{msg . "reportReason4"}}</label></li>
|
||||
<li><label><input required="" value="" name="reason" type="radio">{{msg . "other"}}</label></li>
|
||||
</ul>
|
||||
<p class="input-container" style="display: none">
|
||||
<input placeholder="{{msg . "reportReason"}}" type="text" name="detail" class="form-control reason-text basic-input" />
|
||||
</p>
|
||||
<p class="footnote"></p>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
{{if and .userBlog.CanComment (eq .userBlog.CommentType "disqus")}}
|
||||
|
||||
<div id="disqus_thread"></div>
|
||||
<!-- comment -->
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -4,57 +4,51 @@
|
||||
<div class="col-md-4">
|
||||
<h3>{{msg . "blogNavs"}}</h3>
|
||||
<ul>
|
||||
<li><a href="/blog/{{$userId}}">{{msg . "home"}}</a></li>
|
||||
<li><a href="{{$.blogUrl}}/{{$.userInfo.Username}}">{{msg . "home"}}</a></li>
|
||||
{{range .notebooks}}
|
||||
<li>
|
||||
<a href="/blog/{{$userId}}/{{.NotebookId.Hex}}">{{.Title}}</a>
|
||||
<a href="{{$.cateUrl}}/{{.NotebookId.Hex}}">{{.Title}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
<li><a href="/blog/aboutMe/{{$userId}}">{{msg . "aboutMe"}}</a></li>
|
||||
<li><a href="{{$.aboutMeUrl}}">{{msg . "aboutMe"}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h3>{{msg . "latestPosts"}}</h3>
|
||||
<ul>
|
||||
{{range .recentBlogs}}
|
||||
<li title="{{.Title}}"><a href="/blog/view/{{.NoteId.Hex}}/">{{.Title}}</a></li>
|
||||
<li title="{{.Title}}"><a href="{{$.blogUrl}}/view/{{.NoteId.Hex}}/">{{.Title}}</a></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h3>{{msg . "quickLinks"}}</h3>
|
||||
<ul>
|
||||
<li><a href="/note">{{msg . "myNote"}}</a></li>
|
||||
<li><a href="/login">{{msg . "login"}}</a></li>
|
||||
<li><a href="http://leanote.com" target="_blank">leanote</a></li>
|
||||
<li><a href="{{$.noteUrl}}">{{msg . "myNote"}}</a></li>
|
||||
<li><a href="{{$.siteUrl}}/login">leanote {{msg . "login"}}</a></li>
|
||||
<li><a href="http://leanote.com" target="_blank">leanote {{msg . "home"}}</a></li>
|
||||
<li><a href="http://lea.leanote.com" target="_blank">lea++</a></li>
|
||||
<li><a href="http://bbs.leanote.com" target="_blank">leanote {{msg . "community"}}</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 src="{{$.siteUrl}}/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="{{$.siteUrl}}/js/bootstrap-min.js"></script>
|
||||
<script src="{{$.siteUrl}}/js/bootstrap-hover-dropdown.js"></script>
|
||||
<script src="{{$.siteUrl}}/js/i18n/blog.{{.locale}}.js"></script>
|
||||
{{if not .isMe}}
|
||||
<script src="{{$.siteUrl}}/blog/isMe.js?userId={{.userBlog.UserId.Hex}}"></script>
|
||||
{{end}}
|
||||
<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;
|
||||
location.href = "{{$.searchUrl}}";
|
||||
} else {
|
||||
var tpl = '<form action="/blog/searchBlog/' + UserInfo.Username +'" method="get">';
|
||||
var tpl = '<form action="{{$.searchUrl}}" method="get">';
|
||||
tpl += '<input name="key" value="' + key + '" />';
|
||||
tpl += "</form";
|
||||
$(tpl).submit();
|
||||
|
||||
@@ -10,10 +10,15 @@
|
||||
|
||||
<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">
|
||||
|
||||
<link href="{{.siteUrl}}/css/bootstrap.css" rel="stylesheet">
|
||||
<!-- 字体必须同一域 -->
|
||||
{{if .set}}
|
||||
<link href="{{.siteUrl}}/css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet">
|
||||
{{else}}
|
||||
<link href="{{$.staticUrl}}/css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet">
|
||||
{{end}}
|
||||
<link id="styleLink" href="{{.siteUrl}}/css/blog/{{if .userBlog.Style}}{{.userBlog.Style}}{{else}}blog_default{{end}}.css" rel="stylesheet">
|
||||
<link href="{{.siteUrl}}/css/blog/comment.css" rel="stylesheet">
|
||||
<script>
|
||||
function log(o) {
|
||||
}
|
||||
@@ -30,7 +35,7 @@ function log(o) {
|
||||
{{$userId := .userBlog.UserId.Hex}} <!-- 可不要了 -->
|
||||
{{$username := .userInfo.Username}}
|
||||
<h1>
|
||||
<a href="/blog/{{$username}}" id="logo">
|
||||
<a href="{{.indexUrl}}" id="logo">
|
||||
{{if .userBlog.Logo}}
|
||||
<img src="{{.userBlog.Logo}}" title="{{.userBlog.Title}}"/>
|
||||
{{else}}
|
||||
@@ -54,23 +59,29 @@ function log(o) {
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="{{.indexUrl}}">
|
||||
{{if .userBlog.Logo}}
|
||||
<img src="{{.userBlog.Logo}}" title="{{.userBlog.Title}}"/>
|
||||
{{else}}
|
||||
{{.userBlog.Title | raw}}
|
||||
{{end}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
{{$navNotebookId := .notebookId}}
|
||||
<li class="{{if .index}}active{{end}}"><a href="/blog/{{$username}}">{{msg . "home"}}</a></li>
|
||||
<li class="{{if .index}}active{{end}}"><a href="{{.indexUrl}}">{{msg . "home"}}</a></li>
|
||||
{{range .notebooks}}
|
||||
{{$notebookId := .NotebookId.Hex}}
|
||||
{{$notebookId := .NotebookId.Hex}}
|
||||
<li class="{{if $navNotebookId}}{{if eq $navNotebookId $notebookId}}active{{else}}{{end}}{{end}}">
|
||||
<a href="/blog/{{$username}}/{{$notebookId}}"
|
||||
<a href="{{$.cateUrl}}/{{$notebookId}}"
|
||||
>{{.Title}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
<li class="{{if .aboutMe}}active{{end}}"><a href="/blog/aboutMe/{{$username}}">{{msg . "aboutMe"}}</a></li>
|
||||
{{if .isMe}}
|
||||
<li class="{{if .set}}active{{end}}"><a href="/blog/set">{{msg . "blogSet"}}</a></li>
|
||||
<li><a href="/note" >{{msg . "myNote"}}</a></li>
|
||||
{{end}}
|
||||
<li class="{{if .aboutMe}}active{{end}}"><a href="{{.aboutMeUrl}}">{{msg . "aboutMe"}}</a></li>
|
||||
<!-- 同源上传logo -->
|
||||
<li class="is-me {{if .set}}active{{end}} {{if not .isMe}}hide{{end}}" ><a href="{{$.siteUrl}}/blog/set/">{{msg . "blogSet"}}</a></li>
|
||||
<li><a href="{{$.noteUrl}}" class="is-me {{if not .isMe}}hide{{end}}">{{msg . "myNote"}}</a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-right" id="search" onsubmit="search(event);return false;">
|
||||
<div class="input-group">
|
||||
@@ -85,4 +96,9 @@ function log(o) {
|
||||
|
||||
<script>
|
||||
var UserInfo = {UserId: "{{$userId}}", Email: "{{.userInfo.Email}}", Username: "{{.userInfo.Username}}"};
|
||||
var UserBlogInfo={CanComment: {{.userBlog.CanComment}}, CommentType: "{{.userBlog.CommentType}}"};
|
||||
var indexUrl = "{{$.indexUrl}}";
|
||||
var viewUrl = "{{$.viewUrl}}";
|
||||
var blogUrl = "{{$.blogUrl}}";
|
||||
var staticUrl = "{{$.staticUrl}}"; // blog.leanote.com, life.leanote.com, aaa.com
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@
|
||||
<div id="postsContainer">
|
||||
<div class="container">
|
||||
{{if .notebookId}}
|
||||
<h2>{{msg . "blogClass"}}: {{.notebook.Title}}</h2>
|
||||
<h2>{{msg . "blogClass"}} - {{.notebook.Title}}</h2>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="posts">
|
||||
@@ -11,30 +11,35 @@
|
||||
{{range .blogs}}
|
||||
<div class="each-post">
|
||||
<div class="title">
|
||||
<a href="/blog/view/{{.NoteId.Hex}}" title="{{msg $G "fullBlog"}}">
|
||||
<a href="{{$.viewUrl}}/{{.NoteId.Hex}}" title="{{msg $ "fullBlog"}}">
|
||||
{{.Title}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="created-time">
|
||||
<i class="fa fa-bookmark-o" style="color: #666"></i>
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
{{if .Tags}}
|
||||
{{blogTags .Tags}}
|
||||
{{blogTags $ .Tags}}
|
||||
{{else}}
|
||||
{{msg $G "noTag"}}
|
||||
{{msg $ "noTag"}}
|
||||
{{end}}
|
||||
|
|
||||
<i class="fa fa-calendar" style="color: #666"></i> {{msg $G "updatedTime"}} {{.UpdatedTime | datetime}} |
|
||||
<i class="fa fa-calendar" style="color: #666"></i> {{msg $G "createdTime"}} {{.CreatedTime | datetime}}
|
||||
<i class="fa fa-calendar"></i> {{msg $ "updatedTime"}} {{.UpdatedTime | datetime}} |
|
||||
<i class="fa fa-calendar"></i> {{msg $ "createdTime"}} {{.CreatedTime | datetime}}
|
||||
</div>
|
||||
<div class="desc">
|
||||
{{.Content | raw}}
|
||||
</div>
|
||||
<a class="more" href="/blog/view/{{.NoteId.Hex}}" title="{{msg $G "fullBlog"}}">More...</a>
|
||||
<a class="more" href="{{$.viewUrl}}/{{.NoteId.Hex}}" title="{{msg $ "fullBlog"}}">{{msg $ "more"}}.</a>
|
||||
</div>
|
||||
{{end}}
|
||||
<!-- 分页 -->
|
||||
<ul class="pager">
|
||||
{{page .userInfo.Username .notebookId .page .pageSize .count}}
|
||||
{{if .notebookId}}
|
||||
{{set $ "pageUrl" (concatStr $.cateUrl "/" .notebookId)}}
|
||||
{{else}}
|
||||
{{set $ "pageUrl" $.indexUrl}}
|
||||
{{end}}
|
||||
{{page $.pageUrl .page .pageSize .count}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,38 +2,38 @@
|
||||
|
||||
<div id="postsContainer">
|
||||
<div class="container">
|
||||
<h2>搜索 {{.key}} </h2>
|
||||
<h2>{{msg . "search"}} - {{.key}} </h2>
|
||||
</div>
|
||||
|
||||
<div id="posts">
|
||||
{{range .blogs}}
|
||||
<div class="each-post">
|
||||
<div class="title">
|
||||
<a href="/blog/view/{{.NoteId.Hex}}" title="全文">
|
||||
<a href="{{$.viewUrl}}/{{.NoteId.Hex}}" title="{{msg $ "fullBlog"}}">
|
||||
{{.Title}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="created-time">
|
||||
<i class="fa fa-bookmark-o" style="color: #666"></i>
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
{{if .Tags}}
|
||||
{{blogTags .Tags}}
|
||||
{{blogTags $ .Tags}}
|
||||
{{else}}
|
||||
无
|
||||
{{msg $ "noTag"}}
|
||||
{{end}}
|
||||
|
|
||||
<i class="fa fa-calendar" style="color: #666"></i> 更新 {{.UpdatedTime | datetime}} |
|
||||
<i class="fa fa-calendar" style="color: #666"></i> 创建 {{.CreatedTime | datetime}}
|
||||
<i class="fa fa-calendar"></i> {{msg $ "updatedTime"}} {{.UpdatedTime | datetime}} |
|
||||
<i class="fa fa-calendar"></i> {{msg $ "createdTime"}} {{.CreatedTime | datetime}}
|
||||
</div>
|
||||
<div class="desc">
|
||||
{{.Content | raw}}
|
||||
</div>
|
||||
<a class="more" href="/blog/view/{{.NoteId.Hex}}" title="更多">More...</a>
|
||||
<a class="more" href="{{$.viewUrl}}/{{.NoteId.Hex}}" title="{{msg $ "fullBlog"}}">{{msg $ "more"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if not .blogs }}
|
||||
<div class="each-post">
|
||||
无
|
||||
{{msg . "none"}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{{template "Blog/header.html" .}}
|
||||
|
||||
<!-- -->
|
||||
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" type="text/css">
|
||||
<!-- set页面不是自定义域名和二级域名页 -->
|
||||
<link rel="stylesheet" href="{{.siteUrl}}/tinymce/skins/custom/skin.min.css" type="text/css">
|
||||
<style>
|
||||
.tab-pane {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="postsContainer">
|
||||
<div id="posts">
|
||||
@@ -20,9 +21,18 @@
|
||||
<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">{{msg . "theme"}}</label>
|
||||
<label class="col-sm-2 control-label"></label>
|
||||
<div class="col-sm-10">
|
||||
<label><input type="radio" name="Style"
|
||||
<div class="alert alert-success" id="styleMsg" style="display: none; margin-bottom: 3px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Style" class="col-sm-2 control-label">{{msg . "theme"}}</label>
|
||||
<div class="col-sm-10" style="margin-top: 6px;" id="themeList">
|
||||
<label>
|
||||
<img class="preview" src="{{$.siteUrl}}/images/blog/theme/default.png" />
|
||||
<input type="radio" name="Style"
|
||||
value="blog_default"
|
||||
{{if not .userBlog.Style}}
|
||||
checked="checked"
|
||||
@@ -31,53 +41,85 @@
|
||||
checked="checked"
|
||||
{{end}}
|
||||
{{end}}>
|
||||
{{msg . "default"}} </label>
|
||||
<label><input type="radio" name="Style"
|
||||
{{msg . "default"}}
|
||||
</label>
|
||||
<label>
|
||||
<img class="preview" src="{{$.siteUrl}}/images/blog/theme/elegent.png" />
|
||||
<input type="radio" name="Style"
|
||||
value="blog_daqi"
|
||||
{{if eq .userBlog.Style "blog_daqi"}}checked="checked"{{end}}>
|
||||
{{msg . "elegant"}}</label>
|
||||
<label><input type="radio" name="Style"
|
||||
{{msg . "elegant"}}
|
||||
</label>
|
||||
<label>
|
||||
<img class="preview" src="{{$.siteUrl}}/images/blog/theme/left_nav_fix.png" />
|
||||
<input type="radio" name="Style"
|
||||
value="blog_left_fixed"
|
||||
{{if eq .userBlog.Style "blog_left_fixed"}}checked="checked"{{end}}>
|
||||
{{msg . "navFixed"}}</label>
|
||||
{{msg . "navFixed"}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button class="btn btn-success">{{msg . "save"}}</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">{{msg . "openComment"}}</label>
|
||||
<label 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}} >
|
||||
<div class="alert alert-success" id="commentMsg" style="display: none; margin-bottom: 3px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="subTitle" class="col-sm-2 control-label">{{msg . "chooseComment"}}</label>
|
||||
<div class="col-sm-10">
|
||||
<label>
|
||||
<input type="checkbox" id="CanComment" name="CanComment"
|
||||
{{if .userBlog.CanComment}}checked="checked"{{end}} > {{msg . "openComment"}}
|
||||
</label>
|
||||
|
||||
<br />
|
||||
{{msg . "commentSys"}}
|
||||
<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 />
|
||||
{{msg . "disqusHelp"}}
|
||||
<a target="_blank" href="http://leanote.com/blog/view/52db8463e01c530ef8000001">{{msg . "needHelp"}}</a>)
|
||||
|
||||
<div id="commentSet" {{if not .userBlog.CanComment}}style="display: none"{{end}}>
|
||||
<label>
|
||||
<input type="radio"
|
||||
name="commentType"
|
||||
value="default"
|
||||
{{if or (not .userBlog.CommentType) (eq .userBlog.CommentType "default")}}checked="checked"{{end}} > Default
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="commentType" id="disqus"
|
||||
value="disqus"
|
||||
{{if eq .userBlog.CommentType "disqus"}}checked="checked"{{end}} > Disqus
|
||||
</label>
|
||||
|
||||
<div id="disqusSet" {{if not (eq .userBlog.CommentType "disqus")}}style="display: none"{{end}}>
|
||||
<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 />
|
||||
{{msg . "disqusHelp"}}
|
||||
<a target="_blank" href="http://leanote.com/blog/view/52db8463e01c530ef8000001">{{msg . "needHelp"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button class="btn btn-success">{{msg . "save"}}</button>
|
||||
<span class="msg"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button class="btn btn-success">{{msg . "save"}}</button>
|
||||
<span class="msg"></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane active" id="baseInfo">
|
||||
<div class="form-horizontal" role="form" id="userBlogForm">
|
||||
<div class="form-group">
|
||||
@@ -101,7 +143,7 @@
|
||||
<div class="col-sm-10">
|
||||
<input type="hidden" name="Logo" id="Logo"
|
||||
value="{{.userBlog.Logo}}" />
|
||||
<form id="formLogo" action="/file/uploadBlogLogo" method="post"
|
||||
<form id="formLogo" action="{{$.siteUrl}}/file/uploadBlogLogo" method="post"
|
||||
enctype="multipart/form-data" target="logoTarget"
|
||||
onsubmit="inProgress()">
|
||||
<input type="file" class="form-control" id="logo2" name="file"
|
||||
@@ -126,7 +168,7 @@
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="SubTitle"
|
||||
name="SubTitle" value="{{.userBlog.SubTitle}}"
|
||||
placeholder="eg: leanote, {{msg $ "moto"}}">
|
||||
placeholder="eg: leanote, Not Just A Notebook">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -158,11 +200,11 @@
|
||||
|
||||
{{template "Blog/footer.html" .}}
|
||||
|
||||
<script src="/js/common-min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/tinymce/tinymce.min.js"></script>
|
||||
<script src="{{.siteUrl}}/js/common-min.js"></script>
|
||||
<script type="text/javascript" src="{{.siteUrl}}/tinymce/tinymce.min.js"></script>
|
||||
|
||||
<script>
|
||||
var urlPrefix = "{{.siteUrl}}";
|
||||
$(function() {
|
||||
tinymce.init({
|
||||
selector : "#AboutMe",
|
||||
@@ -197,12 +239,19 @@ $(function() {
|
||||
|
||||
$("#CanComment").click(function() {
|
||||
if ($(this).is(":checked")) {
|
||||
$("#commentSet").show();
|
||||
} else {
|
||||
$("#commentSet").hide();
|
||||
}
|
||||
});
|
||||
|
||||
$("input[name='commentType']").click(function() {
|
||||
if ($("input[name='commentType']:checked").val() == "disqus") {
|
||||
$("#disqusSet").show();
|
||||
} else {
|
||||
$("#disqusSet").hide();
|
||||
}
|
||||
});
|
||||
$("#CanComment").trigger("click").trigger("click"); // 恶心的想法
|
||||
|
||||
|
||||
// 基本设置
|
||||
@@ -219,7 +268,7 @@ $(function() {
|
||||
$("#blogDesc").html(data.SubTitle);
|
||||
$("#logo").html(data.Title);
|
||||
if(data.Logo) {
|
||||
$("#logo").html(t('<img src="?" />', data.Logo));
|
||||
$("#logo").html(t('<img src="?" />', urlPrefix + "/" + data.Logo));
|
||||
}
|
||||
}, this);
|
||||
});
|
||||
@@ -228,18 +277,20 @@ $(function() {
|
||||
e.preventDefault();
|
||||
var data = {
|
||||
CanComment : $("#CanComment").is(":checked"),
|
||||
CommentType: $("input[name='commentType']:checked").val(),
|
||||
DisqusId : $("#DisqusId").val(),
|
||||
}
|
||||
post("/blog/setUserBlogComment", data, function(ret) {
|
||||
showMsg2($("#commentInfo .msg"), "{{msg . "saveSuccess"}}", 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"), "{{msg . "saveSuccess"}}", 2000);
|
||||
}, this);
|
||||
@@ -256,7 +307,7 @@ function inProgress() {
|
||||
function uploadFinish(ret) {
|
||||
if (ret) {
|
||||
if (ret.resultCode == '1') {
|
||||
$("#logoImg img").attr("src", ret.filename).parent().show();
|
||||
$("#logoImg img").attr("src", urlPrefix + "/" + ret.filename).parent().show();
|
||||
$("#Logo").val(ret.filename);
|
||||
return;
|
||||
}
|
||||
@@ -266,32 +317,6 @@ function uploadFinish(ret) {
|
||||
// 上传出错
|
||||
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>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{{template "Blog/header.html" .}}
|
||||
|
||||
<!-- -->
|
||||
<div id="postsContainer">
|
||||
<div id="posts">
|
||||
<div class="each-post">
|
||||
@@ -8,15 +7,30 @@
|
||||
{{.blog.Title}}
|
||||
</div>
|
||||
<div class="created-time">
|
||||
<i class="fa fa-bookmark-o" style="color: #666"></i>
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
{{if .blog.Tags}}
|
||||
{{blogTags .blog.Tags}}
|
||||
{{blogTags $ .blog.Tags}}
|
||||
{{else}}
|
||||
{{msg . "noTag"}}
|
||||
{{end}}
|
||||
|
|
||||
<i class="fa fa-calendar" style="color: #666"></i> {{msg . "updatedTime"}} {{.blog.UpdatedTime | datetime}} |
|
||||
<i class="fa fa-calendar" style="color: #666"></i> {{msg . "createdTime"}} {{.blog.CreatedTime | datetime}}
|
||||
<i class="fa fa-calendar"></i> {{msg . "updatedTime"}} {{.blog.UpdatedTime | datetime}} |
|
||||
<i class="fa fa-calendar"></i> {{msg . "createdTime"}} {{.blog.CreatedTime | datetime}}
|
||||
</div>
|
||||
|
||||
<div class="mobile-created-time">
|
||||
{{ if .userInfo.Logo}}
|
||||
<img src="{{.userInfo.Logo}}" id="userLogo">
|
||||
{{else}}
|
||||
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="userLogo">
|
||||
{{end}}
|
||||
{{.userInfo.Username}}
|
||||
|
||||
{{if .blog.Tags}}
|
||||
|
||||
<i class="fa fa-bookmark-o" style="color: #666"></i>
|
||||
{{blogTags $ .blog.Tags}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="desc" id="content">
|
||||
@@ -33,75 +47,41 @@
|
||||
{{else}}
|
||||
{{.blog.Content | raw}}
|
||||
{{end}}
|
||||
|
||||
<div id="desc" class="hide">{{.blog.Desc}}</div>
|
||||
</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 . "blogNav"}}</span>
|
||||
</div>
|
||||
<div id="blogNavContent" style="max-width: 200px">
|
||||
<div id="blogNavContent">
|
||||
</div>
|
||||
</div>
|
||||
<script src="/public/js/app/blog/nav.js"></script>
|
||||
|
||||
<script>
|
||||
var visitUserInfo = eval("(" + {{.visitUserInfoJson}} + ")");
|
||||
var urlPrefix = "{{.siteUrl}}";
|
||||
var noteId = "{{.blog.NoteId.Hex}}";
|
||||
var preLikeNum = +"{{.blog.LikeNum}}";
|
||||
var commentNum = +"{{.blog.CommentNum}}";
|
||||
</script>
|
||||
<script src="/js/app/blog/common.js"></script>
|
||||
<script src="/js/jsrender.js"></script>
|
||||
<script src="/js/jquery-cookie-min.js"></script>
|
||||
<script src="/js/bootstrap-dialog.min.js"></script>
|
||||
<script src="/js/jquery.qrcode.min.js"></script>
|
||||
<script src="/js/app/blog/view.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>
|
||||
@@ -127,13 +107,19 @@ prettyPrint();
|
||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"wmd-preview"]);
|
||||
|
||||
initNav();
|
||||
weixin();
|
||||
</script>
|
||||
{{else}}
|
||||
<script>
|
||||
$(function() {
|
||||
initNav();
|
||||
weixin();
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
<!--google+
|
||||
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"> {lang: 'zh-CN'} </script>
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -6,7 +6,7 @@
|
||||
<section id="box">
|
||||
<div>
|
||||
<div>
|
||||
<h1 class="h text-white animated fadeInDownBig">404</h1>
|
||||
<h1 class="h text-white animated fadeInDownBig">500</h1>
|
||||
</div>
|
||||
<div id="errorBox">
|
||||
<p class="error-info">
|
||||
|
||||
@@ -22,6 +22,51 @@ function log(o) {
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav id="headerContainer" style="background-color:#fff" class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/index">
|
||||
<img src="/images/logo/leanote_black.png" id="" title="leanote, {{msg $ "moto"}}"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-left">
|
||||
<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 href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
|
||||
<li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
|
||||
<li><a id="leanoteBlog" href="{{.leaUrl}}/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li>
|
||||
<li style="position: relative; margin-right: 3px;">
|
||||
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
|
||||
<div class="red-circle" style=""></div>
|
||||
</li>
|
||||
|
||||
<li id="loginBtns">
|
||||
{{if .userInfo.Email}}
|
||||
{{msg . "hi"}}, {{.userInfo.Username}}
|
||||
<a href="{{$.noteUrl}}">{{msg . "myNote"}}</a>
|
||||
<a href="/logout">{{msg . "logout"}}</a>
|
||||
{{else}}
|
||||
<a href="/login">{{msg . "login"}}</a>
|
||||
{{if .openRegister}}
|
||||
<a href="/register" class="btn-register">{{msg . "register"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<!--
|
||||
<div id="headerContainer" style="background-color:#fff" class="navbar-fixed-top">
|
||||
<div class="container" style="clearfix" id="header">
|
||||
<div class="pull-left">
|
||||
@@ -35,7 +80,7 @@ function log(o) {
|
||||
<div class="pull-right" id="loginBtns">
|
||||
{{if .userInfo.Email}}
|
||||
{{msg . "hi"}}, {{.userInfo.Username}}
|
||||
<a href="/note">{{msg . "myNote"}}</a>
|
||||
<a href="{{$.noteUrl}}">{{msg . "myNote"}}</a>
|
||||
<a href="/logout">{{msg . "logout"}}</a>
|
||||
{{else}}
|
||||
<a href="/login">{{msg . "login"}}</a>
|
||||
@@ -47,12 +92,9 @@ function log(o) {
|
||||
|
||||
<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 href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
|
||||
<li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
|
||||
<li><a id="leanoteBlog" href="http://leanote.com/lea/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li>
|
||||
<li><a id="leanoteBlog" href="{{.leaUrl}}/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li>
|
||||
<li style="position: relative; margin-right: 3px;">
|
||||
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
|
||||
<div style="position: absolute;
|
||||
@@ -64,6 +106,6 @@ function log(o) {
|
||||
border-radius: 9px;"></div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
@@ -1,4 +1,13 @@
|
||||
{{template "home/header_box.html" .}}
|
||||
|
||||
<!-- 验证码 -->
|
||||
<script type="text/x-jsrender" id="tCaptcha">
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{rawMsg . "captcha"}}</label>
|
||||
<input type="text" class="form-control" id="captcha" name="captcha">
|
||||
<a id="reloadCaptcha" title="{{msg . "reloadCaptcha"}}" onclick="$('#captchaImage').attr('src', '/captcha/get?' + ((new Date()).getTime()))"><img src="/captcha/get" id="captchaImage"/></a>
|
||||
</div>
|
||||
</script>
|
||||
<section id="box" class="animated fadeInUp">
|
||||
<!--
|
||||
<div>
|
||||
@@ -11,7 +20,8 @@
|
||||
<div id="boxHeader">{{msg . "login"}}</div>
|
||||
<form>
|
||||
<div class="alert alert-danger" id="loginMsg"></div>
|
||||
<div class="form-group">
|
||||
<input id="from" type="hidden" value="{{.from}}" />
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{msg . "usernameOrEmail"}}</label>
|
||||
<input type="text" class="form-control" id="email" name="email" value="{{.email}}">
|
||||
</div>
|
||||
@@ -19,6 +29,11 @@
|
||||
<label class="control-label">{{msg . "password"}}</label>
|
||||
<input type="password" class="form-control" id="pwd" name="pwd">
|
||||
</div>
|
||||
|
||||
<div id="captchaContainer">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
<a href="/findPassword" class="pull-right m-t-xs"><small>{{msg . "forgetPassword"}}</small></a>
|
||||
<button id="loginBtn" class="btn btn-success">{{msg . "login"}}</button>
|
||||
@@ -57,6 +72,12 @@
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
var needCaptcha = {{.needCaptcha}};
|
||||
|
||||
if(needCaptcha){
|
||||
$("#captchaContainer").html($("#tCaptcha").html());
|
||||
}
|
||||
|
||||
$("#email").focus();
|
||||
if($("#email").val()) {
|
||||
$("#pwd").focus();
|
||||
@@ -74,6 +95,7 @@ $(function() {
|
||||
e.preventDefault();
|
||||
var email = $("#email").val();
|
||||
var pwd = $("#pwd").val();
|
||||
var captcha = $("#captcha").val()
|
||||
if(!email) {
|
||||
showMsg("{{msg . "inputUsername"}}", "email");
|
||||
return;
|
||||
@@ -87,17 +109,27 @@ $(function() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(needCaptcha && !captcha) {
|
||||
showMsg("{{msg . "inputCaptcha"}}", "captcha");
|
||||
return;
|
||||
}
|
||||
|
||||
$("#loginBtn").html("{{msg . "logining"}}...").addClass("disabled");
|
||||
// hideMsg();
|
||||
|
||||
$.post("/doLogin", {email: email, pwd: pwd}, function(e) {
|
||||
$.post("/doLogin", {email: email, pwd: pwd, captcha: $("#captcha").val()}, function(e) {
|
||||
$("#loginBtn").html("{{msg . "login"}}").removeClass("disabled");
|
||||
if(e.Ok) {
|
||||
$("#loginBtn").html("{{msg . "loginSuccess"}}...");
|
||||
location.href = '/note';
|
||||
var from = $("#from").val() || "{{.noteUrl}}" || "/note";
|
||||
location.href = from;
|
||||
} else {
|
||||
showMsg(e.Msg, "pwd");
|
||||
if(e.Item && $.trim($("#captchaContainer").text()) == "") {
|
||||
$("#captchaContainer").html($("#tCaptcha").html());
|
||||
needCaptcha = true
|
||||
}
|
||||
|
||||
showMsg(e.Msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
<div id="boxForm">
|
||||
<div id="boxHeader">{{msg . "register"}}</div>
|
||||
<form>
|
||||
<div class="alert alert-danger" id="loginMsg"></div>
|
||||
<div class="alert alert-danger" id="loginMsg"></div>
|
||||
<input id="from" type="hidden" value="{{.from}}" />
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="email">{{msg . "email"}}</label>
|
||||
<input type="text" class="form-control" id="email" name="email">
|
||||
@@ -103,7 +104,8 @@ $(function() {
|
||||
$("#registerBtn").html("{{msg . "register"}}").removeClass("disabled");
|
||||
if(e.Ok) {
|
||||
$("#registerBtn").html("{{msg . "registerSuccessAndRdirectToNote"}}");
|
||||
location.href = '/note';
|
||||
var from = $("#from").val() || "{{.noteUrl}}" || "/note";
|
||||
location.href = from;
|
||||
} else {
|
||||
showMsg(e.Msg, "email");
|
||||
}
|
||||
|
||||
102
app/views/Html2Image/index.html
Normal file
102
app/views/Html2Image/index.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<!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, {{msg $ "moto"}}">
|
||||
<meta name="author" content="leanote">
|
||||
|
||||
<title>{{.title}}</title>
|
||||
<link href="{{.siteUrl}}/css/bootstrap.css" rel="stylesheet">
|
||||
<link id="styleLink" href="{{.siteUrl}}/css/toImage.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<h1 class="title">
|
||||
{{.blog.Title}}
|
||||
</h1>
|
||||
<div class="created-time">
|
||||
{{ if .userBlog.Logo}}
|
||||
<img src="{{.userBlog.Logo}}" id="logo">
|
||||
{{else}}
|
||||
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
|
||||
{{end}}
|
||||
{{.userInfo.Username}}
|
||||
|
||||
{{if .blog.Tags}}
|
||||
<img src="{{$.siteUrl}}/images/blog/tag.png" id="tag">
|
||||
{{blogTags .blog.Tags}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="desc">
|
||||
{{if .blog.IsMarkdown }}
|
||||
<div id="markdownContent" style="display: none">
|
||||
<!-- 用textarea装html, 防止得到的值失真 -->
|
||||
<textarea>
|
||||
{{.content | raw}}
|
||||
</textarea>
|
||||
</div>
|
||||
<div id="parsedContent">
|
||||
</div>
|
||||
{{else}}
|
||||
{{.content | raw}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p>
|
||||
{{ if .userBlog.Logo}}
|
||||
<img src="{{.userBlog.Logo}}" id="logo">
|
||||
{{else}}
|
||||
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
|
||||
{{end}}
|
||||
(<a href="#">http://blog.leanote.com/{{.userInfo.Username}}</a>)
|
||||
</p>
|
||||
|
||||
<img src="{{.siteUrl}}/images/logo/leanote_white.png" id="leanote_logo"/>
|
||||
<p>
|
||||
leanote, {{msg $ "moto"}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script src="{{.siteUrl}}/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="{{.siteUrl}}/js/bootstrap-min.js"></script>
|
||||
|
||||
<link href="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
|
||||
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
|
||||
<script>
|
||||
$("pre").addClass("prettyprint linenums");
|
||||
prettyPrint();
|
||||
</script>
|
||||
|
||||
{{if .blog.IsMarkdown }}
|
||||
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
|
||||
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Converter.js"></script>
|
||||
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Sanitizer.js"></script>
|
||||
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Editor.js"></script>
|
||||
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/local/Markdown.local.zh.js"></script>
|
||||
<script src="{{.siteUrl}}/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="{{.siteUrl}}/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());
|
||||
var converter = Markdown.getSanitizingConverter();
|
||||
Markdown.Extra.init(converter, {extensions: ["tables", "fenced_code_gfm", "def_list"]});
|
||||
var html = converter.makeHtml(content);
|
||||
$("#parsedContent").html(html);
|
||||
$("pre").addClass("prettyprint linenums");
|
||||
prettyPrint();
|
||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"wmd-preview"]);
|
||||
</script>
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
4
app/views/Html2Image/test.html
Normal file
4
app/views/Html2Image/test.html
Normal file
@@ -0,0 +1,4 @@
|
||||
lif------------
|
||||
e
|
||||
|
||||
you can
|
||||
@@ -3,17 +3,19 @@
|
||||
<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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name=”apple-mobile-web-app-capable” content=”yes” />
|
||||
<meta name="keywords" content="leanote,leanote.com">
|
||||
<meta name="description" content="leanote, {{msg $ "moto"}}">
|
||||
<title>leanote, {{msg $ "moto"}}</title>
|
||||
<meta name="description" content="leanote, Not Just A Notebook">
|
||||
<title>leanote, Not Just A Notebook</title>
|
||||
|
||||
<link href="css/bootstrap.css" rel="stylesheet" />
|
||||
<link href="/css/bootstrap.css" rel="stylesheet" />
|
||||
<!-- 先加载, 没有样式, 宽度不定 -->
|
||||
<link rel="stylesheet" href="tinymce/skins/custom/skin.min.css" type="text/css" />
|
||||
<link rel="stylesheet" href="tinymce/skins/custom/skin.min.css" rel="stylesheet"/>
|
||||
|
||||
<!-- leanote css -->
|
||||
<link href="css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" />
|
||||
<link href="css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet" />
|
||||
<link href="css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
|
||||
<script>
|
||||
var hash = location.hash;
|
||||
@@ -27,7 +29,6 @@ document.write(files);
|
||||
|
||||
|
||||
<!-- For Develop writting mod -->
|
||||
|
||||
<script>
|
||||
function log(o) {
|
||||
console.log(o);
|
||||
@@ -56,34 +57,35 @@ function log(o) {
|
||||
</div>
|
||||
<!-- search -->
|
||||
<div class="pull-left" id="searchWrap">
|
||||
<form class="navbar-form form-inline col-lg-2 hidden-xs" id="searchNote">
|
||||
<form class="navbar-form form-inline col-lg-2" id="searchNote">
|
||||
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 全局按钮 -->
|
||||
<div class="pull-left" style="" id="newNoteWrap">
|
||||
|
||||
<!-- 新建笔记 -->
|
||||
<div id="newMyNote">
|
||||
<a id="newNoteBtn" title="{{msg . "newNote"}}">
|
||||
<i class="fa fa-file-o"></i>
|
||||
{{msg . "newNote"}}
|
||||
<span class="new-note-text">{{msg . "newNote"}}</span>
|
||||
<span class="new-note-text-abbr">{{msg . "new"}}</span>
|
||||
</a>
|
||||
<span class="new-split">|</span>
|
||||
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
Markdown
|
||||
<span class="new-markdown-text">Markdown</span>
|
||||
<span class="new-markdown-text-abbr">Md</span>
|
||||
</a>
|
||||
<span class="for-split"> - </span>
|
||||
<span id="curNotebookForNewNote" notebookId=""></span>
|
||||
<div class="dropdown" style="display: inline-block">
|
||||
<a class="ios7-a dropdown-toggle"
|
||||
id="dropdownMenu2" data-toggle="dropdown">
|
||||
id="listNotebookDropdownMenu" data-toggle="dropdown">
|
||||
<i class="fa fa-angle-down"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
|
||||
<input type="text" placeholder="search notebook" class="form-control" id="searchNotebookForAdd"/>
|
||||
<ul class="clearfix" role="menu" aria-labelledby="dropdownMenu2" id="notebookNavForNewNote">
|
||||
<input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/>
|
||||
<ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -93,19 +95,21 @@ function log(o) {
|
||||
<div id="newSharedNote" style="display: none">
|
||||
<a id="newSharedNoteBtn">
|
||||
<i class="fa fa-file-o"></i>
|
||||
{{msg . "newNote"}}
|
||||
<span class="new-note-text">{{msg . "newNote"}}</span>
|
||||
<span class="new-note-text-abbr">{{msg . "new"}}</span>
|
||||
</a>
|
||||
<span class="new-split">|</span>
|
||||
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
Markdown
|
||||
<span class="new-markdown-text">Markdown</span>
|
||||
<span class="new-markdown-text-abbr">Md</span>
|
||||
</a>
|
||||
<span class="for-split"> - </span>
|
||||
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
|
||||
<div class="dropdown" style="display: inline-block">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown">
|
||||
<a id="listShareNotebookDropdownMenu" class="ios7-a dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-angle-down"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-list" style="left: -200px;" >
|
||||
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddShareDropdownList" >
|
||||
<ul id="notebookNavForNewSharedNote"></ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -117,21 +121,11 @@ function log(o) {
|
||||
<span id="loading">
|
||||
</span>
|
||||
</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" />
|
||||
-->
|
||||
<a class="dropdown-toggle" title="{{.userInfo.Username}}" data-toggle="dropdown" style="line-height: 60px;">
|
||||
<img alt="{{.userInfo.Username}}" title="{{.userInfo.Username}}" src="{{if .userInfo.Logo}}{{.userInfo.Logo}}{{else}}/images/blog/default_avatar.png{{end}}" id="myAvatar"/>
|
||||
<span class="username">
|
||||
{{if .userInfo.UsernameRaw}}
|
||||
{{.userInfo.UsernameRaw}}
|
||||
@@ -143,12 +137,22 @@ function log(o) {
|
||||
</a>
|
||||
<ul class="dropdown-menu li-a" role="menu">
|
||||
<li role="presentation" id="setInfo">
|
||||
<a>
|
||||
<i class="fa fa-info"></i>
|
||||
{{msg . "accountSetting"}}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" id="setAvatarMenu">
|
||||
<a>
|
||||
<i class="fa fa-smile-o"></i>
|
||||
{{msg . "setAvatar"}}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" id="setTheme">
|
||||
<a>
|
||||
<i class="fa fa-sun-o"></i>
|
||||
{{msg . "themeSetting"}}
|
||||
</a>
|
||||
</li>
|
||||
<!--
|
||||
<li role="presentation" id="yourSuggestions">
|
||||
@@ -156,6 +160,22 @@ function log(o) {
|
||||
{{msg . "yourSuggestions"}}
|
||||
</li>
|
||||
-->
|
||||
<li role="presentation" class="my-link" >
|
||||
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
|
||||
<i class="fa fa-bold"></i>
|
||||
{{msg . "myBlog"}}</a>
|
||||
</li>
|
||||
|
||||
{{if .isAdmin}}
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation">
|
||||
<a target="_blank" title="{{msg . "amdin"}}" href="/admin/index">
|
||||
<i class="fa fa-dashboard"></i>
|
||||
{{msg . "admin"}}
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation" onclick="logout()">
|
||||
<i class="fa fa-sign-out"></i>
|
||||
{{msg . "logout"}}
|
||||
@@ -164,8 +184,8 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-right" style="margin: 0 10px" id="topNav">
|
||||
<a target="_blank" href="/blog/{{.userInfo.Username}}">
|
||||
<div class="pull-right top-nav" id="myBlog">
|
||||
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
|
||||
{{msg . "myBlog"}}
|
||||
</a>
|
||||
</div>
|
||||
@@ -239,7 +259,7 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<div class="folderBody">
|
||||
<input type="text" class="form-control" id="searchNotebookForList" placeholder="search notebook"/>
|
||||
<input type="text" class="form-control" id="searchNotebookForList" placeholder="Search notebook"/>
|
||||
<ul class="ztree" id="notebookList"></ul>
|
||||
<ul class="ztree" id="notebookListForSearch"></ul>
|
||||
</div>
|
||||
@@ -254,10 +274,10 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<ul class="folderBody clearfix" 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>
|
||||
<li data-tag="red"><a> <span class="label label-red">{{msg . "red"}}</span></a></li>
|
||||
<li data-tag="blue"><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li>
|
||||
<li data-tag="yellow"><a> <span class="label label-yellow">{{msg . "yellow"}}</span></a></li>
|
||||
<li data-tag="green"><a> <span class="label label-green">{{msg . "green"}}</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -309,9 +329,7 @@ function log(o) {
|
||||
<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">
|
||||
@@ -370,14 +388,13 @@ function log(o) {
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 笔记列表 -->
|
||||
<!-- wrap 为了slimScroll -->
|
||||
<div id="noteItemListWrap" style="position: absolute; left: 0; right: 0; top: 41px; bottom: 3px">
|
||||
<div id="noteItemList">
|
||||
</div>
|
||||
<div id="noteItemListWrap">
|
||||
<ul id="noteItemList">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -389,23 +406,17 @@ function log(o) {
|
||||
<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="clearfix" id="noteReadInfo">
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
<span id="noteReadTags"></span>
|
||||
|
||||
<!-- 修改时间 -->
|
||||
<div class="pull-left" style="margin-left: 10px;">
|
||||
<i class="fa fa-calendar"></i>{{msg . "update"}}
|
||||
<span id="noteReadUpdatedTime"></span>
|
||||
</div>
|
||||
<i class="fa fa-calendar"></i>{{msg . "update"}}
|
||||
<span id="noteReadUpdatedTime"></span>
|
||||
|
||||
<!-- 修改时间 -->
|
||||
<div class="pull-left" style="margin-left: 10px;">
|
||||
<i class="fa fa-calendar"></i>{{msg . "create"}}
|
||||
<span id="noteReadCreatedTime"></span>
|
||||
</div>
|
||||
<i class="fa fa-calendar"></i>{{msg . "create"}}
|
||||
<span id="noteReadCreatedTime"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -414,18 +425,22 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
<!-- 遮罩, 为了resize3Columns用 -->
|
||||
<div id="noteMask"
|
||||
style="position: absolute; top: 0px; bottom: 0px; right: 0; left: 10px; z-index: -1"></div>
|
||||
<div id="noteMask" class="note-mask"></div>
|
||||
<div id="noteMaskForLoading" class="note-mask">
|
||||
<img src="/images/loading-24.gif"/>
|
||||
<br />
|
||||
loading...
|
||||
</div>
|
||||
<div id="editorMask">
|
||||
该笔记本下空空如也...何不
|
||||
{{msg . "noNoteNewNoteTips"}}
|
||||
<br />
|
||||
<br />
|
||||
<div id="editorMaskBtns">
|
||||
<br />
|
||||
<a class="note">新建笔记</a>
|
||||
<a class="markdown">新建Markdown笔记</a>
|
||||
<a class="note">{{msg . "newNote"}}</a>
|
||||
<a class="markdown">{{msg . "newMarkdownNote"}}</a>
|
||||
</div>
|
||||
<div id="editorMaskBtnsEmpty">
|
||||
Sorry, 这里不能添加笔记的.
|
||||
{{msg . "canntNewNoteTips"}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="noteTop">
|
||||
@@ -449,7 +464,9 @@ function log(o) {
|
||||
<a
|
||||
class="metro-a dropdown-toggle" data-toggle="dropdown"
|
||||
id="addTagTrigger" style="cursor: text; padding-left: 0">
|
||||
<span class="add-tag-text">
|
||||
{{msg . "clickAddTag"}}
|
||||
</span>
|
||||
</a>
|
||||
<input type="text" id="addTagInput" />
|
||||
<ul class="dropdown-menu" role="menu" id="tagColor">
|
||||
@@ -463,34 +480,34 @@ function log(o) {
|
||||
</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>
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-save"></span>
|
||||
{{msg . "save"}}</a></li>
|
||||
|
||||
<li class="dropdown" id="attachDropdown">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
|
||||
<!--
|
||||
<span class="fa fa-upload"></span>
|
||||
-->
|
||||
<span class="fa fa-paperclip"></span>
|
||||
{{msg . "attachments"}}<span id="attachNum"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu" id="attachMenu">
|
||||
<ul id="attachList">
|
||||
|
||||
</ul>
|
||||
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data">
|
||||
<div id="dropAttach">
|
||||
<div id="dropAttach" class="dropzone">
|
||||
<a class="btn btn-success btn-choose-file">
|
||||
Choose File to Upload
|
||||
<i class="fa fa-upload"></i>
|
||||
<span>Choose File</span>
|
||||
</a>
|
||||
<a class="btn btn-default" id="downloadAllBtn">
|
||||
<i class="fa fa-download"></i>
|
||||
Download All
|
||||
<span>Download All</span>
|
||||
</a>
|
||||
<a class="btn btn-default" id="linkAllBtn">
|
||||
<i class="fa fa-link"></i>
|
||||
Link All
|
||||
<span>Link All</span>
|
||||
</a>
|
||||
<input type="file" name="file" multiple/>
|
||||
</div>
|
||||
@@ -501,9 +518,13 @@ function log(o) {
|
||||
</li>
|
||||
|
||||
<li><a class="ios7-a " id="tipsBtn"
|
||||
data-toggle="dropdown">{{msg . "editorTips"}}</a></li>
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-question"></span>
|
||||
{{msg . "editorTips"}}</a></li>
|
||||
<li><a class="ios7-a " id="contentHistory"
|
||||
data-toggle="dropdown">{{msg . "history"}}</a></li>
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-history"></span>
|
||||
{{msg . "history"}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -514,7 +535,7 @@ function log(o) {
|
||||
|
||||
<div id="editor">
|
||||
<!-- 编辑器 -->
|
||||
<div id="mceToolbar" style="">
|
||||
<div id="mceToolbar">
|
||||
<div id="popularToolbar"
|
||||
style="position: absolute; right: 30px; left: 0"></div>
|
||||
<a
|
||||
@@ -554,12 +575,13 @@ function log(o) {
|
||||
<!-- 为了scroll -->
|
||||
|
||||
<div class="clearfix" id="mdEditorPreview">
|
||||
<div id="left-column" class="pull-left">
|
||||
<div id="left-column">
|
||||
<div id="wmd-panel-editor" class="wmd-panel-editor">
|
||||
<textarea class="wmd-input theme" id="wmd-input" spellcheck="false" tabindex="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div id="right-column" class="pull-right">
|
||||
<div id="mdSplitter"></div>
|
||||
<div id="right-column">
|
||||
<div id="wmd-panel-preview" class="wmd-panel-preview preview-container">
|
||||
<div id="wmd-preview" class="wmd-preview"></div>
|
||||
</div>
|
||||
@@ -567,41 +589,38 @@ function log(o) {
|
||||
</div>
|
||||
<textarea id="md-section-helper"></textarea>
|
||||
</div>
|
||||
<!-- for test -->
|
||||
|
||||
<!-- 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">×</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>
|
||||
|
||||
|
||||
<!-- 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">×</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">{{msg . "cancel"}}</button>
|
||||
<button type="button" class="btn btn-primary" id="editorDialog-confirm">{{msg . "confirm"}}</button>
|
||||
</div>
|
||||
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
<!-- 弹出框 模板 -->
|
||||
<div class="modal fade bs-modal-sm" id="leanoteDialogRemote" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
</div>
|
||||
@@ -644,7 +663,7 @@ function log(o) {
|
||||
</div>
|
||||
<input type="hidden" id="toEmail"/>
|
||||
<label for="emailContent">邮件内容</label>
|
||||
<textarea class="form-control" id="emailContent">Hi, 我是李铁, leanote非常好用, 快来注册吧.</textarea>
|
||||
<textarea class="form-control" id="emailContent">Hi, 我是life, leanote非常好用, 快来注册吧.</textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -657,26 +676,15 @@ function log(o) {
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- theme -->
|
||||
<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">×</button>
|
||||
<h4 class="modal-title" class="modalTitle">主题设置</h4>
|
||||
<h4 class="modal-title" class="modalTitle">{{msg . "theme"}}</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>
|
||||
@@ -699,7 +707,6 @@ function log(o) {
|
||||
<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 -->
|
||||
@@ -724,17 +731,31 @@ function log(o) {
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- 图片上传 -->
|
||||
<div class="modal fade bs-modal-sm" id="imageDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<!-- avatar -->
|
||||
<div class="modal fade bs-modal-sm" id="avatarDialog" 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-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" class="modalTitle">{{msg . "uploadImage"}}</h4>
|
||||
<h4 class="modal-title" class="modalTitle">{{msg . "setAvatar"}}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<iframe style="" height="360" src="" scrolling="no" frameBorder="0" width="99%"></iframe>
|
||||
<form id="uploadAvatar" method="post" action="/file/uploadAvatar" enctype="multipart/form-data">
|
||||
<div id="dropAvatar" class="dropzone">
|
||||
<div>
|
||||
<img src="{{if .userInfo.Logo}}{{.userInfo.Logo}}{{else}}/images/blog/default_avatar.png{{end}}" id="avatar"/>
|
||||
</div>
|
||||
<a class="btn btn-success btn-choose-file">
|
||||
<span class="fa fa-upload"></span> Choose Image
|
||||
</a>
|
||||
<input type="file" name="file" multiple/>
|
||||
</div>
|
||||
<div id="avatarUploadMsg">
|
||||
</div>
|
||||
</form>
|
||||
</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 -->
|
||||
@@ -781,185 +802,29 @@ function log(o) {
|
||||
</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>
|
||||
|
||||
<button type="button" class="btn btn-share btn-default sendRRBtn disabled"><i class="fa fa-renren"></i> 人人</button>
|
||||
<button type="button" class="btn btn-share btn-default sendQQBtn disabled"><i class="fa fa-qq"></i> QQ空间</button>
|
||||
<button type="button" class="btn btn-share btn-primary sendTxWeiboBtn disabled"><i class="fa fa-tencent-weibo"></i> 腾讯微博</button>
|
||||
<button type="button" class="btn btn-share btn-success sendWeiboBtn disabled"><i class="fa fa-weibo"></i> 新浪微博</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">
|
||||
<div style="max-height: 300px; padding: 5px 0; text-align: center; overflow-y: auto; overflow-x: hidden" class="weibo">
|
||||
<span class="text">
|
||||
<img src="/images/loading-24.gif"/>
|
||||
正在发送邮件到{{.userInfo.Email}}...
|
||||
{{msg . "emailInSending"}} {{.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>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
|
||||
<button type="button" class="btn btn-success viewEmailBtn disabled">{{msg . "checkEmail"}}</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">{{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>
|
||||
@@ -969,7 +834,7 @@ function log(o) {
|
||||
<script src="js/i18n/msg.{{.locale}}.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
<script>
|
||||
var UrlPrefix = "http://leanote.com"; // 为了发weibo
|
||||
var UrlPrefix = "{{.siteUrl}}"; // 为了发weibo
|
||||
var UserInfo = json({{.userInfoJson}});
|
||||
var notebooks = json({{.notebooks}});
|
||||
var shareNotebooks = json({{.shareNotebooks}});
|
||||
@@ -979,14 +844,13 @@ var noteContentJson = json({{.noteContentJson}});
|
||||
var tagsJson = json({{.tagsJson}});
|
||||
LEA.locale = "{{.locale}}";
|
||||
</script>
|
||||
|
||||
|
||||
<!-- 渲染view -->
|
||||
<script src="tinymce/tinymce.js"></script>
|
||||
<script src="js/app/page.js"></script>
|
||||
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
|
||||
<script src="/js/contextmenu/jquery.contextmenu.js"></script>
|
||||
<script src="js/app/page.js"></script>
|
||||
|
||||
<script src="tinymce/tinymce.js"></script>
|
||||
<script src="js/jquery-cookie-min.js"></script>
|
||||
<script src="js/jquery-cookie.js"></script>
|
||||
<script src="js/bootstrap-min.js"></script>
|
||||
<script src="js/app/note.js"></script>
|
||||
<script src="js/app/tag.js"></script>
|
||||
@@ -998,64 +862,28 @@ LEA.locale = "{{.locale}}";
|
||||
Notebook.renderNotebooks(notebooks);
|
||||
Share.renderShareNotebooks(sharedUserInfos, shareNotebooks);
|
||||
|
||||
Note.setNoteCache(noteContentJson);
|
||||
Note.renderNotes(notes);
|
||||
if(!isEmpty(notes)) {
|
||||
Note.changeNote(notes[0].NoteId);
|
||||
}
|
||||
|
||||
Note.setNoteCache(noteContentJson);
|
||||
Note.renderNoteContent(noteContentJson)
|
||||
// Note.chanteNote设置content
|
||||
// Note.renderNoteContent(noteContentJson)
|
||||
|
||||
Tag.renderTagNav(tagsJson);
|
||||
|
||||
// init notebook后才调用
|
||||
initSlimScroll();
|
||||
</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="//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" />
|
||||
|
||||
<!-- code -->
|
||||
<link href="/public/mdeditor/editor/google-code-prettify/prettify.css" rel="stylesheet" />
|
||||
<!-- js version 2.0 use require.js -->
|
||||
<script src="/js/require.js"></script>
|
||||
<script>
|
||||
require.config({
|
||||
baseUrl: '/public',
|
||||
paths: {
|
||||
// 'jquery': 'js/jquery-1.9.0.min',
|
||||
'leaui_image': 'tinymce/plugins/leaui_image/public/js/for_editor',
|
||||
'attachment_upload': 'js/app/attachment_upload',
|
||||
'jquery.ui.widget': 'tinymce/plugins/leaui_image/public/js/jquery.ui.widget',
|
||||
'fileupload': '/tinymce/plugins/leaui_image/public/js/jquery.fileupload',
|
||||
'iframe-transport': '/tinymce/plugins/leaui_image/public/js/jquery.iframe-transport'
|
||||
},
|
||||
shim: {
|
||||
'fileupload': {deps: ['jquery.ui.widget', 'iframe-transport']}
|
||||
}
|
||||
});
|
||||
require(['leaui_image'], function(leaui_image) {
|
||||
});
|
||||
require(['attachment_upload'], function(attachment_upload) {
|
||||
});
|
||||
<script src="/js/main.js"></script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,17 +3,19 @@
|
||||
<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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name=”apple-mobile-web-app-capable” content=”yes” />
|
||||
<meta name="keywords" content="leanote,leanote.com">
|
||||
<meta name="description" content="leanote, {{msg $ "moto"}}">
|
||||
<title>leanote, {{msg $ "moto"}}</title>
|
||||
<meta name="description" content="leanote, Not Just A Notebook">
|
||||
<title>leanote, Not Just A Notebook</title>
|
||||
|
||||
<link href="css/bootstrap.css" rel="stylesheet" />
|
||||
<link href="/css/bootstrap.css" rel="stylesheet" />
|
||||
<!-- 先加载, 没有样式, 宽度不定 -->
|
||||
<link rel="stylesheet" href="tinymce/skins/custom/skin.min.css" type="text/css" />
|
||||
<link rel="stylesheet" href="tinymce/skins/custom/skin.min.css" rel="stylesheet"/>
|
||||
|
||||
<!-- leanote css -->
|
||||
<link href="css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" />
|
||||
<link href="css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet" />
|
||||
<link href="css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
|
||||
<script>
|
||||
var hash = location.hash;
|
||||
@@ -27,7 +29,6 @@ document.write(files);
|
||||
|
||||
|
||||
<!-- For Develop writting mod -->
|
||||
|
||||
<script>
|
||||
function log(o) {
|
||||
|
||||
@@ -56,34 +57,35 @@ function log(o) {
|
||||
</div>
|
||||
<!-- search -->
|
||||
<div class="pull-left" id="searchWrap">
|
||||
<form class="navbar-form form-inline col-lg-2 hidden-xs" id="searchNote">
|
||||
<form class="navbar-form form-inline col-lg-2" id="searchNote">
|
||||
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 全局按钮 -->
|
||||
<div class="pull-left" style="" id="newNoteWrap">
|
||||
|
||||
<!-- 新建笔记 -->
|
||||
<div id="newMyNote">
|
||||
<a id="newNoteBtn" title="{{msg . "newNote"}}">
|
||||
<i class="fa fa-file-o"></i>
|
||||
{{msg . "newNote"}}
|
||||
<span class="new-note-text">{{msg . "newNote"}}</span>
|
||||
<span class="new-note-text-abbr">{{msg . "new"}}</span>
|
||||
</a>
|
||||
<span class="new-split">|</span>
|
||||
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
Markdown
|
||||
<span class="new-markdown-text">Markdown</span>
|
||||
<span class="new-markdown-text-abbr">Md</span>
|
||||
</a>
|
||||
<span class="for-split"> - </span>
|
||||
<span id="curNotebookForNewNote" notebookId=""></span>
|
||||
<div class="dropdown" style="display: inline-block">
|
||||
<a class="ios7-a dropdown-toggle"
|
||||
id="dropdownMenu2" data-toggle="dropdown">
|
||||
id="listNotebookDropdownMenu" data-toggle="dropdown">
|
||||
<i class="fa fa-angle-down"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
|
||||
<input type="text" placeholder="search notebook" class="form-control" id="searchNotebookForAdd"/>
|
||||
<ul class="clearfix" role="menu" aria-labelledby="dropdownMenu2" id="notebookNavForNewNote">
|
||||
<input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/>
|
||||
<ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -93,19 +95,21 @@ function log(o) {
|
||||
<div id="newSharedNote" style="display: none">
|
||||
<a id="newSharedNoteBtn">
|
||||
<i class="fa fa-file-o"></i>
|
||||
{{msg . "newNote"}}
|
||||
<span class="new-note-text">{{msg . "newNote"}}</span>
|
||||
<span class="new-note-text-abbr">{{msg . "new"}}</span>
|
||||
</a>
|
||||
<span class="new-split">|</span>
|
||||
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
Markdown
|
||||
<span class="new-markdown-text">Markdown</span>
|
||||
<span class="new-markdown-text-abbr">Md</span>
|
||||
</a>
|
||||
<span class="for-split"> - </span>
|
||||
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
|
||||
<div class="dropdown" style="display: inline-block">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown">
|
||||
<a id="listShareNotebookDropdownMenu" class="ios7-a dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-angle-down"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-list" style="left: -200px;" >
|
||||
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddShareDropdownList" >
|
||||
<ul id="notebookNavForNewSharedNote"></ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -117,21 +121,11 @@ function log(o) {
|
||||
<span id="loading">
|
||||
</span>
|
||||
</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" />
|
||||
-->
|
||||
<a class="dropdown-toggle" title="{{.userInfo.Username}}" data-toggle="dropdown" style="line-height: 60px;">
|
||||
<img alt="{{.userInfo.Username}}" title="{{.userInfo.Username}}" src="{{if .userInfo.Logo}}{{.userInfo.Logo}}{{else}}/images/blog/default_avatar.png{{end}}" id="myAvatar"/>
|
||||
<span class="username">
|
||||
{{if .userInfo.UsernameRaw}}
|
||||
{{.userInfo.UsernameRaw}}
|
||||
@@ -143,12 +137,22 @@ function log(o) {
|
||||
</a>
|
||||
<ul class="dropdown-menu li-a" role="menu">
|
||||
<li role="presentation" id="setInfo">
|
||||
<a>
|
||||
<i class="fa fa-info"></i>
|
||||
{{msg . "accountSetting"}}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" id="setAvatarMenu">
|
||||
<a>
|
||||
<i class="fa fa-smile-o"></i>
|
||||
{{msg . "setAvatar"}}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" id="setTheme">
|
||||
<a>
|
||||
<i class="fa fa-sun-o"></i>
|
||||
{{msg . "themeSetting"}}
|
||||
</a>
|
||||
</li>
|
||||
<!--
|
||||
<li role="presentation" id="yourSuggestions">
|
||||
@@ -156,6 +160,22 @@ function log(o) {
|
||||
{{msg . "yourSuggestions"}}
|
||||
</li>
|
||||
-->
|
||||
<li role="presentation" class="my-link" >
|
||||
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
|
||||
<i class="fa fa-bold"></i>
|
||||
{{msg . "myBlog"}}</a>
|
||||
</li>
|
||||
|
||||
{{if .isAdmin}}
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation">
|
||||
<a target="_blank" title="{{msg . "amdin"}}" href="/admin/index">
|
||||
<i class="fa fa-dashboard"></i>
|
||||
{{msg . "admin"}}
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation" onclick="logout()">
|
||||
<i class="fa fa-sign-out"></i>
|
||||
{{msg . "logout"}}
|
||||
@@ -164,12 +184,8 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-right" style="line-height: 60px; margin:0 10px">
|
||||
<a target="_blank" title="lea++, leanote blog platform" href="/lea/index">lea++</a>
|
||||
</div>
|
||||
|
||||
<div class="pull-right" style="margin: 0 10px" id="topNav">
|
||||
<a target="_blank" href="/blog/{{.userInfo.Username}}">
|
||||
<div class="pull-right top-nav" id="myBlog">
|
||||
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
|
||||
{{msg . "myBlog"}}
|
||||
</a>
|
||||
</div>
|
||||
@@ -243,7 +259,7 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<div class="folderBody">
|
||||
<input type="text" class="form-control" id="searchNotebookForList" placeholder="search notebook"/>
|
||||
<input type="text" class="form-control" id="searchNotebookForList" placeholder="Search notebook"/>
|
||||
<ul class="ztree" id="notebookList"></ul>
|
||||
<ul class="ztree" id="notebookListForSearch"></ul>
|
||||
</div>
|
||||
@@ -258,10 +274,10 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<ul class="folderBody clearfix" 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>
|
||||
<li data-tag="red"><a> <span class="label label-red">{{msg . "red"}}</span></a></li>
|
||||
<li data-tag="blue"><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li>
|
||||
<li data-tag="yellow"><a> <span class="label label-yellow">{{msg . "yellow"}}</span></a></li>
|
||||
<li data-tag="green"><a> <span class="label label-green">{{msg . "green"}}</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -313,9 +329,7 @@ function log(o) {
|
||||
<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">
|
||||
@@ -374,14 +388,13 @@ function log(o) {
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 笔记列表 -->
|
||||
<!-- wrap 为了slimScroll -->
|
||||
<div id="noteItemListWrap" style="position: absolute; left: 0; right: 0; top: 41px; bottom: 3px">
|
||||
<div id="noteItemList">
|
||||
</div>
|
||||
<div id="noteItemListWrap">
|
||||
<ul id="noteItemList">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -393,23 +406,17 @@ function log(o) {
|
||||
<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="clearfix" id="noteReadInfo">
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
<span id="noteReadTags"></span>
|
||||
|
||||
<!-- 修改时间 -->
|
||||
<div class="pull-left" style="margin-left: 10px;">
|
||||
<i class="fa fa-calendar"></i>{{msg . "update"}}
|
||||
<span id="noteReadUpdatedTime"></span>
|
||||
</div>
|
||||
<i class="fa fa-calendar"></i>{{msg . "update"}}
|
||||
<span id="noteReadUpdatedTime"></span>
|
||||
|
||||
<!-- 修改时间 -->
|
||||
<div class="pull-left" style="margin-left: 10px;">
|
||||
<i class="fa fa-calendar"></i>{{msg . "create"}}
|
||||
<span id="noteReadCreatedTime"></span>
|
||||
</div>
|
||||
<i class="fa fa-calendar"></i>{{msg . "create"}}
|
||||
<span id="noteReadCreatedTime"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -418,18 +425,22 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
<!-- 遮罩, 为了resize3Columns用 -->
|
||||
<div id="noteMask"
|
||||
style="position: absolute; top: 0px; bottom: 0px; right: 0; left: 10px; z-index: -1"></div>
|
||||
<div id="noteMask" class="note-mask"></div>
|
||||
<div id="noteMaskForLoading" class="note-mask">
|
||||
<img src="/images/loading-24.gif"/>
|
||||
<br />
|
||||
loading...
|
||||
</div>
|
||||
<div id="editorMask">
|
||||
该笔记本下空空如也...何不
|
||||
{{msg . "noNoteNewNoteTips"}}
|
||||
<br />
|
||||
<br />
|
||||
<div id="editorMaskBtns">
|
||||
<br />
|
||||
<a class="note">新建笔记</a>
|
||||
<a class="markdown">新建Markdown笔记</a>
|
||||
<a class="note">{{msg . "newNote"}}</a>
|
||||
<a class="markdown">{{msg . "newMarkdownNote"}}</a>
|
||||
</div>
|
||||
<div id="editorMaskBtnsEmpty">
|
||||
Sorry, 这里不能添加笔记的.
|
||||
{{msg . "canntNewNoteTips"}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="noteTop">
|
||||
@@ -453,7 +464,9 @@ function log(o) {
|
||||
<a
|
||||
class="metro-a dropdown-toggle" data-toggle="dropdown"
|
||||
id="addTagTrigger" style="cursor: text; padding-left: 0">
|
||||
<span class="add-tag-text">
|
||||
{{msg . "clickAddTag"}}
|
||||
</span>
|
||||
</a>
|
||||
<input type="text" id="addTagInput" />
|
||||
<ul class="dropdown-menu" role="menu" id="tagColor">
|
||||
@@ -467,34 +480,34 @@ function log(o) {
|
||||
</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>
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-save"></span>
|
||||
{{msg . "save"}}</a></li>
|
||||
|
||||
<li class="dropdown" id="attachDropdown">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
|
||||
<!--
|
||||
<span class="fa fa-upload"></span>
|
||||
-->
|
||||
<span class="fa fa-paperclip"></span>
|
||||
{{msg . "attachments"}}<span id="attachNum"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu" id="attachMenu">
|
||||
<ul id="attachList">
|
||||
|
||||
</ul>
|
||||
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data">
|
||||
<div id="dropAttach">
|
||||
<div id="dropAttach" class="dropzone">
|
||||
<a class="btn btn-success btn-choose-file">
|
||||
Choose File to Upload
|
||||
<i class="fa fa-upload"></i>
|
||||
<span>Choose File</span>
|
||||
</a>
|
||||
<a class="btn btn-default" id="downloadAllBtn">
|
||||
<i class="fa fa-download"></i>
|
||||
Download All
|
||||
<span>Download All</span>
|
||||
</a>
|
||||
<a class="btn btn-default" id="linkAllBtn">
|
||||
<i class="fa fa-link"></i>
|
||||
Link All
|
||||
<span>Link All</span>
|
||||
</a>
|
||||
<input type="file" name="file" multiple/>
|
||||
</div>
|
||||
@@ -505,9 +518,13 @@ function log(o) {
|
||||
</li>
|
||||
|
||||
<li><a class="ios7-a " id="tipsBtn"
|
||||
data-toggle="dropdown">{{msg . "editorTips"}}</a></li>
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-question"></span>
|
||||
{{msg . "editorTips"}}</a></li>
|
||||
<li><a class="ios7-a " id="contentHistory"
|
||||
data-toggle="dropdown">{{msg . "history"}}</a></li>
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-history"></span>
|
||||
{{msg . "history"}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -518,7 +535,7 @@ function log(o) {
|
||||
|
||||
<div id="editor">
|
||||
<!-- 编辑器 -->
|
||||
<div id="mceToolbar" style="">
|
||||
<div id="mceToolbar">
|
||||
<div id="popularToolbar"
|
||||
style="position: absolute; right: 30px; left: 0"></div>
|
||||
<a
|
||||
@@ -558,12 +575,13 @@ function log(o) {
|
||||
<!-- 为了scroll -->
|
||||
|
||||
<div class="clearfix" id="mdEditorPreview">
|
||||
<div id="left-column" class="pull-left">
|
||||
<div id="left-column">
|
||||
<div id="wmd-panel-editor" class="wmd-panel-editor">
|
||||
<textarea class="wmd-input theme" id="wmd-input" spellcheck="false" tabindex="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div id="right-column" class="pull-right">
|
||||
<div id="mdSplitter"></div>
|
||||
<div id="right-column">
|
||||
<div id="wmd-panel-preview" class="wmd-panel-preview preview-container">
|
||||
<div id="wmd-preview" class="wmd-preview"></div>
|
||||
</div>
|
||||
@@ -571,41 +589,38 @@ function log(o) {
|
||||
</div>
|
||||
<textarea id="md-section-helper"></textarea>
|
||||
</div>
|
||||
<!-- for test -->
|
||||
|
||||
<!-- 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">×</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>
|
||||
|
||||
|
||||
<!-- 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">×</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">{{msg . "cancel"}}</button>
|
||||
<button type="button" class="btn btn-primary" id="editorDialog-confirm">{{msg . "confirm"}}</button>
|
||||
</div>
|
||||
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
<!-- 弹出框 模板 -->
|
||||
<div class="modal fade bs-modal-sm" id="leanoteDialogRemote" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
</div>
|
||||
@@ -648,7 +663,7 @@ function log(o) {
|
||||
</div>
|
||||
<input type="hidden" id="toEmail"/>
|
||||
<label for="emailContent">邮件内容</label>
|
||||
<textarea class="form-control" id="emailContent">Hi, 我是李铁, leanote非常好用, 快来注册吧.</textarea>
|
||||
<textarea class="form-control" id="emailContent">Hi, 我是life, leanote非常好用, 快来注册吧.</textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -661,26 +676,15 @@ function log(o) {
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- theme -->
|
||||
<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">×</button>
|
||||
<h4 class="modal-title" class="modalTitle">主题设置</h4>
|
||||
<h4 class="modal-title" class="modalTitle">{{msg . "theme"}}</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>
|
||||
@@ -703,7 +707,6 @@ function log(o) {
|
||||
<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 -->
|
||||
@@ -728,17 +731,31 @@ function log(o) {
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- 图片上传 -->
|
||||
<div class="modal fade bs-modal-sm" id="imageDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<!-- avatar -->
|
||||
<div class="modal fade bs-modal-sm" id="avatarDialog" 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-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" class="modalTitle">{{msg . "uploadImage"}}</h4>
|
||||
<h4 class="modal-title" class="modalTitle">{{msg . "setAvatar"}}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<iframe style="" height="360" src="" scrolling="no" frameBorder="0" width="99%"></iframe>
|
||||
<form id="uploadAvatar" method="post" action="/file/uploadAvatar" enctype="multipart/form-data">
|
||||
<div id="dropAvatar" class="dropzone">
|
||||
<div>
|
||||
<img src="{{if .userInfo.Logo}}{{.userInfo.Logo}}{{else}}/images/blog/default_avatar.png{{end}}" id="avatar"/>
|
||||
</div>
|
||||
<a class="btn btn-success btn-choose-file">
|
||||
<span class="fa fa-upload"></span> Choose Image
|
||||
</a>
|
||||
<input type="file" name="file" multiple/>
|
||||
</div>
|
||||
<div id="avatarUploadMsg">
|
||||
</div>
|
||||
</form>
|
||||
</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 -->
|
||||
@@ -785,185 +802,29 @@ function log(o) {
|
||||
</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>
|
||||
|
||||
<button type="button" class="btn btn-share btn-default sendRRBtn disabled"><i class="fa fa-renren"></i> 人人</button>
|
||||
<button type="button" class="btn btn-share btn-default sendQQBtn disabled"><i class="fa fa-qq"></i> QQ空间</button>
|
||||
<button type="button" class="btn btn-share btn-primary sendTxWeiboBtn disabled"><i class="fa fa-tencent-weibo"></i> 腾讯微博</button>
|
||||
<button type="button" class="btn btn-share btn-success sendWeiboBtn disabled"><i class="fa fa-weibo"></i> 新浪微博</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">
|
||||
<div style="max-height: 300px; padding: 5px 0; text-align: center; overflow-y: auto; overflow-x: hidden" class="weibo">
|
||||
<span class="text">
|
||||
<img src="/images/loading-24.gif"/>
|
||||
正在发送邮件到{{.userInfo.Email}}...
|
||||
{{msg . "emailInSending"}} {{.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>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
|
||||
<button type="button" class="btn btn-success viewEmailBtn disabled">{{msg . "checkEmail"}}</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">{{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>
|
||||
@@ -973,7 +834,7 @@ function log(o) {
|
||||
<script src="js/i18n/msg.{{.locale}}.js"></script>
|
||||
<script src="js/common-min.js"></script>
|
||||
<script>
|
||||
var UrlPrefix = "http://leanote.com"; // 为了发weibo
|
||||
var UrlPrefix = "{{.siteUrl}}"; // 为了发weibo
|
||||
var UserInfo = json({{.userInfoJson}});
|
||||
var notebooks = json({{.notebooks}});
|
||||
var shareNotebooks = json({{.shareNotebooks}});
|
||||
@@ -983,14 +844,13 @@ var noteContentJson = json({{.noteContentJson}});
|
||||
var tagsJson = json({{.tagsJson}});
|
||||
LEA.locale = "{{.locale}}";
|
||||
</script>
|
||||
|
||||
|
||||
<!-- 渲染view -->
|
||||
<script src="tinymce/tinymce.js"></script>
|
||||
<script src="js/app/page-min.js"></script>
|
||||
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
|
||||
<script src="/js/contextmenu/jquery.contextmenu-min.js"></script>
|
||||
<script src="js/app/page-min.js"></script>
|
||||
|
||||
<script src="tinymce/tinymce.js"></script>
|
||||
<script src="js/jquery-cookie-min.js"></script>
|
||||
<script src="js/jquery-cookie.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>
|
||||
@@ -1002,64 +862,28 @@ LEA.locale = "{{.locale}}";
|
||||
Notebook.renderNotebooks(notebooks);
|
||||
Share.renderShareNotebooks(sharedUserInfos, shareNotebooks);
|
||||
|
||||
Note.setNoteCache(noteContentJson);
|
||||
Note.renderNotes(notes);
|
||||
if(!isEmpty(notes)) {
|
||||
Note.changeNote(notes[0].NoteId);
|
||||
}
|
||||
|
||||
Note.setNoteCache(noteContentJson);
|
||||
Note.renderNoteContent(noteContentJson)
|
||||
// Note.chanteNote设置content
|
||||
// Note.renderNoteContent(noteContentJson)
|
||||
|
||||
Tag.renderTagNav(tagsJson);
|
||||
|
||||
// init notebook后才调用
|
||||
initSlimScroll();
|
||||
</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="//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" />
|
||||
|
||||
<!-- code -->
|
||||
<link href="/public/mdeditor/editor/google-code-prettify/prettify.css" rel="stylesheet" />
|
||||
<!-- js version 2.0 use require.js -->
|
||||
<script src="/js/require.js"></script>
|
||||
<script>
|
||||
require.config({
|
||||
baseUrl: '/public',
|
||||
paths: {
|
||||
// 'jquery': 'js/jquery-1.9.0.min',
|
||||
'leaui_image': 'tinymce/plugins/leaui_image/public/js/for_editor',
|
||||
'attachment_upload': 'js/app/attachment_upload',
|
||||
'jquery.ui.widget': 'tinymce/plugins/leaui_image/public/js/jquery.ui.widget',
|
||||
'fileupload': '/tinymce/plugins/leaui_image/public/js/jquery.fileupload',
|
||||
'iframe-transport': '/tinymce/plugins/leaui_image/public/js/jquery.iframe-transport'
|
||||
},
|
||||
shim: {
|
||||
'fileupload': {deps: ['jquery.ui.widget', 'iframe-transport']}
|
||||
}
|
||||
});
|
||||
require(['leaui_image'], function(leaui_image) {
|
||||
});
|
||||
require(['attachment_upload'], function(attachment_upload) {
|
||||
});
|
||||
<script src="/js/main-min.js"></script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,18 +1,32 @@
|
||||
{{template "home/header_box.html" .}}
|
||||
|
||||
<section id="box">
|
||||
<section id="box" class="animated fadeInUp">
|
||||
<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>
|
||||
<h1 id="logo">leanote</h1>
|
||||
<div id="boxForm">
|
||||
<div id="boxHeader">We got a error</div>
|
||||
<form>
|
||||
<div class="alert alert-danger" id="loginMsg" style="display: block">
|
||||
Sorry, we can't get your infomation.
|
||||
|
||||
<br />
|
||||
Please <a href="/login">{{msg . "login"}}</a> Or <a href="/register?email={{.email}}">{{msg . "register"}}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="boxFooter">
|
||||
<p>
|
||||
<a href="/login">{{msg . "login"}}</a>
|
||||
|
||||
<a href="/index">{{msg . "home"}}</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/index">leanote</a> © 2014
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,37 +1,36 @@
|
||||
<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">×</button>
|
||||
<h4 class="modal-title" id="modalTitle">分享 <b>{{.title}}</b></h4>
|
||||
<h4 class="modal-title" id="modalTitle">{{msg . "share"}} <b>{{.title}}</b></h4>
|
||||
</div>
|
||||
{{$noteOrNotebookId := .noteOrNotebookId}}
|
||||
|
||||
<div class="modal-body">
|
||||
<button class="btn btn-default" id="addShareNotebookBtn">添加分享</button>
|
||||
<button class="btn btn-default" id="addShareNotebookBtn">{{msg . "addShare"}}</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>
|
||||
<th>{{msg . "friendEmail"}}</th>
|
||||
<th>{{msg . "permission"}}</th>
|
||||
<th width="150px">{{msg . "delete"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr id="tr1">
|
||||
<td>#</td>
|
||||
<td>
|
||||
<input id="friendsEmail" type="text" class="form-control" style="width: 200px" placeholder="好友邮箱">
|
||||
<input id="friendsEmail" type="text" class="form-control" placeholder="{{msg . "friendEmail"}}">
|
||||
</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>
|
||||
<label for="readPerm1"><input type="radio" name="perm1" checked="checked" value="0" id="readPerm1"> {{msg . "readOnly"}}</label>
|
||||
<label for="writePerm1"><input type="radio" name="perm1" value="1" id="writePerm1"> {{msg . "writable"}}</label>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-success" onclick="addShareNoteOrNotebook(1)">分享</button>
|
||||
<button class="btn btn-warning" onclick="deleteShareNoteOrNotebook(1)">删除</button>
|
||||
<button class="btn btn-success" onclick="addShareNoteOrNotebook(1)">{{msg . "share"}}</button>
|
||||
<button class="btn btn-warning" onclick="deleteShareNoteOrNotebook(1)">{{msg . "delete"}}</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{range $i, $v := .noteOrNotebookShareUserInfos}}
|
||||
@@ -41,13 +40,13 @@
|
||||
<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>
|
||||
<a href="#" noteOrNotebookId="{{$noteOrNotebookId}}" perm="{{$v.Perm}}" toUserId="{{$toUserId}}" title="点击改变权限" class="btn btn-default change-perm">{{msg . "readOnly"}}</a>
|
||||
{{else}}
|
||||
<a href="#" noteOrNotebookId="{{$noteOrNotebookId}}" perm="{{$v.Perm}}" toUserId="{{$toUserId}}" title="点击改变权限" class="btn btn-default change-perm">可编辑</a>
|
||||
<a href="#" noteOrNotebookId="{{$noteOrNotebookId}}" perm="{{$v.Perm}}" toUserId="{{$toUserId}}" title="点击改变权限" class="btn btn-default change-perm">{{msg . "writable"}}</a>
|
||||
{{end}}
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" noteOrNotebookId="{{$noteOrNotebookId}}" toUserId="{{$toUserId}}" class="btn btn-warning delete-share">删除</a>
|
||||
<a href="#" noteOrNotebookId="{{$noteOrNotebookId}}" toUserId="{{$toUserId}}" class="btn btn-warning delete-share">{{msg . "delete"}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
@@ -56,7 +55,7 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
|
||||
</div>
|
||||
|
||||
</div><!-- /.modal-content -->
|
||||
|
||||
272
app/views/User/account.html
Normal file
272
app/views/User/account.html
Normal file
@@ -0,0 +1,272 @@
|
||||
<div class="modal-dialog modal-sm" id="accountInfoDialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="modalTitle">{{msg . "accountSetting"}}</h4>
|
||||
</div>
|
||||
|
||||
{{if .userInfo.Email}}
|
||||
<div class="modal-body">
|
||||
<ul class="nav nav-tabs" id="infoTabs">
|
||||
<li class="active"><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>
|
||||
<div class="alert alert-danger" id="usernameMsg" style="display: none"></div>
|
||||
<label for="username">{{msg . "setUsername"}}</label>
|
||||
<input type="text" class="form-control" id="username"
|
||||
value="{{.userInfo.Username}}"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputUsername"},
|
||||
{rule: "noSpecialChars", msg: "noSpecialChars"},
|
||||
{rule: "minLength", data: 4, msg: "minLength", msgData: 4}
|
||||
]'
|
||||
data-msg_target="#usernameMsg"
|
||||
/>
|
||||
{{msg . "setUsernameTips" .userInfo.Email}}
|
||||
|
||||
<div>
|
||||
<button id="usernameBtn" class="btn btn-success">{{msg . "submit"}}</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="emailInfo">
|
||||
<form>
|
||||
|
||||
{{msg . "currentEmail" .userInfo.Email}}
|
||||
{{if .userInfo.Verified}}
|
||||
<span class="label label-green">{{msg . "verified"}}</span>
|
||||
{{else}}
|
||||
<span class="label label-red">{{msg . "unVerified"}}</span>
|
||||
<a class="raw nowToActive">{{msg . "verifiedNow"}}</a>
|
||||
{{msg . "or"}}
|
||||
<a class="raw reSendActiveEmail">{{msg . "resendVerifiedEmail"}}</a>
|
||||
{{end}}
|
||||
<br />
|
||||
<label for="email">{{msg . "updateEmail"}}</label>
|
||||
<div class="alert alert-danger" id="emailMsg" style="display: none" placeholder="New Email"></div>
|
||||
<input type="text" class="form-control"
|
||||
id="email"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputEmail"},
|
||||
{rule: "email", msg: "errorEmail"}
|
||||
]'
|
||||
data-msg_target="#emailMsg"
|
||||
/>
|
||||
{{msg . "updateEmailTips"}}
|
||||
|
||||
<div>
|
||||
<button id="emailBtn" class="btn btn-success">{{msg . "sendVerifiedEmail"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane" id="updatePwd">
|
||||
<form>
|
||||
<div class="alert alert-danger" id="pwdMsg" style="display: none"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="oldPwd">{{msg . "oldPassword"}}</label>
|
||||
<input type="password" class="form-control" id="oldPwd" name="oldPwd"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputPassword"}
|
||||
]'
|
||||
data-msg_target="#pwdMsg"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="pwd">{{msg . "newPassword"}}</label>
|
||||
<input type="password" class="form-control" id="pwd" name="pwd"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputNewPassword"},
|
||||
{rule: "password", msg: "errorPassword"}
|
||||
]'
|
||||
data-msg_target="#pwdMsg"
|
||||
>
|
||||
{{msg . "passwordTips"}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="pwd2">{{msg . "password2"}}</label>
|
||||
<input type="password" class="form-control" id="pwd2" name="pwd2"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputPassword2"},
|
||||
{rule: "equalTo", data:"#pwd", msg: "confirmPassword"}
|
||||
]'
|
||||
data-msg_target="#pwdMsg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button id="pwdBtn" class="btn btn-success">{{msg . "submit"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="modal-body">
|
||||
<ul class="nav nav-tabs" id="thirdMyTabs">
|
||||
<li class="active"><a href="#accountInfo" data-toggle="tab">{{msg . "createAccount"}}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="accountInfo">
|
||||
{{msg . "thirdCreateAcountTips"}}
|
||||
<form>
|
||||
<div class="alert alert-danger" id="thirdAccountMsg" style="display: none"></div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="thirdEmail">{{msg . "email"}}</label>
|
||||
<input type="text" class="form-control" id="thirdEmail" name="email"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputEmail"},
|
||||
{rule: "email", msg: "errorEmail"}
|
||||
]'
|
||||
data-msg_target="#thirdAccountMsg"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="thirdPwd">{{msg . "password"}}</label>
|
||||
<input type="password" class="form-control" id="thirdPwd" name="pwd"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputPassword"},
|
||||
{rule: "password", msg: "errorPassword"}
|
||||
]'
|
||||
data-msg_target="#thirdAccountMsg"
|
||||
/>
|
||||
{{msg . "passwordTips"}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="thirdPwd2">{{msg . "password2"}}</label>
|
||||
<input type="password" class="form-control" id="thirdPwd2" name="pwd2"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputPassword2"},
|
||||
{rule: "equalTo", data:"#thirdPwd", msg: "confirmPassword"}
|
||||
]'
|
||||
data-msg_target="#thirdAccountMsg"
|
||||
>
|
||||
</div>
|
||||
|
||||
<button id="accountBtn" class="btn btn-success">{{msg . "submit"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$('#infoTabs a').eq({{.tab}}).tab('show');
|
||||
|
||||
//--------------
|
||||
// 第三方账号设置
|
||||
var acountVd = new vd.init("#accountInfo");
|
||||
$("#accountInfoDialog").on("click", "#accountBtn", function(e) {
|
||||
e.preventDefault();
|
||||
if(!acountVd.valid()) {
|
||||
return;
|
||||
}
|
||||
var email = $("#thirdEmail").val();
|
||||
var pwd = $("#thirdPwd").val();
|
||||
var pwd2 = $("#thirdPwd2").val();
|
||||
post("/user/addAccount", {email: email, pwd: pwd}, function(ret) {
|
||||
if(ret.Ok) {
|
||||
showAlert("#thirdAccountMsg", getMsg("createAccountSuccess"), "success");
|
||||
UserInfo.Email = email;
|
||||
$("#curEmail").html(email);
|
||||
hideDialogRemote(1000);
|
||||
} else {
|
||||
showAlert("#thirdAccountMsg", ret.Msg || getMsg("createAccountFailed"), "danger");
|
||||
}
|
||||
}, this);
|
||||
});
|
||||
|
||||
//-------------
|
||||
var usernameVd = new vd.init("#baseInfo");
|
||||
$("#usernameBtn").click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if(!usernameVd.valid()) {
|
||||
return;
|
||||
}
|
||||
var username = $("#username").val();
|
||||
post("/user/updateUsername", {username: username}, function(ret) {
|
||||
if(ret.Ok) {
|
||||
UserInfo.UsernameRaw = username;
|
||||
UserInfo.Username = username.toLowerCase();
|
||||
$(".username").html(username);
|
||||
showAlert('#usernameMsg', getMsg("updateUsernameSuccess"), "success");
|
||||
} else {
|
||||
showAlert('#usernameMsg', ret.Msg || getMsg("usernameIsExisted"), "danger");
|
||||
}
|
||||
}, "#usernameBtn");
|
||||
|
||||
});
|
||||
|
||||
// 修改邮箱
|
||||
var emailVd = new vd.init("#emailInfo");
|
||||
$("#emailBtn").click(function(e) {
|
||||
e.preventDefault();
|
||||
if(!emailVd.valid()) {
|
||||
return;
|
||||
}
|
||||
var email = $("#email").val();
|
||||
post("/user/updateEmailSendActiveEmail", {email: email}, function(e) {
|
||||
if(e.Ok) {
|
||||
var url = getEmailLoginAddress(email);
|
||||
showAlert("#emailMsg", getMsg("verifiedEmaiHasSent") +" <a href='" + url + "' target='_blank'>" + getMsg("checkEmail") + "</a>", "success");
|
||||
} else {
|
||||
showAlert("#emailMsg", e.Msg || getMsg("emailSendFailed"), "danger");
|
||||
}
|
||||
}, "#emailBtn");
|
||||
});
|
||||
|
||||
// 修改密码
|
||||
var updatePwdVd = new vd.init("#updatePwd");
|
||||
$("#pwdBtn").click(function(e) {
|
||||
e.preventDefault();
|
||||
if(!updatePwdVd.valid()) {
|
||||
return;
|
||||
}
|
||||
var oldPwd = $("#oldPwd").val();
|
||||
var pwd = $("#pwd").val();
|
||||
post("/user/updatePwd", {oldPwd: oldPwd, pwd: pwd}, function(e) {
|
||||
if(e.Ok) {
|
||||
showAlert("#pwdMsg", getMsg("updatePasswordSuccess"), "success");
|
||||
} else {
|
||||
showAlert("#pwdMsg", e.Msg, "danger");
|
||||
}
|
||||
}, "#pwdBtn");
|
||||
});
|
||||
|
||||
// 重新发送
|
||||
$(".reSendActiveEmail").click(function() {
|
||||
// 弹框出来
|
||||
showDialog("reSendActiveEmailDialog", {title: getMsg("sendVerifiedEmail"), postShow: function() {
|
||||
ajaxGet("/user/reSendActiveEmail", {}, function(ret) {
|
||||
if (typeof ret == "object" && ret.Ok) {
|
||||
$("#leanoteDialog .text").html(getMsg("sendSuccess"))
|
||||
$("#leanoteDialog .viewEmailBtn").removeClass("disabled");
|
||||
$("#leanoteDialog .viewEmailBtn").click(function() {
|
||||
hideDialog();
|
||||
var url = getEmailLoginAddress(UserInfo.Email);
|
||||
window.open(url, "_blank");
|
||||
});
|
||||
} else {
|
||||
$("#leanoteDialog .text").html(getMsg("sendFailed"))
|
||||
}
|
||||
});
|
||||
}});
|
||||
});
|
||||
// 现在去验证
|
||||
$(".nowToActive").click(function() {
|
||||
var url = getEmailLoginAddress(UserInfo.Email);
|
||||
window.open(url, "_blank");
|
||||
});
|
||||
</script>
|
||||
@@ -1,22 +1,32 @@
|
||||
{{template "home/header_box.html" .}}
|
||||
<section id="box">
|
||||
<div id="posts">
|
||||
<h1>
|
||||
leanote 验证邮箱 -
|
||||
{{if .ok}}成功{{else}}失败{{end}}
|
||||
</h1>
|
||||
|
||||
<form class="form-inline" id="boxForm">
|
||||
|
||||
<section id="box" class="animated fadeInUp">
|
||||
<div>
|
||||
<h1 id="logo">leanote</h1>
|
||||
<div id="boxForm">
|
||||
<div id="boxHeader">验证邮箱 - {{if .ok}}成功{{else}}失败{{end}}</div>
|
||||
<form>
|
||||
<div class="alert alert-danger" id="loginMsg"> </div>
|
||||
您的邮箱 {{.email}} 验证
|
||||
{{if .ok}}成功{{else}}失败{{end}}
|
||||
|
||||
您的邮箱 {{.email}} 验证
|
||||
{{if .ok}}成功{{else}}失败{{end}}
|
||||
|
||||
{{if .msg}}<br />{{.msg}}{{end}}
|
||||
|
||||
<br />
|
||||
<a href="/note">回到我的笔记</a>
|
||||
</form>
|
||||
{{if .msg}}<br />
|
||||
{{.msg}}{{end}}
|
||||
|
||||
<br />
|
||||
<a href="/note">回到我的笔记</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="boxFooter">
|
||||
<p>
|
||||
<a href="/index">{{msg . "home"}}</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/index">leanote</a> © 2014
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,25 +1,35 @@
|
||||
{{template "home/header_box.html" .}}
|
||||
<section id="box">
|
||||
|
||||
<section id="box" class="animated fadeInUp">
|
||||
<div>
|
||||
<h1>
|
||||
leanote 验证邮箱 -
|
||||
{{if .ok}}成功{{else}}失败{{end}}
|
||||
</h1>
|
||||
|
||||
<form class="form-inline" id="boxForm">
|
||||
您的邮箱 {{.email}} 验证
|
||||
{{if .ok}}成功{{else}}失败{{end}}
|
||||
{{if .ok}}
|
||||
<br />
|
||||
您的新登录邮箱为 {{.email}}
|
||||
{{end}}
|
||||
|
||||
{{if .msg}}<br />{{.msg}}{{end}}
|
||||
|
||||
<br />
|
||||
<a href="/note">回到我的笔记</a>
|
||||
</form>
|
||||
<h1 id="logo">leanote</h1>
|
||||
<div id="boxForm">
|
||||
<div id="boxHeader">验证邮箱 - {{if .ok}}成功{{else}}失败{{end}}</div>
|
||||
<form>
|
||||
<div class="alert alert-danger" id="loginMsg"> </div>
|
||||
您的邮箱 {{.email}} 验证
|
||||
{{if .ok}}成功{{else}}失败{{end}}
|
||||
{{if .ok}}
|
||||
<br />
|
||||
您的新登录邮箱为 {{.email}}
|
||||
{{end}}
|
||||
|
||||
{{if .msg}}<br />{{.msg}}{{end}}
|
||||
|
||||
<br />
|
||||
<a href="/note">回到我的笔记</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="boxFooter">
|
||||
<p>
|
||||
<a href="/index">{{msg . "home"}}</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/index">leanote</a> © 2014
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,24 +1,24 @@
|
||||
# blog
|
||||
|
||||
blog=Blog
|
||||
aboutMe=About me
|
||||
blogSet=Set blog
|
||||
|
||||
blogNavs=Navs
|
||||
quickLinks=Quick links
|
||||
latestPosts=Latest posts
|
||||
|
||||
noBlog=No blog
|
||||
noTag=No tag
|
||||
blogClass=Classification
|
||||
blogClass=Category
|
||||
updatedTime=Updated at
|
||||
createdTime=Created at
|
||||
fullBlog=Full blog
|
||||
blogNav=Blog nav
|
||||
more=More...
|
||||
previous=Previous
|
||||
next=Next
|
||||
|
||||
#
|
||||
# set blog
|
||||
#
|
||||
blogSet=Blog configuration
|
||||
baseInfoSet=Base info
|
||||
commentSet=Comment
|
||||
themeSet=Theme
|
||||
@@ -26,16 +26,66 @@ theme=Theme
|
||||
blogName=Title
|
||||
blogLogo=Logo
|
||||
blogDesc=Description
|
||||
aboutMe=About Me
|
||||
|
||||
#domain
|
||||
domainSet=Domain
|
||||
subDomain=Sub domain
|
||||
domain=Custom domain
|
||||
|
||||
# theme
|
||||
elegant=Elegant
|
||||
navFixed=Nav fixed at left side
|
||||
|
||||
openComment=Open comment?
|
||||
commentSys=leanote use <a href="http://disqus.com" target="_blank">Disqus</a> as comment system
|
||||
disqusHelp=Please input your Disqus Id or use "leanote"
|
||||
chooseComment=Comment System
|
||||
disqusHelp=Please input your Disqus Id
|
||||
needHelp=Need help?
|
||||
blogLogoTips=Upload image to replace blog title
|
||||
saveSuccess=Save success
|
||||
|
||||
community=Community
|
||||
home=Home
|
||||
none=None
|
||||
moreShare=More
|
||||
sinaWeibo=Weibo
|
||||
weixin=Weichat
|
||||
tencentWeibo=Tencent Weibo
|
||||
qqZone=QQ Zone
|
||||
renren=Renren
|
||||
report=Report
|
||||
like=Like
|
||||
unlike=Unlike
|
||||
viewers=Viewers
|
||||
author=Author
|
||||
delete=Delete
|
||||
reply=Reply
|
||||
comment=Comment
|
||||
comments=Comments
|
||||
cancel=Cancel
|
||||
confirm=Confirm
|
||||
signIn=Sign In
|
||||
signUp=Sign Up
|
||||
submitComment=Submit
|
||||
reportReason1=不友善内容
|
||||
reportReason2=广告等垃圾信息
|
||||
reportReason3=违法违规内容
|
||||
reportReason4=不宜公开讨论的政治内容
|
||||
other=Other
|
||||
reportReason=Reason
|
||||
chooseReason=请选择举报理由
|
||||
reportSuccess=举报成功, 我们处理后会通知作者, 感谢您的监督
|
||||
error=Error
|
||||
reportComment?=举报该评论?
|
||||
reportBlog?=举报该博客?
|
||||
confirmDeleteComment=Are you sure?
|
||||
scanQRCode=Open weichat and scan the QR code
|
||||
justNow=Just now
|
||||
minutesAgo=minutes ago
|
||||
hoursAgo=hours ago
|
||||
daysAgo=days ago
|
||||
weeksAgo=weeks ago
|
||||
monthsAgo=months ago
|
||||
|
||||
|
||||
a=a
|
||||
@@ -14,10 +14,14 @@ updatedTime=更新
|
||||
createdTime=创建
|
||||
fullBlog=全文
|
||||
blogNav=导航
|
||||
more=更多...
|
||||
previous=上一页
|
||||
next=下一页
|
||||
|
||||
#
|
||||
# set blog
|
||||
#
|
||||
blogSet=博客设置
|
||||
baseInfoSet=基本设置
|
||||
commentSet=评论设置
|
||||
themeSet=主题设置
|
||||
@@ -25,17 +29,69 @@ theme=主题
|
||||
blogName=博客标题
|
||||
blogLogo=博客Logo
|
||||
blogDesc=博客描述
|
||||
aboutMe=关于我
|
||||
|
||||
#domain
|
||||
domainSet=域名设置
|
||||
subDomain=博客子域名
|
||||
domain=自定义域名
|
||||
|
||||
# theme
|
||||
elegant=大气
|
||||
navFixed=导航左侧固定
|
||||
|
||||
openComment=开启评论?
|
||||
commentSys=leanote 使用 <a href="http://disqus.com" target="_blank">Disqus</a> 作为评论系统
|
||||
chooseComment=选择评论系统
|
||||
disqusHelp=请填写您申请的Disqus唯一url前缀. 建议您申请Disqus帐号, 这样可以自己管理评论. 或使用leanote的默认Disqus Id.
|
||||
needHelp=需要帮助?
|
||||
blogLogoTips=上传logo将显示logo(替代博客标题)
|
||||
saveSuccess=保存成功
|
||||
|
||||
community=社区
|
||||
home=主页
|
||||
none=无
|
||||
moreShare=更多分享
|
||||
sinaWeibo=新浪微博
|
||||
weixin=微信
|
||||
tencentWeibo=腾讯微博
|
||||
qqZone=QQ空间
|
||||
renren=人人网
|
||||
report=举报
|
||||
like=赞
|
||||
unlike=取消赞
|
||||
viewers=人读过
|
||||
author=作者
|
||||
delete=删除
|
||||
reply=回复
|
||||
comment=评论
|
||||
comments=条评论
|
||||
cancel=取消
|
||||
confirm=确认
|
||||
signIn=登录
|
||||
signUp=注册
|
||||
submitComment=发表评论
|
||||
reportReason1=不友善内容
|
||||
reportReason2=广告等垃圾信息
|
||||
reportReason3=违法违规内容
|
||||
reportReason4=不宜公开讨论的政治内容
|
||||
other=其它
|
||||
reportReason=举报理由
|
||||
chooseReason=请选择举报理由
|
||||
reportSuccess=举报成功, 我们处理后会通知作者, 感谢您的监督
|
||||
error=错误
|
||||
reportComment?=举报该评论?
|
||||
reportBlog?=举报该博客?
|
||||
confirmDeleteComment=确定删除该评论?
|
||||
scanQRCode=打开微信扫一扫二维码
|
||||
|
||||
justNow=刚刚
|
||||
minutesAgo=分钟前
|
||||
hoursAgo=个小时前
|
||||
daysAgo=天前
|
||||
weeksAgo=周前
|
||||
monthsAgo=个月前
|
||||
|
||||
|
||||
|
||||
|
||||
a=a
|
||||
@@ -1,6 +1,7 @@
|
||||
# leanote
|
||||
app=leanote
|
||||
moto=Not Just A Notebook!
|
||||
moto2=Knowledge, Sharing, Cooperation, Blog... all in leanote
|
||||
moto2=Knowledge, Blog, Sharing, Cooperation... all in leanote
|
||||
moto3=Brief But Not Simple
|
||||
fork github=Fork leanote on Github
|
||||
|
||||
@@ -27,6 +28,10 @@ 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
|
||||
captcha=Captcha
|
||||
reloadCaptcha=Reload Captcha
|
||||
captchaError=Captcha Do Not Match
|
||||
inputCaptcha=Captcha is required
|
||||
|
||||
hi=Hi
|
||||
welcomeUseLeanote=Welcome!
|
||||
@@ -53,7 +58,7 @@ hadAcount = Already have an account?
|
||||
hasAcount = Do not have an account?
|
||||
|
||||
# 注册
|
||||
registerSuccessAndRdirectToNote=register success, now redirect to my note...
|
||||
registerSuccessAndRdirectToNote=Register success, redirecting...
|
||||
|
||||
# 找回密码
|
||||
passwordTips=The length is at least 6
|
||||
@@ -75,11 +80,17 @@ save=Save
|
||||
editorTips=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
|
||||
newMarkdownNote=New Markdown Note
|
||||
noNoteNewNoteTips=The notebook is empty, why not...
|
||||
canntNewNoteTips=Sorry, cannot new note in here, please choose a notebook at first.
|
||||
new=New
|
||||
newMarkdown=New markdown note
|
||||
clickAddTag=Click to add Tag
|
||||
notebook=Notebook
|
||||
myNotebook=My notebook
|
||||
addNotebook=Add notebook
|
||||
search=Search
|
||||
clearSearch=Clear Search
|
||||
all=Newest
|
||||
trash=Trash
|
||||
delete=Delete
|
||||
@@ -113,12 +124,14 @@ green=green
|
||||
# 设置
|
||||
accountSetting=Account
|
||||
themeSetting=Theme
|
||||
setAvatar=Avatar
|
||||
logout=Logout
|
||||
basicInfo=Basic
|
||||
updateEmail=Update email
|
||||
usernameSetting=Update username
|
||||
oldPassword=Old password
|
||||
newPassword=New password
|
||||
admin=Admin
|
||||
|
||||
default=Default
|
||||
simple=Simple
|
||||
@@ -139,5 +152,87 @@ howToInstallLeanote=How to install leanote
|
||||
attachments = Attachments
|
||||
donate = Donate
|
||||
|
||||
# contextmenu
|
||||
shareToFriends=Share to friends
|
||||
publicAsBlog=Public as blog
|
||||
cancelPublic=Cancel public
|
||||
move=Move
|
||||
copy=Copy
|
||||
rename=Rename
|
||||
addChildNotebook=Add child notebook
|
||||
deleteAllShared=Delete shared user
|
||||
deleteSharedNotebook=Delete shared notebook
|
||||
copyToMyNotebook=Copy to my notebook
|
||||
|
||||
####note-dev
|
||||
emailInSending=In sending to
|
||||
checkEmail=Check email
|
||||
setUsername=Set username
|
||||
setUsernameTips=Your current email is: <code>%s</code>. You can set a unique username. <br />Username' length is at least 4 and cannot contains special characters.
|
||||
currentEmail=Your current email is: <code>%s</code>
|
||||
updateEmail=Update email
|
||||
updateEmailTips=You must verify the email after you update the email. The verified email will be your new account.
|
||||
sendVerifiedEmail=Send verification email
|
||||
verified=Verified
|
||||
unVerified=Unverfied
|
||||
verifiedNow=Verify now
|
||||
resendVerifiedEmail=Resend verification email
|
||||
|
||||
# 分享
|
||||
defaulthhare=Default
|
||||
addShare=Add Friend
|
||||
friendEmail=Friend email
|
||||
permission=Permission
|
||||
readOnly=Read only
|
||||
writable=Writable
|
||||
inputFriendEmail=Friend email is required
|
||||
clickToChangePermission=Click to change permission
|
||||
sendInviteEmailToYourFriend=Send invite email to your friend
|
||||
copySuccess=Copy success
|
||||
copyFailed=Copy failed
|
||||
friendNotExits=Your friend hasn't %s's account, invite register link: %s
|
||||
emailBodyRequired=Email body is required
|
||||
clickToCopy=Click to copy
|
||||
sendSuccess=success
|
||||
inviteEmailBody=Hi,I am %s, %s is awesome, come on!
|
||||
|
||||
# 历史记录
|
||||
historiesNum=We have saved at most <b>10</b> latest histories with each note
|
||||
noHistories=No histories
|
||||
fold=Fold
|
||||
unfold=Unfold
|
||||
datetime=Datetime
|
||||
restoreFromThisVersion=Restore from this version
|
||||
confirmBackup=Are you sure to restore from this version? We will backup the current note.
|
||||
createAccount=Create account
|
||||
createAccountSuccess=Account create success
|
||||
createAccountFailed=Account create failed
|
||||
thirdCreateAcountTips=You are using the 3th account to login %(app)s, you can create a %(app)s account too. <br />After you create %(app)s account, you can use the account and the 3th account to login %(app)s.
|
||||
|
||||
## valid msg
|
||||
inputUsername=input username
|
||||
updateUsernameSuccess=Update username success
|
||||
usernameIsExisted=Username is already exists
|
||||
noSpecialChars=username cannot contains special chars
|
||||
minLength=The length is at least %s
|
||||
errorEmail=Please input the right email
|
||||
verifiedEmaiHasSent=The verification email has been sent, please check your email.
|
||||
emailSendFailed=Email send failed
|
||||
inputPassword=Password is required
|
||||
inputNewPassword=The new password is required
|
||||
inputPassword2=Please input the new password again
|
||||
errorPassword=The passowd's length is at least 6 and be sure as complex as possible
|
||||
confirmPassword=Password not matched
|
||||
updatePasswordSuccess=Update password success
|
||||
errorDomain=The custom domain is invalid, eg. www.myblog.com
|
||||
domainExisted=Custom domain is already existed
|
||||
errorSubDomain=Please input the valid sub domain, the length is at least 4 and no special chars
|
||||
subDomainExisted=Sub domain is already existed
|
||||
|
||||
# lea++
|
||||
leaDesc=leanote blog platform
|
||||
recommend=Recommend
|
||||
latest=Latest
|
||||
|
||||
# error
|
||||
notFound=This page cann't found.
|
||||
|
||||
118
messages/msg.zh
118
messages/msg.zh
@@ -1,4 +1,5 @@
|
||||
# leanote
|
||||
app=leanote
|
||||
moto=不只是笔记!
|
||||
moto2=知识管理, 博客, 分享, 协作... 尽在leanote
|
||||
moto3=简约而不简单
|
||||
@@ -27,6 +28,30 @@ blogInfo=将笔记公开, 让知识传播的更远!
|
||||
suggestionsInfo=帮助我们完善leanote
|
||||
yourContact=您的联系方式
|
||||
emailOrOthers=Email或其它联系方式
|
||||
captcha=验证码
|
||||
reloadCaptcha=刷新验证码
|
||||
captchaError=验证码错误
|
||||
inputCaptcha=请输入验证码
|
||||
|
||||
hi=Hi
|
||||
welcomeUseLeanote=Welcome!
|
||||
myNote=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
|
||||
|
||||
hi=Hi
|
||||
welcomeUseLeanote=欢迎使用leanote
|
||||
@@ -53,7 +78,7 @@ hadAcount = 已有帐户?
|
||||
hasAcount = 还无帐户?
|
||||
|
||||
# 注册
|
||||
registerSuccessAndRdirectToNote=注册成功, 正在转至我的笔记...
|
||||
registerSuccessAndRdirectToNote=注册成功, 正在跳转...
|
||||
|
||||
# 找回密码
|
||||
passwordTips=密码至少6位
|
||||
@@ -76,11 +101,17 @@ 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=新建笔记
|
||||
newMarkdownNote=新建Markdown笔记
|
||||
noNoteNewNoteTips=该笔记本下空空如也...何不
|
||||
canntNewNoteTips=Sorry, 这里不能添加笔记的. 你需要先选择一个笔记本.
|
||||
new=新建
|
||||
newMarkdown=新建Markdown笔记
|
||||
clickAddTag=点击添加标签
|
||||
notebook=笔记本
|
||||
myNotebook=我的笔记本
|
||||
addNotebook=添加笔记本
|
||||
search=搜索
|
||||
clearSearch=清除搜索
|
||||
all=最新
|
||||
trash=废纸篓
|
||||
delete=删除
|
||||
@@ -114,12 +145,14 @@ green=绿色
|
||||
# 设置
|
||||
accountSetting=帐户设置
|
||||
themeSetting=主题设置
|
||||
setAvatar=头像设置
|
||||
logout=退出
|
||||
basicInfo=基本信息
|
||||
updateEmail=修改Email
|
||||
usernameSetting=用户名设置
|
||||
oldPassword=旧密码
|
||||
newPassword=新密码
|
||||
admin=后台管理
|
||||
|
||||
default=默认
|
||||
simple=简约
|
||||
@@ -143,6 +176,89 @@ howToInstallLeanote=leanote安装步骤
|
||||
attachments = 附件
|
||||
donate = 捐赠
|
||||
|
||||
# contextmenu
|
||||
shareToFriends=分享给好友
|
||||
publicAsBlog=公开为博客
|
||||
cancelPublic=取消公开为博客
|
||||
move=移动
|
||||
copy=复制
|
||||
rename=重命名
|
||||
addChildNotebook=添加子笔记本
|
||||
deleteAllShared=删除所有共享
|
||||
deleteSharedNotebook=删除共享笔记本
|
||||
copyToMyNotebook=复制到我的笔记本
|
||||
|
||||
####note-dev
|
||||
emailInSending=正在发送邮件到
|
||||
checkEmail=查看邮件
|
||||
setUsername=用户名设置
|
||||
setUsernameTips=你的邮箱是 <code>%s</code>, 可以再设置一个唯一的用户名.<br />用户名至少4位, 不可含特殊字符.
|
||||
currentEmail=当前邮箱为: <code>%s</code>
|
||||
updateEmail=修改邮箱
|
||||
updateEmailTips=邮箱修改后, 验证之后才有效, 验证之后新的邮箱地址将会作为登录帐号使用.
|
||||
sendVerifiedEmail=发送验证邮箱
|
||||
sendSuccess=发送成功
|
||||
sendFailed=发送失败
|
||||
verified=已验证
|
||||
unVerified=未验证
|
||||
verifiedNow=现在去验证
|
||||
resendVerifiedEmail=重新发送验证邮件
|
||||
# 分享
|
||||
defaulthhare=默认共享
|
||||
addShare=添加分享
|
||||
friendEmail=好友邮箱
|
||||
permission=权限
|
||||
readOnly=只读
|
||||
writable=可写
|
||||
inputFriendEmail=请输入好友邮箱
|
||||
clickToChangePermission=点击改变权限
|
||||
sendInviteEmailToYourFriend=发送邀请email给Ta
|
||||
copySuccess=复制成功
|
||||
copyFailed=对不起, 复制失败, 请自行复制
|
||||
friendNotExits=该用户还没有注册%s, 复制邀请链接发送给Ta, 邀请链接: %s
|
||||
emailBodyRequired=邮件内容不能为空
|
||||
clickToCopy=点击复制
|
||||
sendSuccess=发送成功
|
||||
inviteEmailBody=Hi, 你好, 我是%s, %s非常好用, 快来注册吧!
|
||||
|
||||
# 历史记录
|
||||
historiesNum=leanote会保存笔记的最近<b>10</b>份历史记录
|
||||
noHistories=无历史记录
|
||||
fold=折叠
|
||||
unfold=展开
|
||||
datetime=日期
|
||||
restoreFromThisVersion=从该版本还原
|
||||
confirmBackup=确定要从该版还原? 还原前leanote会备份当前版本到历史记录中.
|
||||
createAccount=创建帐号
|
||||
createAccountSuccess=帐号创建成功
|
||||
createAccountFailed=帐号创建失败
|
||||
thirdCreateAcountTips=您现在使用的是第三方帐号登录%(app)s, 您也可以注册%(app)s帐号登录, 赶紧注册一个吧. <br />注册成功后仍可以使用第三方帐号登录leanote并管理您现有的笔记.
|
||||
|
||||
## valid msg
|
||||
cannotUpdateDemo=抱歉, Demo用户不允许修改
|
||||
inputUsername=请输入用户名
|
||||
updateUsernameSuccess=用户名修改成功
|
||||
usernameIsExisted=用户名已存在
|
||||
noSpecialChars=不能包含特殊字符
|
||||
minLength=长度至少为%s
|
||||
errorEmail=请输入正确的email
|
||||
verifiedEmaiHasSent=验证邮件已发送, 请及时查阅邮件并验证.
|
||||
emailSendFailed=邮件发送失败
|
||||
inputPassword=请输入密码
|
||||
inputNewPassword=请输入新密码
|
||||
inputPassword2=请输入确认密码
|
||||
errorPassword=请输入长度不少于6位的密码, 尽量复杂
|
||||
confirmPassword=两次密码输入不正确
|
||||
updatePasswordSuccess=修改密码成功
|
||||
errorDomain=请输入正确的域名, 如www.myblog.com
|
||||
domainExisted=域名已存在
|
||||
errorSubDomain=请输入正确的博客子域名, 长度至少为4, 不能包含特殊字符
|
||||
subDomainExisted=博客子域名已存在
|
||||
|
||||
# lea++
|
||||
leaDesc=leanote博客平台
|
||||
recommend=推荐
|
||||
latest=最新
|
||||
|
||||
# 必须要加这个, 奇怪
|
||||
[CN]
|
||||
2004
public/admin/config.codekit
Normal file
2004
public/admin/config.codekit
Normal file
File diff suppressed because it is too large
Load Diff
@@ -897,7 +897,6 @@ html {
|
||||
body {
|
||||
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
background-color: transparent;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.h1,
|
||||
.h2,
|
||||
|
||||
@@ -1080,7 +1080,7 @@ html {
|
||||
body {
|
||||
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
background-color: transparent;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
// -webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.h1,.h2,.h3,.h4,.h5,.h6 {
|
||||
|
||||
@@ -25,7 +25,7 @@ function openDialog(config) {
|
||||
var d = art.dialog(config);
|
||||
|
||||
if(config.url) {
|
||||
ajaxGetHtml(config.url, {}, function(ret) {
|
||||
$.get(config.url, {}, function(ret) {
|
||||
d.content(ret);
|
||||
});
|
||||
}
|
||||
@@ -258,6 +258,18 @@ function enter_submit(btnId) {
|
||||
}
|
||||
}
|
||||
|
||||
// send email dialog
|
||||
function openSendEmailDialog(emails) {
|
||||
openDialog({width: 500, url: "/adminEmail/sendEmailDialog?emails=" + emails, title: "Send Email"});
|
||||
}
|
||||
|
||||
function goNowToDatetime(goNow) {
|
||||
if(!goNow) {
|
||||
return "";
|
||||
}
|
||||
return goNow.substr(0, 10) + " " + goNow.substr(11, 8);
|
||||
}
|
||||
|
||||
!function ($) {
|
||||
$(function(){
|
||||
|
||||
|
||||
1
public/admin/js/min/admin-min.js
vendored
Normal file
1
public/admin/js/min/admin-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,3 +1,258 @@
|
||||
html, * {
|
||||
// -webkit-font-smoothing: antialiased;
|
||||
}
|
||||
#posts img {
|
||||
max-width: 100%;
|
||||
}
|
||||
#content {
|
||||
* {
|
||||
font-size: 16px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
// animation
|
||||
@-webkit-keyframes dropdown {
|
||||
0% {
|
||||
margin-top: -25px;
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
90% {
|
||||
margin-top: 2px
|
||||
}
|
||||
|
||||
100% {
|
||||
margin-top: 0;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes dropdown {
|
||||
0% {
|
||||
margin-top: -25px;
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
90% {
|
||||
margin-top: 2px
|
||||
}
|
||||
|
||||
100% {
|
||||
margin-top: 0;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@-ms-keyframes dropdown {
|
||||
0% {
|
||||
margin-top: -25px;
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
90% {
|
||||
margin-top: 2px
|
||||
}
|
||||
|
||||
100% {
|
||||
margin-top: 0;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dropdown {
|
||||
0% {
|
||||
margin-top: -25px;
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
90% {
|
||||
margin-top: 2px
|
||||
}
|
||||
|
||||
100% {
|
||||
margin-top: 0;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes pulldown {
|
||||
0% {
|
||||
top: 0;
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
90% {
|
||||
top: 90%;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 100%;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes pulldown {
|
||||
0% {
|
||||
top: 0;
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
90% {
|
||||
top: 90%;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 100%;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@-ms-keyframes pulldown {
|
||||
0% {
|
||||
top: 0;
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
90% {
|
||||
top: 90%;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 100%;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulldown {
|
||||
0% {
|
||||
top: 0;
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
90% {
|
||||
top: 90%;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 100%;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
a, .btn {
|
||||
-webkit-transition: all 0.2s ease;
|
||||
-moz-transition: all 0.2s ease;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.btn:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
ul.dropdown-menu {
|
||||
box-shadow: rgba(0, 0, 0, 0.172549) 0px 6px 12px 0px;
|
||||
&:before {
|
||||
content: "";
|
||||
width: 20px;
|
||||
height: 12px;
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
right: 20px;
|
||||
background-image: url("../../images/triangle_2x.png");
|
||||
background-size: 20px 12px;
|
||||
}
|
||||
}
|
||||
ul.dropdown-menu {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
.open ul.dropdown-menu {
|
||||
-webkit-animation: pulldown .2s;
|
||||
animation: pulldown .2s;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
.created-time .fa {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#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;
|
||||
-webkit-overflow-scrolling: touch !important; // for iphone
|
||||
}
|
||||
#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;
|
||||
}
|
||||
.mobile-created-time {
|
||||
display: none;
|
||||
}
|
||||
#footer {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.navbar-brand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// 主题列表
|
||||
#themeList {
|
||||
label {
|
||||
text-align: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.preview {
|
||||
display: block;
|
||||
width: 400px;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user