Compare commits

...
15 Commits
Author SHA1 Message Date
life c26d25deab api init 2014-10-25 01:09:37 +08:00
life fe910fd91d update readme 2014-10-24 10:01:47 +08:00
life 1c6c645c9f v1.0 readme 2014-10-23 11:52:51 +08:00
life 04838cc996 v1.0 readme 2014-10-23 11:49:25 +08:00
life 86ecc55f41 v1.0-beta release 2014-10-23 11:41:02 +08:00
life e4323d0cb2 leanote v1.0 release 2014-10-23 10:52:48 +08:00
life 28fbdb9ee2 open register init data 2014-10-23 10:47:04 +08:00
life b0b304d5dd admin username & jsonp fixed 2014-10-23 10:45:03 +08:00
life 536c5de56a v1.0 routes 2014-10-23 10:10:12 +08:00
life 1f51f6cc9b v1.0 fixed 2014-10-23 10:03:19 +08:00
life 0ea4843cac v1.0 2014-10-22 22:30:49 +08:00
life ebcce0a247 v1.0 2014-10-22 22:24:46 +08:00
life b43c68ec2d Merge branch 'develop-v1.0' 2014-10-22 19:19:40 +08:00
life 762f6b554d conf-default 2014-10-22 17:04:37 +08:00
life 593d2c2965 v1.0 beta init 2014-10-22 16:20:45 +08:00
283 changed files with 27490 additions and 3569 deletions

No files matched your search

+18 -43
View File
@@ -22,10 +22,10 @@ To be honest, our inspiration comes from Evernote. We use Evernote to manage our
### 3.1. Download leanote ### 3.1. Download leanote
Leanote V0.4 has been released. Binaries: Leanote V1.0-beta has been released. Binaries:
* Linux: [leanote-linux-v0.4.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz) * Linux: [leanote-linux-x86_64.v1.0-beta.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-linux-x86_64.v1.0-beta.bin.tar.gz)
* MacOS X: [leanote-mac-v0.4.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz) * MacOS X: [leanote-mac-x86_64.v1.0-beta.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-mac-x86_64.v1.0-beta.bin.tar.gz)
### 3.2. Install MongoDB ### 3.2. Install MongoDB
@@ -41,12 +41,11 @@ The mongodb data is in `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data $> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
``` ```
The initial database contains three users: The initial database contains two users:
``` ```
user1 username: leanote, password: abc123 user2 username: admin, password: abc123 (administrator)
user2 username: admin, password: abc123 user3 username: demo, password: demo@leanote.com (this user is for demo)
user3 username: demo@leanote.com, password: demo@leanote.com (this user is for demo)
``` ```
### 3.4. Configuration ### 3.4. Configuration
@@ -63,21 +62,10 @@ db.username=
db.password= db.password=
``` ```
``http.port`` ``app.secret`` **required** **important**
The secret key used for cryptographic operations (revel.Sign).
Default is 80 FOR SECURITY, YOU MUST CHANGE IT!!
``site.url``
Default is `http://localhost`, you must edit this when hosting leanote anywhere else. This is used when uploading images.
``email``
For password recovery mails
``adminUsername``
Default is `admin`. The landing page is the admin user's blog.
For more infomation please see `app/app.conf` and the [revel manuals](https://revel.github.io/) For more infomation please see `app/app.conf` and the [revel manuals](https://revel.github.io/)
@@ -104,6 +92,7 @@ Please fork this repository and contribute back using [pull requests](https://gi
## Discussion ## Discussion
* [leanote bbs](http://bbs.leanote.com) * [leanote bbs](http://bbs.leanote.com)
* [leanote google group](https://groups.google.com/forum/#!forum/leanote) * [leanote google group](https://groups.google.com/forum/#!forum/leanote)
* QQ Group: 158716820
----------------------------------------------------------------------- -----------------------------------------------------------------------
@@ -134,10 +123,10 @@ leanote是一款私有云笔记, 你可以下载它安装在自己的服务器
### 3.1. 下载leanote ### 3.1. 下载leanote
Leanote V0.4 已发布, 二进制文件(暂时没有windows版的): Leanote V1.0-beta 已发布, 二进制文件(暂时没有windows版的):
* Linux: [leanote-linux-v0.4.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz) * Linux: [leanote-linux-x86_64.v1.0-beta.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-linux-x86_64.v1.0-beta.bin.tar.gz)
* MacOS X: [leanote-mac-v0.4.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz) * MacOS X: [leanote-mac-x86_64.v1.0-beta.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-mac-x86_64.v1.0-beta.bin.tar.gz)
### 3.2. 安装 MongodbDB ### 3.2. 安装 MongodbDB
@@ -153,11 +142,10 @@ MongodbDB初始数据在 `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data $> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
``` ```
初始数据包含三个用户: 初始数据包含两个用户:
``` ```
user1 username: leanote, password: abc123 user2 username: admin, password: abc123 (管理员, 重要!)
user2 username: admin, password: abc123
user3 username: demo@leanote.com, password: demo@leanote.com (为体验使用) user3 username: demo@leanote.com, password: demo@leanote.com (为体验使用)
``` ```
@@ -175,21 +163,8 @@ db.username=
db.password= db.password=
``` ```
``http.port`` ``app.secret`` **重要**
请随意修改一个, app的密钥, 不能使用默认的, 不然会有安全问题
默认为 80
``site.url``
默认是 `http://localhost`, 这会在上传图片后的图片路径中用户, 还有发邮件, 找回密码验证邮箱时用到.
``email``
找回密码和验证邮箱时使用
``adminUsername``
默认是 `admin`. 首页即为该用户的博客
更多配置请查看 `app/app.conf` 和 [revel 手册](https://revel.github.io/) 更多配置请查看 `app/app.conf` 和 [revel 手册](https://revel.github.io/)
@@ -215,5 +190,5 @@ leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善lea
## 讨论 ## 讨论
* [leanote 社区](http://bbs.leanote.com) * [leanote 社区](http://bbs.leanote.com)
* QQ群158716820 * QQ群: 158716820
* [leanote google group](https://groups.google.com/forum/#!forum/leanote) * [leanote google group](https://groups.google.com/forum/#!forum/leanote)
+62 -32
View File
@@ -4,6 +4,7 @@ import (
"github.com/revel/revel" "github.com/revel/revel"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
// "strconv"
) )
// 用户登录/注销/找回密码 // 用户登录/注销/找回密码
@@ -14,11 +15,19 @@ 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["title"] = c.Message("login")
c.RenderArgs["subTitle"] = c.Message("login") c.RenderArgs["subTitle"] = c.Message("login")
c.RenderArgs["email"] = email c.RenderArgs["email"] = email
c.RenderArgs["openRegister"] = openRegister c.RenderArgs["from"] = from
c.RenderArgs["openRegister"] = configService.IsOpenRegister()
sessionId := c.Session.Id()
if sessionService.LoginTimesIsOver(sessionId) {
c.RenderArgs["needCaptcha"] = true
}
c.SetLocale()
if c.Has("demo") { if c.Has("demo") {
c.RenderArgs["demo"] = true c.RenderArgs["demo"] = true
@@ -26,63 +35,85 @@ func (c Auth) Login(email string) revel.Result {
} }
return c.RenderTemplate("home/login.html") 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) userInfo := authService.Login(email, pwd)
if userInfo.Email != "" { if userInfo.Email != "" {
c.SetSession(userInfo) c.SetSession(userInfo)
// 必须要redirect, 不然用户刷新会重复提交登录信息 sessionService.ClearLoginTimes(sessionId)
// return c.Redirect("/")
configService.InitUserConfigs(userInfo.UserId.Hex())
return c.RenderJson(info.Re{Ok: true}) 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 { func (c Auth) Logout() revel.Result {
sessionId := c.Session.Id()
sessionService.Clear(sessionId)
c.ClearSession() c.ClearSession()
return c.Redirect("/login") return c.Redirect("/login")
} }
// 体验一下 // 体验一下
func (c Auth) Demo() revel.Result { 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") return c.Redirect("/note")
} }
//-------- //--------
// 注册 // 注册
func (c Auth) Register() revel.Result { func (c Auth) Register(from string) revel.Result {
if !openRegister { if !configService.IsOpenRegister() {
return c.Redirect("/index") return c.Redirect("/index")
} }
c.SetLocale()
c.RenderArgs["from"] = from
c.RenderArgs["title"] = c.Message("register") c.RenderArgs["title"] = c.Message("register")
c.RenderArgs["subTitle"] = c.Message("register") c.RenderArgs["subTitle"] = c.Message("register")
return c.RenderTemplate("home/register.html") return c.RenderTemplate("home/register.html")
} }
func (c Auth) DoRegister(email, pwd string) revel.Result { func (c Auth) DoRegister(email, pwd string) revel.Result {
if !openRegister { if !configService.IsOpenRegister() {
return c.Redirect("/index") return c.Redirect("/index")
} }
re := info.NewRe(); re := info.NewRe();
if email == "" { if re.Ok, re.Msg = Vd("email", email); !re.Ok {
re.Msg = c.Message("inputEmail") return c.RenderRe(re);
return c.RenderJson(re)
} else if !IsEmail(email) {
re.Msg = c.Message("wrongEmail")
return c.RenderJson(re)
} }
if re.Ok, re.Msg = Vd("password", pwd); !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)
} }
// 注册 // 注册
@@ -90,10 +121,10 @@ func (c Auth) DoRegister(email, pwd string) revel.Result {
// 注册成功, 则立即登录之 // 注册成功, 则立即登录之
if re.Ok { if re.Ok {
c.DoLogin(email, pwd) c.doLogin(email, pwd)
} }
return c.RenderJson(re) return c.RenderRe(re)
} }
//-------- //--------
@@ -131,12 +162,11 @@ func (c Auth) FindPassword2(token string) revel.Result {
func (c Auth) FindPasswordUpdate(token, pwd string) revel.Result { func (c Auth) FindPasswordUpdate(token, pwd string) revel.Result {
re := info.NewRe(); re := info.NewRe();
re.Ok, re.Msg = IsGoodPwd(pwd) if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
if !re.Ok { return c.RenderRe(re);
return c.RenderJson(re)
} }
// 修改之 // 修改之
re.Ok, re.Msg = pwdService.UpdatePwd(token, pwd) re.Ok, re.Msg = pwdService.UpdatePwd(token, pwd)
return c.RenderJson(re) return c.RenderRe(re)
} }
+67
View File
@@ -5,11 +5,13 @@ import (
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"encoding/json" "encoding/json"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea"
// "io/ioutil" // "io/ioutil"
// "fmt" // "fmt"
"math" "math"
"strconv" "strconv"
"strings" "strings"
"bytes"
) )
// 公用Controller, 其它Controller继承它 // 公用Controller, 其它Controller继承它
@@ -54,15 +56,21 @@ func (c BaseController) GetUsername() string {
// 得到用户信息 // 得到用户信息
func (c BaseController) GetUserInfo() info.User { func (c BaseController) GetUserInfo() info.User {
if userId, ok := c.Session["UserId"]; ok && userId != "" { if userId, ok := c.Session["UserId"]; ok && userId != "" {
return userService.GetUserInfo(userId);
/*
notebookWidth, _ := strconv.Atoi(c.Session["NotebookWidth"]) notebookWidth, _ := strconv.Atoi(c.Session["NotebookWidth"])
noteListWidth, _ := strconv.Atoi(c.Session["NoteListWidth"]) noteListWidth, _ := strconv.Atoi(c.Session["NoteListWidth"])
mdEditorWidth, _ := strconv.Atoi(c.Session["MdEditorWidth"])
LogJ(c.Session)
user := info.User{UserId: bson.ObjectIdHex(userId), user := info.User{UserId: bson.ObjectIdHex(userId),
Email: c.Session["Email"], Email: c.Session["Email"],
Logo: c.Session["Logo"],
Username: c.Session["Username"], Username: c.Session["Username"],
UsernameRaw: c.Session["UsernameRaw"], UsernameRaw: c.Session["UsernameRaw"],
Theme: c.Session["Theme"], Theme: c.Session["Theme"],
NotebookWidth: notebookWidth, NotebookWidth: notebookWidth,
NoteListWidth: noteListWidth, NoteListWidth: noteListWidth,
MdEditorWidth: mdEditorWidth,
} }
if c.Session["Verified"] == "1" { if c.Session["Verified"] == "1" {
user.Verified = true user.Verified = true
@@ -71,10 +79,19 @@ func (c BaseController) GetUserInfo() info.User {
user.LeftIsMin = true user.LeftIsMin = true
} }
return user return user
*/
} }
return info.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) { func (c BaseController) SetSession(userInfo info.User) {
if userInfo.UserId.Hex() != "" { if userInfo.UserId.Hex() != "" {
c.Session["UserId"] = 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["Username"] = userInfo.Username
c.Session["UsernameRaw"] = userInfo.UsernameRaw c.Session["UsernameRaw"] = userInfo.UsernameRaw
c.Session["Theme"] = userInfo.Theme c.Session["Theme"] = userInfo.Theme
c.Session["Logo"] = userInfo.Logo
c.Session["NotebookWidth"] = strconv.Itoa(userInfo.NotebookWidth) c.Session["NotebookWidth"] = strconv.Itoa(userInfo.NotebookWidth)
c.Session["NoteListWidth"] = strconv.Itoa(userInfo.NoteListWidth) c.Session["NoteListWidth"] = strconv.Itoa(userInfo.NoteListWidth)
@@ -165,6 +183,12 @@ func (c BaseController) SetLocale() string {
lang = "en"; lang = "en";
} }
c.RenderArgs["locale"] = lang; 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 return lang
} }
@@ -173,3 +197,46 @@ func (c BaseController) SetUserInfo() {
userInfo := c.GetUserInfo() userInfo := c.GetUserInfo()
c.RenderArgs["userInfo"] = userInfo 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)
}
+373 -105
View File
@@ -1,6 +1,8 @@
package controllers package controllers
import ( import (
"strings"
"time"
"github.com/revel/revel" "github.com/revel/revel"
// "encoding/json" // "encoding/json"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
@@ -17,142 +19,283 @@ type Blog struct {
BaseController 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 blogPageSize = 5
var searchBlogPageSize = 30 var searchBlogPageSize = 30
func (c Blog) Index(userId string, notebookId string) revel.Result {
// 用户id为空, 转至博客平台
if userId == "" {
userId = leanoteUserId;
}
// userId可能是 username, email // 分类 /cate/xxxxxxxx?notebookId=1212
userInfo := userService.GetUserInfoByAny(userId) func (c Blog) Cate(notebookId string) revel.Result {
if userInfo.UserId == "" { if notebookId == "" {
return c.E404() return c.E404()
} }
// 自定义域名
userId = userInfo.UserId.Hex() hasDomain, userBlog := c.domain()
c.isMe(userId) userId := ""
if hasDomain {
c.RenderArgs["userInfo"] = userInfo userId = userBlog.UserId.Hex()
}
// 得到博客设置信息
userBlog := blogService.GetUserBlog(userId)
c.RenderArgs["userBlog"] = userBlog
var notebook info.Notebook var notebook info.Notebook
if notebookId != "" { notebook = notebookService.GetNotebookById(notebookId)
notebook = notebookService.GetNotebook(notebookId, userId)
if !notebook.IsBlog { if !notebook.IsBlog {
return c.E404() return c.E404()
} }
if userId != "" && userId != notebook.UserId.Hex() {
c.RenderArgs["title"] = userBlog.Title + " - 分类: " + notebook.Title return c.E404()
} else {
c.RenderArgs["title"] = userBlog.Title
} }
userId = notebook.UserId.Hex()
if !c.blogCommon(userId, userBlog, info.User{}) {
return c.E404()
}
// 分页的话, 需要分页信息, totalPage, curPage // 分页的话, 需要分页信息, totalPage, curPage
page := c.GetPage() 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["blogs"] = blogs
c.RenderArgs["page"] = page c.RenderArgs["page"] = page
c.RenderArgs["pageSize"] = blogPageSize c.RenderArgs["pageSize"] = blogPageSize
c.RenderArgs["count"] = count c.RenderArgs["count"] = count
// 当前notebook return c.RenderTemplate("blog/index.html")
c.RenderArgs["notebookId"] = notebookId }
c.RenderArgs["notebook"] = notebook
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
// 显示分类的最近博客, json
func (c Blog) ListCateLatest(notebookId, callback string) revel.Result {
if notebookId == "" { if notebookId == "" {
c.RenderArgs["index"] = true return c.E404()
}
// 自定义域名
hasDomain, userBlog := c.domain()
userId := ""
if hasDomain {
userId = userBlog.UserId.Hex()
} }
c.getRecentBlogs(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()
}
// 分页的话, 需要分页信息, totalPage, curPage
page := 1
_, blogs := blogService.ListBlogs(userId, notebookId, page, 5, "PublicTime", false)
re := info.NewRe()
re.Ok = true
re.List = blogs
return c.RenderJsonP(callback, 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
c.RenderArgs["index"] = true
c.RenderArgs["notebookId"] = ""
c.RenderArgs["title"] = userBlog.Title
return c.RenderTemplate("blog/index.html") return c.RenderTemplate("blog/index.html")
} }
// 详情 // 详情
func (c Blog) View(noteId string) revel.Result { func (c Blog) View(noteId string) revel.Result {
// 自定义域名
hasDomain, userBlog := c.domain()
userId := ""
if hasDomain {
userId = userBlog.UserId.Hex()
}
blog := blogService.GetBlog(noteId) blog := blogService.GetBlog(noteId)
c.RenderArgs["blog"] = blog
userInfo := userService.GetUserInfo(blog.UserId.Hex()) 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["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() // 得到访问者id
c.isMe(userId) visitUserId := c.GetUserId()
if(visitUserId != "") {
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId) visitUserInfo := userService.GetUserInfo(visitUserId)
c.RenderArgs["visitUserInfoJson"] = c.Json(visitUserInfo)
// 得到博客设置信息 c.RenderArgs["visitUserInfo"] = visitUserInfo
c.RenderArgs["userBlog"] = blogService.GetUserBlog(userId) } else {
c.RenderArgs["visitUserInfoJson"] = "{}";
c.getRecentBlogs(userId) }
return c.RenderTemplate("blog/view.html") return c.RenderTemplate("blog/view.html")
} }
// 搜索 // 搜索
func (c Blog) SearchBlog(userId, key string) revel.Result { func (c Blog) Search(userIdOrEmail, key string) revel.Result {
c.RenderArgs["title"] = "搜索 " + key // 自定义域名
hasDomain, userBlog := c.domain()
userId := ""
if hasDomain {
userId = userBlog.UserId.Hex()
}
c.RenderArgs["title"] = c.Message("search") + " - " + key
c.RenderArgs["key"] = 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 c.RenderArgs["userInfo"] = userInfo
userId = userInfo.UserId.Hex() userId = userInfo.UserId.Hex()
c.blogCommon(userId, userBlog, userInfo)
page := c.GetPage() 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["blogs"] = blogs
c.RenderArgs["key"] = key 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") return c.RenderTemplate("blog/search.html")
} }
@@ -162,17 +305,15 @@ func (c Blog) Set() revel.Result {
userInfo := userService.GetUserInfo(userId) userInfo := userService.GetUserInfo(userId)
c.RenderArgs["userInfo"] = userInfo c.RenderArgs["userInfo"] = userInfo
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
// 得到博客设置信息 // 得到博客设置信息
c.RenderArgs["userBlog"] = blogService.GetUserBlog(userId) c.RenderArgs["title"] = c.Message("blogSet")
c.RenderArgs["title"] = "博客设置"
c.RenderArgs["isMe"] = true c.RenderArgs["isMe"] = true
c.RenderArgs["set"] = 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") return c.RenderTemplate("blog/set.html")
} }
@@ -194,37 +335,38 @@ func (c Blog) SetUserBlogStyle(userBlog info.UserBlogStyle) revel.Result {
} }
// userId可能是其它的 // userId可能是其它的
func (c Blog) AboutMe(userId string) revel.Result { func (c Blog) AboutMe(userIdOrEmail string) revel.Result {
userInfo := userService.GetUserInfoByAny(userId) // 自定义域名
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 == "" { if userInfo.UserId == "" {
return c.E404() return c.E404()
} }
userId = userInfo.UserId.Hex() userId = userInfo.UserId.Hex()
c.RenderArgs["userInfo"] = userInfo c.RenderArgs["userInfo"] = userInfo
c.RenderArgs["notebooks"] = blogService.ListBlogNotebooks(userId)
c.RenderArgs["userBlog"] = blogService.GetUserBlog(userId)
c.RenderArgs["aboutMe"] = true c.RenderArgs["aboutMe"] = true
c.RenderArgs["title"] = "关于我" c.RenderArgs["title"] = c.Message("aboutMe")
c.blogCommon(userId, userBlog, info.User{})
c.isMe(userId)
c.getRecentBlogs(userId)
return c.RenderTemplate("blog/about_me.html") return c.RenderTemplate("blog/about_me.html")
} }
// 当前的博客是否是我的
func (c Blog) isMe(userId string) {
c.RenderArgs["isMe"] = userId == c.GetUserId()
}
// 优化, 这里不要得到count // 优化, 这里不要得到count
func (c Blog) getRecentBlogs(userId string) { 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了 // 可以不要, 因为注册的时候已经把username设为email了
@@ -234,3 +376,129 @@ func (c Blog) setRenderUserInfo(userInfo info.User) {
} }
c.RenderArgs["userInfo"] = userInfo 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
View 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()
}
+52 -1
View File
@@ -5,6 +5,7 @@ import (
// "encoding/json" // "encoding/json"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/lea/netutil"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -22,7 +23,7 @@ type File struct {
func (c File) UploadBlogLogo() revel.Result { func (c File) UploadBlogLogo() revel.Result {
re := c.uploadImage("logo", ""); re := c.uploadImage("logo", "");
c.RenderArgs["fileUrlPath"] = siteUrl + "/" + re.Id c.RenderArgs["fileUrlPath"] = re.Id
c.RenderArgs["resultCode"] = re.Code c.RenderArgs["resultCode"] = re.Code
c.RenderArgs["resultMsg"] = re.Msg c.RenderArgs["resultMsg"] = re.Msg
@@ -53,6 +54,24 @@ func (c File) PasteImage(noteId string) revel.Result {
return c.RenderJson(re) 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 // leaui image plugin upload image
func (c File) UploadImageLeaui(albumId string) revel.Result { func (c File) UploadImageLeaui(albumId string) revel.Result {
re := c.uploadImage("", albumId); re := c.uploadImage("", albumId);
@@ -243,6 +262,38 @@ func (c File) CopyImage(userId, fileId, toUserId string) revel.Result {
return c.RenderJson(re) 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 { func (c File) UploadImage(renderHtml string) revel.Result {
+3 -3
View File
@@ -3,7 +3,7 @@ package controllers
import ( import (
"github.com/revel/revel" "github.com/revel/revel"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea" // . "github.com/leanote/leanote/app/lea"
) )
// 首页 // 首页
@@ -16,7 +16,7 @@ type Index struct {
func (c Index) Index() revel.Result { func (c Index) Index() revel.Result {
c.SetUserInfo() c.SetUserInfo()
c.RenderArgs["title"] = "leanote" c.RenderArgs["title"] = "leanote"
c.RenderArgs["openRegister"] = openRegister c.RenderArgs["openRegister"] = configService.GlobalStringConfigs["openRegister"]
lang := c.SetLocale() lang := c.SetLocale()
return c.RenderTemplate("home/index_" + lang + ".html"); return c.RenderTemplate("home/index_" + lang + ".html");
@@ -29,7 +29,7 @@ func (c Index) Suggestion(addr, suggestion string) revel.Result {
// 发给我 // 发给我
go func() { go func() {
SendToLeanote("建议", "建议", "UserId: " + c.GetUserId() + " <br /> Suggestions: " + suggestion) emailService.SendEmail("leanote@leanote.com", "建议", "UserId: " + c.GetUserId() + " <br /> Suggestions: " + suggestion)
}(); }();
return c.RenderJson(re) return c.RenderJson(re)
+111 -9
View File
@@ -5,12 +5,13 @@ import (
// "encoding/json" // "encoding/json"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/lea/html2image"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
// "os" "os/exec"
// "github.com/leanote/leanote/app/types" // "github.com/leanote/leanote/app/types"
// "io/ioutil" // "io/ioutil"
// "fmt" // "fmt"
// "bytes"
// "os"
) )
type Note struct { type Note struct {
@@ -32,7 +33,7 @@ func (c Note) Index() revel.Result {
return c.Redirect("/login") return c.Redirect("/login")
} }
c.RenderArgs["openRegister"] = openRegister c.RenderArgs["openRegister"] = configService.IsOpenRegister()
// 已登录了, 那么得到所有信息 // 已登录了, 那么得到所有信息
notebooks := notebookService.GetNotebooks(userId) notebooks := notebookService.GetNotebooks(userId)
@@ -52,6 +53,7 @@ func (c Note) Index() revel.Result {
// 当然, 还需要得到第一个notes的content // 当然, 还需要得到第一个notes的content
//... //...
c.RenderArgs["isAdmin"] = leanoteUserId == userInfo.Username
c.RenderArgs["userInfo"] = userInfo c.RenderArgs["userInfo"] = userInfo
c.RenderArgs["userInfoJson"] = c.Json(userInfo) c.RenderArgs["userInfoJson"] = c.Json(userInfo)
c.RenderArgs["notebooks"] = c.Json(notebooks) c.RenderArgs["notebooks"] = c.Json(notebooks)
@@ -221,31 +223,131 @@ func (c Note) SearchNoteByTags(tags []string) revel.Result {
return c.RenderJson(blogs) return c.RenderJson(blogs)
} }
//----------------- //------------------
// html2image // 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 { func (c Note) Html2Image(noteId string) revel.Result {
re := info.NewRe() re := info.NewRe()
userId := c.GetUserId() userId := c.GetUserId()
note := noteService.GetNote(noteId, userId) note := noteService.GetNoteById(noteId)
if note.NoteId == "" { if note.NoteId == "" {
re.Msg = "No Note"
return c.RenderJson(re) 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 判断是否需要重新生成之 // path 判断是否需要重新生成之
fileUrlPath := "/upload/" + userId + "/images/weibo" fileUrlPath := "/upload/" + noteUserId + "/images/weibo"
dir := revel.BasePath + "/public/" + fileUrlPath dir := revel.BasePath + "/public/" + fileUrlPath
if !ClearDir(dir) { if !ClearDir(dir) {
re.Msg = "No Dir"
return c.RenderJson(re) return c.RenderJson(re)
} }
filename := note.NoteId.Hex() + ".png"; filename := note.NoteId.Hex() + ".png";
path := dir + "/" + filename path := dir + "/" + filename
// 生成之 // cookie
html2image.ToImage(userId, c.GetUsername(), noteId, note.Title, content.Content, path) 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:]
}
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.Ok = true
re.Id = fileUrlPath + "/" + filename re.Id = fileUrlPath + "/" + filename
} else {
re.Msg = string(b)
Log("error:......")
Log(string(b))
}
return c.RenderJson(re) 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))
}
*/
} }
+47 -48
View File
@@ -8,7 +8,7 @@ import (
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
// "github.com/leanote/leanote/app/types" // "github.com/leanote/leanote/app/types"
// "io/ioutil" // "io/ioutil"
"fmt" // "fmt"
// "math" // "math"
// "os" // "os"
// "path" // "path"
@@ -19,43 +19,48 @@ type User struct {
BaseController 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 // 修改用户名, 需要重置session
func (c User) UpdateUsername(username string) revel.Result { func (c User) UpdateUsername(username string) revel.Result {
re := info.NewRe(); re := info.NewRe();
// 判断是否满足最基本的, 4位, 不含特殊字符, 大小写无关. email大小写无关 if(c.GetUsername() == "demo") {
if len(username) < 4 { re.Msg = "cannotUpdateDemo"
re.Ok = false return c.RenderRe(re);
re.Msg = "至少4位"
return c.RenderJson(re);
} }
if !IsUsername(username) {
re.Ok = false if re.Ok, re.Msg = Vd("username", username); !re.Ok {
re.Msg = "不能包含特殊字符" return c.RenderRe(re);
return c.RenderJson(re);
} }
re.Ok, re.Msg = userService.UpdateUsername(c.GetUserId(), username) re.Ok, re.Msg = userService.UpdateUsername(c.GetUserId(), username)
if(re.Ok) { if(re.Ok) {
c.UpdateSession("Username", username) c.UpdateSession("Username", username)
} }
return c.RenderJson(re); return c.RenderRe(re);
} }
// 修改密码 // 修改密码
func (c User) UpdatePwd(oldPwd, pwd string) revel.Result { func (c User) UpdatePwd(oldPwd, pwd string) revel.Result {
re := info.NewRe(); re := info.NewRe();
if oldPwd == "" { if(c.GetUsername() == "demo") {
re.Msg = "旧密码错误" re.Msg = "cannotUpdateDemo"
return c.RenderJson(re); return c.RenderRe(re);
} }
if re.Ok, re.Msg = Vd("password", oldPwd); !re.Ok {
re.Ok, re.Msg = IsGoodPwd(pwd) return c.RenderRe(re);
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 = userService.UpdatePwd(c.GetUserId(), oldPwd, pwd) 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); return c.RenderJson(re);
} }
// 发送邮件 re.Ok = emailService.SendInviteEmail(c.GetUserInfo(), toEmail, content)
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)
return c.RenderJson(re); return c.RenderJson(re);
} }
@@ -91,15 +89,23 @@ func (c User) SendRegisterEmail(content, toEmail string) revel.Result {
// 重新发送激活邮件 // 重新发送激活邮件
func (c User) ReSendActiveEmail() revel.Result { func (c User) ReSendActiveEmail() revel.Result {
re := info.NewRe() re := info.NewRe()
re.Ok = userService.RegisterSendActiveEmail(c.GetUserId(), c.GetEmail()) re.Ok = emailService.RegisterSendActiveEmail(c.GetUserInfo(), c.GetEmail())
return c.RenderJson(re) return c.RenderJson(re)
} }
// 修改Email发送激活邮箱 // 修改Email发送激活邮箱
func (c User) UpdateEmailSendActiveEmail(email string) revel.Result { func (c User) UpdateEmailSendActiveEmail(email string) revel.Result {
re := info.NewRe() re := info.NewRe()
re.Ok, re.Msg = userService.UpdateEmailSendActiveEmail(c.GetUserId(), email) if(c.GetUsername() == "demo") {
return c.RenderJson(re) 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)
} }
// 通过点击链接 // 通过点击链接
@@ -146,21 +152,11 @@ func (c User) ActiveEmail(token string) revel.Result {
func (c User) AddAccount(email, pwd string) revel.Result { func (c User) AddAccount(email, pwd string) revel.Result {
re := info.NewRe() re := info.NewRe()
if email == "" { if re.Ok, re.Msg = Vd("email", email); !re.Ok {
re.Msg = "请输入邮箱" return c.RenderRe(re);
return c.RenderJson(re)
} else if !IsEmail(email) {
re.Msg = "请输入正确的邮箱"
return c.RenderJson(re)
} }
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
// 密码 return c.RenderRe(re);
if pwd == "" {
re.Msg = "请输入密码"
return c.RenderJson(re)
} else if len(pwd) < 6 {
re.Msg = "密码长度至少6位"
return c.RenderJson(re)
} }
re.Ok, re.Msg = userService.ThirdAddUser(c.GetUserId(), email, pwd) 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); 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 := info.NewRe()
re.Ok = userService.UpdateColumnWidth(c.GetUserId(), notebookWidth, noteListWidth) re.Ok = userService.UpdateColumnWidth(c.GetUserId(), notebookWidth, noteListWidth, mdEditorWidth)
if re.Ok { if re.Ok {
c.UpdateSession("NotebookWidth", strconv.Itoa(notebookWidth)); 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) return c.RenderJson(re)
} }
@@ -49,3 +49,11 @@ func (c AdminBaseController) getSorter(sorterField string, isAsc bool, okSorter
c.RenderArgs["sorter"] = sorter c.RenderArgs["sorter"] = sorter
return sorterField, isAsc; 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)
}
}
+13
View File
@@ -17,9 +17,22 @@ func (c Admin) Index() revel.Result {
c.RenderArgs["title"] = "leanote" c.RenderArgs["title"] = "leanote"
c.SetLocale() c.SetLocale()
c.RenderArgs["countUser"] = userService.CountUser()
c.RenderArgs["countNote"] = noteService.CountNote()
c.RenderArgs["countBlog"] = noteService.CountBlog()
return c.RenderTemplate("admin/index.html"); 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 { func (c Admin) GetView(view string) revel.Result {
return c.RenderTemplate("admin/" + view); return c.RenderTemplate("admin/" + view);
} }
+114
View 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
}
@@ -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 { func (c AdminSetting) DoBlogTag(recommendTags, newTags string) revel.Result {
re := info.NewRe() re := info.NewRe()
re.Ok = configService.UpdateUserArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ",")) re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
re.Ok = configService.UpdateUserArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ",")) re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
return c.RenderJson(re) 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 // demo
// blog标签设置 // blog标签设置
func (c AdminSetting) Demo() revel.Result { func (c AdminSetting) Demo() revel.Result {
@@ -51,12 +61,59 @@ func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
return c.RenderJson(re) return c.RenderJson(re)
} }
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoUserId", userInfo.UserId.Hex()) re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUserId", userInfo.UserId.Hex())
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoUsername", demoUsername) re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUsername", demoUsername)
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoPassword", demoPassword) re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoPassword", demoPassword)
return c.RenderJson(re) 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) OpenRegister(openRegister string) revel.Result {
re := info.NewRe()
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "openRegister", openRegister)
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)
}
@@ -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;
}
+8 -3
View File
@@ -25,6 +25,8 @@ var noteImageService *service.NoteImageService
var fileService *service.FileService var fileService *service.FileService
var attachService *service.AttachService var attachService *service.AttachService
var configService *service.ConfigService var configService *service.ConfigService
var emailService *service.EmailService
var upgradeService *service.UpgradeService
var adminUsername = "admin" var adminUsername = "admin"
// 拦截器 // 拦截器
@@ -115,13 +117,16 @@ func InitService() {
suggestionService = service.SuggestionS suggestionService = service.SuggestionS
authService = service.AuthS authService = service.AuthS
configService = service.ConfigS configService = service.ConfigS
emailService = service.EmailS
upgradeService = service.UpgradeS
} }
func init() { func init() {
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Admin{}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Admin{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminSetting{}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminSetting{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminUser{}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminUser{})
revel.OnAppStart(func() { revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminBlog{})
adminUsername, _ = revel.Config.String("adminUsername") revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminEmail{})
}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminUpgrade{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminData{})
} }
+155
View File
@@ -0,0 +1,155 @@
package api
import (
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
// "strconv"
)
// 用户登录后生成一个token, 将这个token保存到session中
// 以后每次的请求必须带这个token, 并从session中获取userId
// 用户登录/注销/找回密码
type ApiAuth struct {
ApiBaseContrller
}
// 为了demo和register
func (c ApiAuth) doLogin(email, pwd string) revel.Result {
sessionId := c.Session.Id()
var msg = ""
userInfo := authService.Login(email, pwd)
if userInfo.Email != "" {
c.SetSession(userInfo)
sessionService.ClearLoginTimes(sessionId)
return c.RenderJson(info.Re{Ok: true})
} else {
// 登录错误, 则错误次数++
msg = "wrongUsernameOrPassword"
}
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId) , Msg: c.Message(msg)})
}
func (c ApiAuth) Login(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 ApiAuth) Logout() revel.Result {
sessionId := c.Session.Id()
sessionService.Clear(sessionId)
c.ClearSession()
re := info.NewRe()
re.Ok = true
return c.RenderJson(re)
}
// 体验一下
func (c ApiAuth) Demo() revel.Result {
c.doLogin(configService.GetGlobalStringConfig("demoUsername"), configService.GetGlobalStringConfig("demoPassword"))
return c.Redirect("/note")
}
//--------
// 注册
func (c ApiAuth) Register(from string) revel.Result {
if !configService.IsOpenRegister() {
return c.Redirect("/index")
}
c.SetLocale()
c.RenderArgs["from"] = from
c.RenderArgs["title"] = c.Message("register")
c.RenderArgs["subTitle"] = c.Message("register")
return c.RenderTemplate("home/register.html")
}
func (c ApiAuth) DoRegister(email, pwd string) revel.Result {
if !configService.IsOpenRegister() {
return c.Redirect("/index")
}
re := info.NewRe();
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 = authService.Register(email, pwd)
// 注册成功, 则立即登录之
if re.Ok {
c.doLogin(email, pwd)
}
return c.RenderRe(re)
}
//--------
// 找回密码
func (c ApiAuth) FindPassword() revel.Result {
c.RenderArgs["title"] = c.Message("findPassword")
c.RenderArgs["subTitle"] = c.Message("findPassword")
return c.RenderTemplate("home/find_password.html")
}
func (c ApiAuth) DoFindPassword(email string) revel.Result {
pwdService.FindPwd(email)
re := info.NewRe()
re.Ok = true
return c.RenderJson(re)
}
// 点击链接后, 先验证之
func (c ApiAuth) FindPassword2(token string) revel.Result {
c.RenderArgs["title"] = c.Message("findPassword")
c.RenderArgs["subTitle"] = c.Message("findPassword")
if token == "" {
return c.RenderTemplate("find_password2_timeout.html")
}
ok, _, findPwd := tokenService.VerifyToken(token, info.TokenPwd);
if !ok {
return c.RenderTemplate("home/find_password2_timeout.html")
}
c.RenderArgs["findPwd"] = findPwd
c.RenderArgs["title"] = c.Message("updatePassword")
c.RenderArgs["subTitle"] = c.Message("updatePassword")
return c.RenderTemplate("home/find_password2.html")
}
// 找回密码修改密码
func (c ApiAuth) FindPasswordUpdate(token, pwd string) revel.Result {
re := info.NewRe();
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
return c.RenderRe(re);
}
// 修改之
re.Ok, re.Msg = pwdService.UpdatePwd(token, pwd)
return c.RenderRe(re)
}
+19
View File
@@ -0,0 +1,19 @@
package api
import (
// "github.com/revel/revel"
// "gopkg.in/mgo.v2/bson"
// "encoding/json"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/controllers"
// "io/ioutil"
// "fmt"
// "math"
// "strconv"
// "strings"
)
// 公用Controller, 其它Controller继承它
type ApiBaseContrller struct {
controllers.BaseController // 不能用*BaseController
}
@@ -11,12 +11,13 @@ import (
// "fmt" // "fmt"
// "math" // "math"
// "os" // "os"
// "path" // "path"
// "strconv" // "strconv"
) )
type ApiUser struct { type ApiUser struct {
*revel.Controller ApiBaseContrller
} }
// 修改用户名, 需要重置session // 修改用户名, 需要重置session
+139
View File
@@ -0,0 +1,139 @@
package api
import (
"github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
"strings"
)
var userService *service.UserService
var noteService *service.NoteService
var trashService *service.TrashService
var notebookService *service.NotebookService
var noteContentHistoryService *service.NoteContentHistoryService
var authService *service.AuthService
var shareService *service.ShareService
var blogService *service.BlogService
var tagService *service.TagService
var pwdService *service.PwdService
var tokenService *service.TokenService
var suggestionService *service.SuggestionService
var albumService *service.AlbumService
var noteImageService *service.NoteImageService
var fileService *service.FileService
var attachService *service.AttachService
var configService *service.ConfigService
var emailService *service.EmailService
var sessionService *service.SessionService
var pageSize = 1000
var defaultSortField = "UpdatedTime"
var leanoteUserId = "admin" // 不能更改
var siteUrl = "http://leanote.com"
// 拦截器
// 不需要拦截的url
// Index 除了Note之外都不需要
var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": true,
"Login": true,
"DoLogin": true,
"Logout": true,
"Register": true,
"DoRegister": true,
"FindPasswword": true,
"DoFindPassword": true,
"FindPassword2": true,
"FindPasswordUpdate": true,
"Suggestion": true,
},
"Note": map[string]bool{"ToImage": true},
"Blog": map[string]bool{"Index": true,
"View": true,
"AboutMe": true,
"Cate": true,
"Search": true,
"GetLikeAndComments": true,
"IncReadNum": true,
"ListComments": true,
},
// 用户的激活与修改邮箱都不需要登录, 通过链接地址
"User": map[string]bool{"UpdateEmail": true,
"ActiveEmail":true,
},
"Oauth": map[string]bool{"GithubCallback": true},
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
"Attach": map[string]bool{"Download": true, "DownloadAll": true},
}
func needValidate(controller, method string) bool {
// 在里面
if v, ok := commonUrl[controller]; ok {
// 在commonUrl里
if _, ok2 := v[method]; ok2 {
return false
}
return true
} else {
// controller不在这里的, 肯定要验证
return true;
}
}
func AuthInterceptor(c *revel.Controller) revel.Result {
// 全部变成首字大写
var controller = strings.Title(c.Name)
var method = strings.Title(c.MethodName)
// 是否需要验证?
if !needValidate(controller, method) {
return nil
}
// 验证是否已登录
if userId, ok := c.Session["UserId"]; ok && userId != "" {
return nil // 已登录
}
// 没有登录, 判断是否是ajax操作
if c.Request.Header.Get("X-Requested-With") == "XMLHttpRequest" {
re := info.NewRe()
re.Msg = "NOTLOGIN"
return c.RenderJson(re)
}
return c.Redirect("/login")
}
// 最外层init.go调用
// 获取service, 单例
func InitService() {
notebookService = service.NotebookS
noteService = service.NoteS
noteContentHistoryService = service.NoteContentHistoryS
trashService = service.TrashS
shareService = service.ShareS
userService = service.UserS
tagService = service.TagS
blogService = service.BlogS
tokenService = service.TokenS
noteImageService = service.NoteImageS
fileService = service.FileS
albumService= service.AlbumS
attachService = service.AttachS
pwdService = service.PwdS
suggestionService = service.SuggestionS
authService = service.AuthS
configService = service.ConfigS
emailService = service.EmailS
sessionService = service.SessionS
}
func init() {
// interceptors
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiAuth{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiUser{})
revel.OnAppStart(func() {
siteUrl, _ = revel.Config.String("site.url")
})
}
+11 -5
View File
@@ -25,12 +25,13 @@ var noteImageService *service.NoteImageService
var fileService *service.FileService var fileService *service.FileService
var attachService *service.AttachService var attachService *service.AttachService
var configService *service.ConfigService var configService *service.ConfigService
var emailService *service.EmailService
var sessionService *service.SessionService
var pageSize = 1000 var pageSize = 1000
var defaultSortField = "UpdatedTime" var defaultSortField = "UpdatedTime"
var leanoteUserId = "52d26b4e99c37b609a000001" var leanoteUserId = "admin" // 不能更改
var siteUrl = "http://leanote.com" var siteUrl = "http://leanote.com"
var openRegister = true
// 拦截器 // 拦截器
// 不需要拦截的url // 不需要拦截的url
@@ -47,10 +48,15 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
"FindPasswordUpdate": true, "FindPasswordUpdate": true,
"Suggestion": true, "Suggestion": true,
}, },
"Note": map[string]bool{"ToImage": true},
"Blog": map[string]bool{"Index": true, "Blog": map[string]bool{"Index": true,
"View": true, "View": true,
"AboutMe": true, "AboutMe": true,
"SearchBlog": true, "Cate": true,
"Search": true,
"GetLikeAndComments": true,
"IncReadNum": true,
"ListComments": true,
}, },
// 用户的激活与修改邮箱都不需要登录, 通过链接地址 // 用户的激活与修改邮箱都不需要登录, 通过链接地址
"User": map[string]bool{"UpdateEmail": true, "User": map[string]bool{"UpdateEmail": true,
@@ -118,6 +124,8 @@ func InitService() {
suggestionService = service.SuggestionS suggestionService = service.SuggestionS
authService = service.AuthS authService = service.AuthS
configService = service.ConfigS configService = service.ConfigS
emailService = service.EmailS
sessionService = service.SessionS
} }
func init() { func init() {
@@ -133,8 +141,6 @@ func init() {
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &NoteContentHistory{}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &NoteContentHistory{})
revel.OnAppStart(func() { revel.OnAppStart(func() {
leanoteUserId, _ = revel.Config.String("adminUsername")
siteUrl, _ = revel.Config.String("site.url") siteUrl, _ = revel.Config.String("site.url")
openRegister, _ = revel.Config.Bool("register.open")
}) })
} }
+19 -5
View File
@@ -40,6 +40,15 @@ var Attachs *mgo.Collection
var NoteImages *mgo.Collection var NoteImages *mgo.Collection
var Configs *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() { func Init() {
@@ -113,12 +122,17 @@ func Init() {
NoteImages = Session.DB(dbname).C("note_images") NoteImages = Session.DB(dbname).C("note_images")
Configs = Session.DB(dbname).C("configs") Configs = Session.DB(dbname).C("configs")
} EmailLogs = Session.DB(dbname).C("email_logs")
func init() { // 社交
revel.OnAppStart(func() { BlogLikes = Session.DB(dbname).C("blog_likes")
Init() BlogComments = Session.DB(dbname).C("blog_comments")
})
// 举报
Reports = Session.DB(dbname).C("reports")
// session
Sessions = Session.DB(dbname).C("sessions")
} }
func close() { func close() {
+20 -3
View File
@@ -10,8 +10,8 @@ import (
// convert revel msg to js msg // convert revel msg to js msg
var msgBasePath = "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/messages/" var msgBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/messages/"
var targetBasePath = "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/public/js/i18n/" var targetBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/public/js/i18n/"
func parse(filename string) { func parse(filename string) {
file, err := os.Open(msgBasePath + filename) file, err := os.Open(msgBasePath + filename)
reader := bufio.NewReader(file) reader := bufio.NewReader(file)
@@ -62,11 +62,28 @@ func parse(filename string) {
if err2 != nil { if err2 != nil {
file2, err2 = os.Create(targetName) 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文件 // 生成js的i18n文件
func main() { func main() {
parse("msg.en") parse("msg.en")
parse("msg.zh") parse("msg.zh")
parse("blog.zh")
parse("blog.en")
} }
+42
View File
@@ -2,6 +2,7 @@ package info
import ( import (
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time"
) )
// 只为blog, 不为note // 只为blog, 不为note
@@ -22,11 +23,13 @@ type UserBlogBase struct {
type UserBlogComment struct { type UserBlogComment struct {
CanComment bool `CanComment` // 是否可以评论 CanComment bool `CanComment` // 是否可以评论
CommentType string `CommentType` // default 或 disqus
DisqusId string `DisqusId` DisqusId string `DisqusId`
} }
type UserBlogStyle struct { type UserBlogStyle struct {
Style string `Style` // 风格 Style string `Style` // 风格
Css string `Css` // 自定义css
} }
// 每个用户一份博客设置信息 // 每个用户一份博客设置信息
@@ -38,7 +41,46 @@ type UserBlog struct {
AboutMe string `AboutMe` // 关于我 AboutMe string `AboutMe` // 关于我
CanComment bool `CanComment` // 是否可以评论 CanComment bool `CanComment` // 是否可以评论
CommentType string `CommentType` // default 或 disqus
DisqusId string `DisqusId` DisqusId string `DisqusId`
Style string `Style` // 风格 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
} }
+15 -5
View File
@@ -5,11 +5,21 @@ import (
"time" "time"
) )
// 配置 // 配置, 每一个配置一行记录
// 用户配置高于全局配置
type Config struct { type Config struct {
UserId bson.ObjectId `bson:"_id"` ConfigId bson.ObjectId `bson:"_id"`
StringConfigs map[string]string `StringConfigs` // key => value UserId bson.ObjectId `UserId`
ArrayConfigs map[string][]string `ArrayConfigs` // key => []value 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` UpdatedTime time.Time `UpdatedTime`
} }
+19
View 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`
}
+7
View File
@@ -24,12 +24,19 @@ type Note struct {
IsRecommend bool `IsRecommend,omitempty` // 是否为推荐博客 2014/9/24新加 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 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` CreatedTime time.Time `CreatedTime`
UpdatedTime time.Time `UpdatedTime` UpdatedTime time.Time `UpdatedTime`
RecommendTime time.Time `RecommendTime,omitempty` // 推荐时间
PublicTime time.Time `PublicTime,omitempty` // 发表时间, 公开为博客则设置
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了 UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了
} }
+19
View 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
View 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这个时间会自动清空
}
+1
View File
@@ -27,6 +27,7 @@ type User struct {
// 用户配置 // 用户配置
NotebookWidth int `NotebookWidth` // 笔记本宽度 NotebookWidth int `NotebookWidth` // 笔记本宽度
NoteListWidth int `NoteListWidth` // 笔记列表宽度 NoteListWidth int `NoteListWidth` // 笔记列表宽度
MdEditorWidth int `MdEditorWidth` // markdown 左侧编辑器宽度
LeftIsMin bool `LeftIsMin` // 左侧是否是隐藏的, 默认是打开的 LeftIsMin bool `LeftIsMin` // 左侧是否是隐藏的, 默认是打开的
// 这里 第三方登录 // 这里 第三方登录
+70 -11
View File
@@ -4,9 +4,13 @@ import (
"github.com/revel/revel" "github.com/revel/revel"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/service" "github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/controllers" "github.com/leanote/leanote/app/controllers"
"github.com/leanote/leanote/app/controllers/admin" "github.com/leanote/leanote/app/controllers/admin"
_ "github.com/leanote/leanote/app/lea/binder" _ "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" "reflect"
"fmt" "fmt"
"html/template" "html/template"
@@ -14,20 +18,24 @@ import (
"strings" "strings"
"strconv" "strconv"
"time" "time"
"encoding/json"
) )
func init() { func init() {
// Filters is the default set of global filters. // Filters is the default set of global filters.
revel.Filters = []revel.Filter{ revel.Filters = []revel.Filter{
revel.PanicFilter, // Recover from panics and display an error page instead. 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 // revel.RouterFilter, // Use the routing table to select the right Action
// AuthFilter, // Invoke the action. // AuthFilter, // Invoke the action.
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters. revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params. 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.FlashFilter, // Restore and write the flash cookie.
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie. revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
@@ -48,12 +56,34 @@ func init() {
i = i - 1; i = i - 1;
return i 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 { revel.TemplateFuncs["concat"] = func(s1, s2 string) template.HTML {
return template.HTML(s1 + s2) 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 { revel.TemplateFuncs["datetime"] = func(t time.Time) template.HTML {
return template.HTML(t.Format("2006-01-02 15:04:05")) 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是否有该字段 // interface是否有该字段
revel.TemplateFuncs["has"] = func(i interface{}, key string) bool { revel.TemplateFuncs["has"] = func(i interface{}, key string) bool {
@@ -63,6 +93,26 @@ func init() {
} }
// tags // 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 { revel.TemplateFuncs["blogTags"] = func(tags []string) template.HTML {
if tags == nil || len(tags) == 0 { if tags == nil || len(tags) == 0 {
return "" return ""
@@ -83,7 +133,7 @@ func init() {
} }
return template.HTML(tagStr) return template.HTML(tagStr)
} }
*/
revel.TemplateFuncs["li"] = func(a string) string { revel.TemplateFuncs["li"] = func(a string) string {
Log(a) Log(a)
Log("life==") Log("life==")
@@ -130,6 +180,15 @@ func init() {
return "" 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使用 // 为后台管理sorter th使用
// 必须要返回HTMLAttr, 返回html, golang 会执行安全检查返回ZgotmplZ // 必须要返回HTMLAttr, 返回html, golang 会执行安全检查返回ZgotmplZ
// sorterI 可能是nil, 所以用interfalce{}来接收 // sorterI 可能是nil, 所以用interfalce{}来接收
@@ -155,7 +214,7 @@ func init() {
} }
// pagination // 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 { if count == 0 {
return ""; return "";
} }
@@ -170,11 +229,6 @@ func init() {
nextPage := page + 1 nextPage := page + 1
var preUrl, nextUrl string var preUrl, nextUrl string
urlBase := "/blog/" + userId
if notebookId != "" {
urlBase += "/" + notebookId
}
preUrl = urlBase + "?page=" + strconv.Itoa(prePage) preUrl = urlBase + "?page=" + strconv.Itoa(prePage)
nextUrl = urlBase + "?page=" + strconv.Itoa(nextPage) nextUrl = urlBase + "?page=" + strconv.Itoa(nextPage)
@@ -238,8 +292,13 @@ func init() {
// init Email // init Email
revel.OnAppStart(func() { revel.OnAppStart(func() {
// 数据库
db.Init()
// email配置
InitEmail() InitEmail()
InitVd()
memcache.InitMemcache() // session服务
// 其它service
service.InitService() service.InitService()
controllers.InitService() controllers.InitService()
admin.InitService() admin.InitService()
+6 -7
View File
@@ -22,7 +22,7 @@ func InitEmail() {
var bodyTpl = ` var bodyTpl = `
<html> <html>
<body> <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> <div>
<div style="float:left; height: 40px;"> <div style="float:left; height: 40px;">
@@ -56,7 +56,7 @@ var bodyTpl = `
</body> </body>
</html> </html>
` `
func SendEmail(to, subject, title, body string) bool { func SendEmailOld(to, subject, body string) bool {
hp := strings.Split(host, ":") hp := strings.Split(host, ":")
auth := smtp.PlainAuth("", username, password, hp[0]) 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" content_type = "Content-Type: text/plain" + "; charset=UTF-8"
} }
// 登录之 //body = strings.Replace(bodyTpl, "$body", body, 1)
body = strings.Replace(bodyTpl, "$body", body, 1) //body = strings.Replace(body, "$title", title, 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) 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, ";") send_to := strings.Split(to, ";")
@@ -84,7 +83,7 @@ func SendEmail(to, subject, title, body string) bool {
return true return true
} }
func SendToLeanote(subject, title, body string) { func SendToLeanoteOld(subject, title, body string) {
to := "leanote@leanote.com" to := "leanote@leanote.com"
SendEmail(to, subject, title, body); SendEmailOld(to, subject, body);
} }
+10
View File
@@ -78,3 +78,13 @@ func CopyFile(srcName, dstName string) (written int64, err error) {
defer dst.Close() defer dst.Close()
return io.Copy(dst, src) 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
View 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
View 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
View 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
}
+42 -2
View File
@@ -3,9 +3,20 @@ package memcache
import ( import (
"github.com/robfig/gomemcache/memcache" "github.com/robfig/gomemcache/memcache"
"encoding/json" "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 // 把value转成byte
bytes, _ := json.Marshal(value) bytes, _ := json.Marshal(value)
if expiration == -1 { 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}) 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) item, err := client.Get(key)
if err != nil { if err != nil {
return nil return nil
@@ -24,3 +35,32 @@ func Get(key string) map[string]string {
json.Unmarshal(item.Value, &m) json.Unmarshal(item.Value, &m)
return 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)
}
-11
View File
@@ -1,11 +0,0 @@
package memcache
import (
"github.com/robfig/gomemcache/memcache"
)
var client *memcache.Client
func init() {
// client = memcache.New("localhost:11211")
}
+6 -7
View File
@@ -13,7 +13,7 @@ import (
// toPath 文件保存的目录 // toPath 文件保存的目录
// 默认是/tmp // 默认是/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 == "" { if url == "" {
return; return;
} }
@@ -22,6 +22,8 @@ func WriteUrl(url string, toPath string) (path string, ok bool) {
return; return;
} }
length = int64(len(content))
// a.html?a=a11&xxx // a.html?a=a11&xxx
url = trimQueryParams(url) url = trimQueryParams(url)
_, ext := SplitFilename(url) _, ext := SplitFilename(url)
@@ -29,13 +31,8 @@ func WriteUrl(url string, toPath string) (path string, ok bool) {
toPath = "/tmp" toPath = "/tmp"
} }
// dir := filepath.Dir(toPath) // dir := filepath.Dir(toPath)
newFilename := NewGuid() + ext newFilename = NewGuid() + ext
fullPath := toPath + "/" + newFilename fullPath := toPath + "/" + newFilename
/*
if err := os.MkdirAll(dir, 0777); err != nil {
return
}
*/
// 写到文件中 // 写到文件中
file, err := os.Create(fullPath) 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) { func GetContent(url string) (content []byte, err error) {
var resp *http.Response var resp *http.Response
resp, err = http.Get(url) resp, err = http.Get(url)
Log(err)
if(resp != nil && resp.Body != nil) { if(resp != nil && resp.Body != nil) {
defer resp.Body.Close() defer resp.Body.Close()
} else { } else {
@@ -65,6 +63,7 @@ func GetContent(url string) (content []byte, err error) {
var buf []byte var buf []byte
buf, err = ioutil.ReadAll(resp.Body) buf, err = ioutil.ReadAll(resp.Body)
if(err != nil) { if(err != nil) {
Log(err)
return return
} }
+23 -4
View File
@@ -1,14 +1,20 @@
package lea package route
import ( import (
"github.com/revel/revel" "github.com/revel/revel"
// "github.com/leanote/leanote/app/service"
// . "github.com/leanote/leanote/app/lea"
"net/url" "net/url"
"strings" "strings"
) )
// overwite revel RouterFilter // overwite revel RouterFilter
// /api/user/Info => ApiUser.Info() // /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) { func RouterFilter(c *revel.Controller, fc []revel.Filter) {
// 补全controller部分
path := c.Request.Request.URL.Path
// Figure out the Controller/Action // Figure out the Controller/Action
var route *revel.RouteMatch = revel.MainRouter.Route(c.Request.Request) var route *revel.RouteMatch = revel.MainRouter.Route(c.Request.Request)
if route == nil { if route == nil {
@@ -24,12 +30,25 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
//---------- //----------
// life start // life start
path := c.Request.Request.URL.Path /*
// Log(c.Request.Request.URL.Host) type URL struct {
if strings.HasPrefix(path, "/api") || strings.HasPrefix(path, "api") { 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 route.ControllerName = "Api" + route.ControllerName
} }
// end // end
}
// Set the action. // Set the action.
if err := c.SetAction(route.ControllerName, route.MethodName); err != nil { if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
+38
View 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}
}
+195 -18
View File
@@ -1,31 +1,208 @@
package session package session
import ( import (
"github.com/robfig/revel" "github.com/revel/revel"
"github.com/leanote/leanote/app/lea/memcache" // . "github.com/leanote/leanote/app/lea"
// . "leanote/app/lea" "crypto/rand"
"encoding/hex"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
) )
// 使用filter // 主要修改revel的cookie, 设置Domain
// 很巧妙就使用了memcache来处理session // 为了使sub domain共享cookie
// revel的session(cookie)只存sessionId, 其它信息存在memcache中 // cookie.domain = leanote.com
func SessionFilter(c *revel.Controller, fc []revel.Filter) { // A signed cookie (and thus limited to 4kb in size).
sessionId := c.Session.Id() // Restriction: Keys may not have a colon in them.
type Session map[string]string
// 从memcache中得到cache, 赋给session const (
cache := revel.Session(memcache.Get(sessionId)) SESSION_ID_KEY = "_ID"
if cache == nil { TIMESTAMP_KEY = "_TS"
cache = revel.Session{} )
cache.Id()
// 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))
} }
c.Session = cache
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
}
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:]) fc[0](c, fc[1:])
// 再把session保存之 // Store the signed session if it could have changed.
memcache.Set(sessionId, c.Session, -1) if len(c.Session) > 0 || !sessionWasEmpty {
// 转换成lea.Session
session = Session(c.Session)
c.SetCookie(session.cookie())
}
}
// 只留下sessionId // restoreSession returns either the current session, retrieved from the
c.Session = revel.Session{revel.SESSION_ID_KEY: sessionId} // 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)
} }
+7 -2
View File
@@ -39,6 +39,7 @@ var cmdPath = "/usr/local/bin/uglifyjs"
func cmdError(err error) { func cmdError(err error) {
if err != nil { if err != nil {
fmt.Println(err)
fmt.Fprintf(os.Stderr, "The command failed to perform: %s (Command: %s, Arguments: %s)", err, "", "") fmt.Fprintf(os.Stderr, "The command failed to perform: %s (Command: %s, Arguments: %s)", err, "", "")
} else { } else {
fmt.Println("OK") fmt.Println("OK")
@@ -63,6 +64,7 @@ func combineJs() {
for _, js := range jss { for _, js := range jss {
to := base + js + "-min.js" to := base + js + "-min.js"
fmt.Println(to)
compressJs(js) compressJs(js)
// 每个压缩后的文件放入之 // 每个压缩后的文件放入之
@@ -85,6 +87,7 @@ func dev() {
"notebook.js": "notebook-min.js", "notebook.js": "notebook-min.js",
"share.js": "share-min.js", "share.js": "share-min.js",
"tag.js": "tag-min.js", "tag.js": "tag-min.js",
"main.js": "main-min.js",
"jquery.contextmenu.js": "jquery.contextmenu-min.js", "jquery.contextmenu.js": "jquery.contextmenu-min.js",
"editor/editor.js": "editor/editor-min.js", "editor/editor.js": "editor/editor-min.js",
"/public/mdeditor/editor/scrollLink.js": "/public/mdeditor/editor/scrollLink-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", "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 := 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" 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) cmdError(err)
} }
@@ -116,11 +120,12 @@ func main() {
dev(); dev();
// 其它零散的需要压缩的js // 其它零散的需要压缩的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/scrollLink",
"mdeditor/editor/editor", "mdeditor/editor/editor",
"mdeditor/editor/jquery.waitforimages", "mdeditor/editor/jquery.waitforimages",
"mdeditor/editor/pagedown/local/Markdown.local.zh", "mdeditor/editor/pagedown/local/Markdown.local.zh",
"mdeditor/editor/pagedown/local/Markdown.local.en",
"mdeditor/editor/pagedown/Markdown.Editor", "mdeditor/editor/pagedown/Markdown.Editor",
"mdeditor/editor/pagedown/Markdown.Sanitizer", "mdeditor/editor/pagedown/Markdown.Sanitizer",
"mdeditor/editor/pagedown/Markdown.Converter", "mdeditor/editor/pagedown/Markdown.Converter",
+23 -11
View File
@@ -4,9 +4,10 @@ import (
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
// "github.com/leanote/leanote/app/db" // "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
"github.com/revel/revel" // "github.com/revel/revel"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"fmt" "fmt"
"strconv"
) )
// 登录与权限 // 登录与权限
@@ -16,7 +17,8 @@ type AuthService struct {
// pwd已md5了 // pwd已md5了
func (this *AuthService) Login(emailOrUsername, pwd string) info.User { func (this *AuthService) Login(emailOrUsername, pwd string) info.User {
return userService.LoginGetUserInfo(emailOrUsername, Md5(pwd)) userInfo := userService.LoginGetUserInfo(emailOrUsername, Md5(pwd))
return userInfo
} }
// 注册 // 注册
@@ -56,21 +58,31 @@ func (this *AuthService) register(user info.User) (bool, string) {
email := user.Email email := user.Email
// 添加leanote -> 该用户的共享 // 添加leanote -> 该用户的共享
leanoteUserId, _ := revel.Config.String("register.sharedUserId"); // "5368c1aa99c37b029d000001"; registerSharedUserId := configService.GetGlobalStringConfig("registerSharedUserId")
nk1, _ := revel.Config.String("register.sharedUserShareNotebookId"); // 5368c1aa99c37b029d000002" // leanote if(registerSharedUserId != "") {
welcomeNoteId, _ := revel.Config.String("register.welcomeNoteId") // "5368c1b919807a6f95000000" // 欢迎来到leanote registerSharedNotebooks := configService.GetGlobalArrMapConfig("registerSharedNotebooks")
registerSharedNotes := configService.GetGlobalArrMapConfig("registerSharedNotes")
registerCopyNoteIds := configService.GetGlobalArrayConfig("registerCopyNoteIds")
if leanoteUserId != "" && nk1 != "" && welcomeNoteId != "" { // 添加共享笔记本
shareService.AddShareNotebook(nk1, 0, leanoteUserId, email); for _, notebook := range registerSharedNotebooks {
shareService.AddShareNote(welcomeNoteId, 0, leanoteUserId, email); perm, _ := strconv.Atoi(notebook["perm"])
shareService.AddShareNotebook(notebook["notebookId"], perm, registerSharedUserId, email);
}
// 将welcome copy给我 // 添加共享笔记
note := noteService.CopySharedNote(welcomeNoteId, title2Id["life"].Hex(), leanoteUserId, user.UserId.Hex()); 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} noteUpdate := bson.M{"IsBlog": true}
noteService.UpdateNote(user.UserId.Hex(), user.UserId.Hex(), note.NoteId.Hex(), noteUpdate) noteService.UpdateNote(user.UserId.Hex(), user.UserId.Hex(), note.NoteId.Hex(), noteUpdate)
} }
}
//--------------- //---------------
// 添加一条userBlog // 添加一条userBlog
+338 -10
View File
@@ -3,10 +3,12 @@ package service
import ( import (
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
// "time" // "time"
// "sort" // "sort"
"strings"
"time"
) )
// blog // 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 { func (this *BlogService) GetUserBlog(userId string) info.UserBlog {
userBlog := info.UserBlog{} userBlog := info.UserBlog{}
db.Get(db.UserBlogs, userId, &userBlog) db.Get(db.UserBlogs, userId, &userBlog)
this.fixUserBlog(&userBlog)
if userBlog.Title == "" {
userInfo := userService.GetUserInfo(userId)
userBlog.Title = userInfo.Username + " 的博客"
}
return userBlog return userBlog
} }
@@ -225,7 +237,8 @@ func (this *BlogService) UpdateUserBlog(userBlog info.UserBlog) bool {
} }
// 修改之UserBlogBase // 修改之UserBlogBase
func (this *BlogService) UpdateUserBlogBase(userId string, userBlog info.UserBlogBase) bool { 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 { func (this *BlogService) UpdateUserBlogComment(userId string, userBlog info.UserBlogComment) bool {
return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog) 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) return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
} }
//------------
//---------------------
// 后台管理 // 后台管理
// 推荐博客 // 推荐博客
func (this *BlogService) SetRecommend(noteId string, isRecommend bool) bool { 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)
} }
+478 -78
View File
@@ -2,40 +2,38 @@ package service
import ( import (
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"github.com/revel/revel" "github.com/revel/revel"
"time" "time"
"os"
"os/exec"
"fmt"
"strings"
"strconv"
) )
// 配置服务 // 配置服务
// 只是全局的, 用户的配置没有
type ConfigService struct { type ConfigService struct {
// 全局的 // 全局的
GlobalAllConfigs map[string]interface{}
GlobalStringConfigs map[string]string GlobalStringConfigs map[string]string
GlobalArrayConfigs map[string][]string GlobalArrayConfigs map[string][]string
GlobalMapConfigs map[string]map[string]string
// 两种配置, 用户自己的 GlobalArrMapConfigs map[string][]map[string]string
UserStringConfigs map[string]string
UserArrayConfigs map[string][]string
// 合并之后的
AllStringConfigs map[string]string
AllArrayConfigs map[string][]string
} }
var adminUserId = "" var adminUserId = ""
// appStart时 将全局的配置从数据库中得到作为全局 // appStart时 将全局的配置从数据库中得到作为全局
func (this *ConfigService) InitGlobalConfigs() bool { func (this *ConfigService) InitGlobalConfigs() bool {
this.GlobalAllConfigs = map[string]interface{}{}
this.GlobalStringConfigs = map[string]string{} this.GlobalStringConfigs = map[string]string{}
this.GlobalArrayConfigs = map[string][]string{} this.GlobalArrayConfigs = map[string][]string{}
this.GlobalMapConfigs = map[string]map[string]string{}
this.UserStringConfigs = map[string]string{} this.GlobalArrMapConfigs = map[string][]map[string]string{}
this.UserArrayConfigs = map[string][]string{}
this.AllStringConfigs = map[string]string{}
this.AllArrayConfigs = map[string][]string{}
adminUsername, _ := revel.Config.String("adminUsername") adminUsername, _ := revel.Config.String("adminUsername")
if adminUsername == "" { if adminUsername == "" {
@@ -48,84 +46,95 @@ func (this *ConfigService) InitGlobalConfigs() bool {
} }
adminUserId = userInfo.UserId.Hex() adminUserId = userInfo.UserId.Hex()
configs := info.Config{} configs := []info.Config{}
db.Get2(db.Configs, userInfo.UserId, &configs) db.ListByQ(db.Configs, bson.M{"UserId": userInfo.UserId}, &configs)
if configs.UserId == "" { for _, config := range configs {
db.Insert(db.Configs, info.Config{UserId: userInfo.UserId, StringConfigs: map[string]string{}, ArrayConfigs: map[string][]string{}}) 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
} }
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
} }
return true return true
} }
// 用户登录后获取用户自定义的配置, 并将所有的配置都用上 // 通用方法
func (this *ConfigService) InitUserConfigs(userId string) bool { func (this *ConfigService) updateGlobalConfig(userId, key string, value interface{}, isArr, isMap, isArrMap bool) bool {
configs := info.Config{} // 判断是否存在
db.Get(db.Configs, userId, &configs) if _, ok := this.GlobalAllConfigs[key]; !ok {
// 需要添加
if configs.UserId == "" { config := info.Config{ConfigId: bson.NewObjectId(),
db.Insert(db.Configs, info.Config{UserId: bson.ObjectIdHex(userId), StringConfigs: map[string]string{}, ArrayConfigs: map[string][]string{}}) UserId: bson.ObjectIdHex(userId),
Key: key,
IsArr: isArr,
IsMap: isMap,
IsArrMap: isArrMap,
UpdatedTime: time.Now(),
} }
if(isArr) {
this.UserStringConfigs = configs.StringConfigs; v, _ := value.([]string)
this.UserArrayConfigs = configs.ArrayConfigs; config.ValueArr = v
this.GlobalArrayConfigs[key] = v
// 合并配置 } else if isMap {
for key, value := range this.UserStringConfigs { v, _ := value.(map[string]string)
this.AllStringConfigs[key] = value 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
} }
for key, value := range this.UserArrayConfigs { return db.Insert(db.Configs, config)
this.AllArrayConfigs[key] = value } 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)
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 { func (this *ConfigService) UpdateGlobalStringConfig(userId, key string, value string) bool {
this.UserStringConfigs[key] = value return this.updateGlobalConfig(userId, key, value, false, false, false)
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) UpdateUserArrayConfig(userId, key string, value []string) bool { func (this *ConfigService) UpdateGlobalArrayConfig(userId, key string, value []string) bool {
this.UserArrayConfigs[key] = value return this.updateGlobalConfig(userId, key, value, true, false, false)
this.AllArrayConfigs[key] = value }
if userId == adminUserId { func (this *ConfigService) UpdateGlobalMapConfig(userId, key string, value map[string]string) bool {
this.GlobalArrayConfigs[key] = value 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)
return db.UpdateByQMap(db.Configs, bson.M{"_id": bson.ObjectIdHex(userId)},
bson.M{"ArrayConfigs": this.UserArrayConfigs, "UpdatedTime": time.Now()})
} }
// 获取全局配置, 博客平台使用 // 获取全局配置, 博客平台使用
@@ -139,3 +148,394 @@ func (this *ConfigService) GetGlobalArrayConfig(key string) []string {
} }
return arr 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) IsOpenRegister() bool {
return this.GetGlobalStringConfig("openRegister") != ""
}
//-------
// 修改共享笔记的配置
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-10-25/ -u leanote -p xx
binPath = binPath + " -h " + host + " -d " + dbname + " -port " + port
if username != "" {
binPath += " -u " + username + " -p " + password
}
// 保存的路径
dir := revel.BasePath + "/mongodb_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
View 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
}
+36 -2
View File
@@ -126,13 +126,21 @@ func (this *NoteService) AddNote(note info.Note) info.Note {
note.UpdatedUserId = note.UserId note.UpdatedUserId = note.UserId
// 设为blog // 设为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) db.Insert(db.Notes, note)
// tag1 // tag1
tagService.AddTags(note.UserId.Hex(), note.Tags) tagService.AddTags(note.UserId.Hex(), note.Tags)
// recount notebooks' notes number
notebookService.ReCountNotebookNumberNotes(notebookId)
return note return note
} }
@@ -276,6 +284,9 @@ func (this *NoteService) UpdateTags(noteId string, userId string, tags []string)
// 2. 要判断之前是否是blog, 如果不是, 那么notebook是否是blog? // 2. 要判断之前是否是blog, 如果不是, 那么notebook是否是blog?
func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note { func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
if notebookService.IsMyNotebook(notebookId, userId) { if notebookService.IsMyNotebook(notebookId, userId) {
note := this.GetNote(noteId, userId)
preNotebookId := note.NotebookId.Hex()
re := db.UpdateByIdAndUserId(db.Notes, noteId, userId, re := db.UpdateByIdAndUserId(db.Notes, noteId, userId,
bson.M{"$set": bson.M{"IsTrash": false, bson.M{"$set": bson.M{"IsTrash": false,
"NotebookId": bson.ObjectIdHex(notebookId)}}) "NotebookId": bson.ObjectIdHex(notebookId)}})
@@ -283,6 +294,13 @@ func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
if re { if re {
// 更新blog状态 // 更新blog状态
this.updateToNotebookBlog(noteId, notebookId, userId) 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); return this.GetNote(noteId, userId);
@@ -330,7 +348,11 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
// 更新blog状态 // 更新blog状态
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId) isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId)
// recount
notebookService.ReCountNotebookNumberNotes(notebookId)
note.IsBlog = isBlog note.IsBlog = isBlog
return note 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 { 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) && if notebookService.IsMyNotebook(notebookId, myUserId) &&
(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) { (shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) {
@@ -375,6 +397,9 @@ func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId
// 更新blog状态 // 更新blog状态
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, myUserId) isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, myUserId)
// recount
notebookService.ReCountNotebookNumberNotes(notebookId)
note.IsBlog = isBlog note.IsBlog = isBlog
return note return note
} }
@@ -483,3 +508,12 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb
All(&notes) All(&notes)
return 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})
}
+35 -3
View File
@@ -5,7 +5,7 @@ import (
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"sort" "sort"
"time" "time"
) )
@@ -96,6 +96,11 @@ func (this *NotebookService) GetNotebook(notebookId, userId string) info.Noteboo
db.GetByIdAndUserId(db.Notebooks, notebookId, userId, &notebook) db.GetByIdAndUserId(db.Notebooks, notebookId, userId, &notebook)
return notebook return notebook
} }
func (this *NotebookService) GetNotebookById(notebookId string) info.Notebook {
notebook := info.Notebook{}
db.Get(db.Notebooks, notebookId, &notebook)
return notebook
}
// 得到用户下所有的notebook // 得到用户下所有的notebook
// 排序好之后返回 // 排序好之后返回
@@ -168,11 +173,15 @@ func (this *NotebookService) UpdateNotebook(userId, notebookId string, needUpdat
// 如果有IsBlog之类的, 需要特殊处理 // 如果有IsBlog之类的, 需要特殊处理
if isBlog, ok := needUpdate["IsBlog"]; ok { if isBlog, ok := needUpdate["IsBlog"]; ok {
// 设为blog/取消 // 设为blog/取消, 把它下面所有的note都设为isBlog
if is, ok2 := isBlog.(bool); ok2 { if is, ok2 := isBlog.(bool); ok2 {
q := bson.M{"UserId": bson.ObjectIdHex(userId), q := bson.M{"UserId": bson.ObjectIdHex(userId),
"NotebookId": bson.ObjectIdHex(notebookId)} "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 // noteContents也更新, 这个就麻烦了, noteContents表没有NotebookId
// 先查该notebook下所有notes, 得到id // 先查该notebook下所有notes, 得到id
@@ -249,3 +258,26 @@ func (this *NotebookService) DragNotebooks(userId string, curNotebookId string,
return true 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"}, &notebooks)
for _, each := range notebooks {
this.ReCountNotebookNumberNotes(each.NotebookId.Hex())
}
*/
}
+1 -10
View File
@@ -2,11 +2,9 @@ package service
import ( import (
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"github.com/revel/revel"
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea" . "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") ok, msg = emailService.FindPwdSendEmail(token, email)
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
return return
} }
+71
View 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
}
+7 -1
View File
@@ -28,7 +28,13 @@ func (this *TrashService) DeleteNote(noteId, userId string) bool {
// 首先删除其共享 // 首先删除其共享
if shareService.DeleteShareNoteAll(noteId, userId) { if shareService.DeleteShareNoteAll(noteId, userId) {
// 更新note isTrash = true // 更新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 return false
} }
+27
View 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}, &notes)
// 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
}
+81 -74
View File
@@ -1,20 +1,17 @@
package service package service
import ( import (
"github.com/revel/revel"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time" "time"
"strings" "strings"
"fmt"
) )
type UserService struct { type UserService struct {
} }
// 添加用户 // 添加用户
func (this *UserService) AddUser(user info.User) bool { func (this *UserService) AddUser(user info.User) bool {
if user.UserId == "" { if user.UserId == "" {
@@ -27,7 +24,9 @@ func (this *UserService) AddUser(user info.User) bool {
// 发送验证邮箱 // 发送验证邮箱
go func() { 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) 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 // 得到用户信息 userId
func (this *UserService) GetUserInfo(userId string) info.User { func (this *UserService) GetUserInfo(userId string) info.User {
user := info.User{} user := info.User{}
db.Get(db.Users, userId, &user) db.Get(db.Users, userId, &user)
// Logo路径问题, 有些有http: 有些没有
this.setUserLogo(&user)
return user return user
} }
// 得到用户信息 email // 得到用户信息 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) db.ListByQ(db.Users, bson.M{"_id": bson.M{"$in": userIds}}, &users)
return users return users
} }
// 用户信息和博客设置信息 func (this *UserService) ListUserInfosByEmails(emails []string) []info.User {
func (this *UserService) MapUserInfoAndBlogInfosByUserIds(userIds []bson.ObjectId) map[bson.ObjectId]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{} users := []info.User{}
db.ListByQ(db.Users, bson.M{"_id": bson.M{"$in": userIds}}, &users) 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)) userMap := make(map[bson.ObjectId]info.User, len(users))
for _, user := range users { for _, user := range users {
if userBlog, ok := userBlogMap[user.UserId]; ok { this.setUserLogo(&user)
user.Logo = userBlog.Logo
}
userMap[user.UserId] = user userMap[user.UserId] = user
} }
return userMap return userMap
} }
// 用户信息和博客设置信息
func (this *UserService) MapUserInfoAndBlogInfosByUserIds(userIds []bson.ObjectId) map[bson.ObjectId]info.User {
return this.MapUserInfoByUserIds(userIds)
}
// 通过ids得到users, 按id的顺序组织users // 通过ids得到users, 按id的顺序组织users
func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.User { func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.User {
@@ -174,6 +184,12 @@ func (this *UserService) UpdateUsername(userId, username string) (bool, string)
return ok, "" 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) { func (this *UserService) UpdatePwd(userId, oldPwd, pwd string) (bool, string) {
@@ -194,59 +210,6 @@ func (this *UserService) UpdateTheme(userId, theme string) (bool) {
//--------------- //---------------
// 修改email // 修改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) { func (this *UserService) ActiveEmail(token string) (ok bool, msg, email string) {
tokenInfo := info.Token{} tokenInfo := info.Token{}
@@ -308,13 +271,13 @@ func (this *UserService) ThirdAddUser(userId, email, pwd string) (ok bool, msg s
return return
} }
//------------ //------------
// 偏好设置 // 偏好设置
// 宽度 // 宽度
func (this *UserService)UpdateColumnWidth(userId string, notebookWidth, noteListWidth int) bool { 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}) 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 { func (this *UserService)UpdateLeftIsMin(userId string, leftIsMin bool) bool {
@@ -341,3 +304,47 @@ func (this *UserService) ListUsers(pageNumber, pageSize int, sortField string, i
page = info.NewPage(pageNumber, pageSize, count, nil) page = info.NewPage(pageNumber, pageSize, count, nil)
return 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{})
}
+14 -1
View File
@@ -1,7 +1,7 @@
package service package service
import ( import (
"github.com/revel/revel"
) )
// init service, for share service bettween services // init service, for share service bettween services
@@ -24,7 +24,12 @@ var attachService, AttachS *AttachService
var configService, ConfigS *ConfigService var configService, ConfigS *ConfigService
var PwdS *PwdService var PwdS *PwdService
var SuggestionS *SuggestionService var SuggestionS *SuggestionService
var emailService, EmailS *EmailService
var AuthS *AuthService var AuthS *AuthService
var UpgradeS *UpgradeService
var SessionS, sessionService *SessionService
var siteUrl string
// onAppStart调用 // onAppStart调用
func InitService() { func InitService() {
@@ -45,6 +50,9 @@ func InitService() {
PwdS = &PwdService{} PwdS = &PwdService{}
SuggestionS = &SuggestionService{} SuggestionS = &SuggestionService{}
AuthS = &AuthService{} AuthS = &AuthService{}
EmailS = NewEmailService()
UpgradeS = &UpgradeService{}
SessionS = &SessionService{}
notebookService = NotebookS notebookService = NotebookS
noteService = NoteS noteService = NoteS
@@ -60,4 +68,9 @@ func InitService() {
albumService = AlbumS albumService = AlbumS
attachService = AttachS attachService = AttachS
configService = ConfigS configService = ConfigS
emailService = EmailS
sessionService = SessionS
//
siteUrl, _ = revel.Config.String("site.url")
} }
+2 -1
View File
@@ -189,7 +189,8 @@ func testLea() {
func main() { func main() {
revel.BasePath = "/Users/life/Documents/Go/package/src/leanote" revel.BasePath = "/Users/life/Documents/Go/package/src/leanote"
testLea(); // testLea();
// a, b := SplitFilename("http://ab/c/a.gif#??") // a, b := SplitFilename("http://ab/c/a.gif#??")
// println(a) // println(a)
// println(b) // println(b)
+9 -54
View File
@@ -4,22 +4,11 @@
<section class="panel panel-default"> <section class="panel panel-default">
<div class="row wrapper"> <div class="row wrapper">
<div class="col-sm-5 m-b-xs"> <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"> <button class="btn btn-sm btn-default">
Apply Action1
</button>
<button class="btn btn-sm btn-default">
Action2
</button> </button>
</div> </div>
<div class="col-sm-4 m-b-xs"> <div class="col-sm-4 m-b-xs">
@@ -62,16 +51,7 @@
<i class="fa fa-sort"></i> <i class="fa fa-sort"></i>
</span> </span>
</th> </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 <th
{{sorterTh $url "createdTime" .sorter}} {{sorterTh $url "createdTime" .sorter}}
> >
@@ -82,8 +62,6 @@
<i class="fa fa-sort"></i> <i class="fa fa-sort"></i>
</span> </span>
</th> </th>
<th width="30">
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -100,21 +78,9 @@
{{.User.Username}} {{.User.Username}}
</a> </a>
</td> </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> <td>
{{.CreatedTime|datetime}} {{.CreatedTime|datetime}}
</td> </td>
<td>
<a href="#" class="btn btn-default">Send Email</a>
</td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
@@ -123,22 +89,11 @@
<footer class="panel-footer"> <footer class="panel-footer">
<div class="row"> <div class="row">
<div class="col-sm-4 hidden-xs"> <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"> <button class="btn btn-sm btn-default">
Apply Action1
</button>
<button class="btn btn-sm btn-default">
Action2
</button> </button>
</div> </div>
<div class="col-sm-4 text-center"> <div class="col-sm-4 text-center">
@@ -1,21 +1,21 @@
{{template "admin/top.html" .}} {{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="row">
<div class="col-sm-6"> <div class="col-sm-6">
<form id="add_user_form"> <form id="data_form">
<section class="panel panel-default"> <section class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
<label>Recommend Tags</label> <label>mongodump path</label>
<input type="text" class="form-control" name="recommendTags" value="{{.recommendTags}}"> <input type="text" class="form-control" name="mongodumpPath" value="{{.str.mongodumpPath}}" placeholder="">
Split by ',' Please input the bin mongodump's absolute path
</div> </div>
<div class="form-group"> <div class="form-group">
<label>New Tags</label> <label>mongorestore path</label>
<input type="text" class="form-control" name="newTags" value="{{.newTags}}"> <input type="text" class="form-control" name="mongorestorePath" value="{{.str.mongorestorePath}}" placeholder="">
Split by ',' Please input the bin mongorestore's absolute path
</div> </div>
</div> </div>
@@ -32,14 +32,14 @@
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script> <script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
<script> <script>
$(function() { $(function() {
init_validator("#add_user_form"); init_validator("#data_form");
$("#submit").click(function(e){ $("#submit").click(function(e){
e.preventDefault(); e.preventDefault();
var t = this; var t = this;
if($("#add_user_form").valid()) { if($("#data_form").valid()) {
$(t).button('loading'); $(t).button('loading');
ajaxPost("/adminSetting/doBlogTag", getFormJsonData("add_user_form"), function(ret){ ajaxPost("/adminSetting/mongodb", getFormJsonData("data_form"), function(ret){
$(t).button('reset') $(t).button('reset')
if(!ret.Ok) { if(!ret.Ok) {
art.alert(ret.Msg) art.alert(ret.Msg)
+115
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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" .}}
+2 -2
View File
@@ -9,11 +9,11 @@
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
<label>Demo Username</label> <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>
<div class="form-group"> <div class="form-group">
<label>Demo Password</label> <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>
</div> </div>
+48
View File
@@ -0,0 +1,48 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Open Register</h3></div>
<div class="row">
<div class="col-sm-6">
<form id="form_data">
<section class="panel panel-default">
<div class="panel-body">
<div class="form-group">
<label>Open ?</label>
<input type="checkbox" class="form-control" name="openRegister" {{if .str.openRegister}}checked="checked"{{end}} value="1">
</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("#form_data");
$("#submit").click(function(e){
e.preventDefault();
var t = this;
if($("#form_data").valid()) {
$(t).button('loading');
ajaxPost("/adminSetting/openRegister", getFormJsonData("form_data"), function(ret){
$(t).button('reset')
if(!ret.Ok) {
art.alert(ret.Msg)
} else {
art.tips("Success");
}
});
}
});
});
</script>
{{template "admin/end.html" .}}
+173
View 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" .}}
+1 -1
View File
@@ -45,7 +45,7 @@ $(function() {
ajaxPost("/auth/doRegister", getFormJsonData("add_user_form"), function(ret){ ajaxPost("/auth/doRegister", getFormJsonData("add_user_form"), function(ret){
$(t).button('reset') $(t).button('reset')
if(!ret.Ok) { if(!ret.Ok) {
art.alert(ret.Msg) art.alert(ret.Msg);
} else { } else {
art.tips("Success"); art.tips("Success");
} }
+29 -35
View File
@@ -1,24 +1,18 @@
{{template "admin/top.html" .}} {{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"> <section class="panel panel-default">
<div class="row wrapper"> <div class="row wrapper">
<div class="col-sm-5 m-b-xs"> <div class="col-sm-5 m-b-xs">
<select class="input-sm form-control input-s-sm inline v-middle"> <select class="input-sm form-control input-s-sm inline v-middle">
<option value="0"> <option value="">
Bulk action Bulk action
</option> </option>
<option value="1"> <option value="1">
Delete selected Send Email
</option>
<option value="2">
Bulk edit
</option>
<option value="3">
Export
</option> </option>
</select> </select>
<button class="btn btn-sm btn-default"> <button class="btn btn-sm btn-default bulk-btn">
Apply Apply
</button> </button>
</div> </div>
@@ -90,10 +84,10 @@
{{range .users}} {{range .users}}
<tr> <tr>
<td> <td>
<input type="checkbox" name="post[]" value="2"> <input type="checkbox" class="ck" data-email="{{.Email}}" data-id="{{.UserId.Hex}}">
</td> </td>
<td> <td>
{{.Email}} <a target="_blank" href="/blog/{{.UserId.Hex}}">{{.Email}}</a>
</td> </td>
<td> <td>
{{.Username}} {{.Username}}
@@ -103,15 +97,9 @@
</td> </td>
<td> <td>
{{.CreatedTime|datetime}} {{.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>
<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> </td>
</tr> </tr>
{{end}} {{end}}
@@ -122,32 +110,21 @@
<div class="row"> <div class="row">
<div class="col-sm-4 hidden-xs"> <div class="col-sm-4 hidden-xs">
<select class="input-sm form-control input-s-sm inline v-middle"> <select class="input-sm form-control input-s-sm inline v-middle">
<option value="0"> <option value="">
Bulk action Bulk action
</option> </option>
<option value="1"> <option value="1">
Delete selected Send Email
</option>
<option value="2">
Bulk edit
</option>
<option value="3">
Export
</option> </option>
</select> </select>
<button class="btn btn-sm btn-default"> <button class="btn btn-sm btn-default bulk-btn">
Apply Apply
</button> </button>
</div> </div>
<div class="col-sm-4 text-center">
<small class="text-muted inline m-t-sm m-b-sm"> <div class="col-sm-8 text-right text-center-xs">
showing 20-30 of 50 items
</small>
</div>
<div class="col-sm-4 text-right text-center-xs">
{{set . "url" (urlConcat "/adminUser/index" "sorter" .sorter "keywords" .keywords)}} {{set . "url" (urlConcat "/adminUser/index" "sorter" .sorter "keywords" .keywords)}}
{{template "admin/user/page.html" .}} {{template "admin/user/page.html" .}}
</div> </div>
</div> </div>
</footer> </footer>
@@ -157,6 +134,23 @@
<script> <script>
$(function() { $(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> </script>
+22 -28
View File
@@ -5,32 +5,26 @@
</section> </section>
</section> </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 src="/js/jquery-1.9.0.min.js"></script>
}); <script src="/js/bootstrap.js"></script>
</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>
+49 -66
View File
@@ -1,15 +1,15 @@
{{template "admin/top.html" .}} {{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"> <section class="panel panel-default">
<div class="row m-l-none m-r-none bg-light lter"> <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"> <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"> <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-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> </span>
<a class="clear" href="#"> <a class="clear" href="/adminUser/index">
<span class="h3 block m-t-xs"><strong>52,000</strong></span> <span class="h3 block m-t-xs"><strong>{{.countUser}}</strong></span>
<small class="text-muted text-uc">users</small> <small class="text-muted text-uc">Users</small>
</a> </a>
</div> </div>
<div class="col-sm-6 col-md-3 padder-v b-r b-light lt"> <div class="col-sm-6 col-md-3 padder-v b-r b-light lt">
@@ -17,9 +17,19 @@
<i class="fa fa-circle fa-stack-2x text-warning"></i> <i class="fa fa-circle fa-stack-2x text-warning"></i>
<i class="fa fa-file-o fa-stack-1x text-white"></i> <i class="fa fa-file-o fa-stack-1x text-white"></i>
</span> </span>
<a class="clear" href="#"> <a class="clear" href="javascript:;">
<span class="h3 block m-t-xs"><strong>1312,000</strong></span> <span class="h3 block m-t-xs"><strong>{{.countNote}}</strong></span>
<small class="text-muted text-uc">notes</small> <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> </a>
</div> </div>
</div> </div>
@@ -30,51 +40,10 @@
<h4 class="font-thin padder"> <h4 class="font-thin padder">
Leanote Events Leanote Events
</h4> </h4>
<ul class="list-group"> <ul class="list-group" id="eventsList"></ul>
<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>
</section> </section>
<!--
<section class="panel panel-default"> <section class="panel panel-default">
<form> <form>
<textarea class="form-control no-border" rows="3" placeholder="Suggestions to leanote"></textarea> <textarea class="form-control no-border" rows="3" placeholder="Suggestions to leanote"></textarea>
@@ -84,23 +53,37 @@
POST POST
</button> </button>
<ul class="nav nav-pills nav-sm"> <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> </footer>
</section> </section>
-->
{{template "admin/footer.html" .}} {{template "admin/footer.html" .}}
<script>
$(function() {
// leanote动态
var url = "http://leanote.com/blog/listCateLatest/5446753cfacfaa4f56000000";
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>';
}
$.ajax({
dataType: "jsonp",//跨域访问 dataType 必须是jsonp 类型。
url: url,
type: "GET",
jsonp: "callback",
jsonpCallback: "jsonpCallback",
success: function(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" .}} {{template "admin/end.html" .}}
+114 -43
View File
@@ -1,8 +1,21 @@
<nav class="nav-primary hidden-xs"> <nav class="nav-primary hidden-xs">
<ul class="nav" id="nav"> <ul class="nav" id="nav">
<li class="active" id="adminUserNav"> <li class="active">
<a href="index.html"> <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"> <i class="fa fa-users icon">
<b class="bg-danger"> <b class="bg-danger">
</b> </b>
@@ -22,7 +35,7 @@
<li> <li>
<a href="/adminUser/index"> <a href="/adminUser/index">
<span> <span>
List Users
</span> </span>
</a> </a>
</li> </li>
@@ -47,11 +60,65 @@
</span> </span>
</a> </a>
</li> </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"> <li id="adminSettingNav">
<a href="#layout"> <a href="#layout">
<i class="fa fa-cog icon"> <i class="fa fa-cog icon">
<b class="bg-warning"> <b class="bg-info">
</b> </b>
</i> </i>
<span class="pull-right"> <span class="pull-right">
@@ -66,60 +133,64 @@
</a> </a>
<ul class="nav lt"> <ul class="nav lt">
<li> <li>
<a href="layout-c.html"> <a href="/admin/t?t=setting/demo">
<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">
<span> <span>
Demo User Demo User
</span> </span>
</a> </a>
</li> </li>
<li>
<a href="/admin/t?t=setting/openRegister">
<span>
Open Register
</span>
</a>
</li>
<li>
<a href="/admin/t?t=setting/shareNote">
<span>
Register Share Note
</span>
</a>
</li>
</ul> </ul>
</li> </li>
<li> <li>
<a href="#layout"> <a href="#">
<i class="fa fa-columns icon"> <i class="fa fa-columns icon">
<b class="bg-warning"> <b class="bg-success">
</b> </b>
</i> </i>
<span class="pull-right">
<i class="fa fa-angle-down text">
</i>
<i class="fa fa-angle-up text-active">
</i>
</span>
<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> </span>
</a> </a>
</li> </li>
</ul> </ul>
</li>
</ul>
</nav> </nav>
+26 -9
View File
@@ -26,22 +26,32 @@
<ul class="nav navbar-nav navbar-right m-n hidden-xs nav-user"> <ul class="nav navbar-nav navbar-right m-n hidden-xs nav-user">
<li class="hidden-xs"> <li class="hidden-xs">
<a href="/index" class="dk"> <a href="http://leanote.com" class="dk" target="_blank">
Index Leanote Home
</a> </a>
</li> </li>
<li class="hidden-xs"> <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 My Note
</a> </a>
</li> </li>
<li class="hidden-xs"> <li class="hidden-xs">
<a href="/blog/admin" class="dk"> <a href="/blog/admin" class="dk" target="_blank">
Blog My Blog
</a> </a>
</li> </li>
<li class="hidden-xs"> <li class="hidden-xs">
<a href="/blog/admin" class="dk"> <a href="/logout" class="dk">
Logout Logout
</a> </a>
</li> </li>
@@ -52,6 +62,7 @@
<!-- .aside --> <!-- .aside -->
<aside class="bg-light lter b-r aside-md hidden-print hidden-xs" id="nav"> <aside class="bg-light lter b-r aside-md hidden-print hidden-xs" id="nav">
<section class="vbox"> <section class="vbox">
<!--
<header class="header bg-primary lter text-center clearfix"> <header class="header bg-primary lter text-center clearfix">
<div class="btn-group"> <div class="btn-group">
<div class="hidden-nav-xs"> <div class="hidden-nav-xs">
@@ -61,6 +72,8 @@
</div> </div>
</div> </div>
</header> </header>
-->
<section class="w-f scrollable"> <section class="w-f scrollable">
<div class="slim-scroll" data-height="auto" data-disable-fade-out="true" <div class="slim-scroll" data-height="auto" data-disable-fade-out="true"
data-distance="0" data-size="5px" data-color="#333333"> data-distance="0" data-size="5px" data-color="#333333">
@@ -69,14 +82,17 @@
<!-- / nav --> <!-- / nav -->
</div> </div>
</section> </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"> <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 class="fa fa-angle-left text">
</i> </i>
<i class="fa fa-angle-right text-active"> <i class="fa fa-angle-right text-active">
</i> </i>
</a> </a>
-->
</footer> </footer>
</section> </section>
</aside> </aside>
@@ -84,7 +100,7 @@
<section id="content"> <section id="content">
<section class="vbox"> <section class="vbox">
<section class="scrollable padder"> <section class="scrollable padder">
<!-- 导航 --> <!-- 导航
<ul class="breadcrumb no-border no-radius b-b b-light pull-in"> <ul class="breadcrumb no-border no-radius b-b b-light pull-in">
<li> <li>
<a href="index.html"> <a href="index.html">
@@ -102,5 +118,6 @@
Components Components
</li> </li>
</ul> </ul>
-->
<!-- 主要内容区 --> <!-- 主要内容区 -->
+1 -3
View File
@@ -10,10 +10,8 @@
</div> </div>
<div class="desc"> <div class="desc">
{{.userBlog.AboutMe | raw}} {{.userBlog.AboutMe | raw}}
</div>
<!-- comment --> </div>
{{template "blog/comment.html" .}}
</div> </div>
</div> </div>
</div> </div>
+187 -1
View File
@@ -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> <div id="disqus_thread"></div>
<!-- comment --> <!-- comment -->
<script type="text/javascript"> <script type="text/javascript">
+18 -24
View File
@@ -4,57 +4,51 @@
<div class="col-md-4"> <div class="col-md-4">
<h3>{{msg . "blogNavs"}}</h3> <h3>{{msg . "blogNavs"}}</h3>
<ul> <ul>
<li><a href="/blog/{{$userId}}">{{msg . "home"}}</a></li> <li><a href="{{$.blogUrl}}/{{$.userInfo.Username}}">{{msg . "home"}}</a></li>
{{range .notebooks}} {{range .notebooks}}
<li> <li>
<a href="/blog/{{$userId}}/{{.NotebookId.Hex}}">{{.Title}}</a> <a href="{{$.cateUrl}}/{{.NotebookId.Hex}}">{{.Title}}</a>
</li> </li>
{{end}} {{end}}
<li><a href="/blog/aboutMe/{{$userId}}">{{msg . "aboutMe"}}</a></li> <li><a href="{{$.aboutMeUrl}}">{{msg . "aboutMe"}}</a></li>
</ul> </ul>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<h3>{{msg . "latestPosts"}}</h3> <h3>{{msg . "latestPosts"}}</h3>
<ul> <ul>
{{range .recentBlogs}} {{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}} {{end}}
</ul> </ul>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<h3>{{msg . "quickLinks"}}</h3> <h3>{{msg . "quickLinks"}}</h3>
<ul> <ul>
<li><a href="/note">{{msg . "myNote"}}</a></li> <li><a href="{{$.noteUrl}}">{{msg . "myNote"}}</a></li>
<li><a href="/login">{{msg . "login"}}</a></li> <li><a href="{{$.siteUrl}}/login">leanote {{msg . "login"}}</a></li>
<li><a href="http://leanote.com" target="_blank">leanote</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> <li><a href="https://github.com/leanote/leanote" target="_blank">leanote github</a></li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
<script src="/js/jquery-1.9.0.min.js"></script> <script src="{{$.siteUrl}}/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap-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> <script>
$(function() {
/*
$("#searchInput").click(function() {
$("#search").width("130px");
$("#searchInput").width("100px");
});
$("#searchInput").blur(function() {
$("#search").width(0);
$("#searchInput").width(0);
});
*/
});
// 搜索 // 搜索
function search(e) { function search(e) {
var key = $("#searchInput").val(); var key = $("#searchInput").val();
if(!key) { if(!key) {
location.href = "/blog/" + UserInfo.Username; location.href = "{{$.searchUrl}}";
} else { } 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 += '<input name="key" value="' + key + '" />';
tpl += "</form"; tpl += "</form";
$(tpl).submit(); $(tpl).submit();
+28 -12
View File
@@ -10,10 +10,15 @@
<title>{{.title}}</title> <title>{{.title}}</title>
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link href="/css/bootstrap.css" rel="stylesheet"> <link href="{{.siteUrl}}/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"> {{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> <script>
function log(o) { function log(o) {
} }
@@ -30,7 +35,7 @@ function log(o) {
{{$userId := .userBlog.UserId.Hex}} <!-- 可不要了 --> {{$userId := .userBlog.UserId.Hex}} <!-- 可不要了 -->
{{$username := .userInfo.Username}} {{$username := .userInfo.Username}}
<h1> <h1>
<a href="/blog/{{$username}}" id="logo"> <a href="{{.indexUrl}}" id="logo">
{{if .userBlog.Logo}} {{if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" title="{{.userBlog.Title}}"/> <img src="{{.userBlog.Logo}}" title="{{.userBlog.Title}}"/>
{{else}} {{else}}
@@ -54,23 +59,29 @@ function log(o) {
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </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>
<div class="navbar-collapse collapse"> <div class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
{{$navNotebookId := .notebookId}} {{$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}} {{range .notebooks}}
{{$notebookId := .NotebookId.Hex}} {{$notebookId := .NotebookId.Hex}}
<li class="{{if $navNotebookId}}{{if eq $navNotebookId $notebookId}}active{{else}}{{end}}{{end}}"> <li class="{{if $navNotebookId}}{{if eq $navNotebookId $notebookId}}active{{else}}{{end}}{{end}}">
<a href="/blog/{{$username}}/{{$notebookId}}" <a href="{{$.cateUrl}}/{{$notebookId}}"
>{{.Title}}</a> >{{.Title}}</a>
</li> </li>
{{end}} {{end}}
<li class="{{if .aboutMe}}active{{end}}"><a href="/blog/aboutMe/{{$username}}">{{msg . "aboutMe"}}</a></li> <li class="{{if .aboutMe}}active{{end}}"><a href="{{.aboutMeUrl}}">{{msg . "aboutMe"}}</a></li>
{{if .isMe}} <!-- 同源上传logo -->
<li class="{{if .set}}active{{end}}"><a href="/blog/set">{{msg . "blogSet"}}</a></li> <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="/note" >{{msg . "myNote"}}</a></li> <li><a href="{{$.noteUrl}}" class="is-me {{if not .isMe}}hide{{end}}">{{msg . "myNote"}}</a></li>
{{end}}
</ul> </ul>
<form class="navbar-form navbar-right" id="search" onsubmit="search(event);return false;"> <form class="navbar-form navbar-right" id="search" onsubmit="search(event);return false;">
<div class="input-group"> <div class="input-group">
@@ -85,4 +96,9 @@ function log(o) {
<script> <script>
var UserInfo = {UserId: "{{$userId}}", Email: "{{.userInfo.Email}}", Username: "{{.userInfo.Username}}"}; 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> </script>
+14 -9
View File
@@ -3,7 +3,7 @@
<div id="postsContainer"> <div id="postsContainer">
<div class="container"> <div class="container">
{{if .notebookId}} {{if .notebookId}}
<h2>{{msg . "blogClass"}}: {{.notebook.Title}}</h2> <h2>{{msg . "blogClass"}} - {{.notebook.Title}}</h2>
{{end}} {{end}}
</div> </div>
<div id="posts"> <div id="posts">
@@ -11,30 +11,35 @@
{{range .blogs}} {{range .blogs}}
<div class="each-post"> <div class="each-post">
<div class="title"> <div class="title">
<a href="/blog/view/{{.NoteId.Hex}}" title="{{msg $G "fullBlog"}}"> <a href="{{$.viewUrl}}/{{.NoteId.Hex}}" title="{{msg $ "fullBlog"}}">
{{.Title}} {{.Title}}
</a> </a>
</div> </div>
<div class="created-time"> <div class="created-time">
<i class="fa fa-bookmark-o" style="color: #666"></i> <i class="fa fa-bookmark-o"></i>
{{if .Tags}} {{if .Tags}}
{{blogTags .Tags}} {{blogTags $ .Tags}}
{{else}} {{else}}
{{msg $G "noTag"}} {{msg $ "noTag"}}
{{end}} {{end}}
| |
<i class="fa fa-calendar" style="color: #666"></i> {{msg $G "updatedTime"}} {{.UpdatedTime | datetime}} | <i class="fa fa-calendar"></i> {{msg $ "updatedTime"}} {{.UpdatedTime | datetime}} |
<i class="fa fa-calendar" style="color: #666"></i> {{msg $G "createdTime"}} {{.CreatedTime | datetime}} <i class="fa fa-calendar"></i> {{msg $ "createdTime"}} {{.CreatedTime | datetime}}
</div> </div>
<div class="desc"> <div class="desc">
{{.Content | raw}} {{.Content | raw}}
</div> </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> </div>
{{end}} {{end}}
<!-- 分页 --> <!-- 分页 -->
<ul class="pager"> <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> </ul>
</div> </div>
</div> </div>
+9 -9
View File
@@ -2,38 +2,38 @@
<div id="postsContainer"> <div id="postsContainer">
<div class="container"> <div class="container">
<h2>搜索 {{.key}} </h2> <h2>{{msg . "search"}} - {{.key}} </h2>
</div> </div>
<div id="posts"> <div id="posts">
{{range .blogs}} {{range .blogs}}
<div class="each-post"> <div class="each-post">
<div class="title"> <div class="title">
<a href="/blog/view/{{.NoteId.Hex}}" title="全文"> <a href="{{$.viewUrl}}/{{.NoteId.Hex}}" title="{{msg $ "fullBlog"}}">
{{.Title}} {{.Title}}
</a> </a>
</div> </div>
<div class="created-time"> <div class="created-time">
<i class="fa fa-bookmark-o" style="color: #666"></i> <i class="fa fa-bookmark-o"></i>
{{if .Tags}} {{if .Tags}}
{{blogTags .Tags}} {{blogTags $ .Tags}}
{{else}} {{else}}
无 {{msg $ "noTag"}}
{{end}} {{end}}
| |
<i class="fa fa-calendar" style="color: #666"></i> 更新 {{.UpdatedTime | datetime}} | <i class="fa fa-calendar"></i> {{msg $ "updatedTime"}} {{.UpdatedTime | datetime}} |
<i class="fa fa-calendar" style="color: #666"></i> 创建 {{.CreatedTime | datetime}} <i class="fa fa-calendar"></i> {{msg $ "createdTime"}} {{.CreatedTime | datetime}}
</div> </div>
<div class="desc"> <div class="desc">
{{.Content | raw}} {{.Content | raw}}
</div> </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> </div>
{{end}} {{end}}
{{if not .blogs }} {{if not .blogs }}
<div class="each-post"> <div class="each-post">
无 {{msg . "none"}}
</div> </div>
{{end}} {{end}}
</div> </div>
+75 -50
View File
@@ -1,11 +1,12 @@
{{template "Blog/header.html" .}} {{template "Blog/header.html" .}}
<!-- --> <!-- set页面不是自定义域名和二级域名页 -->
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" type="text/css"> <link rel="stylesheet" href="{{.siteUrl}}/tinymce/skins/custom/skin.min.css" type="text/css">
<style> <style>
.tab-pane { .tab-pane {
padding-top: 10px; padding-top: 10px;
} }
</style> </style>
<div id="postsContainer"> <div id="postsContainer">
<div id="posts"> <div id="posts">
@@ -20,9 +21,18 @@
<div class="tab-pane" id="styleInfo"> <div class="tab-pane" id="styleInfo">
<form class="form-horizontal" role="form"> <form class="form-horizontal" role="form">
<div class="form-group"> <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"> <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" value="blog_default"
{{if not .userBlog.Style}} {{if not .userBlog.Style}}
checked="checked" checked="checked"
@@ -31,15 +41,23 @@
checked="checked" checked="checked"
{{end}} {{end}}
{{end}}> {{end}}>
{{msg . "default"}} </label> {{msg . "default"}}
<label><input type="radio" name="Style" </label>
<label>
<img class="preview" src="{{$.siteUrl}}/images/blog/theme/elegent.png" />
<input type="radio" name="Style"
value="blog_daqi" value="blog_daqi"
{{if eq .userBlog.Style "blog_daqi"}}checked="checked"{{end}}> {{if eq .userBlog.Style "blog_daqi"}}checked="checked"{{end}}>
{{msg . "elegant"}}</label> {{msg . "elegant"}}
<label><input type="radio" name="Style" </label>
<label>
<img class="preview" src="{{$.siteUrl}}/images/blog/theme/left_nav_fix.png" />
<input type="radio" name="Style"
value="blog_left_fixed" value="blog_left_fixed"
{{if eq .userBlog.Style "blog_left_fixed"}}checked="checked"{{end}}> {{if eq .userBlog.Style "blog_left_fixed"}}checked="checked"{{end}}>
{{msg . "navFixed"}}</label> {{msg . "navFixed"}}
</label>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
@@ -47,26 +65,50 @@
<span class="msg"></span> <span class="msg"></span>
</div> </div>
</div> </div>
</div>
</form> </form>
</div> </div>
<div class="tab-pane" id="commentInfo"> <div class="tab-pane" id="commentInfo">
<form class="form-horizontal" role="form"> <form class="form-horizontal" role="form">
<div class="form-group"> <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"> <div class="col-sm-10">
<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" <input type="checkbox" id="CanComment" name="CanComment"
{{if .userBlog.CanComment}}checked="checked"{{end}} > {{if .userBlog.CanComment}}checked="checked"{{end}} > {{msg . "openComment"}}
</label>
<br /> <br />
{{msg . "commentSys"}}
<div id="disqusSet"> <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" <label for="DisqusId">Disqus Id</label> <input type="text"
class="form-control" style="display: inline; width: 50%" class="form-control" style="display: inline; width: 50%"
id="DisqusId" name="DisqusId" id="DisqusId" name="DisqusId"
value="{{if .userBlog.DisqusId}}{{.userBlog.DisqusId}}{{else}}leanote{{end}}"> value="{{if .userBlog.DisqusId}}{{.userBlog.DisqusId}}{{else}}leanote{{end}}">
<br /> <br />
{{msg . "disqusHelp"}} {{msg . "disqusHelp"}}
<a target="_blank" href="http://leanote.com/blog/view/52db8463e01c530ef8000001">{{msg . "needHelp"}}</a>) <a target="_blank" href="http://leanote.com/blog/view/52db8463e01c530ef8000001">{{msg . "needHelp"}}</a>
</div>
</div>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -75,9 +117,9 @@
<span class="msg"></span> <span class="msg"></span>
</div> </div>
</div> </div>
</div>
</form> </form>
</div> </div>
<div class="tab-pane active" id="baseInfo"> <div class="tab-pane active" id="baseInfo">
<div class="form-horizontal" role="form" id="userBlogForm"> <div class="form-horizontal" role="form" id="userBlogForm">
<div class="form-group"> <div class="form-group">
@@ -101,7 +143,7 @@
<div class="col-sm-10"> <div class="col-sm-10">
<input type="hidden" name="Logo" id="Logo" <input type="hidden" name="Logo" id="Logo"
value="{{.userBlog.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" enctype="multipart/form-data" target="logoTarget"
onsubmit="inProgress()"> onsubmit="inProgress()">
<input type="file" class="form-control" id="logo2" name="file" <input type="file" class="form-control" id="logo2" name="file"
@@ -126,7 +168,7 @@
<div class="col-sm-10"> <div class="col-sm-10">
<input type="text" class="form-control" id="SubTitle" <input type="text" class="form-control" id="SubTitle"
name="SubTitle" value="{{.userBlog.SubTitle}}" name="SubTitle" value="{{.userBlog.SubTitle}}"
placeholder="eg: leanote, {{msg $ "moto"}}"> placeholder="eg: leanote, Not Just A Notebook">
</div> </div>
</div> </div>
@@ -158,11 +200,11 @@
{{template "Blog/footer.html" .}} {{template "Blog/footer.html" .}}
<script src="/js/common-min.js"></script> <script src="{{.siteUrl}}/js/common-min.js"></script>
<script type="text/javascript" src="{{.siteUrl}}/tinymce/tinymce.min.js"></script>
<script type="text/javascript" src="/tinymce/tinymce.min.js"></script>
<script> <script>
var urlPrefix = "{{.siteUrl}}";
$(function() { $(function() {
tinymce.init({ tinymce.init({
selector : "#AboutMe", selector : "#AboutMe",
@@ -197,12 +239,19 @@ $(function() {
$("#CanComment").click(function() { $("#CanComment").click(function() {
if ($(this).is(":checked")) { if ($(this).is(":checked")) {
$("#commentSet").show();
} else {
$("#commentSet").hide();
}
});
$("input[name='commentType']").click(function() {
if ($("input[name='commentType']:checked").val() == "disqus") {
$("#disqusSet").show(); $("#disqusSet").show();
} else { } else {
$("#disqusSet").hide(); $("#disqusSet").hide();
} }
}); });
$("#CanComment").trigger("click").trigger("click"); // 恶心的想法
// 基本设置 // 基本设置
@@ -219,7 +268,7 @@ $(function() {
$("#blogDesc").html(data.SubTitle); $("#blogDesc").html(data.SubTitle);
$("#logo").html(data.Title); $("#logo").html(data.Title);
if(data.Logo) { if(data.Logo) {
$("#logo").html(t('<img src="?" />', data.Logo)); $("#logo").html(t('<img src="?" />', urlPrefix + "/" + data.Logo));
} }
}, this); }, this);
}); });
@@ -228,12 +277,14 @@ $(function() {
e.preventDefault(); e.preventDefault();
var data = { var data = {
CanComment : $("#CanComment").is(":checked"), CanComment : $("#CanComment").is(":checked"),
CommentType: $("input[name='commentType']:checked").val(),
DisqusId : $("#DisqusId").val(), DisqusId : $("#DisqusId").val(),
} }
post("/blog/setUserBlogComment", data, function(ret) { post("/blog/setUserBlogComment", data, function(ret) {
showMsg2($("#commentInfo .msg"), "{{msg . "saveSuccess"}}", 2000); showMsg2($("#commentInfo .msg"), "{{msg . "saveSuccess"}}", 2000);
}, this); }, this);
}); });
// 主题 // 主题
$("#styleInfo .btn-success").click(function(e) { $("#styleInfo .btn-success").click(function(e) {
e.preventDefault(); e.preventDefault();
@@ -256,7 +307,7 @@ function inProgress() {
function uploadFinish(ret) { function uploadFinish(ret) {
if (ret) { if (ret) {
if (ret.resultCode == '1') { 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); $("#Logo").val(ret.filename);
return; return;
} }
@@ -266,32 +317,6 @@ function uploadFinish(ret) {
// 上传出错 // 上传出错
alert("上传出错"); 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> </script>
</div> </div>
</body> </body>
+42 -56
View File
@@ -1,6 +1,5 @@
{{template "Blog/header.html" .}} {{template "Blog/header.html" .}}
<!-- -->
<div id="postsContainer"> <div id="postsContainer">
<div id="posts"> <div id="posts">
<div class="each-post"> <div class="each-post">
@@ -8,15 +7,30 @@
{{.blog.Title}} {{.blog.Title}}
</div> </div>
<div class="created-time"> <div class="created-time">
<i class="fa fa-bookmark-o" style="color: #666"></i> <i class="fa fa-bookmark-o"></i>
{{if .blog.Tags}} {{if .blog.Tags}}
{{blogTags .blog.Tags}} {{blogTags $ .blog.Tags}}
{{else}} {{else}}
{{msg . "noTag"}} {{msg . "noTag"}}
{{end}} {{end}}
| |
<i class="fa fa-calendar" style="color: #666"></i> {{msg . "updatedTime"}} {{.blog.UpdatedTime | datetime}} | <i class="fa fa-calendar"></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 . "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}}
&nbsp;
<i class="fa fa-bookmark-o" style="color: #666"></i>
{{blogTags $ .blog.Tags}}
{{end}}
</div> </div>
<div class="desc" id="content"> <div class="desc" id="content">
@@ -33,75 +47,41 @@
{{else}} {{else}}
{{.blog.Content | raw}} {{.blog.Content | raw}}
{{end}} {{end}}
<div id="desc" class="hide">{{.blog.Desc}}</div>
</div> </div>
<!-- comment --> <!-- comment -->
{{template "blog/comment.html" .}} {{template "blog/comment.html" .}}
</div> </div>
</div> </div>
</div> </div>
{{template "Blog/footer.html" .}} {{template "Blog/footer.html" .}}
{{template "Blog/highlight.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="blogNav">
<div id="blogNavNav"> <div id="blogNavNav">
<i class="fa fa-align-justify" title="文档导航"></i> <i class="fa fa-align-justify" title="文档导航"></i>
<span>{{msg . "blogNav"}}</span> <span>{{msg . "blogNav"}}</span>
</div> </div>
<div id="blogNavContent" style="max-width: 200px"> <div id="blogNavContent">
</div> </div>
</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 }} {{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.Converter.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Sanitizer.js"></script> <script src="/public/mdeditor/editor/pagedown/Markdown.Sanitizer.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Editor.js"></script> <script src="/public/mdeditor/editor/pagedown/Markdown.Editor.js"></script>
@@ -127,13 +107,19 @@ prettyPrint();
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"wmd-preview"]); MathJax.Hub.Queue(["Typeset",MathJax.Hub,"wmd-preview"]);
initNav(); initNav();
weixin();
</script> </script>
{{else}} {{else}}
<script> <script>
$(function() { $(function() {
initNav(); initNav();
weixin();
}); });
</script> </script>
{{end}} {{end}}
<!--google+
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"> {lang: 'zh-CN'} </script>
-->
</body> </body>
</html> </html>
+1 -1
View File
@@ -6,7 +6,7 @@
<section id="box"> <section id="box">
<div> <div>
<div> <div>
<h1 class="h text-white animated fadeInDownBig">404</h1> <h1 class="h text-white animated fadeInDownBig">500</h1>
</div> </div>
<div id="errorBox"> <div id="errorBox">
<p class="error-info"> <p class="error-info">
+48 -6
View File
@@ -22,6 +22,51 @@ function log(o) {
</head> </head>
<body> <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 id="headerContainer" style="background-color:#fff" class="navbar-fixed-top">
<div class="container" style="clearfix" id="header"> <div class="container" style="clearfix" id="header">
<div class="pull-left"> <div class="pull-left">
@@ -35,7 +80,7 @@ function log(o) {
<div class="pull-right" id="loginBtns"> <div class="pull-right" id="loginBtns">
{{if .userInfo.Email}} {{if .userInfo.Email}}
{{msg . "hi"}}, {{.userInfo.Username}} {{msg . "hi"}}, {{.userInfo.Username}}
<a href="/note">{{msg . "myNote"}}</a> <a href="{{$.noteUrl}}">{{msg . "myNote"}}</a>
<a href="/logout">{{msg . "logout"}}</a> <a href="/logout">{{msg . "logout"}}</a>
{{else}} {{else}}
<a href="/login">{{msg . "login"}}</a> <a href="/login">{{msg . "login"}}</a>
@@ -47,12 +92,9 @@ function log(o) {
<ul id="blogNav" class="pull-right"> <ul id="blogNav" class="pull-right">
<li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li> <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#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 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;"> <li style="position: relative; margin-right: 3px;">
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a> <a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
<div style="position: absolute; <div style="position: absolute;
@@ -64,6 +106,6 @@ function log(o) {
border-radius: 9px;"></div> border-radius: 9px;"></div>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
-->
+38 -5
View File
@@ -1,4 +1,13 @@
{{template "home/header_box.html" .}} {{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"> <section id="box" class="animated fadeInUp">
<!-- <!--
<div> <div>
@@ -11,6 +20,7 @@
<div id="boxHeader">{{msg . "login"}}</div> <div id="boxHeader">{{msg . "login"}}</div>
<form> <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"> <div class="form-group">
<label class="control-label">{{msg . "usernameOrEmail"}}</label> <label class="control-label">{{msg . "usernameOrEmail"}}</label>
<input type="text" class="form-control" id="email" name="email" value="{{.email}}"> <input type="text" class="form-control" id="email" name="email" value="{{.email}}">
@@ -19,13 +29,20 @@
<label class="control-label">{{msg . "password"}}</label> <label class="control-label">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd"> <input type="password" class="form-control" id="pwd" name="pwd">
</div> </div>
<div id="captchaContainer">
</div>
<div class="clearfix"> <div class="clearfix">
<a href="/findPassword" class="pull-right m-t-xs"><small>{{msg . "forgetPassword"}}</small></a> <a href="/findPassword" class="pull-right m-t-xs"><small>{{msg . "forgetPassword"}}</small></a>
<button id="loginBtn" class="btn btn-success">{{msg . "login"}}</button> <button id="loginBtn" class="btn btn-success">{{msg . "login"}}</button>
</div> </div>
<!--
<div class="line line-dashed"></div> <div class="line line-dashed"></div>
<a href="#" id="github" class="btn btn-github btn-block m-b-sm"><i class="fa fa-github pull-left"></i>{{msg . "use"}} Github</a> <a href="#" id="github" class="btn btn-github btn-block m-b-sm"><i class="fa fa-github pull-left"></i>{{msg . "use"}} Github</a>
-->
<div class="line line-dashed"></div> <div class="line line-dashed"></div>
@@ -33,9 +50,8 @@
{{if .openRegister}} {{if .openRegister}}
<a href="/register" class="btn btn-default btn-block">{{msg . "register"}}</a> <a href="/register" class="btn btn-default btn-block">{{msg . "register"}}</a>
{{end}}
{{msg . "or"}} {{msg . "or"}}
{{end}}
<a id="loginBtn" href="/demo" class="btn btn-default btn-block">{{msg . "try"}}</a> <a id="loginBtn" href="/demo" class="btn btn-default btn-block">{{msg . "try"}}</a>
</form> </form>
@@ -57,6 +73,12 @@
<script> <script>
$(function() { $(function() {
var needCaptcha = {{.needCaptcha}};
if(needCaptcha){
$("#captchaContainer").html($("#tCaptcha").html());
}
$("#email").focus(); $("#email").focus();
if($("#email").val()) { if($("#email").val()) {
$("#pwd").focus(); $("#pwd").focus();
@@ -74,6 +96,7 @@ $(function() {
e.preventDefault(); e.preventDefault();
var email = $("#email").val(); var email = $("#email").val();
var pwd = $("#pwd").val(); var pwd = $("#pwd").val();
var captcha = $("#captcha").val()
if(!email) { if(!email) {
showMsg("{{msg . "inputUsername"}}", "email"); showMsg("{{msg . "inputUsername"}}", "email");
return; return;
@@ -87,17 +110,27 @@ $(function() {
return; return;
} }
} }
if(needCaptcha && !captcha) {
showMsg("{{msg . "inputCaptcha"}}", "captcha");
return;
}
$("#loginBtn").html("{{msg . "logining"}}...").addClass("disabled"); $("#loginBtn").html("{{msg . "logining"}}...").addClass("disabled");
// hideMsg(); // 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"); $("#loginBtn").html("{{msg . "login"}}").removeClass("disabled");
if(e.Ok) { if(e.Ok) {
$("#loginBtn").html("{{msg . "loginSuccess"}}..."); $("#loginBtn").html("{{msg . "loginSuccess"}}...");
location.href = '/note'; var from = $("#from").val() || "{{.noteUrl}}" || "/note";
location.href = from;
} else { } else {
showMsg(e.Msg, "pwd"); if(e.Item && $.trim($("#captchaContainer").text()) == "") {
$("#captchaContainer").html($("#tCaptcha").html());
needCaptcha = true
}
showMsg(e.Msg);
} }
}); });
}); });
+3 -1
View File
@@ -11,6 +11,7 @@
<div id="boxHeader">{{msg . "register"}}</div> <div id="boxHeader">{{msg . "register"}}</div>
<form> <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"> <div class="form-group">
<label class="control-label" for="email">{{msg . "email"}}</label> <label class="control-label" for="email">{{msg . "email"}}</label>
<input type="text" class="form-control" id="email" name="email"> <input type="text" class="form-control" id="email" name="email">
@@ -103,7 +104,8 @@ $(function() {
$("#registerBtn").html("{{msg . "register"}}").removeClass("disabled"); $("#registerBtn").html("{{msg . "register"}}").removeClass("disabled");
if(e.Ok) { if(e.Ok) {
$("#registerBtn").html("{{msg . "registerSuccessAndRdirectToNote"}}"); $("#registerBtn").html("{{msg . "registerSuccessAndRdirectToNote"}}");
location.href = '/note'; var from = $("#from").val() || "{{.noteUrl}}" || "/note";
location.href = from;
} else { } else {
showMsg(e.Msg, "email"); showMsg(e.Msg, "email");
} }
+102
View 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
View File
@@ -0,0 +1,4 @@
lif------------
e
you can
+142 -314
View File
@@ -3,17 +3,19 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <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="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, {{msg $ "moto"}}"> <meta name="description" content="leanote, Not Just A Notebook">
<title>leanote, {{msg $ "moto"}}</title> <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 --> <!-- 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" /> <link href="css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
<script> <script>
var hash = location.hash; var hash = location.hash;
@@ -27,7 +29,6 @@ document.write(files);
<!-- For Develop writting mod --> <!-- For Develop writting mod -->
<script> <script>
function log(o) { function log(o) {
console.log(o); console.log(o);
@@ -56,34 +57,35 @@ function log(o) {
</div> </div>
<!-- search --> <!-- search -->
<div class="pull-left" id="searchWrap"> <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"> <input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
</form> </form>
</div> </div>
<!-- 全局按钮 --> <!-- 全局按钮 -->
<div class="pull-left" style="" id="newNoteWrap"> <div class="pull-left" style="" id="newNoteWrap">
<!-- 新建笔记 --> <!-- 新建笔记 -->
<div id="newMyNote"> <div id="newMyNote">
<a id="newNoteBtn" title="{{msg . "newNote"}}"> <a id="newNoteBtn" title="{{msg . "newNote"}}">
<i class="fa fa-file-o"></i> <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> </a>
<span class="new-split">|</span> <span class="new-split">|</span>
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}"> <a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
Markdown <span class="new-markdown-text">Markdown</span>
<span class="new-markdown-text-abbr">Md</span>
</a> </a>
<span class="for-split"> - </span> <span class="for-split"> - </span>
<span id="curNotebookForNewNote" notebookId=""></span> <span id="curNotebookForNewNote" notebookId=""></span>
<div class="dropdown" style="display: inline-block"> <div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle" <a class="ios7-a dropdown-toggle"
id="dropdownMenu2" data-toggle="dropdown"> id="listNotebookDropdownMenu" data-toggle="dropdown">
<i class="fa fa-angle-down"></i> <i class="fa fa-angle-down"></i>
</a> </a>
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList"> <div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
<input type="text" placeholder="search notebook" class="form-control" id="searchNotebookForAdd"/> <input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/>
<ul class="clearfix" role="menu" aria-labelledby="dropdownMenu2" id="notebookNavForNewNote"> <ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
</ul> </ul>
</div> </div>
</div> </div>
@@ -93,19 +95,21 @@ function log(o) {
<div id="newSharedNote" style="display: none"> <div id="newSharedNote" style="display: none">
<a id="newSharedNoteBtn"> <a id="newSharedNoteBtn">
<i class="fa fa-file-o"></i> <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> </a>
<span class="new-split">|</span> <span class="new-split">|</span>
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}"> <a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
Markdown <span class="new-markdown-text">Markdown</span>
<span class="new-markdown-text-abbr">Md</span>
</a> </a>
<span class="for-split"> - </span> <span class="for-split"> - </span>
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span> <span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<div class="dropdown" style="display: inline-block"> <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> <i class="fa fa-angle-down"></i>
</a> </a>
<div class="dropdown-menu dropdown-list" style="left: -200px;" > <div class="dropdown-menu dropdown-list" id="searchNotebookForAddShareDropdownList" >
<ul id="notebookNavForNewSharedNote"></ul> <ul id="notebookNavForNewSharedNote"></ul>
</div> </div>
</div> </div>
@@ -118,20 +122,10 @@ function log(o) {
</span> </span>
</div> </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="pull-right" style="margin: 0 10px" id="myProfile">
<div class="dropdown"> <div class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" style="line-height: 60px;"> <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"/>
<img src="images/avatar.png" style="height: 40px; border: 1px solid #ccc" />
-->
<span class="username"> <span class="username">
{{if .userInfo.UsernameRaw}} {{if .userInfo.UsernameRaw}}
{{.userInfo.UsernameRaw}} {{.userInfo.UsernameRaw}}
@@ -143,12 +137,22 @@ function log(o) {
</a> </a>
<ul class="dropdown-menu li-a" role="menu"> <ul class="dropdown-menu li-a" role="menu">
<li role="presentation" id="setInfo"> <li role="presentation" id="setInfo">
<a>
<i class="fa fa-info"></i> <i class="fa fa-info"></i>
{{msg . "accountSetting"}} {{msg . "accountSetting"}}
</a>
</li>
<li role="presentation" id="setAvatarMenu">
<a>
<i class="fa fa-smile-o"></i>
{{msg . "setAvatar"}}
</a>
</li> </li>
<li role="presentation" id="setTheme"> <li role="presentation" id="setTheme">
<a>
<i class="fa fa-sun-o"></i> <i class="fa fa-sun-o"></i>
{{msg . "themeSetting"}} {{msg . "themeSetting"}}
</a>
</li> </li>
<!-- <!--
<li role="presentation" id="yourSuggestions"> <li role="presentation" id="yourSuggestions">
@@ -156,6 +160,22 @@ function log(o) {
{{msg . "yourSuggestions"}} {{msg . "yourSuggestions"}}
</li> </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()"> <li role="presentation" onclick="logout()">
<i class="fa fa-sign-out"></i> <i class="fa fa-sign-out"></i>
{{msg . "logout"}} {{msg . "logout"}}
@@ -164,8 +184,8 @@ function log(o) {
</div> </div>
</div> </div>
<div class="pull-right" style="margin: 0 10px" id="topNav"> <div class="pull-right top-nav" id="myBlog">
<a target="_blank" href="/blog/{{.userInfo.Username}}"> <a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
{{msg . "myBlog"}} {{msg . "myBlog"}}
</a> </a>
</div> </div>
@@ -239,7 +259,7 @@ function log(o) {
</div> </div>
<div class="folderBody"> <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="notebookList"></ul>
<ul class="ztree" id="notebookListForSearch"></ul> <ul class="ztree" id="notebookListForSearch"></ul>
</div> </div>
@@ -254,10 +274,10 @@ function log(o) {
</div> </div>
<ul class="folderBody clearfix" id="tagNav"> <ul class="folderBody clearfix" id="tagNav">
<li><a> <span class="label label-red">{{msg . "red"}}</span></a></li> <li data-tag="red"><a> <span class="label label-red">{{msg . "red"}}</span></a></li>
<li><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li> <li data-tag="blue"><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li>
<li><a> <span class="label label-yellow">{{msg . "yellow"}}</span></a></li> <li data-tag="yellow"><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="green"><a> <span class="label label-green">{{msg . "green"}}</span></a></li>
</ul> </ul>
</div> </div>
@@ -309,9 +329,7 @@ function log(o) {
<div id="noteAndEditor"> <div id="noteAndEditor">
<div id="noteList"> <div id="noteList">
<div class="clearfix" id="notesAndSort" style="position: relative"> <div class="clearfix" id="notesAndSort" style="position: relative">
<div class="pull-left"> <div class="pull-left">
<!-- 我的笔记本 --> <!-- 我的笔记本 -->
<div class="dropdown" id="myNotebookNavForListNav"> <div class="dropdown" id="myNotebookNavForListNav">
<a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown"> <a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">
@@ -370,14 +388,13 @@ function log(o) {
--> -->
</div> </div>
</div> </div>
</div> </div>
<!-- 笔记列表 --> <!-- 笔记列表 -->
<!-- wrap 为了slimScroll --> <!-- wrap 为了slimScroll -->
<div id="noteItemListWrap" style="position: absolute; left: 0; right: 0; top: 41px; bottom: 3px"> <div id="noteItemListWrap">
<div id="noteItemList"> <ul id="noteItemList">
</div> </ul>
</div> </div>
</div> </div>
@@ -389,43 +406,41 @@ function log(o) {
<div id="noteReadContainer"> <div id="noteReadContainer">
<div id="noteReadTop"> <div id="noteReadTop">
<h2 id="noteReadTitle"></h2> <h2 id="noteReadTitle"></h2>
<div class="clearfix"> <div class="clearfix" id="noteReadInfo">
<div class="pull-left">
<i class="fa fa-bookmark-o"></i> <i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span> <span id="noteReadTags"></span>
</div>
<!-- 修改时间 --> <!-- 修改时间 -->
<div class="pull-left" style="margin-left: 10px;">
<i class="fa fa-calendar"></i>{{msg . "update"}} <i class="fa fa-calendar"></i>{{msg . "update"}}
<span id="noteReadUpdatedTime"></span> <span id="noteReadUpdatedTime"></span>
</div>
<!-- 修改时间 --> <!-- 修改时间 -->
<div class="pull-left" style="margin-left: 10px;">
<i class="fa fa-calendar"></i>{{msg . "create"}} <i class="fa fa-calendar"></i>{{msg . "create"}}
<span id="noteReadCreatedTime"></span> <span id="noteReadCreatedTime"></span>
</div> </div>
</div> </div>
</div>
<div id="noteReadContent"> <div id="noteReadContent">
</div> </div>
</div> </div>
</div> </div>
<!-- 遮罩, 为了resize3Columns用 --> <!-- 遮罩, 为了resize3Columns用 -->
<div id="noteMask" <div id="noteMask" class="note-mask"></div>
style="position: absolute; top: 0px; bottom: 0px; right: 0; left: 10px; z-index: -1"></div> <div id="noteMaskForLoading" class="note-mask">
<img src="/images/loading-24.gif"/>
<br />
loading...
</div>
<div id="editorMask"> <div id="editorMask">
该笔记本下空空如也...何不 {{msg . "noNoteNewNoteTips"}}
<br />
<br /> <br />
<div id="editorMaskBtns"> <div id="editorMaskBtns">
<br /> <a class="note">{{msg . "newNote"}}</a>
<a class="note">新建笔记</a> <a class="markdown">{{msg . "newMarkdownNote"}}</a>
<a class="markdown">新建Markdown笔记</a>
</div> </div>
<div id="editorMaskBtnsEmpty"> <div id="editorMaskBtnsEmpty">
Sorry, 这里不能添加笔记的. {{msg . "canntNewNoteTips"}}
</div> </div>
</div> </div>
<div id="noteTop"> <div id="noteTop">
@@ -449,7 +464,9 @@ function log(o) {
<a <a
class="metro-a dropdown-toggle" data-toggle="dropdown" class="metro-a dropdown-toggle" data-toggle="dropdown"
id="addTagTrigger" style="cursor: text; padding-left: 0"> id="addTagTrigger" style="cursor: text; padding-left: 0">
<span class="add-tag-text">
{{msg . "clickAddTag"}} {{msg . "clickAddTag"}}
</span>
</a> </a>
<input type="text" id="addTagInput" /> <input type="text" id="addTagInput" />
<ul class="dropdown-menu" role="menu" id="tagColor"> <ul class="dropdown-menu" role="menu" id="tagColor">
@@ -463,34 +480,34 @@ function log(o) {
</div> </div>
</div> </div>
<ul class="pull-right" id="editorTool"> <ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s" <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"> <li class="dropdown" id="attachDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach"> <a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
<!-- <span class="fa fa-paperclip"></span>
<span class="fa fa-upload"></span>
-->
{{msg . "attachments"}}<span id="attachNum"></span> {{msg . "attachments"}}<span id="attachNum"></span>
</a> </a>
<div class="dropdown-menu" id="attachMenu"> <div class="dropdown-menu" id="attachMenu">
<ul id="attachList"> <ul id="attachList">
</ul> </ul>
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data"> <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"> <a class="btn btn-success btn-choose-file">
Choose File to Upload <i class="fa fa-upload"></i>
<span>Choose File</span>
</a> </a>
<a class="btn btn-default" id="downloadAllBtn"> <a class="btn btn-default" id="downloadAllBtn">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
Download All <span>Download All</span>
</a> </a>
<a class="btn btn-default" id="linkAllBtn"> <a class="btn btn-default" id="linkAllBtn">
<i class="fa fa-link"></i> <i class="fa fa-link"></i>
Link All <span>Link All</span>
</a> </a>
<input type="file" name="file" multiple/> <input type="file" name="file" multiple/>
</div> </div>
@@ -501,9 +518,13 @@ function log(o) {
</li> </li>
<li><a class="ios7-a " id="tipsBtn" <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" <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> </ul>
</div> </div>
@@ -514,7 +535,7 @@ function log(o) {
<div id="editor"> <div id="editor">
<!-- 编辑器 --> <!-- 编辑器 -->
<div id="mceToolbar" style=""> <div id="mceToolbar">
<div id="popularToolbar" <div id="popularToolbar"
style="position: absolute; right: 30px; left: 0"></div> style="position: absolute; right: 30px; left: 0"></div>
<a <a
@@ -554,12 +575,13 @@ function log(o) {
<!-- 为了scroll --> <!-- 为了scroll -->
<div class="clearfix" id="mdEditorPreview"> <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"> <div id="wmd-panel-editor" class="wmd-panel-editor">
<textarea class="wmd-input theme" id="wmd-input" spellcheck="false" tabindex="3"></textarea> <textarea class="wmd-input theme" id="wmd-input" spellcheck="false" tabindex="3"></textarea>
</div> </div>
</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-panel-preview" class="wmd-panel-preview preview-container">
<div id="wmd-preview" class="wmd-preview"></div> <div id="wmd-preview" class="wmd-preview"></div>
</div> </div>
@@ -567,7 +589,8 @@ function log(o) {
</div> </div>
<textarea id="md-section-helper"></textarea> <textarea id="md-section-helper"></textarea>
</div> </div>
<!-- for test --> </div>
</div>
<!-- mdEditor --> <!-- mdEditor -->
<!-- Hidden Popup Modal --> <!-- Hidden Popup Modal -->
@@ -577,7 +600,7 @@ function log(o) {
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="editorDialog-title">操作</h4> <h4 class="modal-title" id="editorDialog-title"></h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -591,17 +614,13 @@ function log(o) {
</div> </div>
<div class="modal-footer"> <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 . "cancel"}}</button>
<button type="button" class="btn btn-primary" id="editorDialog-confirm">确认</button> <button type="button" class="btn btn-primary" id="editorDialog-confirm">{{msg . "confirm"}}</button>
</div> </div>
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
</div>
</div>
<!-- 弹出框 模板 --> <!-- 弹出框 模板 -->
<div class="modal fade bs-modal-sm" id="leanoteDialogRemote" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true"> <div class="modal fade bs-modal-sm" id="leanoteDialogRemote" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
</div> </div>
@@ -644,7 +663,7 @@ function log(o) {
</div> </div>
<input type="hidden" id="toEmail"/> <input type="hidden" id="toEmail"/>
<label for="emailContent">邮件内容</label> <label for="emailContent">邮件内容</label>
<textarea class="form-control" id="emailContent">Hi, 我是李铁, leanote非常好用, 快来注册吧.</textarea> <textarea class="form-control" id="emailContent">Hi, 我是life, leanote非常好用, 快来注册吧.</textarea>
</div> </div>
</form> </form>
</div> </div>
@@ -657,26 +676,15 @@ function log(o) {
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </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 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-dialog modal-sm">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">主题设置</h4> <h4 class="modal-title" class="modalTitle">{{msg . "theme"}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<style>
#themeForm td {
padding: 5px;
text-align: center;
}
#themeForm img {
border: 1px solid #eee;
padding: 2px;
}
</style>
<table id="themeForm"> <table id="themeForm">
<tr> <tr>
<td> <td>
@@ -699,7 +707,6 @@ function log(o) {
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div> </div>
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
@@ -724,17 +731,31 @@ function log(o) {
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
<!-- 图片上传 --> <!-- avatar -->
<div class="modal fade bs-modal-sm" id="imageDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true"> <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-dialog modal-sm">
<div class="modal-content" style="height: 460px;" > <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "uploadImage"}}</h4> <h4 class="modal-title" class="modalTitle">{{msg . "setAvatar"}}</h4>
</div> </div>
<div class="modal-body"> <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>
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
@@ -781,183 +802,27 @@ function log(o) {
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "cancel"}}</button> <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> </div>
<!-- 激活邮件 --> <!-- 激活邮件 -->
<div id="reSendActiveEmailDialog"> <div id="reSendActiveEmailDialog">
<div class="modal-body"> <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"> <span class="text">
<img src="/images/loading-24.gif"/> <img src="/images/loading-24.gif"/>
正在发送邮件到{{.userInfo.Email}}... {{msg . "emailInSending"}} {{.userInfo.Email}}...
</span> </span>
</div> </div>
</div> </div>
<div class="modal-footer"> <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>
<button type="button" class="btn btn-success viewEmailBtn disabled">查看邮件</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> </div>
<div class="hide" id="copyDiv"></div> <div class="hide" id="copyDiv"></div>
@@ -969,7 +834,7 @@ function log(o) {
<script src="js/i18n/msg.{{.locale}}.js"></script> <script src="js/i18n/msg.{{.locale}}.js"></script>
<script src="js/common.js"></script> <script src="js/common.js"></script>
<script> <script>
var UrlPrefix = "http://leanote.com"; // 为了发weibo var UrlPrefix = "{{.siteUrl}}"; // 为了发weibo
var UserInfo = json({{.userInfoJson}}); var UserInfo = json({{.userInfoJson}});
var notebooks = json({{.notebooks}}); var notebooks = json({{.notebooks}});
var shareNotebooks = json({{.shareNotebooks}}); var shareNotebooks = json({{.shareNotebooks}});
@@ -981,12 +846,11 @@ LEA.locale = "{{.locale}}";
</script> </script>
<!-- 渲染view --> <!-- 渲染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/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
<script src="/js/contextmenu/jquery.contextmenu.js"></script> <script src="/js/contextmenu/jquery.contextmenu.js"></script>
<script src="js/app/page.js"></script> <script src="js/jquery-cookie.js"></script>
<script src="tinymce/tinymce.js"></script>
<script src="js/jquery-cookie-min.js"></script>
<script src="js/bootstrap-min.js"></script> <script src="js/bootstrap-min.js"></script>
<script src="js/app/note.js"></script> <script src="js/app/note.js"></script>
<script src="js/app/tag.js"></script> <script src="js/app/tag.js"></script>
@@ -998,13 +862,14 @@ LEA.locale = "{{.locale}}";
Notebook.renderNotebooks(notebooks); Notebook.renderNotebooks(notebooks);
Share.renderShareNotebooks(sharedUserInfos, shareNotebooks); Share.renderShareNotebooks(sharedUserInfos, shareNotebooks);
Note.setNoteCache(noteContentJson);
Note.renderNotes(notes); Note.renderNotes(notes);
if(!isEmpty(notes)) { if(!isEmpty(notes)) {
Note.changeNote(notes[0].NoteId); Note.changeNote(notes[0].NoteId);
} }
Note.setNoteCache(noteContentJson); // Note.chanteNote设置content
Note.renderNoteContent(noteContentJson) // Note.renderNoteContent(noteContentJson)
Tag.renderTagNav(tagsJson); Tag.renderTagNav(tagsJson);
@@ -1012,50 +877,13 @@ Tag.renderTagNav(tagsJson);
initSlimScroll(); initSlimScroll();
</script> </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 --> <!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" /> <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 --> <!-- js version 2.0 use require.js -->
<script src="/js/require.js"></script> <script src="/js/require.js"></script>
<script> <script src="/js/main.js"></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> </script>
</body> </body>
</html> </html>
+142 -318
View File
@@ -3,17 +3,19 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <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="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, {{msg $ "moto"}}"> <meta name="description" content="leanote, Not Just A Notebook">
<title>leanote, {{msg $ "moto"}}</title> <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 --> <!-- 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" /> <link href="css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
<script> <script>
var hash = location.hash; var hash = location.hash;
@@ -27,7 +29,6 @@ document.write(files);
<!-- For Develop writting mod --> <!-- For Develop writting mod -->
<script> <script>
function log(o) { function log(o) {
@@ -56,34 +57,35 @@ function log(o) {
</div> </div>
<!-- search --> <!-- search -->
<div class="pull-left" id="searchWrap"> <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"> <input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
</form> </form>
</div> </div>
<!-- 全局按钮 --> <!-- 全局按钮 -->
<div class="pull-left" style="" id="newNoteWrap"> <div class="pull-left" style="" id="newNoteWrap">
<!-- 新建笔记 --> <!-- 新建笔记 -->
<div id="newMyNote"> <div id="newMyNote">
<a id="newNoteBtn" title="{{msg . "newNote"}}"> <a id="newNoteBtn" title="{{msg . "newNote"}}">
<i class="fa fa-file-o"></i> <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> </a>
<span class="new-split">|</span> <span class="new-split">|</span>
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}"> <a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
Markdown <span class="new-markdown-text">Markdown</span>
<span class="new-markdown-text-abbr">Md</span>
</a> </a>
<span class="for-split"> - </span> <span class="for-split"> - </span>
<span id="curNotebookForNewNote" notebookId=""></span> <span id="curNotebookForNewNote" notebookId=""></span>
<div class="dropdown" style="display: inline-block"> <div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle" <a class="ios7-a dropdown-toggle"
id="dropdownMenu2" data-toggle="dropdown"> id="listNotebookDropdownMenu" data-toggle="dropdown">
<i class="fa fa-angle-down"></i> <i class="fa fa-angle-down"></i>
</a> </a>
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList"> <div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
<input type="text" placeholder="search notebook" class="form-control" id="searchNotebookForAdd"/> <input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/>
<ul class="clearfix" role="menu" aria-labelledby="dropdownMenu2" id="notebookNavForNewNote"> <ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
</ul> </ul>
</div> </div>
</div> </div>
@@ -93,19 +95,21 @@ function log(o) {
<div id="newSharedNote" style="display: none"> <div id="newSharedNote" style="display: none">
<a id="newSharedNoteBtn"> <a id="newSharedNoteBtn">
<i class="fa fa-file-o"></i> <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> </a>
<span class="new-split">|</span> <span class="new-split">|</span>
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}"> <a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
Markdown <span class="new-markdown-text">Markdown</span>
<span class="new-markdown-text-abbr">Md</span>
</a> </a>
<span class="for-split"> - </span> <span class="for-split"> - </span>
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span> <span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<div class="dropdown" style="display: inline-block"> <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> <i class="fa fa-angle-down"></i>
</a> </a>
<div class="dropdown-menu dropdown-list" style="left: -200px;" > <div class="dropdown-menu dropdown-list" id="searchNotebookForAddShareDropdownList" >
<ul id="notebookNavForNewSharedNote"></ul> <ul id="notebookNavForNewSharedNote"></ul>
</div> </div>
</div> </div>
@@ -118,20 +122,10 @@ function log(o) {
</span> </span>
</div> </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="pull-right" style="margin: 0 10px" id="myProfile">
<div class="dropdown"> <div class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" style="line-height: 60px;"> <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"/>
<img src="images/avatar.png" style="height: 40px; border: 1px solid #ccc" />
-->
<span class="username"> <span class="username">
{{if .userInfo.UsernameRaw}} {{if .userInfo.UsernameRaw}}
{{.userInfo.UsernameRaw}} {{.userInfo.UsernameRaw}}
@@ -143,12 +137,22 @@ function log(o) {
</a> </a>
<ul class="dropdown-menu li-a" role="menu"> <ul class="dropdown-menu li-a" role="menu">
<li role="presentation" id="setInfo"> <li role="presentation" id="setInfo">
<a>
<i class="fa fa-info"></i> <i class="fa fa-info"></i>
{{msg . "accountSetting"}} {{msg . "accountSetting"}}
</a>
</li>
<li role="presentation" id="setAvatarMenu">
<a>
<i class="fa fa-smile-o"></i>
{{msg . "setAvatar"}}
</a>
</li> </li>
<li role="presentation" id="setTheme"> <li role="presentation" id="setTheme">
<a>
<i class="fa fa-sun-o"></i> <i class="fa fa-sun-o"></i>
{{msg . "themeSetting"}} {{msg . "themeSetting"}}
</a>
</li> </li>
<!-- <!--
<li role="presentation" id="yourSuggestions"> <li role="presentation" id="yourSuggestions">
@@ -156,6 +160,22 @@ function log(o) {
{{msg . "yourSuggestions"}} {{msg . "yourSuggestions"}}
</li> </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()"> <li role="presentation" onclick="logout()">
<i class="fa fa-sign-out"></i> <i class="fa fa-sign-out"></i>
{{msg . "logout"}} {{msg . "logout"}}
@@ -164,12 +184,8 @@ function log(o) {
</div> </div>
</div> </div>
<div class="pull-right" style="line-height: 60px; margin:0 10px"> <div class="pull-right top-nav" id="myBlog">
<a target="_blank" title="lea++, leanote blog platform" href="/lea/index">lea++</a> <a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
</div>
<div class="pull-right" style="margin: 0 10px" id="topNav">
<a target="_blank" href="/blog/{{.userInfo.Username}}">
{{msg . "myBlog"}} {{msg . "myBlog"}}
</a> </a>
</div> </div>
@@ -243,7 +259,7 @@ function log(o) {
</div> </div>
<div class="folderBody"> <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="notebookList"></ul>
<ul class="ztree" id="notebookListForSearch"></ul> <ul class="ztree" id="notebookListForSearch"></ul>
</div> </div>
@@ -258,10 +274,10 @@ function log(o) {
</div> </div>
<ul class="folderBody clearfix" id="tagNav"> <ul class="folderBody clearfix" id="tagNav">
<li><a> <span class="label label-red">{{msg . "red"}}</span></a></li> <li data-tag="red"><a> <span class="label label-red">{{msg . "red"}}</span></a></li>
<li><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li> <li data-tag="blue"><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li>
<li><a> <span class="label label-yellow">{{msg . "yellow"}}</span></a></li> <li data-tag="yellow"><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="green"><a> <span class="label label-green">{{msg . "green"}}</span></a></li>
</ul> </ul>
</div> </div>
@@ -313,9 +329,7 @@ function log(o) {
<div id="noteAndEditor"> <div id="noteAndEditor">
<div id="noteList"> <div id="noteList">
<div class="clearfix" id="notesAndSort" style="position: relative"> <div class="clearfix" id="notesAndSort" style="position: relative">
<div class="pull-left"> <div class="pull-left">
<!-- 我的笔记本 --> <!-- 我的笔记本 -->
<div class="dropdown" id="myNotebookNavForListNav"> <div class="dropdown" id="myNotebookNavForListNav">
<a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown"> <a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">
@@ -374,14 +388,13 @@ function log(o) {
--> -->
</div> </div>
</div> </div>
</div> </div>
<!-- 笔记列表 --> <!-- 笔记列表 -->
<!-- wrap 为了slimScroll --> <!-- wrap 为了slimScroll -->
<div id="noteItemListWrap" style="position: absolute; left: 0; right: 0; top: 41px; bottom: 3px"> <div id="noteItemListWrap">
<div id="noteItemList"> <ul id="noteItemList">
</div> </ul>
</div> </div>
</div> </div>
@@ -393,43 +406,41 @@ function log(o) {
<div id="noteReadContainer"> <div id="noteReadContainer">
<div id="noteReadTop"> <div id="noteReadTop">
<h2 id="noteReadTitle"></h2> <h2 id="noteReadTitle"></h2>
<div class="clearfix"> <div class="clearfix" id="noteReadInfo">
<div class="pull-left">
<i class="fa fa-bookmark-o"></i> <i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span> <span id="noteReadTags"></span>
</div>
<!-- 修改时间 --> <!-- 修改时间 -->
<div class="pull-left" style="margin-left: 10px;">
<i class="fa fa-calendar"></i>{{msg . "update"}} <i class="fa fa-calendar"></i>{{msg . "update"}}
<span id="noteReadUpdatedTime"></span> <span id="noteReadUpdatedTime"></span>
</div>
<!-- 修改时间 --> <!-- 修改时间 -->
<div class="pull-left" style="margin-left: 10px;">
<i class="fa fa-calendar"></i>{{msg . "create"}} <i class="fa fa-calendar"></i>{{msg . "create"}}
<span id="noteReadCreatedTime"></span> <span id="noteReadCreatedTime"></span>
</div> </div>
</div> </div>
</div>
<div id="noteReadContent"> <div id="noteReadContent">
</div> </div>
</div> </div>
</div> </div>
<!-- 遮罩, 为了resize3Columns用 --> <!-- 遮罩, 为了resize3Columns用 -->
<div id="noteMask" <div id="noteMask" class="note-mask"></div>
style="position: absolute; top: 0px; bottom: 0px; right: 0; left: 10px; z-index: -1"></div> <div id="noteMaskForLoading" class="note-mask">
<img src="/images/loading-24.gif"/>
<br />
loading...
</div>
<div id="editorMask"> <div id="editorMask">
该笔记本下空空如也...何不 {{msg . "noNoteNewNoteTips"}}
<br />
<br /> <br />
<div id="editorMaskBtns"> <div id="editorMaskBtns">
<br /> <a class="note">{{msg . "newNote"}}</a>
<a class="note">新建笔记</a> <a class="markdown">{{msg . "newMarkdownNote"}}</a>
<a class="markdown">新建Markdown笔记</a>
</div> </div>
<div id="editorMaskBtnsEmpty"> <div id="editorMaskBtnsEmpty">
Sorry, 这里不能添加笔记的. {{msg . "canntNewNoteTips"}}
</div> </div>
</div> </div>
<div id="noteTop"> <div id="noteTop">
@@ -453,7 +464,9 @@ function log(o) {
<a <a
class="metro-a dropdown-toggle" data-toggle="dropdown" class="metro-a dropdown-toggle" data-toggle="dropdown"
id="addTagTrigger" style="cursor: text; padding-left: 0"> id="addTagTrigger" style="cursor: text; padding-left: 0">
<span class="add-tag-text">
{{msg . "clickAddTag"}} {{msg . "clickAddTag"}}
</span>
</a> </a>
<input type="text" id="addTagInput" /> <input type="text" id="addTagInput" />
<ul class="dropdown-menu" role="menu" id="tagColor"> <ul class="dropdown-menu" role="menu" id="tagColor">
@@ -467,34 +480,34 @@ function log(o) {
</div> </div>
</div> </div>
<ul class="pull-right" id="editorTool"> <ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s" <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"> <li class="dropdown" id="attachDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach"> <a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
<!-- <span class="fa fa-paperclip"></span>
<span class="fa fa-upload"></span>
-->
{{msg . "attachments"}}<span id="attachNum"></span> {{msg . "attachments"}}<span id="attachNum"></span>
</a> </a>
<div class="dropdown-menu" id="attachMenu"> <div class="dropdown-menu" id="attachMenu">
<ul id="attachList"> <ul id="attachList">
</ul> </ul>
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data"> <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"> <a class="btn btn-success btn-choose-file">
Choose File to Upload <i class="fa fa-upload"></i>
<span>Choose File</span>
</a> </a>
<a class="btn btn-default" id="downloadAllBtn"> <a class="btn btn-default" id="downloadAllBtn">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
Download All <span>Download All</span>
</a> </a>
<a class="btn btn-default" id="linkAllBtn"> <a class="btn btn-default" id="linkAllBtn">
<i class="fa fa-link"></i> <i class="fa fa-link"></i>
Link All <span>Link All</span>
</a> </a>
<input type="file" name="file" multiple/> <input type="file" name="file" multiple/>
</div> </div>
@@ -505,9 +518,13 @@ function log(o) {
</li> </li>
<li><a class="ios7-a " id="tipsBtn" <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" <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> </ul>
</div> </div>
@@ -518,7 +535,7 @@ function log(o) {
<div id="editor"> <div id="editor">
<!-- 编辑器 --> <!-- 编辑器 -->
<div id="mceToolbar" style=""> <div id="mceToolbar">
<div id="popularToolbar" <div id="popularToolbar"
style="position: absolute; right: 30px; left: 0"></div> style="position: absolute; right: 30px; left: 0"></div>
<a <a
@@ -558,12 +575,13 @@ function log(o) {
<!-- 为了scroll --> <!-- 为了scroll -->
<div class="clearfix" id="mdEditorPreview"> <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"> <div id="wmd-panel-editor" class="wmd-panel-editor">
<textarea class="wmd-input theme" id="wmd-input" spellcheck="false" tabindex="3"></textarea> <textarea class="wmd-input theme" id="wmd-input" spellcheck="false" tabindex="3"></textarea>
</div> </div>
</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-panel-preview" class="wmd-panel-preview preview-container">
<div id="wmd-preview" class="wmd-preview"></div> <div id="wmd-preview" class="wmd-preview"></div>
</div> </div>
@@ -571,7 +589,8 @@ function log(o) {
</div> </div>
<textarea id="md-section-helper"></textarea> <textarea id="md-section-helper"></textarea>
</div> </div>
<!-- for test --> </div>
</div>
<!-- mdEditor --> <!-- mdEditor -->
<!-- Hidden Popup Modal --> <!-- Hidden Popup Modal -->
@@ -581,7 +600,7 @@ function log(o) {
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="editorDialog-title">操作</h4> <h4 class="modal-title" id="editorDialog-title"></h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -595,17 +614,13 @@ function log(o) {
</div> </div>
<div class="modal-footer"> <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 . "cancel"}}</button>
<button type="button" class="btn btn-primary" id="editorDialog-confirm">确认</button> <button type="button" class="btn btn-primary" id="editorDialog-confirm">{{msg . "confirm"}}</button>
</div> </div>
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
</div>
</div>
<!-- 弹出框 模板 --> <!-- 弹出框 模板 -->
<div class="modal fade bs-modal-sm" id="leanoteDialogRemote" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true"> <div class="modal fade bs-modal-sm" id="leanoteDialogRemote" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
</div> </div>
@@ -648,7 +663,7 @@ function log(o) {
</div> </div>
<input type="hidden" id="toEmail"/> <input type="hidden" id="toEmail"/>
<label for="emailContent">邮件内容</label> <label for="emailContent">邮件内容</label>
<textarea class="form-control" id="emailContent">Hi, 我是李铁, leanote非常好用, 快来注册吧.</textarea> <textarea class="form-control" id="emailContent">Hi, 我是life, leanote非常好用, 快来注册吧.</textarea>
</div> </div>
</form> </form>
</div> </div>
@@ -661,26 +676,15 @@ function log(o) {
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </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 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-dialog modal-sm">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">主题设置</h4> <h4 class="modal-title" class="modalTitle">{{msg . "theme"}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<style>
#themeForm td {
padding: 5px;
text-align: center;
}
#themeForm img {
border: 1px solid #eee;
padding: 2px;
}
</style>
<table id="themeForm"> <table id="themeForm">
<tr> <tr>
<td> <td>
@@ -703,7 +707,6 @@ function log(o) {
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div> </div>
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
@@ -728,17 +731,31 @@ function log(o) {
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
<!-- 图片上传 --> <!-- avatar -->
<div class="modal fade bs-modal-sm" id="imageDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true"> <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-dialog modal-sm">
<div class="modal-content" style="height: 460px;" > <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "uploadImage"}}</h4> <h4 class="modal-title" class="modalTitle">{{msg . "setAvatar"}}</h4>
</div> </div>
<div class="modal-body"> <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>
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
@@ -785,183 +802,27 @@ function log(o) {
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "cancel"}}</button> <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> </div>
<!-- 激活邮件 --> <!-- 激活邮件 -->
<div id="reSendActiveEmailDialog"> <div id="reSendActiveEmailDialog">
<div class="modal-body"> <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"> <span class="text">
<img src="/images/loading-24.gif"/> <img src="/images/loading-24.gif"/>
正在发送邮件到{{.userInfo.Email}}... {{msg . "emailInSending"}} {{.userInfo.Email}}...
</span> </span>
</div> </div>
</div> </div>
<div class="modal-footer"> <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>
<button type="button" class="btn btn-success viewEmailBtn disabled">查看邮件</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> </div>
<div class="hide" id="copyDiv"></div> <div class="hide" id="copyDiv"></div>
@@ -973,7 +834,7 @@ function log(o) {
<script src="js/i18n/msg.{{.locale}}.js"></script> <script src="js/i18n/msg.{{.locale}}.js"></script>
<script src="js/common-min.js"></script> <script src="js/common-min.js"></script>
<script> <script>
var UrlPrefix = "http://leanote.com"; // 为了发weibo var UrlPrefix = "{{.siteUrl}}"; // 为了发weibo
var UserInfo = json({{.userInfoJson}}); var UserInfo = json({{.userInfoJson}});
var notebooks = json({{.notebooks}}); var notebooks = json({{.notebooks}});
var shareNotebooks = json({{.shareNotebooks}}); var shareNotebooks = json({{.shareNotebooks}});
@@ -985,12 +846,11 @@ LEA.locale = "{{.locale}}";
</script> </script>
<!-- 渲染view --> <!-- 渲染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/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
<script src="/js/contextmenu/jquery.contextmenu-min.js"></script> <script src="/js/contextmenu/jquery.contextmenu-min.js"></script>
<script src="js/app/page-min.js"></script> <script src="js/jquery-cookie.js"></script>
<script src="tinymce/tinymce.js"></script>
<script src="js/jquery-cookie-min.js"></script>
<script src="js/bootstrap-min.js"></script> <script src="js/bootstrap-min.js"></script>
<script src="js/app/note-min.js"></script> <script src="js/app/note-min.js"></script>
<script src="js/app/tag-min.js"></script> <script src="js/app/tag-min.js"></script>
@@ -1002,13 +862,14 @@ LEA.locale = "{{.locale}}";
Notebook.renderNotebooks(notebooks); Notebook.renderNotebooks(notebooks);
Share.renderShareNotebooks(sharedUserInfos, shareNotebooks); Share.renderShareNotebooks(sharedUserInfos, shareNotebooks);
Note.setNoteCache(noteContentJson);
Note.renderNotes(notes); Note.renderNotes(notes);
if(!isEmpty(notes)) { if(!isEmpty(notes)) {
Note.changeNote(notes[0].NoteId); Note.changeNote(notes[0].NoteId);
} }
Note.setNoteCache(noteContentJson); // Note.chanteNote设置content
Note.renderNoteContent(noteContentJson) // Note.renderNoteContent(noteContentJson)
Tag.renderTagNav(tagsJson); Tag.renderTagNav(tagsJson);
@@ -1016,50 +877,13 @@ Tag.renderTagNav(tagsJson);
initSlimScroll(); initSlimScroll();
</script> </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 --> <!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" /> <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 --> <!-- js version 2.0 use require.js -->
<script src="/js/require.js"></script> <script src="/js/require.js"></script>
<script> <script src="/js/main-min.js"></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> </script>
</body> </body>
</html> </html>
+22 -8
View File
@@ -1,18 +1,32 @@
{{template "home/header_box.html" .}} {{template "home/header_box.html" .}}
<section id="box"> <section id="box" class="animated fadeInUp">
<div> <div>
<h1> <h1 id="logo">leanote</h1>
leanote | we got a error <div id="boxForm">
</h1> <div id="boxHeader">We got a error</div>
<form class="form-inline" id="boxForm"> <form>
<p> <div class="alert alert-danger" id="loginMsg" style="display: block">
Sorry, we can't get your infomation. Sorry, we can't get your infomation.
<br /> <br />
Please <a href="/login">Sign in</a> Or <a href="/register?email={{.email}}">Sign up</a> Please <a href="/login">{{msg . "login"}}</a> Or <a href="/register?email={{.email}}">{{msg . "register"}}</a>
</p> </div>
</form> </form>
</div> </div>
</div>
</section> </section>
<div id="boxFooter">
<p>
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
</body> </body>
</html> </html>
@@ -1,37 +1,36 @@
<div class="modal-dialog modal-sm"> <div class="modal-dialog modal-sm">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="modalTitle">分享 <b>{{.title}}</b></h4> <h4 class="modal-title" id="modalTitle">{{msg . "share"}} <b>{{.title}}</b></h4>
</div> </div>
{{$noteOrNotebookId := .noteOrNotebookId}} {{$noteOrNotebookId := .noteOrNotebookId}}
<div class="modal-body"> <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> <div id="shareMsg" class="alert alert-danger" style="display: none; margin: 5px 0 0 0;"></div>
<table class="table table-hover" id="shareNotebookTable"> <table class="table table-hover" id="shareNotebookTable">
<thead> <thead>
<tr> <tr>
<th>#</th> <th>#</th>
<th>好友邮箱</th> <th>{{msg . "friendEmail"}}</th>
<th>权限</th> <th>{{msg . "permission"}}</th>
<th>删除分享</th> <th width="150px">{{msg . "delete"}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr id="tr1"> <tr id="tr1">
<td>#</td> <td>#</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>
<td> <td>
<label for="readPerm1"><input type="radio" name="perm1" checked="checked" value="0" id="readPerm1"> 只读</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"> 可编辑</label> <label for="writePerm1"><input type="radio" name="perm1" value="1" id="writePerm1"> {{msg . "writable"}}</label>
</td> </td>
<td> <td>
<button class="btn btn-success" onclick="addShareNoteOrNotebook(1)">分享</button> <button class="btn btn-success" onclick="addShareNoteOrNotebook(1)">{{msg . "share"}}</button>
<button class="btn btn-warning" onclick="deleteShareNoteOrNotebook(1)">删除</button> <button class="btn btn-warning" onclick="deleteShareNoteOrNotebook(1)">{{msg . "delete"}}</button>
</td> </td>
</tr> </tr>
{{range $i, $v := .noteOrNotebookShareUserInfos}} {{range $i, $v := .noteOrNotebookShareUserInfos}}
@@ -41,13 +40,13 @@
<td>{{$v.Email}}</td> <td>{{$v.Email}}</td>
<td> <td>
{{if eq $v.Perm 0}} {{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}} {{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}} {{end}}
</td> </td>
<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> </td>
</tr> </tr>
{{end}} {{end}}
@@ -56,7 +55,7 @@
</div> </div>
<div class="modal-footer"> <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>
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
+272
View 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">&times;</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>
+19 -9
View File
@@ -1,22 +1,32 @@
{{template "home/header_box.html" .}} {{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}} 验证 您的邮箱 {{.email}} 验证
{{if .ok}}成功{{else}}失败{{end}} {{if .ok}}成功{{else}}失败{{end}}
{{if .msg}}<br />{{.msg}}{{end}} {{if .msg}}<br />
{{.msg}}{{end}}
<br /> <br />
<a href="/note">回到我的笔记</a> <a href="/note">回到我的笔记</a>
</form> </form>
</div> </div>
</div>
</section> </section>
<div id="boxFooter">
<p>
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
</body> </body>
</html> </html>
+17 -7
View File
@@ -1,12 +1,12 @@
{{template "home/header_box.html" .}} {{template "home/header_box.html" .}}
<section id="box">
<div>
<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}} 验证 您的邮箱 {{.email}} 验证
{{if .ok}}成功{{else}}失败{{end}} {{if .ok}}成功{{else}}失败{{end}}
{{if .ok}} {{if .ok}}
@@ -20,6 +20,16 @@
<a href="/note">回到我的笔记</a> <a href="/note">回到我的笔记</a>
</form> </form>
</div> </div>
</div>
</section> </section>
<div id="boxFooter">
<p>
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
</body> </body>
</html> </html>
+11 -4
View File
@@ -10,6 +10,8 @@ cd ../
# tmp path to store leanote release files # tmp path to store leanote release files
tmp="/Users/life/Desktop/leanote_release" tmp="/Users/life/Desktop/leanote_release"
version=x86_64.v1.0-beta
rm -rf $tmp/leanote rm -rf $tmp/leanote
mkdir -p $tmp/leanote/app mkdir -p $tmp/leanote/app
mkdir -p $tmp/leanote/conf mkdir -p $tmp/leanote/conf
@@ -19,6 +21,9 @@ mkdir -p $tmp/leanote/bin
cp -r ./bin/src $tmp/leanote/bin/ cp -r ./bin/src $tmp/leanote/bin/
cp ./bin/run.sh $tmp/leanote/bin/ cp ./bin/run.sh $tmp/leanote/bin/
# LICENSE
cp ./LICENSE $tmp/leanote/
# views # views
cp -r ./app/views $tmp/leanote/app cp -r ./app/views $tmp/leanote/app
# 可不要 # 可不要
@@ -39,12 +44,14 @@ cp -r ./messages ./public ./mongodb_backup $tmp/leanote/
rm -r $tmp/leanote/public/tinymce/classes rm -r $tmp/leanote/public/tinymce/classes
rm -r $tmp/leanote/public/upload rm -r $tmp/leanote/public/upload
mkdir $tmp/leanote/public/upload mkdir $tmp/leanote/public/upload
mkdir $tmp/leanote/files
rm -r $tmp/leanote/public/.codekit-cache rm -r $tmp/leanote/public/.codekit-cache
rm $tmp/leanote/public/.DS_Store rm $tmp/leanote/public/.DS_Store
rm $tmp/leanote/public/config.codekit rm $tmp/leanote/public/config.codekit
# make link # make link
cd $tmp/leanote/bin cd $tmp/leanote/bin
mkdir ./src/github.com/leanote
ln -s ../../../../ ./src/github.com/leanote/leanote ln -s ../../../../ ./src/github.com/leanote/leanote
# archieve # archieve
@@ -54,8 +61,8 @@ cd $SP
cd ../ cd ../
cp ./bin/leanote-linux $tmp/leanote/bin/ cp ./bin/leanote-linux $tmp/leanote/bin/
cd $tmp cd $tmp
tar -cvf $tmp/leanote-linux-v0.4.bin.tar leanote tar -cvf $tmp/leanote-linux-$version.bin.tar leanote
gzip $tmp/leanote-linux-v0.4.bin.tar gzip $tmp/leanote-linux-$version.bin.tar
# mac # mac
rm $tmp/leanote/bin/leanote-linux rm $tmp/leanote/bin/leanote-linux
@@ -63,7 +70,7 @@ cd $SP
cd ../ cd ../
cp ./bin/leanote-mac $tmp/leanote/bin/ cp ./bin/leanote-mac $tmp/leanote/bin/
cd $tmp cd $tmp
tar -cvf $tmp/leanote-mac-v0.4.bin.tar leanote tar -cvf $tmp/leanote-mac-$version.bin.tar leanote
gzip $tmp/leanote-mac-v0.4.bin.tar gzip $tmp/leanote-mac-$version.bin.tar
# BLOCK' # BLOCK'
+11 -23
View File
@@ -4,42 +4,25 @@
http.port=80 http.port=80
site.url=http://localhost site.url=http://localhost # or http://x.com:8080, http://www.xx.com:9000
# the administrator username
adminUsername=admin
# mongdb # mongdb
db.host=localhost db.host=localhost
db.port=27017 db.port=27017
db.dbname=leanote # required db.dbname=leanote # required
db.username=root # if not exists, please leave blank db.username=root # if not exists, please leave it blank
db.password=root123 # if not exists, please leave blank db.password=root123 # if not exists, please leave it blank
# or you can set the mongdb url for more complex needs the format is: # or you can set the mongdb url for more complex needs the format is:
# mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb # mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
# db.url=mongodb://root:root123@localhost:27017/leanote # db.url=mongodb://root:root123@localhost:27017/leanote
# email # You Must Change It !! About Security!!
email.host= app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y
email.port=25
email.username=
email.password=
# open for register?
register.open=true
register.sharedUserId=5368c1aa99c37b029d000001
register.sharedUserShareNotebookId=5368c1aa99c37b029d000002
register.welcomeNoteId=5368c1b919807a6f95000000
#--------------------------------
# revel config
# for dev
#--------------------------------
app.name=leanote app.name=leanote
app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y
http.addr= http.addr=
http.ssl=false http.ssl=false
cookie.domain=
cookie.httponly=false cookie.httponly=false
cookie.prefix=LEANOTE cookie.prefix=LEANOTE
cookie.secure=false cookie.secure=false
@@ -47,6 +30,11 @@ format.date=01/02/2006
format.datetime=01/02/2006 15:04 format.datetime=01/02/2006 15:04
results.chunked=false results.chunked=false
#--------------------------------
# revel config
# for dev
#--------------------------------
log.trace.prefix = "TRACE " log.trace.prefix = "TRACE "
log.info.prefix = "INFO " log.info.prefix = "INFO "
log.warn.prefix = "WARN " log.warn.prefix = "WARN "
+23 -6
View File
@@ -27,16 +27,32 @@ POST /findPasswordUpdate Auth.FindPasswordUpdate
GET /mobile Mobile.Index GET /mobile Mobile.Index
# blog # blog
# /blog/view/:noteId必须要放在/blog/:userId...之前
GET /blog/searchBlog/:userId Blog.SearchBlog GET /blog/getLikeAndComments Blog.GetLikeAndComments
GET /blog/getLike Blog.GetLike
GET /blog/listLikes Blog.ListLikes
GET /blog/incReadNum Blog.IncReadNum
POST /blog/likeBlog Blog.LikeBlog
POST /blog/likeComment Blog.LikeComment
POST /blog/deleteComment Blog.DeleteComment
GET /blog/listComments Blog.ListComments
POST /blog/comment Blog.Comment
GET /blog/ListComments Blog.ListComments
GET /blog/search/:userIdOrEmail Blog.Search
GET /blog/search Blog.Search
GET /blog/set Blog.Set GET /blog/set Blog.Set
GET /blog/aboutMe/:userId Blog.AboutMe GET /blog/aboutMe/:userIdOrEmail Blog.AboutMe
GET /blog/aboutMe Blog.AboutMe
GET /blog/view/:noteId Blog.View
POST /blog/setUserBlogBase Blog.SetUserBlogBase POST /blog/setUserBlogBase Blog.SetUserBlogBase
POST /blog/setUserBlogComment Blog.SetUserBlogComment POST /blog/setUserBlogComment Blog.SetUserBlogComment
POST /blog/setUserBlogStyle Blog.SetUserBlogStyle POST /blog/setUserBlogStyle Blog.SetUserBlogStyle
GET /blog/view/:noteId Blog.View GET /blog/cate/:notebookId Blog.Cate
GET /blog/:userId Blog.Index GET /blog/listCateLatest/:notebookId Blog.ListCateLatest
GET /blog/:userId/:notebookId Blog.Index GET /blog/isMe.js Blog.IsMe
GET /blog/:userIdOrEmail Blog.Index
GET /blog Blog.Index GET /blog Blog.Index
# Map static resources from the /app/public folder to the /public path # Map static resources from the /app/public folder to the /public path
@@ -53,3 +69,4 @@ GET /upload/*filepath Static.Serve("public/upload")
# common # common
* /:controller/:action :controller.:action * /:controller/:action :controller.:action
* /api/:controller/:action :controller.:action
+57 -7
View File
@@ -1,24 +1,24 @@
# blog # blog
blog=Blog
aboutMe=About me
blogSet=Set blog
blogNavs=Navs blogNavs=Navs
quickLinks=Quick links quickLinks=Quick links
latestPosts=Latest posts latestPosts=Latest posts
noBlog=No blog noBlog=No blog
noTag=No tag noTag=No tag
blogClass=Classification blogClass=Category
updatedTime=Updated at updatedTime=Updated at
createdTime=Created at createdTime=Created at
fullBlog=Full blog fullBlog=Full blog
blogNav=Blog nav blogNav=Blog nav
more=More...
previous=Previous
next=Next
# #
# set blog # set blog
# #
blogSet=Blog configuration
baseInfoSet=Base info baseInfoSet=Base info
commentSet=Comment commentSet=Comment
themeSet=Theme themeSet=Theme
@@ -26,16 +26,66 @@ theme=Theme
blogName=Title blogName=Title
blogLogo=Logo blogLogo=Logo
blogDesc=Description blogDesc=Description
aboutMe=About Me
#domain
domainSet=Domain
subDomain=Sub domain
domain=Custom domain
# theme # theme
elegant=Elegant elegant=Elegant
navFixed=Nav fixed at left side navFixed=Nav fixed at left side
openComment=Open comment? openComment=Open comment?
commentSys=leanote use <a href="http://disqus.com" target="_blank">Disqus</a> as comment system chooseComment=Comment System
disqusHelp=Please input your Disqus Id or use "leanote" disqusHelp=Please input your Disqus Id
needHelp=Need help? needHelp=Need help?
blogLogoTips=Upload image to replace blog title blogLogoTips=Upload image to replace blog title
saveSuccess=Save success 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 a=a
Loaded 100 of 283 files, more files were not shown because too many files have changed in this diff. Show more