Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64a3177978 | ||
|
|
ed2ad19341 | ||
|
|
b140cd538f | ||
|
|
2654b684df | ||
|
|
d424395d85 | ||
|
|
54810b3458 | ||
|
|
1cf66ad6c0 | ||
|
|
6dc334ca51 | ||
|
|
f9b4ead6be | ||
|
|
3bf6703929 | ||
|
|
ec657b9dcb | ||
|
|
4ffd048b2a | ||
|
|
6af19670da | ||
|
|
721e375d76 | ||
|
|
41f95cf0e5 | ||
|
|
ba411ef580 | ||
|
|
9dcf0ed53e | ||
|
|
d5d853ffd3 | ||
|
|
ae29119664 | ||
|
|
ad644258f5 | ||
|
|
8c98f4da5c | ||
|
|
e2585bf695 | ||
|
|
3b9bc687c4 | ||
|
|
2db182db1b | ||
|
|
f3e1eb6c3f | ||
|
|
956e1ba2bb | ||
|
|
f294fa4124 | ||
|
|
cabf89f9b8 | ||
|
|
d183cd5a77 | ||
|
|
000dfea92d | ||
|
|
a53398ebb4 | ||
|
|
b23592229a | ||
|
|
584bde247e | ||
|
|
6774392807 | ||
|
|
964cf1a750 | ||
|
|
52128e6453 |
No files matched your search
+4
-5
@@ -1,5 +1,5 @@
|
||||
language: go
|
||||
go: 1.4
|
||||
go: 1.7
|
||||
|
||||
services:
|
||||
- mongodb # 2.4.12
|
||||
@@ -7,8 +7,9 @@ services:
|
||||
install:
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- go get -v github.com/leanote/leanote/app
|
||||
- go get -u github.com/revel/cmd/revel
|
||||
- ls $GOPATH/src/github.com/revel/
|
||||
- go get -v github.com/leanote/leanote/cmd
|
||||
# - go get -u github.com/revel/cmd/revel
|
||||
# - ls $GOPATH/src/github.com/revel/
|
||||
# - go get github.com/revel/moudle/revel
|
||||
# - go install github.com/revel/cmd/revel
|
||||
- pwd
|
||||
@@ -18,8 +19,6 @@ script:
|
||||
- mongo --version
|
||||
- mongorestore -h localhost -d leanote --directoryperdb ./mongodb_backup/leanote_install_data/
|
||||
|
||||
- go test github.com/leanote/leanote/app/tests
|
||||
|
||||
# gen tmp/main.go, routes/routes.go
|
||||
- go run app/cmd/main.go
|
||||
# build
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ gulp.task('concatDepJs', function() {
|
||||
'js/jQuery-slimScroll-1.3.0/jquery.slimscroll-min.js',
|
||||
'js/contextmenu/jquery.contextmenu-min.js',
|
||||
'js/bootstrap-min.js',
|
||||
'js/object_id-min.js',
|
||||
'js/object_id.js',
|
||||
];
|
||||
|
||||
for(var i in jss) {
|
||||
@@ -340,7 +340,7 @@ gulp.task('tinymce', function() {
|
||||
|
||||
var cp = require('child_process');
|
||||
|
||||
var bundleCmd = 'grunt bundle --themes leanote --plugins autolink,link,leaui_image,lists,hr,paste,searchreplace,leanote_nav,leanote_code,tabfocus,table,directionality,textcolor';
|
||||
var bundleCmd = 'grunt bundle --themes leanote --plugins autolink,link,leaui_image,leaui_mindmap,lists,hr,paste,searchreplace,leanote_nav,leanote_code,tabfocus,table,directionality,textcolor';
|
||||
// build
|
||||
cp.exec('grunt minify', {cwd: tinymceBase}, function(err, stdout, stderr) {
|
||||
console.log('stdout: ' + stdout);
|
||||
|
||||
@@ -184,6 +184,6 @@ Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善Lea
|
||||
## 联系&加入我们
|
||||
* Email: leanote@leanote.com
|
||||
* [Leanote 社区](http://bbs.leanote.com)
|
||||
* QQ群: 326073529, 256076853, 158716820
|
||||
* [QQ群](http://leanote.leanote.com/post/Leanote-groups)
|
||||
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# Leanote
|
||||
|
||||
[](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://travis-ci.org/leanote/leanote)
|
||||
## 1. 介绍
|
||||
|
||||
Leanote, 不只是笔记!
|
||||

|
||||
|
||||
**特性**
|
||||
|
||||
* 知识管理: 通过leanote来管理知识, leanote有易操作的界面, 包含两款编辑器tinymce和markdown. 在leanote, 你可以尽情享受写作.
|
||||
* 分享: 你也可以通过分享知识给好友, 让好友拥有你的知识.
|
||||
* 协作: 在分享的同时也可以与好友一起协作知识.
|
||||
* 博客: leanote也可以作为你的博客, 将知识公开成博客, 让leanote把你的知识传播的更远!
|
||||
|
||||
## 2. 为什么我们要创建leanote?
|
||||
说实话, 我们曾是evernote的忠实粉丝, 但是我们也发现evernote的不足:
|
||||
* evernote的编辑器不能满足我们的需求, 不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放.
|
||||
* 我们是markdown的爱好者, 可是evernote竟然没有.
|
||||
* 我们也想将知识公开, 所以我们有自己的博客, 如wordpress, 但为什么这两者不能合二为一呢?
|
||||
* 还有...
|
||||
|
||||
## 3.安装leanote
|
||||
leanote是一款私有云笔记, 你可以下载它安装在自己的服务器上, 当然也可以在 http://leanote.com 上注册.
|
||||
|
||||
这里详细整理了leanote二进版和leanote开发版的安装教程, 请移步至:
|
||||
|
||||
* [leanote二进制详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
|
||||
* [leanote开发版详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E5%BC%80%E5%8F%91%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
|
||||
|
||||
## 4. 如何对leanote进行二次开发
|
||||
|
||||
请查看 [How-to-develop-leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-%E5%A6%82%E4%BD%95%E5%BC%80%E5%8F%91leanote)
|
||||
|
||||
## 5 相关文档
|
||||
* [leanote二进制版详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
|
||||
* [leanote开发版详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E5%BC%80%E5%8F%91%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
|
||||
* [Leanote source leanote源码导读](https://github.com/leanote/leanote/wiki/Leanote-source-leanote源码导读)
|
||||
* [leanote blog theme api(中文版)](https://github.com/leanote/leanote/wiki/leanote-blog-theme-api)
|
||||
* [How to develop leanote 如何开发leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-如何开发leanote)
|
||||
|
||||
更多文档请查看 [wiki](https://github.com/leanote/leanote/wiki).
|
||||
|
||||
## 6. 贡献者
|
||||
多谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, leanote因有你们而更完美!
|
||||
|
||||
## 7. 加入我们
|
||||
|
||||
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到leanote.
|
||||
|
||||
有任何问题或建议, 欢迎提交[issue](https://github.com/leanote/leanote/issues).
|
||||
|
||||
Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善leanote.
|
||||
|
||||
## 8. 捐赠
|
||||
支持我们, [捐赠Leanote](http://leanote.org/#donate). 感谢[捐赠者](http://leanote.leanote.com/post/leanote-donation-list), 谢谢你们的鼓励, Leanote会一直坚持!
|
||||
|
||||
## 9. 其它相关项目
|
||||
* [Leanote Desktop App](https://github.com/leanote/desktop-app), [下载地址](http://app.leanote.com)
|
||||
* [Leanote IOS](https://github.com/leanote/leanote-ios), [从App Store下载](https://itunes.apple.com/zn/app/leanote/id1022302858?mt=8)
|
||||
* [Leanote Android](https://github.com/Dminter/leanote-android-client), 开发阶段
|
||||
|
||||
欢迎加入我们!
|
||||
|
||||
## 联系&加入我们
|
||||
* Email: leanote@leanote.com
|
||||
* [leanote 社区](http://bbs.leanote.com)
|
||||
* QQ群: 158716820, 256076853
|
||||
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
|
||||
|
||||
----------------------------------------------------------------
|
||||
[English](README.md)
|
||||
@@ -0,0 +1,20 @@
|
||||
# Modifications
|
||||
|
||||
* revel.go
|
||||
|
||||
```
|
||||
RevelImportPath = "github.com/leanote/leanote/vendor/github.com/revel/revel"
|
||||
```
|
||||
|
||||
|
||||
# RUN
|
||||
```
|
||||
go install github.com/leanote/leanote/cmd/leanote_revel
|
||||
leanote_revel run github.com/leanote/leanote
|
||||
```
|
||||
|
||||
# build leanote, 在当前目录生成了leanote二进制文件
|
||||
go build -o ./leanote github.com/leanote/leanote/app/tmp
|
||||
|
||||
# 运行leanote, 其中-importPath是必须的
|
||||
./leanote -importPath=github.com/leanote/leanote -runMode=prod -port=9000
|
||||
@@ -23,11 +23,11 @@ func (c Album) Index() revel.Result {
|
||||
// all albums by userId
|
||||
func (c Album) GetAlbums() revel.Result {
|
||||
re := albumService.GetAlbums(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c Album) DeleteAlbum(albumId string) revel.Result {
|
||||
re, msg := albumService.DeleteAlbum(c.GetUserId(), albumId)
|
||||
return c.RenderJson(info.Re{Ok: re, Msg: msg})
|
||||
return c.RenderJSON(info.Re{Ok: re, Msg: msg})
|
||||
}
|
||||
|
||||
// add album
|
||||
@@ -40,13 +40,13 @@ func (c Album) AddAlbum(name string) revel.Result {
|
||||
re := albumService.AddAlbum(album)
|
||||
|
||||
if re {
|
||||
return c.RenderJson(album)
|
||||
return c.RenderJSON(album)
|
||||
} else {
|
||||
return c.RenderJson(false)
|
||||
return c.RenderJSON(false)
|
||||
}
|
||||
}
|
||||
|
||||
// update alnum name
|
||||
func (c Album) UpdateAlbum(albumId, name string) revel.Result {
|
||||
return c.RenderJson(albumService.UpdateAlbum(albumId, c.GetUserId(), name))
|
||||
return c.RenderJSON(albumService.UpdateAlbum(albumId, c.GetUserId(), name))
|
||||
}
|
||||
@@ -24,7 +24,7 @@ type Attach struct {
|
||||
// 上传附件
|
||||
func (c Attach) UploadAttach(noteId string) revel.Result {
|
||||
re := c.uploadAttach(noteId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c Attach) uploadAttach(noteId string) (re info.Re) {
|
||||
var fileId = ""
|
||||
@@ -118,7 +118,7 @@ func (c Attach) uploadAttach(noteId string) (re info.Re) {
|
||||
func (c Attach) DeleteAttach(attachId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = attachService.DeleteAttach(attachId, c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// get all attachs by noteId
|
||||
@@ -126,7 +126,7 @@ func (c Attach) GetAttachs(noteId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.List = attachService.ListAttachs(noteId, c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 下载附件
|
||||
|
||||
@@ -17,29 +17,29 @@ type Auth struct {
|
||||
//--------
|
||||
// 登录
|
||||
func (c Auth) Login(email, from string) revel.Result {
|
||||
c.RenderArgs["title"] = c.Message("login")
|
||||
c.RenderArgs["subTitle"] = c.Message("login")
|
||||
c.RenderArgs["email"] = email
|
||||
c.RenderArgs["from"] = from
|
||||
c.RenderArgs["openRegister"] = configService.IsOpenRegister()
|
||||
c.ViewArgs["title"] = c.Message("login")
|
||||
c.ViewArgs["subTitle"] = c.Message("login")
|
||||
c.ViewArgs["email"] = email
|
||||
c.ViewArgs["from"] = from
|
||||
c.ViewArgs["openRegister"] = configService.IsOpenRegister()
|
||||
|
||||
sessionId := c.Session.Id()
|
||||
sessionId := c.Session.ID()
|
||||
if sessionService.LoginTimesIsOver(sessionId) {
|
||||
c.RenderArgs["needCaptcha"] = true
|
||||
c.ViewArgs["needCaptcha"] = true
|
||||
}
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
if c.Has("demo") {
|
||||
c.RenderArgs["demo"] = true
|
||||
c.RenderArgs["email"] = "demo@leanote.com"
|
||||
c.ViewArgs["demo"] = true
|
||||
c.ViewArgs["email"] = "demo@leanote.com"
|
||||
}
|
||||
return c.RenderTemplate("home/login.html")
|
||||
}
|
||||
|
||||
// 为了demo和register
|
||||
func (c Auth) doLogin(email, pwd string) revel.Result {
|
||||
sessionId := c.Session.Id()
|
||||
sessionId := c.Session.ID()
|
||||
var msg = ""
|
||||
|
||||
userInfo, err := authService.Login(email, pwd)
|
||||
@@ -49,13 +49,13 @@ func (c Auth) doLogin(email, pwd string) revel.Result {
|
||||
} else {
|
||||
c.SetSession(userInfo)
|
||||
sessionService.ClearLoginTimes(sessionId)
|
||||
return c.RenderJson(info.Re{Ok: true})
|
||||
return c.RenderJSON(info.Re{Ok: true})
|
||||
}
|
||||
|
||||
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId), Msg: c.Message(msg)})
|
||||
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()
|
||||
sessionId := c.Session.ID()
|
||||
var msg = ""
|
||||
|
||||
// > 5次需要验证码, 直到登录成功
|
||||
@@ -70,16 +70,16 @@ func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result {
|
||||
} else {
|
||||
c.SetSession(userInfo)
|
||||
sessionService.ClearLoginTimes(sessionId)
|
||||
return c.RenderJson(info.Re{Ok: true})
|
||||
return c.RenderJSON(info.Re{Ok: true})
|
||||
}
|
||||
}
|
||||
|
||||
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId), Msg: c.Message(msg)})
|
||||
return c.RenderJSON(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId), Msg: c.Message(msg)})
|
||||
}
|
||||
|
||||
// 注销
|
||||
func (c Auth) Logout() revel.Result {
|
||||
sessionId := c.Session.Id()
|
||||
sessionId := c.Session.ID()
|
||||
sessionService.Clear(sessionId)
|
||||
c.ClearSession()
|
||||
return c.Redirect("/login")
|
||||
@@ -92,7 +92,7 @@ func (c Auth) Demo() revel.Result {
|
||||
|
||||
userInfo, err := authService.Login(email, pwd)
|
||||
if err != nil {
|
||||
return c.RenderJson(info.Re{Ok: false})
|
||||
return c.RenderJSON(info.Re{Ok: false})
|
||||
} else {
|
||||
c.SetSession(userInfo)
|
||||
return c.Redirect("/note")
|
||||
@@ -107,11 +107,11 @@ func (c Auth) Register(from, iu string) revel.Result {
|
||||
return c.Redirect("/index")
|
||||
}
|
||||
c.SetLocale()
|
||||
c.RenderArgs["from"] = from
|
||||
c.RenderArgs["iu"] = iu
|
||||
c.ViewArgs["from"] = from
|
||||
c.ViewArgs["iu"] = iu
|
||||
|
||||
c.RenderArgs["title"] = c.Message("register")
|
||||
c.RenderArgs["subTitle"] = c.Message("register")
|
||||
c.ViewArgs["title"] = c.Message("register")
|
||||
c.ViewArgs["subTitle"] = c.Message("register")
|
||||
return c.RenderTemplate("home/register.html")
|
||||
}
|
||||
func (c Auth) DoRegister(email, pwd, iu string) revel.Result {
|
||||
@@ -145,22 +145,22 @@ func (c Auth) DoRegister(email, pwd, iu string) revel.Result {
|
||||
// 找回密码
|
||||
func (c Auth) FindPassword() revel.Result {
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("findPassword")
|
||||
c.RenderArgs["subTitle"] = c.Message("findPassword")
|
||||
c.ViewArgs["title"] = c.Message("findPassword")
|
||||
c.ViewArgs["subTitle"] = c.Message("findPassword")
|
||||
return c.RenderTemplate("home/find_password.html")
|
||||
}
|
||||
func (c Auth) DoFindPassword(email string) revel.Result {
|
||||
pwdService.FindPwd(email)
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 点击链接后, 先验证之
|
||||
func (c Auth) FindPassword2(token string) revel.Result {
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("findPassword")
|
||||
c.RenderArgs["subTitle"] = c.Message("findPassword")
|
||||
c.ViewArgs["title"] = c.Message("findPassword")
|
||||
c.ViewArgs["subTitle"] = c.Message("findPassword")
|
||||
if token == "" {
|
||||
return c.RenderTemplate("find_password2_timeout.html")
|
||||
}
|
||||
@@ -168,10 +168,10 @@ func (c Auth) FindPassword2(token string) revel.Result {
|
||||
if !ok {
|
||||
return c.RenderTemplate("home/find_password2_timeout.html")
|
||||
}
|
||||
c.RenderArgs["findPwd"] = findPwd
|
||||
c.ViewArgs["findPwd"] = findPwd
|
||||
|
||||
c.RenderArgs["title"] = c.Message("updatePassword")
|
||||
c.RenderArgs["subTitle"] = c.Message("updatePassword")
|
||||
c.ViewArgs["title"] = c.Message("updatePassword")
|
||||
c.ViewArgs["subTitle"] = c.Message("updatePassword")
|
||||
|
||||
return c.RenderTemplate("home/find_password2.html")
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ func (c BaseController) GetTotalPage(page, count int) int {
|
||||
|
||||
//-------------
|
||||
func (c BaseController) E404() revel.Result {
|
||||
c.RenderArgs["title"] = "404"
|
||||
c.ViewArgs["title"] = "404"
|
||||
return c.NotFound("", nil)
|
||||
}
|
||||
|
||||
@@ -199,12 +199,12 @@ func (c BaseController) SetLocale() string {
|
||||
if !i18n.HasLang(locale) {
|
||||
lang = i18n.GetDefaultLang()
|
||||
}
|
||||
c.RenderArgs["locale"] = lang
|
||||
c.RenderArgs["siteUrl"] = configService.GetSiteUrl()
|
||||
c.ViewArgs["locale"] = lang
|
||||
c.ViewArgs["siteUrl"] = configService.GetSiteUrl()
|
||||
|
||||
c.RenderArgs["blogUrl"] = configService.GetBlogUrl()
|
||||
c.RenderArgs["leaUrl"] = configService.GetLeaUrl()
|
||||
c.RenderArgs["noteUrl"] = configService.GetNoteUrl()
|
||||
c.ViewArgs["blogUrl"] = configService.GetBlogUrl()
|
||||
c.ViewArgs["leaUrl"] = configService.GetLeaUrl()
|
||||
c.ViewArgs["noteUrl"] = configService.GetNoteUrl()
|
||||
|
||||
return lang
|
||||
}
|
||||
@@ -212,9 +212,9 @@ func (c BaseController) SetLocale() string {
|
||||
// 设置userInfo
|
||||
func (c BaseController) SetUserInfo() info.User {
|
||||
userInfo := c.GetUserInfo()
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["userInfo"] = userInfo
|
||||
if userInfo.Username == configService.GetAdminUsername() {
|
||||
c.RenderArgs["isAdmin"] = true
|
||||
c.ViewArgs["isAdmin"] = true
|
||||
}
|
||||
return userInfo
|
||||
}
|
||||
@@ -230,11 +230,11 @@ func (c BaseController) RenderTemplateStr(templatePath string) string {
|
||||
|
||||
tpl := &revel.RenderTemplateResult{
|
||||
Template: template,
|
||||
RenderArgs: c.RenderArgs, // 把args给它
|
||||
ViewArgs: c.ViewArgs, // 把args给它
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
tpl.Template.Render(&buffer, c.RenderArgs)
|
||||
tpl.Template.Render(&buffer, c.ViewArgs)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
@@ -263,5 +263,5 @@ func (c BaseController) RenderRe(re info.Re) revel.Result {
|
||||
if strings.HasPrefix(re.Msg, "???") {
|
||||
re.Msg = oldMsg
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -49,8 +49,8 @@ $.bootstrapJsUrl
|
||||
|
||||
func (c Blog) render(templateName string, themePath string) revel.Result {
|
||||
isPreview := false
|
||||
if c.RenderArgs["isPreview"] != nil {
|
||||
themePath2 := c.RenderArgs["themePath"]
|
||||
if c.ViewArgs["isPreview"] != nil {
|
||||
themePath2 := c.ViewArgs["themePath"]
|
||||
if themePath2 == nil {
|
||||
return c.E404()
|
||||
}
|
||||
@@ -59,9 +59,9 @@ func (c Blog) render(templateName string, themePath string) revel.Result {
|
||||
c.setPreviewUrl()
|
||||
|
||||
// 因为common的themeInfo是从UserBlog.ThemeId来取的, 所以这里要fugai下
|
||||
c.RenderArgs["themeInfo"] = c.RenderArgs["themeInfoPreview"]
|
||||
c.ViewArgs["themeInfo"] = c.ViewArgs["themeInfoPreview"]
|
||||
}
|
||||
return blog.RenderTemplate(templateName, c.RenderArgs, revel.BasePath+"/"+themePath, isPreview)
|
||||
return blog.RenderTemplate(templateName, c.ViewArgs, revel.BasePath+"/"+themePath, isPreview)
|
||||
}
|
||||
|
||||
// 404
|
||||
@@ -125,19 +125,19 @@ func (c Blog) setPreviewUrl() {
|
||||
archiveUrl = blogUrl + "/archives/" + userIdOrEmail // blog.leanote.com/archive/userId
|
||||
tagsUrl = blogUrl + "/tags/" + userIdOrEmail // blog.leanote.com/archive/userId
|
||||
|
||||
c.RenderArgs["indexUrl"] = indexUrl
|
||||
c.RenderArgs["cateUrl"] = cateUrl
|
||||
c.RenderArgs["postUrl"] = postUrl
|
||||
c.RenderArgs["searchUrl"] = searchUrl
|
||||
c.RenderArgs["singleUrl"] = singleUrl // 单页
|
||||
c.RenderArgs["archiveUrl"] = archiveUrl
|
||||
c.RenderArgs["archivesUrl"] = archiveUrl // 别名
|
||||
c.RenderArgs["tagsUrl"] = tagsUrl
|
||||
c.RenderArgs["tagPostsUrl"] = blogUrl + "/tag/" + userIdOrEmail
|
||||
c.RenderArgs["tagUrl"] = c.RenderArgs["tagPostsUrl"]
|
||||
c.ViewArgs["indexUrl"] = indexUrl
|
||||
c.ViewArgs["cateUrl"] = cateUrl
|
||||
c.ViewArgs["postUrl"] = postUrl
|
||||
c.ViewArgs["searchUrl"] = searchUrl
|
||||
c.ViewArgs["singleUrl"] = singleUrl // 单页
|
||||
c.ViewArgs["archiveUrl"] = archiveUrl
|
||||
c.ViewArgs["archivesUrl"] = archiveUrl // 别名
|
||||
c.ViewArgs["tagsUrl"] = tagsUrl
|
||||
c.ViewArgs["tagPostsUrl"] = blogUrl + "/tag/" + userIdOrEmail
|
||||
c.ViewArgs["tagUrl"] = c.ViewArgs["tagPostsUrl"]
|
||||
|
||||
// themeBaseUrl 本theme的路径url, 可以加载js, css, images之类的
|
||||
c.RenderArgs["themeBaseUrl"] = "/" + theme.Path
|
||||
c.ViewArgs["themeBaseUrl"] = "/" + theme.Path
|
||||
}
|
||||
|
||||
// 各种地址设置
|
||||
@@ -152,36 +152,36 @@ func (c Blog) setUrl(userBlog info.UserBlog, userInfo info.User) {
|
||||
// 分类
|
||||
// 搜索
|
||||
// 查看
|
||||
c.RenderArgs["siteUrl"] = siteUrl
|
||||
c.RenderArgs["indexUrl"] = blogUrls.IndexUrl
|
||||
c.RenderArgs["cateUrl"] = blogUrls.CateUrl
|
||||
c.RenderArgs["postUrl"] = blogUrls.PostUrl
|
||||
c.RenderArgs["searchUrl"] = blogUrls.SearchUrl
|
||||
c.RenderArgs["singleUrl"] = blogUrls.SingleUrl // 单页
|
||||
c.RenderArgs["archiveUrl"] = blogUrls.ArchiveUrl
|
||||
c.RenderArgs["archivesUrl"] = blogUrls.ArchiveUrl // 别名
|
||||
c.RenderArgs["tagsUrl"] = blogUrls.TagsUrl
|
||||
c.RenderArgs["tagPostsUrl"] = blogUrls.TagPostsUrl
|
||||
c.RenderArgs["tagUrl"] = blogUrls.TagPostsUrl // 别名
|
||||
c.ViewArgs["siteUrl"] = siteUrl
|
||||
c.ViewArgs["indexUrl"] = blogUrls.IndexUrl
|
||||
c.ViewArgs["cateUrl"] = blogUrls.CateUrl
|
||||
c.ViewArgs["postUrl"] = blogUrls.PostUrl
|
||||
c.ViewArgs["searchUrl"] = blogUrls.SearchUrl
|
||||
c.ViewArgs["singleUrl"] = blogUrls.SingleUrl // 单页
|
||||
c.ViewArgs["archiveUrl"] = blogUrls.ArchiveUrl
|
||||
c.ViewArgs["archivesUrl"] = blogUrls.ArchiveUrl // 别名
|
||||
c.ViewArgs["tagsUrl"] = blogUrls.TagsUrl
|
||||
c.ViewArgs["tagPostsUrl"] = blogUrls.TagPostsUrl
|
||||
c.ViewArgs["tagUrl"] = blogUrls.TagPostsUrl // 别名
|
||||
|
||||
// themeBaseUrl 本theme的路径url, 可以加载js, css, images之类的
|
||||
c.RenderArgs["themeBaseUrl"] = "/" + userBlog.ThemePath
|
||||
c.ViewArgs["themeBaseUrl"] = "/" + userBlog.ThemePath
|
||||
|
||||
// 其它static js
|
||||
c.RenderArgs["jQueryUrl"] = "/js/jquery-1.9.0.min.js"
|
||||
c.ViewArgs["jQueryUrl"] = "/js/jquery-1.9.0.min.js"
|
||||
|
||||
c.RenderArgs["prettifyJsUrl"] = "/js/google-code-prettify/prettify.js"
|
||||
c.RenderArgs["prettifyCssUrl"] = "/js/google-code-prettify/prettify.css"
|
||||
c.ViewArgs["prettifyJsUrl"] = "/js/google-code-prettify/prettify.js"
|
||||
c.ViewArgs["prettifyCssUrl"] = "/js/google-code-prettify/prettify.css"
|
||||
|
||||
c.RenderArgs["blogCommonJsUrl"] = "/public/blog/js/common.js"
|
||||
c.ViewArgs["blogCommonJsUrl"] = "/public/blog/js/common.js"
|
||||
|
||||
c.RenderArgs["shareCommentCssUrl"] = "/public/blog/css/share_comment.css"
|
||||
c.RenderArgs["shareCommentJsUrl"] = "/public/blog/js/share_comment.js"
|
||||
c.ViewArgs["shareCommentCssUrl"] = "/public/blog/css/share_comment.css"
|
||||
c.ViewArgs["shareCommentJsUrl"] = "/public/blog/js/share_comment.js"
|
||||
|
||||
c.RenderArgs["fontAwesomeUrl"] = "/css/font-awesome-4.2.0/css/font-awesome.css"
|
||||
c.ViewArgs["fontAwesomeUrl"] = "/css/font-awesome-4.2.0/css/font-awesome.css"
|
||||
|
||||
c.RenderArgs["bootstrapCssUrl"] = "/css/bootstrap.css"
|
||||
c.RenderArgs["bootstrapJsUrl"] = "/js/bootstrap-min.js"
|
||||
c.ViewArgs["bootstrapCssUrl"] = "/css/bootstrap.css"
|
||||
c.ViewArgs["bootstrapJsUrl"] = "/js/bootstrap-min.js"
|
||||
}
|
||||
|
||||
// 笔记本分类
|
||||
@@ -265,8 +265,8 @@ func (c Blog) getCates(userBlog info.UserBlog) {
|
||||
}
|
||||
}
|
||||
|
||||
c.RenderArgs["cates"] = cates
|
||||
c.RenderArgs["catesTree"] = catesTree
|
||||
c.ViewArgs["cates"] = cates
|
||||
c.ViewArgs["catesTree"] = catesTree
|
||||
}
|
||||
|
||||
// 单页
|
||||
@@ -281,7 +281,7 @@ func (c Blog) getSingles(userId string) {
|
||||
singles2[i] = map[string]string{"title": page["Title"], "singleId": page["SingleId"]}
|
||||
}
|
||||
*/
|
||||
c.RenderArgs["singles"] = singles
|
||||
c.ViewArgs["singles"] = singles
|
||||
}
|
||||
|
||||
// $.blog = {userId, title, subTitle, desc, openComment, }
|
||||
@@ -300,11 +300,11 @@ func (c Blog) setBlog(userBlog info.UserBlog, userInfo info.User) {
|
||||
"SubDomain": userBlog.SubDomain,
|
||||
"Domain": userBlog.Domain,
|
||||
}
|
||||
c.RenderArgs["blogInfo"] = blogInfo
|
||||
c.ViewArgs["blogInfo"] = blogInfo
|
||||
}
|
||||
|
||||
func (c Blog) setPaging(pageInfo info.Page) {
|
||||
c.RenderArgs["paging"] = pageInfo
|
||||
c.ViewArgs["paging"] = pageInfo
|
||||
}
|
||||
|
||||
// 公共
|
||||
@@ -315,13 +315,13 @@ func (c Blog) blogCommon(userId string, userBlog info.UserBlog, userInfo info.Us
|
||||
return false, userBlog
|
||||
}
|
||||
}
|
||||
// c.RenderArgs["userInfo"] = userInfo
|
||||
// c.ViewArgs["userInfo"] = userInfo
|
||||
|
||||
// 最新笔记
|
||||
_, recentBlogs := blogService.ListBlogs(userId, "", 1, 5, userBlog.SortField, userBlog.IsAsc)
|
||||
c.RenderArgs["recentPosts"] = blogService.FixBlogs(recentBlogs)
|
||||
c.RenderArgs["latestPosts"] = c.RenderArgs["recentPosts"]
|
||||
c.RenderArgs["tags"] = blogService.GetBlogTags(userId)
|
||||
c.ViewArgs["recentPosts"] = blogService.FixBlogs(recentBlogs)
|
||||
c.ViewArgs["latestPosts"] = c.ViewArgs["recentPosts"]
|
||||
c.ViewArgs["tags"] = blogService.GetBlogTags(userId)
|
||||
|
||||
// 语言, url地址
|
||||
c.SetLocale()
|
||||
@@ -331,7 +331,7 @@ func (c Blog) blogCommon(userId string, userBlog info.UserBlog, userInfo info.Us
|
||||
userBlog = blogService.GetUserBlog(userId)
|
||||
}
|
||||
c.setBlog(userBlog, userInfo)
|
||||
// c.RenderArgs["userBlog"] = userBlog
|
||||
// c.ViewArgs["userBlog"] = userBlog
|
||||
|
||||
// 分类导航
|
||||
c.getCates(userBlog)
|
||||
@@ -342,12 +342,12 @@ func (c Blog) blogCommon(userId string, userBlog info.UserBlog, userInfo info.Us
|
||||
c.setUrl(userBlog, userInfo)
|
||||
|
||||
// 当前分类Id, 全设为""
|
||||
c.RenderArgs["curCateId"] = ""
|
||||
c.RenderArgs["curSingleId"] = ""
|
||||
c.ViewArgs["curCateId"] = ""
|
||||
c.ViewArgs["curSingleId"] = ""
|
||||
|
||||
// 得到主题信息
|
||||
themeInfo := themeService.GetThemeInfo(userBlog.ThemeId.Hex(), userBlog.Style)
|
||||
c.RenderArgs["themeInfo"] = themeInfo
|
||||
c.ViewArgs["themeInfo"] = themeInfo
|
||||
|
||||
// Log(">>")
|
||||
// Log(userBlog.Style)
|
||||
@@ -404,9 +404,9 @@ func (c Blog) Tags(userIdOrEmail string) (re revel.Result) {
|
||||
return c.e404(userBlog.ThemePath) // 404 TODO 使用用户的404
|
||||
}
|
||||
|
||||
c.RenderArgs["curIsTags"] = true
|
||||
c.ViewArgs["curIsTags"] = true
|
||||
tags := blogService.GetBlogTags(userId)
|
||||
c.RenderArgs["tags"] = tags
|
||||
c.ViewArgs["tags"] = tags
|
||||
return c.render("tags.html", userBlog.ThemePath)
|
||||
}
|
||||
|
||||
@@ -443,15 +443,15 @@ func (c Blog) Tag(userIdOrEmail, tag string) (re revel.Result) {
|
||||
tag = userIdOrEmail
|
||||
}
|
||||
|
||||
c.RenderArgs["curIsTagPosts"] = true
|
||||
c.RenderArgs["curTag"] = tag
|
||||
c.ViewArgs["curIsTagPosts"] = true
|
||||
c.ViewArgs["curTag"] = tag
|
||||
page := c.GetPage()
|
||||
pageInfo, blogs := blogService.SearchBlogByTags([]string{tag}, userId, page, userBlog.PerPageSize, userBlog.SortField, userBlog.IsAsc)
|
||||
c.setPaging(pageInfo)
|
||||
|
||||
c.RenderArgs["posts"] = blogService.FixBlogs(blogs)
|
||||
tagPostsUrl := c.RenderArgs["tagPostsUrl"].(string)
|
||||
c.RenderArgs["pagingBaseUrl"] = tagPostsUrl + "/" + tag
|
||||
c.ViewArgs["posts"] = blogService.FixBlogs(blogs)
|
||||
tagPostsUrl := c.ViewArgs["tagPostsUrl"].(string)
|
||||
c.ViewArgs["pagingBaseUrl"] = tagPostsUrl + "/" + tag
|
||||
|
||||
return c.render("tag_posts.html", userBlog.ThemePath)
|
||||
}
|
||||
@@ -490,16 +490,16 @@ func (c Blog) Archives(userIdOrEmail string, cateId string, year, month int) (re
|
||||
}
|
||||
|
||||
arcs := blogService.ListBlogsArchive(userId, notebookId, year, month, "PublicTime", false)
|
||||
c.RenderArgs["archives"] = arcs
|
||||
c.ViewArgs["archives"] = arcs
|
||||
|
||||
c.RenderArgs["curIsArchive"] = true
|
||||
c.ViewArgs["curIsArchive"] = true
|
||||
if notebookId != "" {
|
||||
notebook := notebookService.GetNotebookById(notebookId)
|
||||
c.RenderArgs["curCateTitle"] = notebook.Title
|
||||
c.RenderArgs["curCateId"] = notebookId
|
||||
c.ViewArgs["curCateTitle"] = notebook.Title
|
||||
c.ViewArgs["curCateId"] = notebookId
|
||||
}
|
||||
c.RenderArgs["curYear"] = year
|
||||
c.RenderArgs["curMonth"] = month
|
||||
c.ViewArgs["curYear"] = year
|
||||
c.ViewArgs["curMonth"] = month
|
||||
|
||||
return c.render("archive.html", userBlog.ThemePath)
|
||||
}
|
||||
@@ -541,15 +541,15 @@ func (c Blog) Cate(userIdOrEmail string, notebookId string) (re revel.Result) {
|
||||
page := c.GetPage()
|
||||
pageInfo, blogs := blogService.ListBlogs(userId, notebookId2, page, userBlog.PerPageSize, userBlog.SortField, userBlog.IsAsc)
|
||||
blogs2 := blogService.FixBlogs(blogs)
|
||||
c.RenderArgs["posts"] = blogs2
|
||||
c.ViewArgs["posts"] = blogs2
|
||||
|
||||
c.setPaging(pageInfo)
|
||||
|
||||
c.RenderArgs["curCateTitle"] = notebook.Title
|
||||
c.RenderArgs["curCateId"] = notebookId2
|
||||
cateUrl := c.RenderArgs["cateUrl"].(string)
|
||||
c.RenderArgs["pagingBaseUrl"] = cateUrl + "/" + notebookId
|
||||
c.RenderArgs["curIsCate"] = true
|
||||
c.ViewArgs["curCateTitle"] = notebook.Title
|
||||
c.ViewArgs["curCateId"] = notebookId2
|
||||
cateUrl := c.ViewArgs["cateUrl"].(string)
|
||||
c.ViewArgs["pagingBaseUrl"] = cateUrl + "/" + notebookId
|
||||
c.ViewArgs["curIsCate"] = true
|
||||
|
||||
return c.render("cate.html", userBlog.ThemePath)
|
||||
}
|
||||
@@ -594,12 +594,12 @@ func (c Blog) Index(userIdOrEmail string) (re revel.Result) {
|
||||
page := c.GetPage()
|
||||
pageInfo, blogs := blogService.ListBlogs(userId, "", page, userBlog.PerPageSize, userBlog.SortField, userBlog.IsAsc)
|
||||
blogs2 := blogService.FixBlogs(blogs)
|
||||
c.RenderArgs["posts"] = blogs2
|
||||
c.ViewArgs["posts"] = blogs2
|
||||
|
||||
c.setPaging(pageInfo)
|
||||
c.RenderArgs["pagingBaseUrl"] = c.RenderArgs["indexUrl"]
|
||||
c.ViewArgs["pagingBaseUrl"] = c.ViewArgs["indexUrl"]
|
||||
|
||||
c.RenderArgs["curIsIndex"] = true
|
||||
c.ViewArgs["curIsIndex"] = true
|
||||
|
||||
return c.render("index.html", userBlog.ThemePath)
|
||||
}
|
||||
@@ -631,9 +631,9 @@ func (c Blog) Post(userIdOrEmail, noteId string) (re revel.Result) {
|
||||
}
|
||||
|
||||
post := blogService.FixBlog(blogInfo)
|
||||
c.RenderArgs["post"] = post
|
||||
// c.RenderArgs["userInfo"] = userInfo
|
||||
c.RenderArgs["curIsPost"] = true
|
||||
c.ViewArgs["post"] = post
|
||||
// c.ViewArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["curIsPost"] = true
|
||||
|
||||
// 上一篇, 下一篇
|
||||
var baseTime interface{}
|
||||
@@ -649,10 +649,10 @@ func (c Blog) Post(userIdOrEmail, noteId string) (re revel.Result) {
|
||||
|
||||
prePost, nextPost := blogService.PreNextBlog(userId, userBlog.SortField, userBlog.IsAsc, post.NoteId, baseTime)
|
||||
if prePost.NoteId != "" {
|
||||
c.RenderArgs["prePost"] = prePost
|
||||
c.ViewArgs["prePost"] = prePost
|
||||
}
|
||||
if nextPost.NoteId != "" {
|
||||
c.RenderArgs["nextPost"] = nextPost
|
||||
c.ViewArgs["nextPost"] = nextPost
|
||||
}
|
||||
return c.render("post.html", userBlog.ThemePath)
|
||||
}
|
||||
@@ -682,7 +682,7 @@ func (c Blog) Single(userIdOrEmail, singleId string) (re revel.Result) {
|
||||
panic("")
|
||||
}
|
||||
|
||||
c.RenderArgs["single"] = map[string]interface{}{
|
||||
c.ViewArgs["single"] = map[string]interface{}{
|
||||
"SingleId": single.SingleId.Hex(),
|
||||
"Title": single.Title,
|
||||
"UrlTitle": single.UrlTitle,
|
||||
@@ -690,8 +690,8 @@ func (c Blog) Single(userIdOrEmail, singleId string) (re revel.Result) {
|
||||
"CreatedTime": single.CreatedTime,
|
||||
"UpdatedTime": single.UpdatedTime,
|
||||
}
|
||||
c.RenderArgs["curSingleId"] = single.SingleId.Hex()
|
||||
c.RenderArgs["curIsSingle"] = true
|
||||
c.ViewArgs["curSingleId"] = single.SingleId.Hex()
|
||||
c.ViewArgs["curIsSingle"] = true
|
||||
|
||||
return c.render("single.html", userBlog.ThemePath)
|
||||
}
|
||||
@@ -716,7 +716,7 @@ func (c Blog) Search(userIdOrEmail, keywords string) (re revel.Result) {
|
||||
} else {
|
||||
userInfo = userService.GetUserInfoByAny(userIdOrEmail)
|
||||
}
|
||||
// c.RenderArgs["userInfo"] = userInfo
|
||||
// c.ViewArgs["userInfo"] = userInfo
|
||||
userId = userInfo.UserId.Hex()
|
||||
var ok = false
|
||||
if ok, userBlog = c.blogCommon(userId, userBlog, userInfo); !ok {
|
||||
@@ -727,11 +727,11 @@ func (c Blog) Search(userIdOrEmail, keywords string) (re revel.Result) {
|
||||
pageInfo, blogs := blogService.SearchBlog(keywords, userId, page, userBlog.PerPageSize, userBlog.SortField, userBlog.IsAsc)
|
||||
c.setPaging(pageInfo)
|
||||
|
||||
c.RenderArgs["posts"] = blogService.FixBlogs(blogs)
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
searchUrl, _ := c.RenderArgs["searchUrl"].(string)
|
||||
c.RenderArgs["pagingBaseUrl"] = searchUrl + "?keywords=" + keywords
|
||||
c.RenderArgs["curIsSearch"] = true
|
||||
c.ViewArgs["posts"] = blogService.FixBlogs(blogs)
|
||||
c.ViewArgs["keywords"] = keywords
|
||||
searchUrl, _ := c.ViewArgs["searchUrl"].(string)
|
||||
c.ViewArgs["pagingBaseUrl"] = searchUrl + "?keywords=" + keywords
|
||||
c.ViewArgs["curIsSearch"] = true
|
||||
|
||||
return c.render("search.html", userBlog.ThemePath)
|
||||
}
|
||||
@@ -741,7 +741,7 @@ func (c Blog) setRenderUserInfo(userInfo info.User) {
|
||||
if userInfo.Username == "" {
|
||||
userInfo.Username = userInfo.Email
|
||||
}
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["userInfo"] = userInfo
|
||||
}
|
||||
|
||||
//----------------
|
||||
@@ -753,7 +753,7 @@ func (c Blog) GetPostStat(noteId string) revel.Result {
|
||||
re.Ok = true
|
||||
statInfo := blogService.GetBlogStat(noteId)
|
||||
re.Item = statInfo
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// jsonP
|
||||
@@ -778,7 +778,7 @@ func (c Blog) GetLikes(noteId string, callback string) revel.Result {
|
||||
result["hasMoreLikedUser"] = hasMoreLikedUser
|
||||
|
||||
re.Item = result
|
||||
return c.RenderJsonP(callback, re)
|
||||
return c.RenderJSONP(callback, re)
|
||||
}
|
||||
func (c Blog) GetLikesAndComments(noteId, callback string) revel.Result {
|
||||
userId := c.GetUserId()
|
||||
@@ -806,13 +806,13 @@ func (c Blog) GetLikesAndComments(noteId, callback string) revel.Result {
|
||||
result["comments"] = comments
|
||||
result["commentUserInfo"] = commentUserInfo
|
||||
re.Item = result
|
||||
return c.RenderJsonP(callback, re)
|
||||
return c.RenderJSONP(callback, re)
|
||||
}
|
||||
|
||||
func (c Blog) IncReadNum(noteId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.IncReadNum(noteId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 点赞, 要用jsonp
|
||||
@@ -820,7 +820,7 @@ func (c Blog) LikePost(noteId string, callback string) revel.Result {
|
||||
re := info.NewRe()
|
||||
userId := c.GetUserId()
|
||||
re.Ok, re.Item = blogService.LikeBlog(noteId, userId)
|
||||
return c.RenderJsonP(callback, re)
|
||||
return c.RenderJSONP(callback, re)
|
||||
}
|
||||
func (c Blog) GetComments(noteId string, callback string) revel.Result {
|
||||
// 评论
|
||||
@@ -836,24 +836,24 @@ func (c Blog) GetComments(noteId string, callback string) revel.Result {
|
||||
re.Item = result
|
||||
|
||||
if callback != "" {
|
||||
return c.RenderJsonP(callback, result)
|
||||
return c.RenderJSONP(callback, result)
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// jsonp
|
||||
func (c Blog) DeleteComment(noteId, commentId string, callback string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.DeleteComment(noteId, commentId, c.GetUserId())
|
||||
return c.RenderJsonP(callback, re)
|
||||
return c.RenderJSONP(callback, re)
|
||||
}
|
||||
|
||||
// jsonp
|
||||
func (c Blog) CommentPost(noteId, content, toCommentId string, callback string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Item = blogService.Comment(noteId, toCommentId, c.GetUserId(), content)
|
||||
return c.RenderJsonP(callback, re)
|
||||
return c.RenderJSONP(callback, re)
|
||||
}
|
||||
|
||||
// jsonp
|
||||
@@ -862,7 +862,7 @@ func (c Blog) LikeComment(commentId string, callback string) revel.Result {
|
||||
ok, isILikeIt, num := blogService.LikeComment(commentId, c.GetUserId())
|
||||
re.Ok = ok
|
||||
re.Item = bson.M{"IsILikeIt": isILikeIt, "Num": num}
|
||||
return c.RenderJsonP(callback, re)
|
||||
return c.RenderJSONP(callback, re)
|
||||
}
|
||||
|
||||
// 显示分类的最近博客, jsonp
|
||||
@@ -898,5 +898,5 @@ func (c Blog) ListCateLatest(notebookId, callback string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.List = blogs
|
||||
return c.RenderJsonP(callback, re)
|
||||
return c.RenderJSONP(callback, re)
|
||||
}
|
||||
@@ -24,9 +24,9 @@ type File struct {
|
||||
func (c File) UploadBlogLogo() revel.Result {
|
||||
re := c.uploadImage("blogLogo", "")
|
||||
|
||||
c.RenderArgs["fileUrlPath"] = re.Id
|
||||
c.RenderArgs["resultCode"] = re.Code
|
||||
c.RenderArgs["resultMsg"] = re.Msg
|
||||
c.ViewArgs["fileUrlPath"] = re.Id
|
||||
c.ViewArgs["resultCode"] = re.Code
|
||||
c.ViewArgs["resultMsg"] = re.Msg
|
||||
|
||||
return c.RenderTemplate("file/blog_logo.html")
|
||||
}
|
||||
@@ -54,16 +54,16 @@ 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
|
||||
c.ViewArgs["fileUrlPath"] = re.Id
|
||||
c.ViewArgs["resultCode"] = re.Code
|
||||
c.ViewArgs["resultMsg"] = re.Msg
|
||||
|
||||
if re.Ok {
|
||||
re.Ok = userService.UpdateAvatar(c.GetUserId(), re.Id)
|
||||
@@ -72,13 +72,13 @@ func (c File) UploadAvatar() revel.Result {
|
||||
}
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// leaui image plugin upload image
|
||||
func (c File) UploadImageLeaui(albumId string) revel.Result {
|
||||
re := c.uploadImage("", albumId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 上传图片, 公用方法
|
||||
@@ -201,19 +201,19 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
|
||||
// get all images by userId with page
|
||||
func (c File) GetImages(albumId, key string, page int) revel.Result {
|
||||
re := fileService.ListImagesWithPage(c.GetUserId(), albumId, key, page, 12)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c File) UpdateImageTitle(fileId, title string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = fileService.UpdateImageTitle(c.GetUserId(), fileId, title)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c File) DeleteImage(fileId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = fileService.DeleteImage(c.GetUserId(), fileId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
//-----------
|
||||
@@ -235,7 +235,7 @@ func (c File) OutputImage(noteId, fileId string) revel.Result {
|
||||
func (c File) CopyImage(userId, fileId, toUserId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Id = fileService.CopyImage(userId, fileId, toUserId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 复制外网的图片
|
||||
@@ -251,13 +251,13 @@ func (c File) CopyHttpImage(src string) revel.Result {
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
filesize, filename, _, ok := netutil.WriteUrl(src, dir)
|
||||
|
||||
if !ok {
|
||||
re.Msg = "copy error"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// File
|
||||
@@ -273,5 +273,5 @@ func (c File) CopyHttpImage(src string) revel.Result {
|
||||
// re.Item = fileInfo.Path
|
||||
re.Ok, re.Msg = fileService.AddImage(fileInfo, "", c.GetUserId(), true)
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -23,8 +23,8 @@ func (c Index) Default() revel.Result {
|
||||
// leanote展示页, 没有登录的, 或已登录明确要进该页的
|
||||
func (c Index) Index() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.RenderArgs["title"] = "leanote"
|
||||
c.RenderArgs["openRegister"] = configService.GlobalStringConfigs["openRegister"]
|
||||
c.ViewArgs["title"] = "leanote"
|
||||
c.ViewArgs["openRegister"] = configService.GlobalStringConfigs["openRegister"]
|
||||
c.SetLocale()
|
||||
|
||||
return c.RenderTemplate("home/index.html")
|
||||
@@ -40,5 +40,5 @@ func (c Index) Suggestion(addr, suggestion string) revel.Result {
|
||||
emailService.SendEmail("leanote@leanote.com", "建议", "UserId: "+c.GetUserId()+" <br /> Suggestions: "+suggestion)
|
||||
}()
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -12,5 +12,5 @@ type NoteContentHistory struct {
|
||||
func (c NoteContentHistory) ListHistories(noteId string) revel.Result {
|
||||
histories := noteContentHistoryService.ListHistories(noteId, c.GetUserId())
|
||||
|
||||
return c.RenderJson(histories)
|
||||
return c.RenderJSON(histories)
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func (c Note) Index(noteId, online string) revel.Result {
|
||||
return c.Redirect("/login")
|
||||
}
|
||||
|
||||
c.RenderArgs["openRegister"] = configService.IsOpenRegister()
|
||||
c.ViewArgs["openRegister"] = configService.IsOpenRegister()
|
||||
|
||||
// 已登录了, 那么得到所有信息
|
||||
notebooks := notebookService.GetNotebooks(userId)
|
||||
@@ -58,16 +58,16 @@ func (c Note) Index(noteId, online string) revel.Result {
|
||||
noteContent = noteService.GetNoteContent(noteId, noteOwner)
|
||||
|
||||
hasRightNoteId = true
|
||||
c.RenderArgs["curNoteId"] = noteId
|
||||
c.RenderArgs["curNotebookId"] = note.NotebookId.Hex()
|
||||
c.ViewArgs["curNoteId"] = noteId
|
||||
c.ViewArgs["curNotebookId"] = note.NotebookId.Hex()
|
||||
|
||||
// 打开的是共享的笔记, 那么判断是否是共享给我的默认笔记
|
||||
if noteOwner != c.GetUserId() {
|
||||
if shareService.HasReadPerm(noteOwner, c.GetUserId(), noteId) {
|
||||
// 不要获取notebook下的笔记
|
||||
// 在前端下发请求
|
||||
c.RenderArgs["curSharedNoteNotebookId"] = note.NotebookId.Hex()
|
||||
c.RenderArgs["curSharedUserId"] = noteOwner
|
||||
c.ViewArgs["curSharedNoteNotebookId"] = note.NotebookId.Hex()
|
||||
c.ViewArgs["curSharedUserId"] = noteOwner
|
||||
// 没有读写权限
|
||||
} else {
|
||||
hasRightNoteId = false
|
||||
@@ -97,7 +97,7 @@ func (c Note) Index(noteId, online string) revel.Result {
|
||||
|
||||
// 得到最近的笔记
|
||||
_, latestNotes := noteService.ListNotes(c.GetUserId(), "", false, c.GetPage(), 50, defaultSortField, false, false)
|
||||
c.RenderArgs["latestNotes"] = latestNotes
|
||||
c.ViewArgs["latestNotes"] = latestNotes
|
||||
}
|
||||
|
||||
// 没有传入笔记
|
||||
@@ -106,27 +106,27 @@ func (c Note) Index(noteId, online string) revel.Result {
|
||||
_, notes = noteService.ListNotes(c.GetUserId(), "", false, c.GetPage(), 50, defaultSortField, false, false)
|
||||
if len(notes) > 0 {
|
||||
noteContent = noteService.GetNoteContent(notes[0].NoteId.Hex(), userId)
|
||||
c.RenderArgs["curNoteId"] = notes[0].NoteId.Hex()
|
||||
c.ViewArgs["curNoteId"] = notes[0].NoteId.Hex()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 当然, 还需要得到第一个notes的content
|
||||
//...
|
||||
c.RenderArgs["isAdmin"] = configService.GetAdminUsername() == userInfo.Username
|
||||
c.ViewArgs["isAdmin"] = configService.GetAdminUsername() == userInfo.Username
|
||||
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.RenderArgs["notebooks"] = notebooks
|
||||
c.RenderArgs["shareNotebooks"] = shareNotebooks // note信息在notes列表中
|
||||
c.RenderArgs["sharedUserInfos"] = sharedUserInfos
|
||||
c.ViewArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["notebooks"] = notebooks
|
||||
c.ViewArgs["shareNotebooks"] = shareNotebooks // note信息在notes列表中
|
||||
c.ViewArgs["sharedUserInfos"] = sharedUserInfos
|
||||
|
||||
c.RenderArgs["notes"] = notes
|
||||
c.RenderArgs["noteContentJson"] = noteContent
|
||||
c.RenderArgs["noteContent"] = noteContent.Content
|
||||
c.ViewArgs["notes"] = notes
|
||||
c.ViewArgs["noteContentJson"] = noteContent
|
||||
c.ViewArgs["noteContent"] = noteContent.Content
|
||||
|
||||
c.RenderArgs["tags"] = tagService.GetTags(c.GetUserId())
|
||||
c.ViewArgs["tags"] = tagService.GetTags(c.GetUserId())
|
||||
|
||||
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
|
||||
c.ViewArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
|
||||
|
||||
// return c.RenderTemplate("note/note.html")
|
||||
|
||||
@@ -142,24 +142,34 @@ func (c Note) Index(noteId, online string) revel.Result {
|
||||
// 否则, 转向登录页面
|
||||
func (c Note) ListNotes(notebookId string) revel.Result {
|
||||
_, notes := noteService.ListNotes(c.GetUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(notes)
|
||||
return c.RenderJSON(notes)
|
||||
}
|
||||
|
||||
// 得到trash
|
||||
func (c Note) ListTrashNotes() revel.Result {
|
||||
_, notes := noteService.ListNotes(c.GetUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(notes)
|
||||
return c.RenderJSON(notes)
|
||||
}
|
||||
|
||||
// 得到note和内容
|
||||
func (c Note) GetNoteAndContent(noteId string) revel.Result {
|
||||
return c.RenderJson(noteService.GetNoteAndContent(noteId, c.GetUserId()))
|
||||
return c.RenderJSON(noteService.GetNoteAndContent(noteId, c.GetUserId()))
|
||||
}
|
||||
|
||||
func (c Note) GetNoteAndContentBySrc(src string) revel.Result {
|
||||
noteId, noteAndContent := noteService.GetNoteAndContentBySrc(src, c.GetUserId())
|
||||
ret := info.Re{}
|
||||
if noteId != "" {
|
||||
ret.Ok = true
|
||||
ret.Item = noteAndContent
|
||||
}
|
||||
return c.RenderJSON(ret)
|
||||
}
|
||||
|
||||
// 得到内容
|
||||
func (c Note) GetNoteContent(noteId string) revel.Result {
|
||||
noteContent := noteService.GetNoteContent(noteId, c.GetUserId())
|
||||
return c.RenderJson(noteContent)
|
||||
return c.RenderJSON(noteContent)
|
||||
}
|
||||
|
||||
// 这里不能用json, 要用post
|
||||
@@ -177,6 +187,7 @@ func (c Note) UpdateNoteOrContent(noteOrContent info.NoteOrContent) revel.Result
|
||||
NoteId: bson.ObjectIdHex(noteOrContent.NoteId),
|
||||
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
|
||||
Title: noteOrContent.Title,
|
||||
Src: noteOrContent.Src, // 来源
|
||||
Tags: strings.Split(noteOrContent.Tags, ","),
|
||||
Desc: noteOrContent.Desc,
|
||||
ImgSrc: noteOrContent.ImgSrc,
|
||||
@@ -190,7 +201,7 @@ func (c Note) UpdateNoteOrContent(noteOrContent info.NoteOrContent) revel.Result
|
||||
Abstract: noteOrContent.Abstract}
|
||||
|
||||
note = noteService.AddNoteAndContentForController(note, noteContent, c.GetUserId())
|
||||
return c.RenderJson(note)
|
||||
return c.RenderJSON(note)
|
||||
}
|
||||
|
||||
noteUpdate := bson.M{}
|
||||
@@ -237,7 +248,7 @@ func (c Note) UpdateNoteOrContent(noteOrContent info.NoteOrContent) revel.Result
|
||||
Log(contentOk)
|
||||
Log(contentMsg)
|
||||
|
||||
return c.RenderJson(true)
|
||||
return c.RenderJSON(true)
|
||||
}
|
||||
|
||||
// 删除note/ 删除别人共享给我的笔记
|
||||
@@ -247,19 +258,19 @@ func (c Note) DeleteNote(noteIds []string, isShared bool) revel.Result {
|
||||
for _, noteId := range noteIds {
|
||||
trashService.DeleteNote(noteId, c.GetUserId())
|
||||
}
|
||||
return c.RenderJson(true)
|
||||
return c.RenderJSON(true)
|
||||
}
|
||||
|
||||
for _, noteId := range noteIds {
|
||||
trashService.DeleteSharedNote(noteId, c.GetUserId())
|
||||
}
|
||||
|
||||
return c.RenderJson(true)
|
||||
return c.RenderJSON(true)
|
||||
}
|
||||
|
||||
// 删除trash, 已弃用, 用DeleteNote
|
||||
func (c Note) DeleteTrash(noteId string) revel.Result {
|
||||
return c.RenderJson(trashService.DeleteTrash(noteId, c.GetUserId()))
|
||||
return c.RenderJSON(trashService.DeleteTrash(noteId, c.GetUserId()))
|
||||
}
|
||||
|
||||
// 移动note
|
||||
@@ -268,7 +279,7 @@ func (c Note) MoveNote(noteIds []string, notebookId string) revel.Result {
|
||||
for _, noteId := range noteIds {
|
||||
noteService.MoveNote(noteId, notebookId, userId)
|
||||
}
|
||||
return c.RenderJson(true)
|
||||
return c.RenderJSON(true)
|
||||
}
|
||||
|
||||
// 复制note
|
||||
@@ -281,7 +292,7 @@ func (c Note) CopyNote(noteIds []string, notebookId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.Item = copyNotes
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 复制别人共享的笔记给我
|
||||
@@ -294,7 +305,7 @@ func (c Note) CopySharedNote(noteIds []string, notebookId, fromUserId string) re
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.Item = copyNotes
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
//------------
|
||||
@@ -302,13 +313,13 @@ func (c Note) CopySharedNote(noteIds []string, notebookId, fromUserId string) re
|
||||
// 通过title搜索
|
||||
func (c Note) SearchNote(key string) revel.Result {
|
||||
_, blogs := noteService.SearchNote(key, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false, false)
|
||||
return c.RenderJson(blogs)
|
||||
return c.RenderJSON(blogs)
|
||||
}
|
||||
|
||||
// 通过tags搜索
|
||||
func (c Note) SearchNoteByTags(tags []string) revel.Result {
|
||||
_, blogs := noteService.SearchNoteByTags(tags, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false)
|
||||
return c.RenderJson(blogs)
|
||||
return c.RenderJSON(blogs)
|
||||
}
|
||||
|
||||
// 生成PDF
|
||||
@@ -338,6 +349,8 @@ func (c Note) ToPdf(noteId, appKey string) revel.Result {
|
||||
siteUrlPattern = strings.Replace(siteUrlPattern, "http", "https*", 1)
|
||||
}
|
||||
|
||||
siteUrlPattern = "(?:" + siteUrlPattern + ")*"
|
||||
|
||||
regImage, _ := regexp.Compile(`<img .*?(src=('|")` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})("|'))`)
|
||||
|
||||
findsImage := regImage.FindAllStringSubmatch(contentStr, -1) // 查找所有的
|
||||
@@ -384,11 +397,11 @@ func (c Note) ToPdf(noteId, appKey string) revel.Result {
|
||||
} else {
|
||||
note.Tags = nil
|
||||
}
|
||||
c.RenderArgs["blog"] = note
|
||||
c.RenderArgs["content"] = contentStr
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["blog"] = note
|
||||
c.ViewArgs["content"] = contentStr
|
||||
c.ViewArgs["userInfo"] = userInfo
|
||||
userBlog := blogService.GetUserBlog(noteUserId)
|
||||
c.RenderArgs["userBlog"] = userBlog
|
||||
c.ViewArgs["userBlog"] = userBlog
|
||||
|
||||
return c.RenderTemplate("file/pdf.html")
|
||||
}
|
||||
@@ -475,5 +488,5 @@ func (c Note) SetNote2Blog(noteIds []string, isBlog, isTop bool) revel.Result {
|
||||
for _, noteId := range noteIds {
|
||||
noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
|
||||
}
|
||||
return c.RenderJson(true)
|
||||
return c.RenderJSON(true)
|
||||
}
|
||||
@@ -14,18 +14,18 @@ type Notebook struct {
|
||||
}
|
||||
|
||||
func (c Notebook) Index(notebook info.Notebook, i int, name string) revel.Result {
|
||||
return c.RenderJson(notebook)
|
||||
return c.RenderJSON(notebook)
|
||||
}
|
||||
|
||||
// 得到用户的所有笔记本
|
||||
func (c Notebook) GetNotebooks() revel.Result {
|
||||
re := notebookService.GetNotebooks(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c Notebook) DeleteNotebook(notebookId string) revel.Result {
|
||||
re, msg := notebookService.DeleteNotebook(c.GetUserId(), notebookId)
|
||||
return c.RenderJson(info.Re{Ok: re, Msg: msg})
|
||||
return c.RenderJSON(info.Re{Ok: re, Msg: msg})
|
||||
}
|
||||
|
||||
// 添加notebook
|
||||
@@ -41,21 +41,21 @@ func (c Notebook) AddNotebook(notebookId, title, parentNotebookId string) revel.
|
||||
re, notebook := notebookService.AddNotebook(notebook)
|
||||
|
||||
if re {
|
||||
return c.RenderJson(notebook)
|
||||
return c.RenderJSON(notebook)
|
||||
} else {
|
||||
return c.RenderJson(false)
|
||||
return c.RenderJSON(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 修改标题
|
||||
func (c Notebook) UpdateNotebookTitle(notebookId, title string) revel.Result {
|
||||
return c.RenderJson(notebookService.UpdateNotebookTitle(notebookId, c.GetUserId(), title))
|
||||
return c.RenderJSON(notebookService.UpdateNotebookTitle(notebookId, c.GetUserId(), title))
|
||||
}
|
||||
|
||||
// 排序
|
||||
// 无用
|
||||
// func (c Notebook) SortNotebooks(notebookId2Seqs map[string]int) revel.Result {
|
||||
// return c.RenderJson(notebookService.SortNotebooks(c.GetUserId(), notebookId2Seqs))
|
||||
// return c.RenderJSON(notebookService.SortNotebooks(c.GetUserId(), notebookId2Seqs))
|
||||
// }
|
||||
|
||||
// 调整notebooks, 可能是排序, 可能是移动到其它笔记本下
|
||||
@@ -70,11 +70,11 @@ func (c Notebook) DragNotebooks(data string) revel.Result {
|
||||
info := DragNotebooksInfo{}
|
||||
json.Unmarshal([]byte(data), &info)
|
||||
|
||||
return c.RenderJson(notebookService.DragNotebooks(c.GetUserId(), info.CurNotebookId, info.ParentNotebookId, info.Siblings))
|
||||
return c.RenderJSON(notebookService.DragNotebooks(c.GetUserId(), info.CurNotebookId, info.ParentNotebookId, info.Siblings))
|
||||
}
|
||||
|
||||
// 设置notebook <-> blog
|
||||
func (c Notebook) SetNotebook2Blog(notebookId string, isBlog bool) revel.Result {
|
||||
re := notebookService.ToBlog(c.GetUserId(), notebookId, isBlog)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -32,10 +32,10 @@ func (c Preview) getPreviewThemeAbsolutePath(themeId string) bool {
|
||||
}
|
||||
theme := themeService.GetTheme(c.GetUserId(), themeId)
|
||||
|
||||
c.RenderArgs["isPreview"] = true
|
||||
c.RenderArgs["themeId"] = themeId
|
||||
c.RenderArgs["themeInfoPreview"] = theme.Info
|
||||
c.RenderArgs["themePath"] = theme.Path
|
||||
c.ViewArgs["isPreview"] = true
|
||||
c.ViewArgs["themeId"] = themeId
|
||||
c.ViewArgs["themeInfoPreview"] = theme.Info
|
||||
c.ViewArgs["themePath"] = theme.Path
|
||||
if theme.Path == "" {
|
||||
return false
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func (c Preview) Index(userIdOrEmail string, themeId string) revel.Result {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Index(c.GetUserId())
|
||||
// return blog.RenderTemplate("index.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(themeId))
|
||||
// return blog.RenderTemplate("index.html", c.ViewArgs, c.getPreviewThemeAbsolutePath(themeId))
|
||||
}
|
||||
|
||||
func (c Preview) Tag(userIdOrEmail, tag string) revel.Result {
|
||||
@@ -62,42 +62,42 @@ func (c Preview) Tags(userIdOrEmail string) revel.Result {
|
||||
}
|
||||
return c.Blog.Tags(c.GetUserId())
|
||||
// if tag == "" {
|
||||
// return blog.RenderTemplate("tags.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("tags.html", c.ViewArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// }
|
||||
// return blog.RenderTemplate("tag_posts.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("tag_posts.html", c.ViewArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Archives(userIdOrEmail string, notebookId string, year, month int) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Archives(c.GetUserId(), notebookId, year, month)
|
||||
// return blog.RenderTemplate("archive.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("archive.html", c.ViewArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Cate(userIdOrEmail, notebookId string) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Cate(userIdOrEmail, notebookId)
|
||||
// return blog.RenderTemplate("cate.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("cate.html", c.ViewArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Post(userIdOrEmail, noteId string) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Post(userIdOrEmail, noteId)
|
||||
// return blog.RenderTemplate("view.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("view.html", c.ViewArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Single(userIdOrEmail, singleId string) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Single(userIdOrEmail, singleId)
|
||||
// return blog.RenderTemplate("single.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("single.html", c.ViewArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Search(userIdOrEmail, keywords string) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Search(c.GetUserId(), keywords)
|
||||
// return blog.RenderTemplate("search.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("search.html", c.ViewArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Resu
|
||||
}
|
||||
}
|
||||
|
||||
return c.RenderJson(status)
|
||||
return c.RenderJSON(status)
|
||||
}
|
||||
|
||||
// 添加共享notebook
|
||||
@@ -53,7 +53,7 @@ func (c Share) AddShareNotebook(notebookId string, emails []string, perm int) re
|
||||
}
|
||||
}
|
||||
|
||||
return c.RenderJson(status)
|
||||
return c.RenderJSON(status)
|
||||
}
|
||||
|
||||
// 得到notes
|
||||
@@ -63,10 +63,10 @@ func (c Share) ListShareNotes(notebookId, userId string) revel.Result {
|
||||
var notes []info.ShareNoteWithPerm
|
||||
if notebookId == "" {
|
||||
notes = shareService.ListShareNotes(c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false)
|
||||
return c.RenderJson(notes)
|
||||
return c.RenderJSON(notes)
|
||||
} else {
|
||||
// 有notebookId的
|
||||
return c.RenderJson(shareService.ListShareNotesByNotebookId(notebookId, c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false))
|
||||
return c.RenderJSON(shareService.ListShareNotesByNotebookId(notebookId, c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func (c Share) ListShareNotes(notebookId, userId string) revel.Result {
|
||||
// sharedUserId 是谁的笔记
|
||||
func (c Share) GetShareNoteContent(noteId, sharedUserId string) revel.Result {
|
||||
noteContent := shareService.GetShareNoteContent(noteId, c.GetUserId(), sharedUserId)
|
||||
return c.RenderJson(noteContent)
|
||||
return c.RenderJSON(noteContent)
|
||||
}
|
||||
|
||||
// 查看note的分享信息
|
||||
@@ -85,13 +85,13 @@ func (c Share) ListNoteShareUserInfo(noteId string) revel.Result {
|
||||
note := noteService.GetNote(noteId, c.GetUserId())
|
||||
|
||||
noteShareUserInfos := shareService.ListNoteShareUserInfo(noteId, c.GetUserId())
|
||||
c.RenderArgs["noteOrNotebookShareUserInfos"] = noteShareUserInfos
|
||||
c.ViewArgs["noteOrNotebookShareUserInfos"] = noteShareUserInfos
|
||||
|
||||
c.RenderArgs["noteOrNotebookShareGroupInfos"] = shareService.GetNoteShareGroups(noteId, c.GetUserId())
|
||||
c.ViewArgs["noteOrNotebookShareGroupInfos"] = shareService.GetNoteShareGroups(noteId, c.GetUserId())
|
||||
|
||||
c.RenderArgs["isNote"] = true
|
||||
c.RenderArgs["noteOrNotebookId"] = note.NoteId.Hex()
|
||||
c.RenderArgs["title"] = note.Title
|
||||
c.ViewArgs["isNote"] = true
|
||||
c.ViewArgs["noteOrNotebookId"] = note.NoteId.Hex()
|
||||
c.ViewArgs["title"] = note.Title
|
||||
|
||||
return c.RenderTemplate("share/note_notebook_share_user_infos.html")
|
||||
}
|
||||
@@ -99,14 +99,14 @@ func (c Share) ListNotebookShareUserInfo(notebookId string) revel.Result {
|
||||
notebook := notebookService.GetNotebook(notebookId, c.GetUserId())
|
||||
|
||||
notebookShareUserInfos := shareService.ListNotebookShareUserInfo(notebookId, c.GetUserId())
|
||||
c.RenderArgs["noteOrNotebookShareUserInfos"] = notebookShareUserInfos
|
||||
c.ViewArgs["noteOrNotebookShareUserInfos"] = notebookShareUserInfos
|
||||
|
||||
c.RenderArgs["noteOrNotebookShareGroupInfos"] = shareService.GetNotebookShareGroups(notebookId, c.GetUserId())
|
||||
LogJ(c.RenderArgs["noteOrNotebookShareGroupInfos"])
|
||||
c.ViewArgs["noteOrNotebookShareGroupInfos"] = shareService.GetNotebookShareGroups(notebookId, c.GetUserId())
|
||||
LogJ(c.ViewArgs["noteOrNotebookShareGroupInfos"])
|
||||
|
||||
c.RenderArgs["isNote"] = false
|
||||
c.RenderArgs["noteOrNotebookId"] = notebook.NotebookId.Hex()
|
||||
c.RenderArgs["title"] = notebook.Title
|
||||
c.ViewArgs["isNote"] = false
|
||||
c.ViewArgs["noteOrNotebookId"] = notebook.NotebookId.Hex()
|
||||
c.ViewArgs["title"] = notebook.Title
|
||||
|
||||
return c.RenderTemplate("share/note_notebook_share_user_infos.html")
|
||||
}
|
||||
@@ -114,38 +114,38 @@ func (c Share) ListNotebookShareUserInfo(notebookId string) revel.Result {
|
||||
//------------
|
||||
// 改变share note 权限
|
||||
func (c Share) UpdateShareNotePerm(noteId string, perm int, toUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.UpdateShareNotePerm(noteId, perm, c.GetUserId(), toUserId))
|
||||
return c.RenderJSON(shareService.UpdateShareNotePerm(noteId, perm, c.GetUserId(), toUserId))
|
||||
}
|
||||
|
||||
// 改变share notebook 权限
|
||||
func (c Share) UpdateShareNotebookPerm(notebookId string, perm int, toUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.UpdateShareNotebookPerm(notebookId, perm, c.GetUserId(), toUserId))
|
||||
return c.RenderJSON(shareService.UpdateShareNotebookPerm(notebookId, perm, c.GetUserId(), toUserId))
|
||||
}
|
||||
|
||||
//---------------
|
||||
// 删除share note
|
||||
func (c Share) DeleteShareNote(noteId string, toUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteShareNote(noteId, c.GetUserId(), toUserId))
|
||||
return c.RenderJSON(shareService.DeleteShareNote(noteId, c.GetUserId(), toUserId))
|
||||
}
|
||||
|
||||
// 删除share notebook
|
||||
func (c Share) DeleteShareNotebook(notebookId string, toUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteShareNotebook(notebookId, c.GetUserId(), toUserId))
|
||||
return c.RenderJSON(shareService.DeleteShareNotebook(notebookId, c.GetUserId(), toUserId))
|
||||
}
|
||||
|
||||
// 删除share note, 被共享方删除
|
||||
func (c Share) DeleteShareNoteBySharedUser(noteId string, fromUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteShareNote(noteId, fromUserId, c.GetUserId()))
|
||||
return c.RenderJSON(shareService.DeleteShareNote(noteId, fromUserId, c.GetUserId()))
|
||||
}
|
||||
|
||||
// 删除share notebook, 被共享方删除
|
||||
func (c Share) DeleteShareNotebookBySharedUser(notebookId string, fromUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteShareNotebook(notebookId, fromUserId, c.GetUserId()))
|
||||
return c.RenderJSON(shareService.DeleteShareNotebook(notebookId, fromUserId, c.GetUserId()))
|
||||
}
|
||||
|
||||
// 删除fromUserId分享给我的所有note, notebook
|
||||
func (c Share) DeleteUserShareNoteAndNotebook(fromUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteUserShareNoteAndNotebook(fromUserId, c.GetUserId()))
|
||||
return c.RenderJSON(shareService.DeleteUserShareNoteAndNotebook(fromUserId, c.GetUserId()))
|
||||
}
|
||||
|
||||
//-------------
|
||||
@@ -155,14 +155,14 @@ func (c Share) DeleteUserShareNoteAndNotebook(fromUserId string) revel.Result {
|
||||
func (c Share) AddShareNoteGroup(noteId, groupId string, perm int) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = shareService.AddShareNoteGroup(c.GetUserId(), noteId, groupId, perm)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 删除
|
||||
func (c Share) DeleteShareNoteGroup(noteId, groupId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = shareService.DeleteShareNoteGroup(c.GetUserId(), noteId, groupId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 更新, 也是一样, 先删后加
|
||||
@@ -176,14 +176,14 @@ func (c Share) UpdateShareNoteGroupPerm(noteId, groupId string, perm int) revel.
|
||||
func (c Share) AddShareNotebookGroup(notebookId, groupId string, perm int) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = shareService.AddShareNotebookGroup(c.GetUserId(), notebookId, groupId, perm)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 删除
|
||||
func (c Share) DeleteShareNotebookGroup(notebookId, groupId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = shareService.DeleteShareNotebookGroup(c.GetUserId(), notebookId, groupId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 更新, 也是一样, 先删后加
|
||||
|
||||
@@ -18,7 +18,7 @@ func (c Tag) UpdateTag(tag string) revel.Result {
|
||||
ret := info.NewRe()
|
||||
ret.Ok = true
|
||||
ret.Item = tagService.AddOrUpdateTag(c.GetUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
return c.RenderJSON(ret)
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
@@ -26,5 +26,5 @@ func (c Tag) DeleteTag(tag string) revel.Result {
|
||||
ret := info.Re{}
|
||||
ret.Ok = true
|
||||
ret.Item = tagService.DeleteTag(c.GetUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
return c.RenderJSON(ret)
|
||||
}
|
||||
@@ -21,8 +21,8 @@ type User struct {
|
||||
|
||||
func (c User) Account(tab int) revel.Result {
|
||||
userInfo := c.GetUserInfo()
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.RenderArgs["tab"] = tab
|
||||
c.ViewArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["tab"] = tab
|
||||
c.SetLocale()
|
||||
return c.RenderTemplate("user/account.html")
|
||||
}
|
||||
@@ -70,18 +70,18 @@ func (c User) UpdateTheme(theme string) revel.Result {
|
||||
if re.Ok {
|
||||
c.UpdateSession("Theme", theme)
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 发送邀请链接
|
||||
func (c User) SendRegisterEmail(content, toEmail string) revel.Result {
|
||||
re := info.NewRe()
|
||||
if content == "" || !IsEmail(toEmail) {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
re.Ok = emailService.SendInviteEmail(c.GetUserInfo(), toEmail, content)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
@@ -90,7 +90,7 @@ func (c User) SendRegisterEmail(content, toEmail string) revel.Result {
|
||||
func (c User) ReSendActiveEmail() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = emailService.RegisterSendActiveEmail(c.GetUserInfo(), c.GetEmail())
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 通过点击链接
|
||||
@@ -100,10 +100,10 @@ func (c User) UpdateEmail(token string) revel.Result {
|
||||
|
||||
ok, msg, email := userService.UpdateEmail(token)
|
||||
|
||||
c.RenderArgs["title"] = "验证邮箱"
|
||||
c.RenderArgs["ok"] = ok
|
||||
c.RenderArgs["msg"] = msg
|
||||
c.RenderArgs["email"] = email
|
||||
c.ViewArgs["title"] = "验证邮箱"
|
||||
c.ViewArgs["ok"] = ok
|
||||
c.ViewArgs["msg"] = msg
|
||||
c.ViewArgs["email"] = email
|
||||
|
||||
// 修改session
|
||||
if ok {
|
||||
@@ -124,10 +124,10 @@ func (c User) ActiveEmail(token string) revel.Result {
|
||||
c.UpdateSession("Verified", "1")
|
||||
}
|
||||
|
||||
c.RenderArgs["title"] = "验证邮箱"
|
||||
c.RenderArgs["ok"] = ok
|
||||
c.RenderArgs["msg"] = msg
|
||||
c.RenderArgs["email"] = email
|
||||
c.ViewArgs["title"] = "验证邮箱"
|
||||
c.ViewArgs["ok"] = ok
|
||||
c.ViewArgs["msg"] = msg
|
||||
c.ViewArgs["email"] = email
|
||||
|
||||
return c.RenderTemplate("user/active_email.html")
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func (c User) UpdateColumnWidth(notebookWidth, noteListWidth, mdEditorWidth int)
|
||||
|
||||
LogJ(c.Session)
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c User) UpdateLeftIsMin(leftIsMin bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
@@ -156,5 +156,5 @@ func (c User) UpdateLeftIsMin(leftIsMin bool) revel.Result {
|
||||
c.UpdateSession("LeftIsMin", "0")
|
||||
}
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func (c AdminBaseController) getSorter(sorterField string, isAsc bool, okSorter
|
||||
} else {
|
||||
isAsc = false
|
||||
}
|
||||
c.RenderArgs["sorter"] = sorter
|
||||
c.ViewArgs["sorter"] = sorter
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
|
||||
@@ -17,14 +17,14 @@ func (c AdminBlog) Index(sorter, keywords string) revel.Result {
|
||||
pageNumber := c.GetPage()
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"title", "userId", "isRecommed", "createdTime"})
|
||||
pageInfo, blogs := blogService.ListAllBlogs("", "", keywords, false, pageNumber, userPageSize, sorterField, isAsc)
|
||||
c.RenderArgs["pageInfo"] = pageInfo
|
||||
c.RenderArgs["blogs"] = blogs
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
c.ViewArgs["pageInfo"] = pageInfo
|
||||
c.ViewArgs["blogs"] = blogs
|
||||
c.ViewArgs["keywords"] = keywords
|
||||
return c.RenderTemplate("admin/blog/list.html")
|
||||
}
|
||||
|
||||
func (c AdminBlog) SetRecommend(noteId string, recommend bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.SetRecommend(noteId, recommend)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -14,23 +14,23 @@ type Admin struct {
|
||||
func (c Admin) Index() revel.Result {
|
||||
c.SetUserInfo()
|
||||
|
||||
c.RenderArgs["title"] = "leanote"
|
||||
c.ViewArgs["title"] = "leanote"
|
||||
c.SetLocale()
|
||||
|
||||
c.RenderArgs["countUser"] = userService.CountUser()
|
||||
c.RenderArgs["countNote"] = noteService.CountNote("")
|
||||
c.RenderArgs["countBlog"] = noteService.CountBlog("")
|
||||
c.ViewArgs["countUser"] = userService.CountUser()
|
||||
c.ViewArgs["countNote"] = noteService.CountNote("")
|
||||
c.ViewArgs["countBlog"] = noteService.CountBlog("")
|
||||
|
||||
return c.RenderTemplate("admin/index.html")
|
||||
}
|
||||
|
||||
// 模板
|
||||
func (c Admin) T(t string) revel.Result {
|
||||
c.RenderArgs["str"] = configService.GlobalStringConfigs
|
||||
c.RenderArgs["arr"] = configService.GlobalArrayConfigs
|
||||
c.RenderArgs["map"] = configService.GlobalMapConfigs
|
||||
c.RenderArgs["arrMap"] = configService.GlobalArrMapConfigs
|
||||
c.RenderArgs["version"] = configService.GetVersion()
|
||||
c.ViewArgs["str"] = configService.GlobalStringConfigs
|
||||
c.ViewArgs["arr"] = configService.GlobalArrayConfigs
|
||||
c.ViewArgs["map"] = configService.GlobalMapConfigs
|
||||
c.ViewArgs["arrMap"] = configService.GlobalArrMapConfigs
|
||||
c.ViewArgs["version"] = configService.GetVersion()
|
||||
return c.RenderTemplate("admin/" + t + ".html")
|
||||
}
|
||||
|
||||
|
||||
@@ -26,33 +26,33 @@ func (c AdminData) Index() revel.Result {
|
||||
backups2[j] = backups[i]
|
||||
j++
|
||||
}
|
||||
c.RenderArgs["backups"] = backups2
|
||||
c.ViewArgs["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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c AdminData) Download(createdTime string) revel.Result {
|
||||
backup, ok := configService.GetBackup(createdTime)
|
||||
|
||||
@@ -23,8 +23,8 @@ func (c AdminEmail) Email() revel.Result {
|
||||
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, ",")
|
||||
c.ViewArgs["recommendTags"] = strings.Join(recommendTags, ",")
|
||||
c.ViewArgs["newTags"] = strings.Join(newTags, ",")
|
||||
return c.RenderTemplate("admin/setting/blog.html")
|
||||
}
|
||||
func (c AdminEmail) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
@@ -33,14 +33,14 @@ func (c AdminEmail) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// demo
|
||||
// blog标签设置
|
||||
func (c AdminEmail) Demo() revel.Result {
|
||||
c.RenderArgs["demoUsername"] = configService.GetGlobalStringConfig("demoUsername")
|
||||
c.RenderArgs["demoPassword"] = configService.GetGlobalStringConfig("demoPassword")
|
||||
c.ViewArgs["demoUsername"] = configService.GetGlobalStringConfig("demoUsername")
|
||||
c.ViewArgs["demoPassword"] = configService.GetGlobalStringConfig("demoPassword")
|
||||
return c.RenderTemplate("admin/setting/demo.html")
|
||||
}
|
||||
func (c AdminEmail) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
@@ -48,30 +48,30 @@ func (c AdminEmail) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
|
||||
userInfo, err := authService.Login(demoUsername, demoPassword)
|
||||
if err != nil {
|
||||
return c.RenderJson(info.Re{Ok: false})
|
||||
return c.RenderJSON(info.Re{Ok: false})
|
||||
}
|
||||
if userInfo.UserId == "" {
|
||||
re.Msg = "The User is Not Exists"
|
||||
return c.RenderJson(re)
|
||||
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)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// ToImage
|
||||
// 长微博的bin路径phantomJs
|
||||
func (c AdminEmail) ToImage() revel.Result {
|
||||
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
|
||||
c.ViewArgs["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)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c AdminEmail) Set(emailHost, emailPort, emailUsername, emailPassword, emailSSL string) revel.Result {
|
||||
@@ -82,7 +82,7 @@ func (c AdminEmail) Set(emailHost, emailPort, emailUsername, emailPassword, emai
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailPassword", emailPassword)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailSSL", emailSSL)
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c AdminEmail) Template() revel.Result {
|
||||
re := info.NewRe()
|
||||
@@ -108,7 +108,7 @@ func (c AdminEmail) Template() revel.Result {
|
||||
if !ok {
|
||||
re.Ok = false
|
||||
re.Msg = "Error key: " + key + "<br />" + msg
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
} else {
|
||||
configService.UpdateGlobalStringConfig(userId, key, v)
|
||||
}
|
||||
@@ -116,7 +116,7 @@ func (c AdminEmail) Template() revel.Result {
|
||||
}
|
||||
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 发送Email
|
||||
@@ -127,7 +127,7 @@ func (c AdminEmail) SendEmailToEmails(sendEmails, latestEmailSubject, latestEmai
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
if saveAsOldEmail {
|
||||
@@ -140,7 +140,7 @@ func (c AdminEmail) SendEmailToEmails(sendEmails, latestEmailSubject, latestEmai
|
||||
emails := strings.Split(sendEmails, "\n")
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToEmails(emails, latestEmailSubject, latestEmailBody)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 发送Email
|
||||
@@ -151,7 +151,7 @@ func (c AdminEmail) SendToUsers2(emails, latestEmailSubject, latestEmailBody str
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
if saveAsOldEmail {
|
||||
@@ -168,7 +168,7 @@ func (c AdminEmail) SendToUsers2(emails, latestEmailSubject, latestEmailBody str
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToUsers(users, latestEmailSubject, latestEmailBody)
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// send Email dialog
|
||||
@@ -176,9 +176,9 @@ 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
|
||||
c.ViewArgs["emailsNl"] = emailsNl
|
||||
c.ViewArgs["str"] = configService.GlobalStringConfigs
|
||||
c.ViewArgs["map"] = configService.GlobalMapConfigs
|
||||
|
||||
return c.RenderTemplate("admin/email/emailDialog.html")
|
||||
}
|
||||
@@ -190,7 +190,7 @@ func (c AdminEmail) SendToUsers(userFilterEmail, userFilterWhiteList, userFilter
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
if saveAsOldEmail {
|
||||
@@ -204,33 +204,33 @@ func (c AdminEmail) SendToUsers(userFilterEmail, userFilterWhiteList, userFilter
|
||||
if users == nil || len(users) == 0 {
|
||||
re.Ok = false
|
||||
re.Msg = "no users"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToUsers(users, latestEmailSubject, latestEmailBody)
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
re.Ok = true
|
||||
re.Msg = "users:" + strconv.Itoa(len(users))
|
||||
|
||||
return c.RenderJson(re)
|
||||
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)
|
||||
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
|
||||
c.ViewArgs["pageInfo"] = pageInfo
|
||||
c.ViewArgs["emails"] = emails
|
||||
c.ViewArgs["keywords"] = keywords
|
||||
return c.RenderTemplate("admin/email/list.html")
|
||||
}
|
||||
@@ -23,8 +23,8 @@ func (c AdminSetting) Email() revel.Result {
|
||||
func (c AdminSetting) Blog() revel.Result {
|
||||
recommendTags := configService.GetGlobalArrayConfig("recommendTags")
|
||||
newTags := configService.GetGlobalArrayConfig("newTags")
|
||||
c.RenderArgs["recommendTags"] = strings.Join(recommendTags, ",")
|
||||
c.RenderArgs["newTags"] = strings.Join(newTags, ",")
|
||||
c.ViewArgs["recommendTags"] = strings.Join(recommendTags, ",")
|
||||
c.ViewArgs["newTags"] = strings.Join(newTags, ",")
|
||||
return c.RenderTemplate("admin/setting/blog.html")
|
||||
}
|
||||
func (c AdminSetting) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
@@ -33,7 +33,7 @@ func (c AdminSetting) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 共享设置
|
||||
@@ -43,14 +43,14 @@ func (c AdminSetting) ShareNote(registerSharedUserId string,
|
||||
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = configService.UpdateShareNoteConfig(registerSharedUserId, registerSharedNotebookPerms, registerSharedNotePerms, registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// demo
|
||||
// blog标签设置
|
||||
func (c AdminSetting) Demo() revel.Result {
|
||||
c.RenderArgs["demoUsername"] = configService.GetGlobalStringConfig("demoUsername")
|
||||
c.RenderArgs["demoPassword"] = configService.GetGlobalStringConfig("demoPassword")
|
||||
c.ViewArgs["demoUsername"] = configService.GetGlobalStringConfig("demoUsername")
|
||||
c.ViewArgs["demoPassword"] = configService.GetGlobalStringConfig("demoPassword")
|
||||
return c.RenderTemplate("admin/setting/demo.html")
|
||||
}
|
||||
func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
@@ -59,40 +59,40 @@ func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
userInfo, err := authService.Login(demoUsername, demoPassword)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return c.RenderJson(info.Re{Ok: false})
|
||||
return c.RenderJSON(info.Re{Ok: false})
|
||||
}
|
||||
if userInfo.UserId == "" {
|
||||
re.Msg = "The User is Not Exists"
|
||||
return c.RenderJson(re)
|
||||
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)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c AdminSetting) ExportPdf(path string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "exportPdfBinPath", path)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c AdminSetting) DoSiteUrl(siteUrl string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "siteUrl", siteUrl)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// SubDomain
|
||||
func (c AdminSetting) SubDomain() revel.Result {
|
||||
c.RenderArgs["str"] = configService.GlobalStringConfigs
|
||||
c.RenderArgs["arr"] = configService.GlobalArrayConfigs
|
||||
c.ViewArgs["str"] = configService.GlobalStringConfigs
|
||||
c.ViewArgs["arr"] = configService.GlobalArrayConfigs
|
||||
|
||||
c.RenderArgs["noteSubDomain"] = configService.GetGlobalStringConfig("noteSubDomain")
|
||||
c.RenderArgs["blogSubDomain"] = configService.GetGlobalStringConfig("blogSubDomain")
|
||||
c.RenderArgs["leaSubDomain"] = configService.GetGlobalStringConfig("leaSubDomain")
|
||||
c.ViewArgs["noteSubDomain"] = configService.GetGlobalStringConfig("noteSubDomain")
|
||||
c.ViewArgs["blogSubDomain"] = configService.GetGlobalStringConfig("blogSubDomain")
|
||||
c.ViewArgs["leaSubDomain"] = configService.GetGlobalStringConfig("leaSubDomain")
|
||||
|
||||
return c.RenderTemplate("admin/setting/subDomain.html")
|
||||
}
|
||||
@@ -106,13 +106,13 @@ func (c AdminSetting) DoSubDomain(noteSubDomain, blogSubDomain, leaSubDomain, bl
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "blackSubDomains", strings.Split(blackSubDomains, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "blackCustomDomains", strings.Split(blackCustomDomains, ","))
|
||||
|
||||
return c.RenderJson(re)
|
||||
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)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c AdminSetting) HomePage(homePage string) revel.Result {
|
||||
@@ -121,7 +121,7 @@ func (c AdminSetting) HomePage(homePage string) revel.Result {
|
||||
homePage = ""
|
||||
}
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "homePage", homePage)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c AdminSetting) Mongodb(mongodumpPath, mongorestorePath string) revel.Result {
|
||||
@@ -129,7 +129,7 @@ func (c AdminSetting) Mongodb(mongodumpPath, mongorestorePath string) revel.Resu
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "mongodumpPath", mongodumpPath)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "mongorestorePath", mongorestorePath)
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c AdminSetting) UploadSize(uploadImageSize, uploadAvatarSize, uploadBlogLogoSize, uploadAttachSize float64) revel.Result {
|
||||
@@ -138,5 +138,5 @@ func (c AdminSetting) UploadSize(uploadImageSize, uploadAvatarSize, uploadBlogLo
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "uploadAvatarSize", fmt.Sprintf("%v", uploadAvatarSize))
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "uploadBlogLogoSize", fmt.Sprintf("%v", uploadBlogLogoSize))
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "uploadAttachSize", fmt.Sprintf("%v", uploadAttachSize))
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -20,11 +20,11 @@ func (c AdminUpgrade) UpgradeBlog() revel.Result {
|
||||
func (c AdminUpgrade) UpgradeBetaToBeta2() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = upgradeService.UpgradeBetaToBeta2(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c AdminUpgrade) UpgradeBeta3ToBeta4() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = upgradeService.Api(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -18,14 +18,14 @@ var userPageSize = 10
|
||||
|
||||
func (c AdminUser) Index(sorter, keywords string, pageSize int) revel.Result {
|
||||
pageNumber := c.GetPage()
|
||||
if userPageSize == 0 {
|
||||
if pageSize == 0 {
|
||||
pageSize = userPageSize
|
||||
}
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"email", "username", "verified", "createdTime", "accountType"})
|
||||
pageInfo, users := userService.ListUsers(pageNumber, pageSize, sorterField, isAsc, keywords)
|
||||
c.RenderArgs["pageInfo"] = pageInfo
|
||||
c.RenderArgs["users"] = users
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
c.ViewArgs["pageInfo"] = pageInfo
|
||||
c.ViewArgs["users"] = users
|
||||
c.ViewArgs["keywords"] = keywords
|
||||
return c.RenderTemplate("admin/user/list.html")
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (c AdminUser) Register(email, pwd string) revel.Result {
|
||||
// 修改帐户
|
||||
func (c AdminUser) ResetPwd(userId string) revel.Result {
|
||||
userInfo := userService.GetUserInfo(userId)
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["userInfo"] = userInfo
|
||||
return c.RenderTemplate("admin/user/reset_pwd.html")
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
if c.Request.Header.Get("X-Requested-With") == "XMLHttpRequest" {
|
||||
re := info.NewRe()
|
||||
re.Msg = "NOTLOGIN"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
return c.Redirect("/login")
|
||||
|
||||
@@ -28,12 +28,12 @@ func (c ApiAuth) Login(email, pwd string) revel.Result {
|
||||
if err == nil {
|
||||
token := bson.NewObjectId().Hex()
|
||||
sessionService.SetUserId(token, userInfo.UserId.Hex())
|
||||
return c.RenderJson(info.AuthOk{Ok: true, Token: token, UserId: userInfo.UserId, Email: userInfo.Email, Username: userInfo.Username})
|
||||
return c.RenderJSON(info.AuthOk{Ok: true, Token: token, UserId: userInfo.UserId, Email: userInfo.Email, Username: userInfo.Username})
|
||||
} else {
|
||||
// 登录错误, 则错误次数++
|
||||
msg = "wrongUsernameOrPassword"
|
||||
}
|
||||
return c.RenderJson(info.ApiRe{Ok: false, Msg: c.Message(msg)})
|
||||
return c.RenderJSON(info.ApiRe{Ok: false, Msg: c.Message(msg)})
|
||||
}
|
||||
|
||||
// 注销
|
||||
@@ -43,7 +43,7 @@ func (c ApiAuth) Logout() revel.Result {
|
||||
sessionService.Clear(token)
|
||||
re := info.NewApiRe()
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 注册
|
||||
@@ -53,17 +53,17 @@ func (c ApiAuth) Register(email, pwd string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if !configService.IsOpenRegister() {
|
||||
re.Msg = "notOpenRegister" // 未开放注册
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 注册
|
||||
re.Ok, re.Msg = authService.Register(email, pwd, "")
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -142,10 +142,10 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
_, ext := SplitFilename(filename)
|
||||
if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
|
||||
msg = "notImage"
|
||||
return
|
||||
}
|
||||
// if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
|
||||
// msg = "notImage"
|
||||
// return
|
||||
// }
|
||||
|
||||
filename = newGuid + ext
|
||||
data, err := ioutil.ReadAll(file)
|
||||
|
||||
@@ -30,7 +30,7 @@ func (c ApiFile) CopyImage(userId, fileId, toUserId string) revel.Result {
|
||||
|
||||
re.Ok, re.Id = fileService.CopyImage(userId, fileId, toUserId)
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// get all images by userId with page
|
||||
@@ -39,19 +39,19 @@ func (c ApiFile) GetImages(albumId, key string, page int) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.Item = imagesPage
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c ApiFile) UpdateImageTitle(fileId, title string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = fileService.UpdateImageTitle(c.getUserId(), fileId, title)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c ApiFile) DeleteImage(fileId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = fileService.DeleteImage(c.getUserId(), fileId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -52,7 +52,7 @@ func (c ApiNote) GetSyncNotes(afterUsn, maxEntry int) revel.Result {
|
||||
maxEntry = 100
|
||||
}
|
||||
notes := noteService.GetSyncNotes(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(notes)
|
||||
return c.RenderJSON(notes)
|
||||
}
|
||||
|
||||
// 得到笔记本下的笔记
|
||||
@@ -61,17 +61,17 @@ func (c ApiNote) GetNotes(notebookId string) revel.Result {
|
||||
if notebookId != "" && !bson.IsObjectIdHex(notebookId) {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "notebookIdInvalid"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
_, notes := noteService.ListNotes(c.getUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(noteService.ToApiNotes(notes))
|
||||
return c.RenderJSON(noteService.ToApiNotes(notes))
|
||||
}
|
||||
|
||||
// 得到trash
|
||||
// [OK]
|
||||
func (c ApiNote) GetTrashNotes() revel.Result {
|
||||
_, notes := noteService.ListNotes(c.getUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(noteService.ToApiNotes(notes))
|
||||
return c.RenderJSON(noteService.ToApiNotes(notes))
|
||||
|
||||
}
|
||||
|
||||
@@ -127,17 +127,17 @@ func (c ApiNote) GetNote(noteId string) revel.Result {
|
||||
if !bson.IsObjectIdHex(noteId) {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "noteIdInvalid"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
note := noteService.GetNote(noteId, c.getUserId())
|
||||
if note.NoteId == "" {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "notExists"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
apiNotes := noteService.ToApiNotes([]info.Note{note})
|
||||
return c.RenderJson(apiNotes[0])
|
||||
return c.RenderJSON(apiNotes[0])
|
||||
}
|
||||
|
||||
// 得到note和内容
|
||||
@@ -148,7 +148,7 @@ func (c ApiNote) GetNoteAndContent(noteId string) revel.Result {
|
||||
apiNotes := noteService.ToApiNotes([]info.Note{noteAndContent.Note})
|
||||
apiNote := apiNotes[0]
|
||||
apiNote.Content = noteService.FixContent(noteAndContent.Content, noteAndContent.IsMarkdown)
|
||||
return c.RenderJson(apiNote)
|
||||
return c.RenderJSON(apiNote)
|
||||
}
|
||||
|
||||
// content里的image, attach链接是
|
||||
@@ -166,18 +166,20 @@ func (c ApiNote) fixPostNotecontent(noteOrContent *info.ApiNote) {
|
||||
if file.LocalFileId != "" {
|
||||
LogJ(file)
|
||||
if !file.IsAttach {
|
||||
reg, _ := regexp.Compile(`"https*://[^/]*?/api/file/getImage\?fileId=`+file.LocalFileId)
|
||||
// <img src="https://"
|
||||
// 
|
||||
reg, _ := regexp.Compile(`https*://[^/]*?/api/file/getImage\?fileId=`+file.LocalFileId)
|
||||
// Log(reg)
|
||||
noteOrContent.Content = reg.ReplaceAllString(noteOrContent.Content, `"/api/file/getImage?fileId=`+file.FileId)
|
||||
noteOrContent.Content = reg.ReplaceAllString(noteOrContent.Content, `/api/file/getImage?fileId=`+file.FileId)
|
||||
|
||||
// // "http://a.com/api/file/getImage?fileId=localId" => /api/file/getImage?fileId=serverId
|
||||
// noteOrContent.Content = strings.Replace(noteOrContent.Content,
|
||||
// baseUrl + "/api/file/getImage?fileId="+file.LocalFileId,
|
||||
// "/api/file/getImage?fileId="+file.FileId, -1)
|
||||
} else {
|
||||
reg, _ := regexp.Compile(`"https*://[^/]*?/api/file/getAttach\?fileId=`+file.LocalFileId)
|
||||
reg, _ := regexp.Compile(`https*://[^/]*?/api/file/getAttach\?fileId=`+file.LocalFileId)
|
||||
Log(reg)
|
||||
noteOrContent.Content = reg.ReplaceAllString(noteOrContent.Content, `"/api/file/getAttach?fileId=`+file.FileId)
|
||||
noteOrContent.Content = reg.ReplaceAllString(noteOrContent.Content, `/api/file/getAttach?fileId=`+file.FileId)
|
||||
/*
|
||||
noteOrContent.Content = strings.Replace(noteOrContent.Content,
|
||||
baseUrl + "/api/file/getAttach?fileId="+file.LocalFileId,
|
||||
@@ -205,7 +207,7 @@ func (c ApiNote) GetNoteContent(noteId string) revel.Result {
|
||||
Content: noteContent.Content,
|
||||
}
|
||||
|
||||
return c.RenderJson(apiNoteContent)
|
||||
return c.RenderJSON(apiNoteContent)
|
||||
}
|
||||
|
||||
// 添加笔记
|
||||
@@ -231,10 +233,10 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
|
||||
LogJ(file)
|
||||
}
|
||||
*/
|
||||
// return c.RenderJson(re)
|
||||
// return c.RenderJSON(re)
|
||||
if noteOrContent.NotebookId == "" || !bson.IsObjectIdHex(noteOrContent.NotebookId) {
|
||||
re.Msg = "notebookIdNotExists"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
noteId := bson.NewObjectId()
|
||||
@@ -257,7 +259,7 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
|
||||
}
|
||||
// 报不是图片的错误没关系, 证明客户端传来非图片的数据
|
||||
if msg != "notImage" {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
} else {
|
||||
// 建立映射
|
||||
@@ -269,7 +271,7 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,7 +282,7 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
|
||||
// Log("Add")
|
||||
// LogJ(noteOrContent)
|
||||
|
||||
// return c.RenderJson(re)
|
||||
// return c.RenderJSON(re)
|
||||
|
||||
note := info.Note{UserId: userId,
|
||||
NoteId: noteId,
|
||||
@@ -316,7 +318,7 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
|
||||
|
||||
if note.NoteId == "" {
|
||||
re.Ok = false
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 添加需要返回的
|
||||
@@ -330,7 +332,7 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
|
||||
noteOrContent.Content = ""
|
||||
noteOrContent.Abstract = ""
|
||||
// apiNote := info.NoteToApiNote(note, noteOrContent.Files)
|
||||
return c.RenderJson(noteOrContent)
|
||||
return c.RenderJSON(noteOrContent)
|
||||
}
|
||||
|
||||
// 更新笔记
|
||||
@@ -345,12 +347,12 @@ func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
|
||||
|
||||
if noteOrContent.NoteId == "" {
|
||||
re.Msg = "noteIdNotExists"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
if noteOrContent.Usn <= 0 {
|
||||
re.Msg = "usnNotExists"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// Log("_____________")
|
||||
@@ -366,12 +368,12 @@ func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
|
||||
note := noteService.GetNote(noteId, userId)
|
||||
if note.NoteId == "" {
|
||||
re.Msg = "notExists"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
if note.Usn != noteOrContent.Usn {
|
||||
re.Msg = "conflict"
|
||||
Log("conflict")
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
Log("没有冲突")
|
||||
|
||||
@@ -400,14 +402,14 @@ func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
|
||||
} else {
|
||||
re.Msg = msg
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
} else {
|
||||
// 建立映射
|
||||
file.FileId = fileId
|
||||
noteOrContent.Files[i] = file
|
||||
}
|
||||
} else {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -488,7 +490,7 @@ func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
|
||||
if !noteOk {
|
||||
re.Ok = false
|
||||
re.Msg = noteMsg
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,7 +528,7 @@ func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
|
||||
}
|
||||
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
noteOrContent.Content = ""
|
||||
@@ -537,14 +539,14 @@ func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
|
||||
// LogJ(noteOrContent.Files)
|
||||
noteOrContent.UserId = c.getUserId()
|
||||
|
||||
return c.RenderJson(noteOrContent)
|
||||
return c.RenderJSON(noteOrContent)
|
||||
}
|
||||
|
||||
// 删除trash
|
||||
func (c ApiNote) DeleteTrash(noteId string, usn int) revel.Result {
|
||||
re := info.NewReUpdate()
|
||||
re.Ok, re.Msg, re.Usn = trashService.DeleteTrashApi(noteId, c.getUserId(), usn)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 得到历史列表
|
||||
@@ -556,7 +558,7 @@ func (c ApiNote) GetHistories(noteId string) revel.Result {
|
||||
re.Ok = true
|
||||
re.Item = histories
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -567,13 +569,13 @@ func (c ApiNote) ExportPdf(noteId string) revel.Result {
|
||||
userId := c.getUserId()
|
||||
if noteId == "" {
|
||||
re.Msg = "noteNotExists"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
re.Msg = "noteNotExists"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
noteUserId := note.UserId.Hex()
|
||||
@@ -582,7 +584,7 @@ func (c ApiNote) ExportPdf(noteId string) revel.Result {
|
||||
// 是否是有权限协作的
|
||||
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
|
||||
re.Msg = "noteNotExists"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,7 +594,7 @@ func (c ApiNote) ExportPdf(noteId string) revel.Result {
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
if !MkdirAll(dir) {
|
||||
re.Msg = "noDir"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
filename := guid + ".pdf"
|
||||
path := dir + "/" + filename
|
||||
@@ -621,12 +623,12 @@ func (c ApiNote) ExportPdf(noteId string) revel.Result {
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
re.Msg = "sysError"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
re.Msg = "sysError"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
filenameReturn := note.Title
|
||||
|
||||
@@ -53,7 +53,7 @@ func (c ApiNotebook) GetSyncNotebooks(afterUsn, maxEntry int) revel.Result {
|
||||
maxEntry = 100
|
||||
}
|
||||
notebooks := notebookService.GeSyncNotebooks(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(c.fixNotebooks(notebooks))
|
||||
return c.RenderJSON(c.fixNotebooks(notebooks))
|
||||
}
|
||||
|
||||
// 得到用户的所有笔记本
|
||||
@@ -61,7 +61,7 @@ func (c ApiNotebook) GetSyncNotebooks(afterUsn, maxEntry int) revel.Result {
|
||||
// info.SubNotebooks
|
||||
func (c ApiNotebook) GetNotebooks() revel.Result {
|
||||
notebooks := notebookService.GeSyncNotebooks(c.getUserId(), 0, 99999)
|
||||
return c.RenderJson(c.fixNotebooks(notebooks))
|
||||
return c.RenderJSON(c.fixNotebooks(notebooks))
|
||||
}
|
||||
|
||||
// 添加notebook
|
||||
@@ -77,9 +77,9 @@ func (c ApiNotebook) AddNotebook(title, parentNotebookId string, seq int) revel.
|
||||
re := info.NewRe()
|
||||
re.Ok, notebook = notebookService.AddNotebook(notebook)
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
return c.RenderJson(c.fixNotebook(¬ebook))
|
||||
return c.RenderJSON(c.fixNotebook(¬ebook))
|
||||
}
|
||||
|
||||
// 修改笔记
|
||||
@@ -91,10 +91,10 @@ func (c ApiNotebook) UpdateNotebook(notebookId, title, parentNotebookId string,
|
||||
if !ok {
|
||||
re.Ok = false
|
||||
re.Msg = msg
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
LogJ(notebook)
|
||||
return c.RenderJson(c.fixNotebook(¬ebook))
|
||||
return c.RenderJSON(c.fixNotebook(¬ebook))
|
||||
}
|
||||
|
||||
// 删除笔记本
|
||||
@@ -102,5 +102,5 @@ func (c ApiNotebook) UpdateNotebook(notebookId, title, parentNotebookId string,
|
||||
func (c ApiNotebook) DeleteNotebook(notebookId string, usn int) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
re.Ok, re.Msg = notebookService.DeleteNotebookForce(c.getUserId(), notebookId, usn)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func (c ApiTag) GetSyncTags(afterUsn, maxEntry int) revel.Result {
|
||||
maxEntry = 100
|
||||
}
|
||||
tags := tagService.GeSyncTags(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(tags)
|
||||
return c.RenderJSON(tags)
|
||||
}
|
||||
|
||||
// 添加Tag
|
||||
@@ -44,7 +44,7 @@ func (c ApiTag) GetSyncTags(afterUsn, maxEntry int) revel.Result {
|
||||
*/
|
||||
func (c ApiTag) AddTag(tag string) revel.Result {
|
||||
ret := tagService.AddOrUpdateTag(c.getUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
return c.RenderJSON(ret)
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
@@ -52,5 +52,5 @@ func (c ApiTag) AddTag(tag string) revel.Result {
|
||||
func (c ApiTag) DeleteTag(tag string, usn int) revel.Result {
|
||||
re := info.NewReUpdate()
|
||||
re.Ok, re.Msg, re.Usn = tagService.DeleteTagApi(c.getUserId(), tag, usn)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func (c ApiUser) Info() revel.Result {
|
||||
|
||||
userInfo := c.getUserInfo()
|
||||
if userInfo.UserId == "" {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
apiUser := info.ApiUser{
|
||||
UserId: userInfo.UserId.Hex(),
|
||||
@@ -37,7 +37,7 @@ func (c ApiUser) Info() revel.Result {
|
||||
Logo: userInfo.Logo,
|
||||
Verified: userInfo.Verified,
|
||||
}
|
||||
return c.RenderJson(apiUser)
|
||||
return c.RenderJSON(apiUser)
|
||||
}
|
||||
|
||||
// 修改用户名
|
||||
@@ -46,15 +46,15 @@ func (c ApiUser) UpdateUsername(username string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if c.GetUsername() == "demo" {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
if re.Ok, re.Msg = Vd("username", username); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = userService.UpdateUsername(c.getUserId(), username)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
@@ -63,23 +63,23 @@ func (c ApiUser) UpdatePwd(oldPwd, pwd string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if c.GetUsername() == "demo" {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", oldPwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
re.Ok, re.Msg = userService.UpdatePwd(c.getUserId(), oldPwd, pwd)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 获得同步状态
|
||||
// [OK]
|
||||
func (c ApiUser) GetSyncState() revel.Result {
|
||||
ret := bson.M{"LastSyncUsn": userService.GetUsn(c.getUserId()), "LastSyncTime": time.Now().Unix()}
|
||||
return c.RenderJson(ret)
|
||||
return c.RenderJSON(ret)
|
||||
}
|
||||
|
||||
// 头像设置
|
||||
@@ -91,11 +91,11 @@ func (c ApiUser) UpdateLogo() revel.Result {
|
||||
|
||||
if ok {
|
||||
ok = userService.UpdateAvatar(c.getUserId(), url)
|
||||
return c.RenderJson(map[string]string{"Logo": url})
|
||||
return c.RenderJSON(map[string]string{"Logo": url})
|
||||
} else {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = msg
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
noToken := false
|
||||
if token == "" {
|
||||
// 若无, 则取sessionId
|
||||
token = c.Session.Id()
|
||||
token = c.Session.ID()
|
||||
noToken = true
|
||||
}
|
||||
c.Session["_token"] = token
|
||||
@@ -106,7 +106,7 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
// 没有登录, 返回错误的信息, 需要登录
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "NOTLOGIN"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -103,7 +103,7 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
if c.Request.Header.Get("X-Requested-With") == "XMLHttpRequest" {
|
||||
re := info.NewRe()
|
||||
re.Msg = "NOTLOGIN"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
return c.Redirect("/login")
|
||||
|
||||
@@ -46,7 +46,7 @@ func (c MemberBaseController) getSorter(sorterField string, isAsc bool, okSorter
|
||||
} else {
|
||||
isAsc = false
|
||||
}
|
||||
c.RenderArgs["sorter"] = sorter
|
||||
c.ViewArgs["sorter"] = sorter
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
|
||||
@@ -21,13 +21,13 @@ type MemberBlog struct {
|
||||
func (c MemberBlog) common() info.UserBlog {
|
||||
userId := c.GetUserId()
|
||||
userInfo := userService.GetUserInfo(userId)
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["userInfo"] = userInfo
|
||||
|
||||
// 得到博客设置信息
|
||||
c.RenderArgs["allowCustomDomain"] = configService.GetGlobalStringConfig("allowCustomDomain")
|
||||
c.ViewArgs["allowCustomDomain"] = configService.GetGlobalStringConfig("allowCustomDomain")
|
||||
|
||||
userBlog := blogService.GetUserBlog(userId)
|
||||
c.RenderArgs["userBlog"] = userBlog
|
||||
c.ViewArgs["userBlog"] = userBlog
|
||||
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
@@ -62,7 +62,7 @@ func (c MemberBlog) getSorter(sorterField string, isAsc bool, okSorter []string)
|
||||
} else {
|
||||
isAsc = false
|
||||
}
|
||||
c.RenderArgs["sorter"] = sorter
|
||||
c.ViewArgs["sorter"] = sorter
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
@@ -72,18 +72,18 @@ var userPageSize = 15
|
||||
func (c MemberBlog) Index(sorter, keywords string) revel.Result {
|
||||
userId := c.GetUserId()
|
||||
userInfo := userService.GetUserInfo(userId)
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.ViewArgs["userInfo"] = userInfo
|
||||
|
||||
c.RenderArgs["title"] = c.Message("Posts")
|
||||
c.ViewArgs["title"] = c.Message("Posts")
|
||||
pageNumber := c.GetPage()
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"title", "urlTitle", "updatedTime", "publicTime", "createdTime"})
|
||||
pageInfo, blogs := blogService.ListAllBlogs(c.GetUserId(), "", keywords, false, pageNumber, userPageSize, sorterField, isAsc)
|
||||
c.RenderArgs["pageInfo"] = pageInfo
|
||||
c.RenderArgs["blogs"] = blogs
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
c.ViewArgs["pageInfo"] = pageInfo
|
||||
c.ViewArgs["blogs"] = blogs
|
||||
c.ViewArgs["keywords"] = keywords
|
||||
|
||||
userAndBlog := userService.GetUserAndBlog(c.GetUserId())
|
||||
c.RenderArgs["userAndBlog"] = userAndBlog
|
||||
c.ViewArgs["userAndBlog"] = userAndBlog
|
||||
|
||||
c.common()
|
||||
|
||||
@@ -94,48 +94,48 @@ func (c MemberBlog) Index(sorter, keywords string) revel.Result {
|
||||
func (c MemberBlog) UpdateBlogUrlTitle(noteId, urlTitle string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Item = blogService.UpateBlogUrlTitle(c.GetUserId(), noteId, urlTitle)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 修改笔记的urlTitle
|
||||
func (c MemberBlog) UpdateBlogAbstract(noteId string) revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = c.Message("Update Post Abstract")
|
||||
c.ViewArgs["title"] = c.Message("Update Post Abstract")
|
||||
note := noteService.GetNoteAndContent(noteId, c.GetUserId())
|
||||
if !note.Note.IsBlog {
|
||||
return c.E404()
|
||||
}
|
||||
c.RenderArgs["note"] = note
|
||||
c.RenderArgs["noteId"] = noteId
|
||||
c.ViewArgs["note"] = note
|
||||
c.ViewArgs["noteId"] = noteId
|
||||
return c.RenderTemplate("member/blog/update_abstract.html")
|
||||
}
|
||||
func (c MemberBlog) DoUpdateBlogAbstract(noteId, imgSrc, desc, abstract string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.UpateBlogAbstract(c.GetUserId(), noteId, imgSrc, desc, abstract)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 基本信息设置
|
||||
func (c MemberBlog) Base() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = c.Message("Blog Base Info")
|
||||
c.ViewArgs["title"] = c.Message("Blog Base Info")
|
||||
return c.RenderTemplate("member/blog/base.html")
|
||||
}
|
||||
func (c MemberBlog) Comment() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = c.Message("Comment")
|
||||
c.ViewArgs["title"] = c.Message("Comment")
|
||||
return c.RenderTemplate("member/blog/comment.html")
|
||||
}
|
||||
|
||||
func (c MemberBlog) Paging() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = c.Message("Paging")
|
||||
c.ViewArgs["title"] = c.Message("Paging")
|
||||
return c.RenderTemplate("member/blog/paging.html")
|
||||
}
|
||||
|
||||
func (c MemberBlog) Cate() revel.Result {
|
||||
userBlog := c.common()
|
||||
c.RenderArgs["title"] = c.Message("Category")
|
||||
c.ViewArgs["title"] = c.Message("Category")
|
||||
|
||||
notebooks := blogService.ListBlogNotebooks(c.GetUserId())
|
||||
notebooksMap := map[string]info.Notebook{}
|
||||
@@ -166,7 +166,7 @@ func (c MemberBlog) Cate() revel.Result {
|
||||
i++
|
||||
}
|
||||
}
|
||||
c.RenderArgs["notebooks"] = notebooks2
|
||||
c.ViewArgs["notebooks"] = notebooks2
|
||||
|
||||
return c.RenderTemplate("member/blog/cate.html")
|
||||
}
|
||||
@@ -175,54 +175,54 @@ func (c MemberBlog) Cate() revel.Result {
|
||||
func (c MemberBlog) UpateCateIds(cateIds []string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.UpateCateIds(c.GetUserId(), cateIds)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c MemberBlog) UpdateCateUrlTitle(cateId, urlTitle string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Item = blogService.UpateCateUrlTitle(c.GetUserId(), cateId, urlTitle)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 保存之, 包含增加与保存
|
||||
func (c MemberBlog) DoAddOrUpdateSingle(singleId, title, content string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.AddOrUpdateSingle(c.GetUserId(), singleId, title, content)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c MemberBlog) AddOrUpdateSingle(singleId string) revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = c.Message("Add Single")
|
||||
c.RenderArgs["singleId"] = singleId
|
||||
c.ViewArgs["title"] = c.Message("Add Single")
|
||||
c.ViewArgs["singleId"] = singleId
|
||||
if singleId != "" {
|
||||
c.RenderArgs["title"] = c.Message("Update Single")
|
||||
c.RenderArgs["single"] = blogService.GetSingle(singleId)
|
||||
c.ViewArgs["title"] = c.Message("Update Single")
|
||||
c.ViewArgs["single"] = blogService.GetSingle(singleId)
|
||||
}
|
||||
return c.RenderTemplate("member/blog/add_single.html")
|
||||
}
|
||||
func (c MemberBlog) SortSingles(singleIds []string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.SortSingles(c.GetUserId(), singleIds)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c MemberBlog) DeleteSingle(singleId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.DeleteSingle(c.GetUserId(), singleId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 修改页面标题
|
||||
func (c MemberBlog) UpdateSingleUrlTitle(singleId, urlTitle string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Item = blogService.UpdateSingleUrlTitle(c.GetUserId(), singleId, urlTitle)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c MemberBlog) Single() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = c.Message("Single")
|
||||
c.RenderArgs["singles"] = blogService.GetSingles(c.GetUserId())
|
||||
c.ViewArgs["title"] = c.Message("Single")
|
||||
c.ViewArgs["singles"] = blogService.GetSingles(c.GetUserId())
|
||||
|
||||
return c.RenderTemplate("member/blog/single.html")
|
||||
}
|
||||
@@ -231,12 +231,12 @@ func (c MemberBlog) Single() revel.Result {
|
||||
func (c MemberBlog) Theme() revel.Result {
|
||||
c.common()
|
||||
activeTheme, otherThemes := themeService.GetUserThemes(c.GetUserId())
|
||||
c.RenderArgs["activeTheme"] = activeTheme
|
||||
c.RenderArgs["otherThemes"] = otherThemes
|
||||
c.ViewArgs["activeTheme"] = activeTheme
|
||||
c.ViewArgs["otherThemes"] = otherThemes
|
||||
|
||||
c.RenderArgs["optionThemes"] = themeService.GetDefaultThemes()
|
||||
c.ViewArgs["optionThemes"] = themeService.GetDefaultThemes()
|
||||
|
||||
c.RenderArgs["title"] = c.Message("Theme")
|
||||
c.ViewArgs["title"] = c.Message("Theme")
|
||||
return c.RenderTemplate("member/blog/theme.html")
|
||||
}
|
||||
|
||||
@@ -253,11 +253,11 @@ func (c MemberBlog) UpdateTheme(themeId string, isNew int) revel.Result {
|
||||
}
|
||||
|
||||
c.common()
|
||||
c.RenderArgs["title"] = c.Message("Update Theme")
|
||||
c.RenderArgs["isNew"] = isNew
|
||||
c.ViewArgs["title"] = c.Message("Update Theme")
|
||||
c.ViewArgs["isNew"] = isNew
|
||||
|
||||
// 先复制之
|
||||
c.RenderArgs["themeId"] = themeId
|
||||
c.ViewArgs["themeId"] = themeId
|
||||
|
||||
// 得到脚本目录
|
||||
userId := c.GetUserId()
|
||||
@@ -266,7 +266,7 @@ func (c MemberBlog) UpdateTheme(themeId string, isNew int) revel.Result {
|
||||
if theme.ThemeId == "" {
|
||||
return c.E404()
|
||||
}
|
||||
c.RenderArgs["theme"] = theme
|
||||
c.ViewArgs["theme"] = theme
|
||||
|
||||
path := revel.BasePath + "/" + theme.Path
|
||||
|
||||
@@ -287,7 +287,7 @@ func (c MemberBlog) UpdateTheme(themeId string, isNew int) revel.Result {
|
||||
}
|
||||
}
|
||||
|
||||
c.RenderArgs["myTpls"] = myTpls
|
||||
c.ViewArgs["myTpls"] = myTpls
|
||||
|
||||
return c.RenderTemplate("member/blog/update_theme.html")
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func (c MemberBlog) GetTplContent(themeId string, filename string) revel.Result
|
||||
re.Ok = true
|
||||
re.Item = themeService.GetTplContent(c.GetUserId(), themeId, filename)
|
||||
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c MemberBlog) UpdateTplContent(themeId, filename, content string) revel.Result {
|
||||
re := info.NewRe()
|
||||
@@ -309,7 +309,7 @@ func (c MemberBlog) UpdateTplContent(themeId, filename, content string) revel.Re
|
||||
func (c MemberBlog) DeleteTpl(themeId, filename string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = themeService.DeleteTpl(c.GetUserId(), themeId, filename)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c MemberBlog) ListThemeImages(themeId string) revel.Result {
|
||||
@@ -320,22 +320,22 @@ func (c MemberBlog) ListThemeImages(themeId string) revel.Result {
|
||||
images := ListDir(path)
|
||||
re.List = images
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c MemberBlog) DeleteThemeImage(themeId, filename string) revel.Result {
|
||||
re := info.NewRe()
|
||||
path := themeService.GetThemeAbsolutePath(c.GetUserId(), themeId) + "/images/" + filename
|
||||
re.Ok = DeleteFile(path)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 上传主题图片
|
||||
func (c MemberBlog) UploadThemeImage(themeId string) revel.Result {
|
||||
re := c.uploadImage(themeId)
|
||||
c.RenderArgs["fileUrlPath"] = re.Id
|
||||
c.RenderArgs["resultCode"] = re.Code
|
||||
c.RenderArgs["resultMsg"] = re.Msg
|
||||
c.ViewArgs["fileUrlPath"] = re.Id
|
||||
c.ViewArgs["resultCode"] = re.Code
|
||||
c.ViewArgs["resultMsg"] = re.Msg
|
||||
return c.RenderTemplate("file/blog_logo.html")
|
||||
}
|
||||
func (c MemberBlog) uploadImage(themeId string) (re info.Re) {
|
||||
@@ -405,21 +405,21 @@ func (c MemberBlog) uploadImage(themeId string) (re info.Re) {
|
||||
func (c MemberBlog) ActiveTheme(themeId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = themeService.ActiveTheme(c.GetUserId(), themeId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 删除主题
|
||||
func (c MemberBlog) DeleteTheme(themeId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = themeService.DeleteTheme(c.GetUserId(), themeId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 管理员公开主题
|
||||
func (c MemberBlog) PublicTheme(themeId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = themeService.PublicTheme(c.GetUserId(), themeId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 导出
|
||||
@@ -444,7 +444,7 @@ func (c MemberBlog) ImportTheme() revel.Result {
|
||||
file, handel, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
re.Msg = fmt.Sprintf("%v", err)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
@@ -454,7 +454,7 @@ func (c MemberBlog) ImportTheme() revel.Result {
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
re.Msg = fmt.Sprintf("%v", err)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
@@ -463,26 +463,26 @@ func (c MemberBlog) ImportTheme() revel.Result {
|
||||
_, ext = SplitFilename(filename)
|
||||
if ext != ".zip" {
|
||||
re.Msg = "Please upload zip file"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
filename = filename
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// > 10M?
|
||||
if len(data) > 10*1024*1024 {
|
||||
re.Msg = "File is big than 10M"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
re.Msg = fmt.Sprintf("%v", err)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 上传好后, 增加之
|
||||
@@ -494,7 +494,7 @@ func (c MemberBlog) ImportTheme() revel.Result {
|
||||
func (c MemberBlog) InstallTheme(themeId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = themeService.InstallTheme(c.GetUserId(), themeId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
// 新建主题
|
||||
@@ -508,17 +508,17 @@ func (c MemberBlog) NewTheme() revel.Result {
|
||||
func (c MemberBlog) SetUserBlogBase(userBlog info.UserBlogBase) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.UpdateUserBlogBase(c.GetUserId(), userBlog)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c MemberBlog) SetUserBlogComment(userBlog info.UserBlogComment) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.UpdateUserBlogComment(c.GetUserId(), userBlog)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
func (c MemberBlog) SetUserBlogStyle(userBlog info.UserBlogStyle) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.UpdateUserBlogStyle(c.GetUserId(), userBlog)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c MemberBlog) SetUserBlogPaging(perPageSize int, sortField string, isAsc bool) revel.Result {
|
||||
|
||||
@@ -14,8 +14,8 @@ type MemberGroup struct {
|
||||
func (c MemberGroup) Index() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("My Group")
|
||||
c.RenderArgs["groups"] = groupService.GetGroupsAndUsers(c.GetUserId())
|
||||
c.ViewArgs["title"] = c.Message("My Group")
|
||||
c.ViewArgs["groups"] = groupService.GetGroupsAndUsers(c.GetUserId())
|
||||
return c.RenderTemplate("member/group/index.html")
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ func (c MemberGroup) Index() revel.Result {
|
||||
func (c MemberGroup) AddGroup(title string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Item = groupService.AddGroup(c.GetUserId(), title)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c MemberGroup) UpdateGroupTitle(groupId, title string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = groupService.UpdateGroupTitle(c.GetUserId(), groupId, title)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
func (c MemberGroup) DeleteGroup(groupId string) revel.Result {
|
||||
|
||||
@@ -13,10 +13,10 @@ type MemberIndex struct {
|
||||
// admin 主页
|
||||
func (c MemberIndex) Index() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.RenderArgs["title"] = c.Message("Leanote Member Center")
|
||||
c.ViewArgs["title"] = c.Message("Leanote Member Center")
|
||||
|
||||
c.RenderArgs["countNote"] = noteService.CountNote(c.GetUserId())
|
||||
c.RenderArgs["countBlog"] = noteService.CountBlog(c.GetUserId())
|
||||
c.ViewArgs["countNote"] = noteService.CountNote(c.GetUserId())
|
||||
c.ViewArgs["countBlog"] = noteService.CountBlog(c.GetUserId())
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
@@ -25,10 +25,10 @@ func (c MemberIndex) Index() revel.Result {
|
||||
|
||||
// 模板
|
||||
func (c MemberIndex) 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
|
||||
c.ViewArgs["str"] = configService.GlobalStringConfigs
|
||||
c.ViewArgs["arr"] = configService.GlobalArrayConfigs
|
||||
c.ViewArgs["map"] = configService.GlobalMapConfigs
|
||||
c.ViewArgs["arrMap"] = configService.GlobalArrMapConfigs
|
||||
return c.RenderTemplate("admin/" + t + ".html")
|
||||
}
|
||||
|
||||
|
||||
@@ -13,28 +13,28 @@ type MemberUser struct {
|
||||
func (c MemberUser) Username() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("Username")
|
||||
c.ViewArgs["title"] = c.Message("Username")
|
||||
return c.RenderTemplate("member/user/username.html")
|
||||
}
|
||||
|
||||
func (c MemberUser) Email() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("Email")
|
||||
c.ViewArgs["title"] = c.Message("Email")
|
||||
return c.RenderTemplate("member/user/email.html")
|
||||
}
|
||||
|
||||
func (c MemberUser) Password() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("Password")
|
||||
c.ViewArgs["title"] = c.Message("Password")
|
||||
return c.RenderTemplate("member/user/password.html")
|
||||
}
|
||||
|
||||
func (c MemberUser) Avatar() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("Avatar")
|
||||
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
|
||||
c.ViewArgs["title"] = c.Message("Avatar")
|
||||
c.ViewArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
|
||||
return c.RenderTemplate("member/user/avatar.html")
|
||||
}
|
||||
@@ -93,7 +93,7 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
if c.Request.Header.Get("X-Requested-With") == "XMLHttpRequest" {
|
||||
re := info.NewRe()
|
||||
re.Msg = "NOTLOGIN"
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJSON(re)
|
||||
}
|
||||
|
||||
return c.Redirect("/login")
|
||||
|
||||
@@ -15,6 +15,8 @@ type Note struct {
|
||||
Title string `Title` // 标题
|
||||
Desc string `Desc` // 描述, 非html
|
||||
|
||||
Src string `Src,omitempty` // 来源, 2016/4/22
|
||||
|
||||
ImgSrc string `ImgSrc` // 图片, 第一张缩略图地址
|
||||
Tags []string `Tags,omitempty`
|
||||
|
||||
@@ -92,6 +94,7 @@ type NoteOrContent struct {
|
||||
UserId string
|
||||
Title string
|
||||
Desc string
|
||||
Src string
|
||||
ImgSrc string
|
||||
Tags string
|
||||
Content string
|
||||
@@ -101,3 +104,9 @@ type NoteOrContent struct {
|
||||
FromUserId string // 为共享而新建
|
||||
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
|
||||
}
|
||||
|
||||
// 分开的
|
||||
type NoteAndContentSep struct {
|
||||
NoteInfo Note
|
||||
NoteContentInfo NoteContent
|
||||
}
|
||||
+7
-7
@@ -129,7 +129,7 @@ func init() {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
locale, _ := renderArgs[revel.CurrentLocaleViewArg].(string)
|
||||
tagStr := ""
|
||||
lenTags := len(tags)
|
||||
|
||||
@@ -148,7 +148,7 @@ func init() {
|
||||
}
|
||||
|
||||
classes += " label-post"
|
||||
var url = tagPostUrl + "/" + url.QueryEscape(tag)
|
||||
var url = tagPostUrl + "/" + tag
|
||||
tagStr += "<a class=\"" + classes + "\" href=\"" + url + "\">" + str + "</a>"
|
||||
if i != lenTags-1 {
|
||||
tagStr += " "
|
||||
@@ -183,7 +183,7 @@ func init() {
|
||||
}
|
||||
|
||||
revel.TemplateFuncs["msg"] = func(renderArgs map[string]interface{}, message string, args ...interface{}) template.HTML {
|
||||
str, ok := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
str, ok := renderArgs[revel.CurrentLocaleViewArg].(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
@@ -192,7 +192,7 @@ func init() {
|
||||
|
||||
// 不用revel的msg
|
||||
revel.TemplateFuncs["leaMsg"] = func(renderArgs map[string]interface{}, key string) template.HTML {
|
||||
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
locale, _ := renderArgs[revel.CurrentLocaleViewArg].(string)
|
||||
str := i18n.Message(locale, key)
|
||||
if strings.HasPrefix(str, "???") {
|
||||
str = key
|
||||
@@ -205,7 +205,7 @@ func init() {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
locale, _ := renderArgs[revel.CurrentLocaleViewArg].(string)
|
||||
tagStr := ""
|
||||
lenTags := len(tags)
|
||||
|
||||
@@ -230,7 +230,7 @@ func init() {
|
||||
classes += " label-default"
|
||||
}
|
||||
classes += " label-post"
|
||||
var url = tagPostUrl + url.QueryEscape(tag)
|
||||
var url = tagPostUrl + tag
|
||||
tagStr += "<a class=\"" + classes + "\" href=\"" + url + "\">" + str + "</a>"
|
||||
if i != lenTags-1 {
|
||||
tagStr += " "
|
||||
@@ -307,7 +307,7 @@ func init() {
|
||||
|
||||
// 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)
|
||||
str, ok := renderArgs[revel.CurrentLocaleViewArg].(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
+11
-11
@@ -39,7 +39,7 @@ var CloneTemplate *template.Template
|
||||
type RenderTemplateResult struct {
|
||||
Template *template.Template
|
||||
PathContent map[string]string
|
||||
RenderArgs map[string]interface{}
|
||||
ViewArgs map[string]interface{}
|
||||
|
||||
IsPreview bool // 是否是预览
|
||||
CurBlogTpl *BlogTpl
|
||||
@@ -62,7 +62,7 @@ func parseTemplateError(err error) (templateName string, line int, description s
|
||||
return templateName, line, description
|
||||
}
|
||||
func (r *RenderTemplateResult) render(req *revel.Request, resp *revel.Response, wr io.Writer) {
|
||||
err := r.Template.Execute(wr, r.RenderArgs)
|
||||
err := r.Template.Execute(wr, r.ViewArgs)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func (r *RenderTemplateResult) render(req *revel.Request, resp *revel.Response,
|
||||
// 这里, 错误!!
|
||||
// 这里应该导向到本主题的错误页面
|
||||
resp.Status = 500
|
||||
ErrorResult{r.RenderArgs, compileError, r.IsPreview, r.CurBlogTpl}.Apply(req, resp)
|
||||
ErrorResult{r.ViewArgs, compileError, r.IsPreview, r.CurBlogTpl}.Apply(req, resp)
|
||||
}
|
||||
|
||||
func (r *RenderTemplateResult) Apply(req *revel.Request, resp *revel.Response) {
|
||||
@@ -162,7 +162,7 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
r = &RenderTemplateResult{
|
||||
Template: t,
|
||||
PathContent: BlogTplObject.PathContent, // 为了显示错误
|
||||
RenderArgs: args, // 把args给它
|
||||
ViewArgs: args, // 把args给它
|
||||
}
|
||||
} else {
|
||||
// 复制一份
|
||||
@@ -202,7 +202,7 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
r = &RenderTemplateResult{
|
||||
Template: t,
|
||||
PathContent: newBlogTplObject.PathContent, // 为了显示错误
|
||||
RenderArgs: args,
|
||||
ViewArgs: args,
|
||||
CurBlogTpl: newBlogTplObject,
|
||||
IsPreview: isPreview,
|
||||
}
|
||||
@@ -215,7 +215,7 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
//
|
||||
|
||||
type ErrorResult struct {
|
||||
RenderArgs map[string]interface{}
|
||||
ViewArgs map[string]interface{}
|
||||
Error error
|
||||
IsPreview bool
|
||||
CurBlogTpl *BlogTpl
|
||||
@@ -280,18 +280,18 @@ func (r ErrorResult) Apply(req *revel.Request, resp *revel.Response) {
|
||||
panic("no error provided")
|
||||
}
|
||||
|
||||
if r.RenderArgs == nil {
|
||||
r.RenderArgs = make(map[string]interface{})
|
||||
if r.ViewArgs == nil {
|
||||
r.ViewArgs = make(map[string]interface{})
|
||||
}
|
||||
r.RenderArgs["Error"] = revelError
|
||||
r.RenderArgs["Router"] = revel.MainRouter
|
||||
r.ViewArgs["Error"] = revelError
|
||||
r.ViewArgs["Router"] = revel.MainRouter
|
||||
|
||||
// 不是preview就不要显示错误了
|
||||
if r.IsPreview {
|
||||
var b bytes.Buffer
|
||||
out := io.Writer(resp.Out)
|
||||
// out = ioutil.Discard
|
||||
err = tmpl.Execute(&b, r.RenderArgs)
|
||||
err = tmpl.Execute(&b, r.ViewArgs)
|
||||
resp.WriteHeader(http.StatusOK, "text/html; charset=utf-8")
|
||||
b.WriteTo(out)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
CurrentLocaleRenderArg = "currentLocale" // The key for the current locale render arg value
|
||||
CurrentLocaleViewArg = "currentLocale" // The key for the current locale render arg value
|
||||
|
||||
messageFilesDirectory = "messages"
|
||||
messageFilePattern = `^\w+\.conf$`
|
||||
@@ -193,7 +193,7 @@ func I18nFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
// Set the current locale controller argument (CurrentLocaleControllerArg) with the given locale.
|
||||
func setCurrentLocaleControllerArguments(c *revel.Controller, locale string) {
|
||||
c.Request.Locale = locale
|
||||
c.RenderArgs[CurrentLocaleRenderArg] = locale
|
||||
c.ViewArgs[CurrentLocaleViewArg] = locale
|
||||
}
|
||||
|
||||
// Determine whether the given request has valid Accept-Language value.
|
||||
|
||||
@@ -673,19 +673,18 @@ func (this *BlogService) LikeBlog(noteId, userId string) (ok bool, isLike bool)
|
||||
|
||||
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}})
|
||||
|
||||
count := db.Count(db.BlogLikes, bson.M{"NoteId": noteIdO})
|
||||
ok = db.UpdateByQI(db.Notes, bson.M{"_id": noteIdO}, bson.M{"LikeNum": count})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -606,5 +606,5 @@ func (this *ConfigService) HomePageIsAdminsBlog() bool {
|
||||
}
|
||||
|
||||
func (this *ConfigService) GetVersion() string {
|
||||
return "2.2"
|
||||
return "2.4"
|
||||
}
|
||||
@@ -62,6 +62,35 @@ func (this *NoteService) GetNoteAndContent(noteId, userId string) (noteAndConten
|
||||
return info.NoteAndContent{note, noteContent}
|
||||
}
|
||||
|
||||
func (this *NoteService) GetNoteBySrc(src, userId string) (note info.Note) {
|
||||
note = info.Note{}
|
||||
if src == "" {
|
||||
return
|
||||
}
|
||||
|
||||
notes := []info.Note{}
|
||||
q := db.Notes.Find(bson.M{
|
||||
"UserId": bson.ObjectIdHex(userId),
|
||||
"Src": src,
|
||||
})
|
||||
q.Sort("-Usn").Limit(1).All(¬es)
|
||||
if len(notes) > 0 {
|
||||
return notes[0]
|
||||
}
|
||||
// db.GetByQ(db.Notes, bson.M{"Src": src, "UserId": bson.ObjectIdHex(userId), "IsDeleted": false}, ¬e)
|
||||
return
|
||||
}
|
||||
|
||||
func (this *NoteService) GetNoteAndContentBySrc(src, userId string) (noteId string, noteAndContent info.NoteAndContentSep) {
|
||||
note := this.GetNoteBySrc(src, userId)
|
||||
if (note.NoteId != "") {
|
||||
noteId = note.NoteId.Hex()
|
||||
noteContent := this.GetNoteContent(note.NoteId.Hex(), userId)
|
||||
return noteId, info.NoteAndContentSep{note, noteContent}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 获取同步的笔记
|
||||
// > afterUsn的笔记
|
||||
func (this *NoteService) GetSyncNotes(userId string, afterUsn, maxEntry int) []info.ApiNote {
|
||||
@@ -863,7 +892,13 @@ func (this *NoteService) searchNoteFromContent(notes []info.Note, userId, key st
|
||||
notes2 := this.ListNotesByNoteIds(noteIds2)
|
||||
|
||||
// 合并之
|
||||
notes = append(notes, notes2...)
|
||||
// 不能是删除的
|
||||
for _, n := range notes2 {
|
||||
if !n.IsDeleted && !n.IsTrash {
|
||||
// notes = append(notes, notes2...)
|
||||
notes = append(notes, n)
|
||||
}
|
||||
}
|
||||
return notes
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package service
|
||||
import (
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
)
|
||||
@@ -61,6 +61,14 @@ func (this *TagService) AddOrUpdateTag(userId string, tag string) info.NoteTag {
|
||||
noteTag.Count = count
|
||||
noteTag.UpdatedTime = time.Now()
|
||||
// noteTag.Usn = userService.IncrUsn(userId), 更新count而已
|
||||
|
||||
// 之前删除过的, 现在要添加回来了
|
||||
if noteTag.IsDeleted {
|
||||
Log("之前删除过的, 现在要添加回来了: " + tag)
|
||||
noteTag.Usn = userService.IncrUsn(userId)
|
||||
noteTag.IsDeleted = false
|
||||
}
|
||||
|
||||
db.UpdateByIdAndUserId(db.NoteTags, noteTag.TagId.Hex(), userId, noteTag)
|
||||
return noteTag
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ function log(o) {
|
||||
<a data-lang="zh-cn">简体中文</a>
|
||||
<a data-lang="zh-hk">繁体中文</a>
|
||||
<a data-lang="pt-pt">Português</a>
|
||||
<a data-lang="es-co">Spanish</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav navbar-nav navbar-left">
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
name="sortField"
|
||||
value="">
|
||||
<option value="PublicTime" {{if eq $.userBlog.SortField "PublicTime"}}selected{{end}}>{{msg $ "publicTime"}}</option>
|
||||
<option value="CreatedTime" {{if eq $.userBlog.SortField "CreatedTimeTime"}}selected{{end}}>{{msg $ "createdTime"}}</option>
|
||||
<option value="CreatedTime" {{if eq $.userBlog.SortField "CreatedTime"}}selected{{end}}>{{msg $ "createdTime"}}</option>
|
||||
<option value="UpdatedTime" {{if eq $.userBlog.SortField "UpdatedTime"}}selected{{end}}>{{msg $ "updatedTime"}}</option>
|
||||
<option value="Title" {{if eq $.userBlog.SortField "Title"}}selected{{end}}>{{msg . "title"}}</option>
|
||||
</select>
|
||||
|
||||
@@ -345,63 +345,74 @@ function log(o) {
|
||||
</div>
|
||||
<div id="noteList">
|
||||
<div class="clearfix" id="notesAndSort" style="position: relative">
|
||||
|
||||
<div class="pull-left">
|
||||
<!-- 我的笔记本 -->
|
||||
<div class="dropdown" id="myNotebookNavForListNav">
|
||||
<a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">
|
||||
<a class="ios7-a">
|
||||
<span id="curNotebookForListNote">{{msg . "all"}}</span>
|
||||
<!--
|
||||
<i class="fa fa-angle-down"></i>
|
||||
-->
|
||||
</a>
|
||||
<!-- 如果notebook过多, 则会产生滚动 弃用
|
||||
<div class="dropdown-menu dropdown-list" style="left: 5px;">
|
||||
<ul role="menu" aria-labelledby="dropdownMenu1" id="notebookNavForListNote"></ul>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<!-- 共享的笔记本 -->
|
||||
<div class="dropdown" id="sharedNotebookNavForListNav" style="display: none">
|
||||
<a class="ios7-a dropdown-toggle" id="dropdownMenu2" data-toggle="dropdown">
|
||||
<a class="ios7-a">
|
||||
<span id="curSharedNotebookForListNote">{{msg . "all"}}</span>
|
||||
<!--
|
||||
<i class="fa fa-angle-down"></i>
|
||||
-->
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu"
|
||||
aria-labelledby="dropdownMenu2" style="left: 5px;" id="sharedNotebookNavForListNote">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pull-left" id="tagSearch" style="line-height: 38px">
|
||||
</div>
|
||||
<!-- tag search -->
|
||||
<div class="pull-left" id="tagSearch"></div>
|
||||
|
||||
<!-- sort type -->
|
||||
<div id="sortType">
|
||||
<div class="dropdown">
|
||||
<a class="ios7-a dropdown-toggle" id="dropdownMenu1"
|
||||
data-toggle="dropdown">
|
||||
<!--
|
||||
<i class="fa fa-sort-alpha-asc">
|
||||
</i>Sort <i class="fa fa-angle-down"></i>
|
||||
-->
|
||||
</i><i class="fa fa-th-list"></i>
|
||||
</a>
|
||||
<!--
|
||||
<ul class="dropdown-menu" role="menu"
|
||||
aria-labelledby="dropdownMenu1"
|
||||
style="right: 3px; ! important; left: -100px; min-width: 100px;">
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="#">创建日期</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="#">修改日期</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="#">标题</a></li>
|
||||
>
|
||||
|
||||
<li role="presentation"><a data-view="snippet" class="view-style view-snippet" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Snippet View"}}
|
||||
</a></li>
|
||||
<li role="presentation"><a data-view="list" class="view-style view-list" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "List View"}}
|
||||
</a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="#">Separated </a></li>
|
||||
<li role="presentation"><a data-sorter="dateCreatedASC" class="sorter-style sorter-dateCreatedASC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Date Created"}} <i class="fa fa-sort-alpha-asc"></i>
|
||||
</a></li>
|
||||
<li role="presentation"><a data-sorter="dateCreatedDESC" class="sorter-style sorter-dateCreatedDESC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Date Created"}} <i class="fa fa-sort-alpha-desc"></i>
|
||||
</a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a data-sorter="dateUpdatedASC" class="sorter-style sorter-dateUpdatedASC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Date Updated"}} <i class="fa fa-sort-alpha-asc"></i>
|
||||
</a></li>
|
||||
<li role="presentation"><a data-sorter="dateUpdatedDESC" class="sorter-style sorter-dateUpdatedDESC checked" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Date Updated"}} <i class="fa fa-sort-alpha-desc"></i>
|
||||
</a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a data-sorter="titleASC" class="sorter-style sorter-titleASC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Title"}} <i class="fa fa-sort-alpha-asc"></i>
|
||||
|
||||
</a></li>
|
||||
<li role="presentation"><a data-sorter="titleDESC" class="sorter-style sorter-titleDESC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Title"}} <i class="fa fa-sort-alpha-desc"></i>
|
||||
</a></li>
|
||||
</ul>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -856,7 +867,7 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var LEA = {};
|
||||
var LEA = {sPath: ''};
|
||||
LEA.locale = "{{.locale}}";
|
||||
var UrlPrefix = '{{.siteUrl}}';
|
||||
var UserInfo = {{.userInfo|jsonJs}};
|
||||
@@ -882,7 +893,7 @@ var GlobalConfigs = {{.globalConfigs|jsonJs}};
|
||||
<!-- dev -->
|
||||
<script src="/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="/js/jquery.ztree.all-3.5.js"></script>
|
||||
<script src="/js/object_id-min.js"></script>
|
||||
<script src="/js/object_id.js"></script>
|
||||
<!-- /dev -->
|
||||
|
||||
<script src="/tinymce/tinymce.js"></script>
|
||||
|
||||
+43
-34
@@ -333,58 +333,67 @@ function log(o) {
|
||||
<div class="pull-left">
|
||||
<!-- 我的笔记本 -->
|
||||
<div class="dropdown" id="myNotebookNavForListNav">
|
||||
<a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">
|
||||
<a class="ios7-a">
|
||||
<span id="curNotebookForListNote">{{msg . "all"}}</span>
|
||||
<!--
|
||||
<i class="fa fa-angle-down"></i>
|
||||
-->
|
||||
</a>
|
||||
<!-- 如果notebook过多, 则会产生滚动 弃用
|
||||
<div class="dropdown-menu dropdown-list" style="left: 5px;">
|
||||
<ul role="menu" aria-labelledby="dropdownMenu1" id="notebookNavForListNote"></ul>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<!-- 共享的笔记本 -->
|
||||
<div class="dropdown" id="sharedNotebookNavForListNav" style="display: none">
|
||||
<a class="ios7-a dropdown-toggle" id="dropdownMenu2" data-toggle="dropdown">
|
||||
<a class="ios7-a">
|
||||
<span id="curSharedNotebookForListNote">{{msg . "all"}}</span>
|
||||
<!--
|
||||
<i class="fa fa-angle-down"></i>
|
||||
-->
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu"
|
||||
aria-labelledby="dropdownMenu2" style="left: 5px;" id="sharedNotebookNavForListNote">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-left" id="tagSearch" style="line-height: 38px">
|
||||
</div>
|
||||
<!-- tag search -->
|
||||
<div class="pull-left" id="tagSearch"></div>
|
||||
<!-- sort type -->
|
||||
<div id="sortType">
|
||||
<div class="dropdown">
|
||||
<a class="ios7-a dropdown-toggle" id="dropdownMenu1"
|
||||
data-toggle="dropdown">
|
||||
<!--
|
||||
<i class="fa fa-sort-alpha-asc">
|
||||
</i>Sort <i class="fa fa-angle-down"></i>
|
||||
-->
|
||||
</i><i class="fa fa-th-list"></i>
|
||||
</a>
|
||||
<!--
|
||||
<ul class="dropdown-menu" role="menu"
|
||||
aria-labelledby="dropdownMenu1"
|
||||
style="right: 3px; ! important; left: -100px; min-width: 100px;">
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="#">创建日期</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="#">修改日期</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="#">标题</a></li>
|
||||
>
|
||||
<li role="presentation"><a data-view="snippet" class="view-style view-snippet" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Snippet View"}}
|
||||
</a></li>
|
||||
<li role="presentation"><a data-view="list" class="view-style view-list" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "List View"}}
|
||||
</a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="#">Separated </a></li>
|
||||
<li role="presentation"><a data-sorter="dateCreatedASC" class="sorter-style sorter-dateCreatedASC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Date Created"}} <i class="fa fa-sort-alpha-asc"></i>
|
||||
</a></li>
|
||||
<li role="presentation"><a data-sorter="dateCreatedDESC" class="sorter-style sorter-dateCreatedDESC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Date Created"}} <i class="fa fa-sort-alpha-desc"></i>
|
||||
</a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a data-sorter="dateUpdatedASC" class="sorter-style sorter-dateUpdatedASC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Date Updated"}} <i class="fa fa-sort-alpha-asc"></i>
|
||||
</a></li>
|
||||
<li role="presentation"><a data-sorter="dateUpdatedDESC" class="sorter-style sorter-dateUpdatedDESC checked" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Date Updated"}} <i class="fa fa-sort-alpha-desc"></i>
|
||||
</a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a data-sorter="titleASC" class="sorter-style sorter-titleASC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Title"}} <i class="fa fa-sort-alpha-asc"></i>
|
||||
|
||||
</a></li>
|
||||
<li role="presentation"><a data-sorter="titleDESC" class="sorter-style sorter-titleDESC" role="menuitem">
|
||||
<i class="fa fa-check"></i>
|
||||
{{leaMsg . "Title"}} <i class="fa fa-sort-alpha-desc"></i>
|
||||
</a></li>
|
||||
</ul>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -828,7 +837,7 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var LEA = {};
|
||||
var LEA = {sPath: ''};
|
||||
LEA.locale = "{{.locale}}";
|
||||
var UrlPrefix = '{{.siteUrl}}';
|
||||
var UserInfo = {{.userInfo|jsonJs}};
|
||||
|
||||
+7
-3
@@ -9,7 +9,7 @@ SP=$(cd "$(dirname "$0")"; pwd)
|
||||
tmp="/Users/life/Desktop/leanote_release"
|
||||
|
||||
# version
|
||||
V="v2.2"
|
||||
V="v2.5"
|
||||
|
||||
##=================================
|
||||
# 1. 先build 成 3个平台, 2种bit = 6种
|
||||
@@ -32,7 +32,7 @@ function build()
|
||||
suffix=".exe"
|
||||
fi
|
||||
|
||||
GOOS=$1 GOARCH=$2 go build -o leanote-$1-$2$suffix ../app/tmp
|
||||
GOOS=$1 GOARCH=$2 go build -o leanote-$1-$2$suffix github.com/leanote/leanote/app/tmp
|
||||
}
|
||||
|
||||
build "linux" "386";
|
||||
@@ -62,12 +62,16 @@ cd $SP
|
||||
cd ../
|
||||
|
||||
# bin
|
||||
cp -r ./bin/src $tmp/leanote/bin/
|
||||
# cp -r ./bin/src $tmp/leanote/bin/
|
||||
mkdir -p $tmp/leanote/bin/src/github.com/leanote
|
||||
cp ./bin/run.sh $tmp/leanote/bin/
|
||||
cp ./bin/run.bat $tmp/leanote/bin/
|
||||
|
||||
# views
|
||||
cp -r ./app/views $tmp/leanote/app
|
||||
# vendor
|
||||
cp -r ./vendor $tmp/leanote/
|
||||
|
||||
# 可不要, 源码
|
||||
#cp -r ./app/service $tmp/leanote/app/service
|
||||
#cp -r ./app/controllers $tmp/leanote/app/controllers
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Revel command line tools
|
||||
|
||||
Provides the `revel` command, used to create and run Revel apps.
|
||||
|
||||
- More info at http://revel.github.io/manual/tool.html
|
||||
|
||||
Install
|
||||
------------
|
||||
```bash
|
||||
go get github.com/revel/cmd/revel
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package harness
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
// App contains the configuration for running a Revel app. (Not for the app itself)
|
||||
// Its only purpose is constructing the command to execute.
|
||||
type App struct {
|
||||
BinaryPath string // Path to the app executable
|
||||
Port int // Port to pass as a command line argument.
|
||||
cmd AppCmd // The last cmd returned.
|
||||
}
|
||||
|
||||
// NewApp returns app instance with binary path in it
|
||||
func NewApp(binPath string) *App {
|
||||
return &App{BinaryPath: binPath}
|
||||
}
|
||||
|
||||
// Cmd returns a command to run the app server using the current configuration.
|
||||
func (a *App) Cmd() AppCmd {
|
||||
a.cmd = NewAppCmd(a.BinaryPath, a.Port)
|
||||
return a.cmd
|
||||
}
|
||||
|
||||
// Kill the last app command returned.
|
||||
func (a *App) Kill() {
|
||||
a.cmd.Kill()
|
||||
}
|
||||
|
||||
// AppCmd manages the running of a Revel app server.
|
||||
// It requires revel.Init to have been called previously.
|
||||
type AppCmd struct {
|
||||
*exec.Cmd
|
||||
}
|
||||
|
||||
// NewAppCmd returns the AppCmd with parameters initialized for running app
|
||||
func NewAppCmd(binPath string, port int) AppCmd {
|
||||
cmd := exec.Command(binPath,
|
||||
fmt.Sprintf("-port=%d", port),
|
||||
fmt.Sprintf("-importPath=%s", revel.ImportPath),
|
||||
fmt.Sprintf("-runMode=%s", revel.RunMode))
|
||||
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
return AppCmd{cmd}
|
||||
}
|
||||
|
||||
// Start the app server, and wait until it is ready to serve requests.
|
||||
func (cmd AppCmd) Start() error {
|
||||
listeningWriter := startupListeningWriter{os.Stdout, make(chan bool)}
|
||||
cmd.Stdout = listeningWriter
|
||||
revel.TRACE.Println("Exec app:", cmd.Path, cmd.Args)
|
||||
if err := cmd.Cmd.Start(); err != nil {
|
||||
revel.ERROR.Fatalln("Error running:", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-cmd.waitChan():
|
||||
return errors.New("revel/harness: app died")
|
||||
|
||||
case <-time.After(30 * time.Second):
|
||||
cmd.Kill()
|
||||
return errors.New("revel/harness: app timed out")
|
||||
|
||||
case <-listeningWriter.notifyReady:
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO remove this unreachable code and document it
|
||||
panic("Impossible")
|
||||
}
|
||||
|
||||
// Run the app server inline. Never returns.
|
||||
func (cmd AppCmd) Run() {
|
||||
revel.TRACE.Println("Exec app:", cmd.Path, cmd.Args)
|
||||
if err := cmd.Cmd.Run(); err != nil {
|
||||
revel.ERROR.Fatalln("Error running:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Kill terminates the app server if it's running.
|
||||
func (cmd AppCmd) Kill() {
|
||||
if cmd.Cmd != nil && (cmd.ProcessState == nil || !cmd.ProcessState.Exited()) {
|
||||
revel.TRACE.Println("Killing revel server pid", cmd.Process.Pid)
|
||||
err := cmd.Process.Kill()
|
||||
if err != nil {
|
||||
revel.ERROR.Fatalln("Failed to kill revel server:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return a channel that is notified when Wait() returns.
|
||||
func (cmd AppCmd) waitChan() <-chan struct{} {
|
||||
ch := make(chan struct{}, 1)
|
||||
go func() {
|
||||
_ = cmd.Wait()
|
||||
ch <- struct{}{}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// A io.Writer that copies to the destination, and listens for "Listening on.."
|
||||
// in the stream. (Which tells us when the revel server has finished starting up)
|
||||
// This is super ghetto, but by far the simplest thing that should work.
|
||||
type startupListeningWriter struct {
|
||||
dest io.Writer
|
||||
notifyReady chan bool
|
||||
}
|
||||
|
||||
func (w startupListeningWriter) Write(p []byte) (n int, err error) {
|
||||
if w.notifyReady != nil && bytes.Contains(p, []byte("Listening")) {
|
||||
w.notifyReady <- true
|
||||
w.notifyReady = nil
|
||||
}
|
||||
return w.dest.Write(p)
|
||||
}
|
||||
Executable
+451
@@ -0,0 +1,451 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package harness
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var importErrorPattern = regexp.MustCompile("cannot find package \"([^\"]+)\"")
|
||||
|
||||
// Build the app:
|
||||
// 1. Generate the the main.go file.
|
||||
// 2. Run the appropriate "go build" command.
|
||||
// Requires that revel.Init has been called previously.
|
||||
// Returns the path to the built binary, and an error if there was a problem building it.
|
||||
func Build(buildFlags ...string) (app *App, compileError *revel.Error) {
|
||||
// First, clear the generated files (to avoid them messing with ProcessSource).
|
||||
cleanSource("tmp", "routes")
|
||||
|
||||
sourceInfo, compileError := ProcessSource(revel.CodePaths)
|
||||
if compileError != nil {
|
||||
return nil, compileError
|
||||
}
|
||||
|
||||
// Add the db.import to the import paths.
|
||||
if dbImportPath, found := revel.Config.String("db.import"); found {
|
||||
sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, dbImportPath)
|
||||
}
|
||||
|
||||
// Generate two source files.
|
||||
templateArgs := map[string]interface{}{
|
||||
"Controllers": sourceInfo.ControllerSpecs(),
|
||||
"ValidationKeys": sourceInfo.ValidationKeys,
|
||||
"ImportPaths": calcImportAliases(sourceInfo),
|
||||
"TestSuites": sourceInfo.TestSuites(),
|
||||
}
|
||||
// fmt.Println("revel.CodePaths", revel.CodePaths)
|
||||
// fmt.Println("----genSource tmp")
|
||||
genSource("tmp", "main.go", RevelMainTemplate, templateArgs)
|
||||
// fmt.Println("-----genSource routes", templateArgs)
|
||||
genSource("routes", "routes.go", RevelRoutesTemplate, templateArgs)
|
||||
|
||||
// Read build config.
|
||||
buildTags := revel.Config.StringDefault("build.tags", "")
|
||||
|
||||
// Build the user program (all code under app).
|
||||
// It relies on the user having "go" installed.
|
||||
goPath, err := exec.LookPath("go")
|
||||
if err != nil {
|
||||
revel.ERROR.Fatalf("Go executable not found in PATH.")
|
||||
}
|
||||
|
||||
pkg, err := build.Default.Import(revel.ImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
revel.ERROR.Fatalln("Failure importing", revel.ImportPath)
|
||||
}
|
||||
|
||||
// Binary path is a combination of $GOBIN/revel.d directory, app's import path and its name.
|
||||
binName := filepath.Join(pkg.BinDir, "revel.d", revel.ImportPath, filepath.Base(revel.BasePath))
|
||||
|
||||
// Change binary path for Windows build
|
||||
goos := runtime.GOOS
|
||||
if goosEnv := os.Getenv("GOOS"); goosEnv != "" {
|
||||
goos = goosEnv
|
||||
}
|
||||
if goos == "windows" {
|
||||
binName += ".exe"
|
||||
}
|
||||
|
||||
gotten := make(map[string]struct{})
|
||||
for {
|
||||
appVersion := getAppVersion()
|
||||
|
||||
buildTime := time.Now().UTC().Format(time.RFC3339)
|
||||
versionLinkerFlags := fmt.Sprintf("-X %s/app.AppVersion=%s -X %s/app.BuildTime=%s",
|
||||
revel.ImportPath, appVersion, revel.ImportPath, buildTime)
|
||||
|
||||
// TODO remove version check for versionLinkerFlags after Revel becomes Go min version to go1.5
|
||||
goVersion, err := strconv.ParseFloat(runtime.Version()[2:5], 64)
|
||||
// runtime.Version() may return commit hash, we assume it is above 1.5
|
||||
if goVersion < 1.5 && err == nil {
|
||||
versionLinkerFlags = fmt.Sprintf("-X %s/app.AppVersion \"%s\" -X %s/app.BuildTime \"%s\"",
|
||||
revel.ImportPath, appVersion, revel.ImportPath, buildTime)
|
||||
}
|
||||
flags := []string{
|
||||
"build",
|
||||
"-i",
|
||||
"-ldflags", versionLinkerFlags,
|
||||
"-tags", buildTags,
|
||||
"-o", binName}
|
||||
|
||||
// Add in build flags
|
||||
flags = append(flags, buildFlags...)
|
||||
|
||||
// This is Go main path
|
||||
// Note: It's not applicable for filepath.* usage
|
||||
flags = append(flags, path.Join(revel.ImportPath, "app", "tmp"))
|
||||
|
||||
buildCmd := exec.Command(goPath, flags...)
|
||||
revel.TRACE.Println("Exec:", buildCmd.Args)
|
||||
output, err := buildCmd.CombinedOutput()
|
||||
|
||||
// If the build succeeded, we're done.
|
||||
if err == nil {
|
||||
return NewApp(binName), nil
|
||||
}
|
||||
revel.ERROR.Println(string(output))
|
||||
|
||||
// See if it was an import error that we can go get.
|
||||
matches := importErrorPattern.FindStringSubmatch(string(output))
|
||||
if matches == nil {
|
||||
return nil, newCompileError(output)
|
||||
}
|
||||
|
||||
// Ensure we haven't already tried to go get it.
|
||||
pkgName := matches[1]
|
||||
if _, alreadyTried := gotten[pkgName]; alreadyTried {
|
||||
return nil, newCompileError(output)
|
||||
}
|
||||
gotten[pkgName] = struct{}{}
|
||||
|
||||
// Execute "go get <pkg>"
|
||||
getCmd := exec.Command(goPath, "get", pkgName)
|
||||
revel.TRACE.Println("Exec:", getCmd.Args)
|
||||
getOutput, err := getCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
revel.ERROR.Println(string(getOutput))
|
||||
return nil, newCompileError(output)
|
||||
}
|
||||
|
||||
// Success getting the import, attempt to build again.
|
||||
}
|
||||
|
||||
// TODO remove this unreachable code and document it
|
||||
revel.ERROR.Fatalf("Not reachable")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Try to define a version string for the compiled app
|
||||
// The following is tried (first match returns):
|
||||
// - Read a version explicitly specified in the APP_VERSION environment
|
||||
// variable
|
||||
// - Read the output of "git describe" if the source is in a git repository
|
||||
// If no version can be determined, an empty string is returned.
|
||||
func getAppVersion() string {
|
||||
if version := os.Getenv("APP_VERSION"); version != "" {
|
||||
return version
|
||||
}
|
||||
|
||||
// Check for the git binary
|
||||
if gitPath, err := exec.LookPath("git"); err == nil {
|
||||
// Check for the .git directory
|
||||
gitDir := filepath.Join(revel.BasePath, ".git")
|
||||
info, err := os.Stat(gitDir)
|
||||
if (err != nil && os.IsNotExist(err)) || !info.IsDir() {
|
||||
return ""
|
||||
}
|
||||
gitCmd := exec.Command(gitPath, "--git-dir="+gitDir, "describe", "--always", "--dirty")
|
||||
revel.TRACE.Println("Exec:", gitCmd.Args)
|
||||
output, err := gitCmd.Output()
|
||||
|
||||
if err != nil {
|
||||
revel.WARN.Println("Cannot determine git repository version:", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return "git-" + strings.TrimSpace(string(output))
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func cleanSource(dirs ...string) {
|
||||
for _, dir := range dirs {
|
||||
cleanDir(dir)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanDir(dir string) {
|
||||
revel.INFO.Println("Cleaning dir " + dir)
|
||||
tmpPath := filepath.Join(revel.AppPath, dir)
|
||||
f, err := os.Open(tmpPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
revel.ERROR.Println("Failed to clean dir:", err)
|
||||
}
|
||||
} else {
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
|
||||
infos, err := f.Readdir(0)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
revel.ERROR.Println("Failed to clean dir:", err)
|
||||
}
|
||||
} else {
|
||||
for _, info := range infos {
|
||||
path := filepath.Join(tmpPath, info.Name())
|
||||
if info.IsDir() {
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
revel.ERROR.Println("Failed to remove dir:", err)
|
||||
}
|
||||
} else {
|
||||
err := os.Remove(path)
|
||||
if err != nil {
|
||||
revel.ERROR.Println("Failed to remove file:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// genSource renders the given template to produce source code, which it writes
|
||||
// to the given directory and file.
|
||||
func genSource(dir, filename, templateSource string, args map[string]interface{}) {
|
||||
sourceCode := revel.ExecuteTemplate(
|
||||
template.Must(template.New("").Parse(templateSource)),
|
||||
args)
|
||||
|
||||
// Create a fresh dir.
|
||||
cleanSource(dir)
|
||||
tmpPath := filepath.Join(revel.AppPath, dir)
|
||||
err := os.Mkdir(tmpPath, 0777)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
revel.ERROR.Fatalf("Failed to make '%v' directory: %v", dir, err)
|
||||
}
|
||||
|
||||
// Create the file
|
||||
file, err := os.Create(filepath.Join(tmpPath, filename))
|
||||
if err != nil {
|
||||
revel.ERROR.Fatalf("Failed to create file: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
if _, err = file.WriteString(sourceCode); err != nil {
|
||||
revel.ERROR.Fatalf("Failed to write to file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Looks through all the method args and returns a set of unique import paths
|
||||
// that cover all the method arg types.
|
||||
// Additionally, assign package aliases when necessary to resolve ambiguity.
|
||||
func calcImportAliases(src *SourceInfo) map[string]string {
|
||||
aliases := make(map[string]string)
|
||||
typeArrays := [][]*TypeInfo{src.ControllerSpecs(), src.TestSuites()}
|
||||
for _, specs := range typeArrays {
|
||||
for _, spec := range specs {
|
||||
addAlias(aliases, spec.ImportPath, spec.PackageName)
|
||||
|
||||
for _, methSpec := range spec.MethodSpecs {
|
||||
for _, methArg := range methSpec.Args {
|
||||
if methArg.ImportPath == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
addAlias(aliases, methArg.ImportPath, methArg.TypeExpr.PkgName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the "InitImportPaths", with alias "_"
|
||||
for _, importPath := range src.InitImportPaths {
|
||||
if _, ok := aliases[importPath]; !ok {
|
||||
aliases[importPath] = "_"
|
||||
}
|
||||
}
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
func addAlias(aliases map[string]string, importPath, pkgName string) {
|
||||
alias, ok := aliases[importPath]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
alias = makePackageAlias(aliases, pkgName)
|
||||
aliases[importPath] = alias
|
||||
}
|
||||
|
||||
func makePackageAlias(aliases map[string]string, pkgName string) string {
|
||||
i := 0
|
||||
alias := pkgName
|
||||
for containsValue(aliases, alias) || alias=="revel" {
|
||||
alias = fmt.Sprintf("%s%d", pkgName, i)
|
||||
i++
|
||||
}
|
||||
return alias
|
||||
}
|
||||
|
||||
func containsValue(m map[string]string, val string) bool {
|
||||
for _, v := range m {
|
||||
if v == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Parse the output of the "go build" command.
|
||||
// Return a detailed Error.
|
||||
func newCompileError(output []byte) *revel.Error {
|
||||
errorMatch := regexp.MustCompile(`(?m)^([^:#]+):(\d+):(\d+:)? (.*)$`).
|
||||
FindSubmatch(output)
|
||||
if errorMatch == nil {
|
||||
errorMatch = regexp.MustCompile(`(?m)^(.*?)\:(\d+)\:\s(.*?)$`).FindSubmatch(output)
|
||||
|
||||
if errorMatch == nil {
|
||||
revel.ERROR.Println("Failed to parse build errors:\n", string(output))
|
||||
return &revel.Error{
|
||||
SourceType: "Go code",
|
||||
Title: "Go Compilation Error",
|
||||
Description: "See console for build error.",
|
||||
}
|
||||
}
|
||||
|
||||
errorMatch = append(errorMatch, errorMatch[3])
|
||||
|
||||
revel.ERROR.Println("Build errors:\n", string(output))
|
||||
}
|
||||
|
||||
// Read the source for the offending file.
|
||||
var (
|
||||
relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go"
|
||||
absFilename, _ = filepath.Abs(relFilename)
|
||||
line, _ = strconv.Atoi(string(errorMatch[2]))
|
||||
description = string(errorMatch[4])
|
||||
compileError = &revel.Error{
|
||||
SourceType: "Go code",
|
||||
Title: "Go Compilation Error",
|
||||
Path: relFilename,
|
||||
Description: description,
|
||||
Line: line,
|
||||
}
|
||||
)
|
||||
|
||||
errorLink := revel.Config.StringDefault("error.link", "")
|
||||
|
||||
if errorLink != "" {
|
||||
compileError.SetLink(errorLink)
|
||||
}
|
||||
|
||||
fileStr, err := revel.ReadLines(absFilename)
|
||||
if err != nil {
|
||||
compileError.MetaError = absFilename + ": " + err.Error()
|
||||
revel.ERROR.Println(compileError.MetaError)
|
||||
return compileError
|
||||
}
|
||||
|
||||
compileError.SourceLines = fileStr
|
||||
return compileError
|
||||
}
|
||||
|
||||
// RevelMainTemplate template for app/tmp/main.go
|
||||
const RevelMainTemplate = `// GENERATED CODE - DO NOT EDIT
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"reflect"
|
||||
"github.com/revel/revel"{{range $k, $v := $.ImportPaths}}
|
||||
{{$v}} "{{$k}}"{{end}}
|
||||
"github.com/revel/revel/testing"
|
||||
)
|
||||
|
||||
var (
|
||||
runMode *string = flag.String("runMode", "", "Run mode.")
|
||||
port *int = flag.Int("port", 0, "By default, read from app.conf")
|
||||
importPath *string = flag.String("importPath", "", "Go Import Path for the app.")
|
||||
srcPath *string = flag.String("srcPath", "", "Path to the source root.")
|
||||
|
||||
// So compiler won't complain if the generated code doesn't reference reflect package...
|
||||
_ = reflect.Invalid
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
revel.Init(*runMode, *importPath, *srcPath)
|
||||
revel.INFO.Println("Running revel server")
|
||||
{{range $i, $c := .Controllers}}
|
||||
revel.RegisterController((*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),
|
||||
[]*revel.MethodType{
|
||||
{{range .MethodSpecs}}&revel.MethodType{
|
||||
Name: "{{.Name}}",
|
||||
Args: []*revel.MethodArg{ {{range .Args}}
|
||||
&revel.MethodArg{Name: "{{.Name}}", Type: reflect.TypeOf((*{{index $.ImportPaths .ImportPath | .TypeExpr.TypeName}})(nil)) },{{end}}
|
||||
},
|
||||
RenderArgNames: map[int][]string{ {{range .RenderCalls}}
|
||||
{{.Line}}: []string{ {{range .Names}}
|
||||
"{{.}}",{{end}}
|
||||
},{{end}}
|
||||
},
|
||||
},
|
||||
{{end}}
|
||||
})
|
||||
{{end}}
|
||||
revel.DefaultValidationKeys = map[string]map[int]string{ {{range $path, $lines := .ValidationKeys}}
|
||||
"{{$path}}": { {{range $line, $key := $lines}}
|
||||
{{$line}}: "{{$key}}",{{end}}
|
||||
},{{end}}
|
||||
}
|
||||
testing.TestSuites = []interface{}{ {{range .TestSuites}}
|
||||
(*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),{{end}}
|
||||
}
|
||||
|
||||
revel.Run(*port)
|
||||
}
|
||||
`
|
||||
|
||||
// RevelRoutesTemplate template for app/conf/routes
|
||||
const RevelRoutesTemplate = `// GENERATED CODE - DO NOT EDIT
|
||||
package routes
|
||||
|
||||
import "github.com/revel/revel"
|
||||
|
||||
{{range $i, $c := .Controllers}}
|
||||
type t{{.StructName}} struct {}
|
||||
var {{.StructName}} t{{.StructName}}
|
||||
|
||||
{{range .MethodSpecs}}
|
||||
func (_ t{{$c.StructName}}) {{.Name}}({{range .Args}}
|
||||
{{.Name}} {{if .ImportPath}}interface{}{{else}}{{.TypeExpr.TypeName ""}}{{end}},{{end}}
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
{{range .Args}}
|
||||
revel.Unbind(args, "{{.Name}}", {{.Name}}){{end}}
|
||||
return revel.MainRouter.Reverse("{{$c.StructName}}.{{.Name}}", args).URL
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
@@ -0,0 +1,271 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package harness for a Revel Framework.
|
||||
//
|
||||
// It has a following responsibilities:
|
||||
// 1. Parse the user program, generating a main.go file that registers
|
||||
// controller classes and starts the user's server.
|
||||
// 2. Build and run the user program. Show compile errors.
|
||||
// 3. Monitor the user source and re-build / restart the program when necessary.
|
||||
//
|
||||
// Source files are generated in the app/tmp directory.
|
||||
package harness
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var (
|
||||
watcher *revel.Watcher
|
||||
doNotWatch = []string{"tmp", "views", "routes"}
|
||||
|
||||
lastRequestHadError int32
|
||||
)
|
||||
|
||||
// Harness reverse proxies requests to the application server.
|
||||
// It builds / runs / rebuilds / restarts the server when code is changed.
|
||||
type Harness struct {
|
||||
app *App
|
||||
serverHost string
|
||||
port int
|
||||
proxy *httputil.ReverseProxy
|
||||
}
|
||||
|
||||
func renderError(w http.ResponseWriter, r *http.Request, err error) {
|
||||
req, resp := revel.NewRequest(r), revel.NewResponse(w)
|
||||
c := revel.NewController(req, resp)
|
||||
c.RenderError(err).Apply(req, resp)
|
||||
}
|
||||
|
||||
// ServeHTTP handles all requests.
|
||||
// It checks for changes to app, rebuilds if necessary, and forwards the request.
|
||||
func (h *Harness) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Don't rebuild the app for favicon requests.
|
||||
if lastRequestHadError > 0 && r.URL.Path == "/favicon.ico" {
|
||||
return
|
||||
}
|
||||
|
||||
// Flush any change events and rebuild app if necessary.
|
||||
// Render an error page if the rebuild / restart failed.
|
||||
err := watcher.Notify()
|
||||
if err != nil {
|
||||
atomic.CompareAndSwapInt32(&lastRequestHadError, 0, 1)
|
||||
renderError(w, r, err)
|
||||
return
|
||||
}
|
||||
atomic.CompareAndSwapInt32(&lastRequestHadError, 1, 0)
|
||||
|
||||
// Reverse proxy the request.
|
||||
// (Need special code for websockets, courtesy of bradfitz)
|
||||
if strings.EqualFold(r.Header.Get("Upgrade"), "websocket") {
|
||||
proxyWebsocket(w, r, h.serverHost)
|
||||
} else {
|
||||
h.proxy.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// NewHarness method returns a reverse proxy that forwards requests
|
||||
// to the given port.
|
||||
func NewHarness() *Harness {
|
||||
// Get a template loader to render errors.
|
||||
// Prefer the app's views/errors directory, and fall back to the stock error pages.
|
||||
revel.MainTemplateLoader = revel.NewTemplateLoader(
|
||||
[]string{filepath.Join(revel.RevelPath, "templates")})
|
||||
if err := revel.MainTemplateLoader.Refresh(); err != nil {
|
||||
revel.ERROR.Println(err)
|
||||
}
|
||||
// fmt.Println("-----revel.RevelPath", revel.RevelPath)
|
||||
|
||||
addr := revel.HTTPAddr
|
||||
port := revel.Config.IntDefault("harness.port", 0)
|
||||
scheme := "http"
|
||||
if revel.HTTPSsl {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
// If the server is running on the wildcard address, use "localhost"
|
||||
if addr == "" {
|
||||
addr = "localhost"
|
||||
}
|
||||
|
||||
if port == 0 {
|
||||
port = getFreePort()
|
||||
}
|
||||
|
||||
serverURL, _ := url.ParseRequestURI(fmt.Sprintf(scheme+"://%s:%d", addr, port))
|
||||
|
||||
harness := &Harness{
|
||||
port: port,
|
||||
serverHost: serverURL.String()[len(scheme+"://"):],
|
||||
proxy: httputil.NewSingleHostReverseProxy(serverURL),
|
||||
}
|
||||
|
||||
if revel.HTTPSsl {
|
||||
harness.proxy.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
return harness
|
||||
}
|
||||
|
||||
// Refresh method rebuilds the Revel application and run it on the given port.
|
||||
func (h *Harness) Refresh() (err *revel.Error) {
|
||||
if h.app != nil {
|
||||
h.app.Kill()
|
||||
}
|
||||
|
||||
revel.TRACE.Println("Rebuild")
|
||||
h.app, err = Build()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h.app.Port = h.port
|
||||
if err2 := h.app.Cmd().Start(); err2 != nil {
|
||||
return &revel.Error{
|
||||
Title: "App failed to start up",
|
||||
Description: err2.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// WatchDir method returns false to file matches with doNotWatch
|
||||
// otheriwse true
|
||||
func (h *Harness) WatchDir(info os.FileInfo) bool {
|
||||
return !revel.ContainsString(doNotWatch, info.Name())
|
||||
}
|
||||
|
||||
// WatchFile method returns true given filename HasSuffix of ".go"
|
||||
// otheriwse false
|
||||
func (h *Harness) WatchFile(filename string) bool {
|
||||
return strings.HasSuffix(filename, ".go")
|
||||
}
|
||||
|
||||
// Run the harness, which listens for requests and proxies them to the app
|
||||
// server, which it runs and rebuilds as necessary.
|
||||
func (h *Harness) Run() {
|
||||
var paths []string
|
||||
if revel.Config.BoolDefault("watch.gopath", false) {
|
||||
gopaths := filepath.SplitList(build.Default.GOPATH)
|
||||
paths = append(paths, gopaths...)
|
||||
}
|
||||
paths = append(paths, revel.CodePaths...)
|
||||
watcher = revel.NewWatcher()
|
||||
watcher.Listen(h, paths...)
|
||||
|
||||
go func() {
|
||||
addr := fmt.Sprintf("%s:%d", revel.HTTPAddr, revel.HTTPPort)
|
||||
revel.INFO.Printf("Listening on %s", addr)
|
||||
|
||||
var err error
|
||||
if revel.HTTPSsl {
|
||||
err = http.ListenAndServeTLS(
|
||||
addr,
|
||||
revel.HTTPSslCert,
|
||||
revel.HTTPSslKey,
|
||||
h)
|
||||
} else {
|
||||
err = http.ListenAndServe(addr, h)
|
||||
}
|
||||
if err != nil {
|
||||
revel.ERROR.Fatalln("Failed to start reverse proxy:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Kill the app on signal.
|
||||
ch := make(chan os.Signal)
|
||||
signal.Notify(ch, os.Interrupt, os.Kill)
|
||||
<-ch
|
||||
if h.app != nil {
|
||||
h.app.Kill()
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Find an unused port
|
||||
func getFreePort() (port int) {
|
||||
conn, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
revel.ERROR.Fatal(err)
|
||||
}
|
||||
|
||||
port = conn.Addr().(*net.TCPAddr).Port
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
revel.ERROR.Fatal(err)
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
// proxyWebsocket copies data between websocket client and server until one side
|
||||
// closes the connection. (ReverseProxy doesn't work with websocket requests.)
|
||||
func proxyWebsocket(w http.ResponseWriter, r *http.Request, host string) {
|
||||
var (
|
||||
d net.Conn
|
||||
err error
|
||||
)
|
||||
if revel.HTTPSsl {
|
||||
// since this proxy isn't used in production,
|
||||
// it's OK to set InsecureSkipVerify to true
|
||||
// no need to add another configuration option.
|
||||
d, err = tls.Dial("tcp", host, &tls.Config{InsecureSkipVerify: true})
|
||||
} else {
|
||||
d, err = net.Dial("tcp", host)
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, "Error contacting backend server.", 500)
|
||||
revel.ERROR.Printf("Error dialing websocket backend %s: %v", host, err)
|
||||
return
|
||||
}
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "Not a hijacker?", 500)
|
||||
return
|
||||
}
|
||||
nc, _, err := hj.Hijack()
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Hijack error: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err = nc.Close(); err != nil {
|
||||
revel.ERROR.Println(err)
|
||||
}
|
||||
if err = d.Close(); err != nil {
|
||||
revel.ERROR.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = r.Write(d)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Error copying request to target: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
errc := make(chan error, 2)
|
||||
cp := func(dst io.Writer, src io.Reader) {
|
||||
_, err := io.Copy(dst, src)
|
||||
errc <- err
|
||||
}
|
||||
go cp(d, nc)
|
||||
go cp(nc, d)
|
||||
<-errc
|
||||
}
|
||||
@@ -0,0 +1,804 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package harness
|
||||
|
||||
// This file handles the app code introspection.
|
||||
// It catalogs the controllers, their methods, and their arguments.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/revel/revel"
|
||||
// "fmt"
|
||||
)
|
||||
|
||||
// life
|
||||
var MRevelImportPath = "github.com/revel/revel"
|
||||
|
||||
// SourceInfo is the top-level struct containing all extracted information
|
||||
// about the app source code, used to generate main.go.
|
||||
type SourceInfo struct {
|
||||
// StructSpecs lists type info for all structs found under the code paths.
|
||||
// They may be queried to determine which ones (transitively) embed certain types.
|
||||
StructSpecs []*TypeInfo
|
||||
// ValidationKeys provides a two-level lookup. The keys are:
|
||||
// 1. The fully-qualified function name,
|
||||
// e.g. "github.com/revel/examples/chat/app/controllers.(*Application).Action"
|
||||
// 2. Within that func's file, the line number of the (overall) expression statement.
|
||||
// e.g. the line returned from runtime.Caller()
|
||||
// The result of the lookup the name of variable being validated.
|
||||
ValidationKeys map[string]map[int]string
|
||||
// A list of import paths.
|
||||
// Revel notices files with an init() function and imports that package.
|
||||
InitImportPaths []string
|
||||
|
||||
// controllerSpecs lists type info for all structs found under
|
||||
// app/controllers/... that embed (directly or indirectly) revel.Controller
|
||||
controllerSpecs []*TypeInfo
|
||||
// testSuites list the types that constitute the set of application tests.
|
||||
testSuites []*TypeInfo
|
||||
}
|
||||
|
||||
// TypeInfo summarizes information about a struct type in the app source code.
|
||||
type TypeInfo struct {
|
||||
StructName string // e.g. "Application"
|
||||
ImportPath string // e.g. "github.com/revel/examples/chat/app/controllers"
|
||||
PackageName string // e.g. "controllers"
|
||||
MethodSpecs []*MethodSpec
|
||||
|
||||
// Used internally to identify controllers that indirectly embed *revel.Controller.
|
||||
embeddedTypes []*embeddedTypeName
|
||||
}
|
||||
|
||||
// methodCall describes a call to c.Render(..)
|
||||
// It documents the argument names used, in order to propagate them to RenderArgs.
|
||||
type methodCall struct {
|
||||
Path string // e.g. "myapp/app/controllers.(*Application).Action"
|
||||
Line int
|
||||
Names []string
|
||||
}
|
||||
|
||||
// MethodSpec holds the information of one Method
|
||||
type MethodSpec struct {
|
||||
Name string // Name of the method, e.g. "Index"
|
||||
Args []*MethodArg // Argument descriptors
|
||||
RenderCalls []*methodCall // Descriptions of Render() invocations from this Method.
|
||||
}
|
||||
|
||||
// MethodArg holds the information of one argument
|
||||
type MethodArg struct {
|
||||
Name string // Name of the argument.
|
||||
TypeExpr TypeExpr // The name of the type, e.g. "int", "*pkg.UserType"
|
||||
ImportPath string // If the arg is of an imported type, this is the import path.
|
||||
}
|
||||
|
||||
type embeddedTypeName struct {
|
||||
ImportPath, StructName string
|
||||
}
|
||||
|
||||
// Maps a controller simple name (e.g. "Login") to the methods for which it is a
|
||||
// receiver.
|
||||
type methodMap map[string][]*MethodSpec
|
||||
|
||||
// ProcessSource parses the app controllers directory and
|
||||
// returns a list of the controller types found.
|
||||
// Otherwise CompileError if the parsing fails.
|
||||
func ProcessSource(roots []string) (*SourceInfo, *revel.Error) {
|
||||
var (
|
||||
srcInfo *SourceInfo
|
||||
compileError *revel.Error
|
||||
)
|
||||
|
||||
// fmt.Println("****roots", roots)
|
||||
|
||||
for _, root := range roots {
|
||||
rootImportPath := importPathFromPath(root)
|
||||
if rootImportPath == "" {
|
||||
revel.WARN.Println("Skipping code path", root)
|
||||
continue
|
||||
}
|
||||
|
||||
// Start walking the directory tree.
|
||||
_ = revel.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
log.Println("Error scanning app source:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !info.IsDir() || info.Name() == "tmp" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the import path of the package.
|
||||
pkgImportPath := rootImportPath
|
||||
if root != path {
|
||||
pkgImportPath = rootImportPath + "/" + filepath.ToSlash(path[len(root)+1:])
|
||||
}
|
||||
|
||||
// Parse files within the path.
|
||||
var pkgs map[string]*ast.Package
|
||||
fset := token.NewFileSet()
|
||||
pkgs, err = parser.ParseDir(fset, path, func(f os.FileInfo) bool {
|
||||
return !f.IsDir() && !strings.HasPrefix(f.Name(), ".") && strings.HasSuffix(f.Name(), ".go")
|
||||
}, 0)
|
||||
if err != nil {
|
||||
if errList, ok := err.(scanner.ErrorList); ok {
|
||||
var pos = errList[0].Pos
|
||||
compileError = &revel.Error{
|
||||
SourceType: ".go source",
|
||||
Title: "Go Compilation Error",
|
||||
Path: pos.Filename,
|
||||
Description: errList[0].Msg,
|
||||
Line: pos.Line,
|
||||
Column: pos.Column,
|
||||
SourceLines: revel.MustReadLines(pos.Filename),
|
||||
}
|
||||
|
||||
errorLink := revel.Config.StringDefault("error.link", "")
|
||||
|
||||
if errorLink != "" {
|
||||
compileError.SetLink(errorLink)
|
||||
}
|
||||
|
||||
return compileError
|
||||
}
|
||||
|
||||
// This is exception, err alredy checked above. Here just a print
|
||||
ast.Print(nil, err)
|
||||
log.Fatalf("Failed to parse dir: %s", err)
|
||||
}
|
||||
|
||||
// Skip "main" packages.
|
||||
delete(pkgs, "main")
|
||||
|
||||
// If there is no code in this directory, skip it.
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// There should be only one package in this directory.
|
||||
if len(pkgs) > 1 {
|
||||
log.Println("Most unexpected! Multiple packages in a single directory:", pkgs)
|
||||
}
|
||||
|
||||
var pkg *ast.Package
|
||||
for _, v := range pkgs {
|
||||
pkg = v
|
||||
}
|
||||
|
||||
srcInfo = appendSourceInfo(srcInfo, processPackage(fset, pkgImportPath, path, pkg))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return srcInfo, compileError
|
||||
}
|
||||
|
||||
func appendSourceInfo(srcInfo1, srcInfo2 *SourceInfo) *SourceInfo {
|
||||
if srcInfo1 == nil {
|
||||
return srcInfo2
|
||||
}
|
||||
|
||||
srcInfo1.StructSpecs = append(srcInfo1.StructSpecs, srcInfo2.StructSpecs...)
|
||||
srcInfo1.InitImportPaths = append(srcInfo1.InitImportPaths, srcInfo2.InitImportPaths...)
|
||||
for k, v := range srcInfo2.ValidationKeys {
|
||||
if _, ok := srcInfo1.ValidationKeys[k]; ok {
|
||||
log.Println("Key conflict when scanning validation calls:", k)
|
||||
continue
|
||||
}
|
||||
srcInfo1.ValidationKeys[k] = v
|
||||
}
|
||||
return srcInfo1
|
||||
}
|
||||
|
||||
func processPackage(fset *token.FileSet, pkgImportPath, pkgPath string, pkg *ast.Package) *SourceInfo {
|
||||
var (
|
||||
structSpecs []*TypeInfo
|
||||
initImportPaths []string
|
||||
|
||||
methodSpecs = make(methodMap)
|
||||
validationKeys = make(map[string]map[int]string)
|
||||
scanControllers = strings.HasSuffix(pkgImportPath, "/controllers") ||
|
||||
strings.Contains(pkgImportPath, "/controllers/")
|
||||
scanTests = strings.HasSuffix(pkgImportPath, "/tests") ||
|
||||
strings.Contains(pkgImportPath, "/tests/")
|
||||
)
|
||||
|
||||
// For each source file in the package...
|
||||
for _, file := range pkg.Files {
|
||||
|
||||
// Imports maps the package key to the full import path.
|
||||
// e.g. import "sample/app/models" => "models": "sample/app/models"
|
||||
imports := map[string]string{}
|
||||
|
||||
// For each declaration in the source file...
|
||||
for _, decl := range file.Decls {
|
||||
addImports(imports, decl, pkgPath)
|
||||
|
||||
if scanControllers {
|
||||
// Match and add both structs and methods
|
||||
structSpecs = appendStruct(structSpecs, pkgImportPath, pkg, decl, imports, fset)
|
||||
appendAction(fset, methodSpecs, decl, pkgImportPath, pkg.Name, imports)
|
||||
} else if scanTests {
|
||||
structSpecs = appendStruct(structSpecs, pkgImportPath, pkg, decl, imports, fset)
|
||||
}
|
||||
|
||||
// If this is a func...
|
||||
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
||||
// Scan it for validation calls
|
||||
lineKeys := getValidationKeys(fset, funcDecl, imports)
|
||||
if len(lineKeys) > 0 {
|
||||
validationKeys[pkgImportPath+"."+getFuncName(funcDecl)] = lineKeys
|
||||
}
|
||||
|
||||
// Check if it's an init function.
|
||||
if funcDecl.Name.Name == "init" {
|
||||
initImportPaths = []string{pkgImportPath}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the method specs to the struct specs.
|
||||
for _, spec := range structSpecs {
|
||||
spec.MethodSpecs = methodSpecs[spec.StructName]
|
||||
}
|
||||
|
||||
return &SourceInfo{
|
||||
StructSpecs: structSpecs,
|
||||
ValidationKeys: validationKeys,
|
||||
InitImportPaths: initImportPaths,
|
||||
}
|
||||
}
|
||||
|
||||
// getFuncName returns a name for this func or method declaration.
|
||||
// e.g. "(*Application).SayHello" for a method, "SayHello" for a func.
|
||||
func getFuncName(funcDecl *ast.FuncDecl) string {
|
||||
prefix := ""
|
||||
if funcDecl.Recv != nil {
|
||||
recvType := funcDecl.Recv.List[0].Type
|
||||
if recvStarType, ok := recvType.(*ast.StarExpr); ok {
|
||||
prefix = "(*" + recvStarType.X.(*ast.Ident).Name + ")"
|
||||
} else {
|
||||
prefix = recvType.(*ast.Ident).Name
|
||||
}
|
||||
prefix += "."
|
||||
}
|
||||
return prefix + funcDecl.Name.Name
|
||||
}
|
||||
|
||||
func addImports(imports map[string]string, decl ast.Decl, srcDir string) {
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if genDecl.Tok != token.IMPORT {
|
||||
return
|
||||
}
|
||||
|
||||
for _, spec := range genDecl.Specs {
|
||||
importSpec := spec.(*ast.ImportSpec)
|
||||
var pkgAlias string
|
||||
if importSpec.Name != nil {
|
||||
pkgAlias = importSpec.Name.Name
|
||||
if pkgAlias == "_" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\""
|
||||
fullPath := quotedPath[1 : len(quotedPath)-1] // Remove the quotes
|
||||
|
||||
// If the package was not aliased (common case), we have to import it
|
||||
// to see what the package name is.
|
||||
// TODO: Can improve performance here a lot:
|
||||
// 1. Do not import everything over and over again. Keep a cache.
|
||||
// 2. Exempt the standard library; their directories always match the package name.
|
||||
// 3. Can use build.FindOnly and then use parser.ParseDir with mode PackageClauseOnly
|
||||
if pkgAlias == "" {
|
||||
pkg, err := build.Import(fullPath, srcDir, 0)
|
||||
if err != nil {
|
||||
// We expect this to happen for apps using reverse routing (since we
|
||||
// have not yet generated the routes). Don't log that.
|
||||
if !strings.HasSuffix(fullPath, "/app/routes") {
|
||||
revel.TRACE.Println("Could not find import:", fullPath)
|
||||
}
|
||||
continue
|
||||
}
|
||||
pkgAlias = pkg.Name
|
||||
}
|
||||
|
||||
imports[pkgAlias] = fullPath
|
||||
}
|
||||
}
|
||||
|
||||
// If this Decl is a struct type definition, it is summarized and added to specs.
|
||||
// Else, specs is returned unchanged.
|
||||
func appendStruct(specs []*TypeInfo, pkgImportPath string, pkg *ast.Package, decl ast.Decl, imports map[string]string, fset *token.FileSet) []*TypeInfo {
|
||||
// Filter out non-Struct type declarations.
|
||||
spec, found := getStructTypeDecl(decl, fset)
|
||||
if !found {
|
||||
return specs
|
||||
}
|
||||
structType := spec.Type.(*ast.StructType)
|
||||
|
||||
// At this point we know it's a type declaration for a struct.
|
||||
// Fill in the rest of the info by diving into the fields.
|
||||
// Add it provisionally to the Controller list -- it's later filtered using field info.
|
||||
controllerSpec := &TypeInfo{
|
||||
StructName: spec.Name.Name,
|
||||
ImportPath: pkgImportPath,
|
||||
PackageName: pkg.Name,
|
||||
}
|
||||
|
||||
for _, field := range structType.Fields.List {
|
||||
// If field.Names is set, it's not an embedded type.
|
||||
if field.Names != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// A direct "sub-type" has an ast.Field as either:
|
||||
// Ident { "AppController" }
|
||||
// SelectorExpr { "rev", "Controller" }
|
||||
// Additionally, that can be wrapped by StarExprs.
|
||||
fieldType := field.Type
|
||||
pkgName, typeName := func() (string, string) {
|
||||
// Drill through any StarExprs.
|
||||
for {
|
||||
if starExpr, ok := fieldType.(*ast.StarExpr); ok {
|
||||
fieldType = starExpr.X
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// If the embedded type is in the same package, it's an Ident.
|
||||
if ident, ok := fieldType.(*ast.Ident); ok {
|
||||
return "", ident.Name
|
||||
}
|
||||
|
||||
if selectorExpr, ok := fieldType.(*ast.SelectorExpr); ok {
|
||||
if pkgIdent, ok := selectorExpr.X.(*ast.Ident); ok {
|
||||
return pkgIdent.Name, selectorExpr.Sel.Name
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}()
|
||||
|
||||
// If a typename wasn't found, skip it.
|
||||
if typeName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find the import path for this type.
|
||||
// If it was referenced without a package name, use the current package import path.
|
||||
// Else, look up the package's import path by name.
|
||||
var importPath string
|
||||
if pkgName == "" {
|
||||
importPath = pkgImportPath
|
||||
} else {
|
||||
var ok bool
|
||||
if importPath, ok = imports[pkgName]; !ok {
|
||||
log.Print("Failed to find import path for ", pkgName, ".", typeName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
controllerSpec.embeddedTypes = append(controllerSpec.embeddedTypes, &embeddedTypeName{
|
||||
ImportPath: importPath,
|
||||
StructName: typeName,
|
||||
})
|
||||
}
|
||||
|
||||
return append(specs, controllerSpec)
|
||||
}
|
||||
|
||||
// If decl is a Method declaration, it is summarized and added to the array
|
||||
// underneath its receiver type.
|
||||
// e.g. "Login" => {MethodSpec, MethodSpec, ..}
|
||||
func appendAction(fset *token.FileSet, mm methodMap, decl ast.Decl, pkgImportPath, pkgName string, imports map[string]string) {
|
||||
// Func declaration?
|
||||
funcDecl, ok := decl.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Have a receiver?
|
||||
if funcDecl.Recv == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Is it public?
|
||||
if !funcDecl.Name.IsExported() {
|
||||
return
|
||||
}
|
||||
|
||||
// Does it return a Result?
|
||||
if funcDecl.Type.Results == nil || len(funcDecl.Type.Results.List) != 1 {
|
||||
return
|
||||
}
|
||||
selExpr, ok := funcDecl.Type.Results.List[0].Type.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if selExpr.Sel.Name != "Result" {
|
||||
return
|
||||
}
|
||||
if pkgIdent, ok := selExpr.X.(*ast.Ident); !ok || imports[pkgIdent.Name] != MRevelImportPath {
|
||||
return
|
||||
}
|
||||
|
||||
method := &MethodSpec{
|
||||
Name: funcDecl.Name.Name,
|
||||
}
|
||||
|
||||
// Add a description of the arguments to the method.
|
||||
for _, field := range funcDecl.Type.Params.List {
|
||||
for _, name := range field.Names {
|
||||
var importPath string
|
||||
typeExpr := NewTypeExpr(pkgName, field.Type)
|
||||
if !typeExpr.Valid {
|
||||
log.Printf("Didn't understand argument '%s' of action %s. Ignoring.\n", name, getFuncName(funcDecl))
|
||||
return // We didn't understand one of the args. Ignore this action.
|
||||
}
|
||||
if typeExpr.PkgName != "" {
|
||||
var ok bool
|
||||
if importPath, ok = imports[typeExpr.PkgName]; !ok {
|
||||
log.Println("Failed to find import for arg of type:", typeExpr.TypeName(""))
|
||||
}
|
||||
}
|
||||
method.Args = append(method.Args, &MethodArg{
|
||||
Name: name.Name,
|
||||
TypeExpr: typeExpr,
|
||||
ImportPath: importPath,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Add a description of the calls to Render from the method.
|
||||
// Inspect every node (e.g. always return true).
|
||||
method.RenderCalls = []*methodCall{}
|
||||
ast.Inspect(funcDecl.Body, func(node ast.Node) bool {
|
||||
// Is it a function call?
|
||||
callExpr, ok := node.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// Is it calling (*Controller).Render?
|
||||
selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// The type of the receiver is not easily available, so just store every
|
||||
// call to any method called Render.
|
||||
if selExpr.Sel.Name != "Render" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Add this call's args to the renderArgs.
|
||||
pos := fset.Position(callExpr.Rparen)
|
||||
methodCall := &methodCall{
|
||||
Line: pos.Line,
|
||||
Names: []string{},
|
||||
}
|
||||
for _, arg := range callExpr.Args {
|
||||
argIdent, ok := arg.(*ast.Ident)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
methodCall.Names = append(methodCall.Names, argIdent.Name)
|
||||
}
|
||||
method.RenderCalls = append(method.RenderCalls, methodCall)
|
||||
return true
|
||||
})
|
||||
|
||||
var recvTypeName string
|
||||
var recvType = funcDecl.Recv.List[0].Type
|
||||
if recvStarType, ok := recvType.(*ast.StarExpr); ok {
|
||||
recvTypeName = recvStarType.X.(*ast.Ident).Name
|
||||
} else {
|
||||
recvTypeName = recvType.(*ast.Ident).Name
|
||||
}
|
||||
|
||||
mm[recvTypeName] = append(mm[recvTypeName], method)
|
||||
}
|
||||
|
||||
// Scan app source code for calls to X.Y(), where X is of type *Validation.
|
||||
//
|
||||
// Recognize these scenarios:
|
||||
// - "Y" = "Validation" and is a member of the receiver.
|
||||
// (The common case for inline validation)
|
||||
// - "X" is passed in to the func as a parameter.
|
||||
// (For structs implementing Validated)
|
||||
//
|
||||
// The line number to which a validation call is attributed is that of the
|
||||
// surrounding ExprStmt. This is so that it matches what runtime.Callers()
|
||||
// reports.
|
||||
//
|
||||
// The end result is that we can set the default validation key for each call to
|
||||
// be the same as the local variable.
|
||||
func getValidationKeys(fset *token.FileSet, funcDecl *ast.FuncDecl, imports map[string]string) map[int]string {
|
||||
var (
|
||||
lineKeys = make(map[int]string)
|
||||
|
||||
// Check the func parameters and the receiver's members for the *revel.Validation type.
|
||||
validationParam = getValidationParameter(funcDecl, imports)
|
||||
)
|
||||
|
||||
ast.Inspect(funcDecl.Body, func(node ast.Node) bool {
|
||||
// e.g. c.Validation.Required(arg) or v.Required(arg)
|
||||
callExpr, ok := node.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// e.g. c.Validation.Required or v.Required
|
||||
funcSelector, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
switch x := funcSelector.X.(type) {
|
||||
case *ast.SelectorExpr: // e.g. c.Validation
|
||||
if x.Sel.Name != "Validation" {
|
||||
return true
|
||||
}
|
||||
|
||||
case *ast.Ident: // e.g. v
|
||||
if validationParam == nil || x.Obj != validationParam {
|
||||
return true
|
||||
}
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
if len(callExpr.Args) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Given the validation expression, extract the key.
|
||||
key := callExpr.Args[0]
|
||||
switch expr := key.(type) {
|
||||
case *ast.BinaryExpr:
|
||||
// If the argument is a binary expression, take the first expression.
|
||||
// (e.g. c.Validation.Required(myName != ""))
|
||||
key = expr.X
|
||||
case *ast.UnaryExpr:
|
||||
// If the argument is a unary expression, drill in.
|
||||
// (e.g. c.Validation.Required(!myBool)
|
||||
key = expr.X
|
||||
case *ast.BasicLit:
|
||||
// If it's a literal, skip it.
|
||||
return true
|
||||
}
|
||||
|
||||
if typeExpr := NewTypeExpr("", key); typeExpr.Valid {
|
||||
lineKeys[fset.Position(callExpr.End()).Line] = typeExpr.TypeName("")
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return lineKeys
|
||||
}
|
||||
|
||||
// Check to see if there is a *revel.Validation as an argument.
|
||||
func getValidationParameter(funcDecl *ast.FuncDecl, imports map[string]string) *ast.Object {
|
||||
for _, field := range funcDecl.Type.Params.List {
|
||||
starExpr, ok := field.Type.(*ast.StarExpr) // e.g. *revel.Validation
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
selExpr, ok := starExpr.X.(*ast.SelectorExpr) // e.g. revel.Validation
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
xIdent, ok := selExpr.X.(*ast.Ident) // e.g. rev
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if selExpr.Sel.Name == "Validation" && imports[xIdent.Name] == MRevelImportPath {
|
||||
return field.Names[0].Obj
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TypeInfo) String() string {
|
||||
return s.ImportPath + "." + s.StructName
|
||||
}
|
||||
|
||||
func (s *embeddedTypeName) String() string {
|
||||
return s.ImportPath + "." + s.StructName
|
||||
}
|
||||
|
||||
// getStructTypeDecl checks if the given decl is a type declaration for a
|
||||
// struct. If so, the TypeSpec is returned.
|
||||
func getStructTypeDecl(decl ast.Decl, fset *token.FileSet) (spec *ast.TypeSpec, found bool) {
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if genDecl.Tok != token.TYPE {
|
||||
return
|
||||
}
|
||||
|
||||
if len(genDecl.Specs) == 0 {
|
||||
revel.WARN.Printf("Surprising: %s:%d Decl contains no specifications", fset.Position(decl.Pos()).Filename, fset.Position(decl.Pos()).Line)
|
||||
return
|
||||
}
|
||||
|
||||
spec = genDecl.Specs[0].(*ast.TypeSpec)
|
||||
_, found = spec.Type.(*ast.StructType)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TypesThatEmbed returns all types that (directly or indirectly) embed the
|
||||
// target type, which must be a fully qualified type name,
|
||||
// e.g. "github.com/revel/revel.Controller"
|
||||
func (s *SourceInfo) TypesThatEmbed(targetType string) (filtered []*TypeInfo) {
|
||||
// Do a search in the "embedded type graph", starting with the target type.
|
||||
var (
|
||||
nodeQueue = []string{targetType}
|
||||
processed []string
|
||||
)
|
||||
for len(nodeQueue) > 0 {
|
||||
controllerSimpleName := nodeQueue[0]
|
||||
nodeQueue = nodeQueue[1:]
|
||||
processed = append(processed, controllerSimpleName)
|
||||
|
||||
// Look through all known structs.
|
||||
for _, spec := range s.StructSpecs {
|
||||
// If this one has been processed or is already in nodeQueue, then skip it.
|
||||
if revel.ContainsString(processed, spec.String()) ||
|
||||
revel.ContainsString(nodeQueue, spec.String()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Look through the embedded types to see if the current type is among them.
|
||||
for _, embeddedType := range spec.embeddedTypes {
|
||||
|
||||
// If so, add this type's simple name to the nodeQueue, and its spec to
|
||||
// the filtered list.
|
||||
if controllerSimpleName == embeddedType.String() {
|
||||
nodeQueue = append(nodeQueue, spec.String())
|
||||
filtered = append(filtered, spec)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ControllerSpecs returns the all the contollers that embeds
|
||||
// `revel.Controller`
|
||||
func (s *SourceInfo) ControllerSpecs() []*TypeInfo {
|
||||
// fmt.Println("_____RevelImportPath", MRevelImportPath)
|
||||
if s.controllerSpecs == nil {
|
||||
// fmt.Println("_____RevelImportPath", MRevelImportPath)
|
||||
s.controllerSpecs = s.TypesThatEmbed(MRevelImportPath + ".Controller")
|
||||
}
|
||||
return s.controllerSpecs
|
||||
}
|
||||
|
||||
// TestSuites returns the all the Application tests that embeds
|
||||
// `testing.TestSuite`
|
||||
func (s *SourceInfo) TestSuites() []*TypeInfo {
|
||||
if s.testSuites == nil {
|
||||
s.testSuites = s.TypesThatEmbed(MRevelImportPath + "/testing.TestSuite")
|
||||
}
|
||||
return s.testSuites
|
||||
}
|
||||
|
||||
// TypeExpr provides a type name that may be rewritten to use a package name.
|
||||
type TypeExpr struct {
|
||||
Expr string // The unqualified type expression, e.g. "[]*MyType"
|
||||
PkgName string // The default package idenifier
|
||||
pkgIndex int // The index where the package identifier should be inserted.
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// TypeName returns the fully-qualified type name for this expression.
|
||||
// The caller may optionally specify a package name to override the default.
|
||||
func (e TypeExpr) TypeName(pkgOverride string) string {
|
||||
pkgName := revel.FirstNonEmpty(pkgOverride, e.PkgName)
|
||||
if pkgName == "" {
|
||||
return e.Expr
|
||||
}
|
||||
return e.Expr[:e.pkgIndex] + pkgName + "." + e.Expr[e.pkgIndex:]
|
||||
}
|
||||
|
||||
// NewTypeExpr returns the syntactic expression for referencing this type in Go.
|
||||
func NewTypeExpr(pkgName string, expr ast.Expr) TypeExpr {
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
if IsBuiltinType(t.Name) {
|
||||
pkgName = ""
|
||||
}
|
||||
return TypeExpr{t.Name, pkgName, 0, true}
|
||||
case *ast.SelectorExpr:
|
||||
e := NewTypeExpr(pkgName, t.X)
|
||||
return TypeExpr{t.Sel.Name, e.Expr, 0, e.Valid}
|
||||
case *ast.StarExpr:
|
||||
e := NewTypeExpr(pkgName, t.X)
|
||||
return TypeExpr{"*" + e.Expr, e.PkgName, e.pkgIndex + 1, e.Valid}
|
||||
case *ast.ArrayType:
|
||||
e := NewTypeExpr(pkgName, t.Elt)
|
||||
return TypeExpr{"[]" + e.Expr, e.PkgName, e.pkgIndex + 2, e.Valid}
|
||||
case *ast.Ellipsis:
|
||||
e := NewTypeExpr(pkgName, t.Elt)
|
||||
return TypeExpr{"[]" + e.Expr, e.PkgName, e.pkgIndex + 2, e.Valid}
|
||||
default:
|
||||
log.Println("Failed to generate name for field. Make sure the field name is valid.")
|
||||
}
|
||||
return TypeExpr{Valid: false}
|
||||
}
|
||||
|
||||
var builtInTypes = map[string]struct{}{
|
||||
"bool": {},
|
||||
"byte": {},
|
||||
"complex128": {},
|
||||
"complex64": {},
|
||||
"error": {},
|
||||
"float32": {},
|
||||
"float64": {},
|
||||
"int": {},
|
||||
"int16": {},
|
||||
"int32": {},
|
||||
"int64": {},
|
||||
"int8": {},
|
||||
"rune": {},
|
||||
"string": {},
|
||||
"uint": {},
|
||||
"uint16": {},
|
||||
"uint32": {},
|
||||
"uint64": {},
|
||||
"uint8": {},
|
||||
"uintptr": {},
|
||||
}
|
||||
|
||||
// IsBuiltinType checks the given type is built-in types of Go
|
||||
func IsBuiltinType(name string) bool {
|
||||
_, ok := builtInTypes[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func importPathFromPath(root string) string {
|
||||
vendoringPath := revel.BasePath + "/vendor/"
|
||||
if strings.HasPrefix(root, vendoringPath) {
|
||||
return filepath.ToSlash(root[len(vendoringPath):])
|
||||
}
|
||||
for _, gopath := range filepath.SplitList(build.Default.GOPATH) {
|
||||
srcPath := filepath.Join(gopath, "src")
|
||||
if strings.HasPrefix(root, srcPath) {
|
||||
return filepath.ToSlash(root[len(srcPath)+1:])
|
||||
}
|
||||
}
|
||||
|
||||
srcPath := filepath.Join(build.Default.GOROOT, "src", "pkg")
|
||||
if strings.HasPrefix(root, srcPath) {
|
||||
revel.WARN.Println("Code path should be in GOPATH, but is in GOROOT:", root)
|
||||
return filepath.ToSlash(root[len(srcPath)+1:])
|
||||
}
|
||||
|
||||
revel.ERROR.Println("Unexpected! Code path is not in GOPATH:", root)
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package harness
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
const validationKeysSource = `
|
||||
package test
|
||||
|
||||
func (c *Application) testFunc(a, b int, user models.User) revel.Result {
|
||||
// Line 5
|
||||
c.Validation.Required(a)
|
||||
c.Validation.Required(a).Message("Error message")
|
||||
c.Validation.Required(a).
|
||||
Message("Error message")
|
||||
|
||||
// Line 11
|
||||
c.Validation.Required(user.Name)
|
||||
c.Validation.Required(user.Name).Message("Error message")
|
||||
|
||||
// Line 15
|
||||
c.Validation.MinSize(b, 12)
|
||||
c.Validation.MinSize(b, 12).Message("Error message")
|
||||
c.Validation.MinSize(b,
|
||||
12)
|
||||
|
||||
// Line 21
|
||||
c.Validation.Required(b == 5)
|
||||
}
|
||||
|
||||
func (m Model) Validate(v *revel.Validation) {
|
||||
// Line 26
|
||||
v.Required(m.name)
|
||||
v.Required(m.name == "something").
|
||||
Message("Error Message")
|
||||
v.Required(!m.bool)
|
||||
}
|
||||
`
|
||||
|
||||
var expectedValidationKeys = []map[int]string{
|
||||
{
|
||||
6: "a",
|
||||
7: "a",
|
||||
8: "a",
|
||||
12: "user.Name",
|
||||
13: "user.Name",
|
||||
16: "b",
|
||||
17: "b",
|
||||
19: "b",
|
||||
22: "b",
|
||||
}, {
|
||||
27: "m.name",
|
||||
28: "m.name",
|
||||
30: "m.bool",
|
||||
},
|
||||
}
|
||||
|
||||
// This tests the recording of line number to validation key of the preceeding
|
||||
// example source.
|
||||
func TestGetValidationKeys(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
|
||||
file, err := parser.ParseFile(fset, "validationKeysSource", validationKeysSource, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(file.Decls) != 2 {
|
||||
t.Fatal("Expected 2 decl in the source, found", len(file.Decls))
|
||||
}
|
||||
|
||||
for i, decl := range file.Decls {
|
||||
lineKeys := getValidationKeys(fset, decl.(*ast.FuncDecl), map[string]string{"revel": revel.RevelImportPath})
|
||||
for k, v := range expectedValidationKeys[i] {
|
||||
if lineKeys[k] != v {
|
||||
t.Errorf("Not found - %d: %v - Actual Map: %v", k, v, lineKeys)
|
||||
}
|
||||
}
|
||||
|
||||
if len(lineKeys) != len(expectedValidationKeys[i]) {
|
||||
t.Error("Validation key map not the same size as expected:", lineKeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var TypeExprs = map[string]TypeExpr{
|
||||
"int": {"int", "", 0, true},
|
||||
"*int": {"*int", "", 1, true},
|
||||
"[]int": {"[]int", "", 2, true},
|
||||
"...int": {"[]int", "", 2, true},
|
||||
"[]*int": {"[]*int", "", 3, true},
|
||||
"...*int": {"[]*int", "", 3, true},
|
||||
"MyType": {"MyType", "pkg", 0, true},
|
||||
"*MyType": {"*MyType", "pkg", 1, true},
|
||||
"[]MyType": {"[]MyType", "pkg", 2, true},
|
||||
"...MyType": {"[]MyType", "pkg", 2, true},
|
||||
"[]*MyType": {"[]*MyType", "pkg", 3, true},
|
||||
"...*MyType": {"[]*MyType", "pkg", 3, true},
|
||||
}
|
||||
|
||||
func TestTypeExpr(t *testing.T) {
|
||||
for typeStr, expected := range TypeExprs {
|
||||
// Handle arrays and ... myself, since ParseExpr() does not.
|
||||
array := strings.HasPrefix(typeStr, "[]")
|
||||
if array {
|
||||
typeStr = typeStr[2:]
|
||||
}
|
||||
|
||||
ellipsis := strings.HasPrefix(typeStr, "...")
|
||||
if ellipsis {
|
||||
typeStr = typeStr[3:]
|
||||
}
|
||||
|
||||
expr, err := parser.ParseExpr(typeStr)
|
||||
if err != nil {
|
||||
t.Error("Failed to parse test expr:", typeStr)
|
||||
continue
|
||||
}
|
||||
|
||||
if array {
|
||||
expr = &ast.ArrayType{Lbrack: expr.Pos(), Len: nil, Elt: expr}
|
||||
}
|
||||
if ellipsis {
|
||||
expr = &ast.Ellipsis{Ellipsis: expr.Pos(), Elt: expr}
|
||||
}
|
||||
|
||||
actual := NewTypeExpr("pkg", expr)
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Error("Fail, expected", expected, ", was", actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBookingSource(t *testing.T) {
|
||||
revel.Init("prod", "github.com/revel/examples/booking", "")
|
||||
sourceInfo, err := ProcessSource([]string{revel.AppPath})
|
||||
if err != nil {
|
||||
t.Fatal("Failed to process booking source with error:", err)
|
||||
}
|
||||
|
||||
controllerPackage := "github.com/revel/examples/booking/app/controllers"
|
||||
expectedControllerSpecs := []*TypeInfo{
|
||||
{"GorpController", controllerPackage, "controllers", nil, nil},
|
||||
{"Application", controllerPackage, "controllers", nil, nil},
|
||||
{"Hotels", controllerPackage, "controllers", nil, nil},
|
||||
}
|
||||
if len(sourceInfo.ControllerSpecs()) != len(expectedControllerSpecs) {
|
||||
t.Errorf("Unexpected number of controllers found. Expected %d, Found %d",
|
||||
len(expectedControllerSpecs), len(sourceInfo.ControllerSpecs()))
|
||||
}
|
||||
|
||||
NEXT_TEST:
|
||||
for _, expected := range expectedControllerSpecs {
|
||||
for _, actual := range sourceInfo.ControllerSpecs() {
|
||||
if actual.StructName == expected.StructName {
|
||||
if actual.ImportPath != expected.ImportPath {
|
||||
t.Errorf("%s expected to have import path %s, actual %s",
|
||||
actual.StructName, expected.ImportPath, actual.ImportPath)
|
||||
}
|
||||
if actual.PackageName != expected.PackageName {
|
||||
t.Errorf("%s expected to have package name %s, actual %s",
|
||||
actual.StructName, expected.PackageName, actual.PackageName)
|
||||
}
|
||||
continue NEXT_TEST
|
||||
}
|
||||
}
|
||||
t.Errorf("Expected to find controller %s, but did not. Actuals: %s",
|
||||
expected.StructName, sourceInfo.ControllerSpecs())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProcessBookingSource(b *testing.B) {
|
||||
revel.Init("", "github.com/revel/examples/booking", "")
|
||||
revel.TRACE = log.New(ioutil.Discard, "", 0)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := ProcessSource(revel.CodePaths)
|
||||
if err != nil {
|
||||
b.Error("Unexpected error:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/leanote/leanote/cmd/harness"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var cmdBuild = &Command{
|
||||
UsageLine: "build [import path] [target path] [run mode]",
|
||||
Short: "build a Revel application (e.g. for deployment)",
|
||||
Long: `
|
||||
Build the Revel web application named by the given import path.
|
||||
This allows it to be deployed and run on a machine that lacks a Go installation.
|
||||
|
||||
The run mode is used to select which set of app.conf configuration should
|
||||
apply and may be used to determine logic in the application itself.
|
||||
|
||||
Run mode defaults to "dev".
|
||||
|
||||
WARNING: The target path will be completely deleted, if it already exists!
|
||||
|
||||
For example:
|
||||
|
||||
revel build github.com/revel/examples/chat /tmp/chat
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdBuild.Run = buildApp
|
||||
}
|
||||
|
||||
func buildApp(args []string) {
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintf(os.Stderr, "%s\n%s", cmdBuild.UsageLine, cmdBuild.Long)
|
||||
return
|
||||
}
|
||||
|
||||
appImportPath, destPath, mode := args[0], args[1], DefaultRunMode
|
||||
if len(args) >= 3 {
|
||||
mode = args[2]
|
||||
}
|
||||
|
||||
if !revel.Initialized {
|
||||
revel.Init(mode, appImportPath, "")
|
||||
}
|
||||
|
||||
// First, verify that it is either already empty or looks like a previous
|
||||
// build (to avoid clobbering anything)
|
||||
if exists(destPath) && !empty(destPath) && !exists(filepath.Join(destPath, "run.sh")) {
|
||||
errorf("Abort: %s exists and does not look like a build directory.", destPath)
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(destPath); err != nil && !os.IsNotExist(err) {
|
||||
revel.ERROR.Fatalln(err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(destPath, 0777); err != nil {
|
||||
revel.ERROR.Fatalln(err)
|
||||
}
|
||||
|
||||
app, reverr := harness.Build()
|
||||
panicOnError(reverr, "Failed to build")
|
||||
|
||||
// Included are:
|
||||
// - run scripts
|
||||
// - binary
|
||||
// - revel
|
||||
// - app
|
||||
|
||||
// Revel and the app are in a directory structure mirroring import path
|
||||
srcPath := filepath.Join(destPath, "src")
|
||||
destBinaryPath := filepath.Join(destPath, filepath.Base(app.BinaryPath))
|
||||
tmpRevelPath := filepath.Join(srcPath, filepath.FromSlash(revel.RevelImportPath))
|
||||
mustCopyFile(destBinaryPath, app.BinaryPath)
|
||||
mustChmod(destBinaryPath, 0755)
|
||||
_ = mustCopyDir(filepath.Join(tmpRevelPath, "conf"), filepath.Join(revel.RevelPath, "conf"), nil)
|
||||
_ = mustCopyDir(filepath.Join(tmpRevelPath, "templates"), filepath.Join(revel.RevelPath, "templates"), nil)
|
||||
_ = mustCopyDir(filepath.Join(srcPath, filepath.FromSlash(appImportPath)), revel.BasePath, nil)
|
||||
|
||||
// Find all the modules used and copy them over.
|
||||
config := revel.Config.Raw()
|
||||
modulePaths := make(map[string]string) // import path => filesystem path
|
||||
for _, section := range config.Sections() {
|
||||
options, _ := config.SectionOptions(section)
|
||||
for _, key := range options {
|
||||
if !strings.HasPrefix(key, "module.") {
|
||||
continue
|
||||
}
|
||||
moduleImportPath, _ := config.String(section, key)
|
||||
if moduleImportPath == "" {
|
||||
continue
|
||||
}
|
||||
modulePath, err := revel.ResolveImportPath(moduleImportPath)
|
||||
if err != nil {
|
||||
revel.ERROR.Fatalln("Failed to load module %s: %s", key[len("module."):], err)
|
||||
}
|
||||
modulePaths[moduleImportPath] = modulePath
|
||||
}
|
||||
}
|
||||
for importPath, fsPath := range modulePaths {
|
||||
_ = mustCopyDir(filepath.Join(srcPath, importPath), fsPath, nil)
|
||||
}
|
||||
|
||||
tmplData, runShPath := map[string]interface{}{
|
||||
"BinName": filepath.Base(app.BinaryPath),
|
||||
"ImportPath": appImportPath,
|
||||
"Mode": mode,
|
||||
}, filepath.Join(destPath, "run.sh")
|
||||
|
||||
mustRenderTemplate(
|
||||
runShPath,
|
||||
filepath.Join(revel.RevelPath, "..", "cmd", "revel", "package_run.sh.template"),
|
||||
tmplData)
|
||||
|
||||
mustChmod(runShPath, 0755)
|
||||
|
||||
mustRenderTemplate(
|
||||
filepath.Join(destPath, "run.bat"),
|
||||
filepath.Join(revel.RevelPath, "..", "cmd", "revel", "package_run.bat.template"),
|
||||
tmplData)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var cmdClean = &Command{
|
||||
UsageLine: "clean [import path]",
|
||||
Short: "clean a Revel application's temp files",
|
||||
Long: `
|
||||
Clean the Revel web application named by the given import path.
|
||||
|
||||
For example:
|
||||
|
||||
revel clean github.com/revel/examples/chat
|
||||
|
||||
It removes the app/tmp and app/routes directory.
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdClean.Run = cleanApp
|
||||
}
|
||||
|
||||
func cleanApp(args []string) {
|
||||
if len(args) == 0 {
|
||||
fmt.Fprintf(os.Stderr, cmdClean.Long)
|
||||
return
|
||||
}
|
||||
|
||||
appPkg, err := build.Import(args[0], "", build.FindOnly)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Abort: Failed to find import path:", err)
|
||||
return
|
||||
}
|
||||
|
||||
purgeDirs := []string{
|
||||
filepath.Join(appPkg.Dir, "app", "tmp"),
|
||||
filepath.Join(appPkg.Dir, "app", "routes"),
|
||||
}
|
||||
|
||||
for _, dir := range purgeDirs {
|
||||
fmt.Println("Removing:", dir)
|
||||
err = os.RemoveAll(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Abort:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var cmdNew = &Command{
|
||||
UsageLine: "new [path] [skeleton]",
|
||||
Short: "create a skeleton Revel application",
|
||||
Long: `
|
||||
New creates a few files to get a new Revel application running quickly.
|
||||
|
||||
It puts all of the files in the given import path, taking the final element in
|
||||
the path to be the app name.
|
||||
|
||||
Skeleton is an optional argument, provided as an import path
|
||||
|
||||
For example:
|
||||
|
||||
revel new import/path/helloworld
|
||||
|
||||
revel new import/path/helloworld import/path/skeleton
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdNew.Run = newApp
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
// go related paths
|
||||
gopath string
|
||||
gocmd string
|
||||
srcRoot string
|
||||
|
||||
// revel related paths
|
||||
revelPkg *build.Package
|
||||
revelCmdPkg *build.Package
|
||||
appPath string
|
||||
appName string
|
||||
basePath string
|
||||
importPath string
|
||||
skeletonPath string
|
||||
)
|
||||
|
||||
func newApp(args []string) {
|
||||
// check for proper args by count
|
||||
if len(args) == 0 {
|
||||
errorf("No import path given.\nRun 'revel help new' for usage.\n")
|
||||
}
|
||||
if len(args) > 2 {
|
||||
errorf("Too many arguments provided.\nRun 'revel help new' for usage.\n")
|
||||
}
|
||||
|
||||
revel.ERROR.SetFlags(log.LstdFlags)
|
||||
|
||||
// checking and setting go paths
|
||||
initGoPaths()
|
||||
|
||||
// checking and setting application
|
||||
setApplicationPath(args)
|
||||
|
||||
// checking and setting skeleton
|
||||
setSkeletonPath(args)
|
||||
|
||||
// copy files to new app directory
|
||||
copyNewAppFiles()
|
||||
|
||||
// goodbye world
|
||||
fmt.Fprintln(os.Stdout, "Your application is ready:\n ", appPath)
|
||||
fmt.Fprintln(os.Stdout, "\nYou can run it with:\n revel run", importPath)
|
||||
}
|
||||
|
||||
const alphaNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
func generateSecret() string {
|
||||
chars := make([]byte, 64)
|
||||
for i := 0; i < 64; i++ {
|
||||
chars[i] = alphaNumeric[rand.Intn(len(alphaNumeric))]
|
||||
}
|
||||
return string(chars)
|
||||
}
|
||||
|
||||
// lookup and set Go related variables
|
||||
func initGoPaths() {
|
||||
// lookup go path
|
||||
gopath = build.Default.GOPATH
|
||||
if gopath == "" {
|
||||
errorf("Abort: GOPATH environment variable is not set. " +
|
||||
"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
|
||||
}
|
||||
|
||||
// check for go executable
|
||||
var err error
|
||||
gocmd, err = exec.LookPath("go")
|
||||
if err != nil {
|
||||
errorf("Go executable not found in PATH.")
|
||||
}
|
||||
|
||||
// revel/revel#1004 choose go path relative to current working directory
|
||||
workingDir, _ := os.Getwd()
|
||||
goPathList := filepath.SplitList(gopath)
|
||||
for _, path := range goPathList {
|
||||
if strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
|
||||
srcRoot = path
|
||||
break
|
||||
}
|
||||
|
||||
path, _ = filepath.EvalSymlinks(path)
|
||||
if len(path) > 0 && strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
|
||||
srcRoot = path
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(srcRoot) == 0 {
|
||||
revel.ERROR.Fatalln("Abort: could not create a Revel application outside of GOPATH.")
|
||||
}
|
||||
|
||||
// set go src path
|
||||
srcRoot = filepath.Join(srcRoot, "src")
|
||||
}
|
||||
|
||||
func setApplicationPath(args []string) {
|
||||
var err error
|
||||
importPath = args[0]
|
||||
|
||||
// revel/revel#1014 validate relative path, we cannot use built-in functions
|
||||
// since Go import path is valid relative path too.
|
||||
// so check basic part of the path, which is "."
|
||||
if filepath.IsAbs(importPath) || strings.HasPrefix(importPath, ".") {
|
||||
errorf("Abort: '%s' looks like a directory. Please provide a Go import path instead.",
|
||||
importPath)
|
||||
}
|
||||
|
||||
_, err = build.Import(importPath, "", build.FindOnly)
|
||||
if err == nil {
|
||||
errorf("Abort: Import path %s already exists.\n", importPath)
|
||||
}
|
||||
|
||||
revelPkg, err = build.Import(revel.RevelImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
errorf("Abort: Could not find Revel source code: %s\n", err)
|
||||
}
|
||||
|
||||
appPath = filepath.Join(srcRoot, filepath.FromSlash(importPath))
|
||||
appName = filepath.Base(appPath)
|
||||
basePath = filepath.ToSlash(filepath.Dir(importPath))
|
||||
|
||||
if basePath == "." {
|
||||
// we need to remove the a single '.' when
|
||||
// the app is in the $GOROOT/src directory
|
||||
basePath = ""
|
||||
} else {
|
||||
// we need to append a '/' when the app is
|
||||
// is a subdirectory such as $GOROOT/src/path/to/revelapp
|
||||
basePath += "/"
|
||||
}
|
||||
}
|
||||
|
||||
func setSkeletonPath(args []string) {
|
||||
var err error
|
||||
if len(args) == 2 { // user specified
|
||||
skeletonName := args[1]
|
||||
_, err = build.Import(skeletonName, "", build.FindOnly)
|
||||
if err != nil {
|
||||
// Execute "go get <pkg>"
|
||||
getCmd := exec.Command(gocmd, "get", "-d", skeletonName)
|
||||
fmt.Println("Exec:", getCmd.Args)
|
||||
getOutput, err := getCmd.CombinedOutput()
|
||||
|
||||
// check getOutput for no buildible string
|
||||
bpos := bytes.Index(getOutput, []byte("no buildable Go source files in"))
|
||||
if err != nil && bpos == -1 {
|
||||
errorf("Abort: Could not find or 'go get' Skeleton source code: %s\n%s\n", getOutput, skeletonName)
|
||||
}
|
||||
}
|
||||
// use the
|
||||
skeletonPath = filepath.Join(srcRoot, skeletonName)
|
||||
|
||||
} else {
|
||||
// use the revel default
|
||||
revelCmdPkg, err = build.Import(RevelCmdImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
errorf("Abort: Could not find Revel Cmd source code: %s\n", err)
|
||||
}
|
||||
|
||||
skeletonPath = filepath.Join(revelCmdPkg.Dir, "revel", "skeleton")
|
||||
}
|
||||
}
|
||||
|
||||
func copyNewAppFiles() {
|
||||
var err error
|
||||
err = os.MkdirAll(appPath, 0777)
|
||||
panicOnError(err, "Failed to create directory "+appPath)
|
||||
|
||||
_ = mustCopyDir(appPath, skeletonPath, map[string]interface{}{
|
||||
// app.conf
|
||||
"AppName": appName,
|
||||
"BasePath": basePath,
|
||||
"Secret": generateSecret(),
|
||||
})
|
||||
|
||||
// Dotfiles are skipped by mustCopyDir, so we have to explicitly copy the .gitignore.
|
||||
gitignore := ".gitignore"
|
||||
mustCopyFile(filepath.Join(appPath, gitignore), filepath.Join(skeletonPath, gitignore))
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var cmdPackage = &Command{
|
||||
UsageLine: "package [import path] [run mode]",
|
||||
Short: "package a Revel application (e.g. for deployment)",
|
||||
Long: `
|
||||
Package the Revel web application named by the given import path.
|
||||
This allows it to be deployed and run on a machine that lacks a Go installation.
|
||||
|
||||
The run mode is used to select which set of app.conf configuration should
|
||||
apply and may be used to determine logic in the application itself.
|
||||
|
||||
Run mode defaults to "dev".
|
||||
|
||||
For example:
|
||||
|
||||
revel package github.com/revel/examples/chat
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdPackage.Run = packageApp
|
||||
}
|
||||
|
||||
func packageApp(args []string) {
|
||||
if len(args) == 0 {
|
||||
fmt.Fprint(os.Stderr, cmdPackage.Long)
|
||||
return
|
||||
}
|
||||
|
||||
// Determine the run mode.
|
||||
mode := DefaultRunMode
|
||||
if len(args) >= 2 {
|
||||
mode = args[1]
|
||||
}
|
||||
|
||||
appImportPath := args[0]
|
||||
revel.Init(mode, appImportPath, "")
|
||||
|
||||
// Remove the archive if it already exists.
|
||||
destFile := filepath.Base(revel.BasePath) + ".tar.gz"
|
||||
if err := os.Remove(destFile); err != nil && !os.IsNotExist(err) {
|
||||
revel.ERROR.Fatal(err)
|
||||
}
|
||||
|
||||
// Collect stuff in a temp directory.
|
||||
tmpDir, err := ioutil.TempDir("", filepath.Base(revel.BasePath))
|
||||
panicOnError(err, "Failed to get temp dir")
|
||||
|
||||
buildApp([]string{args[0], tmpDir, mode})
|
||||
|
||||
// Create the zip file.
|
||||
archiveName := mustTarGzDir(destFile, tmpDir)
|
||||
|
||||
fmt.Println("Your archive is ready:", archiveName)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
{{.BinName}} -importPath {{.ImportPath}} -srcPath %CD%\src -runMode {{.Mode}}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
|
||||
"$SCRIPTPATH/{{.BinName}}" -importPath {{.ImportPath}} -srcPath "$SCRIPTPATH/src" -runMode {{.Mode}}
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The command line tool for running Revel apps.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/agtorre/gocolorize"
|
||||
)
|
||||
|
||||
const (
|
||||
// RevelCmdImportPath Revel framework cmd tool import path
|
||||
RevelCmdImportPath = "github.com/revel/cmd"
|
||||
|
||||
// DefaultRunMode for revel's application
|
||||
DefaultRunMode = "dev"
|
||||
)
|
||||
|
||||
// Command structure cribbed from the genius organization of the "go" command.
|
||||
type Command struct {
|
||||
Run func(args []string)
|
||||
UsageLine, Short, Long string
|
||||
}
|
||||
|
||||
// Name returns command name from usage line
|
||||
func (cmd *Command) Name() string {
|
||||
name := cmd.UsageLine
|
||||
i := strings.Index(name, " ")
|
||||
if i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
var commands = []*Command{
|
||||
cmdNew,
|
||||
cmdRun,
|
||||
cmdBuild,
|
||||
cmdPackage,
|
||||
cmdClean,
|
||||
cmdTest,
|
||||
cmdVersion,
|
||||
}
|
||||
|
||||
func main() {
|
||||
if runtime.GOOS == "windows" {
|
||||
gocolorize.SetPlain(true)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, gocolorize.NewColor("blue").Paint(header))
|
||||
flag.Usage = func() { usage(1) }
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
|
||||
if len(args) < 1 || args[0] == "help" {
|
||||
if len(args) == 1 {
|
||||
usage(0)
|
||||
}
|
||||
if len(args) > 1 {
|
||||
for _, cmd := range commands {
|
||||
if cmd.Name() == args[1] {
|
||||
tmpl(os.Stdout, helpTemplate, cmd)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
usage(2)
|
||||
}
|
||||
|
||||
// Commands use panic to abort execution when something goes wrong.
|
||||
// Panics are logged at the point of error. Ignore those.
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
if _, ok := err.(LoggedError); !ok {
|
||||
// This panic was not expected / logged.
|
||||
panic(err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, cmd := range commands {
|
||||
if cmd.Name() == args[0] {
|
||||
cmd.Run(args[1:])
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
errorf("unknown command %q\nRun 'revel help' for usage.\n", args[0])
|
||||
}
|
||||
|
||||
func errorf(format string, args ...interface{}) {
|
||||
// Ensure the user's command prompt starts on the next line.
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
panic(LoggedError{}) // Panic instead of os.Exit so that deferred will run.
|
||||
}
|
||||
|
||||
const header = `~
|
||||
~ revel! http://revel.github.io
|
||||
~
|
||||
`
|
||||
|
||||
const usageTemplate = `usage: revel command [arguments]
|
||||
|
||||
The commands are:
|
||||
{{range .}}
|
||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}
|
||||
|
||||
Use "revel help [command]" for more information.
|
||||
`
|
||||
|
||||
var helpTemplate = `usage: revel {{.UsageLine}}
|
||||
{{.Long}}
|
||||
`
|
||||
|
||||
func usage(exitCode int) {
|
||||
tmpl(os.Stderr, usageTemplate, commands)
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func tmpl(w io.Writer, text string, data interface{}) {
|
||||
t := template.New("top")
|
||||
template.Must(t.Parse(text))
|
||||
if err := t.Execute(w, data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"strconv"
|
||||
|
||||
"github.com/leanote/leanote/cmd/harness"
|
||||
"github.com/revel/revel"
|
||||
// "fmt"
|
||||
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var cmdRun = &Command{
|
||||
UsageLine: "run [import path] [run mode] [port]",
|
||||
Short: "run a Revel application",
|
||||
Long: `
|
||||
Run the Revel web application named by the given import path.
|
||||
|
||||
For example, to run the chat room sample application:
|
||||
|
||||
revel run github.com/revel/examples/chat dev
|
||||
|
||||
The run mode is used to select which set of app.conf configuration should
|
||||
apply and may be used to determine logic in the application itself.
|
||||
|
||||
Run mode defaults to "dev".
|
||||
|
||||
You can set a port as an optional third parameter. For example:
|
||||
|
||||
revel run github.com/revel/examples/chat prod 8080`,
|
||||
}
|
||||
|
||||
// RunArgs holds revel run parameters
|
||||
type RunArgs struct {
|
||||
ImportPath string
|
||||
Mode string
|
||||
Port int
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdRun.Run = runApp
|
||||
}
|
||||
|
||||
func parseRunArgs(args []string) *RunArgs {
|
||||
inputArgs := RunArgs{
|
||||
ImportPath: importPathFromCurrentDir(),
|
||||
Mode: DefaultRunMode,
|
||||
Port: revel.HTTPPort,
|
||||
}
|
||||
switch len(args) {
|
||||
case 3:
|
||||
// Possibile combinations
|
||||
// revel run [import-path] [run-mode] [port]
|
||||
port, err := strconv.Atoi(args[2])
|
||||
if err != nil {
|
||||
errorf("Failed to parse port as integer: %s", args[2])
|
||||
}
|
||||
inputArgs.ImportPath = args[0]
|
||||
inputArgs.Mode = args[1]
|
||||
inputArgs.Port = port
|
||||
case 2:
|
||||
// Possibile combinations
|
||||
// 1. revel run [import-path] [run-mode]
|
||||
// 2. revel run [import-path] [port]
|
||||
// 3. revel run [run-mode] [port]
|
||||
if _, err := build.Import(args[0], "", build.FindOnly); err == nil {
|
||||
// 1st arg is the import path
|
||||
inputArgs.ImportPath = args[0]
|
||||
if port, err := strconv.Atoi(args[1]); err == nil {
|
||||
// 2nd arg is the port number
|
||||
inputArgs.Port = port
|
||||
} else {
|
||||
// 2nd arg is the run mode
|
||||
inputArgs.Mode = args[1]
|
||||
}
|
||||
} else {
|
||||
// 1st arg is the run mode
|
||||
port, err := strconv.Atoi(args[1])
|
||||
if err != nil {
|
||||
errorf("Failed to parse port as integer: %s", args[1])
|
||||
}
|
||||
inputArgs.Mode = args[0]
|
||||
inputArgs.Port = port
|
||||
}
|
||||
case 1:
|
||||
// Possibile combinations
|
||||
// 1. revel run [import-path]
|
||||
// 2. revel run [port]
|
||||
// 3. revel run [run-mode]
|
||||
if _, err := build.Import(args[0], "", build.FindOnly); err == nil {
|
||||
// 1st arg is the import path
|
||||
inputArgs.ImportPath = args[0]
|
||||
} else if port, err := strconv.Atoi(args[0]); err == nil {
|
||||
// 1st arg is the port number
|
||||
inputArgs.Port = port
|
||||
} else {
|
||||
// 1st arg is the run mode
|
||||
inputArgs.Mode = args[0]
|
||||
}
|
||||
}
|
||||
|
||||
return &inputArgs
|
||||
}
|
||||
|
||||
|
||||
// findSrcPaths uses the "go/build" package to find the source root for Revel
|
||||
// and the app.
|
||||
func findSrcPaths(importPath string) (appSourcePath string) {
|
||||
var (
|
||||
gopaths = filepath.SplitList(build.Default.GOPATH)
|
||||
goroot = build.Default.GOROOT
|
||||
)
|
||||
|
||||
if len(gopaths) == 0 {
|
||||
revel.ERROR.Fatalln("GOPATH environment variable is not set. ",
|
||||
"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
|
||||
}
|
||||
|
||||
if revel.ContainsString(gopaths, goroot) {
|
||||
revel.ERROR.Fatalf("GOPATH (%s) must not include your GOROOT (%s). "+
|
||||
"Please refer to http://golang.org/doc/code.html to configure your Go environment.",
|
||||
gopaths, goroot)
|
||||
}
|
||||
|
||||
appPkg, err := build.Import(importPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
revel.ERROR.Fatalln("Failed to import", importPath, "with error:", err)
|
||||
}
|
||||
|
||||
return appPkg.SrcRoot
|
||||
}
|
||||
|
||||
func runApp(args []string) {
|
||||
runArgs := parseRunArgs(args)
|
||||
|
||||
// Find and parse app.conf
|
||||
// fmt.Println(runArgs.ImportPath + "/vendor")
|
||||
// revel.Init(runArgs.Mode, runArgs.ImportPath, runArgs.ImportPath + "/vendor")
|
||||
srcPath := findSrcPaths(runArgs.ImportPath)
|
||||
srcPath = ""
|
||||
revel.Init(runArgs.Mode, runArgs.ImportPath, srcPath)
|
||||
revel.LoadMimeConfig()
|
||||
|
||||
// fallback to default port
|
||||
if runArgs.Port == 0 {
|
||||
runArgs.Port = revel.HTTPPort
|
||||
}
|
||||
|
||||
revel.INFO.Printf("Running %s (%s) in %s mode\n", revel.AppName, revel.ImportPath, runArgs.Mode)
|
||||
revel.TRACE.Println("Base path:", revel.BasePath)
|
||||
|
||||
// If the app is run in "watched" mode, use the harness to run it.
|
||||
if revel.Config.BoolDefault("watch", true) && revel.Config.BoolDefault("watch.code", true) {
|
||||
revel.TRACE.Println("Running in watched mode.")
|
||||
revel.HTTPPort = runArgs.Port
|
||||
harness.NewHarness().Run() // Never returns.
|
||||
}
|
||||
|
||||
// Else, just build and run the app.
|
||||
revel.TRACE.Println("Running in live build mode.")
|
||||
app, err := harness.Build()
|
||||
if err != nil {
|
||||
errorf("Failed to build app: %s", err)
|
||||
}
|
||||
app.Port = runArgs.Port
|
||||
app.Cmd().Run()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
test-results/
|
||||
tmp/
|
||||
routes/
|
||||
@@ -0,0 +1,43 @@
|
||||
# Welcome to Revel
|
||||
|
||||
A high-productivity web framework for the [Go language](http://www.golang.org/).
|
||||
|
||||
|
||||
### Start the web server:
|
||||
|
||||
revel run myapp
|
||||
|
||||
### Go to http://localhost:9000/ and you'll see:
|
||||
|
||||
"It works"
|
||||
|
||||
## Code Layout
|
||||
|
||||
The directory structure of a generated Revel application:
|
||||
|
||||
conf/ Configuration directory
|
||||
app.conf Main app configuration file
|
||||
routes Routes definition file
|
||||
|
||||
app/ App sources
|
||||
init.go Interceptor registration
|
||||
controllers/ App controllers go here
|
||||
views/ Templates directory
|
||||
|
||||
messages/ Message files
|
||||
|
||||
public/ Public static assets
|
||||
css/ CSS files
|
||||
js/ Javascript files
|
||||
images/ Image files
|
||||
|
||||
tests/ Test suites
|
||||
|
||||
|
||||
## Help
|
||||
|
||||
* The [Getting Started with Revel](http://revel.github.io/tutorial/gettingstarted.html).
|
||||
* The [Revel guides](http://revel.github.io/manual/index.html).
|
||||
* The [Revel sample apps](http://revel.github.io/examples/index.html).
|
||||
* The [API documentation](https://godoc.org/github.com/revel/revel).
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
func (c App) Index() revel.Result {
|
||||
return c.Render()
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var (
|
||||
// AppVersion revel app version (ldflags)
|
||||
AppVersion string
|
||||
|
||||
// BuildTime revel app build-time (ldflags)
|
||||
BuildTime string
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Filters is the default set of global filters.
|
||||
revel.Filters = []revel.Filter{
|
||||
revel.PanicFilter, // Recover from panics and display an error page instead.
|
||||
revel.RouterFilter, // Use the routing table to select the right Action
|
||||
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
|
||||
revel.ParamsFilter, // Parse parameters into Controller.Params.
|
||||
revel.SessionFilter, // Restore and write the session cookie.
|
||||
revel.FlashFilter, // Restore and write the flash cookie.
|
||||
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
|
||||
revel.I18nFilter, // Resolve the requested language
|
||||
HeaderFilter, // Add some security based headers
|
||||
revel.InterceptorFilter, // Run interceptors around the action.
|
||||
revel.CompressFilter, // Compress the result.
|
||||
revel.ActionInvoker, // Invoke the action.
|
||||
}
|
||||
|
||||
|
||||
// register startup functions with OnAppStart
|
||||
// revel.DevMode and revel.RunMode only work inside of OnAppStart. See Example Startup Script
|
||||
// ( order dependent )
|
||||
// revel.OnAppStart(ExampleStartupScript)
|
||||
// revel.OnAppStart(InitDB)
|
||||
// revel.OnAppStart(FillCache)
|
||||
}
|
||||
|
||||
// HeaderFilter adds common security headers
|
||||
// TODO turn this into revel.HeaderFilter
|
||||
// should probably also have a filter for CSRF
|
||||
// not sure if it can go in the same filter or not
|
||||
var HeaderFilter = func(c *revel.Controller, fc []revel.Filter) {
|
||||
c.Response.Out.Header().Add("X-Frame-Options", "SAMEORIGIN")
|
||||
c.Response.Out.Header().Add("X-XSS-Protection", "1; mode=block")
|
||||
c.Response.Out.Header().Add("X-Content-Type-Options", "nosniff")
|
||||
|
||||
fc[0](c, fc[1:]) // Execute the next filter stage.
|
||||
}
|
||||
|
||||
//func ExampleStartupScript() {
|
||||
// // revel.DevMod and revel.RunMode work here
|
||||
// // Use this script to check for dev mode and set dev/prod startup scripts here!
|
||||
// if revel.DevMode == true {
|
||||
// // Dev mode
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,21 @@
|
||||
{{set . "title" "Home"}}
|
||||
{{template "header.html" .}}
|
||||
|
||||
<header class="jumbotron" style="background-color:#A9F16C">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<h1>It works!</h1>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span6">
|
||||
{{template "flash.html" .}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{template "footer.html" .}}
|
||||
@@ -0,0 +1,64 @@
|
||||
<style type="text/css">
|
||||
#sidebar {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top:69px;
|
||||
max-width: 75%;
|
||||
z-index: 1000;
|
||||
background-color: #fee;
|
||||
border: thin solid grey;
|
||||
padding: 10px;
|
||||
}
|
||||
#toggleSidebar {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 50px;
|
||||
background-color: #fee;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="sidebar" style="display:none;">
|
||||
<h4>Available pipelines</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
<h4>Flash</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .flash}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
|
||||
<h4>Errors</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .errors}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
</div>
|
||||
<a id="toggleSidebar" href="#" class="toggles"><i class="glyphicon glyphicon-chevron-left"></i></a>
|
||||
|
||||
<script>
|
||||
$sidebar = 0;
|
||||
$('#toggleSidebar').click(function() {
|
||||
if ($sidebar === 1) {
|
||||
$('#sidebar').hide();
|
||||
$('#toggleSidebar i').addClass('glyphicon-chevron-left');
|
||||
$('#toggleSidebar i').removeClass('glyphicon-chevron-right');
|
||||
$sidebar = 0;
|
||||
}
|
||||
else {
|
||||
$('#sidebar').show();
|
||||
$('#toggleSidebar i').addClass('glyphicon-chevron-right');
|
||||
$('#toggleSidebar i').removeClass('glyphicon-chevron-left');
|
||||
$sidebar = 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Not found</title>
|
||||
</head>
|
||||
<body>
|
||||
{{if eq .RunMode "dev"}}
|
||||
{{template "errors/404-dev.html" .}}
|
||||
{{else}}
|
||||
{{with .Error}}
|
||||
<h1>
|
||||
{{.Title}}
|
||||
</h1>
|
||||
<p>
|
||||
{{.Description}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Application error</title>
|
||||
</head>
|
||||
<body>
|
||||
{{if eq .RunMode "dev"}}
|
||||
{{template "errors/500-dev.html" .}}
|
||||
{{else}}
|
||||
<h1>Oops, an error occured</h1>
|
||||
<p>
|
||||
This exception has been logged.
|
||||
</p>
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
{{if .flash.success}}
|
||||
<div class="alert alert-success">
|
||||
{{.flash.success}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if or .errors .flash.error}}
|
||||
<div class="alert alert-danger">
|
||||
{{if .flash.error}}
|
||||
{{.flash.error}}
|
||||
{{end}}
|
||||
<ul style="margin-top:10px;">
|
||||
{{range .errors}}
|
||||
<li>{{.}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,5 @@
|
||||
{{if eq .RunMode "dev"}}
|
||||
{{template "debug.html" .}}
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.title}}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="/public/css/bootstrap-3.3.6.min.css">
|
||||
<link rel="shortcut icon" type="image/png" href="/public/img/favicon.png">
|
||||
<script src="/public/js/jquery-2.2.4.min.js"></script>
|
||||
<script src="/public/js/bootstrap-3.3.6.min.js"></script>
|
||||
{{range .moreStyles}}
|
||||
<link rel="stylesheet" type="text/css" href="/public/{{.}}">
|
||||
{{end}}
|
||||
{{range .moreScripts}}
|
||||
<script src="/public/{{.}}" type="text/javascript" charset="utf-8"></script>
|
||||
{{end}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -0,0 +1,258 @@
|
||||
################################################################################
|
||||
# Revel configuration file
|
||||
# More info at http://revel.github.io/manual/appconf.html
|
||||
################################################################################
|
||||
|
||||
# Sets `revel.AppName` for use in-app.
|
||||
# Example:
|
||||
# `if revel.AppName {...}`
|
||||
app.name = {{ .AppName }}
|
||||
|
||||
# A secret string which is passed to cryptographically sign the cookie to prevent
|
||||
# (and detect) user modification.
|
||||
# Keep this string secret or users will be able to inject arbitrary cookie values
|
||||
# into your application
|
||||
app.secret = {{ .Secret }}
|
||||
|
||||
# Revel running behind proxy like nginx, haproxy, etc.
|
||||
app.behind.proxy = false
|
||||
|
||||
|
||||
# The IP address on which to listen.
|
||||
http.addr =
|
||||
|
||||
# The port on which to listen.
|
||||
http.port = 9000
|
||||
|
||||
# Whether to use SSL or not.
|
||||
http.ssl = false
|
||||
|
||||
# Path to an X509 certificate file, if using SSL.
|
||||
#http.sslcert =
|
||||
|
||||
# Path to an X509 certificate key, if using SSL.
|
||||
#http.sslkey =
|
||||
|
||||
|
||||
# Timeout specifies a time limit for request (in seconds) made by a single client.
|
||||
# A Timeout of zero means no timeout.
|
||||
http.timeout.read = 90
|
||||
http.timeout.write = 60
|
||||
|
||||
|
||||
# For any cookies set by Revel (Session,Flash,Error) these properties will set
|
||||
# the fields of:
|
||||
# http://golang.org/pkg/net/http/#Cookie
|
||||
#
|
||||
# Each cookie set by Revel is prefixed with this string.
|
||||
cookie.prefix = REVEL
|
||||
|
||||
# A secure cookie has the secure attribute enabled and is only used via HTTPS,
|
||||
# ensuring that the cookie is always encrypted when transmitting from client to
|
||||
# server. This makes the cookie less likely to be exposed to cookie theft via
|
||||
# eavesdropping.
|
||||
#
|
||||
# Defaults to false. If 'http.ssl' is enabled, this will be defaulted to true.
|
||||
# This should only be true when Revel is handling SSL connections. If you are
|
||||
# using a proxy in front of revel (Nginx, Apache, etc), then this should be left
|
||||
# as false.
|
||||
# cookie.secure = false
|
||||
|
||||
# Limit cookie access to a given domain.
|
||||
#cookie.domain =
|
||||
|
||||
# Define when your session cookie expires.
|
||||
# Values:
|
||||
# "720h"
|
||||
# A time duration (http://golang.org/pkg/time/#ParseDuration) after which
|
||||
# the cookie expires and the session is invalid.
|
||||
# "session"
|
||||
# Sets a session cookie which invalidates the session when the user close
|
||||
# the browser.
|
||||
session.expires = 720h
|
||||
|
||||
|
||||
# The date format used by Revel. Possible formats defined by the Go `time`
|
||||
# package (http://golang.org/pkg/time/#Parse)
|
||||
format.date = 2006-01-02
|
||||
format.datetime = 2006-01-02 15:04
|
||||
|
||||
|
||||
# Determines whether the template rendering should use chunked encoding.
|
||||
# Chunked encoding can decrease the time to first byte on the client side by
|
||||
# sending data before the entire template has been fully rendered.
|
||||
results.chunked = false
|
||||
|
||||
|
||||
# Prefixes for each log message line.
|
||||
# User can override these prefix values within any section
|
||||
# For e.g: [dev], [prod], etc
|
||||
log.trace.prefix = "TRACE "
|
||||
log.info.prefix = "INFO "
|
||||
log.warn.prefix = "WARN "
|
||||
log.error.prefix = "ERROR "
|
||||
|
||||
|
||||
# The default language of this application.
|
||||
i18n.default_language = en
|
||||
|
||||
# The default format when message is missing.
|
||||
# The original message shows in %s
|
||||
#i18n.unknown_format = "??? %s ???"
|
||||
|
||||
|
||||
# Module to serve static content such as CSS, JavaScript and Media files
|
||||
# Allows Routes like this:
|
||||
# `Static.ServeModule("modulename","public")`
|
||||
module.static=github.com/revel/modules/static
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
# Section: dev
|
||||
# This section is evaluated when running Revel in dev mode. Like so:
|
||||
# `revel run path/to/myapp`
|
||||
[dev]
|
||||
|
||||
# This sets `revel.DevMode` for use in-app.
|
||||
# Example:
|
||||
# `if revel.DevMode {...}`
|
||||
# or in your templates with
|
||||
# `{{.DevMode}}`
|
||||
# Values:
|
||||
# "true"
|
||||
# Sets `DevMode` to `true`.
|
||||
# "false"
|
||||
# Sets `DevMode` to `false`.
|
||||
mode.dev = true
|
||||
|
||||
|
||||
# Pretty print JSON/XML when calling RenderJSON/RenderXML
|
||||
# Values:
|
||||
# "true"
|
||||
# Enables pretty printing.
|
||||
# "false"
|
||||
# Disables pretty printing.
|
||||
results.pretty = true
|
||||
|
||||
|
||||
# Watch your applicaton files for changes and automatically rebuild
|
||||
# Values:
|
||||
# "true"
|
||||
# Enables auto rebuilding.
|
||||
# "false"
|
||||
# Disables auto rebuilding.
|
||||
watch = true
|
||||
|
||||
|
||||
# Define when to rebuild new changes.
|
||||
# Values:
|
||||
# "normal"
|
||||
# Rebuild when a new request is received and changes have been detected.
|
||||
# "eager"
|
||||
# Rebuild as soon as changes are detected.
|
||||
watch.mode = normal
|
||||
|
||||
# Watch the entire `$GOPATH` for changes.
|
||||
# Values:
|
||||
# "true"
|
||||
# Includes `$GOPATH` in watch path.
|
||||
# "false"
|
||||
# Excludes `$GOPATH` from watch path. Default value.
|
||||
#watch.gopath = true
|
||||
|
||||
|
||||
# Module to run code tests in the browser
|
||||
# See:
|
||||
# http://revel.github.io/manual/testing.html
|
||||
module.testrunner = github.com/revel/modules/testrunner
|
||||
|
||||
|
||||
# Where to log the various Revel logs
|
||||
# Values:
|
||||
# "off"
|
||||
# Disable log output.
|
||||
# "stdout"
|
||||
# Log to OS's standard output.
|
||||
# "stderr"
|
||||
# Log to Os's standard error output. Default value.
|
||||
# "relative/path/to/log"
|
||||
# Log to file.
|
||||
log.trace.output = off
|
||||
log.info.output = stderr
|
||||
log.warn.output = stderr
|
||||
log.error.output = stderr
|
||||
|
||||
|
||||
# Revel log flags. Possible flags defined by the Go `log` package. Go log is
|
||||
# "Bits OR'ed together to control what's printed
|
||||
# See:
|
||||
# https://golang.org/pkg/log/#pkg-constants
|
||||
# Values:
|
||||
# "0"
|
||||
# Just log the message, turn off the flags.
|
||||
# "3"
|
||||
# log.LstdFlags (log.Ldate|log.Ltime)
|
||||
# "19"
|
||||
# log.Ldate|log.Ltime|log.Lshortfile
|
||||
# "23"
|
||||
# log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile
|
||||
log.trace.flags = 19
|
||||
log.info.flags = 19
|
||||
log.warn.flags = 19
|
||||
log.error.flags = 19
|
||||
|
||||
|
||||
# Revel request access log
|
||||
# Access log line format:
|
||||
# RequestStartTime ClientIP ResponseStatus RequestLatency HTTPMethod URLPath
|
||||
# Sample format:
|
||||
# 2016/05/25 17:46:37.112 127.0.0.1 200 270.157µs GET /
|
||||
log.request.output = stderr
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
# Section: prod
|
||||
# This section is evaluated when running Revel in production mode. Like so:
|
||||
# `revel run path/to/myapp prod`
|
||||
# See:
|
||||
# [dev] section for documentation of the various settings
|
||||
[prod]
|
||||
|
||||
mode.dev = false
|
||||
|
||||
results.pretty = false
|
||||
|
||||
watch = false
|
||||
|
||||
module.testrunner =
|
||||
|
||||
log.trace.output = off
|
||||
log.info.output = off
|
||||
log.warn.output = log/%(app.name)s.log
|
||||
log.error.output = log/%(app.name)s.log
|
||||
|
||||
# Revel log flags. Possible flags defined by the Go `log` package,
|
||||
# please refer https://golang.org/pkg/log/#pkg-constants
|
||||
# Go log is "Bits or'ed together to control what's printed"
|
||||
# Examples:
|
||||
# 0 => just log the message, turn off the flags
|
||||
# 3 => log.LstdFlags (log.Ldate|log.Ltime)
|
||||
# 19 => log.Ldate|log.Ltime|log.Lshortfile
|
||||
# 23 => log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile
|
||||
log.trace.flags = 3
|
||||
log.info.flags = 3
|
||||
log.warn.flags = 3
|
||||
log.error.flags = 3
|
||||
|
||||
|
||||
# Revel request access log
|
||||
# Access log line format:
|
||||
# RequestStartTime ClientIP ResponseStatus RequestLatency HTTPMethod URLPath
|
||||
# Sample format:
|
||||
# 2016/05/25 17:46:37.112 127.0.0.1 200 270.157µs GET /
|
||||
# Example:
|
||||
# log.request.output = %(app.name)s-request.log
|
||||
log.request.output = off
|
||||
@@ -0,0 +1,19 @@
|
||||
# Routes Config
|
||||
#
|
||||
# This file defines all application routes (Higher priority routes first)
|
||||
#
|
||||
|
||||
module:testrunner
|
||||
# module:jobs
|
||||
|
||||
|
||||
GET / App.Index
|
||||
|
||||
# Ignore favicon requests
|
||||
GET /favicon.ico 404
|
||||
|
||||
# Map static resources from the /app/public folder to the /public path
|
||||
GET /public/*filepath Static.Serve("public")
|
||||
|
||||
# Catch all
|
||||
* /:controller/:action :controller.:action
|
||||
@@ -0,0 +1,7 @@
|
||||
# Sample messages file for the English language (en)
|
||||
# Message file extensions should be ISO 639-1 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
|
||||
# Sections within each message file can optionally override the defaults using ISO 3166-1 alpha-2 codes (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
|
||||
# See also:
|
||||
# - http://www.rfc-editor.org/rfc/bcp/bcp47.txt
|
||||
# - http://www.w3.org/International/questions/qa-accept-lang-locales
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Bootstrap v3.3.6 (http://getbootstrap.com)
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\Line truncated
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* Bootstrap v3.3.6 (http://getbootstrap.com)
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under the MIT license
|
||||
*/
|
||||
if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>2)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.6",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.6",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.6",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b|Line truncated
|
||||
d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.6",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.6",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
|
||||
@@ -0,0 +1,4 @@
|
||||
/*! jQuery v2.2.4 | (c) jQuery Foundation | jquery.org/license */
|
||||
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="2.2.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new ReLine truncated
|
||||
}catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=N.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),U=["Top","Right","Bottom","Left"],V=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)};function W(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return n.css(a,b,"")},i=h(),j=c&&c[3]||(n.cssNumber[b]?"":"px"),k=(n.cssNumber[b]||"px"!==j&&+i)&&T.exec(n.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,n.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var X=/^(?:checkbox|radio)$/i,Y=/<([\w:-]+)/,Z=/^$|\/(?:java|ecma)script/i,$={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);forLine truncated
|
||||
void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=n.find.attr(a,"tabindex");return b?parseInt(b,10):cb.test(a.nodeName)||db.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),l.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var eb=/[\t\r\n\f]/g;function fb(a){return a.getAttribute&&a.getAttribute("class")||""}n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,fb(this)))});if("string"==typeof a&&a){b=a.match(G)||[];while(c=this[i++])if(e=fb(c),d=1===c.nodeType&&(" "+e+" ").replace(eb," ")){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=n.trim(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,fb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(G)||[];while(c=this[i++])if(e=fb(c),d=1===c.nodeType&&(" "+e+" ").replace(eb," ")){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=n.trim(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):n.isFunction(a)?this.each(function(c){n(this).toggleClass(a.call(this,c,fb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=n(this),f=a.match(G)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=fb(this),b&&N.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":N.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+fb(c)+" ").replace(eb," ").indexOf(b)>-1)return!0;return!1}});var gb=/\r/g,hb=/[\x20\t\r\n\f]+/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(gb,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a)).replace(hb," ")}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],(c.selected||i===e)&&(l.optDisabled?!c.disabled:null===c.getAttribute("disabled"))&&(!c.parentNode.disabled||!n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(n.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>-1:void 0}},l.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var ib=/^(?:focusinfocus|focusoutblur)$/;n.extend(n.event,{trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!ib.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),l=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},f||!o.trigger||o.trigger.apply(e,c)!==!1)){if(!f&&!o.noBubble&&!n.isWindow(e)){for(j=o.delegateType||q,ib.test(j+q)||(h=h.parentNode);h;h=h.parentNode)p.push(h),i=h;i===(e.ownerDocument||d)&&p.push(i.defaultView||i.parentWindow||a)}g=0;while((h=p[g++])&&!b.isPropagationStopped())b.type=g>1?j:o.bindType||q,m=(N.get(h,"events")||{})[b.type]&&N.get(h,"handle"),m&&m.apply(h,c),m=l&&h[l],m&&m.apply&&L(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=q,f||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==Line truncated
|
||||
@@ -0,0 +1,23 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"github.com/revel/revel/testing"
|
||||
)
|
||||
|
||||
type AppTest struct {
|
||||
testing.TestSuite
|
||||
}
|
||||
|
||||
func (t *AppTest) Before() {
|
||||
println("Set up")
|
||||
}
|
||||
|
||||
func (t *AppTest) TestThatIndexPageWorks() {
|
||||
t.Get("/")
|
||||
t.AssertOk()
|
||||
t.AssertContentType("text/html; charset=utf-8")
|
||||
}
|
||||
|
||||
func (t *AppTest) After() {
|
||||
println("Tear down")
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/revel/cmd/harness"
|
||||
"github.com/revel/modules/testrunner/app/controllers"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var cmdTest = &Command{
|
||||
UsageLine: "test [import path] [run mode] [suite.method]",
|
||||
Short: "run all tests from the command-line",
|
||||
Long: `
|
||||
Run all tests for the Revel app named by the given import path.
|
||||
|
||||
For example, to run the booking sample application's tests:
|
||||
|
||||
revel test github.com/revel/examples/booking dev
|
||||
|
||||
The run mode is used to select which set of app.conf configuration should
|
||||
apply and may be used to determine logic in the application itself.
|
||||
|
||||
Run mode defaults to "dev".
|
||||
|
||||
You can run a specific suite (and function) by specifying a third parameter.
|
||||
For example, to run all of UserTest:
|
||||
|
||||
revel test outspoken test UserTest
|
||||
|
||||
or one of UserTest's methods:
|
||||
|
||||
revel test outspoken test UserTest.Test1
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdTest.Run = testApp
|
||||
}
|
||||
|
||||
func testApp(args []string) {
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
errorf("No import path given.\nRun 'revel help test' for usage.\n")
|
||||
}
|
||||
|
||||
mode := DefaultRunMode
|
||||
if len(args) >= 2 {
|
||||
mode = args[1]
|
||||
}
|
||||
|
||||
// Find and parse app.conf
|
||||
revel.Init(mode, args[0], "")
|
||||
|
||||
// Ensure that the testrunner is loaded in this mode.
|
||||
checkTestRunner()
|
||||
|
||||
// Create a directory to hold the test result files.
|
||||
resultPath := filepath.Join(revel.BasePath, "test-results")
|
||||
if err = os.RemoveAll(resultPath); err != nil {
|
||||
errorf("Failed to remove test result directory %s: %s", resultPath, err)
|
||||
}
|
||||
if err = os.Mkdir(resultPath, 0777); err != nil {
|
||||
errorf("Failed to create test result directory %s: %s", resultPath, err)
|
||||
}
|
||||
|
||||
// Direct all the output into a file in the test-results directory.
|
||||
file, err := os.OpenFile(filepath.Join(resultPath, "app.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
errorf("Failed to create test result log file: %s", err)
|
||||
}
|
||||
|
||||
app, reverr := harness.Build()
|
||||
if reverr != nil {
|
||||
errorf("Error building: %s", reverr)
|
||||
}
|
||||
cmd := app.Cmd()
|
||||
cmd.Stderr = io.MultiWriter(cmd.Stderr, file)
|
||||
cmd.Stdout = io.MultiWriter(cmd.Stderr, file)
|
||||
|
||||
// Start the app...
|
||||
if err := cmd.Start(); err != nil {
|
||||
errorf("%s", err)
|
||||
}
|
||||
defer cmd.Kill()
|
||||
revel.INFO.Printf("Testing %s (%s) in %s mode\n", revel.AppName, revel.ImportPath, mode)
|
||||
|
||||
var httpAddr = revel.HTTPAddr
|
||||
if httpAddr == "" {
|
||||
httpAddr = "127.0.0.1"
|
||||
}
|
||||
|
||||
var httpProto = "http"
|
||||
if revel.HTTPSsl {
|
||||
httpProto = "https"
|
||||
}
|
||||
|
||||
// Get a list of tests
|
||||
var baseURL = fmt.Sprintf("%s://%s:%d", httpProto, httpAddr, revel.HTTPPort)
|
||||
testSuites, _ := getTestsList(baseURL)
|
||||
|
||||
// If a specific TestSuite[.Method] is specified, only run that suite/test
|
||||
if len(args) == 3 {
|
||||
testSuites = filterTestSuites(testSuites, args[2])
|
||||
}
|
||||
testSuiteCount := len(*testSuites)
|
||||
fmt.Printf("\n%d test suite%s to run.\n", testSuiteCount, pluralize(testSuiteCount, "", "s"))
|
||||
fmt.Println()
|
||||
|
||||
// Run each suite.
|
||||
failedResults, overallSuccess := runTestSuites(baseURL, resultPath, testSuites)
|
||||
|
||||
fmt.Println()
|
||||
if overallSuccess {
|
||||
writeResultFile(resultPath, "result.passed", "passed")
|
||||
fmt.Println("All Tests Passed.")
|
||||
} else {
|
||||
for _, failedResult := range *failedResults {
|
||||
fmt.Printf("Failures:\n")
|
||||
for _, result := range failedResult.Results {
|
||||
if !result.Passed {
|
||||
fmt.Printf("%s.%s\n", failedResult.Name, result.Name)
|
||||
fmt.Printf("%s\n\n", result.ErrorSummary)
|
||||
}
|
||||
}
|
||||
}
|
||||
writeResultFile(resultPath, "result.failed", "failed")
|
||||
errorf("Some tests failed. See file://%s for results.", resultPath)
|
||||
}
|
||||
}
|
||||
|
||||
func writeResultFile(resultPath, name, content string) {
|
||||
if err := ioutil.WriteFile(filepath.Join(resultPath, name), []byte(content), 0666); err != nil {
|
||||
errorf("Failed to write result file %s: %s", filepath.Join(resultPath, name), err)
|
||||
}
|
||||
}
|
||||
|
||||
func pluralize(num int, singular, plural string) string {
|
||||
if num == 1 {
|
||||
return singular
|
||||
}
|
||||
return plural
|
||||
}
|
||||
|
||||
// Filters test suites and individual tests to match
|
||||
// the parsed command line parameter
|
||||
func filterTestSuites(suites *[]controllers.TestSuiteDesc, suiteArgument string) *[]controllers.TestSuiteDesc {
|
||||
var suiteName, testName string
|
||||
argArray := strings.Split(suiteArgument, ".")
|
||||
suiteName = argArray[0]
|
||||
if suiteName == "" {
|
||||
return suites
|
||||
}
|
||||
if len(argArray) == 2 {
|
||||
testName = argArray[1]
|
||||
}
|
||||
for _, suite := range *suites {
|
||||
if suite.Name != suiteName {
|
||||
continue
|
||||
}
|
||||
if testName == "" {
|
||||
return &[]controllers.TestSuiteDesc{suite}
|
||||
}
|
||||
// Only run a particular test in a suite
|
||||
for _, test := range suite.Tests {
|
||||
if test.Name != testName {
|
||||
continue
|
||||
}
|
||||
return &[]controllers.TestSuiteDesc{
|
||||
{
|
||||
Name: suite.Name,
|
||||
Tests: []controllers.TestDesc{test},
|
||||
},
|
||||
}
|
||||
}
|
||||
errorf("Couldn't find test %s in suite %s", testName, suiteName)
|
||||
}
|
||||
errorf("Couldn't find test suite %s", suiteName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTestRunner() {
|
||||
testRunnerFound := false
|
||||
for _, module := range revel.Modules {
|
||||
if module.ImportPath == revel.Config.StringDefault("module.testrunner", "github.com/revel/modules/testrunner") {
|
||||
testRunnerFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !testRunnerFound {
|
||||
errorf(`Error: The testrunner module is not running.
|
||||
|
||||
You can add it to a run mode configuration with the following line:
|
||||
|
||||
module.testrunner = github.com/revel/modules/testrunner
|
||||
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
// Get a list of tests from server.
|
||||
// Since this is the first request to the server, retry/sleep a couple times
|
||||
// in case it hasn't finished starting up yet.
|
||||
func getTestsList(baseURL string) (*[]controllers.TestSuiteDesc, error) {
|
||||
var (
|
||||
err error
|
||||
resp *http.Response
|
||||
testSuites []controllers.TestSuiteDesc
|
||||
)
|
||||
for i := 0; ; i++ {
|
||||
if resp, err = http.Get(baseURL + "/@tests.list"); err == nil {
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i < 3 {
|
||||
time.Sleep(3 * time.Second)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
errorf("Failed to request test list: %s", err)
|
||||
} else {
|
||||
errorf("Failed to request test list: non-200 response")
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&testSuites)
|
||||
|
||||
return &testSuites, err
|
||||
}
|
||||
|
||||
func runTestSuites(baseURL, resultPath string, testSuites *[]controllers.TestSuiteDesc) (*[]controllers.TestSuiteResult, bool) {
|
||||
// Load the result template, which we execute for each suite.
|
||||
module, _ := revel.ModuleByName("testrunner")
|
||||
TemplateLoader := revel.NewTemplateLoader([]string{filepath.Join(module.Path, "app", "views")})
|
||||
if err := TemplateLoader.Refresh(); err != nil {
|
||||
errorf("Failed to compile templates: %s", err)
|
||||
}
|
||||
resultTemplate, err := TemplateLoader.Template("TestRunner/SuiteResult.html")
|
||||
if err != nil {
|
||||
errorf("Failed to load suite result template: %s", err)
|
||||
}
|
||||
|
||||
var (
|
||||
overallSuccess = true
|
||||
failedResults []controllers.TestSuiteResult
|
||||
)
|
||||
for _, suite := range *testSuites {
|
||||
// Print the name of the suite we're running.
|
||||
name := suite.Name
|
||||
if len(name) > 22 {
|
||||
name = name[:19] + "..."
|
||||
}
|
||||
fmt.Printf("%-22s", name)
|
||||
|
||||
// Run every test.
|
||||
startTime := time.Now()
|
||||
suiteResult := controllers.TestSuiteResult{Name: suite.Name, Passed: true}
|
||||
for _, test := range suite.Tests {
|
||||
testURL := baseURL + "/@tests/" + suite.Name + "/" + test.Name
|
||||
resp, err := http.Get(testURL)
|
||||
if err != nil {
|
||||
errorf("Failed to fetch test result at url %s: %s", testURL, err)
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
var testResult controllers.TestResult
|
||||
err = json.NewDecoder(resp.Body).Decode(&testResult)
|
||||
if err == nil && !testResult.Passed {
|
||||
suiteResult.Passed = false
|
||||
}
|
||||
suiteResult.Results = append(suiteResult.Results, testResult)
|
||||
}
|
||||
overallSuccess = overallSuccess && suiteResult.Passed
|
||||
|
||||
// Print result. (Just PASSED or FAILED, and the time taken)
|
||||
suiteResultStr, suiteAlert := "PASSED", ""
|
||||
if !suiteResult.Passed {
|
||||
suiteResultStr, suiteAlert = "FAILED", "!"
|
||||
failedResults = append(failedResults, suiteResult)
|
||||
}
|
||||
fmt.Printf("%8s%3s%6ds\n", suiteResultStr, suiteAlert, int(time.Since(startTime).Seconds()))
|
||||
// Create the result HTML file.
|
||||
suiteResultFilename := filepath.Join(resultPath,
|
||||
fmt.Sprintf("%s.%s.html", suite.Name, strings.ToLower(suiteResultStr)))
|
||||
suiteResultFile, err := os.Create(suiteResultFilename)
|
||||
if err != nil {
|
||||
errorf("Failed to create result file %s: %s", suiteResultFilename, err)
|
||||
}
|
||||
if err = resultTemplate.Render(suiteResultFile, suiteResult); err != nil {
|
||||
errorf("Failed to render result template: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &failedResults, overallSuccess
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
// LoggedError is wrapper to differentiate logged panics from unexpected ones.
|
||||
type LoggedError struct{ error }
|
||||
|
||||
func panicOnError(err error, msg string) {
|
||||
if revErr, ok := err.(*revel.Error); (ok && revErr != nil) || (!ok && err != nil) {
|
||||
fmt.Fprintf(os.Stderr, "Abort: %s: %s\n", msg, err)
|
||||
panic(LoggedError{err})
|
||||
}
|
||||
}
|
||||
|
||||
func mustCopyFile(destFilename, srcFilename string) {
|
||||
destFile, err := os.Create(destFilename)
|
||||
panicOnError(err, "Failed to create file "+destFilename)
|
||||
|
||||
srcFile, err := os.Open(srcFilename)
|
||||
panicOnError(err, "Failed to open file "+srcFilename)
|
||||
|
||||
_, err = io.Copy(destFile, srcFile)
|
||||
panicOnError(err,
|
||||
fmt.Sprintf("Failed to copy data from %s to %s", srcFile.Name(), destFile.Name()))
|
||||
|
||||
err = destFile.Close()
|
||||
panicOnError(err, "Failed to close file "+destFile.Name())
|
||||
|
||||
err = srcFile.Close()
|
||||
panicOnError(err, "Failed to close file "+srcFile.Name())
|
||||
}
|
||||
|
||||
func mustRenderTemplate(destPath, srcPath string, data map[string]interface{}) {
|
||||
tmpl, err := template.ParseFiles(srcPath)
|
||||
panicOnError(err, "Failed to parse template "+srcPath)
|
||||
|
||||
f, err := os.Create(destPath)
|
||||
panicOnError(err, "Failed to create "+destPath)
|
||||
|
||||
err = tmpl.Execute(f, data)
|
||||
panicOnError(err, "Failed to render template "+srcPath)
|
||||
|
||||
err = f.Close()
|
||||
panicOnError(err, "Failed to close "+f.Name())
|
||||
}
|
||||
|
||||
func mustChmod(filename string, mode os.FileMode) {
|
||||
err := os.Chmod(filename, mode)
|
||||
panicOnError(err, fmt.Sprintf("Failed to chmod %d %q", mode, filename))
|
||||
}
|
||||
|
||||
// copyDir copies a directory tree over to a new directory. Any files ending in
|
||||
// ".template" are treated as a Go template and rendered using the given data.
|
||||
// Additionally, the trailing ".template" is stripped from the file name.
|
||||
// Also, dot files and dot directories are skipped.
|
||||
func mustCopyDir(destDir, srcDir string, data map[string]interface{}) error {
|
||||
return revel.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
|
||||
// Get the relative path from the source base, and the corresponding path in
|
||||
// the dest directory.
|
||||
relSrcPath := strings.TrimLeft(srcPath[len(srcDir):], string(os.PathSeparator))
|
||||
destPath := filepath.Join(destDir, relSrcPath)
|
||||
|
||||
// Skip dot files and dot directories.
|
||||
if strings.HasPrefix(relSrcPath, ".") {
|
||||
if info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a subdirectory if necessary.
|
||||
if info.IsDir() {
|
||||
err := os.MkdirAll(filepath.Join(destDir, relSrcPath), 0777)
|
||||
if !os.IsExist(err) {
|
||||
panicOnError(err, "Failed to create directory")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If this file ends in ".template", render it as a template.
|
||||
if strings.HasSuffix(relSrcPath, ".template") {
|
||||
mustRenderTemplate(destPath[:len(destPath)-len(".template")], srcPath, data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Else, just copy it over.
|
||||
mustCopyFile(destPath, srcPath)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func mustTarGzDir(destFilename, srcDir string) string {
|
||||
zipFile, err := os.Create(destFilename)
|
||||
panicOnError(err, "Failed to create archive")
|
||||
defer func() {
|
||||
_ = zipFile.Close()
|
||||
}()
|
||||
|
||||
gzipWriter := gzip.NewWriter(zipFile)
|
||||
defer func() {
|
||||
_ = gzipWriter.Close()
|
||||
}()
|
||||
|
||||
tarWriter := tar.NewWriter(gzipWriter)
|
||||
defer func() {
|
||||
_ = tarWriter.Close()
|
||||
}()
|
||||
|
||||
_ = revel.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
srcFile, err := os.Open(srcPath)
|
||||
panicOnError(err, "Failed to read source file")
|
||||
defer func() {
|
||||
_ = srcFile.Close()
|
||||
}()
|
||||
|
||||
err = tarWriter.WriteHeader(&tar.Header{
|
||||
Name: strings.TrimLeft(srcPath[len(srcDir):], string(os.PathSeparator)),
|
||||
Size: info.Size(),
|
||||
Mode: int64(info.Mode()),
|
||||
ModTime: info.ModTime(),
|
||||
})
|
||||
panicOnError(err, "Failed to write tar entry header")
|
||||
|
||||
_, err = io.Copy(tarWriter, srcFile)
|
||||
panicOnError(err, "Failed to copy")
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return zipFile.Name()
|
||||
}
|
||||
|
||||
func exists(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// empty returns true if the given directory is empty.
|
||||
// the directory must exist.
|
||||
func empty(dirname string) bool {
|
||||
dir, err := os.Open(dirname)
|
||||
if err != nil {
|
||||
errorf("error opening directory: %s", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = dir.Close()
|
||||
}()
|
||||
results, _ := dir.Readdir(1)
|
||||
return len(results) == 0
|
||||
}
|
||||
|
||||
func importPathFromCurrentDir() string {
|
||||
pwd, _ := os.Getwd()
|
||||
importPath, _ := filepath.Rel(filepath.Join(build.Default.GOPATH, "src"), pwd)
|
||||
return filepath.ToSlash(importPath)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
||||
// Revel Framework source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var cmdVersion = &Command{
|
||||
UsageLine: "version",
|
||||
Short: "displays the Revel Framework and Go version",
|
||||
Long: `
|
||||
Displays the Revel Framework and Go version.
|
||||
|
||||
For example:
|
||||
|
||||
revel version
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdVersion.Run = versionApp
|
||||
}
|
||||
|
||||
func versionApp(args []string) {
|
||||
fmt.Printf("Version(s):")
|
||||
fmt.Printf("\n Revel v%v (%v)", revel.Version, revel.BuildDate)
|
||||
fmt.Printf("\n %s %s/%s\n\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
+3
-1
@@ -34,6 +34,7 @@ cookie.httponly=false
|
||||
cookie.prefix=LEANOTE
|
||||
cookie.domain= # for share cookie with sub-domain
|
||||
cookie.secure=false
|
||||
session.expires=3h # 3 hour. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
format.date=2006-01-02
|
||||
format.datetime=2006-01-02 15:04:05 # 必须这样配置
|
||||
results.chunked=false
|
||||
@@ -46,7 +47,8 @@ log.error.prefix = "ERROR "
|
||||
# The default language of this application.
|
||||
i18n.default_language=en-us
|
||||
|
||||
module.static=github.com/revel/modules/static
|
||||
# module.static=github.com/revel/modules/static
|
||||
module.static=github.com/leanote/leanote/vendor/github.com/revel/modules/static
|
||||
|
||||
[dev]
|
||||
mode.dev=true
|
||||
|
||||
@@ -48,6 +48,7 @@ log.error.prefix = "ERROR "
|
||||
i18n.default_language=en-us
|
||||
|
||||
module.static=github.com/revel/modules/static
|
||||
module.static=github.com/leanote/leanote/vendor/github.com/revel/modules/static
|
||||
|
||||
[dev]
|
||||
mode.dev=true
|
||||
|
||||
@@ -40,6 +40,7 @@ POST /findPasswordUpdate Auth.FindPasswordUpdate
|
||||
* /note/setNote2Blog Note.SetNote2Blog
|
||||
* /note/exportPdf Note.ExportPDF
|
||||
* /note/toPdf Note.ToPdf
|
||||
* /note/getNoteAndContentBySrc Note.GetNoteAndContentBySrc
|
||||
# pjax
|
||||
GET /note/:noteId Note.Index
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Cut=Cut
|
||||
Header 2=Header 2
|
||||
Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.=Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.
|
||||
Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.=Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.
|
||||
Div=Div
|
||||
Paste=Paste
|
||||
Close=Close
|
||||
@@ -73,7 +73,7 @@ Language=Language
|
||||
Fullscreen=Full-screen
|
||||
Horizontal line=Horizontal line
|
||||
Horizontal space=Horizontal space
|
||||
Insert\/edit image=Insert\/edit image
|
||||
Insert/edit image=Insert/edit image
|
||||
General=General
|
||||
Advanced=Advanced
|
||||
Source=Source
|
||||
@@ -84,7 +84,7 @@ Image description=Image description
|
||||
Style=Style
|
||||
Dimensions=Dimensions
|
||||
Insert image=Insert image
|
||||
Insert date\/time=Insert date\/time
|
||||
Insert date/time=Insert date/time
|
||||
Remove link=Remove link
|
||||
Url=URL
|
||||
Text to display=Text to display
|
||||
@@ -92,11 +92,11 @@ Anchors=Anchors
|
||||
Insert link=Insert link
|
||||
New window=New window
|
||||
None=None
|
||||
The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?=The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?
|
||||
The URL you entered seems to be an external link. Do you want to add the required http:// prefix?=The URL you entered seems to be an external link. Do you want to add the required http:// prefix?
|
||||
Target=Target
|
||||
The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?=The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?
|
||||
Insert\/edit link=Insert\/edit link
|
||||
Insert\/edit video=Insert\/edit video
|
||||
Insert/edit link=Insert/edit link
|
||||
Insert/edit video=Insert/edit video
|
||||
Poster=Poster
|
||||
Alternative source=Alternative source
|
||||
Paste your embed code below:=Paste your embed code below:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Cut=Cut
|
||||
Header 2=Header 2
|
||||
Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.=Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.
|
||||
Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.=Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.
|
||||
Div=Div
|
||||
Paste=Paste
|
||||
Close=Close
|
||||
@@ -73,7 +73,7 @@ Language=Language
|
||||
Fullscreen=Full-screen
|
||||
Horizontal line=Horizontal line
|
||||
Horizontal space=Horizontal space
|
||||
Insert\/edit image=Insert\/edit image
|
||||
Insert/edit image=Insert/edit image
|
||||
General=General
|
||||
Advanced=Advanced
|
||||
Source=Source
|
||||
@@ -84,7 +84,7 @@ Image description=Image description
|
||||
Style=Style
|
||||
Dimensions=Dimensions
|
||||
Insert image=Insert image
|
||||
Insert date\/time=Insert date\/time
|
||||
Insert date/time=Insert date/time
|
||||
Remove link=Remove link
|
||||
Url=URL
|
||||
Text to display=Text to display
|
||||
@@ -92,11 +92,11 @@ Anchors=Anchors
|
||||
Insert link=Insert link
|
||||
New window=New window
|
||||
None=None
|
||||
The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?=The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?
|
||||
The URL you entered seems to be an external link. Do you want to add the required http:// prefix?=The URL you entered seems to be an external link. Do you want to add the required http:// prefix?
|
||||
Target=Target
|
||||
The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?=The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?
|
||||
Insert\/edit link=Insert\/edit link
|
||||
Insert\/edit video=Insert\/edit video
|
||||
Insert/edit link=Insert/edit link
|
||||
Insert/edit video=Insert/edit video
|
||||
Poster=Poster
|
||||
Alternative source=Alternative source
|
||||
Paste your embed code below:=Paste your embed code below:
|
||||
|
||||
Loaded 100 of 814 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user