Compare commits

...

11 Commits

Author SHA1 Message Date
binnchx
fb484b2132 Merge remote-tracking branch 'origin/dev' into dev-binnchx 2015-01-22 23:18:20 +08:00
binnchx
4d5a3ffd0f .gitignore is now working 2015-01-22 23:16:26 +08:00
binnchx
f78eb4db49 add share note 2015-01-22 23:13:58 +08:00
binnchx
c35aa1eaa4 ignore app.conf 2015-01-11 22:34:17 +08:00
binnchx
96b094844e rm file 2015-01-11 22:06:09 +08:00
binnchx
9af13bcc32 Merge remote-tracking branch 'origin/dev' into dev-binnchx
Conflicts:
2015-01-11 22:04:18 +08:00
binnchx
29215a1d64 modify git ignore 2015-01-11 21:29:54 +08:00
binnchx
d828a10e7b add conf file 2015-01-11 21:29:26 +08:00
binnchx
a851aaa399 commit sharing note 2015-01-11 17:59:07 +08:00
binnchx
a393bb8723 Merge branch 'master' into dev-binnchx 2015-01-03 21:25:34 +08:00
binnchx
599e54de5b share note to unregistered users
share note to unregistered users
2015-01-03 17:57:52 +08:00
46 changed files with 463 additions and 7116 deletions

1
.gitignore vendored
View File

@@ -16,3 +16,4 @@ app/tmp/main.go
.project
public/config.codekit
files
conf/app.conf

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>leanote-public</name>
<comment>leanote, your own cloud note!</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.googlecode.goclipse.goBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>goclipse.goNature</nature>
</natures>
</projectDescription>

View File

@@ -129,9 +129,18 @@ func (c Attach) GetAttachs(noteId string) revel.Result {
// 下载附件
// 权限判断
func (c Attach) Download(attachId string) revel.Result {
attach := attachService.GetAttach(attachId, c.GetUserId()); // 得到路径
func (c Attach) Download(attachId, token string) revel.Result {
if c.GetUserId() == "" && token == "" {
return c.RenderText("你需要从分享页面下载附件!")
}
sessionId := c.Session.Id()
attach := attachService.GetAttach(attachId, c.GetUserId(), token, sessionId); // 得到路径
path := attach.Path
if token != "" && attach.Path == "" {
return c.RenderText("该下载链接已经失效,请重新刷新原分享笔记页面下载")
}
if path == "" {
return c.RenderText("")
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
// "strconv"
"fmt"
)
// 用户登录/注销/找回密码
@@ -40,7 +41,7 @@ func (c Auth) Login(email, from string) revel.Result {
func (c Auth) doLogin(email, pwd string) revel.Result {
sessionId := c.Session.Id()
var msg = ""
fmt.Println(sessionId)
userInfo := authService.Login(email, pwd)
if userInfo.Email != "" {
c.SetSession(userInfo)
@@ -55,8 +56,8 @@ func (c Auth) doLogin(email, pwd string) revel.Result {
}
func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result {
sessionId := c.Session.Id()
var msg = ""
var msg = ""
// > 5次需要验证码, 直到登录成功
if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha {
msg = "captchaError"

View File

@@ -540,21 +540,27 @@ func (c Blog) Index(userIdOrEmail string) (re revel.Result) {
}
userId, userInfo := c.userIdOrEmail(hasDomain, userBlog, userIdOrEmail)
var ok = false
fmt.Println("before test...");
if ok, userBlog = c.blogCommon(userId, userBlog, userInfo); !ok {
fmt.Println("404 occur");
return c.e404(userBlog.ThemePath) // 404 TODO 使用用户的404
}
fmt.Println("after test0...");
// 分页的话, 需要分页信息, totalPage, curPage
page := c.GetPage()
pageInfo, blogs := blogService.ListBlogs(userId, "", page, userBlog.PerPageSize, userBlog.SortField, userBlog.IsAsc)
blogs2 := blogService.FixBlogs(blogs)
c.RenderArgs["posts"] = blogs2
c.setPaging(pageInfo)
c.RenderArgs["pagingBaseUrl"] = c.RenderArgs["indexUrl"]
c.RenderArgs["curIsIndex"] = true
return c.render("index.html", userBlog.ThemePath)
}

View File

@@ -9,9 +9,9 @@ import (
"github.com/leanote/leanote/app/info"
"io/ioutil"
"os"
"fmt"
// "strconv"
"strings"
"fmt"
)
// 首页
@@ -208,8 +208,9 @@ func (c File) DeleteImage(fileId string) revel.Result {
// 输出image
// 权限判断
func (c File) OutputImage(noteId, fileId string) revel.Result {
path := fileService.GetFile(c.GetUserId(), fileId); // 得到路径
func (c File) OutputImage(noteId, fileId , token string) revel.Result {
sessionId := c.Session.Id()
path := fileService.GetFile(c.GetUserId(), fileId, sessionId, token); // 得到路径
if path == "" {
return c.RenderText("")
}

View File

@@ -9,12 +9,15 @@ import (
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
// "time"
)
type Share struct {
BaseController
}
const AttachToken = 2
// 添加共享note
func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Result {
status := make(map[string]info.Re, len(emails))
@@ -36,6 +39,8 @@ func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Resu
return c.RenderJson(status)
}
// 添加共享notebook
func (c Share) AddShareNotebook(notebookId string, emails []string, perm int) revel.Result {
status := make(map[string]info.Re, len(emails))
@@ -185,4 +190,64 @@ func (c Share) DeleteShareNotebookGroup(notebookId, groupId string) revel.Result
// 更新, 也是一样, 先删后加
func (c Share) UpdateShareNotebookGroupPerm(notebookId, groupId string, perm int) revel.Result {
return c.AddShareNotebookGroup(notebookId, groupId, perm)
}
//生成笔记分享密码及更新到db
func (c Share) GenShareLinkPass(noteId string) revel.Result {
pass, ok := shareService.GenSharePass(noteId)
re := info.Re{Ok : true, Item : pass, List: ok}
return c.RenderJson(re);
}
func (c Share) QuerySharePass(noteId string) revel.Result {
pass := shareService.QuerySharePass(noteId)
re := info.Re{Ok : true, Item : pass}
return c.RenderJson(re);
}
//展示分享笔记
func (c Share) ShowShareNote(noteId string) revel.Result {
note := noteService.GetNoteById(noteId)
//
c.RenderArgs["noteId"] = noteId
username := userService.GetUsernameById(note.UserId)
c.RenderArgs["userName"] = username
c.RenderArgs["isMarkDown"] = note.IsMarkdown
// c.RenderArgs["timestamp"] = time.Now().Unix()
c.SetLocale()
return c.RenderTemplate("share/show_share_note.html")
}
//验证分享密码
func (c Share) Verify4ShareNote(noteId string, sharePass int) revel.Result {
ok, note, noteContent := shareService.Verify4ShareNote(noteId, sharePass)
attaches := []info.Attach{}
if ok && note.AttachNum > 0 {
attaches = attachService.ListAttachs(noteId, "")
}
token := tokenService.NewToken(noteId, noteId, AttachToken)
//插入笔记作为链接中的附件
noteAttachIds := map[string]bool{}
noteContent.Content, noteAttachIds = shareService.AppendToken4URL(token, noteContent.Content)
//过滤掉插入笔记的附件
filteredAttaches := []info.Attach{}
if len(noteAttachIds) > 0 && len(attaches) > 0 {
for _, attach := range attaches {
if !noteAttachIds[attach.AttachId.Hex()] {
filteredAttaches = append(filteredAttaches, attach)
}
}
} else {
filteredAttaches = attaches
}
sessionId := c.Session.Id()
sessionService.SetToken(sessionId, token)
re := info.Re{Ok : ok, Item: note, List: filteredAttaches, Id: token, Msg: noteContent.Content}
return c.RenderJson(re)
}

View File

@@ -49,6 +49,7 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
"Suggestion": true,
},
"Note": map[string]bool{"ToImage": true},
"Share": map[string]bool{"ShowShareNote": true, "Verify4ShareNote": true},
"Blog": map[string]bool{"Index": true,
"View": true,
"AboutMe": true,

View File

@@ -40,6 +40,8 @@ type Note struct {
RecommendTime time.Time `RecommendTime,omitempty` // 推荐时间
PublicTime time.Time `PublicTime,omitempty` // 发表时间, 公开为博客则设置
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了
SharePass int `SharePass` //分享笔记的密码
}
// 内容

View File

@@ -10,10 +10,11 @@ type Session struct {
Id bson.ObjectId `bson:"_id,omitempty"` // 没有意义
SessionId string `bson:"SessionId"` // SessionId
LoginTimes int `LoginTimes` // 登录错误时间
Captcha string `Captcha` // 验证码
Token string `Token` //attach token
TokenTime time.Time `TokenTime` //token生成时间
CreatedTime time.Time `CreatedTime`
UpdatedTime time.Time `UpdatedTime` // 更新时间, expire这个时间会自动清空
}

View File

@@ -51,6 +51,7 @@ func init() {
// Id retrieves from the cookie or creates a time-based UUID identifying this
// session.
func (s Session) Id() string {
if sessionIdStr, ok := s[SESSION_ID_KEY]; ok {
return sessionIdStr
}
@@ -131,7 +132,7 @@ func getSessionFromCookie(cookie *http.Cookie) Session {
return session
}
sig, data := cookie.Value[:hyphen], cookie.Value[hyphen+1:]
// Verify the signature.
if !revel.Verify(data, sig) {
revel.INFO.Println("Session cookie signature failed")
@@ -145,7 +146,7 @@ func getSessionFromCookie(cookie *http.Cookie) Session {
if sessionTimeoutExpiredOrMissing(session) {
session = make(Session)
}
return session
}
@@ -157,6 +158,7 @@ func SessionFilter(c *revel.Controller, fc []revel.Filter) {
// c.Session, 重新生成一个revel.Session给controller!!!
// Log("sessoin--------")
// LogJ(session)
revelSession := revel.Session(session) // 强制转换 还是同一个对象, 但有个问题, 这样Session.Id()方法是用revel的了
c.Session = revelSession
// 生成sessionId

View File

@@ -46,8 +46,8 @@ func (this *AttachService) updateNoteAttachNum(noteId bson.ObjectId, addNum int)
// list attachs
func (this *AttachService) ListAttachs(noteId, userId string) []info.Attach {
attachs := []info.Attach{}
// 判断是否有权限为笔记添加附件
if !shareService.HasUpdateNotePerm(noteId, userId) {
// 判断是否有权限为笔记添加附件, userId为空时表示是分享笔记的附件
if userId != "" && !shareService.HasUpdateNotePerm(noteId, userId) {
return attachs
}
@@ -56,6 +56,7 @@ func (this *AttachService) ListAttachs(noteId, userId string) []info.Attach {
return attachs
}
func (this *AttachService) UpdateImageTitle(userId, fileId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
}
@@ -105,7 +106,7 @@ func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string)
// 获取文件路径
// 要判断是否具有权限
// userId是否具有attach的访问权限
func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attach) {
func (this *AttachService) GetAttach(attachId, userId, token, sessionId string) (attach info.Attach) {
if attachId == "" {
return
}
@@ -136,6 +137,17 @@ func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attac
return
}
//userId为空则是分享笔记的附件
if userId == "" && sessionId != "" {
if token != "" {
realToken := sessionService.GetToken(sessionId)
if token == realToken {
Log("attach token is equal!")
return
}
}
}
attach = info.Attach{}
return
}

View File

@@ -9,6 +9,7 @@ import (
"time"
"os"
"strings"
// "fmt"
)
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
@@ -118,7 +119,8 @@ func (this *FileService) UpdateImage(userId, fileId, title string) bool {
// 获取文件路径
// 要判断是否具有权限
// userId是否具有fileId的访问权限
func (this *FileService) GetFile(userId, fileId string) string {
func (this *FileService) GetFile(userId, fileId , sessionId , token string) string {
if fileId == "" {
return ""
}
@@ -131,7 +133,8 @@ func (this *FileService) GetFile(userId, fileId string) string {
}
// 1. 判断权限
//未登录用户判断token
noteIds := noteImageService.GetNoteIds(fileId)
// 是否是我的文件
if userId != "" && file.UserId.Hex() == userId {
return path
@@ -141,13 +144,30 @@ func (this *FileService) GetFile(userId, fileId string) string {
// 这些笔记是否有public的, 若有则ok
// 这些笔记(笔记本)是否有共享给我的, 若有则ok
noteIds := noteImageService.GetNoteIds(fileId)
if noteIds != nil && len(noteIds) > 0 {
// 这些笔记是否有public的
if db.Has(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}, "IsBlog": true}) {
return path
}
//分享给未注册用户
if userId == "" && sessionId != "" {
if token != "" {
realToken := sessionService.GetToken(sessionId)
if token == realToken {
Log("image token is equal!")
return path
} else {
Log("image token is different!")
return ""
}
} else {
return ""
}
}
// 2014/12/28 修复, 如果是分享给用户组, 那就不行, 这里可以实现
for _, noteId := range noteIds {
note := noteService.GetNoteById(noteId.Hex())
@@ -155,6 +175,7 @@ func (this *FileService) GetFile(userId, fileId string) string {
return path;
}
}
/*
// 若有共享给我的笔记?
// 对该笔记可读?
@@ -191,6 +212,7 @@ func (this *FileService) GetFile(userId, fileId string) string {
return ""
}
// 复制图片
func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, string) {
// 是否已经复制过了

View File

@@ -17,6 +17,17 @@ func (this *NoteService) GetNote(noteId, userId string) (note info.Note) {
db.GetByIdAndUserId(db.Notes, noteId, userId, &note)
return
}
//通过附件id得到note
func (this *NoteService) GetNoteByAttachId(attachId string) (note info.Note) {
attach := info.Attach{}
db.Get(db.Attachs, attachId, &attach)
note = info.Note{}
noteId := attach.NoteId
db.Get(db.Notes, noteId.Hex(), &note)
return
}
// fileService调用
func (this *NoteService) GetNoteById(noteId string) (note info.Note) {
note = info.Note{}

View File

@@ -17,6 +17,13 @@ func (this *SessionService) Update(sessionId, key string, value interface{}) boo
return db.UpdateByQMap(db.Sessions, bson.M{"SessionId": sessionId},
bson.M{key: value, "UpdatedTime": time.Now()})
}
func (this *SessionService) UpdateToken(sessionId, key string, value interface{}) bool {
return db.UpdateByQMap(db.Sessions, bson.M{"SessionId": sessionId},
bson.M{key: value, "UpdatedTime": time.Now(), "TokenTime": time.Now()})
}
// 注销时清空session
func (this *SessionService) Clear(sessionId string) bool {
return db.Delete(db.Sessions, bson.M{"SessionId": sessionId})
@@ -69,3 +76,19 @@ func (this *SessionService) SetCaptcha(sessionId, captcha string) bool {
Log(ok)
return ok
}
// 附件token
func (this *SessionService) GetToken(sessionId string) string {
session := this.Get(sessionId)
return session.Token
}
func (this *SessionService) SetToken(sessionId, token string) bool {
this.Get(sessionId)
Log(sessionId)
Log(token)
ok := this.UpdateToken(sessionId, "Token", token)
Log(ok)
return ok
}

View File

@@ -7,6 +7,9 @@ import (
"gopkg.in/mgo.v2/bson"
"time"
"sort"
"math/rand"
"regexp"
"strings"
)
// 共享Notebook, Note服务
@@ -337,12 +340,14 @@ func (this *ShareService) AddShareNote(noteId string, perm int, userId, email st
"ToUserId": bson.ObjectIdHex(toUserId),
});
shareNote := info.ShareNote{NoteId: bson.ObjectIdHex(noteId),
UserId: bson.ObjectIdHex(userId),
ToUserId: bson.ObjectIdHex(toUserId),
Perm: perm,
CreatedTime: time.Now(),
}
return db.Insert(db.ShareNotes, shareNote), "", toUserId
}
@@ -777,3 +782,59 @@ func (this *ShareService) DeleteShareNotebookGroup(userId, notebookId, groupId s
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
func (this *ShareService) GenSharePass(noteId string) (int, bool) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
pass := 1000 + r.Intn(9000)
ok := db.Update(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, bson.M{"$set": bson.M{"SharePass": pass}})
return pass, ok
}
func (this *ShareService) QuerySharePass(noteId string) int {
note := &info.Note{}
db.Get(db.Notes, noteId, note)
return note.SharePass
}
func (this *ShareService) Verify4ShareNote(noteId string, sharePass int) (flag bool, note info.Note, noteContent info.NoteContent) {
note = info.Note{}
db.Get(db.Notes, noteId, &note)
if note.SharePass == sharePass {
note = noteService.GetNoteById(noteId)
noteContent = noteService.GetNoteContent(noteId, note.UserId.Hex())
flag = true
} else {
note = info.Note{}
noteContent = info.NoteContent{}
flag = false
}
return
}
//把笔记中含有attachID的url后加上token返回笔记中含有的attachids
func (this *ShareService) AppendToken4URL(token, content string) (c string, attachIds map[string]bool) {
re := regexp.MustCompile(`href=\"(.*?attachId.*?)\".*?data-mce-href=\"(.*?attachId.*?)\"`)
re2 := regexp.MustCompile(`\"(.*?attachId.*?)\"`)
attachIds = map[string]bool{}
s := re.ReplaceAllStringFunc(content, func(m string) string {
idx := strings.Index(m, "attachId=") + len("attachId=")
var ss []byte = []byte(m)
attachId := string(ss[idx: idx + 24])
attachIds[attachId] = true
return re2.ReplaceAllString(m, `"${1}&token=` + token + `"`)
})
re3 := regexp.MustCompile(`<img\s+src=\"(.*?fileId.*?)\".*?data-mce-src=\"(.*?fileId.*?)\"`)
re4 := regexp.MustCompile(`\"(.*?fileId.*?)\"`)
c = re3.ReplaceAllStringFunc(s, func(m string) string {
return re4.ReplaceAllString(m, `"${1}&token=` + token + `"`)
})
return
}

View File

@@ -48,6 +48,13 @@ func (this *UserService) GetUsername(userId string) string {
return user.Username
}
// 得到用户名
func (this *UserService) GetUsernameById(userId bson.ObjectId) string {
user := info.User{}
db.GetByQWithFields(db.Users, bson.M{"_id": userId}, []string{"Username"}, &user)
return user.Username
}
// 是否存在该用户 email
func (this *UserService) IsExistsUser(email string) bool {
if this.GetUserId(email) == "" {

View File

@@ -5,11 +5,14 @@
<h4 class="modal-title" id="modalTitle">{{msg . "share"}} <b>{{.title}}</b></h4>
</div>
{{$noteOrNotebookId := .noteOrNotebookId}}
{{$isNote := .isNote}}
<div class="modal-body">
<ul id="myTab" class="nav nav-tabs">
<li class="active"><a href="#baseInfo" data-toggle="tab">分享给好友</a></li>
<li class=""><a href="#groupInfo" data-toggle="tab">分享给项目组</a></li>
{{if $isNote}}
<li class=""><a href="#shareInfo" data-toggle="tab">分享给未注册好友</a></li>
{{end}}
</ul>
<div class="tab-content">
@@ -95,6 +98,19 @@
{{end}}
</table>
</div>
<div class="tab-pane" id="shareInfo">
<p>
<button class="btn btn-default" id="genShareLink">生成该笔记的分享链接和密码</button>
<span id="showMsg"></span>
</p>
<br>
分享链接:<input type="text" id="shareLink" value="" size=50> <br/><br/>
查看密码:<input type="text" id="sharePass" value="" />
</div>
</div>
</div>
@@ -108,11 +124,55 @@
<script>
Share.dialogIsNote = {{.isNote}};
Share.dialogNoteOrNotebookId = '{{.noteOrNotebookId}}';
var getHost = function(url) {
var host = "null";
if(typeof url == "undefined"
|| null == url)
url = window.location.href;
var regex = /.*\:\/\/([^\/]*).*/;
var match = url.match(regex);
if(typeof match != "undefined"
&& null != match)
host = match[1];
return host;
}
$(function() {
setTimeout(function() {
$("#tr1 #friendsEmail").focus();
}, 500);
var host = getHost();
if (Share.dialogIsNote) {
var url = "/share/querySharePass";
var data = {noteId: Share.dialogNoteOrNotebookId};
ajaxPost(url, data, function(re) {
if(reIsOk(re)) {
if (re.Item >= 1000 && re.Item < 10000) {
var shareLink = host + '/share/note/{{.noteOrNotebookId}}';
$("#shareLink").val(shareLink);
$("#sharePass").val(re.Item);
}
}
});
}
$("#shareLink").hover(function() {
$("#shareLink").focus(function(){
$(this).css({'background-color' : '#f5f5dc'});
}).select();
})
$("#sharePass").hover(function() {
$("#sharePass").focus(function(){
$(this).css({'background-color' : '#f5f5dc'});
}).select();
})
// 分享/删除给分组
$("#groupInfo").on("click", ".btn-share-or-not", function() {
var $t = $(this);
@@ -155,6 +215,28 @@ $("#groupInfo").on("click", ".btn-share-or-not", function() {
});
});
//生成分享链接和密码
$("#genShareLink").on("click", function() {
var sharePass = $("#sharePass").val();
if (sharePass != '' && sharePass > 1000 && sharePass < 10000) {
$("#showMsg").html("<font color='red'>不能重复生成分享链接和密码</font>");
return;
}
var url = "/share/genShareLinkPass";
var shareLink = host + '/share/note/{{.noteOrNotebookId}}';
var data = {noteId: Share.dialogNoteOrNotebookId};
ajaxPost(url, data, function(re) {
if(reIsOk(re)) {
$("#shareLink").val(shareLink);
$("#sharePass").val(re.Item);
}
});
});
$(".group-perm").click(function() {
var $t = $(this);
var $ptr = $t.closest("tr");
@@ -179,5 +261,6 @@ $(".group-perm").click(function() {
});
});
});
</script>

View File

@@ -0,0 +1,134 @@
{{template "home/header.html" .}}
<section>
</section>
{{$isMarkDown := .isMarkDown}}
<div id="queryNote" style="height:300px;padding-top:80px" align="center">
{{ .userName }}分享了笔记<br/><br>
您需要输入分享密码才能查看:<br><br>
<input type="password" id="sharePass" />
<button id="showShareNote">点击查看</button><br/><br/>
<span id="showMsg"></span>
</div>
<div id="noteContainer" style="display:none">
</div>
{{template "home/footer.html"}}
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script src="/js/common-min.js"></script>
<script>
var noteId = {{ .noteId }};
var isMarkDown = {{ .isMarkDown}}
String.prototype.format = function() {
var s = this,
i = arguments.length;
while (i--) {
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
}
return s;
};
var getHost = function(url) {
var host = "null";
if(typeof url == "undefined"
|| null == url)
url = window.location.href;
var regex = /.*\:\/\/([^\/]*).*/;
var match = url.match(regex);
if(typeof match != "undefined"
&& null != match)
host = match[1];
return "http://" + host;
}
$(function() {
$("#showShareNote").on("click", function() {
$("#showMsg").html("");
var url = "/share/verify4ShareNote";
var sharePass = $("#sharePass").val();
var data = {noteId: noteId, sharePass: sharePass};
ajaxPost(url, data, function(re) {
if(reIsOk(re)) {
$("#queryNote").css("display", "none");
var title = re.Item.Title;
var content = re.Msg;
var imgSrc = re.Item.ImgSrc;
var htmlContent = "<div><h1 height='50px'>{0}</h1></div><hr/><div style='vertical-align:center'><p height='auto'>{1}<br>{2}</p>";
var attaches = re.List;
var attachesHtml = "";
if (attaches.length > 0) {
var token = re.Id;
for (var i=0; i<attaches.length; i++) {
var linkUrl = "{0}/attach/download?attachId={1}&token={2}".format(getHost(), attaches[i].AttachId, token);
attachesHtml += "<p><a href='{0}' target='_blank' data-mce-href='{1}' >附件{2}</a></p><br/>".format(linkUrl, linkUrl, i+1);
}
}
htmlContent = htmlContent.format(title, content, attachesHtml) + "</div>";
/* if (isMarkDown) {
var converter = Markdown.getSanitizingConverter();
Markdown.Extra.init(converter, {extensions: ["tables", "fenced_code_gfm", "def_list"]});
var mdDesc = converter.makeHtml(desc);
$("pre").addClass("prettyprint linenums");
prettyPrint();
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"wmd-preview"]);
htmlContent = "<div><h1 height='50px'>"+title+"</h1></div><hr/><div style='vertical-align:center'><p height='auto'>"
+mdDesc+"</p></div>";
} else {
htmlContent = "<div><h1 height='50px'>"+title+"</h1></div><hr/><div style='vertical-align:center'><p height='auto'>"
+desc+"</p></div>";
} */
$("#noteContainer").css("display", "block").css("height", "auto")
.css("padding-top", "20px").css("text-align", "center")
.css("margin-left", "auto").css("margin-right", "auto");
$("#noteContainer").html(htmlContent);
} else {
$("#showMsg").html("<font color='red'>密码错误</font>");
}
});
});
// 平滑滚动
$(".smooth-scroll").click(function(e) {
e.preventDefault();
var t = $(this).attr("target");
var targetOffset = $(t).offset().top - 80;
$('html,body').animate({scrollTop: targetOffset}, 300);
});
});
</script>
{{if $isMarkDown}}
<script src="/public/mdeditor/editor/pagedown/Markdown.Converter.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Sanitizer.js"></script>
<script src="/public/mdeditor/editor/pagedown/Markdown.Editor.js"></script>
<script src="/public/mdeditor/editor/pagedown/local/Markdown.local.zh.js"></script>
<script src="/public/mdeditor/editor/Markdown.Extra.js"></script>
<script src="/public/mdeditor/editor/editor.js"></script>
<script src="/public/js/google-code-prettify/prettify.js"></script>
<!--mathjax-->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]], processEscapes: true }, messageStyle: "none"});
</script>
<script src="/public/mdeditor/editor/mathJax.js"></script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{{end}}

View File

@@ -1,545 +0,0 @@
3dm=x-world/x-3dmf
3dmf=x-world/x-3dmf
7z=application/x-7z-compressed
a=application/octet-stream
aab=application/x-authorware-bin
aam=application/x-authorware-map
aas=application/x-authorware-seg
abc=text/vndabc
ace=application/x-ace-compressed
acgi=text/html
afl=video/animaflex
ai=application/postscript
aif=audio/aiff
aifc=audio/aiff
aiff=audio/aiff
aim=application/x-aim
aip=text/x-audiosoft-intra
alz=application/x-alz-compressed
ani=application/x-navi-animation
aos=application/x-nokia-9000-communicator-add-on-software
aps=application/mime
arc=application/x-arc-compressed
arj=application/arj
art=image/x-jg
asf=video/x-ms-asf
asm=text/x-asm
asp=text/asp
asx=application/x-mplayer2
au=audio/basic
avi=video/x-msvideo
avs=video/avs-video
bcpio=application/x-bcpio
bin=application/mac-binary
bmp=image/bmp
boo=application/book
book=application/book
boz=application/x-bzip2
bsh=application/x-bsh
bz2=application/x-bzip2
bz=application/x-bzip
c++=text/plain
c=text/x-c
cab=application/vnd.ms-cab-compressed
cat=application/vndms-pkiseccat
cc=text/x-c
ccad=application/clariscad
cco=application/x-cocoa
cdf=application/cdf
cer=application/pkix-cert
cha=application/x-chat
chat=application/x-chat
chrt=application/vnd.kde.kchart
class=application/java
# ? class=application/java-vm
com=text/plain
conf=text/plain
cpio=application/x-cpio
cpp=text/x-c
cpt=application/mac-compactpro
crl=application/pkcs-crl
crt=application/pkix-cert
crx=application/x-chrome-extension
csh=text/x-scriptcsh
css=text/css
csv=text/csv
cxx=text/plain
dar=application/x-dar
dcr=application/x-director
deb=application/x-debian-package
deepv=application/x-deepv
def=text/plain
der=application/x-x509-ca-cert
dif=video/x-dv
dir=application/x-director
divx=video/divx
dl=video/dl
dmg=application/x-apple-diskimage
doc=application/msword
dot=application/msword
dp=application/commonground
drw=application/drafting
dump=application/octet-stream
dv=video/x-dv
dvi=application/x-dvi
dwf=drawing/x-dwf=(old)
dwg=application/acad
dxf=application/dxf
dxr=application/x-director
el=text/x-scriptelisp
elc=application/x-bytecodeelisp=(compiled=elisp)
eml=message/rfc822
env=application/x-envoy
eps=application/postscript
es=application/x-esrehber
etx=text/x-setext
evy=application/envoy
exe=application/octet-stream
f77=text/x-fortran
f90=text/x-fortran
f=text/x-fortran
fdf=application/vndfdf
fif=application/fractals
fli=video/fli
flo=image/florian
flv=video/x-flv
flx=text/vndfmiflexstor
fmf=video/x-atomic3d-feature
for=text/x-fortran
fpx=image/vndfpx
frl=application/freeloader
funk=audio/make
g3=image/g3fax
g=text/plain
gif=image/gif
gl=video/gl
gsd=audio/x-gsm
gsm=audio/x-gsm
gsp=application/x-gsp
gss=application/x-gss
gtar=application/x-gtar
gz=application/x-compressed
gzip=application/x-gzip
h=text/x-h
hdf=application/x-hdf
help=application/x-helpfile
hgl=application/vndhp-hpgl
hh=text/x-h
hlb=text/x-script
hlp=application/hlp
hpg=application/vndhp-hpgl
hpgl=application/vndhp-hpgl
hqx=application/binhex
hta=application/hta
htc=text/x-component
htm=text/html
html=text/html
htmls=text/html
htt=text/webviewhtml
htx=text/html
ice=x-conference/x-cooltalk
ico=image/x-icon
ics=text/calendar
icz=text/calendar
idc=text/plain
ief=image/ief
iefs=image/ief
iges=application/iges
igs=application/iges
ima=application/x-ima
imap=application/x-httpd-imap
inf=application/inf
ins=application/x-internett-signup
ip=application/x-ip2
isu=video/x-isvideo
it=audio/it
iv=application/x-inventor
ivr=i-world/i-vrml
ivy=application/x-livescreen
jam=audio/x-jam
jav=text/x-java-source
java=text/x-java-source
jcm=application/x-java-commerce
jfif-tbnl=image/jpeg
jfif=image/jpeg
jnlp=application/x-java-jnlp-file
jpe=image/jpeg
jpeg=image/jpeg
jpg=image/jpeg
jps=image/x-jps
js=application/javascript
json=application/json
jut=image/jutvision
kar=audio/midi
karbon=application/vnd.kde.karbon
kfo=application/vnd.kde.kformula
flw=application/vnd.kde.kivio
kml=application/vnd.google-earth.kml+xml
kmz=application/vnd.google-earth.kmz
kon=application/vnd.kde.kontour
kpr=application/vnd.kde.kpresenter
kpt=application/vnd.kde.kpresenter
ksp=application/vnd.kde.kspread
kwd=application/vnd.kde.kword
kwt=application/vnd.kde.kword
ksh=text/x-scriptksh
la=audio/nspaudio
lam=audio/x-liveaudio
latex=application/x-latex
lha=application/lha
lhx=application/octet-stream
list=text/plain
lma=audio/nspaudio
log=text/plain
lsp=text/x-scriptlisp
lst=text/plain
lsx=text/x-la-asf
ltx=application/x-latex
lzh=application/octet-stream
lzx=application/lzx
m1v=video/mpeg
m2a=audio/mpeg
m2v=video/mpeg
m3u=audio/x-mpegurl
m=text/x-m
man=application/x-troff-man
manifest=text/cache-manifest
map=application/x-navimap
mar=text/plain
mbd=application/mbedlet
mc$=application/x-magic-cap-package-10
mcd=application/mcad
mcf=text/mcf
mcp=application/netmc
me=application/x-troff-me
mht=message/rfc822
mhtml=message/rfc822
mid=application/x-midi
midi=application/x-midi
mif=application/x-frame
mime=message/rfc822
mjf=audio/x-vndaudioexplosionmjuicemediafile
mjpg=video/x-motion-jpeg
mm=application/base64
mme=application/base64
mod=audio/mod
moov=video/quicktime
mov=video/quicktime
movie=video/x-sgi-movie
mp2=audio/mpeg
mp3=audio/mpeg3
mp4=video/mp4
mpa=audio/mpeg
mpc=application/x-project
mpe=video/mpeg
mpeg=video/mpeg
mpg=video/mpeg
mpga=audio/mpeg
mpp=application/vndms-project
mpt=application/x-project
mpv=application/x-project
mpx=application/x-project
mrc=application/marc
ms=application/x-troff-ms
mv=video/x-sgi-movie
my=audio/make
mzz=application/x-vndaudioexplosionmzz
nap=image/naplps
naplps=image/naplps
nc=application/x-netcdf
ncm=application/vndnokiaconfiguration-message
nif=image/x-niff
niff=image/x-niff
nix=application/x-mix-transfer
nsc=application/x-conference
nvd=application/x-navidoc
o=application/octet-stream
oda=application/oda
odb=application/vnd.oasis.opendocument.database
odc=application/vnd.oasis.opendocument.chart
odf=application/vnd.oasis.opendocument.formula
odg=application/vnd.oasis.opendocument.graphics
odi=application/vnd.oasis.opendocument.image
odm=application/vnd.oasis.opendocument.text-master
odp=application/vnd.oasis.opendocument.presentation
ods=application/vnd.oasis.opendocument.spreadsheet
odt=application/vnd.oasis.opendocument.text
oga=audio/ogg
ogg=audio/ogg
ogv=video/ogg
omc=application/x-omc
omcd=application/x-omcdatamaker
omcr=application/x-omcregerator
otc=application/vnd.oasis.opendocument.chart-template
otf=application/vnd.oasis.opendocument.formula-template
otg=application/vnd.oasis.opendocument.graphics-template
oth=application/vnd.oasis.opendocument.text-web
oti=application/vnd.oasis.opendocument.image-template
otm=application/vnd.oasis.opendocument.text-master
otp=application/vnd.oasis.opendocument.presentation-template
ots=application/vnd.oasis.opendocument.spreadsheet-template
ott=application/vnd.oasis.opendocument.text-template
p10=application/pkcs10
p12=application/pkcs-12
p7a=application/x-pkcs7-signature
p7c=application/pkcs7-mime
p7m=application/pkcs7-mime
p7r=application/x-pkcs7-certreqresp
p7s=application/pkcs7-signature
p=text/x-pascal
part=application/pro_eng
pas=text/pascal
pbm=image/x-portable-bitmap
pcl=application/vndhp-pcl
pct=image/x-pict
pcx=image/x-pcx
pdb=chemical/x-pdb
pdf=application/pdf
pfunk=audio/make
pgm=image/x-portable-graymap
pic=image/pict
pict=image/pict
pkg=application/x-newton-compatible-pkg
pko=application/vndms-pkipko
pl=text/x-scriptperl
plx=application/x-pixclscript
pm4=application/x-pagemaker
pm5=application/x-pagemaker
pm=text/x-scriptperl-module
png=image/png
pnm=application/x-portable-anymap
pot=application/mspowerpoint
pov=model/x-pov
ppa=application/vndms-powerpoint
ppm=image/x-portable-pixmap
pps=application/mspowerpoint
ppt=application/mspowerpoint
ppz=application/mspowerpoint
pre=application/x-freelance
prt=application/pro_eng
ps=application/postscript
psd=application/octet-stream
pvu=paleovu/x-pv
pwz=application/vndms-powerpoint
py=text/x-scriptphyton
pyc=applicaiton/x-bytecodepython
qcp=audio/vndqcelp
qd3=x-world/x-3dmf
qd3d=x-world/x-3dmf
qif=image/x-quicktime
qt=video/quicktime
qtc=video/x-qtc
qti=image/x-quicktime
qtif=image/x-quicktime
ra=audio/x-pn-realaudio
ram=audio/x-pn-realaudio
rar=application/x-rar-compressed
ras=application/x-cmu-raster
rast=image/cmu-raster
rexx=text/x-scriptrexx
rf=image/vndrn-realflash
rgb=image/x-rgb
rm=application/vndrn-realmedia
rmi=audio/mid
rmm=audio/x-pn-realaudio
rmp=audio/x-pn-realaudio
rng=application/ringing-tones
rnx=application/vndrn-realplayer
roff=application/x-troff
rp=image/vndrn-realpix
rpm=audio/x-pn-realaudio-plugin
rt=text/vndrn-realtext
rtf=text/richtext
rtx=text/richtext
rv=video/vndrn-realvideo
s=text/x-asm
s3m=audio/s3m
s7z=application/x-7z-compressed
saveme=application/octet-stream
sbk=application/x-tbook
scm=text/x-scriptscheme
sdml=text/plain
sdp=application/sdp
sdr=application/sounder
sea=application/sea
set=application/set
sgm=text/x-sgml
sgml=text/x-sgml
sh=text/x-scriptsh
shar=application/x-bsh
shtml=text/x-server-parsed-html
sid=audio/x-psid
skd=application/x-koan
skm=application/x-koan
skp=application/x-koan
skt=application/x-koan
sit=application/x-stuffit
sitx=application/x-stuffitx
sl=application/x-seelogo
smi=application/smil
smil=application/smil
snd=audio/basic
sol=application/solids
spc=text/x-speech
spl=application/futuresplash
spr=application/x-sprite
sprite=application/x-sprite
spx=audio/ogg
src=application/x-wais-source
ssi=text/x-server-parsed-html
ssm=application/streamingmedia
sst=application/vndms-pkicertstore
step=application/step
stl=application/sla
stp=application/step
sv4cpio=application/x-sv4cpio
sv4crc=application/x-sv4crc
svf=image/vnddwg
svg=image/svg+xml
svr=application/x-world
swf=application/x-shockwave-flash
t=application/x-troff
talk=text/x-speech
tar=application/x-tar
tbk=application/toolbook
tcl=text/x-scripttcl
tcsh=text/x-scripttcsh
tex=application/x-tex
texi=application/x-texinfo
texinfo=application/x-texinfo
text=text/plain
tgz=application/gnutar
tif=image/tiff
tiff=image/tiff
tr=application/x-troff
tsi=audio/tsp-audio
tsp=application/dsptype
tsv=text/tab-separated-values
turbot=image/florian
txt=text/plain
uil=text/x-uil
uni=text/uri-list
unis=text/uri-list
unv=application/i-deas
uri=text/uri-list
uris=text/uri-list
ustar=application/x-ustar
uu=text/x-uuencode
uue=text/x-uuencode
vcd=application/x-cdlink
vcf=text/x-vcard
vcard=text/x-vcard
vcs=text/x-vcalendar
vda=application/vda
vdo=video/vdo
vew=application/groupwise
viv=video/vivo
vivo=video/vivo
vmd=application/vocaltec-media-desc
vmf=application/vocaltec-media-file
voc=audio/voc
vos=video/vosaic
vox=audio/voxware
vqe=audio/x-twinvq-plugin
vqf=audio/x-twinvq
vql=audio/x-twinvq-plugin
vrml=application/x-vrml
vrt=x-world/x-vrt
vsd=application/x-visio
vst=application/x-visio
vsw=application/x-visio
w60=application/wordperfect60
w61=application/wordperfect61
w6w=application/msword
wav=audio/wav
wb1=application/x-qpro
wbmp=image/vnd.wap.wbmp
web=application/vndxara
wiz=application/msword
wk1=application/x-123
wmf=windows/metafile
wml=text/vnd.wap.wml
wmlc=application/vnd.wap.wmlc
wmls=text/vnd.wap.wmlscript
wmlsc=application/vnd.wap.wmlscriptc
word=application/msword
wp5=application/wordperfect
wp6=application/wordperfect
wp=application/wordperfect
wpd=application/wordperfect
wq1=application/x-lotus
wri=application/mswrite
wrl=application/x-world
wrz=model/vrml
wsc=text/scriplet
wsrc=application/x-wais-source
wtk=application/x-wintalk
x-png=image/png
xbm=image/x-xbitmap
xdr=video/x-amt-demorun
xgz=xgl/drawing
xif=image/vndxiff
xl=application/excel
xla=application/excel
xlb=application/excel
xlc=application/excel
xld=application/excel
xlk=application/excel
xll=application/excel
xlm=application/excel
xls=application/excel
xlt=application/excel
xlv=application/excel
xlw=application/excel
xm=audio/xm
xml=text/xml
xmz=xgl/movie
xpix=application/x-vndls-xpix
xpm=image/x-xpixmap
xsr=video/x-amt-showrun
xwd=image/x-xwd
xyz=chemical/x-pdb
z=application/x-compress
zip=application/zip
zoo=application/octet-stream
zsh=text/x-scriptzsh
# Office 2007 mess - http://wdg.uncc.edu/Microsoft_Office_2007_MIME_Types_for_Apache_and_IIS
docx=application/vnd.openxmlformats-officedocument.wordprocessingml.document
docm=application/vnd.ms-word.document.macroEnabled.12
dotx=application/vnd.openxmlformats-officedocument.wordprocessingml.template
dotm=application/vnd.ms-word.template.macroEnabled.12
xlsx=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsm=application/vnd.ms-excel.sheet.macroEnabled.12
xltx=application/vnd.openxmlformats-officedocument.spreadsheetml.template
xltm=application/vnd.ms-excel.template.macroEnabled.12
xlsb=application/vnd.ms-excel.sheet.binary.macroEnabled.12
xlam=application/vnd.ms-excel.addin.macroEnabled.12
pptx=application/vnd.openxmlformats-officedocument.presentationml.presentation
pptm=application/vnd.ms-powerpoint.presentation.macroEnabled.12
ppsx=application/vnd.openxmlformats-officedocument.presentationml.slideshow
ppsm=application/vnd.ms-powerpoint.slideshow.macroEnabled.12
potx=application/vnd.openxmlformats-officedocument.presentationml.template
potm=application/vnd.ms-powerpoint.template.macroEnabled.12
ppam=application/vnd.ms-powerpoint.addin.macroEnabled.12
sldx=application/vnd.openxmlformats-officedocument.presentationml.slide
sldm=application/vnd.ms-powerpoint.slide.macroEnabled.12
thmx=application/vnd.ms-officetheme
onetoc=application/onenote
onetoc2=application/onenote
onetmp=application/onenote
onepkg=application/onenote
# koffice
# iWork
key=application/x-iwork-keynote-sffkey
kth=application/x-iwork-keynote-sffkth
nmbtemplate=application/x-iwork-numbers-sfftemplate
numbers=application/x-iwork-numbers-sffnumbers
pages=application/x-iwork-pages-sffpages
template=application/x-iwork-pages-sfftemplate
# Extensions for Mozilla apps (Firefox and friends)
xpi=application/x-xpinstall
# Opera extensions
oex=application/x-opera-extension

View File

@@ -1,100 +0,0 @@
package controllers
import (
"github.com/revel/revel"
"os"
fpath "path/filepath"
"strings"
"syscall"
)
type Static struct {
*revel.Controller
}
// This method handles requests for files. The supplied prefix may be absolute
// or relative. If the prefix is relative it is assumed to be relative to the
// application directory. The filepath may either be just a file or an
// additional filepath to search for the given file. This response may return
// the following responses in the event of an error or invalid request;
// 403(Forbidden): If the prefix filepath combination results in a directory.
// 404(Not found): If the prefix and filepath combination results in a non-existent file.
// 500(Internal Server Error): There are a few edge cases that would likely indicate some configuration error outside of revel.
//
// Note that when defining routes in routes/conf the parameters must not have
// spaces around the comma.
// Bad: Static.Serve("public/img", "favicon.png")
// Good: Static.Serve("public/img","favicon.png")
//
// Examples:
// Serving a directory
// Route (conf/routes):
// GET /public/{<.*>filepath} Static.Serve("public")
// Request:
// public/js/sessvars.js
// Calls
// Static.Serve("public","js/sessvars.js")
//
// Serving a file
// Route (conf/routes):
// GET /favicon.ico Static.Serve("public/img","favicon.png")
// Request:
// favicon.ico
// Calls:
// Static.Serve("public/img", "favicon.png")
func (c Static) Serve(prefix, filepath string) revel.Result {
var basePath string
if !fpath.IsAbs(prefix) {
basePath = revel.BasePath
}
basePathPrefix := fpath.Join(basePath, fpath.FromSlash(prefix))
fname := fpath.Join(basePathPrefix, fpath.FromSlash(filepath))
if !strings.HasPrefix(fname, basePathPrefix) {
revel.WARN.Printf("Attempted to read file outside of base path: %s", fname)
return c.NotFound("")
}
finfo, err := os.Stat(fname)
if err != nil {
if os.IsNotExist(err) || err.(*os.PathError).Err == syscall.ENOTDIR {
revel.WARN.Printf("File not found (%s): %s ", fname, err)
return c.NotFound("File not found")
}
revel.ERROR.Printf("Error trying to get fileinfo for '%s': %s", fname, err)
return c.RenderError(err)
}
if finfo.Mode().IsDir() {
revel.WARN.Printf("Attempted directory listing of %s", fname)
return c.Forbidden("Directory listing not allowed")
}
file, err := os.Open(fname)
if err != nil {
if os.IsNotExist(err) {
revel.WARN.Printf("File not found (%s): %s ", fname, err)
return c.NotFound("File not found")
}
revel.ERROR.Printf("Error opening '%s': %s", fname, err)
return c.RenderError(err)
}
return c.RenderFile(file, revel.Inline)
}
// This method allows modules to serve binary files. The parameters are the same
// as Static.Serve with the additional module name pre-pended to the list of
// arguments.
func (c Static) ServeModule(moduleName, prefix, filepath string) revel.Result {
var basePath string
for _, module := range revel.Modules {
if module.Name == moduleName {
basePath = module.Path
}
}
absPath := fpath.Join(basePath, fpath.FromSlash(prefix))
return c.Serve(absPath, filepath)
}

View File

@@ -1,152 +0,0 @@
package controllers
import (
"bytes"
"fmt"
"github.com/revel/revel"
"html"
"html/template"
"reflect"
"strings"
)
type TestRunner struct {
*revel.Controller
}
type TestSuiteDesc struct {
Name string
Tests []TestDesc
}
type TestDesc struct {
Name string
}
type TestSuiteResult struct {
Name string
Passed bool
Results []TestResult
}
type TestResult struct {
Name string
Passed bool
ErrorHtml template.HTML
ErrorSummary string
}
var NONE = []reflect.Value{}
func (c TestRunner) Index() revel.Result {
var testSuites []TestSuiteDesc
for _, testSuite := range revel.TestSuites {
testSuites = append(testSuites, DescribeSuite(testSuite))
}
return c.Render(testSuites)
}
// Run runs a single test, given by the argument.
func (c TestRunner) Run(suite, test string) revel.Result {
result := TestResult{Name: test}
for _, testSuite := range revel.TestSuites {
t := reflect.TypeOf(testSuite).Elem()
if t.Name() != suite {
continue
}
// Found the suite, create a new instance and run the named method.
v := reflect.New(t)
func() {
defer func() {
if err := recover(); err != nil {
error := revel.NewErrorFromPanic(err)
if error == nil {
result.ErrorHtml = template.HTML(html.EscapeString(fmt.Sprint(err)))
} else {
var buffer bytes.Buffer
tmpl, _ := revel.MainTemplateLoader.Template("TestRunner/FailureDetail.html")
tmpl.Render(&buffer, error)
result.ErrorSummary = errorSummary(error)
result.ErrorHtml = template.HTML(buffer.String())
}
}
}()
// Initialize the test suite with a NewTestSuite()
testSuiteInstance := v.Elem().FieldByName("TestSuite")
testSuiteInstance.Set(reflect.ValueOf(revel.NewTestSuite()))
// Call Before(), call the test, and call After().
if m := v.MethodByName("Before"); m.IsValid() {
m.Call(NONE)
}
if m := v.MethodByName("After"); m.IsValid() {
defer m.Call(NONE)
}
v.MethodByName(test).Call(NONE)
// No panic means success.
result.Passed = true
}()
break
}
return c.RenderJson(result)
}
// List returns a JSON list of test suites and tests.
// Used by the "test" command line tool.
func (c TestRunner) List() revel.Result {
var testSuites []TestSuiteDesc
for _, testSuite := range revel.TestSuites {
testSuites = append(testSuites, DescribeSuite(testSuite))
}
return c.RenderJson(testSuites)
}
func DescribeSuite(testSuite interface{}) TestSuiteDesc {
t := reflect.TypeOf(testSuite)
// Get a list of methods of the embedded test type.
super := t.Elem().Field(0).Type
superMethodNameSet := map[string]struct{}{}
for i := 0; i < super.NumMethod(); i++ {
superMethodNameSet[super.Method(i).Name] = struct{}{}
}
// Get a list of methods on the test suite that take no parameters, return
// no results, and were not part of the embedded type's method set.
var tests []TestDesc
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
mt := m.Type
_, isSuperMethod := superMethodNameSet[m.Name]
if mt.NumIn() == 1 &&
mt.NumOut() == 0 &&
mt.In(0) == t &&
!isSuperMethod &&
strings.HasPrefix(m.Name, "Test") {
tests = append(tests, TestDesc{m.Name})
}
}
return TestSuiteDesc{
Name: t.Elem().Name(),
Tests: tests,
}
}
func errorSummary(error *revel.Error) string {
var message = fmt.Sprintf("%4sStatus: %s\n%4sIn %s", "", error.Description, "", error.Path)
if error.Line != 0 {
message += fmt.Sprintf(" (around line %d): ", error.Line)
for _, line := range error.ContextSource() {
if line.IsError {
message += line.Source
}
}
}
return message
}

View File

@@ -1,12 +0,0 @@
package app
import (
"fmt"
"github.com/revel/revel"
)
func init() {
revel.OnAppStart(func() {
fmt.Println("Go to /@tests to run the tests.")
})
}

View File

@@ -1,9 +0,0 @@
<b>{{.Description}}</b><br>
In {{.Path}}
{{if .Line}}
(around {{if .Line}}line {{.Line}}{{end}}{{if .Column}} column {{.Column}}{{end}})
{{end}}:
{{range .ContextSource}}{{if .IsError}}<code>{{.Source}}</code>{{end}}{{end}}<br>
<a style="cursor:pointer;"
onclick="x=this.nextSibling.style;if(!x.display)x.display='none';else x.display=''">
Show Stack</a><pre style="display:none;">{{.Stack}}</pre>

View File

@@ -1,84 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Revel Test Runner</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link href="/@tests/public/css/bootstrap.css" type="text/css" rel="stylesheet"></link>
<script src="/@tests/public/js/jquery-1.9.1.min.js" type="text/javascript"></script>
<style>
header { padding:20px 0; background-color:#ADD8E6 }
.passed td { background-color: #90EE90 !important; }
.failed td { background-color: #FFB6C1 !important; }
.tests td.name, .tests td.result { padding-top: 13px; }
pre { font-size:10px; white-space: pre; }
</style>
</head>
<body>
<header>
<div class="container">
<table><tr><td>
<h1>Test Runner</h1>
<p class="lead">Run all of your application's tests from here.</p>
</td><td style="padding-left:150px;">
<button class="btn btn-large" all-tests="">Run All Tests</button>
</td></tr></table>
</div>
</header>
<div class="container">
{{range .testSuites}}
<p class="lead" style="margin-top:20px;">{{.Name}}</p>
<table class="table table-striped tests" suite="{{.Name}}">
{{range .Tests}}
<tr>
<td class="name">{{.Name}}</td>
<td class="result">
</td>
<td><button test="{{.Name}}" class="btn">Run</button></td>
</tr>
{{end}}
</table>
{{end}}
</div>
<script>
var running = 0;
$("button[test]").click(function() {
var button = $(this).addClass("disabled").text("Running");
running += 1;
runTest(button);
});
$("button[all-tests]").click(function() {
var button = $(this).addClass("disabled").text("Running");
$("button[test]").click();
});
function runTest(button) {
var suite = button.parents("table").attr("suite");
var test = button.attr("test");
var row = button.parents("tr");
var resultCell = row.children(".result");
$.ajax({
dataType: "json",
url: "/@tests/"+suite+"/"+test,
async: false,
success: function(result) {
row.attr("class", result.Passed ? "passed" : "failed");
if (result.Passed) {
resultCell.html("");
} else {
resultCell.html(result.ErrorHtml);
}
button.removeClass("disabled").text("Run");
running -= 1;
if (running == 0) {
$("button[all-tests]").removeClass("disabled").text("Run All Tests");
}
}});
}
</script>
</body>
</html>

View File

@@ -1,47 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Revel Test Runner</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<style>
body {
margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
color: #333333;
background-color: #ffffff;
}
header { padding:20px 0; }
header.passed { background-color: #90EE90 !important; }
header.failed { background-color: #FFB6C1 !important; }
table { margin-top: 20px; padding: 8px; line-height: 20px; }
td { vertical-align: top; padding-right:20px; }
a { color: #0088cc; }
.container { margin-left: auto; margin-right: auto; width: 940px; }
.result.failed b { font-weight:bold; color: #C00; font-size: 14px; }
.result.failed span { color: #C00; font-size: 14px; }
</style>
</head>
<body>
<header class="{{if .Passed}}passed{{else}}failed{{end}}">
<div class="container">
<h1>{{.Name}}</h1>
<p>{{if .Passed}}PASSED{{else}}FAILED{{end}}</p>
</div>
</header>
<div class="container">
<table>
{{range .Results}}
<tr class="result {{if .Passed}}passed{{else}}failed{{end}}">
<td><span>{{.Name}}</span></td>
<td>{{if .ErrorHtml}}{{.ErrorHtml}}{{else}}PASSED{{end}}</td>
</tr>
{{end}}
</table>
</div>
</body>
</html>

View File

@@ -1,4 +0,0 @@
GET /@tests TestRunner.Index
GET /@tests.list TestRunner.List
GET /@tests/public/*filepath Static.ServeModule(testrunner,public)
GET /@tests/:suite/:test TestRunner.Run

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Forbidden</title>
</head>
<body>
{{with .Error}}
<h1>
{{.Title}}
</h1>
<p>
{{.Description}}
</p>
{{end}}
</body>
</html>

View File

@@ -1,4 +0,0 @@
{
"title": "{{js .Error.Title}}",
"description": "{{js .Error.Description}}"
}

View File

@@ -1,3 +0,0 @@
{{.Error.Title}}
{{.Error.Description}}

View File

@@ -1 +0,0 @@
<forbidden>{{.Error.Description}}</forbidden>

View File

@@ -1,63 +0,0 @@
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, Sans;
background: #EEEEEE;
}
.block {
padding: 20px;
border-bottom: 1px solid #aaa;
}
#header h1 {
font-weight: normal;
font-size: 28px;
margin: 0;
}
#more {
color: #666;
font-size: 80%;
border: none;
}
#header {
background: #FFFFCC;
}
#header p {
color: #333;
}
#routes {
background: #f6f6f6;
}
#routes h2 {
font-weight: normal;
font-size: 18px;
margin: 0 0 10px 0;
}
#routes ol {
}
#routes li {
font-size: 14px;
font-family: monospace;
color: #333;
}
</style>
<div id="header" class="block">
{{with .Error}}
<h1>
{{.Title}}
</h1>
<p>
{{.Description}}
</p>
{{end}}
</div>
<div id="routes" class="block">
<h2>These routes have been tried, in this order :</h2>
<ol>
{{range .Router.Routes}}
<li>{{pad .Method 10}}{{pad .Path 50}}{{.Action}}</li>
{{end}}
</ol>
</div>

View File

@@ -1,26 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Not found</title>
</head>
<body>
{{if .DevMode}}
{{template "errors/404-dev.html" .}}
{{else}}
{{with .Error}}
<h1>
{{.Title}}
</h1>
<p>
{{.Description}}
</p>
{{end}}
{{end}}
</body>
</html>

View File

@@ -1,4 +0,0 @@
{
"title": "{{js .Error.Title}}",
"description": "{{js .Error.Description}}"
}

View File

@@ -1,3 +0,0 @@
{{.Error.Title}}
{{.Error.Description}}

View File

@@ -1 +0,0 @@
<notfound>{{.Error.Description}}</notfound>

View File

@@ -1,118 +0,0 @@
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, Sans;
background: #EEEEEE;
}
.block {
padding: 20px;
border-bottom: 1px solid #aaa;
}
#header h1 {
font-weight: normal;
font-size: 28px;
margin: 0;
}
#more {
color: #666;
font-size: 80%;
border: none;
}
#header {
background: #fcd2da;
}
#header p {
color: #333;
}
#source {
background: #f6f6f6;
}
#source h2 {
font-weight: normal;
font-size: 18px;
margin: 0 0 10px 0;
}
#source .lineNumber {
float: left;
display: block;
width: 40px;
text-align: right;
margin-right: 10px;
font-size: 14px;
font-family: monospace;
background: #333;
color: #fff;
}
#source .line {
clear: both;
color: #333;
margin-bottom: 1px;
}
#source pre {
font-size: 14px;
margin: 0;
overflow-x: hidden;
}
#source .error {
color: #c00 !important;
}
#source .error .lineNumber {
background: #c00;
}
#source a {
text-decoration: none;
}
#source a:hover * {
cursor: pointer !important;
}
#source a:hover pre {
background: #FAFFCF !important;
}
#source em {
font-style: normal;
text-decoration: underline;
font-weight: bold;
}
#source strong {
font-style: normal;
font-weight: bold;
}
</style>
{{with .Error}}
<div id="header" class="block">
<h1>
{{.Title}}
</h1>
<p>
{{if .SourceType}}
The {{.SourceType}} <strong>{{.Path}}</strong> does not compile: <strong>{{.Description}}</strong>
{{else}}
{{.Description}}
{{end}}
</p>
</div>
{{if .Path}}
<div id="source" class="block">
<h2>In {{.Path}}
{{if .Line}}
(around {{if .Line}}line {{.Line}}{{end}}{{if .Column}} column {{.Column}}{{end}})
{{end}}
</h2>
{{range .ContextSource}}
<div class="line {{if .IsError}}error{{end}}">
<span class="lineNumber">{{.Line}}:</span>
<pre>{{.Source}}</pre>
</div>
{{end}}
</div>
{{end}}
{{if .MetaError}}
<div id="source" class="block">
<h2>Additionally, an error occurred while handling this error.</h2>
<div class="line error">
{{.MetaError}}
</div>
</div>
{{end}}
{{end}}

View File

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Application error</title>
</head>
<body>
{{if .DevMode}}
{{template "errors/500-dev.html" .}}
{{else}}
<h1>Oops, an error occured</h1>
<p>
This exception has been logged.
</p>
{{end}}
</body>
</html>

View File

@@ -1,4 +0,0 @@
{
"title": "{{js .Error.Title}}",
"description": "{{js .Error.Description}}"
}

View File

@@ -1,15 +0,0 @@
{{.Error.Title}}
{{.Error.Description}}
{{if eq .RunMode "dev"}}
{{with .Error}}
{{if .Path}}
----------
In {{.Path}} {{if .Line}}(around line {{.Line}}){{end}}
{{range .ContextSource}}
{{if .IsError}}>{{else}} {{end}} {{.Line}}: {{.Source}}{{end}}
{{end}}
{{end}}
{{end}}

View File

@@ -1,4 +0,0 @@
<error>
<title>{{.Error.Title}}</title>
<description>{{.Error.Description}}</description>
</error>

View File

@@ -1,69 +0,0 @@
#------------------------
# leanote config
#------------------------
http.port=9000
site.url=http://localhost:9000
# mongdb
db.host=localhost
#db.host=leanote
db.port=27017
db.dbname=leanote_beta2 # required
db.username= # if not exists, please leave it blank
db.password= # if not exists, please leave it blank
# or you can set the mongdb url
# mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
# db.url=mongodb://root:root123@localhost:27017/leanote
#--------------------------------
# revel config
# for dev
#--------------------------------
app.name=leanote
app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y
http.addr=
http.ssl=false
cookie.httponly=false
cookie.prefix=LEANOTE
cookie.domain= # for share cookie with subdomain, 默认为空, 即不设置 不能设置为localhost, 要么就为空
cookie.secure=false
format.date=01/02/2006
format.datetime=01/02/2006 15:04
results.chunked=false
log.trace.prefix = "TRACE "
log.info.prefix = "INFO "
log.warn.prefix = "WARN "
log.error.prefix = "ERROR "
# The default language of this application.
i18n.default_language=en
module.static=github.com/revel/revel/modules/static
[dev]
mode.dev=true
results.pretty=true
watch=true
module.testrunner = github.com/revel/revel/modules/testrunner
log.trace.output = stderr
log.info.output = stderr
log.warn.output = stderr
log.error.output = stderr
[prod]
mode.dev=false
results.pretty=false
watch=false
module.testrunner =
log.trace.output = off
log.info.output = off
log.warn.output = %(app.name)s.log
log.error.output = %(app.name)s.log

View File

@@ -128,6 +128,7 @@ GET /upload/*filepath Static.Serve("public/upload")
* /member MemberIndex.Index
* /member/index MemberIndex.Index
GET /share/note/:noteId Share.ShowShareNote
# common
* /:controller/:action :controller.:action
* /api/:controller/:action :controller.:action