Compare commits

...

185 Commits
dev ... 1.3.1

Author SHA1 Message Date
lealife
cba69444a8 v1.3.1 released 2015-10-30 14:44:57 +08:00
lealife
5e02816c09 v1.3.1 release 2015-10-30 14:41:01 +08:00
lealife
20b085aeae Markdown编辑器中文回车有时不换行 #249 2015-10-30 14:12:32 +08:00
lealife
0f5e683a4f Markdown 笔记无法分辨 hyperlink #240 2015-10-30 14:09:35 +08:00
lealife
e134ca7244 个个中心美化 2015-10-30 14:07:42 +08:00
lealife
6818574605 删除leaui不要文件; file/outputImage 变成 api/file/getImage 2015-10-30 14:05:51 +08:00
lealife
6eea03f81e 自动保存优化, 时间缩短成10s
https://github.com/leanote/leanote/issues/243
2015-10-30 14:02:28 +08:00
lealife
1242834b7f 增加修改与只读的快捷键 ctrl/cmd + e
https://github.com/leanote/leanote/issues/241
2015-10-30 13:57:28 +08:00
lealife
1844513f08 mac下cmd+shift+c 不能调出toggle code
https://github.com/leanote/leanote/issues/248
2015-10-30 13:52:50 +08:00
lealife
b0be7a14d2 ctrl+v 复制图片, 有时不显示, 但上传了
https://github.com/leanote/leanote/issues/227
2015-10-30 13:48:54 +08:00
lealife
7e17c68dba update readme 2015-10-27 23:44:53 +08:00
lealife
6259f15ffa update readme 2015-10-27 23:42:19 +08:00
lealife
f65d027a0d update readme 2015-10-27 23:25:17 +08:00
lealife
e9e2ca50ee update readme 2015-10-27 23:19:56 +08:00
lealife
a64ef8517e update readme 2015-10-27 23:17:15 +08:00
lealife
c9bda60554 v1.3 release 2015-10-27 23:14:02 +08:00
lealife
c3478520c0 batch release 2015-10-27 23:08:00 +08:00
lealife
8346ffe915 batch release
https://github.com/leanote/leanote/issues/238
2015-10-27 23:07:49 +08:00
lealife
f49b046601 Merge branch 'feature-batch' 2015-10-27 23:06:51 +08:00
lealife
263c03291e batch ok 2015-10-27 23:04:39 +08:00
lealife
29244c247e lit indent 2015-10-20 09:28:03 +08:00
lealife
320de8637f v1.2 update initial db data 2015-10-18 20:34:44 +08:00
lealife
33bc6bf84b v1.2 update initial data 2015-10-18 20:34:31 +08:00
lealife
0cd2c42b68 v1.2 release 2015-10-18 00:16:54 +08:00
lealife
1716acf9e0 release v1.2 for export pdf 2015-10-17 23:46:09 +08:00
lealife
45477119d4 Merge branch 'feature-pdf' 2015-10-17 23:23:36 +08:00
lealife
716fbeae0c update wkhtmltopdf link 2015-10-17 23:22:40 +08:00
lealife
103891e0c0 wkhtmltopdf configuration 2015-10-17 19:36:49 +08:00
lealife
638b5b51e0 export pdf ok 2015-10-17 17:15:33 +08:00
lealife
90fea722aa 笔记内拖动图片, release 2015-10-17 15:55:14 +08:00
lealife
90ce35327f 笔记内拖动图片, 拖动上传图片
https://github.com/leanote/leanote/issues/231
2015-10-17 15:53:35 +08:00
lealife
60e3b9446a 空白笔记前有一个空格 2015-10-17 15:51:21 +08:00
lealife
16a726223c 富文本编辑器不支持 多层级列表
https://github.com/leanote/leanote/issues/234
2015-10-17 15:42:40 +08:00
lealife
dfe45f39bb i18n 2015-10-14 19:03:41 +08:00
lealife
413be4a6d0 markdown editor, image manager, [ok] 2015-10-14 18:53:29 +08:00
lealife
6a35f98f89 image manager optimization, i18n 2015-10-14 18:47:01 +08:00
lealife
ad295d97c8 markdown editor i18n 2015-10-14 18:38:08 +08:00
lealife
cf8171c9cd member upload avatar 2015-10-12 21:18:01 +08:00
lealife
c616a4e9a6 download attach 2015-10-12 21:08:29 +08:00
lealife
04d641a5ad download attach 2015-10-12 21:03:51 +08:00
lealife
ec68570a38 attach list 2015-10-12 20:55:08 +08:00
lealife
407b44382c avatar upload 2015-10-12 19:44:43 +08:00
lealife
71a2bcc7f0 v1.1 Proudly powered by <a href="https://leanote.com">Leanote</a> 2015-10-12 12:15:53 +08:00
lealife
a2dd09725c v1.1 update theme <div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div> 2015-10-12 12:09:44 +08:00
lealife
f757e00f03 Merge branch 'sync-from-leanote.com' 2015-10-10 18:10:41 +08:00
lealife
b7652bb321 opt, fix 1.1 2015-10-10 18:10:26 +08:00
lealife
e6018d32ec tinymce 复制纯文本bug 2015-10-10 17:41:18 +08:00
lealife
cb7e272bbe leanote v1.1 release 2015-10-10 17:23:27 +08:00
lealife
a4d9d8e0d3 leanote v1.1 build 2015-10-10 16:55:36 +08:00
lealife
c844c8b188 复制外链图片, 复制到leanote 2015-10-10 16:18:26 +08:00
lealife
320263eefa 笔记信息, 历史记录, 提示, 附件优化
https://github.com/leanote/leanote/issues/224
2015-10-10 16:10:54 +08:00
lealife
274875c6c4 note dev 优化 2015-10-10 15:24:26 +08:00
lealife
1ea35324c1 内容的IsBlog没有note IsBlog一致, 导致前端判断有问题 2015-10-10 15:20:34 +08:00
lealife
3e20ad3cdd CPU性能过高优化, 字符串多次连接导致
https://github.com/leanote/leanote/issues/223
2015-10-10 15:06:48 +08:00
lealife
711e6b3d17 注册邮箱支持多个"_", 如 a__a@a.com 2015-10-10 15:02:55 +08:00
lealife
0438bbb414 email 不让修改! 2015-10-10 14:53:50 +08:00
lealife
70ee362cc8 db connection lost #222 2015-10-10 14:49:02 +08:00
lealife
0479f1a433 attach bug 2015-10-10 14:42:49 +08:00
lealife
7e01cb8227 防止blog urlTitle 无限循环 2015-10-10 14:40:52 +08:00
lealife
ae0eb3b918 get all attachs 权限未验证 2015-10-10 14:38:18 +08:00
lealife
097d2709e2 tag标题不能为空 2015-10-10 14:33:40 +08:00
lealife
f1e56272ef 修改email只能小写 2015-10-10 14:32:30 +08:00
lealife
db4cfbf605 已删除的笔记还显示 #226 2015-10-10 14:28:53 +08:00
lealife
da7d31fa00 files, upload下文件过多
https://github.com/leanote/leanote/issues/225
2015-10-10 14:12:22 +08:00
lealife
62bc74d3c6 update msg 2015-10-10 13:51:42 +08:00
lealife
4467689ec3 add debug mode 2015-10-10 13:46:58 +08:00
life
69a874d90d Merge pull request #214 from nosqldb/master
compress images
2015-10-07 00:48:30 +08:00
lealife
b259a00c94 update default conf 2015-10-07 00:29:29 +08:00
lealife
a0088ead9e update default conf 2015-10-07 00:28:35 +08:00
duoyun
228fd80abd compress images 2015-09-21 21:39:28 +08:00
lealife
8b3a1a646a update travis 2015-09-18 13:48:06 +08:00
lealife
c4954b94b8 rmove testrunner 2015-09-18 13:44:25 +08:00
lealife
2b56ebd620 update travis 2015-09-18 13:28:51 +08:00
lealife
a78d95ddad update travis 2015-09-18 13:10:23 +08:00
lealife
938362154a update travis 2015-09-18 13:06:04 +08:00
lealife
8bff2b7000 delete session 2015-09-18 12:55:57 +08:00
lealife
c56f228646 delete memcache 2015-09-18 12:21:32 +08:00
lealife
98f0313e3e update travis 2015-09-18 12:17:07 +08:00
lealife
bde2e891ef update travis 2015-09-18 11:38:55 +08:00
lealife
7acdaede0d update travis 2015-09-18 11:35:17 +08:00
lealife
45d1b4bee3 add cmd to gen routes.go, main.go 2015-09-18 11:30:38 +08:00
lealife
4d0a170f01 update travis 2015-09-18 11:23:00 +08:00
lealife
5e37e5a37d add adminUsername on app.conf 2015-09-17 18:52:10 +08:00
lealife
a60da3d592 1.0 版在网页端删除笔记本出错
https://github.com/leanote/leanote/issues/207
2015-09-17 17:14:57 +08:00
lealife
9a88fab84e fix demo 2015-09-17 12:30:57 +08:00
lealife
4981d6aad4 move tests to app/tests 2015-09-16 11:14:37 +08:00
life
7193ac6833 Merge pull request #206 from duoyun/patch-1
use english  double quote
2015-09-14 21:57:36 +08:00
Cyrus.Chu
3d1a32764d use english double quote 2015-09-14 09:03:02 +08:00
lealife
2b8df3ed76 update travis 2015-09-13 21:53:01 +08:00
lealife
e94b004a5a update travis 2015-09-13 21:43:47 +08:00
lealife
6cc6f26fa1 travis 2015-09-13 21:40:50 +08:00
lealife
120cdd53c3 update travis 2015-09-13 21:34:30 +08:00
lealife
435aac4a9c https://github.com/leanote/leanote/issues/205 2015-09-13 21:23:58 +08:00
lealife
f234cf285d update travis 2015-09-11 18:41:47 +08:00
lealife
54953b69c6 add travis status 2015-09-11 18:40:08 +08:00
lealife
60878b7a65 ad travis 2015-09-11 18:36:51 +08:00
lealife
dada88d8a2 add travis 2015-09-11 18:34:38 +08:00
lealife
7167705409 add db/auth test 2015-09-11 18:31:37 +08:00
life
4217abca6e Merge pull request #203 from duoyun/master
remove unused func LoginGetUserInfo
2015-09-11 17:40:16 +08:00
duoyun
49a97f2285 remove unused func LoginGetUserInfo 2015-09-10 20:36:22 +08:00
lealife
4f2d7b8cd0 get mongodb configuration from env 2015-09-09 10:10:24 +08:00
lealife
afbda7bfb2 reset conf 2015-09-07 22:20:53 +08:00
lealife
c568756d16 使用Crypto加密, 找回密码, 修改密码修复 2015-09-07 15:39:46 +08:00
life
5794f76b0a Merge pull request #195 from duoyun/master
change md5 to bcrypt to strengthen  password hash and keep md5
2015-09-07 15:02:25 +08:00
life
74a768c0fb Merge pull request #196 from bencevans/patch-1
English Translation Updates
2015-09-07 14:58:13 +08:00
Ben Evans
4136060825 English Translation UnTitled -> Untitled 2015-09-07 00:16:50 +01:00
Ben Evans
e249a953cd English Translation Update 2015-09-07 00:04:56 +01:00
Ben Evans
eddc2e6356 English Translation Updates 2015-09-07 00:04:18 +01:00
duoyun
1604474d6e mv crypto to lea 2015-09-07 00:29:02 +08:00
duoyun
bbaf71481c use bcrypt and keep Md5 2015-09-06 23:16:56 +08:00
lealife
952117818c delete unused 2015-09-06 10:36:10 +08:00
lealife
a31432f1bf update package.json 2015-09-05 23:56:12 +08:00
lealife
b0afe25730 gulp build 2015-09-05 23:48:41 +08:00
lealife
024e0e9bae ace editor 最后没有元素, 或者元素不是p, 则在最后插入之 2015-09-05 23:46:02 +08:00
lealife
dbabc3a5a8 use gulp to build leanote front-end 2015-09-05 23:42:10 +08:00
lealife
a30aec8254 界面优化 2015-09-05 23:30:31 +08:00
lealife
7440218974 界面优化 2015-09-05 23:30:24 +08:00
life
b3c771a87f Merge pull request #182 from ZhuangER/yu-huang
fix a nav bar's bug
2015-09-05 22:47:34 +08:00
ZhuangEr
8f8451cdff fix iss183 2015-08-31 14:15:52 -04:00
yu huang
6d7fe83469 move script from footer.js
have fixed bug of multiple active of email and send to users.
also make URL like "http://localhost:9000/admin/t?p=0&t=email/sendToUsers" work.
2015-08-29 19:19:11 -04:00
yu huang
05a5280162 move inline script code to admin.js 2015-08-29 19:15:08 -04:00
yu huang
8dd55eb949 Update footer.html 2015-08-29 19:13:54 -04:00
life
5838cc218f Merge pull request #180 from ZhuangER/yu-huang
Fix a nav bar's bug
2015-08-29 13:23:09 +08:00
yu huang
2e52c957e0 Update nav.html 2015-08-28 20:10:05 -04:00
yu huang
9486c58d78 Update nav.html 2015-08-28 20:04:57 -04:00
lealife
6e7c1e0d41 add chat room 2015-08-27 10:25:05 +08:00
lealife
6a0a60a644 update readme 2015-08-21 23:52:28 +08:00
lealife
94f9c2c3cb update readme 2015-08-20 23:42:12 +08:00
lealife
37e7ed8d14 update readme 2015-08-20 23:37:55 +08:00
lealife
4227c80ec7 Merge branch 'master' of github.com:leanote/leanote
merge it
2015-08-20 23:34:46 +08:00
lealife
6ec6ef698b add qq group2 2015-08-20 23:30:46 +08:00
life
0543dd015e Merge pull request #159 from alexsourcerer/patch-6
Update msg.fr
2015-07-13 18:22:28 +08:00
alexsourcerer
b88f895acf Update msg.fr
I made some translation mistakes I did not see before.
2015-06-16 12:52:56 +02:00
lealife
fc517700cc v1.0 release.sh 2015-06-15 19:47:22 +08:00
lealife
b5e0cd31dd v1.0 reset app.conf 2015-06-15 19:13:59 +08:00
lealife
ab4b8b77a3 v1.0 use revel 0.12 2015-06-15 19:13:00 +08:00
lealife
04c339896b v1.0 preview 2015-06-15 18:55:33 +08:00
lealife
e37056737d v1.0 preview 2015-06-15 18:55:07 +08:00
lealife
89f8bb934c v1.0 initial db data 2015-06-15 18:53:10 +08:00
lealife
b564989214 v1.0 initial db data 2015-06-15 18:53:02 +08:00
lealife
03aa8edd58 v1.0 french lang 2015-06-15 18:24:28 +08:00
lealife
803fc90dd9 v1.0 remove unused code 2015-06-15 18:07:52 +08:00
lealife
6987a38820 v1.0
只读模式
group, 分享
评论更多问题
博客标签总是存在一个
2015-06-15 18:01:48 +08:00
lealife
7e458bb433 分享给项目组无法输出已设置项目组 #143 2015-05-21 20:49:58 +08:00
lealife
b391375008 font-awesome.css找不到 #140 2015-05-14 19:23:56 +08:00
lealife
3f89e1cf83 update readme 2015-05-10 15:10:56 +08:00
lealife
9f5dc8a0e2 update readme 2015-05-10 15:07:51 +08:00
lealife
12ea17e89f 编译之前修改的 2015-05-07 00:13:04 +08:00
lealife
1fd6610fa3 #133 修改默认主题, 更新markdown解析器 2015-05-07 00:02:54 +08:00
lealife
1ec28d80c8 添加markdown解析器 for blog 博客与note的markdown解析器不一致 #133 2015-05-06 23:54:44 +08:00
lealife
fcbc23e964 Ace编辑器出现XXXXXXX #132 2015-05-06 23:54:10 +08:00
lealife
6fba2a6416 test for commit 2015-05-06 23:39:56 +08:00
life
0e5edb16f6 #131 2015-05-06 23:32:45 +08:00
life
5726df1a81 update read 2015-05-03 12:32:16 +08:00
life
f3a0a786c5 release 2015-04-20 20:51:03 +08:00
life
fa08b3393a restore dbname 2015-04-20 19:52:13 +08:00
life
5db148be9f release for windows 2015-04-20 19:51:20 +08:00
life
d03cce2761 initial db data fixed 2015-04-08 14:01:34 +08:00
life
3a0bdd803d initial data fixed 2015-04-08 14:01:24 +08:00
life
d1b6dddfd0 readme 2015-04-07 20:34:09 +08:00
life
2676c5a85d Readme 2015-04-07 20:32:50 +08:00
life
bde731d84c Merge pull request #108 from goodbest/patch-3
fix restore/backup bug: mongo -port to --port
2015-04-03 00:32:49 +08:00
life
46bc714b45 Merge pull request #109 from goodbest/patch-4
When admin username changes, this link is dead
2015-04-02 16:20:18 +08:00
goodbest
7269226422 When admin username changes, this link is dead 2015-04-02 16:05:08 +08:00
life
c390d12dd2 update controller 2015-04-02 13:58:02 +08:00
goodbest
68eaac9622 mongo3 dont't have --directoryperdb option;
Just append dumpPath by the end of the command is capable for both mongo2 and 3
2015-04-02 10:43:46 +08:00
goodbest
a089292461 fix restore/backup bug: mongo -port to --port 2015-04-02 10:18:59 +08:00
life
b59eba4ca4 Merge pull request #104 from Refactoring/master
实现组内人员可分享内容至这个组
2015-04-02 09:43:12 +08:00
life
8e52074346 Merge pull request #105 from goodbest/patch-2
add missing key for notebook
2015-04-02 09:38:08 +08:00
goodbest
4fb7e8dbaa add missing key for notebook 2015-04-01 12:54:37 +08:00
Refactoring
8ed260976a 删除无用的.svn文件夹 2015-04-01 11:10:40 +08:00
Refactoring
d8dac67913 Merge remote-tracking branch 'masterfork/master'
Conflicts:
	app/lea/Util.go
	public/js/common-min.js
	public/js/common.js
2015-04-01 08:55:08 +08:00
life
ade77bba58 Update app.conf 2015-03-31 18:34:45 +08:00
life
849118f9b8 update api doc 2015-03-31 18:27:53 +08:00
life
f0b45deaec Merge branch 'dev' 2015-03-31 18:17:57 +08:00
life
801263f50c Merge branch 'dev' 2015-03-31 18:13:21 +08:00
life
f38a86cb07 v1.0-beta.4 fix conflict 2015-03-31 18:00:49 +08:00
life
f755574576 Merge branch 'dev'
Conflicts:
	conf/app.conf
2015-03-31 17:59:16 +08:00
Refactoring
e186cf984c #96 实现组内的任何一个人都可以分享内容到这个组中.
存在不足:共享内容以分享者进行归类组织.  更好的内容组织方式:共享给组的以组归类组织,共享给个人的以分享者个人组织
2015-03-30 20:19:48 +08:00
Refactoring
0f7ebb6c53 fix bug: Email validation missing contains "-"
example: name1-name2@site.com, name@site1-site2.com
2015-03-27 14:56:37 +08:00
leanote
257a141519 Update README_zh.md 2015-03-05 17:27:15 +08:00
leanote
12bc62e990 Update README.md 2015-03-05 17:26:24 +08:00
leanote
fbeecdf062 Update README.md 2015-03-05 17:25:20 +08:00
leanote
1e0fd166db Update README.md
Leanote V1.0.3-beta bin url
2015-03-05 17:24:29 +08:00
life
9803249ab1 Update app.conf 2015-01-13 20:04:05 +08:00
1998 changed files with 149872 additions and 134567 deletions

5
.gitignore vendored
View File

@@ -2,9 +2,9 @@
pkg
bin2
bin/i18n
bin/leanote-linux
bin/leanote-mac
bin/leanote*
bin/release
bin/test.sh
bin/tmp
bin/test
bin/src
@@ -16,3 +16,4 @@ app/tmp/main.go
.project
public/config.codekit
files
/node_modules

37
.travis.yml Normal file
View File

@@ -0,0 +1,37 @@
language: go
go: 1.3
services:
- mongodb # 2.4.12
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 github.com/revel/moudle/revel
# - go install github.com/revel/cmd/revel
- pwd
- ls
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
- go build -o leanote github.com/leanote/leanote/app/tmp
# run with port 9000
- ./leanote -importPath=github.com/leanote/leanote -runMode=dev -port=9000 &
- sleep 10s;
# test
- curl http://localhost:9000
- curl http://localhost:9000/blog
- curl http://localhost:9000/login
- curl http://localhost:9000/demo
# - revel build github.com/leanote/leanote tmp
# OK

345
Gulpfile.js Normal file
View File

@@ -0,0 +1,345 @@
var gulp = require('gulp');
var clean = require('gulp-clean');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var minifyHtml = require("gulp-minify-html");
var concat = require('gulp-concat');
var replace = require('gulp-replace');
var inject = require('gulp-inject');
var gulpSequence = require('gulp-sequence');
var fs = require('fs');
var leanoteBase = './';
var base = leanoteBase + '/public'; // public base
var noteDev = leanoteBase + '/app/views/note/note-dev.html';
var noteProBase = leanoteBase + '/app/views/note';
// 合并Js, 这些js都是不怎么修改, 且是依赖
// 840kb, 非常耗时!!
gulp.task('concatDepJs', function() {
var jss = [
'js/jquery-1.9.0.min.js',
'js/jquery.ztree.all-3.5-min.js',
'tinymce/tinymce.full.min.js', // 使用打成的包, 加载速度快
// 'libs/ace/ace.js',
'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',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
// .pipe(uglify()) // 压缩
.pipe(concat('dep.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// 合并app js 这些js会经常变化 90kb
gulp.task('concatAppJs', function() {
var jss = [
'js/common.js',
'js/app/note.js',
'js/app/page.js', // 写作模式下, page依赖note
'js/app/tag.js',
'js/app/notebook.js',
'js/app/share.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('app.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// 合并requirejs和markdown为一个文件
gulp.task('concatMarkdownJs', function() {
var jss = [
'js/require.js',
'dist/main.min.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('markdown.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// note-dev.html -> note.html, 替换css, js
// TODO 加?t=2323232, 强制浏览器更新, 一般只需要把app.min.js上加
gulp.task('devToProHtml', function() {
return gulp
.src(noteDev)
.pipe(replace(/<!-- dev -->[.\s\S]+?<!-- \/dev -->/g, '')) // 把dev 去掉
.pipe(replace(/<!-- pro_dep_js -->/, '<script src="/js/dep.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_app_js -->/, '<script src="/js/app.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_markdown_js -->/, '<script src="/js/markdown.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_tinymce_init_js -->/, "var tinyMCEPreInit = {base: '/public/tinymce', suffix: '.min'};")) // 替换
.pipe(replace(/plugins\/main.js/, "plugins/main.min.js")) // 替换
// 连续两个空行换成一个空行, 没用
.pipe(replace(/\n\n/g, '\n'))
.pipe(replace(/\n\n/g, '\n'))
.pipe(replace('console.log(o);', ''))
// .pipe(minifyHtml()) // 不行, 压缩后golang报错
.pipe(rename('note.html'))
.pipe(gulp.dest(noteProBase));
});
// Get used keys
// 只获取需要js i18n的key
var path = require('path');
gulp.task('i18n', function() {
var keys = {};
var reg = /getMsg\(["']+(.+?)["']+/g;
function getKey(data) {
while(ret = reg.exec(data)) {
keys[ret[1]] = 1;
}
}
// 先获取需要的key
function ls(ff) {
var files = fs.readdirSync(ff);
for(fn in files) {
var fname = ff + path.sep + files[fn];
var stat = fs.lstatSync(fname);
if(stat.isDirectory() == true) {
ls(fname);
}
else {
if ((fname.indexOf('.html') > 0 || fname.indexOf('.js') > 0)) {
// console.log(fname);
// if (fname.indexOf('min.js') < 0) {
var data = fs.readFileSync(fname, "utf-8");
// 得到getMsg里的key
getKey(data);
// }
}
}
}
}
console.log('parsing used keys');
ls(base + '/admin');
ls(base + '/blog');
ls(base + '/dist');
ls(base + '/js');
ls(base + '/album');
ls(base + '/libs');
ls(base + '/member');
ls(base + '/tinymce');
console.log('parsed');
// msg.zh
function getAllMsgs(fname) {
var msg = {};
var data = fs.readFileSync(fname, "utf-8");
var lines = data.split('\n');
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];
// 忽略注释
if (line[0] == '#' || line[1] == '#') {
continue;
}
var lineArr = line.split('=');
if (lineArr.length >= 2) {
var key = lineArr[0];
lineArr.shift();
msg[key] = lineArr.join('=');
// msg[lineArr[0]] = lineArr[1];
}
}
return msg;
}
// msg.zh, msg.js
function genI18nJsFile(fromFilename, keys) {
var msgs = getAllMsgs(leanoteBase + '/messages/' + fromFilename);
var toFilename = fromFilename + '.js';
var toMsgs = {};
for (var i in msgs) {
// 只要需要的
if (i in keys) {
toMsgs[i] = msgs[i];
}
}
var str = 'var MSG=' + JSON.stringify(toMsgs) + ';';
str += 'function getMsg(key, data) {var msg = MSG[key];if(msg) {if(data) {if(!isArray(data)) {data = [data];}' +
'for(var i = 0; i < data.length; ++i) {' +
'msg = msg.replace("%s", data[i]);' +
'}' +
'}' +
'return msg;' +
'}' +
'return key;' +
'}';
// 写入到文件中
fs.writeFile(base + '/js/i18n/' + toFilename, str);
}
genI18nJsFile('msg.zh', keys);
genI18nJsFile('msg.en', keys);
genI18nJsFile('msg.fr', keys);
genI18nJsFile('blog.zh', keys);
genI18nJsFile('blog.en', keys);
genI18nJsFile('blog.fr', keys);
});
// 合并album需要的js
gulp.task('concatAlbumJs', function() {
/*
gulp.src(base + '/album/js/main.js')
.pipe(uglify()) // 压缩
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest(base + '/album/js/'));
*/
gulp.src(base + '/album/css/style.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/album/css'));
var jss = [
'js/jquery-1.9.0.min.js',
'js/bootstrap-min.js',
'js/plugins/libs-min/fileupload.js',
'js/jquery.pagination.js',
'album/js/main.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('main.all.js'))
.pipe(gulp.dest(base + '/album/js'));
});
// plugins压缩
gulp.task('plugins', function() {
gulp.src(base + '/js/plugins/libs/*.js')
.pipe(uglify()) // 压缩
// .pipe(concat('main.min.js'))
.pipe(gulp.dest(base + '/js/plugins/libs-min'));
// 所有js合并成一个
var jss = [
'note_info',
'tips',
'history',
'attachment_upload',
'editor_drop_paste',
'main'
];
for(var i in jss) {
jss[i] = base + '/js/plugins/' + jss[i] + '.js';
}
gulp.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('main.min.js'))
.pipe(gulp.dest(base + '/js/plugins'));
});
// mincss
var minifycss = require('gulp-minify-css');
gulp.task('minifycss', function() {
gulp.src(base + '/css/bootstrap.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css'));
gulp.src(base + '/css/font-awesome-4.2.0/css/font-awesome.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/font-awesome-4.2.0/css'));
gulp.src(base + '/css/zTreeStyle/zTreeStyle.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/zTreeStyle'));
gulp.src(base + '/dist/themes/default.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/dist/themes'));
gulp.src(base + '/js/contextmenu/css/contextmenu.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/js/contextmenu/css'));
// theme
// 用codekit
var as = ['default', 'simple', 'writting', /*'writting-overwrite', */ 'mobile'];
/*
for(var i = 0; i < as.length; ++i) {
gulp.src(base + '/css/theme/' + as[i] + '.css')
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/theme'));
}
*/
});
// tinymce
// please set the right path on your own env
var tinymceBase = '/Users/life/leanote/leanote-tools/tinymce_4.1.9_leanote_public';
gulp.task('tinymce', function() {
// 先清理
fs.unlink(tinymceBase + '/js/tinymce/tinymce.dev.js');
fs.unlink(tinymceBase + '/js/tinymce/tinymce.jquery.dev.js');
fs.unlink(tinymceBase + '/js/tinymce/tinymce.full.js');
fs.unlink(tinymceBase + '/js/tinymce/tinymce.full.min.js');
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';
// build
cp.exec('grunt minify', {cwd: tinymceBase}, function(err, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
// 将所有都合并成一起
cp.exec(bundleCmd, {cwd: tinymceBase}, function(err, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});
});
});
// 合并css, 无用
// Deprecated
gulp.task('concatCss', function() {
return gulp
.src([markdownRaw + '/css/default.css', markdownRaw + '/css/md.css'])
.pipe(concat('all.css'))
.pipe(gulp.dest(markdownMin));
});
gulp.task('concat', ['concatDepJs', 'concatAppJs', 'concatMarkdownJs']);
gulp.task('html', ['devToProHtml']);
gulp.task('default', ['concat', 'plugins', 'minifycss', 'i18n', 'concatAlbumJs', 'html']);

View File

@@ -1,6 +1,6 @@
leanote - you own cloud note!
LEANOTE - NOT JUST A NOTEPAD!
Copyright 2014 by the contributors
Copyright 2014-2015 by the contributors
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,7 +12,7 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
leanote is licensed under the GPL v2.
Leanote is licensed under the GPL v2.
life(life@leanote.com, lifephp@gmail.com)

206
README.md
View File

@@ -1,113 +1,167 @@
# Leanote
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.org/leanote/leanote.svg)](https://travis-ci.org/leanote/leanote)
## 1. Introduction
Leanote, not just a notebook!
Leanote, Not Just A Notepad!
![leanote.png](leanote.png "")
**Some Features**
* Knowledge: Manage your knowledge in leanote. leanote contains the tinymce editor and a markdown editor, just enjoy yourself writing.
* Share: Share your knowledge with your friends in leanote. You can invite your friends to join your notepad in the cloud so you can share knowledge.
* Knowledge: Manage your knowledge in Leanote. Leanote contains the tinymce editor and a markdown editor, just enjoy yourself writing.
* Share: Share your knowledge with your friends in Leanote. You can invite your friends to join your notepad in the cloud so you can share knowledge.
* Cooperation: Collaborate with friends to improve your skills.
* Blog: Publish your knowledge and make leanote your blog.
* Blog: Publish your knowledge and make Leanote your blog.
## 2. Why we created leanote
**Other Features**
* Support Markdown
* Writting mode
* Export note to PDF
* Note batch operation
* Custom theme for blog
## 2. Why we created Leanote
To be honest, our inspiration comes from Evernote. We use Evernote to manage our knowledge everyday. But we find that:
* Evernote's editor can't meet our needs, it does not have document navigation, it does not render code properly (as a programmer, syntax highlighted code rendering is a basic need), it cannot resize images and so forth
* We like markdown, but Evernote does not support it.
* We want to share our knowledge, so all of us have our blogs (e.g. on Wordpress) and our Evernote accounts, but why can not those two be one!
* ......
## 3. How to install leanote
## 3. How to install Leanote
More information about how to install leanote please see:
* [leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
Leanote contains: Leanote Web & Server (this repository), [Desktop app](https://github.com/leanote/desktop-app), [iOS](https://github.com/leanote/leanote-ios), [Android](https://github.com/leanote/leanote-android). And all the products are open source!
### 3.1. Download leanote
You can install Leanote on your server, and use Leanote App (Desktop, iOs, Android) to sync notes with your self-hosted server.
Leanote V1.0.2-beta has been released. Binaries:
Welcome to sign up on https://leanote.com, Leanote team provide a stable and reliable service for you.
* Linux: [leanote-linux-x86_64.v1.0-beta.2.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-linux-x86_64.v1.0-beta.2.bin.tar.gz)
* MacOS X: [leanote-mac-x86_64.v1.0-beta.2.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-mac-x86_64.v1.0-beta.bin.2.tar.gz)
More information about how to install Leanote please see:
Or you can clone [Leanote bin repository](https://github.com/leanote/leanote-bin) (Recommend)
* [Leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [Leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
### 3.2. Install MongoDB
Leanote is written in go using [revel](https://revel.github.io/) and [MongoDB](https://www.mongodb.org). Thus, you need to first install MongoDB.
For more tips please have a look at [our wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
### 3.3. Import initial MongoDB data
The mongodb data is in `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
```
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
```
The initial database contains two users:
```
user2 username: admin, password: abc123 (administrator)
user3 username: demo, password: demo@leanote.com (this user is for demo)
```
### 3.4. Configuration
Modify `[PATH_TO_LEANOTE]/conf/app.conf`. Available configuration options are:
``mongodb`` **required**
```Shell
db.host=localhost
db.port=27017
db.dbname=leanote
db.username=
db.password=
```
``app.secret`` **required** **important**
The secret key used for cryptographic operations (revel.Sign).
FOR SECURITY, YOU MUST CHANGE IT!!
For more infomation please see `app/app.conf` and the [revel manuals](https://revel.github.io/)
### 3.5. Run leanote
```
$> cd PATH_TO_LEANOTE/bin
$> sudo sh run.sh
```
## 4. How to develop leanote
## 4. How to develop Leanote
Please see [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. Docs
* [Leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [Leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
## 5. Contributors
More docs please see [wiki](https://github.com/leanote/leanote/wiki).
## 6. Contributors
Thank you to all the [contributors](https://github.com/leanote/leanote/graphs/contributors) on
this project. Your help is much appreciated.
## 6. Contributing
## 7.Join us
Please fork this repository and contribute back using [pull requests](https://github.com/leanote/leanote/pulls).
## Docs
* [leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
* [leanote blog theme api](https://github.com/leanote/leanote/wiki/leanote-blog-theme-api_en)
If you find some problems or has some good ideas, please submit [issues](https://github.com/leanote/leanote/issues).
## Discussion
* [leanote bbs](http://bbs.leanote.com)
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)
* QQ Group: 158716820
You are always welcomed!
## 8. Donation
Support us, [donate us](http://leanote.org/#donate). And thanks [donators](http://leanote.leanote.com/post/leanote-donation-list).
## 9. Related projects
* [Leanote Desktop App](https://github.com/leanote/desktop-app), [Download](http://app.leanote.com)
* [Leanote iOS](https://github.com/leanote/leanote-ios), [Download From App Store](https://itunes.apple.com/en/app/leanote/id1022302858?mt=8)
* [Leanote Android](https://github.com/leanote/leanote-android), development phase
Yare are welcome to join us.
## 9. Support & Join us
* Email: leanote@leanote.com
* [Leanote BBS](http://bbs.leanote.com)
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
* QQ Group: 256076853, 158716820
-----------------------------------------------------------------------
[中文](README_zh.md)
# Leanote
## 1. 介绍
Leanote, 不只是笔记!
**特性**
* 知识管理: 通过Leanote来管理知识, Leanote有易操作的界面, 包含两款编辑器富文本编辑器和Markdown编辑器. 在Leanote, 你可以尽情享受写作.
* 博客: Leanote也可以作为你的博客, 将知识公开成博客, 让Leanote把你的知识传播的更远!
* 分享: 你也可以通过分享知识给好友, 让好友拥有你的知识.
* 协作: 在分享的同时也可以与好友一起协作知识.
**一些其它特性**
* 支持Markdown编辑
* 写作模式
* 支持PDF导出
* 支持批量操作
* 博客自定义主题, 实现高度定制化
## 2. 为什么我们要创建Leanote?
说实话, 我们曾是evernote的忠实粉丝, 但是我们也发现evernote的不足:
* evernote的编辑器不能满足我们的需求, 不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放.
* 我们是markdown的爱好者, 可是evernote竟然没有.
* 我们也想将知识公开, 所以我们有自己的博客, 如wordpress, 但为什么这两者不能合二为一呢?
* 还有...
## 3.安装Leanote
Leanote云笔记产品包括: Leanote Web & Server(即本仓库), 桌面客户端, ios, android. 4端全部开源! 你可以下载它安装在自己的服务器上, Leanote的客户端可以连接自建的服务.
欢迎在 https://leanote.com 上注册, Leanote团队为你提供稳定可靠的服务.
这里详细整理了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/leanote/leanote-android), 开发阶段
欢迎加入我们!
## 联系&加入我们
* Email: leanote@leanote.com
* [Leanote 社区](http://bbs.leanote.com)
* QQ群: 256076853, 158716820(已满)
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)

View File

@@ -1,113 +1,73 @@
# Leanote
# Leanote<74><65>Ʒ
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.org/leanote/leanote.svg)](https://travis-ci.org/leanote/leanote)
## 1. 介绍
## 1. <20><><EFBFBD><EFBFBD>
Leanote, <20><>ֻ<EFBFBD>DZʼ<C7B1>!
Leanote, 不只是笔记!
![leanote.png](leanote.png "")
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>**
**特性**
* ֪ʶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>: ͨ<><CDA8>leanote<74><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֪ʶ, leanote<74><65><EFBFBD>ײ<EFBFBD><D7B2><EFBFBD><EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0BCAD>tinymce<EFBFBD><EFBFBD>markdown. <EFBFBD><EFBFBD>leanote, <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ծ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><EFBFBD>.
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20><>Ҳ<EFBFBD><D2B2><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֪ʶ<D6AA><CAB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20>ú<EFBFBD><C3BA><EFBFBD>ӵ<EFBFBD><D3B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֪ʶ.
* Э<EFBFBD><EFBFBD>: <20>ڷ<EFBFBD><DAB7><EFBFBD><EFBFBD><EFBFBD>ͬʱҲ<CAB1><D2B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>Э<EFBFBD><D0AD>֪ʶ.
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>: leanoteҲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD>IJ<EFBFBD><EFBFBD><EFBFBD>, <20><>֪ʶ<D6AA><CAB6><EFBFBD><EFBFBD><EFBFBD>ɲ<EFBFBD><C9B2><EFBFBD>, <20><>leanote<74><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֪ʶ<D6AA><CAB6><EFBFBD><EFBFBD><EFBFBD>ĸ<EFBFBD>Զ!
* 知识管理: 通过leanote来管理知识, leanote有易操作的界面, 包含两款编辑器tinymcemarkdown. leanote, 你可以尽情享受写作.
* 分享: 你也可以通过分享知识给好友, 让好友拥有你的知识.
* 协作: 在分享的同时也可以与好友一起协作知识.
* 博客: leanote也可以作为你的博客, 将知识公开成博客, 让leanote把你的知识传播的更远!
## 2. Ϊʲô<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>leanote?
˵ʵ<EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>evernote<74><65><EFBFBD><EFBFBD>ʵ<EFBFBD><CAB5>˿, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҳ<EFBFBD><D2B2><EFBFBD><EFBFBD>evernote<EFBFBD>IJ<EFBFBD><EFBFBD><EFBFBD>:
* evernote<EFBFBD>ı<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<28><>ʽ<EFBFBD><CABD><EFBFBD>ҵ<EFBFBD>, <20><>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD>Ա, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǵĻ<C7B5><C4BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>), ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>markdown<EFBFBD>İ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD>evernote<EFBFBD><EFBFBD>Ȼû<EFBFBD><EFBFBD>.
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҳ<EFBFBD>뽫֪ʶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD><D4BC>IJ<EFBFBD><C4B2><EFBFBD>, <20><>wordpress, <20><>Ϊʲô<CAB2><C3B4><EFBFBD><EFBFBD><EFBFBD>߲<EFBFBD><DFB2>ܺ϶<DCBA>Ϊһ<CEAA><D2BB>?
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>...
## 2. 为什么我们要创建leanote?
说实话, 我们曾是evernote的忠实粉丝, 但是我们也发现evernote的不足:
* evernote的编辑器不能满足我们的需求, 不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放.
* 我们是markdown的爱好者, 可是evernote竟然没有.
* 我们也想将知识公开, 所以我们有自己的博客, 如wordpress, 但为什么这两者不能合二为一呢?
* 还有...
## 3.<EFBFBD><EFBFBD>װleanote
leanote<EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD>˽<EFBFBD><EFBFBD><EFBFBD>Ʊʼ<EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ<EFBFBD><D7B0><EFBFBD>Լ<EFBFBD><D4BC>ķ<EFBFBD><C4B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><>ȻҲ<C8BB><D2B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD> http://leanote.com <EFBFBD><EFBFBD>ע<EFBFBD><EFBFBD>.
## 3.安装leanote
leanote是一款私有云笔记, 你可以下载它安装在自己的服务器上, 当然也可以在 http://leanote.com 上注册.
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>leanote<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>leanote<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>İ<EFBFBD>װ<EFBFBD>̳<EFBFBD>, <20><><EFBFBD>Ʋ<EFBFBD><C6B2><EFBFBD>:
* [leanote<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD><EFBFBD>װ<EFBFBD>̳<EFBFBD>](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<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD><EFBFBD>װ<EFBFBD>̳<EFBFBD>](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二进版和leanote开发版的安装教程, 请移步至:
### 3.1. <20><><EFBFBD><EFBFBD>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)
Leanote V1.0-beta.2 <20>ѷ<EFBFBD><D1B7><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>(<28><>ʱû<CAB1><C3BB>windows<77><73><EFBFBD><EFBFBD>):
## 4. 如何对leanote进行二次开发
* Linux: [leanote-linux-x86_64.v1.0-beta.2.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-linux-x86_64.v1.0-beta.2.bin.tar.gz)
* MacOS X: [leanote-mac-x86_64.v1.0-beta.2.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-mac-x86_64.v1.0-beta.2.bin.tar.gz)
请查看 [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)
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֱ<EFBFBD>Ӽ<EFBFBD><EFBFBD><EFBFBD>[Leanote bin repository](https://github.com/leanote/leanote-bin) (<28>Ƽ<EFBFBD>, <20><>ΪΪ<CEAA><CEAA><EFBFBD>°汾)
## 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)
### 3.2. <20><>װ MongodbDB
更多文档请查看 [wiki](https://github.com/leanote/leanote/wiki).
Leanote<EFBFBD><EFBFBD><EFBFBD><EFBFBD>golang(ʹ<><CAB9>[revel](https://revel.github.io/)<29><><EFBFBD><EFBFBD> <20><> [MongoDB](https://www.mongodb.org)<29><><EFBFBD>ݿ<EFBFBD>), <20><><EFBFBD><EFBFBD>Ҫ<EFBFBD>Ȱ<EFBFBD>װMongodb.
## 6. 贡献者
多谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, leanote因有你们而更完美!
<EFBFBD><EFBFBD>װMongodbDB, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݸ<EFBFBD><DDB8><EFBFBD>ϸ<EFBFBD><CFB8><EFBFBD><EFBFBD><EFBFBD>鿴: [wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
## 7. 加入我们
### 3.3. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到leanote.
MongodbDB<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
有任何问题或建议, 欢迎提交[issue](https://github.com/leanote/leanote/issues).
```
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
```
Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善leanote.
<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD>ݰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD>:
## 8. 捐赠
支持我们, [捐赠Leanote](http://leanote.org/#donate). 感谢[捐赠者](http://leanote.leanote.com/post/leanote-donation-list), 谢谢你们的鼓励, Leanote会一直坚持!
```
user2 username: admin, password: abc123 (<28><><EFBFBD><EFBFBD>Ա, <20><>Ҫ!)
user3 username: demo@leanote.com, password: demo@leanote.com (Ϊ<><CEAA><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>)
```
## 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), 开发阶段
### 3.4. <20><><EFBFBD><EFBFBD>
欢迎加入我们!
<EFBFBD>޸<EFBFBD> `[PATH_TO_LEANOTE]/conf/app.conf`. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѡ<EFBFBD><D1A1>:
``mongodb`` **<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>!**
```Shell
db.host=localhost
db.port=27017
db.dbname=leanote
db.username=
db.password=
```
``app.secret`` **<2A><>Ҫ**
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>޸<EFBFBD>һ<EFBFBD><EFBFBD>, app<70><70><EFBFBD><EFBFBD>Կ, <20><><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Ĭ<EFBFBD>ϵ<EFBFBD>, <20><>Ȼ<EFBFBD><C8BB><EFBFBD>а<EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鿴 `app/app.conf` <20><> [revel <20>ֲ<EFBFBD>](https://revel.github.io/)
### 3.5. <20><><EFBFBD><EFBFBD>leanote
```
$> cd PATH_TO_LEANOTE/bin
$> sudo sh run.sh
```
## 4. <20><><EFBFBD>ζ<EFBFBD>leanote<74><65><EFBFBD>ж<EFBFBD><D0B6>ο<EFBFBD><CEBF><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD>鿴 [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. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD>л [<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>](https://github.com/leanote/leanote/graphs/contributors) <20>Ĺ<EFBFBD><C4B9><EFBFBD>, leanote<74><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƕ<EFBFBD><C7B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>!
## 6. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD>ӭ<EFBFBD>ύ[pull requests](https://github.com/leanote/leanote/pulls) <20><>leanote.
leanote<EFBFBD><EFBFBD><EFBFBD>кܶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϲ<EFBFBD><CFB2><EFBFBD><EFBFBD>, <20><>ӭ<EFBFBD><D3AD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>leanote.
## <20><><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD>
* [leanote<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ư<EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD><EFBFBD>װ<EFBFBD>̳<EFBFBD>](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<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD><EFBFBD>װ<EFBFBD>̳<EFBFBD>](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Դ<65><EFBFBD><EBB5BC>](https://github.com/leanote/leanote/wiki/Leanote-source-leanoteԴ<65><EFBFBD><EBB5BC>)
* [leanote blog theme api(<28><><EFBFBD>İ<EFBFBD>)](https://github.com/leanote/leanote/wiki/leanote-blog-theme-api)
* [How to develop leanote <20><><EFBFBD>ο<EFBFBD><CEBF><EFBFBD>leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-<2D><><EFBFBD>ο<EFBFBD><CEBF><EFBFBD>leanote)
## <20><><EFBFBD><EFBFBD>
* [leanote <20><><EFBFBD><EFBFBD>](http://bbs.leanote.com)
* QQȺ: 158716820
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)
## 联系&加入我们
* 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)

19
app/cmd/main.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import (
"github.com/revel/revel"
"github.com/revel/cmd/harness"
"fmt"
)
func main() {
// go run main.go
// 生成routes.go, main.go
revel.Init("", "github.com/leanote/leanote", "")
_, err := harness.Build() // ok, err
if err != nil {
panic(err)
}
fmt.Println("Ok")
// panicOnError(reverr, "Failed to build")
}

View File

@@ -2,11 +2,11 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
// "encoding/json"
"github.com/leanote/leanote/app/info"
"gopkg.in/mgo.v2/bson"
// . "github.com/leanote/leanote/app/lea"
// "io/ioutil"
// . "github.com/leanote/leanote/app/lea"
// "io/ioutil"
)
// Album controller
@@ -14,12 +14,17 @@ type Album struct {
BaseController
}
// 图片管理, iframe
func (c Album) Index() revel.Result {
c.SetLocale();
return c.RenderTemplate("album/index.html")
}
// all albums by userId
func (c Album) GetAlbums() revel.Result {
re := albumService.GetAlbums(c.GetUserId())
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})
@@ -29,12 +34,12 @@ func (c Album) DeleteAlbum(albumId string) revel.Result {
func (c Album) AddAlbum(name string) revel.Result {
album := info.Album{
AlbumId: bson.NewObjectId(),
Name: name,
Seq: -1,
UserId: c.GetObjectUserId()}
Name: name,
Seq: -1,
UserId: c.GetObjectUserId()}
re := albumService.AddAlbum(album)
if(re) {
if re {
return c.RenderJson(album)
} else {
return c.RenderJson(false)
@@ -44,4 +49,4 @@ func (c Album) AddAlbum(name string) revel.Result {
// update alnum name
func (c Album) UpdateAlbum(albumId, name string) revel.Result {
return c.RenderJson(albumService.UpdateAlbum(albumId, c.GetUserId(), name))
}
}

View File

@@ -40,16 +40,16 @@ func (c Auth) Login(email, from string) revel.Result {
func (c Auth) doLogin(email, pwd string) revel.Result {
sessionId := c.Session.Id()
var msg = ""
userInfo := authService.Login(email, pwd)
if userInfo.Email != "" {
userInfo, err := authService.Login(email, pwd)
if err != nil {
// 登录错误, 则错误次数++
msg = "wrongUsernameOrPassword"
} else {
c.SetSession(userInfo)
sessionService.ClearLoginTimes(sessionId)
return c.RenderJson(info.Re{Ok: true})
} else {
// 登录错误, 则错误次数++
msg = "wrongUsernameOrPassword"
}
}
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId) , Msg: c.Message(msg)})
}
@@ -61,16 +61,16 @@ func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result {
if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha {
msg = "captchaError"
} else {
userInfo := authService.Login(email, pwd)
if userInfo.Email != "" {
c.SetSession(userInfo)
sessionService.ClearLoginTimes(sessionId)
return c.RenderJson(info.Re{Ok: true})
} else {
userInfo, err := authService.Login(email, pwd)
if err != nil {
// 登录错误, 则错误次数++
msg = "wrongUsernameOrPassword"
sessionService.IncrLoginTimes(sessionId)
}
} else {
c.SetSession(userInfo)
sessionService.ClearLoginTimes(sessionId)
return c.RenderJson(info.Re{Ok: true})
}
}
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId) , Msg: c.Message(msg)})
@@ -85,8 +85,17 @@ func (c Auth) Logout() revel.Result {
// 体验一下
func (c Auth) Demo() revel.Result {
c.doLogin(configService.GetGlobalStringConfig("demoUsername"), configService.GetGlobalStringConfig("demoPassword"))
return c.Redirect("/note")
email := configService.GetGlobalStringConfig("demoUsername")
pwd := configService.GetGlobalStringConfig("demoPassword")
userInfo, err := authService.Login(email, pwd)
if err != nil {
return c.RenderJson(info.Re{Ok: false})
} else {
c.SetSession(userInfo)
return c.Redirect("/note")
}
return nil
}
//--------

View File

@@ -84,6 +84,13 @@ func (c BaseController) GetUserInfo() info.User {
return info.User{}
}
func (c BaseController) GetUserAndBlogUrl() info.UserAndBlogUrl {
if userId, ok := c.Session["UserId"]; ok && userId != "" {
return userService.GetUserAndBlogUrl(userId);
}
return info.UserAndBlogUrl{}
}
// 这里的session都是cookie中的, 与数据库session无关
func (c BaseController) GetSession(key string) string {
v, ok := c.Session[key]

View File

@@ -265,10 +265,6 @@ func (c Blog) getCates(userBlog info.UserBlog) {
}
}
Log("cates")
LogJ(cates)
LogJ(catesTree);
c.RenderArgs["cates"] = cates
c.RenderArgs["catesTree"] = catesTree
}
@@ -352,9 +348,10 @@ func (c Blog) blogCommon(userId string, userBlog info.UserBlog, userInfo info.Us
// 得到主题信息
themeInfo := themeService.GetThemeInfo(userBlog.ThemeId.Hex(), userBlog.Style)
c.RenderArgs["themeInfo"] = themeInfo
Log(">>")
Log(userBlog.Style)
Log(userBlog.ThemeId.Hex())
// Log(">>")
// Log(userBlog.Style)
// Log(userBlog.ThemeId.Hex())
return true, userBlog
}
@@ -824,7 +821,7 @@ func (c Blog) LikePost(noteId string, callback string) revel.Result {
re.Ok, re.Item = blogService.LikeBlog(noteId, userId)
return c.RenderJsonP(callback, re)
}
func (c Blog) GetComments(noteId string) revel.Result {
func (c Blog) GetComments(noteId string, callback string) revel.Result {
// 评论
userId := c.GetUserId()
page := c.GetPage()
@@ -836,6 +833,10 @@ func (c Blog) GetComments(noteId string) revel.Result {
result["comments"] = comments
result["commentUserInfo"] = commentUserInfo
re.Item = result
if callback != "" {
return c.RenderJsonP(callback, result)
}
return c.RenderJson(re)
}

View File

@@ -69,13 +69,13 @@ func (c File) UploadAvatar() revel.Result {
c.UpdateSession("Logo", re.Id);
}
}
return c.RenderJson(re)
}
// leaui image plugin upload image
func (c File) UploadImageLeaui(albumId string) revel.Result {
re := c.uploadImage("", albumId);
re := c.uploadImage("", albumId)
return c.RenderJson(re)
}
@@ -84,74 +84,80 @@ func (c File) UploadImageLeaui(albumId string) revel.Result {
func (c File) uploadImage(from, albumId string) (re info.Re) {
var fileUrlPath = ""
var fileId = ""
var resultCode = 0 // 1表示正常
var resultCode = 0 // 1表示正常
var resultMsg = "error" // 错误信息
var Ok = false
defer func() {
re.Id = fileId // 只是id, 没有其它信息
re.Code = resultCode
re.Msg = resultMsg
re.Ok = Ok
}()
file, handel, err := c.Request.FormFile("file")
if err != nil {
return re
}
defer file.Close()
// 生成上传路径
newGuid := NewGuid()
userId := c.GetUserId()
if(from == "logo" || from == "blogLogo") {
fileUrlPath = "public/upload/" + c.GetUserId() + "/images/logo"
fileUrlPath = "public/upload/" + Digest3(userId) + "/" + userId + "/images/logo"
} else {
fileUrlPath = "files/" + c.GetUserId() + "/images"
fileUrlPath = "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
}
dir := revel.BasePath + "/" + fileUrlPath
dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755)
if err != nil {
return re
}
// 生成新的文件名
filename := handel.Filename
var ext string;
var ext string
if from == "pasteImage" {
ext = ".png"; // TODO 可能不是png类型
ext = ".png" // TODO 可能不是png类型
} else {
_, ext = SplitFilename(filename)
if(ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg") {
if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
resultMsg = "Please upload image"
return re
}
}
filename = NewGuid() + ext
filename = newGuid + ext
data, err := ioutil.ReadAll(file)
if err != nil {
LogJ(err)
return re
}
var maxFileSize float64
if(from == "logo") {
maxFileSize = configService.GetUploadSize("uploadAvatarSize");
if from == "logo" {
maxFileSize = configService.GetUploadSize("uploadAvatarSize")
} else if from == "blogLogo" {
maxFileSize = configService.GetUploadSize("uploadBlogLogoSize");
maxFileSize = configService.GetUploadSize("uploadBlogLogoSize")
} else {
maxFileSize = configService.GetUploadSize("uploadImageSize");
maxFileSize = configService.GetUploadSize("uploadImageSize")
}
if maxFileSize <= 0 {
maxFileSize = 1000
}
// > 2M?
if(float64(len(data)) > maxFileSize * float64(1024*1024)) {
if float64(len(data)) > maxFileSize*float64(1024*1024) {
resultCode = 0
resultMsg = fmt.Sprintf("The file Size is bigger than %vM", maxFileSize)
return re
}
toPath := dir + "/" + filename;
toPath := dir + "/" + filename
err = ioutil.WriteFile(toPath, data, 0777)
if err != nil {
LogJ(err)
@@ -164,26 +170,27 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
fileUrlPath += "/" + filename
resultCode = 1
resultMsg = "Upload Success!"
// File
fileInfo := info.File{Name: filename,
Title: handel.Filename,
Path: fileUrlPath,
Size: filesize}
id := bson.NewObjectId();
Path: fileUrlPath,
Size: filesize}
id := bson.NewObjectId()
fileInfo.FileId = id
fileId = id.Hex()
if(from == "logo" || from == "blogLogo") {
fileId = "public/upload/" + c.GetUserId() + "/images/logo/" + filename
}
if(from == "logo" || from == "blogLogo") {
fileId = fileUrlPath
}
Ok, resultMsg = fileService.AddImage(fileInfo, albumId, c.GetUserId(), from == "" || from == "pasteImage")
resultMsg = c.Message(resultMsg)
fileInfo.Path = ""; // 不要返回
fileInfo.Path = "" // 不要返回
re.Item = fileInfo
return re
}
@@ -204,6 +211,7 @@ func (c File) DeleteImage(fileId string) revel.Result {
re.Ok, re.Msg = fileService.DeleteImage(c.GetUserId(), fileId)
return c.RenderJson(re)
}
//-----------
// 输出image
@@ -226,35 +234,39 @@ func (c File) CopyImage(userId, fileId, toUserId string) revel.Result {
return c.RenderJson(re)
}
// 复制外网的图片, 成公共图片 放在/upload下
// 复制外网的图片
// 都要好好的计算大小
func (c File) CopyHttpImage(src string) revel.Result {
re := info.NewRe()
fileUrlPath := "upload/" + c.GetUserId() + "/images"
dir := revel.BasePath + "/public/" + fileUrlPath
// 生成上传路径
newGuid := NewGuid()
userId := c.GetUserId()
fileUrlPath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
dir := revel.BasePath + "/" + fileUrlPath
err := os.MkdirAll(dir, 0755)
if err != nil {
return c.RenderJson(re)
}
filesize, filename, _, ok := netutil.WriteUrl(src, dir)
if !ok {
re.Msg = "copy error"
return c.RenderJson(re)
}
// File
fileInfo := info.File{Name: filename,
Title: filename,
Path: fileUrlPath + "/" + filename,
Size: filesize}
id := bson.NewObjectId();
Path: fileUrlPath + "/" + filename,
Size: filesize}
id := bson.NewObjectId()
fileInfo.FileId = id
re.Id = id.Hex()
re.Item = fileInfo.Path
// re.Item = fileInfo.Path
re.Ok, re.Msg = fileService.AddImage(fileInfo, "", c.GetUserId(), true)
return c.RenderJson(re)
}
}

View File

@@ -21,13 +21,12 @@ 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"]
lang := c.SetLocale()
c.SetLocale()
return c.RenderTemplate("home/index_" + lang + ".html");
return c.RenderTemplate("home/index.html");
}
// 建议

View File

@@ -2,18 +2,20 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
// "encoding/json"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson"
"os"
"os/exec"
"regexp"
"strings"
// "time"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
// "bytes"
// "os"
"time"
"fmt"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "bytes"
// "os"
)
type Note struct {
@@ -23,10 +25,10 @@ type Note struct {
// 笔记首页, 判断是否已登录
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
// 否则, 转向登录页面
func (c Note) Index(noteId string) revel.Result {
func (c Note) Index(noteId, online string) revel.Result {
c.SetLocale()
userInfo := c.GetUserInfo()
userInfo := c.GetUserAndBlogUrl()
userId := userInfo.UserId.Hex()
// 没有登录
@@ -50,10 +52,11 @@ func (c Note) Index(noteId string) revel.Result {
hasRightNoteId := false
if IsObjectId(noteId) {
note := noteService.GetNoteById(noteId)
var noteOwner = note.UserId.Hex()
noteContent = noteService.GetNoteContent(noteId, noteOwner)
if note.NoteId != "" {
var noteOwner = note.UserId.Hex()
noteContent = noteService.GetNoteContent(noteId, noteOwner)
hasRightNoteId = true
c.RenderArgs["curNoteId"] = noteId
c.RenderArgs["curNotebookId"] = note.NotebookId.Hex()
@@ -125,8 +128,9 @@ func (c Note) Index(noteId string) revel.Result {
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
// return c.RenderTemplate("note/note.html")
if isDev, _ := revel.Config.Bool("mode.dev"); isDev {
// return c.RenderTemplate("note/note.html")
if isDev, _ := revel.Config.Bool("mode.dev"); isDev && online == "" {
return c.RenderTemplate("note/note-dev.html")
} else {
return c.RenderTemplate("note/note.html")
@@ -137,13 +141,13 @@ func (c Note) Index(noteId string) revel.Result {
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
// 否则, 转向登录页面
func (c Note) ListNotes(notebookId string) revel.Result {
_, notes := noteService.ListNotes(c.GetUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false);
_, notes := noteService.ListNotes(c.GetUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false)
return c.RenderJson(notes)
}
// 得到trash
func (c Note) ListTrashNotes() revel.Result {
_, notes := noteService.ListNotes(c.GetUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false);
_, notes := noteService.ListNotes(c.GetUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
return c.RenderJson(notes)
}
@@ -255,28 +259,59 @@ func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
// 删除note/ 删除别人共享给我的笔记
// userId 是note.UserId
func (c Note) DeleteNote(noteId, userId string, isShared bool) revel.Result {
if(!isShared) {
return c.RenderJson(trashService.DeleteNote(noteId, c.GetUserId()));
func (c Note) DeleteNote(noteIds []string, isShared bool) revel.Result {
if !isShared {
for _, noteId := range noteIds {
trashService.DeleteNote(noteId, c.GetUserId())
}
return c.RenderJson(true)
}
return c.RenderJson(trashService.DeleteSharedNote(noteId, userId, c.GetUserId()));
for _, noteId := range noteIds {
trashService.DeleteSharedNote(noteId, c.GetUserId())
}
return c.RenderJson(true)
}
// 删除trash
// 删除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
func (c Note) MoveNote(noteId, notebookId string) revel.Result {
return c.RenderJson(noteService.MoveNote(noteId, notebookId, c.GetUserId()));
func (c Note) MoveNote(noteIds []string, notebookId string) revel.Result {
userId := c.GetUserId()
for _, noteId := range noteIds {
noteService.MoveNote(noteId, notebookId, userId)
}
return c.RenderJson(true)
}
// 复制note
func (c Note) CopyNote(noteId, notebookId string) revel.Result {
return c.RenderJson(noteService.CopyNote(noteId, notebookId, c.GetUserId()));
func (c Note) CopyNote(noteIds []string, notebookId string) revel.Result {
copyNotes := make([]info.Note, len(noteIds))
userId := c.GetUserId()
for i, noteId := range noteIds {
copyNotes[i] = noteService.CopyNote(noteId, notebookId, userId)
}
re := info.NewRe()
re.Ok = true
re.Item = copyNotes
return c.RenderJson(re)
}
// 复制别人共享的笔记给我
func (c Note) CopySharedNote(noteId, notebookId, fromUserId string) revel.Result {
return c.RenderJson(noteService.CopySharedNote(noteId, notebookId, fromUserId, c.GetUserId()));
func (c Note) CopySharedNote(noteIds []string, notebookId, fromUserId string) revel.Result {
copyNotes := make([]info.Note, len(noteIds))
userId := c.GetUserId()
for i, noteId := range noteIds {
copyNotes[i] = noteService.CopySharedNote(noteId, notebookId, fromUserId, userId)
}
re := info.NewRe()
re.Ok = true
re.Item = copyNotes
return c.RenderJson(re)
}
//------------
@@ -286,141 +321,93 @@ func (c Note) SearchNote(key string) revel.Result {
_, blogs := noteService.SearchNote(key, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false, false)
return c.RenderJson(blogs)
}
// 通过tags搜索
func (c Note) SearchNoteByTags(tags []string) revel.Result {
_, blogs := noteService.SearchNoteByTags(tags, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false)
return c.RenderJson(blogs)
}
//------------------
// html2image
// 判断是否有权限生成
// 博客也可以调用
// 这是脚本调用, 没有cookie, 不执行权限控制, 通过传来的appKey判断
func (c Note) ToImage(noteId, appKey string) revel.Result {
// 生成PDF
func (c Note) ToPdf(noteId, appKey string) revel.Result {
// 虽然传了cookie但是这里还是不能得到userId, 所以还是通过appKey来验证之
/*
appKeyTrue, _ := revel.Config.String("app.secret")
if appKeyTrue != appKey {
return c.RenderText("")
return c.RenderText("error")
}
*/
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
return c.RenderText("")
return c.RenderText("error")
}
c.SetLocale()
noteUserId := note.UserId.Hex()
content := noteService.GetNoteContent(noteId, noteUserId)
userInfo := userService.GetUserInfo(noteUserId);
c.RenderArgs["blog"] = note
c.RenderArgs["content"] = content.Content
c.RenderArgs["userInfo"] = userInfo
userBlog := blogService.GetUserBlog(noteUserId)
c.RenderArgs["userBlog"] = userBlog
return c.RenderTemplate("html2Image/index.html")
}
userInfo := userService.GetUserInfo(noteUserId)
func (c Note) Html2Image(noteId string) revel.Result {
re := info.NewRe()
userId := c.GetUserId()
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
re.Msg = "No Note"
return c.RenderJson(re)
//------------------
// 将content的图片转换为base64
contentStr := content.Content
siteUrlPattern := configService.GetSiteUrl()
if strings.Contains(siteUrlPattern, "https") {
siteUrlPattern = strings.Replace(siteUrlPattern, "https", "https*", 1)
} else {
siteUrlPattern = strings.Replace(siteUrlPattern, "http", "https*", 1)
}
noteUserId := note.UserId.Hex()
// 是否有权限
if noteUserId != userId {
// 是否是有权限协作的
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
re.Msg = "No Perm"
return c.RenderJson(re)
regImage, _ := regexp.Compile(`<img .*?(src=('|")` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})("|'))`)
findsImage := regImage.FindAllStringSubmatch(contentStr, -1) // 查找所有的
// [<img src="http://leanote.com/api/getImage?fileId=3354672e8d38f411286b000069" alt="" width="692" height="302" data-mce-src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " file/outputImage 54672e8d38f411286b000069 "]
for _, eachFind := range findsImage {
if len(eachFind) == 6 {
fileId := eachFind[4]
// 得到base64编码文件
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
if fileBase64 == "" {
continue
}
// 1
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
allFixed := strings.Replace(eachFind[0], eachFind[1], "src=\""+fileBase64+"\"", -1)
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
}
}
// path 判断是否需要重新生成之
fileUrlPath := "/upload/" + noteUserId + "/images/weibo"
dir := revel.BasePath + "/public/" + fileUrlPath
if !ClearDir(dir) {
re.Msg = "No Dir"
return c.RenderJson(re)
// markdown
if note.IsMarkdown {
// ![enter image description here](url)
regImageMarkdown, _ := regexp.Compile(`!\[.*?\]\(` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})\)`)
findsImageMarkdown := regImageMarkdown.FindAllStringSubmatch(contentStr, -1) // 查找所有的
for _, eachFind := range findsImageMarkdown {
if len(eachFind) == 3 {
fileId := eachFind[2]
// 得到base64编码文件
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
if fileBase64 == "" {
continue
}
// 1
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
allFixed := "![](" + fileBase64 + ")"
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
}
}
}
filename := note.NoteId.Hex() + ".png";
path := dir + "/" + filename
// cookie
cookieName := revel.CookiePrefix + "_SESSION"
cookie, err := c.Request.Cookie(cookieName)
cookieStr := cookie.String()
cookieValue := ""
if err == nil && len(cookieStr) > len(cookieName) {
cookieValue = cookieStr[len(cookieName)+1:]
if note.Tags != nil && len(note.Tags) > 0 && note.Tags[0] != "" {
} else {
note.Tags = nil
}
appKey, _ := revel.Config.String("app.secret")
cookieDomain, _ := revel.Config.String("cookie.domain")
// 生成之
url := configService.GetSiteUrl() + "/note/toImage?noteId=" + noteId + "&appKey=" + appKey;
// /Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/b.js
binPath := configService.GetGlobalStringConfig("toImageBinPath")
if binPath == "" {
return c.RenderJson(re);
}
cc := binPath + " \"" + url + "\" \"" + path + "\" \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
cmd := exec.Command("/bin/sh", "-c", cc)
Log(cc);
b, err := cmd.Output()
if err == nil {
re.Ok = true
re.Id = fileUrlPath + "/" + filename
} else {
re.Msg = string(b)
Log("error:......")
Log(string(b))
}
return c.RenderJson(re)
/*
// 这里速度慢, 生成不完全(图片和内容都不全)
content := noteService.GetNoteContent(noteId, noteUserId)
userInfo := userService.GetUserInfo(noteUserId);
c.SetLocale()
c.RenderArgs["blog"] = note
c.RenderArgs["content"] = content.Content
c.RenderArgs["content"] = contentStr
c.RenderArgs["userInfo"] = userInfo
userBlog := blogService.GetUserBlog(noteUserId)
c.RenderArgs["userBlog"] = userBlog
html := c.RenderTemplateStr("html2Image/index.html") // Result类型的
contentFile := dir + "/html";
fout, err := os.Create(contentFile)
if err != nil {
return c.RenderJson(re)
}
fout.WriteString(html);
fout.Close()
cc := "/Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/c.js \"" + contentFile + "\" \"" + path + "\""
cmd := exec.Command("/bin/sh", "-c", cc)
b, err := cmd.Output()
if err == nil {
re.Ok = true
re.Id = fileUrlPath + "/" + filename
} else {
Log(string(b))
}
*/
return c.RenderTemplate("file/pdf.html")
}
// 导出成PDF
@@ -430,70 +417,80 @@ func (c Note) ExportPdf(noteId string) revel.Result {
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
re.Msg = "No Note"
return c.RenderJson(re)
return c.RenderText("error")
}
noteUserId := note.UserId.Hex()
// 是否有权限
if noteUserId != userId {
// 是否是有权限协作的
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
re.Msg = "No Perm"
return c.RenderJson(re)
return c.RenderText("error")
}
}
// path 判断是否需要重新生成之
fileUrlPath := "upload/" + noteUserId + "/images/weibo"
dir := revel.BasePath + "/public/" + fileUrlPath
if !ClearDir(dir) {
re.Msg = "No Dir"
return c.RenderJson(re)
}
filename := note.Title + ".pdf";
if note.Title == "" {
filename = "Untitled.pdf";
guid := NewGuid()
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
dir := revel.BasePath + "/" + fileUrlPath
if !MkdirAll(dir) {
return c.RenderText("error, no dir")
}
filename := guid + ".pdf"
path := dir + "/" + filename
// cookie
cookieName := revel.CookiePrefix + "_SESSION"
cookie, err := c.Request.Cookie(cookieName)
cookieStr := cookie.String()
cookieValue := ""
if err == nil && len(cookieStr) > len(cookieName) {
cookieValue = cookieStr[len(cookieName)+1:]
// leanote.com的secret
appKey, _ := revel.Config.String("app.secretLeanote")
if appKey == "" {
appKey, _ = revel.Config.String("app.secret")
}
Log(cookieValue)
appKey, _ := revel.Config.String("app.secret")
// cookieDomain, _ := revel.Config.String("cookie.domain")
// 生成之
url := configService.GetSiteUrl() + "/note/toImage?noteId=" + noteId + "&appKey=" + appKey;
// /Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/b.js
binPath := "/usr/local/bin/wkhtmltopdf" // configService.GetGlobalStringConfig("toImageBinPath")
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
// 默认路径
if binPath == "" {
return c.RenderJson(re);
binPath = "/usr/local/bin/wkhtmltopdf"
}
// cc := binPath + " \"" + url + "\" \"" + path + "\" \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
cc := binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
// cc := binPath + " --no-stop-slow-scripts --javascript-delay 10000 \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// cc := binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// 等待--window-status为done的状态
// http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html
// wkhtmltopdf参数大全
var cc string
if note.IsMarkdown {
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
} else {
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
}
cmd := exec.Command("/bin/sh", "-c", cc)
Log(cc);
b, err := cmd.Output()
if err == nil {
re.Ok = true
re.Id = fileUrlPath + "/" + filename
} else {
re.Msg = string(b)
Log("error:......")
Log(string(b))
}
return c.RenderJson(re)
_, err := cmd.Output()
if err != nil {
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))
}
file, err := os.Open(path)
if err != nil {
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))
}
// http://stackoverflow.com/questions/8588818/chrome-pdf-display-duplicate-headers-received-from-the-server
// filenameReturn = strings.Replace(filenameReturn, ",", "-", -1)
filenameReturn := note.Title
filenameReturn = FixFilename(filenameReturn)
if filenameReturn == "" {
filenameReturn = "Untitled.pdf"
} else {
filenameReturn += ".pdf"
}
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
}
// 设置/取消Blog; 置顶
func (c Note) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
re := noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
return c.RenderJson(re)
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)
}

View File

@@ -1,70 +0,0 @@
package controllers
import (
"code.google.com/p/goauth2/oauth"
"github.com/revel/revel"
"github.com/leanote/leanote/app/lea/netutil"
. "github.com/leanote/leanote/app/lea"
"encoding/json"
"fmt"
)
type Oauth struct {
BaseController
}
var oauthCfg = &oauth.Config{
ClientId: "3790fbf1fc14bc6c5d85",
ClientSecret: "e9dadfe601c7caa6df9b33db3e7539945c60dfa2",
AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token",
RedirectURL: "http://leanote.com/oauth/githubCallback",
Scope: "user",
}
// 用户允许后, github返回到leanote
// 通过code得到token
// https://github.com/login/oauth/authorize?access_type=&approval_prompt=&client_id=3790fbf1fc14bc6c5d85&redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Foauth2callback&response_type=code&scope=user&state=
func (c Oauth) GithubCallback(code string) revel.Result {
t := &oauth.Transport{Config: oauthCfg}
// Exchange the received code for a token
tok, err := t.Exchange(code)
token := tok.AccessToken
if err != nil || token == "" {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
// 得到用户信息
profileInfoURL := "https://api.github.com/user"
url := fmt.Sprintf("%s?access_token=%s", profileInfoURL, token)
content, err2 := netutil.GetContent(url)
if err2 != nil {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
// 转成map
profileInfo := map[string]interface{}{}
Log(string(content))
err2 = json.Unmarshal(content, &profileInfo)
if err2 != nil {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
usernameI := profileInfo["login"]
username, _ := usernameI.(string)
userId := username
// 注册
isExists, userInfo := authService.ThirdRegister("github", userId, username)
c.RenderArgs["isExists"] = isExists
c.RenderArgs["userInfo"] = userInfo
// 登录之
c.SetSession(userInfo)
return c.Redirect("/note")
}

View File

@@ -29,8 +29,8 @@ func (c User) Account(tab int) revel.Result {
// 修改用户名, 需要重置session
func (c User) UpdateUsername(username string) revel.Result {
re := info.NewRe();
if(c.GetUsername() == "demo") {
re := info.NewRe()
if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
re.Msg = "cannotUpdateDemo"
return c.RenderRe(re);
}
@@ -48,8 +48,8 @@ func (c User) UpdateUsername(username string) revel.Result {
// 修改密码
func (c User) UpdatePwd(oldPwd, pwd string) revel.Result {
re := info.NewRe();
if(c.GetUsername() == "demo") {
re := info.NewRe()
if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
re.Msg = "cannotUpdateDemo"
return c.RenderRe(re);
}
@@ -94,9 +94,9 @@ func (c User) ReSendActiveEmail() revel.Result {
}
// 修改Email发送激活邮箱
func (c User) UpdateEmailSendActiveEmail(email string) revel.Result {
func (c User) updateEmailSendActiveEmail(email, pwd string) revel.Result {
re := info.NewRe()
if(c.GetUsername() == "demo") {
if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
re.Msg = "cannotUpdateDemo"
return c.RenderJson(re);
}
@@ -193,4 +193,4 @@ func (c User) UpdateLeftIsMin(leftIsMin bool) revel.Result {
}
}
return c.RenderJson(re)
}
}

View File

@@ -46,7 +46,10 @@ func (c AdminEmail) Demo() revel.Result {
func (c AdminEmail) DoDemo(demoUsername, demoPassword string) revel.Result {
re := info.NewRe()
userInfo := authService.Login(demoUsername, demoPassword)
userInfo, err := authService.Login(demoUsername, demoPassword)
if err != nil {
return c.RenderJson(info.Re{Ok: false})
}
if userInfo.UserId == "" {
re.Msg = "The User is Not Exists";
return c.RenderJson(re)

View File

@@ -56,7 +56,11 @@ func (c AdminSetting) Demo() revel.Result {
func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
re := info.NewRe()
userInfo := authService.Login(demoUsername, demoPassword)
userInfo, err := authService.Login(demoUsername, demoPassword)
if err != nil {
fmt.Println(err)
return c.RenderJson(info.Re{Ok: false})
}
if userInfo.UserId == "" {
re.Msg = "The User is Not Exists";
return c.RenderJson(re)
@@ -69,15 +73,9 @@ func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
return c.RenderJson(re)
}
// ToImage
// 长微博的bin路径phantomJs
func (c AdminSetting) ToImage() revel.Result {
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
return c.RenderTemplate("admin/setting/toImage.html");
}
func (c AdminSetting) DoToImage(toImageBinPath string) revel.Result {
func (c AdminSetting) ExportPdf(path string) revel.Result {
re := info.NewRe()
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "toImageBinPath", toImageBinPath)
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "exportPdfBinPath", path)
return c.RenderJson(re)
}

View File

@@ -253,7 +253,6 @@ http://leanote.com/api/file/getImage?fileId=xx
http://leanote.com/api/file/getAttach?fileId=xx
所有附件:
http://leanote.com/api/file/getAllAttachs?noteId=xxx
```
**注意:**
addNote时必须要把Files, 和相关的图片/附件一起传到服务器中
@@ -272,7 +271,9 @@ addNote时必须要把Files, 和相关的图片/附件一起传到服务器中
http://leanote.com 不绝对, 因为用户可以自建服务, 所以在开发时需要可配置
### /note/updateNote 更新笔记
当更新了笔记某个属性时, 只要传某个属性就行, 其它不用传, 比如把笔记拉入了trash, 那么就传IsTrash: true
```
参数: (注意首字大写)
NoteId string 必传
@@ -386,6 +387,7 @@ Notebook {
Seq int // 排序
Title string
IsBlog bool
IsDeleted bool
CreatedTime time.Time
UpdatedTime time.Time

View File

@@ -23,9 +23,9 @@ type ApiAuth struct {
// 失败返回 {Ok: false, Msg: ""}
func (c ApiAuth) Login(email, pwd string) revel.Result {
var msg = ""
userInfo := authService.Login(email, pwd)
if userInfo.Email != "" {
userInfo, err := authService.Login(email, pwd)
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})
@@ -66,4 +66,4 @@ func (c ApiAuth) Register(email, pwd string) revel.Result {
// 注册
re.Ok, re.Msg = authService.Register(email, pwd, "")
return c.RenderJson(re)
}
}

View File

@@ -74,7 +74,9 @@ func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg
}
// 生成上传路径
filePath := "files/" + userId + "/attachs"
newGuid := NewGuid()
filePath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/attachs"
dir := revel.BasePath + "/" + filePath
err = os.MkdirAll(dir, 0755)
if err != nil {
@@ -83,7 +85,7 @@ func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg
// 生成新的文件名
filename := handel.Filename
_, ext := SplitFilename(filename) // .doc
filename = NewGuid() + ext
filename = newGuid + ext
toPath := dir + "/" + filename;
err = ioutil.WriteFile(toPath, data, 0777)
if err != nil {
@@ -124,8 +126,12 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
return
}
defer file.Close()
newGuid := NewGuid()
// 生成上传路径
fileUrlPath := "files/" + c.getUserId() + "/images"
userId := c.getUserId()
fileUrlPath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755)
if err != nil {
@@ -139,7 +145,7 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
return
}
filename = NewGuid() + ext
filename = newGuid + ext
data, err := ioutil.ReadAll(file)
if err != nil {
return

View File

@@ -9,6 +9,8 @@ import (
"regexp"
"strings"
"time"
"os/exec"
"os"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
@@ -70,6 +72,7 @@ func (c ApiNote) GetNotes(notebookId string) revel.Result {
func (c ApiNote) GetTrashNotes() revel.Result {
_, notes := noteService.ListNotes(c.getUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
return c.RenderJson(noteService.ToApiNotes(notes))
}
// get Note
@@ -335,8 +338,12 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
Abstract: noteOrContent.Abstract}
// 通过内容得到Desc, abstract
note.Desc = SubStringHTMLToRaw(noteContent.Content, 50)
noteContent.Abstract = SubStringHTML(noteContent.Content, 200, "")
if noteOrContent.Abstract == "" {
note.Desc = SubStringHTMLToRaw(noteContent.Content, 200)
noteContent.Abstract = SubStringHTML(noteContent.Content, 200, "")
} else {
note.Desc = SubStringHTMLToRaw(noteContent.Abstract, 200)
}
note = noteService.AddNoteAndContentApi(note, noteContent, myUserId)
@@ -352,7 +359,7 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
noteOrContent.UpdatedTime = note.UpdatedTime
noteOrContent.UserId = c.getUserId()
noteOrContent.IsMarkdown = note.IsMarkdown
// 删除一些不要返回的
// 删除一些不要返回的, 删除Desc?
noteOrContent.Content = ""
noteOrContent.Abstract = ""
// apiNote := info.NoteToApiNote(note, noteOrContent.Files)
@@ -494,8 +501,12 @@ func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
}
if c.Has("Content") {
// 通过内容得到Desc, abstract
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Content, 50)
// 通过内容得到Desc, 如果有Abstract, 则用Abstract生成Desc
if noteOrContent.Abstract == "" {
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Content, 200)
} else {
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Abstract, 200)
}
}
afterNoteUsn := 0
@@ -570,4 +581,84 @@ func (c ApiNote) GetHistories(noteId string) revel.Result {
}
return c.RenderJson(re)
}
*/
*/
// 0.2 新增
// 导出成PDF
// test localhost:9000/api/note/exportPdf?noteId=554f07bf05fcd15fa9000000&token=562211dc99c37ba6a7000001
func (c ApiNote) ExportPdf(noteId string) revel.Result {
re := info.NewApiRe()
userId := c.getUserId()
if noteId == "" {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
noteUserId := note.UserId.Hex()
// 是否有权限
if noteUserId != userId {
// 是否是有权限协作的
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
}
// path 判断是否需要重新生成之
guid := NewGuid()
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
dir := revel.BasePath + "/" + fileUrlPath
if !MkdirAll(dir) {
re.Msg = "noDir"
return c.RenderJson(re)
}
filename := guid + ".pdf"
path := dir + "/" + filename
appKey, _ := revel.Config.String("app.secretLeanote")
if appKey == "" {
appKey, _ = revel.Config.String("app.secret")
}
// 生成之
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
// 默认路径
if binPath == "" {
binPath = "/usr/local/bin/wkhtmltopdf"
}
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
var cc string
if(note.IsMarkdown) {
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
} else {
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
}
cmd := exec.Command("/bin/sh", "-c", cc)
_, err := cmd.Output()
if err != nil {
re.Msg = "sysError"
return c.RenderJson(re)
}
file, err := os.Open(path)
if err != nil {
re.Msg = "sysError"
return c.RenderJson(re)
}
filenameReturn := note.Title
filenameReturn = FixFilename(filenameReturn)
if filenameReturn == "" {
filenameReturn = "Untitled.pdf"
} else {
filenameReturn += ".pdf"
}
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
}

View File

@@ -48,7 +48,7 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
"FindPasswordUpdate": true,
"Suggestion": true,
},
"Note": map[string]bool{"ToImage": true},
"Note": map[string]bool{"ToPdf": true},
"Blog": map[string]bool{"Index": true,
"View": true,
"AboutMe": true,
@@ -68,7 +68,7 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
},
"Oauth": map[string]bool{"GithubCallback": true},
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
"Attach": map[string]bool{"Download": true, "DownloadAll": true},
"Attach": map[string]bool{"Download": true/*, "DownloadAll": true*/},
}
func needValidate(controller, method string) bool {
@@ -146,9 +146,10 @@ func init() {
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Note{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Share{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &User{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Album{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &File{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Attach{})
// revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Blog{})
// revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Blog{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &NoteContentHistory{})
revel.OnAppStart(func() {

View File

@@ -460,7 +460,7 @@ func (c MemberBlog) ImportTheme() revel.Result {
var ext string
_, ext = SplitFilename(filename)
if ext != ".zip" {
re.Msg = "请上传zip文件"
re.Msg = "Please upload zip file"
return c.RenderJson(re)
}
@@ -472,7 +472,7 @@ func (c MemberBlog) ImportTheme() revel.Result {
// > 10M?
if len(data) > 10*1024*1024 {
re.Msg = "文件大于10M"
re.Msg = "File is big than 10M"
return c.RenderJson(re)
}

View File

@@ -6,6 +6,7 @@ import (
. "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"strings"
)
// Init mgo and the common DAO
@@ -62,10 +63,26 @@ func Init(url, dbname string) {
config := revel.Config
if url == "" {
url, ok = config.String("db.url")
if !ok {
url, ok = config.String("db.urlEnv")
if ok {
Log("get db conf from urlEnv: " + url)
}
} else {
Log("get db conf from db.url: " + url)
}
if ok {
// get dbname from urlEnv
urls := strings.Split(url, "/")
dbname = urls[len(urls)-1]
}
}
if dbname == "" {
dbname, _ = config.String("db.dbname")
}
// get db config from host, port, username, password
if !ok {
host, _ := revel.Config.String("db.host")
port, _ := revel.Config.String("db.port")
@@ -338,3 +355,20 @@ func Err(err error) bool {
}
return true
}
// 检查mognodb是否lost connection
// 每个请求之前都要检查!!
func CheckMongoSessionLost() {
// fmt.Println("检查CheckMongoSessionLostErr")
err := Session.Ping()
if err != nil {
Log("Lost connection to db!")
Session.Refresh()
err = Session.Ping()
if err == nil {
Log("Reconnect to db successful.")
} else {
Log("重连失败!!!! 警告")
}
}
}

View File

@@ -10,8 +10,8 @@ import (
// convert revel msg to js msg
var msgBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/messages/"
var targetBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/public/js/i18n/"
var msgBasePath = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/messages/"
var targetBasePath = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/public/js/i18n/"
func parse(filename string) {
file, err := os.Open(msgBasePath + filename)
reader := bufio.NewReader(file)
@@ -84,6 +84,8 @@ function getMsg(key, data) {
func main() {
parse("msg.en")
parse("msg.zh")
parse("msg.fr")
parse("blog.zh")
parse("blog.en")
parse("blog.fr")
}

View File

@@ -78,7 +78,7 @@ type ShareNotebook struct {
UserId bson.ObjectId `bson:"UserId"`
ToUserId bson.ObjectId `bson:"ToUserId,omitempty"`
ToGroupId bson.ObjectId `bson:"ToGroupId,omitempty"` // 分享给的用户组
ToGroup Group `ToGroup,omitempty` // 仅仅为了显示, 不存储, 分组信息
ToGroup Group `ToGroup,omitempty` // 仅仅为了显示, 不存储, 分组信息
NotebookId bson.ObjectId `bson:"NotebookId"`
Seq int `bson:"Seq"` // 排序
Perm int `bson:"Perm"` // 权限, 其下所有notes 0只读, 1可修改
@@ -138,7 +138,7 @@ type ShareNote struct {
UserId bson.ObjectId `bson:"UserId"`
ToUserId bson.ObjectId `bson:"ToUserId,omitempty"`
ToGroupId bson.ObjectId `bson:"ToGroupId,omitempty"` // 分享给的用户组
ToGroup Group `ToGroup,omitempty` // 仅仅为了显示, 不存储, 分组信息
ToGroup Group `ToGroup,omitempty` // 仅仅为了显示, 不存储, 分组信息
NoteId bson.ObjectId `bson:"NoteId"`
Perm int `bson:"Perm"` // 权限, 0只读, 1可修改
CreatedTime time.Time `CreatedTime`

View File

@@ -14,7 +14,7 @@ type Theme struct {
Version string `Version`
Author string `Author`
AuthorUrl string `AuthorUrl`
Path string `Path` // 文件夹路径
Path string `Path` // 文件夹路径, public/upload/54d7620d99c37b030600002c/themes/54d867c799c37b533e000001
Info map[string]interface{} `Info` // 所有信息
IsActive bool `IsActive` // 是否在用

View File

@@ -70,6 +70,13 @@ type UserAccount struct {
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
}
// note主页需要
type UserAndBlogUrl struct {
User
BlogUrl string `BlogUrl`
PostUrl string `PostUrl`
}
// 用户与博客信息结合, 公开
type UserAndBlog struct {
UserId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键

View File

@@ -10,8 +10,6 @@ import (
"github.com/leanote/leanote/app/controllers/admin"
"github.com/leanote/leanote/app/controllers/member"
_ "github.com/leanote/leanote/app/lea/binder"
"github.com/leanote/leanote/app/lea/session"
"github.com/leanote/leanote/app/lea/memcache"
"github.com/leanote/leanote/app/lea/route"
"reflect"
"fmt"
@@ -33,11 +31,11 @@ func init() {
// AuthFilter, // Invoke the action.
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params.
// revel.SessionFilter, // Restore and write the session cookie.
revel.SessionFilter, // Restore and write the session cookie.
// 使用SessionFilter标准版从cookie中得到sessionID, 然后通过MssessionFilter从Memcache中得到
// session, 之后MSessionFilter将session只存sessionID然后返回给SessionFilter返回到web
session.SessionFilter, // leanote session
// session.SessionFilter, // leanote session
// session.MSessionFilter, // leanote memcache session
revel.FlashFilter, // Restore and write the flash cookie.
@@ -51,6 +49,18 @@ func init() {
revel.TemplateFuncs["raw"] = func(str string) template.HTML {
return template.HTML(str)
}
revel.TemplateFuncs["trim"] = func(str string) string {
str = strings.Trim(str, " ")
str = strings.Trim(str, " ")
str = strings.Trim(str, "\n")
str = strings.Trim(str, "&nbsp;")
// 以下两个空格不一样
str = strings.Trim(str, " ")
str = strings.Trim(str, " ")
return str
}
revel.TemplateFuncs["add"] = func(i int) string {
i = i + 1;
return fmt.Sprintf("%v", i)
@@ -145,8 +155,43 @@ func init() {
return template.HTML(tagStr)
}
revel.TemplateFuncs["blogTagsForExport"] = func(renderArgs map[string]interface{}, tags []string) template.HTML {
if tags == nil || len(tags) == 0 {
return ""
}
tagStr := ""
lenTags := len(tags)
for i, tag := range tags {
str := tag
var classes = "label"
if InArray([]string{"red", "blue", "yellow", "green"}, tag) {
classes += " label-" + tag
} else {
classes += " label-default"
}
classes += " label-post"
tagStr += "<span class=\"" + classes + "\" >" + str + "</span>";
if i != lenTags - 1 {
tagStr += " "
}
}
return template.HTML(tagStr)
}
// 不用revel的msg
revel.TemplateFuncs["leaMsg"] = func(renderArgs map[string]interface{}, key string) template.HTML {
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
str := revel.Message(locale, key)
if strings.HasPrefix(str, "???") {
str = key
}
return template.HTML(str);
}
// lea++
revel.TemplateFuncs["blogTagsLea"] = func(renderArgs map[string]interface{}, tags []string, isRecommend bool) template.HTML {
revel.TemplateFuncs["blogTagsLea"] = func(renderArgs map[string]interface{}, tags []string, typeStr string) template.HTML {
if tags == nil || len(tags) == 0 {
return ""
}
@@ -155,10 +200,12 @@ func init() {
lenTags := len(tags)
tagPostUrl := "http://lea.leanote.com/"
if isRecommend {
if typeStr == "recommend" {
tagPostUrl += "?tag=";
} else {
} else if typeStr == "latest" {
tagPostUrl += "latest?tag=";
} else {
tagPostUrl += "subscription?tag=";
}
for i, tag := range tags {
@@ -367,7 +414,7 @@ func init() {
// email配置
InitEmail()
InitVd()
memcache.InitMemcache() // session服务
// memcache.InitMemcache() // session服务
// 其它service
service.InitService()
controllers.InitService()

View File

@@ -56,6 +56,14 @@ func ClearDir(dir string) bool {
return true
}
func MkdirAll(dir string) bool {
err := os.MkdirAll(dir, 0777)
if err != nil {
return false
}
return true
}
// list dir's all file, return filenames
func ListDir(dir string) []string {
f, err := os.Open(dir)

22
app/lea/Pwd.go Normal file
View File

@@ -0,0 +1,22 @@
package lea
// 对比密码是否一致
// 因为之前密码是用md5加密的, 所以通过密码长度来判断
// rawPwd 原始, 用户输入的密码
func ComparePwd(rawPwd, dbPwd string) bool {
if len(dbPwd) == 32 {
return Md5(rawPwd) == dbPwd
}
hex := []byte(dbPwd)
return CompareHash(hex, rawPwd)
}
// 加密
func GenPwd(rawPwd string) string {
digest, err := GenerateHash(rawPwd)
if err != nil {
return ""
}
return string(digest)
}

View File

@@ -10,6 +10,9 @@ import (
"io"
"gopkg.in/mgo.v2/bson"
"time"
"strings"
"github.com/PuerkitoBio/goquery"
"bytes"
math_rand "math/rand"
)
@@ -22,6 +25,22 @@ func Md5(s string) string {
return hex.EncodeToString(h.Sum(nil))
}
// 3位数的转换, 为了用bson.id -> 3位数
func Digest3(str string) string {
var b rune = 0
for _, k := range str {
b += k
}
return fmt.Sprintf("%d", b % 1000)
}
func Digest2(str string) string {
var b rune = 0
for _, k := range str {
b += k
}
return fmt.Sprintf("%d", b % 100)
}
// Guid
func NewGuid() string {
b := make([]byte, 48)
@@ -123,73 +142,48 @@ func ReplaceAll(oldStr, pattern, newStr string) string {
}
// 获取纯文本
func SubStringHTMLToRaw(param string, length int) (result string) {
func SubStringHTMLToRaw(param string, length int) string {
if param == "" {
return ""
return param
}
result = ""
n := 0
var temp rune // 中文问题, 用rune来解决
rStr := []rune(param)
lenStr := len(rStr)
isCode := false
for i := 0; i < len(rStr); i++ {
resultRune := make([]rune, length)
// s := ""
for i := 0; i < lenStr; i++ {
temp = rStr[i]
if temp == '<' {
isCode = true
continue
} else if temp == '>' {
isCode = false
result += " "; // 空格
resultRune[n] = ' ';
n++
if n >= length {
break
}
continue
}
if !isCode {
result += string(temp)
resultRune[n] = temp;
// s += string(temp)
n++
if n >= length {
break
}
}
}
return
result := string(resultRune[0:n])
return strings.Trim(result, " ")
}
// 获取摘要, HTML
func SubStringHTML(param string, length int, end string) string {
if param == "" {
return ""
}
// 先取出<pre></pre>占位..
result := ""
// 1
n := 0
var temp rune // 中文问题, 用rune来解决
isCode := false //是不是HTML代码
isHTML := false //是不是HTML特殊字符,如&nbsp;
rStr := []rune(param)
for i := 0; i < len(rStr); i++ {
temp = rStr[i]
if temp == '<' {
isCode = true
} else if temp == '&' {
isHTML = true
} else if temp == '>' && isCode {
n = n - 1
isCode = false
} else if temp == ';' && isHTML {
isHTML = false
}
if !isCode && !isHTML {
n = n + 1
}
result += string(temp)
if n >= length {
break
}
}
result += end
// 自带方法补全html
func fixHtml(result string) string {
// 取出所有标签
tempResult := ReplaceAll(result, "(>)[^<>]*(<?)", "$1$2") // 把标签中间的所有内容都去掉了
@@ -232,6 +226,71 @@ func SubStringHTML(param string, length int, end string) string {
return result
}
// 获取摘要, HTML
func SubStringHTML(param string, length int, end string) string {
if param == "" {
return param
}
result := ""
rStr := []rune(param)
lenStr := len(rStr)
if lenStr <= length {
result = param
} else {
// 1
n := 0
var temp rune // 中文问题, 用rune来解决
isCode := false //是不是HTML代码
isHTML := false //是不是HTML特殊字符,如&nbsp;
var i = 0;
for ; i < lenStr; i++ {
temp = rStr[i]
if temp == '<' {
isCode = true
} else if temp == '&' {
isHTML = true
} else if temp == '>' && isCode {
// n = n - 1
isCode = false
} else if temp == ';' && isHTML {
isHTML = false
}
if !isCode && !isHTML {
n = n + 1
}
// 每一次都相加, 速度非常慢!, 重新分配内存, 7倍的差距
// result += string(temp)
if n >= length {
break
}
}
result = string(rStr[0:i])
if end != "" {
result += end
}
}
// 使用goquery来取出html, 为了补全html
htmlReader := bytes.NewBufferString(result)
dom, err1 := goquery.NewDocumentFromReader(htmlReader)
if err1 == nil {
html, _ := dom.Html()
html = strings.Replace(html, "<html><head></head><body>", "", 1)
html = strings.Replace(html, "</body></html>", "", 1)
// TODO 把style="float: left"去掉
return html
// 如果有错误, 则使用自己的方法补全, 有风险
} else {
return fixHtml(result)
}
}
// 是否是合格的密码
func IsGoodPwd(pwd string) (bool, string) {
if pwd == "" {
@@ -248,7 +307,7 @@ func IsEmail(email string) bool {
if email == "" {
return false;
}
ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-z\-A-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,3}$`, email)
ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[_a-z\-A-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,6}$`, email)
return ok
}
@@ -314,4 +373,24 @@ func InArray(arr []string, str string) bool {
}
}
return false
}
}
// 将名称的特殊字符去掉
func FixFilename(filename string) string {
if filename != "" {
// 把特殊字段给替换掉
// str := `life "%&()+,/:;<>=?@\|`
// . == \\.
// $ === \\$
reg, _ := regexp.Compile("\\.|/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\")
filename = reg.ReplaceAllString(filename, "-")
filename = strings.Trim(filename, "-") // 左右单独的-去掉
// 把空格替换成-
// filename = strings.Replace(filename, " ", "-", -1)
for strings.Index(filename, "--") >= 0 { // 防止出现连续的--
filename = strings.Replace(filename, "--", "-", -1)
}
return filename
}
return filename
}

26
app/lea/crypto.go Normal file
View File

@@ -0,0 +1,26 @@
// contains two cryptographic functions for both storing and comparing passwords.
package lea
import (
"golang.org/x/crypto/bcrypt"
)
// GenerateHash generates bcrypt hash from plaintext password
func GenerateHash(password string) ([]byte, error) {
hex := []byte(password)
hashedPassword, err := bcrypt.GenerateFromPassword(hex, 10)
if err != nil {
return hashedPassword, err
}
return hashedPassword, nil
}
// CompareHash compares bcrypt password with a plaintext one. Returns true if passwords match
// and false if they do not.
func CompareHash(digest []byte, password string) bool {
hex := []byte(password)
if err := bcrypt.CompareHashAndPassword(digest, hex); err == nil {
return true
}
return false
}

View File

@@ -1,66 +0,0 @@
package memcache
import (
"github.com/robfig/gomemcache/memcache"
"encoding/json"
"strconv"
)
var client *memcache.Client
// onAppStart后调用
func InitMemcache() {
client = memcache.New("localhost:11211")
}
//------------
// map
func SetMap(key string, value map[string]string, expiration int32) {
// 把value转成byte
bytes, _ := json.Marshal(value)
if expiration == -1 {
expiration = 30 * 24 * 60 * 60 // 30天
}
client.Set(&memcache.Item{Key: key, Value: bytes, Expiration: expiration})
}
func GetMap(key string) map[string]string {
item, err := client.Get(key)
if err != nil {
return nil
}
m := map[string]string{}
json.Unmarshal(item.Value, &m)
return m
}
//------------
// string
func GetString(key string) string {
item, err := client.Get(key)
if err != nil {
return ""
}
return string(item.Value)
}
func SetString(key string, value string, expiration int32) {
if expiration == -1 {
expiration = 30 * 24 * 60 * 60 // 30天
}
client.Set(&memcache.Item{Key: key, Value: []byte(value), Expiration: expiration})
}
//-------------------------
// int, 是通过转成string来存的
func GetInt(key string) int {
str := GetString(key)
i, _ := strconv.Atoi(str)
return i
}
func SetInt(key string, value int, expiration int32) {
str := strconv.Itoa(value)
SetString(key, str, expiration)
}

View File

@@ -2,7 +2,7 @@ package route
import (
"github.com/revel/revel"
// "github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"net/url"
"strings"
@@ -42,6 +42,10 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
}
*/
if route.ControllerName != "Static" {
// 检查mongodb 是否lost
db.CheckMongoSessionLost()
// api设置
// leanote.com/api/user/get => ApiUser::Get
//* /api/login ApiAuth.Login, 这里的设置, 其实已经转成了ApiAuth了

View File

@@ -1,38 +0,0 @@
package session
import (
"github.com/revel/revel"
"github.com/leanote/leanote/app/lea/memcache"
. "github.com/leanote/leanote/app/lea"
)
// 使用filter
// 很巧妙就使用了memcache来处理session
// revel的session(cookie)只存sessionId, 其它信息存在memcache中
func MSessionFilter(c *revel.Controller, fc []revel.Filter) {
sessionId := c.Session.Id()
// 从memcache中得到cache, 赋给session
cache := revel.Session(memcache.GetMap(sessionId))
Log("memcache")
LogJ(cache)
if cache == nil {
cache = revel.Session{}
cache.Id()
}
c.Session = cache
// Make session vars available in templates as {{.session.xyz}}
c.RenderArgs["session"] = c.Session
fc[0](c, fc[1:])
// 再把session保存之
LogJ(c.Session)
memcache.SetMap(sessionId, c.Session, -1)
// 只留下sessionId
c.Session = revel.Session{revel.SESSION_ID_KEY: sessionId}
}

View File

@@ -1,208 +0,0 @@
package session
import (
"github.com/revel/revel"
// . "github.com/leanote/leanote/app/lea"
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
// 主要修改revel的cookie, 设置Domain
// 为了使sub domain共享cookie
// cookie.domain = leanote.com
// A signed cookie (and thus limited to 4kb in size).
// Restriction: Keys may not have a colon in them.
type Session map[string]string
const (
SESSION_ID_KEY = "_ID"
TIMESTAMP_KEY = "_TS"
)
// expireAfterDuration is the time to live, in seconds, of a session cookie.
// It may be specified in config as "session.expires". Values greater than 0
// set a persistent cookie with a time to live as specified, and the value 0
// sets a session cookie.
var expireAfterDuration time.Duration
var cookieDomain = "" // life
func init() {
// Set expireAfterDuration, default to 30 days if no value in config
revel.OnAppStart(func() {
var err error
if expiresString, ok := revel.Config.String("session.expires"); !ok {
expireAfterDuration = 30 * 24 * time.Hour
} else if expiresString == "session" {
expireAfterDuration = 0
} else if expireAfterDuration, err = time.ParseDuration(expiresString); err != nil {
panic(fmt.Errorf("session.expires invalid: %s", err))
}
cookieDomain, _ = revel.Config.String("cookie.domain")
})
}
// Id retrieves from the cookie or creates a time-based UUID identifying this
// session.
func (s Session) Id() string {
if sessionIdStr, ok := s[SESSION_ID_KEY]; ok {
return sessionIdStr
}
buffer := make([]byte, 32)
if _, err := rand.Read(buffer); err != nil {
panic(err)
}
s[SESSION_ID_KEY] = hex.EncodeToString(buffer)
return s[SESSION_ID_KEY]
}
// getExpiration return a time.Time with the session's expiration date.
// If previous session has set to "session", remain it
func (s Session) getExpiration() time.Time {
if expireAfterDuration == 0 || s[TIMESTAMP_KEY] == "session" {
// Expire after closing browser
return time.Time{}
}
return time.Now().Add(expireAfterDuration)
}
// cookie returns an http.Cookie containing the signed session.
func (s Session) cookie() *http.Cookie {
var sessionValue string
ts := s.getExpiration()
s[TIMESTAMP_KEY] = getSessionExpirationCookie(ts)
for key, value := range s {
if strings.ContainsAny(key, ":\x00") {
panic("Session keys may not have colons or null bytes")
}
if strings.Contains(value, "\x00") {
panic("Session values may not have null bytes")
}
sessionValue += "\x00" + key + ":" + value + "\x00"
}
sessionData := url.QueryEscape(sessionValue)
cookie := http.Cookie{
Name: revel.CookiePrefix + "_SESSION",
Value: revel.Sign(sessionData) + "-" + sessionData,
Path: "/",
HttpOnly: revel.CookieHttpOnly,
Secure: revel.CookieSecure,
Expires: ts.UTC(),
}
if cookieDomain != "" {
cookie.Domain = cookieDomain
}
return &cookie
}
// sessionTimeoutExpiredOrMissing returns a boolean of whether the session
// cookie is either not present or present but beyond its time to live; i.e.,
// whether there is not a valid session.
func sessionTimeoutExpiredOrMissing(session Session) bool {
if exp, present := session[TIMESTAMP_KEY]; !present {
return true
} else if exp == "session" {
return false
} else if expInt, _ := strconv.Atoi(exp); int64(expInt) < time.Now().Unix() {
return true
}
return false
}
// getSessionFromCookie returns a Session struct pulled from the signed
// session cookie.
func getSessionFromCookie(cookie *http.Cookie) Session {
session := make(Session)
// Separate the data from the signature.
hyphen := strings.Index(cookie.Value, "-")
if hyphen == -1 || hyphen >= len(cookie.Value)-1 {
return session
}
sig, data := cookie.Value[:hyphen], cookie.Value[hyphen+1:]
// Verify the signature.
if !revel.Verify(data, sig) {
revel.INFO.Println("Session cookie signature failed")
return session
}
revel.ParseKeyValueCookie(data, func(key, val string) {
session[key] = val
})
if sessionTimeoutExpiredOrMissing(session) {
session = make(Session)
}
return session
}
// SessionFilter is a Revel Filter that retrieves and sets the session cookie.
// Within Revel, it is available as a Session attribute on Controller instances.
// The name of the Session cookie is set as CookiePrefix + "_SESSION".
func SessionFilter(c *revel.Controller, fc []revel.Filter) {
session := restoreSession(c.Request.Request)
// c.Session, 重新生成一个revel.Session给controller!!!
// Log("sessoin--------")
// LogJ(session)
revelSession := revel.Session(session) // 强制转换 还是同一个对象, 但有个问题, 这样Session.Id()方法是用revel的了
c.Session = revelSession
// 生成sessionId
c.Session.Id()
sessionWasEmpty := len(c.Session) == 0
// Make session vars available in templates as {{.session.xyz}}
c.RenderArgs["session"] = c.Session
fc[0](c, fc[1:])
// Store the signed session if it could have changed.
if len(c.Session) > 0 || !sessionWasEmpty {
// 转换成lea.Session
session = Session(c.Session)
c.SetCookie(session.cookie())
}
}
// restoreSession returns either the current session, retrieved from the
// session cookie, or a new session.
func restoreSession(req *http.Request) Session {
cookie, err := req.Cookie(revel.CookiePrefix + "_SESSION")
if err != nil {
return make(Session)
} else {
return getSessionFromCookie(cookie)
}
}
// getSessionExpirationCookie retrieves the cookie's time to live as a
// string of either the number of seconds, for a persistent cookie, or
// "session".
func getSessionExpirationCookie(t time.Time) string {
if t.IsZero() {
return "session"
}
return strconv.FormatInt(t.Unix(), 10)
}
// SetNoExpiration sets session to expire when browser session ends
func (s Session) SetNoExpiration() {
s[TIMESTAMP_KEY] = "session"
}
// SetDefaultExpiration sets session to expire after default duration
func (s Session) SetDefaultExpiration() {
delete(s, TIMESTAMP_KEY)
}

View File

@@ -6,6 +6,7 @@ import (
"os/exec"
"io/ioutil"
"strings"
// "time"
)
/*
@@ -32,7 +33,7 @@ import (
//var jss = []string{"js/jquery-cookie", "js/bootstrap"}
var jss = []string{"js/jquery-cookie", "js/bootstrap",
"js/common", "js/app/note", "js/app/tag", "js/app/notebook", "js/app/share",
"js/object_id"}
"js/object_id", "js/ZeroClipboard/ZeroClipboard"}
var base1 = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/"
var base = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/public/"
@@ -91,7 +92,6 @@ func dev() {
"notebook.js": "notebook-min.js",
"share.js": "share-min.js",
"tag.js": "tag-min.js",
"main.js": "main-min.js",
"jquery.slimscroll.js": "jquery.slimscroll-min.js",
"jquery.contextmenu.js": "jquery.contextmenu-min.js",
"editor/editor.js": "editor/editor-min.js",
@@ -107,6 +107,13 @@ func dev() {
for key, value := range m {
content = strings.Replace(content, key, value, -1)
}
// var time = time.Now().Unix() % 1000
// content = strings.Replace(content, "-min.js", fmt.Sprintf("-min.js?r=%d", time), -1)
// content = strings.Replace(content, "default{{end}}.css", fmt.Sprintf("default{{end}}.css?r=%d", time), 1)
// content = strings.Replace(content, "writting-overwrite.css", fmt.Sprintf("writting-overwrite.css?r=%d", time), 1)
ioutil.WriteFile(target, []byte(content), os.ModeAppend)
}
@@ -114,8 +121,10 @@ func dev() {
func tinymce() {
// cmdStr := "node_modules/jake/bin/cli.js minify bundle[themes:modern,plugins:table,paste,advlist,autolink,link,image,lists,charmap,hr,searchreplace,visualblocks,visualchars,code,nav,tabfocus,contextmenu,directionality,codemirror,codesyntax,textcolor,fullpage]"
// cmd := exec.Command("/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master/node_modules/jake/bin/cli.js", "minify", "bundle[themes:modern,plugins:table,paste,advlist,autolink,link,image,lists,charmap,hr,searchreplace,visualblocks,visualchars,code,nav,tabfocus,contextmenu,directionality,codemirror,codesyntax,textcolor,fullpage]")
cmd := exec.Command("/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master/node_modules/jake/bin/cli.js", "minify")
cmd.Dir = "/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master"
cmd := exec.Command("/bin/sh", "-c", "grunt minify");
cmd.Dir = base + "/tinymce_4.1.9"
fmt.Println("正在build tinymce");
// 必须要先删除
cmd2 := exec.Command("/bin/sh", "-c", "rm " + cmd.Dir + "/js/tinymce/tinymce.dev.js")
@@ -123,32 +132,19 @@ func tinymce() {
cmd2 = exec.Command("/bin/sh", "-c", "rm " + cmd.Dir + "/js/tinymce/tinymce.jquery.dev.js")
c, _ := cmd2.CombinedOutput()
fmt.Println(string(c))
c, _ = cmd.CombinedOutput()
fmt.Println(string(c))
}
func main() {
// 压缩tinymce
tinymce()
// tinymce()
dev();
// 其它零散的需要压缩的js
otherJss := []string{"tinymce/tinymce", "js/main", "js/app/page", "js/contextmenu/jquery.contextmenu",
"mdeditor/editor/scrollLink",
"mdeditor/editor/editor",
"mdeditor/editor/jquery.waitforimages",
"mdeditor/editor/pagedown/local/Markdown.local.zh",
"mdeditor/editor/pagedown/local/Markdown.local.en",
"mdeditor/editor/pagedown/Markdown.Editor",
"mdeditor/editor/pagedown/Markdown.Sanitizer",
"mdeditor/editor/pagedown/Markdown.Converter",
"mdeditor/editor/Markdown.Extra",
"mdeditor/editor/underscore",
"mdeditor/editor/mathJax",
"js/jQuery-slimScroll-1.3.0/jquery.slimscroll",
"js/app/editor_drop_paste",
"js/app/attachment_upload",
otherJss := []string{"js/main", "js/app/page", "js/contextmenu/jquery.contextmenu",
"js/jquery.ztree.all-3.5",
"js/jQuery-slimScroll-1.3.0/jquery.slimscroll",
}

View File

@@ -63,10 +63,19 @@ 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
}
// 笔记是否是自己的
note := noteService.GetNoteByIdAndUserId(noteId, userId)
if note.NoteId == "" {
return attachs
}
// TODO 这里, 优化权限控制
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)

View File

@@ -2,26 +2,31 @@ package service
import (
"gopkg.in/mgo.v2/bson"
// "github.com/leanote/leanote/app/db"
// "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
// "github.com/revel/revel"
"strings"
. "github.com/leanote/leanote/app/lea"
// "github.com/revel/revel"
"errors"
"fmt"
. "github.com/leanote/leanote/app/lea"
"strconv"
"strings"
)
// 登录与权限
// 登录与权限 Login & Register
type AuthService struct {
}
// pwd已md5了
func (this *AuthService) Login(emailOrUsername, pwd string) info.User {
// 使用bcrypt认证或者Md5认证
// Use bcrypt (Md5 depreciated)
func (this *AuthService) Login(emailOrUsername, pwd string) (info.User, error) {
emailOrUsername = strings.Trim(emailOrUsername, " ")
// pwd = strings.Trim(pwd, " ")
userInfo := userService.LoginGetUserInfo(emailOrUsername, Md5(pwd))
return userInfo
// pwd = strings.Trim(pwd, " ")
userInfo := userService.GetUserInfoByName(emailOrUsername)
if userInfo.UserId == "" || !ComparePwd(pwd, userInfo.Pwd) {
return userInfo, errors.New("wrong username or password")
}
return userInfo, nil
}
// 注册
@@ -40,71 +45,74 @@ func (this *AuthService) Register(email, pwd, fromUserId string) (bool, string)
if userService.IsExistsUser(email) {
return false, "userHasBeenRegistered-" + email
}
user := info.User{UserId: bson.NewObjectId(), Email: email, Username: email, Pwd: Md5(pwd)}
passwd := GenPwd(pwd)
if passwd == "" {
return false, "GenerateHash error"
}
user := info.User{UserId: bson.NewObjectId(), Email: email, Username: email, Pwd: passwd}
if fromUserId != "" && IsObjectId(fromUserId) {
user.FromUserId = bson.ObjectIdHex(fromUserId)
}
LogJ(user)
return this.register(user)
}
func (this *AuthService) register(user info.User) (bool, string) {
if userService.AddUser(user) {
// 添加笔记本, 生活, 学习, 工作
userId := user.UserId.Hex();
userId := user.UserId.Hex()
notebook := info.Notebook{
Seq: -1,
Seq: -1,
UserId: user.UserId}
title2Id := map[string]bson.ObjectId{"life": bson.NewObjectId(), "study": bson.NewObjectId(), "work": bson.NewObjectId()}
for title, objectId := range title2Id {
notebook.Title = title
notebook.NotebookId = objectId
notebook.UserId = user.UserId
notebookService.AddNotebook(notebook);
notebookService.AddNotebook(notebook)
}
// 添加leanote -> 该用户的共享
registerSharedUserId := configService.GetGlobalStringConfig("registerSharedUserId")
if(registerSharedUserId != "") {
if registerSharedUserId != "" {
registerSharedNotebooks := configService.GetGlobalArrMapConfig("registerSharedNotebooks")
registerSharedNotes := configService.GetGlobalArrMapConfig("registerSharedNotes")
registerCopyNoteIds := configService.GetGlobalArrayConfig("registerCopyNoteIds")
// 添加共享笔记本
for _, notebook := range registerSharedNotebooks {
perm, _ := strconv.Atoi(notebook["perm"])
shareService.AddShareNotebookToUserId(notebook["notebookId"], perm, registerSharedUserId, userId);
shareService.AddShareNotebookToUserId(notebook["notebookId"], perm, registerSharedUserId, userId)
}
// 添加共享笔记
for _, note := range registerSharedNotes {
perm, _ := strconv.Atoi(note["perm"])
shareService.AddShareNoteToUserId(note["noteId"], perm, registerSharedUserId, userId);
shareService.AddShareNoteToUserId(note["noteId"], perm, registerSharedUserId, userId)
}
// 复制笔记
for _, noteId := range registerCopyNoteIds {
note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex());
// Log(noteId)
// Log("Copy")
// LogJ(note)
note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex())
// Log(noteId)
// Log("Copy")
// LogJ(note)
noteUpdate := bson.M{"IsBlog": false} // 不要是博客
noteService.UpdateNote(user.UserId.Hex(), note.NoteId.Hex(), noteUpdate, -1)
}
}
//---------------
// 添加一条userBlog
blogService.UpdateUserBlog(info.UserBlog{UserId: user.UserId,
Title: user.Username + " 's Blog",
SubTitle: "Love Leanote!",
AboutMe: "Hello, I am (^_^)",
blogService.UpdateUserBlog(info.UserBlog{UserId: user.UserId,
Title: user.Username + " 's Blog",
SubTitle: "Love Leanote!",
AboutMe: "Hello, I am (^_^)",
CanComment: true,
})
})
// 添加一个单页面
blogService.AddOrUpdateSingle(user.UserId.Hex(), "", "About Me", "Hello, I am (^_^)")
}
return true, ""
}
@@ -115,7 +123,7 @@ func (this *AuthService) register(user info.User) (bool, string) {
func (this *AuthService) getUsername(thirdType, thirdUsername string) (username string) {
username = thirdType + "-" + thirdUsername
i := 1
for ;; {
for {
if !userService.IsExistsUserByUsername(username) {
return
}
@@ -131,11 +139,11 @@ func (this *AuthService) ThirdRegister(thirdType, thirdUserId, thirdUsername str
}
username := this.getUsername(thirdType, thirdUsername)
userInfo = info.User{UserId: bson.NewObjectId(),
Username: username,
ThirdUserId: thirdUserId,
userInfo = info.User{UserId: bson.NewObjectId(),
Username: username,
ThirdUserId: thirdUserId,
ThirdUsername: thirdUsername,
}
}
_, _ = this.register(userInfo)
return
}

View File

@@ -45,7 +45,9 @@ func (this *BlogService) GetBlogByIdAndUrlTitle(userId string, noteIdOrUrlTitle
return this.GetBlog(noteIdOrUrlTitle)
}
note := info.Note{}
db.GetByQ(db.Notes, bson.M{"UserId": bson.ObjectIdHex(userId), "UrlTitle": encodeValue(noteIdOrUrlTitle), "IsBlog": true, "IsTrash": false}, &note)
db.GetByQ(db.Notes, bson.M{"UserId": bson.ObjectIdHex(userId), "UrlTitle": encodeValue(noteIdOrUrlTitle),
"IsBlog": true,
"IsTrash": false, "IsDeleted": false}, &note)
return this.GetBlogItem(note)
}
@@ -56,7 +58,7 @@ func (this *BlogService) GetBlog(noteId string) (blog info.BlogItem) {
}
func (this *BlogService) GetBlogItem(note info.Note) (blog info.BlogItem) {
if note.NoteId == "" || !note.IsBlog {
return
return info.BlogItem{}
}
// 内容
@@ -131,7 +133,8 @@ func (this *BlogService) ListBlogs(userId, notebookId string, page, pageSize int
func (this *BlogService) GetBlogTags(userId string) []info.TagCount {
// 得到所有博客
tagCounts := []info.TagCount{}
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true}
// tag不能为空
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "Tag": bson.M{"$ne": ""}}
db.TagCounts.Find(query).Sort("-Count").All(&tagCounts)
return tagCounts
}
@@ -142,7 +145,7 @@ func (this *BlogService) ReCountBlogTags(userId string) bool {
// 得到所有博客
notes := []info.Note{}
userIdO := bson.ObjectIdHex(userId)
query := bson.M{"UserId": userIdO, "IsTrash": false, "IsBlog": true}
query := bson.M{"UserId": userIdO, "IsTrash": false, "IsDeleted": false, "IsBlog": true}
db.ListByQWithFields(db.Notes, query, []string{"Tags"}, &notes)
db.DeleteAll(db.TagCounts, bson.M{"UserId": userIdO, "IsBlog": true})
@@ -184,7 +187,7 @@ Posts: []
*/
func (this *BlogService) ListBlogsArchive(userId, notebookId string, year, month int, sortField string, isAsc bool) []info.Archive {
// _, notes := noteService.ListNotes(userId, notebookId, false, 1, 99999, sortField, isAsc, true);
q := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "IsTrash": false}
q := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "IsTrash": false, "IsDeleted": false}
if notebookId != "" {
q["NotebookId"] = bson.ObjectIdHex(notebookId)
}
@@ -291,9 +294,10 @@ func (this *BlogService) SearchBlogByTags(tags []string, userId string, pageNumb
// 不是trash的
query := bson.M{"UserId": bson.ObjectIdHex(userId),
"IsTrash": false,
"IsBlog": true,
"Tags": bson.M{"$all": tags}}
"IsTrash": false,
"IsDeleted": false,
"IsBlog": true,
"Tags": bson.M{"$all": tags}}
q := db.Notes.Find(query)
@@ -366,10 +370,10 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
if !isAsc {
// 降序
/*
------- pre
----- now
--- next
--
------- pre
----- now
--- next
--
*/
// 上一篇时间要比它大, 找最小的
sortFieldT1 = bson.M{"$gte": baseTime} // 为什么要相等, 因为将notebook发布成博客, 会统一修改note的publicTime, 此时所有notes都一样
@@ -393,12 +397,14 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
sortFieldR2 = sorterField
}
// 1
// 上一篇, 比基时间要小, 但是是最后一篇, 所以是降序
note := info.Note{}
query := bson.M{"UserId": userIdO,
"IsTrash": false,
"IsBlog": true,
"_id": bson.M{"$ne": bson.ObjectIdHex(noteId)},
query := bson.M{"UserId": userIdO,
"IsTrash": false,
"IsDeleted": false,
"IsBlog": true,
"_id": bson.M{"$ne": bson.ObjectIdHex(noteId)},
sorterField: sortFieldT1,
}
q := db.Notes.Find(query)
@@ -430,7 +436,7 @@ func (this *BlogService) ListAllBlogs(userId, tag string, keywords string, isRec
skipNum, sortFieldR := parsePageAndSort(page, pageSize, sorterField, isAsc)
// 不是trash的
query := bson.M{"IsTrash": false, "IsBlog": true, "Title": bson.M{"$ne": "欢迎来到leanote!"}}
query := bson.M{"IsTrash": false, "IsDeleted": false, "IsBlog": true, "Title": bson.M{"$ne": "欢迎来到leanote!"}}
if tag != "" {
query["Tags"] = bson.M{"$in": []string{tag}}
}
@@ -496,6 +502,9 @@ func (this *BlogService) ListAllBlogs(userId, tag string, keywords string, isRec
content = noteContent.Abstract
}
*/
if len(note.Tags) == 1 && note.Tags[0] == "" {
note.Tags = nil
}
blogs[i] = info.BlogItem{note, "", content, hasMore, userMap[note.UserId]}
}
pageInfo = info.NewPage(page, pageSize, count, nil)
@@ -1197,6 +1206,10 @@ func (this *BlogService) FixBlog(blog info.BlogItem) info.Post {
LikeNum: blog.LikeNum,
IsMarkdown: blog.IsMarkdown,
}
if blog2.Tags != nil && len(blog2.Tags) > 0 && blog2.Tags[0] != "" {
} else {
blog2.Tags = nil
}
return blog2
}

View File

@@ -305,7 +305,7 @@ func (this *ConfigService) Backup(remark string) (ok bool, msg string) {
username, _ := revel.Config.String("db.username")
password, _ := revel.Config.String("db.password")
// mongodump -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
binPath = binPath + " -h " + host + " -d " + dbname + " -port " + port
binPath = binPath + " -h " + host + " -d " + dbname + " --port " + port
if username != "" {
binPath += " -u " + username + " -p " + password
}
@@ -361,7 +361,7 @@ func (this *ConfigService) Restore(createdTime string) (ok bool, msg string) {
username, _ := revel.Config.String("db.username")
password, _ := revel.Config.String("db.password")
// mongorestore -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
binPath = binPath + " --drop -h " + host + " -d " + dbname + " -port " + port
binPath = binPath + " --drop -h " + host + " -d " + dbname + " --port " + port
if username != "" {
binPath += " -u " + username + " -p " + password
}
@@ -372,7 +372,7 @@ func (this *ConfigService) Restore(createdTime string) (ok bool, msg string) {
return false, path + " Is Not Exists"
}
binPath += " --directoryperdb " + path
binPath += " " + path
cmd := exec.Command("/bin/sh", "-c", binPath)
Log(binPath);
@@ -455,12 +455,15 @@ var port string
func init() {
revel.OnAppStart(func() {
/*
不用配置的, 因为最终通过命令可以改, 而且有的使用nginx代理
port = strconv.Itoa(revel.HttpPort)
if port != "80" {
port = ":" + port
} else {
port = "";
}
*/
siteUrl, _ := revel.Config.String("site.url") // 已包含:9000, http, 去掉成 leanote.com
if strings.HasPrefix(siteUrl, "http://") {
@@ -469,6 +472,17 @@ func init() {
defaultDomain = siteUrl[len("https://"):]
schema = "https://"
}
// port localhost:9000
ports := strings.Split(defaultDomain, ":")
if len(ports) == 2 {
port = ports[1]
}
if port == "80" {
port = ""
} else {
port = ":" + port
}
})
}
@@ -580,5 +594,5 @@ func (this *ConfigService) HomePageIsAdminsBlog() bool {
}
func (this *ConfigService) GetVersion() string {
return "1.0-beta.4"
return "1.3.1"
}

View File

@@ -1,14 +1,18 @@
package service
import (
"encoding/base64"
"fmt"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson"
"time"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
)
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
@@ -115,6 +119,53 @@ func (this *FileService) UpdateImage(userId, fileId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
}
func (this *FileService) GetFileBase64(userId, fileId string) (str string, mine string) {
defer func() { // 必须要先声明defer否则不能捕获到panic异常
if err := recover(); err != nil {
fmt.Println(err) // 这里的err其实就是panic传入的内容55
}
}()
path := this.GetFile(userId, fileId)
if path == "" {
return "", ""
}
path = revel.BasePath + "/" + strings.TrimLeft(path, "/")
ff, err := ioutil.ReadFile(path)
if err != nil {
return "", ""
}
e64 := base64.StdEncoding
maxEncLen := e64.EncodedLen(len(ff))
encBuf := make([]byte, maxEncLen)
e64.Encode(encBuf, ff)
mime := http.DetectContentType(ff)
str = string(encBuf)
return str, mime
}
// 得到图片base64, 图片要在之前添加data:image/png;base64,
func (this *FileService) GetImageBase64(userId, fileId string) string {
str, mime := this.GetFileBase64(userId, fileId)
if str == "" {
return ""
}
switch mime {
case "image/gif", "image/jpeg", "image/pjpeg", "image/png", "image/tiff":
return fmt.Sprintf("data:%s;base64,%s", mime, str)
default:
}
return "data:image/png;base64," + str
}
// 获取文件路径
// 要判断是否具有权限
// userId是否具有fileId的访问权限

View File

@@ -32,11 +32,21 @@ func (this *GroupService) DeleteGroup(userId, groupId string) (ok bool, msg stri
return false, "groupHasUsers"
}
*/
if !this.isMyGroup(userId, groupId) {
return false, "notMyGroup"
}
// 删除分组后, 需要删除所有用户分享到该组的笔记本, 笔记
shareService.DeleteAllShareNotebookGroup(groupId);
shareService.DeleteAllShareNoteGroup(groupId);
db.DeleteAll(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId)})
return db.DeleteByIdAndUserId(db.Groups, groupId, userId), ""
// TODO 删除分组后, 在shareNote, shareNotebook中也要删除
}
// 修改group标题
func (this *GroupService) UpdateGroupTitle(userId, groupId, title string) (ok bool) {
return db.UpdateByIdAndUserIdField(db.Groups, groupId, userId, "Title", title)
@@ -44,9 +54,14 @@ func (this *GroupService) UpdateGroupTitle(userId, groupId, title string) (ok bo
// 得到用户的所有分组(包括下的所有用户)
func (this *GroupService) GetGroupsAndUsers(userId string) ([]info.Group) {
// 得到分组s
/*
// 得到我的分组
groups := []info.Group{}
db.ListByQ(db.Groups, bson.M{"UserId": bson.ObjectIdHex(userId)}, &groups)
*/
// 我的分组, 及我所属的分组
groups := this.GetGroupsContainOf(userId);
// 得到其下的用户
for i, group := range groups {
group.Users = this.GetUsers(group.GroupId.Hex())
@@ -61,6 +76,55 @@ func (this *GroupService) GetGroups(userId string) ([]info.Group) {
db.ListByQ(db.Groups, bson.M{"UserId": bson.ObjectIdHex(userId)}, &groups)
return groups
}
// 得到我的和我所属组的ids
func (this *GroupService) GetMineAndBelongToGroupIds(userId string) ([]bson.ObjectId) {
// 所属组
groupIds := this.GetBelongToGroupIds(userId)
m := map[bson.ObjectId]bool{}
for _, groupId := range groupIds {
m[groupId] = true
}
// 我的组
myGroups := this.GetGroups(userId)
for _, group := range myGroups {
if !m[group.GroupId] {
groupIds = append(groupIds, group.GroupId)
}
}
return groupIds
}
// 获取包含此用户的组对象数组
// 获取该用户所属组, 和我的组
func (this *GroupService) GetGroupsContainOf(userId string) []info.Group {
// 我的组
myGroups := this.GetGroups(userId)
myGroupMap := map[bson.ObjectId]bool{}
for _, group := range myGroups {
myGroupMap[group.GroupId] = true
}
// 所属组
groupIds := this.GetBelongToGroupIds(userId)
groups := []info.Group{}
db.ListByQ(db.Groups, bson.M{"_id": bson.M{"$in": groupIds}}, &groups)
for _, group := range groups {
if !myGroupMap[group.GroupId] {
myGroups = append(myGroups, group)
}
}
return myGroups
}
// 得到分组, shareService用
func (this *GroupService) GetGroup(userId, groupId string) (info.Group) {
// 得到分组s
@@ -102,11 +166,26 @@ func (this *GroupService) GetBelongToGroupIds(userId string) ([]bson.ObjectId) {
func (this *GroupService) isMyGroup(ownUserId, groupId string) (ok bool) {
return db.Has(db.Groups, bson.M{"_id": bson.ObjectIdHex(groupId), "UserId": bson.ObjectIdHex(ownUserId)})
}
}
// 判断组中是否包含指定用户
func (this *GroupService) IsExistsGroupUser(userId, groupId string) (ok bool) {
// 如果我拥有这个组, 那也行
if this.isMyGroup(userId, groupId) {
return true
}
return db.Has(db.GroupUsers, bson.M{"UserId": bson.ObjectIdHex(userId), "GroupId": bson.ObjectIdHex(groupId)})
}
// 为group添加用户
// 用户是否已存在?
func (this *GroupService) AddUser(ownUserId, groupId, userId string) (ok bool, msg string) {
// groupId是否是ownUserId的?
/*
if !this.IsExistsGroupUser(ownUserId, groupId) {
return false, "forbidden"
}
*/
if !this.isMyGroup(ownUserId, groupId) {
return false, "forbidden"
}
@@ -123,11 +202,22 @@ func (this *GroupService) AddUser(ownUserId, groupId, userId string) (ok bool, m
CreatedTime: time.Now(),
}), ""
}
// 删除用户
func (this *GroupService) DeleteUser(ownUserId, groupId, userId string) (ok bool, msg string) {
// groupId是否是ownUserId的?
/*
if !this.IsExistsGroupUser(ownUserId, groupId) {
return false, "forbidden"
}
*/
if !this.isMyGroup(ownUserId, groupId) {
return false, "forbidden"
}
// 删除该用户分享到本组的笔记本, 笔记
shareService.DeleteShareNotebookGroupWhenDeleteGroupUser(userId, groupId);
shareService.DeleteShareNoteGroupWhenDeleteGroupUser(userId, groupId);
return db.Delete(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId), "UserId": bson.ObjectIdHex(userId)}), ""
}

View File

@@ -73,17 +73,13 @@ func (this *NoteImageService) UpdateNoteImages(userId, noteId, imgSrc, content s
// 复制图片, 把note的图片都copy给我, 且修改noteContent图片路径
func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId, content, toUserId string) string {
/* 弃用之
// 得到fromNoteId的noteImages, 如果为空, 则直接返回content
noteImages := []info.NoteImage{}
db.ListByQWithFields(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(fromNoteId)}, []string{"ImageId"}, &noteImages)
if len(noteImages) == 0 {
return content;
}
// <img src="/file/outputImage?fileId=12323232" />
// 把fileId=1232替换成新的
replaceMap := map[string]string{}
for _, noteImage := range noteImages {
imageId := noteImage.ImageId.Hex()
ok, newImageId := fileService.CopyImage(fromUserId, imageId, toUserId)
@@ -91,20 +87,44 @@ func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId,
replaceMap[imageId] = newImageId
}
}
if len(replaceMap) > 0 {
// 替换之
reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})")
content = reg.ReplaceAllStringFunc(content, func(each string) string {
// each=outputImage?fileId=541bd2f599c37b4f3r000003
fileId := each[len(each)-24:] // 得到后24位, 也即id
if replaceFileId, ok := replaceMap[fileId]; ok {
*/
// 因为很多图片上传就会删除, 所以直接从内容中查看图片id进行复制
// <img src="/file/outputImage?fileId=12323232" />
// 把fileId=1232替换成新的
replaceMap := map[string]string{}
reg, _ := regexp.Compile("(outputImage|getImage)\\?fileId=([a-z0-9A-Z]{24})")
content = reg.ReplaceAllStringFunc(content, func(each string) string {
// each = outputImage?fileId=541bd2f599c37b4f3r000003
// each = getImage?fileId=541bd2f599c37b4f3r000003
fileId := each[len(each)-24:] // 得到后24位, 也即id
if _, ok := replaceMap[fileId]; !ok {
if bson.IsObjectIdHex(fileId) {
ok2, newImageId := fileService.CopyImage(fromUserId, fileId, toUserId)
if ok2 {
replaceMap[fileId] = newImageId
} else {
replaceMap[fileId] = ""
}
} else {
replaceMap[fileId] = ""
}
}
replaceFileId := replaceMap[fileId]
if replaceFileId != "" {
if each[0] == 'o' {
return "outputImage?fileId=" + replaceFileId
}
return each
});
}
return "getImage?fileId=" + replaceFileId
}
return each
});
return content;
}

View File

@@ -18,16 +18,29 @@ func (this *NoteService) GetNote(noteId, userId string) (note info.Note) {
return
}
// fileService调用
// 不能是已经删除了的, life bug, 客户端删除后, 竟然还能在web上打开
func (this *NoteService) GetNoteById(noteId string) (note info.Note) {
note = info.Note{}
db.Get(db.Notes, noteId, &note)
if noteId == "" {
return
}
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsDeleted": false}, &note)
return
}
func (this *NoteService) GetNoteByIdAndUserId(noteId, userId string) (note info.Note) {
note = info.Note{}
if noteId == "" || userId == "" {
return
}
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "UserId": bson.ObjectIdHex(userId), "IsDeleted": false}, &note)
return
}
// 得到blog, blogService用
// 不要传userId, 因为是公开的
func (this *NoteService) GetBlogNote(noteId string) (note info.Note) {
note = info.Note{}
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsBlog": true, "IsTrash": false}, &note)
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId),
"IsBlog": true, "IsTrash": false, "IsDeleted": false}, &note)
return
}
// 通过id, userId得到noteContent
@@ -238,7 +251,6 @@ func (this *NoteService) AddNote(note info.Note, fromApi bool) info.Note {
// api会传IsBlog, web不会传
if !fromApi {
note.PublicTime = note.UpdatedTime
// 设为blog
note.IsBlog = notebookService.IsBlog(notebookId)
}
@@ -418,7 +430,7 @@ func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bso
if isBlog, ok := needUpdate["IsBlog"]; ok {
isBlog2 := isBlog.(bool)
if note.IsBlog != isBlog2 {
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog2})
this.UpdateNoteContentIsBlog(noteId, userId, isBlog2);
// 重新发布成博客
if !note.IsBlog {
@@ -459,6 +471,11 @@ func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bso
return true, "", afterUsn
}
// 当设置/取消了笔记为博客
func (this *NoteService) UpdateNoteContentIsBlog(noteId, userId string, isBlog bool) {
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog})
}
// 附件修改, 增加noteIncr
func (this *NoteService) IncrNoteUsn(noteId, userId string) int {
afterUsn := userService.IncrUsn(userId)
@@ -572,6 +589,8 @@ func (this *NoteService) ToBlog(userId, noteId string, isBlog, isTop bool) bool
ok := db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, noteUpdate)
// 重新计算tags
go (func() {
this.UpdateNoteContentIsBlog(noteId, userId, isBlog);
blogService.ReCountBlogTags(userId)
})()
return ok
@@ -645,11 +664,11 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
note.NotebookId = bson.ObjectIdHex(notebookId)
noteContent.NoteId = note.NoteId
this.AddNoteAndContent(note, noteContent, note.UserId);
note = this.AddNoteAndContent(note, noteContent, note.UserId);
// 更新blog状态
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId)
// recount
notebookService.ReCountNotebookNumberNotes(notebookId)
@@ -664,16 +683,15 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
// 复制别人的共享笔记给我
// 将别人可用的图片转为我的图片, 复制图片
func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId string) info.Note {
// Log(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId))
// 判断是否共享了给我
if notebookService.IsMyNotebook(notebookId, myUserId) &&
(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) {
// Log(notebookService.IsMyNotebook(notebookId, myUserId))
if notebookService.IsMyNotebook(notebookId, myUserId) && shareService.HasReadPerm(fromUserId, myUserId, noteId) {
note := this.GetNote(noteId, fromUserId)
if note.NoteId == "" {
return info.Note{}
}
noteContent := this.GetNoteContent(noteId, fromUserId)
// 重新生成noteId
note.NoteId = bson.NewObjectId();
note.NotebookId = bson.ObjectIdHex(notebookId)
@@ -734,6 +752,7 @@ func (this *NoteService) SearchNote(key, userId string, pageNumber, pageSize int
// 不是trash的
query := bson.M{"UserId": bson.ObjectIdHex(userId),
"IsTrash": false,
"IsDeleted": false, // 不能搜索已删除了的
"$or": orQ,
}
if isBlog {
@@ -819,14 +838,14 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb
//------------
// 统计
func (this *NoteService) CountNote(userId string) int {
q := bson.M{"IsTrash": false}
q := bson.M{"IsTrash": false, "IsDeleted": false}
if userId != "" {
q["UserId"] = bson.ObjectIdHex(userId)
}
return db.Count(db.Notes, q)
}
func (this *NoteService) CountBlog(userId string) int {
q := bson.M{"IsBlog": true, "IsTrash": false}
q := bson.M{"IsBlog": true, "IsTrash": false, "IsDeleted": false}
if userId != "" {
q["UserId"] = bson.ObjectIdHex(userId)
}

View File

@@ -8,6 +8,8 @@ import (
. "github.com/leanote/leanote/app/lea"
"sort"
"time"
"strings"
// "html"
)
// 笔记本
@@ -40,7 +42,9 @@ func ParseAndSortNotebooks(userNotebooks []info.Notebook, noParentDelete, needSo
for _, each := range userNotebooks {
newNotebooks := info.Notebooks{Subs: info.SubNotebooks{}}
newNotebooks.NotebookId = each.NotebookId
newNotebooks.Title = each.Title
newNotebooks.Title = each.Title;
// newNotebooks.Title = html.EscapeString(each.Title)
newNotebooks.Title = strings.Replace(strings.Replace(each.Title, "<script>", "", -1), "</script", "", -1)
newNotebooks.Seq = each.Seq
newNotebooks.UserId = each.UserId
newNotebooks.ParentNotebookId = each.ParentNotebookId
@@ -271,11 +275,15 @@ func (this *NotebookService) ToBlog(userId, notebookId string, isBlog bool) (boo
// 查看是否有子notebook
// 先查看该notebookId下是否有notes, 没有则删除
func (this *NotebookService) DeleteNotebook(userId, notebookId string) (bool, string) {
if db.Count(db.Notebooks, bson.M{"ParentNotebookId": bson.ObjectIdHex(notebookId),
"UserId": bson.ObjectIdHex(userId)}) == 0 { // 无
if db.Count(db.Notebooks, bson.M{
"ParentNotebookId": bson.ObjectIdHex(notebookId),
"UserId": bson.ObjectIdHex(userId),
"IsDeleted": false,
}) == 0 { // 无
if db.Count(db.Notes, bson.M{"NotebookId": bson.ObjectIdHex(notebookId),
"UserId": bson.ObjectIdHex(userId),
"IsTrash": false}) == 0 { // 不包含trash
"IsTrash": false,
"IsDeleted": false}) == 0 { // 不包含trash
// 不是真删除 1/20, 为了同步笔记本
ok := db.UpdateByQMap(db.Notebooks, bson.M{"_id": bson.ObjectIdHex(notebookId)}, bson.M{"IsDeleted": true, "Usn": userService.IncrUsn(userId)})
return ok, ""

View File

@@ -45,9 +45,13 @@ func (this *PwdService) UpdatePwd(token, pwd string) (bool, string) {
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenPwd); !ok {
return ok, msg
}
digest, err := GenerateHash(pwd)
if err != nil {
return false,"GenerateHash error"
}
passwd := string(digest)
// 修改密码之
ok = db.UpdateByQField(db.Users, bson.M{"_id": tokenInfo.UserId}, "Pwd", Md5(pwd))
ok = db.UpdateByQField(db.Users, bson.M{"_id": tokenInfo.UserId}, "Pwd", passwd)
// 删除token
tokenService.DeleteToken(tokenInfo.UserId.Hex(), info.TokenPwd)

View File

@@ -33,8 +33,9 @@ type ShareService struct {
// 谁共享给了我的Query
func (this *ShareService) getOrQ(userId string) bson.M {
// 得到我参与的组织
groupIds := groupService.GetBelongToGroupIds(userId)
// 得到我和和我参与的组织
groupIds := groupService.GetMineAndBelongToGroupIds(userId)
q := bson.M{}
if len(groupIds) > 0 {
orQ := []bson.M{
@@ -51,6 +52,8 @@ func (this *ShareService) getOrQ(userId string) bson.M {
// 得到共享给我的笔记本和用户(谁共享给了我)
func (this *ShareService) GetShareNotebooks(userId string) (info.ShareNotebooksByUser, []info.User) {
myUserId := userId
// 得到共享给我的用户s信息
// 得到我参与的组织
q := this.getOrQ(userId)
@@ -64,7 +67,15 @@ func (this *ShareService) GetShareNotebooks(userId string) (info.ShareNotebooksB
db.Distinct(db.ShareNotebooks, q, "UserId", &userIds2) // BUG之前是userId1, 2014/12/29
userIds := append(userIds1, userIds2...)
userInfos := userService.GetUserInfosOrderBySeq(userIds);
// 不要我的id
for i, userInfo := range userInfos {
if userInfo.UserId.Hex() == myUserId {
userInfos = append(userInfos[:i], userInfos[i+1:]...)
break;
}
}
//--------------------
// 得到他们共享给我的notebooks
@@ -102,6 +113,11 @@ func (this *ShareService) GetShareNotebooks(userId string) (info.ShareNotebooksB
// 先建立userId => []
for _, eachSub := range subShareNotebooks {
userId := eachSub.Notebook.UserId
// 我自己的, 算了
if userId.Hex() == myUserId {
continue;
}
if _, ok := shareNotebooksByUsersMap[userId]; ok {
shareNotebooksByUsersMap[userId] = append(shareNotebooksByUsersMap[userId], eachSub)
} else {
@@ -674,8 +690,7 @@ func (this *ShareService) HasReadNotePerm(noteId, userId string) bool {
// 得到笔记分享给的groups
func (this *ShareService) GetNoteShareGroups(noteId, userId string) []info.ShareNote {
// 得到分组s
groups := groupService.GetGroups(userId)
groups := groupService.GetGroupsContainOf(userId)
// 得到有分享的分组
shares := []info.ShareNote{}
@@ -702,9 +717,7 @@ func (this *ShareService) GetNoteShareGroups(noteId, userId string) []info.Share
// 共享笔记给分组
func (this *ShareService) AddShareNoteGroup(userId, noteId, groupId string, perm int) (bool) {
// 得到group, 是否是我的group
group := groupService.GetGroup(userId, groupId)
if group.GroupId == "" {
if !groupService.IsExistsGroupUser(userId, groupId) {
return false
}
@@ -732,8 +745,7 @@ func (this *ShareService) DeleteShareNoteGroup(userId, noteId, groupId string) b
// 得到笔记本分享给的groups
func (this *ShareService) GetNotebookShareGroups(notebookId, userId string) []info.ShareNotebook {
// 得到分组s
groups := groupService.GetGroups(userId)
groups := groupService.GetGroupsContainOf(userId)
// 得到有分享的分组
shares := []info.ShareNotebook{}
@@ -760,9 +772,7 @@ func (this *ShareService) GetNotebookShareGroups(notebookId, userId string) []in
}
// 共享笔记给分组
func (this *ShareService) AddShareNotebookGroup(userId, notebookId, groupId string, perm int) (bool) {
// 得到group, 是否是我的group
group := groupService.GetGroup(userId, groupId)
if group.GroupId == "" {
if !groupService.IsExistsGroupUser(userId, groupId) {
return false
}
@@ -785,3 +795,36 @@ func (this *ShareService) DeleteShareNotebookGroup(userId, notebookId, groupId s
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
//--------------------
// 删除组时, 删除所有的
//--------------------
func (this *ShareService) DeleteAllShareNotebookGroup(groupId string) bool {
return db.Delete(db.ShareNotebooks, bson.M{
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
func (this *ShareService) DeleteAllShareNoteGroup(groupId string) bool {
return db.Delete(db.ShareNotes, bson.M{
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
//--------------------
// 删除组内用户时, 删除其分享的
//--------------------
func (this *ShareService) DeleteShareNotebookGroupWhenDeleteGroupUser(userId, groupId string) bool {
return db.Delete(db.ShareNotebooks, bson.M{
"UserId": bson.ObjectIdHex(userId),
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
func (this *ShareService) DeleteShareNoteGroupWhenDeleteGroupUser(userId, groupId string) bool {
return db.Delete(db.ShareNotes, bson.M{
"UserId": bson.ObjectIdHex(userId),
"ToGroupId": bson.ObjectIdHex(groupId),
});
}

View File

@@ -85,13 +85,13 @@ func (this *ThemeService) getDefaultTheme(style string) info.Theme {
// 用户的主题路径设置
func (this *ThemeService) getUserThemeBasePath(userId string) string {
return revel.BasePath + "/public/upload/" + userId + "/themes"
return revel.BasePath + "/public/upload/" + Digest3(userId) + "/" + userId + "/themes"
}
func (this *ThemeService) getUserThemePath(userId, themeId string) string {
return this.getUserThemeBasePath(userId) + "/" + themeId
}
func (this *ThemeService) getUserThemePath2(userId, themeId string) string {
return "public/upload/" + userId + "/themes/" + themeId
return "public/upload/" + Digest3(userId) + "/" + userId + "/themes/" + themeId
}
// 新建主题
@@ -412,10 +412,18 @@ func (this *ThemeService) ImportTheme(userId, path string) (ok bool, msg string)
themeIdO := bson.NewObjectId()
themeId := themeIdO.Hex()
targetPath := this.getUserThemePath(userId, themeId) // revel.BasePath + "/public/upload/" + userId + "/themes/" + themeId
err := os.MkdirAll(targetPath, 0755)
if err != nil {
msg = "error"
return
}
if ok, msg = archive.Unzip(path, targetPath); !ok {
DeleteFile(targetPath)
Log("oh no")
return
}
// 主题验证
if ok, msg = this.ValidateTheme(targetPath, "", ""); !ok {
DeleteFile(targetPath)

View File

@@ -25,6 +25,12 @@ type TrashService struct {
// 应该放在回收站里
// 有trashService
func (this *TrashService) DeleteNote(noteId, userId string) bool {
note := noteService.GetNote(noteId, userId);
// 如果是垃圾, 则彻底删除
if (note.IsTrash) {
return this.DeleteTrash(noteId, userId)
}
// 首先删除其共享
if shareService.DeleteShareNoteAll(noteId, userId) {
// 更新note isTrash = true
@@ -36,13 +42,15 @@ func (this *TrashService) DeleteNote(noteId, userId string) bool {
return true
}
}
return false
}
// 删除别人共享给我的笔记
// 先判断我是否有权限, 笔记是否是我创建的
func (this *TrashService) DeleteSharedNote(noteId, userId, myUserId string) bool {
note := noteService.GetNote(noteId, userId)
func (this *TrashService) DeleteSharedNote(noteId, myUserId string) bool {
note := noteService.GetNoteById(noteId)
userId := note.UserId.Hex()
if shareService.HasUpdatePerm(userId, myUserId, noteId) && note.CreatedUserId.Hex() == myUserId {
return db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true, "Usn": userService.IncrUsn(userId)}})
}
@@ -116,4 +124,4 @@ func (this *TrashService) ListNotes(userId string,
pageNumber, pageSize int, sortField string, isAsc bool) (notes []info.Note) {
_, notes = noteService.ListNotes(userId, "", true, pageNumber, pageSize, sortField, isAsc, false)
return
}
}

View File

@@ -69,6 +69,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) == "" {
@@ -107,6 +114,13 @@ func (this *UserService) setUserLogo(user *info.User) {
}
}
// 仅得到用户
func (this *UserService) GetUser(userId string) info.User {
user := info.User{}
db.Get(db.Users, userId, &user)
return user
}
// 得到用户信息 userId
func (this *UserService) GetUserInfo(userId string) info.User {
user := info.User{}
@@ -202,19 +216,33 @@ func (this *UserService) MapUserAndBlogByUserIds(userIds []bson.ObjectId) map[st
return userAndBlogMap
}
// 得到用户信息+博客主页
func (this *UserService) GetUserAndBlogUrl(userId string) info.UserAndBlogUrl {
user := this.GetUserInfo(userId)
userBlog := blogService.GetUserBlog(userId)
blogUrls := blogService.GetBlogUrls(&userBlog, &user)
return info.UserAndBlogUrl{
User: user,
BlogUrl: blogUrls.IndexUrl,
PostUrl: blogUrls.PostUrl,
}
}
// 得到userAndBlog公开信息
func (this *UserService) GetUserAndBlog(userId string) info.UserAndBlog {
user := this.GetUserInfo(userId)
userBlog := blogService.GetUserBlog(userId)
return info.UserAndBlog{
UserId: user.UserId,
Username: user.Username,
Email: user.Email,
Logo: user.Logo,
UserId: user.UserId,
Username: user.Username,
Email: user.Email,
Logo: user.Logo,
BlogTitle: userBlog.Title,
BlogLogo: userBlog.Logo,
BlogUrl: blogService.GetUserBlogUrl(&userBlog, user.Username),
BlogUrls: blogService.GetBlogUrls(&userBlog, &user),
BlogLogo: userBlog.Logo,
BlogUrl: blogService.GetUserBlogUrl(&userBlog, user.Username),
BlogUrls: blogService.GetBlogUrls(&userBlog, &user),
}
}
@@ -239,15 +267,15 @@ func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.
return users2
}
// 使用email(username), pwd得到用户信息
func (this *UserService) LoginGetUserInfo(emailOrUsername, md5Pwd string) info.User {
// 使用email(username), 得到用户信息
func (this *UserService) GetUserInfoByName(emailOrUsername string) info.User {
emailOrUsername = strings.ToLower(emailOrUsername)
user := info.User{}
if strings.Contains(emailOrUsername, "@") {
db.GetByQ(db.Users, bson.M{"Email": emailOrUsername, "Pwd": md5Pwd}, &user)
db.GetByQ(db.Users, bson.M{"Email": emailOrUsername}, &user)
} else {
db.GetByQ(db.Users, bson.M{"Username": emailOrUsername, "Pwd": md5Pwd}, &user)
db.GetByQ(db.Users, bson.M{"Username": emailOrUsername}, &user)
}
this.setUserLogo(&user)
return user
@@ -281,10 +309,16 @@ func (this *UserService) UpdateAvatar(userId, avatarPath string) (bool) {
// 已经登录了的用户修改密码
func (this *UserService) UpdatePwd(userId, oldPwd, pwd string) (bool, string) {
userInfo := this.GetUserInfo(userId)
if userInfo.Pwd != Md5(oldPwd) {
if !ComparePwd(oldPwd, userInfo.Pwd) {
return false, "oldPasswordError"
}
ok := db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", Md5(pwd))
passwd := GenPwd(pwd)
if passwd == "" {
return false, "GenerateHash error"
}
ok := db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", passwd)
return ok, ""
}
@@ -293,7 +327,12 @@ func (this *UserService) ResetPwd(adminUserId, userId, pwd string) (ok bool, msg
if configService.GetAdminUserId() != adminUserId {
return
}
ok = db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", Md5(pwd))
passwd := GenPwd(pwd)
if passwd == "" {
return false, "GenerateHash error"
}
ok = db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", passwd)
return
}
@@ -351,7 +390,7 @@ func (this *UserService) UpdateEmail(token string) (ok bool, msg, email string)
tokenInfo := info.Token{}
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenUpdateEmail); ok {
// 修改之后的邮箱
email = tokenInfo.Email
email = strings.ToLower(tokenInfo.Email)
// 先验证该email是否被注册了
if userService.IsExistsUser(email) {
ok = false

View File

@@ -90,7 +90,7 @@ func decodeValue(val string) string {
v, _ := url.ParseQuery("a=" + val)
return v.Get("a")
}
func encodeValue(val string) string {
if val == "" {
return val
@@ -99,16 +99,17 @@ func encodeValue(val string) string {
v.Set("", val)
return v.Encode()[1:]
}
// 添加笔记时通过title得到urlTitle
func fixUrlTitle(urlTitle string) string {
if urlTitle != "" {
// 把特殊字段给替换掉
// str := `life "%&()+,/:;<>=?@\|`
// str := `life "%&()+,/:;<>=?@\|`
reg, _ := regexp.Compile("/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\")
urlTitle = reg.ReplaceAllString(urlTitle, "-")
urlTitle = strings.Trim(urlTitle, "-") // 左右单独的-去掉
// 把空格替换成-
// urlTitle = strings.Replace(urlTitle, " ", "-", -1)
// urlTitle = strings.Replace(urlTitle, " ", "-", -1)
for strings.Index(urlTitle, "--") >= 0 { // 防止出现连续的--
urlTitle = strings.Replace(urlTitle, "--", "-", -1)
}
@@ -119,11 +120,20 @@ func fixUrlTitle(urlTitle string) string {
func getUniqueUrlTitle(userId string, urlTitle string, types string, padding int) string {
urlTitle2 := urlTitle
// 判断urlTitle是不是过长, 过长则截断, 300
// 不然生成index有问题
// it will not index a single field with more than 1024 bytes.
// If you're indexing a field that is 2.5MB, it's not really indexing it, it's being skipped.
if len(urlTitle2) > 320 {
urlTitle2 = urlTitle2[:300] // 为什么要少些, 因为怕无限循环, 因为把padding截了
}
if padding > 1 {
urlTitle2 = urlTitle + "-" + strconv.Itoa(padding)
}
userIdO := bson.ObjectIdHex(userId)
var collection *mgo.Collection
if types == "note" {
collection = db.Notes
@@ -136,9 +146,10 @@ func getUniqueUrlTitle(userId string, urlTitle string, types string, padding int
padding++
urlTitle2 = urlTitle + "-" + strconv.Itoa(padding)
}
return urlTitle2
}
// types == note,notebook,single
func GetUrTitle(userId string, title string, types string) string {
urlTitle := strings.Trim(title, " ")

View File

@@ -1,237 +0,0 @@
package main
import (
"github.com/robfig/revel"
"github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
// "github.com/leanote/leanote/app/lea/memcache"
// "github.com/leanote/leanote/app/lea/netutil"
"github.com/leanote/leanote/app/lea/html2image"
"time"
"fmt"
"gopkg.in/mgo.v2/bson"
// "gopkg.in/mgo.v2"
// "encoding/json"
// "strings"
)
var userId = "5295d4c95b1dd58edb4a7f4f"
var userIdO = bson.ObjectIdHex(userId)
var userId3 = "52b43ae4cfeeae33ef073b2b"
var userId3O = bson.ObjectIdHex(userId3)
var toUserId = "52b4376bcfeeae33ef073b21"
var toUserIdO = bson.ObjectIdHex(toUserId)
var notebookId = "52b2d051ea3ba3d3fb35910c"
var notebookIdO = bson.ObjectIdHex(notebookId)
var notebookId2O = bson.ObjectIdHex("52b2d051ea3ba3d3fb35910b")
var noteId = "52b2dd34ea3ba3d3fb35910d"
var noteIdO = bson.ObjectIdHex(noteId)
func testNoteService() {
println("testNoteService")
noteService := &service.NoteService{}
note := noteService.GetBlogNote("535f9e6b19807a4c8d000000")
LogJ(note)
/*
_, notes := noteService.SearchNote("go", "52d3e8ac99c37b7f0d000001", 1, 30, "", false, true);
LogJ(notes)
*/
return;
/*
noteService.AddNote(info.Note{UserId: userIdO,
NotebookId: notebookIdO,
Title: "life you", Tags: []string{"red", "yellow"}})
*/
noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
NoteId: bson.ObjectIdHex("52b4531dcfeeae33ef073b33"),
Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.UpdateNoteContent(userId, userId, "52b2dd34ea3ba3d3fb35910d", "life2---------")
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testNotebookService() {
service := &service.NotebookService{}
Log(service.IsBlog("52ccb959bcbf21610d000001"))
return;
notebooks := service.GetNotebooks(userId3)
LogJ(notebooks)
// b, _ := json.MarshalIndent(notebooks, "", " ")
// fmt.Println(string(b))
// service.UpdateNotebookTitle("52b2cf9eea3ba3d3fb359108", userId, "JS")
// service.SortNotebooks(userId, map[string]int{"52b2d051ea3ba3d3fb35910c": 4, "52b2d051ea3ba3d3fb35910b": 3})
/*
service.AddNotebook(info.Notebook{UserId: userId3O,
ParentNotebookId: bson.ObjectIdHex("52b43c1fcfeeae33ef073b2e"),
Title: "Mac-life",
Seq: 0})
*/
println("xxx")
}
func testShareService() {
service := &service.ShareService{}
// service.AddShareNote("52bd127dbcbf216d0b000000", 1, "5295d4c95b1dd58edb4a7f4f", "c@a.com")
LogJ(service.ListNoteShareUserInfo("52cd11a1bcbf215680000000", "5295d4c95b1dd58edb4a7f4f"))
return;
/*
service.AddShareNotebook(info.ShareNotebook{UserId: userId3O, ToUserId: userIdO,
NotebookId: bson.ObjectIdHex("52b43c38cfeeae33ef073b30")})
*/
notebooks,_ := service.GetShareNotebooks(userId)
LogJ(notebooks)
// noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
// NoteId: bson.ObjectIdHex("52b2dd34ea3ba3d3fb35910d"), Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testAuthService() {
userService := &service.UserService{}
LogJ(userService.GetUserInfo("52d26b4e99c37b609a000001"))
// userService.AddUser(info.User{UserId: bson.ObjectIdHex("52d26b4e99c37b609a000001"), Email: "leanote@leanote.com", Pwd:"abc"})
return;
authService := &service.AuthService{}
authService.Register("f@a.com", "abc")
// fmt.Println(authService.LogonGetUserInfo("a@a.com", "abc"))
// noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
// NoteId: bson.ObjectIdHex("52b2dd34ea3ba3d3fb35910d"), Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testTagService() {
service := &service.TagService{}
// service.AddTags("5295d4c95b1dd58edb4a7f4f", []string{"life", "blue", "yellow"})
// service.AddTags("5295d4c95b1dd58edb4a7f4f", []string{"what", "can", "make"})
LogJ(service.GetTags("5295d4c95b1dd58edb4a7f4f"))
}
func testHtml2Image() {
start := time.Now()
Log("start...")
// TestFillString()
html2image.ToImage("uid", "username", "noteId", "开发一款属于自己的编程语言,开发一款属于自己的编程语言听起来是不是很酷?", `
<div class="each-post">
<p>
一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace很喜欢那里的氛围希望国内其他地方也能有更多这类空间供创客发挥。
假如你有一个比较成型的想法,想在硬件领域做点事情,核心团队也基本组好,硬件软件交互基本都有专人了。</p>
<p><a>这时候你的首要目标</a>就是找个地方按照你的计划尽早做出一个可用的原型。Techspace可能是一个合适的地方。
一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace很喜欢那里的氛围希望国内其他地方也能有更多这类空间供创客发挥。
深圳 Techspace 位于工业区园区内有奥迪、BMW 等企业的厂房在大门口我停下来问保安M10 栋在哪里?保安答,去 Techspace最里面靠右手那栋。惊叹于保安的机智我也在想莫非有许多朋友都慕名来到这巨大园区寻访 Techspace
穿过一片工业区里的高楼和各种建筑材料
</p>
<!--
<pre class="">cd jpeg-9a/<br>./configure --enable-shared --enable-static
make<br>make --- install</pre>
<p>
life you can, !!@kk
</p>
-->
</div>
`, "/Users/life/Desktop/a.png")
fmt.Printf("time cost %v\n", time.Now().Sub(start))
}
func testLea() {
names := ListDir("/Users/life/Documents/Go/package/src/leanote")
fmt.Println(names);
}
func main() {
revel.BasePath = "/Users/life/Documents/Go/package/src/leanote"
// testLea();
// a, b := SplitFilename("http://ab/c/a.gif#??")
// println(a)
// println(b)
// path, ok := netutil.WriteUrl("http://a.36krcnd.com/photo/2014/9bd1a07c0973d79ca05ad13c3c2e16b8.png!slider", "/tmp")
// println(path)
// testHtml2Image();
// println(IsObjectId("52d26b4e99c37b609a000001"))
// b := `请点击链接验证邮箱: <a href="">http://leanote.com/user/activeEmail?token=d8ca086cce5550a6227f9dc84dbac09d</a>. 48小时后过期.`
// SendEmail("lifephp@gmail.com", "leanote-验证邮箱", "验证邮", b)
//_, err := mgo.Dial("mongodb://leanote:nKFAkxKnWkEQy8Vv2LlM@115.28.133.226:27017/leanote")
// testNotebookService();
// testNoteService();
// testShareService()
// testAuthService()
// testTagService();
/*
filename := "你好59.26.png"
ext := SubstringByte(filename, strings.LastIndex(filename, "."))
ext = strings.ToLower(ext)
print(ext)
52d26ab199c37b5f80000001
Log(bson.NewObjectId())
Log(bson.NewObjectId())
Log(bson.NewObjectId())
*/
// Log(TransferExt("/你好a/b/a.gif", ".jpg"))
// TransToGif("/Users/life/Desktop/a2.png", 0, false)
// Log(IsUsername("xx**x"))
/*
memcache.Set("xx", map[string]string{"A":"you"}, 0)
a := memcache.Get("xx")
Log(a)
*/
}

23
app/tests/auth_test.go Normal file
View File

@@ -0,0 +1,23 @@
package tests
import (
"testing"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/service"
// "gopkg.in/mgo.v2"
// "fmt"
)
func init() {
db.Init("mongodb://localhost:27017/leanote", "leanote")
service.InitService()
}
// 测试登录
func TestAuth(t *testing.T) {
_, err := service.AuthS.Login("admin", "abc123")
if err != nil {
t.Error("Admin User Auth Error")
}
}

14
app/tests/db_test.go Normal file
View File

@@ -0,0 +1,14 @@
package tests
import (
"testing"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
// "github.com/leanote/leanote/app/service"
// "gopkg.in/mgo.v2"
// "fmt"
)
func TestDBConnect(t *testing.T) {
db.Init("mongodb://localhost:27017/leanote", "leanote")
}

3
app/tests/tmp.go Normal file
View File

@@ -0,0 +1,3 @@
package tests
func a() {
}

View File

@@ -11,20 +11,3 @@
<script src="/public/admin/js/artDialog/jquery.artDialog.js?skin=default"></script>
<script src="/public/js/common.js"></script>
<script src="/public/admin/js/admin.js"></script>
<script>
$(function() {
var pathname = location.pathname; // admin/t
var search = location.search; // ?t=xxx, 如果有?page呢
var fullPath = pathname;
if(search.indexOf("?t=") >= 0) {
var fullPath = pathname + search; // /admin/t?t=xxx
}
$("#nav > li").removeClass("active");
// 自己
var $thisLi = $('#nav a[href^="' + fullPath + '"]').parent();
$thisLi.addClass("active");
// 父也active
$thisLi.parent().parent().addClass('active');
});
</script>

View File

@@ -13,7 +13,6 @@
</a>
</li>
<li id="adminUserNav">
<a href="#">
<i class="fa fa-users icon">
@@ -157,7 +156,7 @@
</li>
<li>
<a href="/admin/t?t=setting/shareNote">
<a href="/admin/t?t=setting/share_note">
<span>
Register Share Note
</span>
@@ -171,6 +170,15 @@
</span>
</a>
</li>
<li>
<a href="/admin/t?t=setting/export_pdf">
<span>
Export PDF
</span>
</a>
</li>
</ul>
</li>
@@ -232,6 +240,8 @@
v1.0-beta to v1.0-beta.2
</span>
</a>
</li>
<li>
<a href="/admin/t?t=upgrade/beta3">
<span>
v1.0-beta.2/3 to v1.0-beta.4
@@ -241,4 +251,4 @@
</ul>
</li>
</ul>
</nav>
</nav>

View File

@@ -0,0 +1,52 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Export PDF</h3></div>
<div class="row">
<div class="col-sm-6">
<form id="add_user_form">
<section class="panel panel-default">
<div class="panel-body">
<div class="form-group">
<label>Wkhtmltopdf Binary Path</label>
<input type="text" class="form-control" placeholder="/usr/local/bin/wkhtmltopdf" name="path" value="{{.str.exportPdfBinPath}}">
Leanote use <a target="_blank" href="http://wkhtmltopdf.org">wkhtmltopdf</a> to export pdf. You should install it on your server.
<br />
Please input the path that wkhtmltopdf installed, e.g. <code>/usr/local/bin/wkhtmltopdf</code>
</div>
</div>
<footer class="panel-footer text-right bg-light lter">
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
</footer>
</section>
</form>
</div>
</div>
{{template "admin/footer.html" .}}
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
<script>
$(function() {
init_validator("#add_user_form");
$("#submit").click(function(e){
e.preventDefault();
var t = this;
if($("#add_user_form").valid()) {
$(t).button('loading');
ajaxPost("/adminSetting/exportPdf", getFormJsonData("add_user_form"), function(ret){
$(t).button('reset')
if(!ret.Ok) {
art.alert(ret.Msg)
} else {
art.tips("Success");
}
});
}
});
});
</script>
{{template "admin/end.html" .}}

View File

@@ -46,7 +46,7 @@
</a>
</li>
<li class="hidden-xs">
<a href="/blog/admin" class="dk" target="_blank">
<a href="/blog" class="dk" target="_blank">
My Blog
</a>
</li>
@@ -120,4 +120,4 @@ padding: 10px 15px;text-align:center;">
</ul>
-->
<!-- 主要内容区 -->

View File

@@ -1,20 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>
TinyMCE 4 Leaui Image Manager
Leanote Album Image Manager
</title>
<link href="public/bootstrap3/css/bootstrap.css" rel="stylesheet" media="screen">
<link href="public/css/style.css" rel="stylesheet" />
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="public/js/html5shiv.js">
</script>
<![endif]-->
<link href="/css/bootstrap-min.css" rel="stylesheet" />
<link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
<link href="/public/album/css/style-min.css" rel="stylesheet" />
</head>
<body class="md" id="body">
@@ -23,9 +17,9 @@
<div class="tabs">
<ul id="myTab" class="nav nav-tabs">
<li class="active"><a href="#images" data-toggle="tab">Images</a></li>
<li class=""><a href="#uploadTab" data-toggle="tab">Upload</a></li>
<li class=""><a href="#url" data-toggle="tab">URL</a></li>
<li class="active"><a href="#images" data-toggle="tab">{{leaMsg . "Images"}}</a></li>
<li class=""><a href="#uploadTab" data-toggle="tab">{{leaMsg . "Upload"}}</a></li>
<li class=""><a href="#url" data-toggle="tab">{{leaMsg . "Image URL"}}</a></li>
</ul>
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="images">
@@ -33,18 +27,18 @@
<div>
<form class="form-inline" role="form">
<div class="form-group">
<label class="control-label" for="albums">Albums:</label>
<label class="control-label" for="albums">{{leaMsg . "Albums"}}:</label>
</div>
<div class="form-group">
<select class="form-control" id="albumsForList">
<option value="">Default</option>
<option value="">{{leaMsg . "Default"}}</option>
</select>
</div>
<div class="form-group">
<input class="form-control" type="text" id="key" placeholder="file title search"/>
<input class="form-control" type="text" id="key" placeholder="{{leaMsg . "File title search"}}"/>
</div>
<div class="form-group">
&nbsp;&nbsp;<a href="javascript:;" title="refresh" id="refresh"><span class="glyphicon glyphicon-refresh"></span></a>
&nbsp;&nbsp;<a href="javascript:;" title="refresh" id="refresh"><span class="fa fa-refresh"></span></a>
</div>
</form>
</div>
@@ -61,8 +55,8 @@
<div id="imageMask">
<div id="noImages">
<p>No Images!</p>
<btn class="btn btn-default" id="goAddImageBtn">Go to upload images</btn>
<p>{{msg . "No Images"}}!</p>
<btn class="btn btn-default" id="goAddImageBtn">{{leaMsg . "Go to upload images"}}</btn>
</div>
<div id="loading">
loading....
@@ -74,22 +68,22 @@
<div>
<form class="form-inline" role="form">
<div class="form-group" id="albumSelect">
<label class="control-label" for="albums">Albums:</label>
<label class="control-label" for="albums">{{leaMsg . "Albums"}}:</label>
<select class="form-control" id="albumsForUpload">
<option value="">Default</option>
<option value="">{{leaMsg . "Default"}}</option>
</select>
<a href="javascript:;" id="deleteAlbumBtn">Delete</a> |
<a href="javascript:;" id="renameAlbumBtn">Rename</a> |
<a href="javascript:;" id="addAlbumBtn">Add</a>
<a href="javascript:;" id="deleteAlbumBtn">{{leaMsg . "Delete"}}</a> |
<a href="javascript:;" id="renameAlbumBtn">{{leaMsg . "Rename"}}</a> |
<a href="javascript:;" id="addAlbumBtn">{{leaMsg . "Add"}}</a>
</div>
<!-- rename or add album -->
<div class="form-group" style="display: none" id="addOrUpdateAlbumForm">
<input type="text" class="form-control" style="width: 150px" id="albumName">
<button class="btn btn-success" type="button" id="addOrUpdateAlbumBtn">Add Album</button>
<button class="btn btn-default" type="button" id="cancelAlbumBtn">Cancel</button>
<button class="btn btn-success" type="button" id="addOrUpdateAlbumBtn">{{leaMsg . "Add Album"}}</button>
<button class="btn btn-default" type="button" id="cancelAlbumBtn">{{leaMsg . "Cancel"}}</button>
</div>
<div class="form-group">
@@ -101,7 +95,7 @@
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
<div id="drop">
<a class="btn btn-default">
Click to upload images Or Drop images to here
{{leaMsg . "Click to upload images Or Drop images to here"}}
</a>
<input type="file" name="file" multiple />
</div>
@@ -113,18 +107,18 @@
<div class="tab-pane fade" id="url">
<form class="form-inline" id="imageUrlForm" style="">
<div class="form-group">
<label class="control-label" for="imageUrl">URL:</label>
<label class="control-label" for="imageUrl">{{leaMsg . "Image URL"}}:</label>
</div>
<div class="form-group">
<input type="text" class="form-control" id="imageUrl" size="51"/>
</div>
<div class="form-group">
<button class="btn btn-success" id="addImageUrlBtn">Add Image</button>
<button class="btn btn-success" id="addImageUrlBtn">{{leaMsg . "Add Image"}}</button>
</div>
<div class="form-group">
<div class="alert alert-danger" id="msgForUrl"
style="display: none; padding: 5px; width: 150px;">
Can't load this url
{{leaMsg . "Can't load this url"}}
</div>
</div>
</form>
@@ -135,6 +129,7 @@
<ul id="preview" class="clearfix">
</ul>
<!--
<div id="previewAttrs" style="margin-left: 10px">
<form class="form-inline" role="form">
<div class="form-group">
@@ -145,6 +140,7 @@
</div>
</form>
</div>
-->
</div>
</body>
@@ -154,24 +150,17 @@ var G = {};
G.imageSrcPrefix = 'upload';
G.perPageItems = 12;
G.maxSelected = 1;
/*
var url = location.href;
var classes = "";
if(url.indexOf("md=1") >= 0) {
classes = "md";
document.getElementById("body").className = classes;
}
*/
var UrlPrefix = '{{.siteUrl}}';
</script>
<script src="public/js/jquery.js"></script>
<script src="public/bootstrap3/js/bootstrap.min.js"></script>
<script src="public/js/jquery.ui.widget.js"></script>
<script src="public/js/jquery.fileupload.js"></script>
<script src="public/js/jquery.iframe-transport.js"></script>
<script src="public/js/jquery.pagination.js"></script>
<!--
<script src="public/js/jquery.lazyload.min.js"></script>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap-min.js"></script>
<script src="/js/plugins/libs-min/fileupload.js"></script>
<script src="/js/jquery.pagination.js"></script>
<script src="/public/album/js/main.js"></script>
-->
<script src="public/js/main.js"></script>
<script src="/public/album/js/main.all.js"></script>
</html>

View File

@@ -26,10 +26,10 @@
</section>
<div id="boxFooter">
<p>
<a href="/index">leanote</a> © 2014
<a href="/index">leanote</a> © 2015
</p>
</div>
</body>
</html>
{{end}}
{{end}}

View File

@@ -26,10 +26,10 @@
</section>
<div id="boxFooter">
<p>
<a href="/index">leanote</a> © 2014
<a href="/index">leanote</a> © 2015
</p>
</div>
</body>
</html>
{{end}}
{{end}}

124
app/views/file/pdf.html Normal file
View File

@@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.title}}</title>
<link href="/css/bootstrap.css" rel="stylesheet">
<link id="styleLink" href="/css/pdf.css" rel="stylesheet">
<style>
body {
margin-top: 30px;
}
table {
margin-bottom: 16px;
}
table th, table td {
padding: 6px 13px;
border: 1px solid #ddd;
}
table th {
font-weight: bold;
}
table tr {
background-color: #fff;
border-top: 1px solid #ccc;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
.mce-item-table, .mce-item-table td, .mce-item-table th, .mce-item-table caption {
border: 1px solid #ddd;
border-collapse: collapse;
padding: 6px 13px;
}
</style>
</head>
<body>
<div id="content">
<h1 class="title tex2jax_ignore">
{{if .blog.Title}}
{{.blog.Title}}
{{else}}
Untitled
{{end}}
</h1>
<div class="created-time">
<!--
{{ if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" id="logo">
{{else}}
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
{{end}}
{{.userInfo.Username}}
-->
{{if .blog.Tags}}
<img src="{{$.siteUrl}}/images/blog/tag.png" id="tag">
{{blogTagsForExport $ .blog.Tags}}
{{end}}
</div>
<div class="desc">
{{if .blog.IsMarkdown }}
<div id="markdownContent" style="display: none">
<!-- 用textarea装html, 防止得到的值失真 -->
<textarea>
{{.content | raw}}
</textarea>
</div>
<div id="parsedContent">
</div>
{{else}}
{{.content | raw}}
{{end}}
</div>
</div>
<!--
<div id="footer">
<p class="split"></p>
<a href="http://leanote.com"><img src="{{.siteUrl}}/images/logo/leanote_icon_blue.png" id="leanote_logo"/></a>
<p>
<a href="http://leanote.com">Leanote</a>
<br />
<a href="http://leanote.com/service">Upgrade Account</a>
</p>
</div>
-->
<script src="{{.siteUrl}}/js/jquery-1.9.0.min.js"></script>
<link href="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script>
function ok() {
setTimeout(function() {
window.status = 'done';
}, 0);
}
</script>
{{if not .blog.IsMarkdown }}
<script>
$("pre").addClass("prettyprint");
prettyPrint();
ok();
</script>
{{end}}
{{if .blog.IsMarkdown }}
<script src="/public/libs/md2html/md2html_for_export.js"></script>
<script>
var content = $.trim($("#markdownContent textarea").val());
md2Html(content, $("#parsedContent"), function(html) {
$("pre").addClass("prettyprint");
prettyPrint();
ok();
});
</script>
{{end}}
</body>
</html>

View File

@@ -27,7 +27,7 @@
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
<a href="/index">leanote</a> © 2014-2015
</p>
</div>

View File

@@ -36,7 +36,7 @@
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
<a href="/index">leanote</a> © 2014-2015
</p>
</div>

View File

@@ -21,7 +21,7 @@
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
<a href="/index">leanote</a> © 2014-2015
</p>
</div>

View File

@@ -7,18 +7,19 @@
<a href="http://weibo.com/leanotecom" target="_blank"><i class="fa fa-weibo"></i></a>
<br />
leanote@leanote.com
<div id="beian">
<a href="http://www.miibeian.gov.cn" target="_blank">沪ICP备14006011号</a>
</div>
<br />
Copyright © 2014-2015 <a href="https://leanote.com">Leanote</a>
</div>
<div class="col-md-6">
<i class="fa fa-globe fa-3x icon-muted"></i>
<h2>Join Us</h2>
<a href="https://github.com/leanote/leanote">github leanote</a>
<a href="https://github.com/leanote/leanote">Github Leanote</a>
<br />
QQ Group: 158716820
QQ Group: 158716820, 158716820
</div>
</div>
</div>
<div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div>
</div>

View File

@@ -5,8 +5,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote">
<meta name="description" content="Leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote">
<title>{{.title}}</title>
<link href="/css/bootstrap.css" rel="stylesheet">
@@ -37,31 +37,33 @@ function log(o) {
</div>
<div id="navbar" class="navbar-collapse collapse">
<div id="lang">
<a data-lang="en-us">English</a>
<a data-lang="fr-fr">Français</a>
<a data-lang="zh-cn">简体中文</a>
</div>
<ul class="nav navbar-nav navbar-left">
<li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<!--
<li><a href="/index#aboutLeanote" target="#aboutLeanote" class="smooth-scroll">{{msg . "aboutLeanote"}}</a> </li>
-->
<li><a href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
<li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
<li><a id="leanoteBlog" href="{{.leaUrl}}/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li>
<li><a href="/index#" data-target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<li style="position: relative; margin-right: 3px;">
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
<div class="red-circle" style=""></div>
</li>
<li id="loginBtns">
{{if .userInfo.Email}}
{{msg . "hi"}}, {{.userInfo.Username}}
<a href="{{$.noteUrl}}">{{msg . "myNote"}}</a>
<a href="/logout">{{msg . "logout"}}</a>
{{else}}
<a href="/login">{{msg . "login"}}</a>
{{if .openRegister}}
<a href="/register" class="btn-register">{{msg . "register"}}</a>
{{end}}
{{end}}
<li><a id="leanoteBlog" href="http://lea.leanote.com" target="_blank" title="Lea++, leanote blog platform" class="">Lea++</a></li>
<li><a id="leanoteApp" href="http://app.leanote.com" target="_blank" title="Leanote App" class="">{{msg . "desktopApp"}}</a></li>
<li>
<a href="http://leanote.org#donate" target="_blank" class="">{{msg . "donate"}}</a>
</li>
<li><a href="http://leanote.org" target="_blank" title="leanote.org" class="">leanote.org</a></li>
{{if .userInfo.Email}}
<li id="loginBtns">
{{msg . "hi"}}, {{.userInfo.Username}}
<a href="{{$.noteUrl}}">{{msg . "myNote"}}</a>
<a href="/logout">{{msg . "logout"}}</a>
</li>
{{end}}
</ul>
</div>
</div>
@@ -91,10 +93,11 @@ function log(o) {
</div>
<ul id="blogNav" class="pull-right">
<li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<li><a href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
<li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
<li><a href="#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<li><a href="#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
<li><a href="#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
<li><a id="leanoteBlog" href="{{.leaUrl}}/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li>
<li style="position: relative; margin-right: 3px;">
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
<div style="position: absolute;
@@ -108,4 +111,4 @@ function log(o) {
</ul>
</div>
</div>
-->
-->

View File

@@ -1,26 +1,22 @@
{{template "home/header.html" .}}
<style>
</style>
<section>
<div class="header">
<h2>leanote, {{msg . "moto"}}</h2>
<h2>Leanote, {{msg . "moto"}}</h2>
<p>{{msg . "moto3"}}</p>
<p>{{msg . "moto2"}}</p>
<div>
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
<a class="btn btn-default btn-primary" href="/register">{{msg . "register"}}</a>
&nbsp;
&nbsp;
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
{{if .openRegister}}
&nbsp;
&nbsp;
OR
&nbsp;
&nbsp;
<a class="btn btn-default" href="/register">{{msg . "register"}}</a>
{{if not .userInfo.UserId}}
<a class="btn btn-default" href="/login">{{msg . "login"}}</a>
&nbsp;
&nbsp;
{{end}}
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
</div>
</div>
@@ -29,13 +25,23 @@
<div class="img-header">
<img src="/images/home/mac-btns.png"/>
</div>
<img src="/images/home/preview2.png" style="width: 750px;" />
<div id="webSliderContainer">
<img class="web-slider" data-text="Default theme - markdown" src="/images/slider/v2/default_markdown.png"/>
<img class="web-slider hide-img" data-text="Default theme - rich editor" src="/images/slider/v2/simple_tinymce.png"/>
<img class="web-slider hide-img" data-text="Simple theme - markdown" src="/images/slider/v2/simple_markdown.png"/>
<img class="web-slider hide-img" data-text="Writting mode" src="/images/slider/v2/writting.png"/>
</div>
</div>
<div class="slider-desc"><span id="webText">Default theme - markdown</span></div>
<div class="mobile">
<div class="mobile-header">
<img src="/images/home/mac-dot.png" />
</div>
<img class="mobile-image" src="/images/home/mobile.png" />
<div id="mobileSliderContainer">
<img class="mobile-slider" data-text="Mobile" src="/images/slider/v2/mobile_simple_list.png" />
<img class="mobile-slider hide-img" data-text="Mobile markdown" src="/images/slider/v2/mobile_markdown.png" />
</div>
<div class="slider-desc mobile-text"><span id="mobileText">Default theme - markdown</span></div>
</div>
</div>
</section>
@@ -46,6 +52,10 @@
<div class="col-md-3">
<h3>{{msg . "knowledge"}}</h3>
<p>{{msg . "knowledgeInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "share"}}</h3>
@@ -55,81 +65,27 @@
<h3>{{msg . "cooperation"}}</h3>
<p>{{msg . "cooperationInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div>
</div>
</div>
<div class="container" id="download">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "download"}}</h2>
<div class="row">
<div style="width:300px; margin:auto; padding: 10px;">
Linux : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz">leanote-linux-v0.4.bin.tar.gz</a> <br />
MacOS X : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz">leanote-mac-v0.4.bin.tar.gz</a> <br />
<br />
<a href="https://github.com/leanote/leanote#3-how-to-install-leanote">{{msg . "howToInstallLeanote"}}</a>
</div>
</div>
</div>
<div class="container" id="donate">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "donate"}}</h2>
<div class="row">
<div style="width:500px; margin:auto; padding: 10px;">
<p>
您可以通过支付宝向leanote捐赠, 所捐赠的钱将用来leanote开发.
</p>
<p>
支付宝账号: <b>pay@leanote.com</b>
</p>
<p>
或使用支付宝扫以下二维码捐赠:
</p>
<p style="text-align: center">
<img src="/images/leanote/leanote_alipay.jpg" style="padding: 10px;
border: 2px solid #eee;
border-radius: 10px;"/>
</p>
<p>
我们会定期将捐赠名单发布在 <a href="http://leanote.com/blog/view/5417ecf81a910828fd000000">捐赠列表</a>
</p>
<p>
感谢您对leanote的支持!
</p>
<hr />
<p>
有任何疑问, 建议或需其它服务或支持, 欢迎联系 <code>leanote@leanote.com</code> 或加入官方QQ群: <code>158716820</code> 谢谢!
</p>
</div>
</div>
</div>
{{template "home/footer.html"}}
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
var lang = '{{.locale}}';
</script>
<script src="/js/home/index.js"></script>
<script>
$(function() {
/*
var u = navigator.userAgent;
var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1;
if(isMobile || $("body").width() < 600) {
location.href = "/mobile/index";
}
*/
// 平滑滚动
$(".smooth-scroll").click(function(e) {
e.preventDefault();
var t = $(this).attr("target");
var targetOffset = $(t).offset().top - 80;
$('html,body').animate({scrollTop: targetOffset}, 300);
});
});
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?d0c988e37b452b3bf1220d45b30f2de2";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</body>
</html>

View File

@@ -1,140 +0,0 @@
{{template "home/header.html" .}}
<style>
</style>
<section>
<div class="header">
<h2>leanote, {{msg . "moto"}}</h2>
<p>{{msg . "moto3"}}</p>
<p>
Knowledge, Blog, Sharing, Cooperation... all in leanote
</p>
<div>
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
&nbsp;
&nbsp;
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
{{if .openRegister}}
&nbsp;
&nbsp;
OR
&nbsp;
&nbsp;
<a class="btn btn-default" href="/register">{{msg . "register"}}</a>
{{end}}
</div>
</div>
<div class="preview" style="position: relative;">
<div>
<div class="img-header">
<img src="/images/home/mac-btns.png"/>
</div>
<img src="/images/home/preview2.png" style="width: 750px;" />
</div>
<div class="mobile">
<div class="mobile-header">
<img src="/images/home/mac-dot.png" />
</div>
<img class="mobile-image" src="/images/home/mobile.png" />
</div>
</div>
</section>
<div class="container" id="aboutLeanote">
<h2>{{msg . "aboutLeanote"}}</h2>
<div class="row">
<div class="col-md-3">
<h3>{{msg . "knowledge"}}</h3>
<p>{{msg . "knowledgeInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "share"}}</h3>
<p>{{msg . "shareInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "cooperation"}}</h3>
<p>{{msg . "cooperationInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div>
</div>
</div>
<hr />
<div class="container" id="download">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "download"}}</h2>
<div class="row">
<div style="width:300px; margin:auto; padding: 10px;">
Linux : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz">leanote-linux-v0.4.bin.tar.gz</a> <br />
MacOS X : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz">leanote-mac-v0.4.bin.tar.gz</a> <br />
<br />
<a href="https://github.com/leanote/leanote#3-how-to-install-leanote">{{msg . "howToInstallLeanote"}}</a>
</div>
</div>
</div>
<hr />
<div class="container" id="donate">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "donate"}}</h2>
<div class="row">
<div style="width:500px; margin:auto; padding: 10px;">
<p>
You can use <a href="http://alipay.com">alipay</a> to donate us, The donated money will be used to develop leanote.
</p>
<p>
Alipay Account: <b>pay@leanote.com</b>
</p>
<p>
Or you can use alipay app to scan the code to donate us:
</p>
<p style="text-align: center">
<img src="/images/leanote/leanote_alipay.jpg" style="padding: 10px;
border: 2px solid #eee;
border-radius: 10px; width: 200px;"/>
</p>
<p>
The donation list will be published at <a href="http://leanote.com/blog/view/5417ecf81a910828fd000000">Donation List</a>
</p>
<p>
Thanks for your support!
</p>
<hr />
<p>
Any questions, suggestions or need more supports, you are welcomed to contact us via <code>leanote@leanote.com</code> or the QQ Group <code>158716820</code> Thanks!
</p>
</div>
</div>
</div>
{{template "home/footer.html"}}
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
$(function() {
/*
var u = navigator.userAgent;
var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1;
if(isMobile || $("body").width() < 600) {
location.href = "/mobile/index";
}
*/
// 平滑滚动
$(".smooth-scroll").click(function(e) {
e.preventDefault();
var t = $(this).attr("target");
var targetOffset = $(t).offset().top - 80;
$('html,body').animate({scrollTop: targetOffset}, 300);
});
});
</script>
</body>
</html>

View File

@@ -1,139 +0,0 @@
{{template "home/header.html" .}}
<style>
</style>
<section>
<div class="header">
<h2>leanote, {{msg . "moto"}}</h2>
<p>{{msg . "moto3"}}</p>
<p>{{msg . "moto2"}}</p>
<div>
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
&nbsp;
&nbsp;
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
{{if .openRegister}}
&nbsp;
&nbsp;
OR
&nbsp;
&nbsp;
<a class="btn btn-default" href="/register">{{msg . "register"}}</a>
{{end}}
</div>
</div>
<div class="preview" style="position: relative;">
<div>
<div class="img-header">
<img src="/images/home/mac-btns.png"/>
</div>
<img src="/images/home/preview2.png" style="width: 750px;" />
</div>
<div class="mobile">
<div class="mobile-header">
<img src="/images/home/mac-dot.png" />
</div>
<img class="mobile-image" src="/images/home/mobile.png" />
</div>
</div>
</section>
<div class="container" id="aboutLeanote">
<h2>{{msg . "aboutLeanote"}}</h2>
<div class="row">
<div class="col-md-3">
<h3>{{msg . "knowledge"}}</h3>
<p>{{msg . "knowledgeInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "share"}}</h3>
<p>{{msg . "shareInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "cooperation"}}</h3>
<p>{{msg . "cooperationInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div>
</div>
</div>
<hr />
<div class="container" id="download">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "download"}}</h2>
<div class="row">
<div style="width:300px; margin:auto; padding: 10px;">
Linux : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz">leanote-linux-v0.4.bin.tar.gz</a> <br />
MacOS X : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz">leanote-mac-v0.4.bin.tar.gz</a> <br />
<br />
<a href="https://github.com/leanote/leanote#3-how-to-install-leanote">{{msg . "howToInstallLeanote"}}</a>
</div>
</div>
</div>
<hr />
<div class="container" id="donate">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "donate"}}</h2>
<div class="row">
<div style="width:500px; margin:auto; padding: 10px;">
<p>
您可以通过<a href="http://alipay.com">支付宝</a>向leanote捐赠, 所捐赠的款项将用于开发leanote.
</p>
<p>
支付宝账号: <b>pay@leanote.com</b>
</p>
<p>
或使用支付宝扫以下二维码捐赠:
</p>
<p style="text-align: center">
<img src="/images/leanote/leanote_alipay.jpg" style="padding: 10px;
border: 2px solid #eee;
border-radius: 10px; width: 200px;"/>
</p>
<p>
我们会定期将捐赠名单发布在 <a href="http://leanote.com/blog/view/5417ecf81a910828fd000000">捐赠列表</a>
</p>
<p>
感谢您对leanote的支持!
</p>
<hr />
<p>
有任何疑问, 建议或需其它服务或支持, 欢迎联系 <code>leanote@leanote.com</code> 或加入官方QQ群: <code>158716820</code> 谢谢!
</p>
</div>
</div>
</div>
{{template "home/footer.html"}}
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
$(function() {
/*
var u = navigator.userAgent;
var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1;
if(isMobile || $("body").width() < 600) {
location.href = "/mobile/index";
}
*/
// 平滑滚动
$(".smooth-scroll").click(function(e) {
e.preventDefault();
var t = $(this).attr("target");
var targetOffset = $(t).offset().top - 80;
$('html,body').animate({scrollTop: targetOffset}, 300);
});
});
</script>
</body>
</html>

View File

@@ -64,7 +64,7 @@
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
<a href="/index">leanote</a> © 2014-2015
</p>
</div>

View File

@@ -43,7 +43,7 @@
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
<a href="/index">leanote</a> © 2014-2015
</p>
</div>
@@ -72,7 +72,7 @@ $(function() {
showMsg("{{msg . "inputEmail"}}", "email");
return;
} else {
var myreg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[0-9a-zA-Z]{2,3}$/;
var myreg = /^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-_]+@([a-zA-Z0-9\-]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,6}$/;
if(!myreg.test(email)) {
showMsg("{{msg . "wrongEmail"}}", "email");
return;
@@ -117,4 +117,4 @@ $(function() {
});
</script>
</body>
</html>
</html>

View File

@@ -1,102 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote">
<title>{{.title}}</title>
<link href="{{.siteUrl}}/css/bootstrap.css" rel="stylesheet">
<link id="styleLink" href="{{.siteUrl}}/css/toImage.css" rel="stylesheet">
</head>
<body>
<div id="content">
<h1 class="title">
{{.blog.Title}}
</h1>
<div class="created-time">
{{ if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" id="logo">
{{else}}
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
{{end}}
{{.userInfo.Username}}
{{if .blog.Tags}}
<img src="{{$.siteUrl}}/images/blog/tag.png" id="tag">
{{blogTags .blog.Tags}}
{{end}}
</div>
<div class="desc">
{{if .blog.IsMarkdown }}
<div id="markdownContent" style="display: none">
<!-- 用textarea装html, 防止得到的值失真 -->
<textarea>
{{.content | raw}}
</textarea>
</div>
<div id="parsedContent">
</div>
{{else}}
{{.content | raw}}
{{end}}
</div>
</div>
<div id="footer">
<p>
{{ if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" id="logo">
{{else}}
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
{{end}}
(<a href="#">http://blog.leanote.com/{{.userInfo.Username}}</a>)
</p>
<img src="{{.siteUrl}}/images/logo/leanote_white.png" id="leanote_logo"/>
<p>
leanote, {{msg $ "moto"}}
</p>
</div>
<script src="{{.siteUrl}}/js/jquery-1.9.0.min.js"></script>
<script src="{{.siteUrl}}/js/bootstrap-min.js"></script>
<link href="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script>
$("pre").addClass("prettyprint linenums");
prettyPrint();
</script>
{{if .blog.IsMarkdown }}
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Converter.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Sanitizer.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Editor.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/local/Markdown.local.zh.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/Markdown.Extra.js"></script>
<!--mathjax-->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]], processEscapes: true }, messageStyle: "none"});
</script>
<script src="{{.siteUrl}}/public/mdeditor/editor/mathJax.js"></script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
var content = $.trim($("#markdownContent textarea").val());
var converter = Markdown.getSanitizingConverter();
Markdown.Extra.init(converter, {extensions: ["tables", "fenced_code_gfm", "def_list"]});
var html = converter.makeHtml(content);
$("#parsedContent").html(html);
$("pre").addClass("prettyprint linenums");
prettyPrint();
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"wmd-preview"]);
</script>
{{end}}
</body>
</html>

View File

@@ -1,4 +0,0 @@
lif------------
e
you can

View File

@@ -40,23 +40,32 @@
</div>
</div>
<style>
.mce-btn.mce-active, .mce-btn.mce-active:hover {
background-color: #FFFFFF;
}
.mce-btn {
background-color: #FFFFFF;
}
</style>
{{template "member/footer.html" .}}
<script type="text/javascript" src="/tinymce/tinymce.js"></script>
<script>
var urlPrefix = "{{.siteUrl}}";
var LeaAce = {nowIsInAce: function() {return false;}};
$(function() {
tinymce.init({
selector : "#content1",
content_css : [ "/css/bootstrap.css", "/css/editor/editor.css" ],
skin : "custom",
content_css : ["/css/editor/editor.css" ],
theme : "leanote",
language : "{{.locale}}",
height : 300,
width : "100%",
skin : "custom",
// skin : "custom",
plugins : [
"advlist autolink link leanote_image lists charmap hr ",
"advlist autolink link lists charmap hr ",
"searchreplace visualblocks visualchars tabfocus",
"table contextmenu directionality textcolor paste fullpage textcolor"],
"table contextmenu directionality textcolor fullpage textcolor"],
toolbar1 : "formatselect |fontselect fontsizeselect| forecolor backcolor | bold italic underline strikethrough | bullist numlist |",
menubar : false,
statusbar : false,

View File

@@ -55,7 +55,7 @@
{{range .myTpls}}
<a href="#" class="list-group-item " data-file="{{.}}">
<i class="fa fa-remove icon-muted remove-file" title="delete file" ></i>
{{.}}
<span class="filename" data-filename="{{.}}">{{.}}</span>
<span class="badge"></span>
</a>
{{end}}
@@ -123,7 +123,7 @@ var theme = {
saveBtnO: $("#saveBtn"),
newFileO: $("#newFile"),
curTpl: "",
tplInfos: {"header.html": "{{msg . "header"}}", "footer.html": "{{msg . "footer"}}", "index.html": "{{msg . "header"}}", "cate.html": "{{msg . "cate"}}", "search.html": "{{msg . "search"}}", "single.html": "{{msg . "single"}}", "archive.html": "{{msg . "archive"}}", "post.html": "{{msg . "post"}}", "tags.html": "{{msg . "tags"}}", "tag_posts.html": "{{msg . "tag_posts"}}", "share_comment.html": "{{msg . "share_comment"}}", "404.html":"404", "theme.json": "{{msg . "themeJson"}}", "paging.html": "{{msg . "paging"}}", "highlight.html": "{{msg . "highlight"}}"},
tplInfos: {"header.html": "{{msg . "header"}}", "footer.html": "{{msg . "footer"}}", "index.html": "{{msg . "index"}}", "cate.html": "{{msg . "cate"}}", "search.html": "{{msg . "search"}}", "single.html": "{{msg . "single"}}", "archive.html": "{{msg . "archive"}}", "post.html": "{{msg . "post"}}", "tags.html": "{{msg . "tags"}}", "tag_posts.html": "{{msg . "tag_posts"}}", "share_comment.html": "{{msg . "share_comment"}}", "404.html":"404", "theme.json": "{{msg . "themeJson"}}", "paging.html": "{{msg . "paging"}}", "highlight.html": "{{msg . "highlight"}}"},
init: function() {
var self = this;
self.showTplInfo();
@@ -171,12 +171,22 @@ var theme = {
newFile: function() {
var self = this;
self.newFileO.click(function() {
art.prompt("文件名", function(file){
// 文件是否存在?
art.prompt("Filename", function(file) {
if(!file) {
return;
}
var t = '<a href="#" class="list-group-item " data-file="' + file + '"> <i class="fa fa-remove icon-muted remove-file" title="delete file" ></i> ' + file + '<span class="badge"></span></a>';
if($('[data-filename="' + file + '"]').length > 0) {
art.alert('File exists');
return;
}
var t = $('<a href="#" class="list-group-item " data-file="' + file + '"> <i class="fa fa-remove icon-muted remove-file" title="delete file" ></i> <span class="filename" data-filename="' + file + '">' + file + '</span><span class="badge"></span></a>');
$(".file-list").append(t);
self.cache[file] = "";
// trigger click, 选中之
t.click();
});
});
},
@@ -206,6 +216,9 @@ var theme = {
var content = editor.getValue()
var filename = self.curTpl;
if(!filename) {
return;
}
// 之前的内容是否有改动
if(self.cache[filename] == content) {
return;
@@ -234,7 +247,7 @@ var theme = {
renderContent: function(name, content) {
var self = this;
self.curTplO.html(name);
self.curTpl = name; // 这里设计
self.curTpl = name; // 这里才设置
var lang = "html";
if(name.indexOf(".js") > 0 || name.indexOf(".json") > 0) {
lang = "javascript";
@@ -251,8 +264,8 @@ var theme = {
renderTpl: function(name) {
var self = this;
var content = "";
self.curTpl = name; // 这里设置
if(self.cache[name]) {
self.curTpl = ""; // 这里设置为空
if(self.cache[name] != undefined) {
content = self.cache[name];
self.renderContent(name, content);
return;
@@ -264,6 +277,7 @@ var theme = {
}
});
},
//-----------------
// theme images
imageListO: $("#imageList"),

View File

@@ -36,18 +36,12 @@ var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2
require.config({
baseUrl: '/public',
paths: {
// ajax upload image/attach
'attachment_upload': 'js/app/attachment_upload',
'jquery.ui.widget': 'tinymce/plugins/leaui_image/public/js/jquery.ui.widget',
'fileupload': '/tinymce/plugins/leaui_image/public/js/jquery.fileupload',
'iframe-transport': '/tinymce/plugins/leaui_image/public/js/jquery.iframe-transport'
},
shim: {
'fileupload': {deps: ['jquery.ui.widget', 'iframe-transport']}
'avatar': 'member/js/avatar',
'fileupload': 'js/plugins/libs-min/fileupload',
}
});
$(function() {
require(['attachment_upload'], function(attachment_upload) {});
require(['avatar'], function(avatar) {});
});
</script>

View File

@@ -13,11 +13,15 @@
<span class="label label-green">{{msg . "verified"}}</span>
{{else}}
<span class="label label-red">{{msg . "unVerified"}}</span>
<a class="raw nowToActive">{{msg . "verifiedNow"}}</a>
<a class="a raw nowToActive">{{msg . "verifiedNow"}}</a>
{{msg . "or"}}
<a class="raw reSendActiveEmail">{{msg . "resendVerifiedEmail"}}</a>
<a class="a raw reSendActiveEmail">{{msg . "resendVerifiedEmail"}}</a>
{{end}}
<!--
<hr />
<div class="alert alert-danger" id="emailMsg" style="display: none"></div>
<div class="form-group">
<label>{{msg . "email"}}</label>
@@ -29,12 +33,28 @@
]'
data-msg_target="#emailMsg"
/>
{{msg . "updateEmailTips"}}
<div class="form-tips">{{msg . "updateEmailTips"}}</div>
</div>
<div class="form-group">
<label class="control-label" for="pwd">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd"
data-rules='[
{rule: "required", msg: "inputPassword"},
{rule: "password", msg: "errorPassword"}
]'
data-msg_target="#emailMsg"
>
<div class="form-tips">{{msg . "inputLoginPasswordTips"}}</div>
</div>
-->
</div>
<!--
<footer class="panel-footer text-right bg-light lter">
<button type="submit" id="emailBtn" class="btn btn-success">{{msg . "submit"}}</button>
</footer>
-->
</section>
</form>
</div>
@@ -76,7 +96,8 @@ $("#emailBtn").click(function(e) {
return;
}
var email = $("#email").val();
post("/user/updateEmailSendActiveEmail", {email: email}, function(e) {
var pwd = $("#pwd").val();
post("/user/updateEmailSendActiveEmail", {email: email, pwd: pwd}, function(e) {
if(e.Ok) {
var url = getEmailLoginAddress(email);
showAlert("#emailMsg", getMsg("verifiedEmaiHasSent") +" <a href='" + url + "' target='_blank'>" + getMsg("checkEmail") + "</a>", "success");

View File

@@ -24,7 +24,7 @@
]'
data-msg_target="#pwdMsg"
>
{{msg . "passwordTips"}}
<div class="form-tips">{{msg . "passwordTips"}}</div>
</div>
<div class="form-group">
<label class="control-label" for="pwd2">{{msg . "password2"}}</label>

View File

@@ -7,24 +7,26 @@
<meta name="apple-touch-fullscreen" content="yes">
<meta name=”apple-mobile-web-app-capable” content=”yes” />
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, Not Just A Notebook">
<title>leanote, Not Just A Notebook</title>
<meta name="description" content="Leanote, {{msg $ "moto"}}">
<title>Leanote, {{msg $ "moto"}}</title>
<link href="/css/bootstrap.css" rel="stylesheet" />
<link href="/css/bootstrap-min.css" rel="stylesheet" />
<!-- 先加载, 没有样式, 宽度不定 -->
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" rel="stylesheet"/>
<!-- leanote css -->
<link href="/css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet" />
<link href="/css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
<link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
<link href="/css/zTreeStyle/zTreeStyle-min.css" rel="stylesheet" />
<!-- mdeditor -->
<link href="/public/dist/themes/default.css" rel="stylesheet" />
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu-min.css?id=1" type="text/css" />
<script>
var hash = location.hash;
if(hash.indexOf("writing") >= 0) {
var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css" type="text/css" id="themeLink" />';
var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css?id=3" type="text/css" id="themeLink" />';
} else {
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css" type="text/css" id="themeLink" />';
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css?id=7" type="text/css" id="themeLink" />';
}
document.write(files);
</script>
@@ -60,7 +62,7 @@ function log(o) {
<!-- search -->
<div class="pull-left" id="searchWrap">
<form class="navbar-form form-inline col-lg-2" id="searchNote">
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
<input class="form-control" placeholder="{{msg . "SearchNote"}}" type="text" id="searchNoteInput">
</form>
</div>
@@ -68,45 +70,52 @@ function log(o) {
<div class="pull-left" style="" id="newNoteWrap">
<!-- 新建笔记 -->
<div id="newMyNote">
<a id="newNoteBtn" title="{{msg . "newNote"}}">
<i class="fa fa-file-o"></i>
<a class="new-note" id="newNoteBtn" title="{{msg . "newNote"}}">
<!--<i class="fa fa-file-o"></i>-->
<span class="new-note-text">{{msg . "newNote"}}</span>
<span class="new-note-text-abbr">{{msg . "new"}}</span>
</a>
<span class="new-split">|</span>
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">Markdown</span>
<a class="new-note new-markdown" id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
<!-- <i class="lea-icon icon-markdown"></i> -->
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
<span class="new-markdown-text-abbr">Md</span>
</a>
<span class="for-split"> - </span>
<span id="curNotebookForNewNote" notebookId=""></span>
<!--
<div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle"
id="listNotebookDropdownMenu" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
<input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/>
<input type="text" placeholder="{{msg . "SearchNotebook"}}" class="form-control" id="searchNotebookForAdd"/>
<ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
</ul>
</div>
</div>
-->
</div>
<!-- 只为新建别人的笔记 -->
<div id="newSharedNote" style="display: none">
<a id="newSharedNoteBtn">
<i class="fa fa-file-o"></i>
<a id="newSharedNoteBtn" class="new-note">
<!--<i class="fa fa-file-o"></i> -->
<span class="new-note-text">{{msg . "newNote"}}</span>
<span class="new-note-text-abbr">{{msg . "new"}}</span>
</a>
<span class="new-split">|</span>
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">Markdown</span>
<a id="newShareNoteMarkdownBtn" class="new-note new-markdown" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
<span class="new-markdown-text-abbr">Md</span>
</a>
<span class="for-split"> - </span>
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<!--
<div class="dropdown" style="display: inline-block">
<a id="listShareNotebookDropdownMenu" class="ios7-a dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
@@ -115,6 +124,7 @@ function log(o) {
<ul id="notebookNavForNewSharedNote"></ul>
</div>
</div>
-->
</div>
</div>
@@ -157,14 +167,14 @@ function log(o) {
</li>
<li role="presentation" class="my-link" >
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
<a target="_blank" href="{{$.userInfo.BlogUrl}}">
<i class="fa fa-bold"></i>
{{msg . "myBlog"}}</a>
</li>
<li role="presentation" class="divider my-link writing-mobile-hide"></li>
<li role="presentation" class="my-link toggle-editor-mode writing-mobile-hide" >
<a href="#writing"><i class="fa fa-rocket"></i>
<a href="#writing" title="{{msg . "writingMode"}} ctrl/cmd+e"><i class="fa fa-rocket"></i>
{{msg . "writingMode"}}</a>
</li>
@@ -186,13 +196,11 @@ function log(o) {
</div>
</div>
<!--
<div class="pull-right top-nav writting-hide lea-blog">
<a target="_blank" href="http://lea.leanote.com">
lea++
</a>
</div>
-->
<div class="pull-right top-nav writting-hide" id="myBlog">
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
@@ -269,7 +277,7 @@ function log(o) {
</div>
<div class="folderBody">
<input type="text" class="form-control" id="searchNotebookForList" placeholder="Search notebook"/>
<input type="text" class="form-control" id="searchNotebookForList" placeholder="{{msg . "SearchNotebook"}}"/>
<ul class="ztree" id="notebookList"></ul>
<ul class="ztree" id="notebookListForSearch"></ul>
</div>
@@ -416,37 +424,17 @@ function log(o) {
<div class="noteSplit" id="noteSplitter"></div>
<div id="note">
<!-- 只读模式 -->
<div id="noteRead">
<div id="noteReadContainer">
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix" id="noteReadInfo">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i>{{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i>{{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
<div id="noteReadContent">
</div>
</div>
</div>
<!-- 遮罩, 为了resize3Columns用 -->
<div id="noteMask" class="note-mask"></div>
<div id="noteMaskForLoading" class="note-mask">
<!-- for loading -->
<div id="noteMaskForLoading" class="note-mask"> <!-- z-index: 11 -->
<img src="/images/loading-24.gif"/>
<br />
loading...
</div>
<div id="editorMask">
<!-- for tips -->
<div id="editorMask"> <!-- z-index: 10 -->
{{msg . "noNoteNewNoteTips"}}
<br />
<br />
@@ -458,6 +446,16 @@ function log(o) {
{{msg . "canntNewNoteTips"}}
</div>
</div>
<div id="batchMask" class="note-mask"> <!-- z-index: 99 -->
<div class="batch-ctn" id="batchCtn"></div>
<div class="batch-info">
{{leaMsg . "<span></span> notes selected"}}
<p><i class="fa fa-cog"></i></p>
</div>
</div>
<div id="noteTop">
<!-- 左侧放tag, 右侧放按钮, 比如save, info, move, delete -->
<div id="tool" class="clearfix">
@@ -496,34 +494,36 @@ function log(o) {
</div>
<ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s"
data-toggle="dropdown">
<span class="fa fa-save"></span>
{{msg . "save"}}</a></li>
<li><a class="ios7-a " id="saveBtn"
data-toggle="dropdown" title="ctrl+s {{msg . "save"}}">
<span class="fa fa-save"></span></a></li>
<li class="dropdown" id="noteInfoDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" title="{{msg . "Information"}}">
<span class="fa fa-info"></span>
</a>
<div class="dropdown-menu" id="noteInfo"></div>
</li>
<li class="dropdown" id="attachDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach" title="{{msg . "attachments"}}">
<span class="fa fa-paperclip"></span>
{{msg . "attachments"}}<span id="attachNum"></span>
<span id="attachNum"></span>
</a>
<div class="dropdown-menu" id="attachMenu">
<ul id="attachList">
</ul>
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data">
<div id="dropAttach" class="dropzone">
<a class="btn btn-success btn-choose-file">
<i class="fa fa-upload"></i>
<span>Choose File</span>
<span>{{msg . "Choose File"}}</span>
</a>
<a class="btn btn-default" id="downloadAllBtn">
<i class="fa fa-download"></i>
<span>Download All</span>
</a>
<a class="btn btn-default" id="linkAllBtn">
<i class="fa fa-link"></i>
<span>Link All</span>
<span>{{msg . "Download All"}}</span>
</a>
<input type="file" name="file" multiple/>
</div>
<div id="attachUploadMsg">
@@ -532,31 +532,50 @@ function log(o) {
</div>
</li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown">
<span class="fa fa-question"></span>
{{msg . "editorTips"}}</a></li>
<li><a class="ios7-a " id="contentHistory"
data-toggle="dropdown">
<span class="fa fa-history"></span>
{{msg . "history"}}</a></li>
data-toggle="dropdown" title="{{msg . "history"}}">
<span class="fa fa-history"></span></a></li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown" title="{{msg . "editorTips"}}">
<span class="fa fa-question"></span></a></li>
</ul>
</div>
<div id="noteTitleDiv">
<input name="noteTitle" id="noteTitle" value="" placeholder="{{msg . "unTitled"}}" tabindex="1" />
</div>
<!-- 只读时 -->
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix" id="noteReadInfo">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i> {{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i> {{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
</div>
<div id="editor">
<div id="editor" class="read-only">
<!-- 编辑器 -->
<div id="mceToolbar">
<div id="popularToolbar"
style="position: absolute; right: 30px; left: 0"></div>
<a
style="dispaly: block; position: absolute; cursor: pointer; right: 10px; padding: 3px 10px"
id="moreBtn"> <i class="fa fa-angle-down"></i>
</a>
<div id="mceToolbarContainer">
<div id="popularToolbar" style="position: absolute; right: 30px; left: 0"></div>
<a id="moreBtn"> <i class="fa more-fa"></i></a>
</div>
<!-- 查看信息 -->
<div id="infoToolbar" class="info-toolbar invisible">
<a class="toolbar-update" title="{{msg . "modify"}} ctrl/cmd+e"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
</div>
</div>
<div class="editorBg"></div>
<div id="leanoteNav" class="leanoteNav">
@@ -571,7 +590,7 @@ function log(o) {
<!-- leaui image drop image to editor-->
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
<div id="drop">
Drop images to here
{{leaMsg . "Drop images to here"}}
<input type="file" name="file" multiple style="display: none"/>
</div>
<ul id="uploadMsg">
@@ -584,50 +603,56 @@ function log(o) {
</div>
</div>
<div id="mdEditor">
<div id="mdEditor" class="read-only">
<div class="layout-wrapper-l1">
<div class="layout-wrapper-l2">
<div class="navbar navbar-default">
<div class="navbar-inner" id="wmd-button-bar">
<ul class="nav left-buttons">
<li class="wmd-button-group1 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group2 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group3 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group5 btn-group"></li>
</ul>
<!-- 帮助 -->
<ul class="nav left-buttons">
<li class="wmd-button-group6 btn-group">
<li class="wmd-button btn btn-success" id="wmd-help-button" title="Markdown syntax" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
</li>
</ul>
<!--
<ul class="nav pull-right right-buttons">
<li class="offline-status hide">
<div class="text-danger">
<i class="icon-attention-circled"></i>offline
</div>
</li>
<li class="extension-buttons"></li>
</ul>
<ul class="nav pull-right title-container">
<li><div class="working-indicator"></div></li>
<li><a class="btn btn-success file-title-navbar" href="#"
title="Rename document"> </a></li>
<li><div class="input-file-title-container"><input type="text"
class="col-sm-4 form-control hide input-file-title"
placeholder="Document title" /></div></li>
</ul>
-->
<div id="mdInfoToolbar" class="info-toolbar invisible">
<a class="toolbar-update" title="{{msg . "modify"}} ctrl/cmd+e"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
</div>
<div class="wmd-button-bar-inner">
<ul class="nav left-buttons">
<li class="wmd-button-group1 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group2 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group3 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group5 btn-group"></li>
</ul>
<!-- 帮助 -->
<ul class="nav left-buttons">
<li class="wmd-button-group6 btn-group">
<li class="wmd-button btn btn-success" id="wmd-help-button" title="{{msg . "Markdown syntax"}}" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
</li>
</ul>
<!--
<ul class="nav pull-right right-buttons">
<li class="offline-status hide">
<div class="text-danger">
<i class="icon-attention-circled"></i>offline
</div>
</li>
<li class="extension-buttons"></li>
</ul>
<ul class="nav pull-right title-container">
<li><div class="working-indicator"></div></li>
<li><a class="btn btn-success file-title-navbar" href="#"
title="Rename document"> </a></li>
<li><div class="input-file-title-container"><input type="text"
class="col-sm-4 form-control hide input-file-title"
placeholder="Document title" /></div></li>
</ul>
-->
</div>
</div>
<div class="editorBg"></div>
</div>
@@ -685,32 +710,6 @@ function log(o) {
</div>
<!-- mdEditor -->
<!-- v2 -->
<div class="modal fade modal-insert-link">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h4 class="modal-title">Hyperlink</h4>
</div>
<div class="modal-body">
<p>Please provide the link URL and an optional title:</p>
<div class="input-group">
<span class="input-group-addon"><i class="icon-globe"></i></span><input
id="input-insert-link" type="text" class="col-sm-5 form-control"
placeholder='http://example.com/ "optional title"' />
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a>
<a href="#" class="btn btn-primary action-insert-link"
data-dismiss="modal">OK</a>
</div>
</div>
</div>
</div>
<!-- 插入图片 -->
<div class="modal fade modal-insert-image">
<div class="modal-dialog" style="width: 840px;max-width:100%;">
@@ -718,15 +717,15 @@ function log(o) {
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h4 class="modal-title">Image</h4>
<h4 class="modal-title">{{msg . "Image"}}</h4>
</div>
<div class="modal-body" style="padding-top: 0; padding-bottom: 0">
<iframe name="mdImageManager" style="width: 100%; height: 350px" scrolling="no" id="leauiIfrForMD" src="" frameborder="0"></iframe>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-default"
data-dismiss="modal">Cancel</a> <a href="#"
class="btn btn-primary action-insert-image" data-dismiss="modal">Insert Image</a>
data-dismiss="modal">{{msg . "Cancel"}}</a> <a href="#"
class="btn btn-primary action-insert-image" data-dismiss="modal">{{msg . "Insert Image"}}</a>
</div>
</div>
</div>
@@ -741,7 +740,7 @@ function log(o) {
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="editorDialog-title"></h4>
</div>
<div class="modal-body">
<p></p>
<div class="input-group">
@@ -851,24 +850,7 @@ function log(o) {
</div><!-- /.modal -->
<!-- 编辑器提示 -->
<div class="modal fade bs-modal-sm" id="tipsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "editorTips"}}</h4>
</div>
<div class="modal-body">
{{msg . "editorTipsInfo"}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- modal 静态区域 -->
<div class="display: hidden">
@@ -876,12 +858,11 @@ function log(o) {
</div>
</div>
</div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/jquery.ztree.all-3.5.js"></script>
<script src="/js/i18n/msg.{{.locale}}.js"></script>
<script src="/js/common.js"></script>
<script>
var UrlPrefix = '{{.siteUrl}}'; // 为了发weibo
var LEA = {};
LEA.locale = "{{.locale}}";
var UrlPrefix = '{{.siteUrl}}';
var UserInfo = {{.userInfo|jsonJs}};
var notebooks = {{.notebooks|jsonJs}};
var shareNotebooks = {{.shareNotebooks|jsonJs}};
@@ -894,38 +875,51 @@ var notes = {{.notes|jsonJs}};
var latestNotes = {{if .latestNotes}}{{.latestNotes|jsonJs}}{{else}}[]{{end}};
var noteContentJson = {{.noteContentJson|jsonJs}};
var tagsJson = {{.tags|jsonJs}};
LEA.locale = "{{.locale}}";
var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2
var GlobalConfigs = {{.globalConfigs|jsonJs}};
<!-- pro_tinymce_init_js -->
</script>
<!-- 渲染view -->
<script src="/tinymce/tinymce.js"></script>
<script src="/js/i18n/msg.{{.locale}}.js"></script>
<!-- pro_dep_js -->
<!-- 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="/public/tinymce/tinymce.js"></script>
<!-- /dev -->
<script src="/public/libs/ace/ace.js"></script>
<script src="/js/app/page.js"></script>
<!-- dev -->
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
<script src="/js/contextmenu/jquery.contextmenu.js"></script>
<script src="/js/common.js"></script>
<script src="/js/bootstrap-min.js"></script>
<script src="/js/app/note.js"></script>
<script src="/js/app/tag.js"></script>
<script src="/js/app/notebook.js"></script>
<script src="/js/app/share.js"></script>
<script src="/js/object_id-min.js"></script>
<script src="/js/app/page.js?t=1"></script>
<!-- /dev -->
<!-- pro_app_js -->
<script>
initPage();
</script>
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
<!-- v2 use require.js, mdeditor -->
<script>
window.baseDir = '/public/dist';
// 当tinymce.dev.js时, 请注释require
window.require = {
baseUrl: window.baseDir,
deps: ['main']
baseUrl: '/public',
};
</script>
<!-- pro_markdown_js -->
<!-- dev -->
<script src="/js/require.js"></script>
</script>
<script src="/public/dist/main.min.js"></script>
<!-- /dev -->
<script src="/public/js/plugins/main.js"></script>
</body>
</html>

View File

@@ -7,24 +7,26 @@
<meta name="apple-touch-fullscreen" content="yes">
<meta name=”apple-mobile-web-app-capable” content=”yes” />
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, Not Just A Notebook">
<title>leanote, Not Just A Notebook</title>
<meta name="description" content="Leanote, {{msg $ "moto"}}">
<title>Leanote, {{msg $ "moto"}}</title>
<link href="/css/bootstrap.css" rel="stylesheet" />
<link href="/css/bootstrap-min.css" rel="stylesheet" />
<!-- 先加载, 没有样式, 宽度不定 -->
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" rel="stylesheet"/>
<!-- leanote css -->
<link href="/css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet" />
<link href="/css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
<link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
<link href="/css/zTreeStyle/zTreeStyle-min.css" rel="stylesheet" />
<!-- mdeditor -->
<link href="/public/dist/themes/default.css" rel="stylesheet" />
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu-min.css?id=1" type="text/css" />
<script>
var hash = location.hash;
if(hash.indexOf("writing") >= 0) {
var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css" type="text/css" id="themeLink" />';
var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css?id=3" type="text/css" id="themeLink" />';
} else {
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css" type="text/css" id="themeLink" />';
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css?id=7" type="text/css" id="themeLink" />';
}
document.write(files);
</script>
@@ -60,7 +62,7 @@ function log(o) {
<!-- search -->
<div class="pull-left" id="searchWrap">
<form class="navbar-form form-inline col-lg-2" id="searchNote">
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
<input class="form-control" placeholder="{{msg . "SearchNote"}}" type="text" id="searchNoteInput">
</form>
</div>
@@ -68,45 +70,52 @@ function log(o) {
<div class="pull-left" style="" id="newNoteWrap">
<!-- 新建笔记 -->
<div id="newMyNote">
<a id="newNoteBtn" title="{{msg . "newNote"}}">
<i class="fa fa-file-o"></i>
<a class="new-note" id="newNoteBtn" title="{{msg . "newNote"}}">
<!--<i class="fa fa-file-o"></i>-->
<span class="new-note-text">{{msg . "newNote"}}</span>
<span class="new-note-text-abbr">{{msg . "new"}}</span>
</a>
<span class="new-split">|</span>
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">Markdown</span>
<a class="new-note new-markdown" id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
<!-- <i class="lea-icon icon-markdown"></i> -->
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
<span class="new-markdown-text-abbr">Md</span>
</a>
<span class="for-split"> - </span>
<span id="curNotebookForNewNote" notebookId=""></span>
<!--
<div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle"
id="listNotebookDropdownMenu" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
<input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/>
<input type="text" placeholder="{{msg . "SearchNotebook"}}" class="form-control" id="searchNotebookForAdd"/>
<ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
</ul>
</div>
</div>
-->
</div>
<!-- 只为新建别人的笔记 -->
<div id="newSharedNote" style="display: none">
<a id="newSharedNoteBtn">
<i class="fa fa-file-o"></i>
<a id="newSharedNoteBtn" class="new-note">
<!--<i class="fa fa-file-o"></i> -->
<span class="new-note-text">{{msg . "newNote"}}</span>
<span class="new-note-text-abbr">{{msg . "new"}}</span>
</a>
<span class="new-split">|</span>
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">Markdown</span>
<a id="newShareNoteMarkdownBtn" class="new-note new-markdown" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
<span class="new-markdown-text-abbr">Md</span>
</a>
<span class="for-split"> - </span>
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<!--
<div class="dropdown" style="display: inline-block">
<a id="listShareNotebookDropdownMenu" class="ios7-a dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
@@ -115,6 +124,7 @@ function log(o) {
<ul id="notebookNavForNewSharedNote"></ul>
</div>
</div>
-->
</div>
</div>
@@ -157,14 +167,14 @@ function log(o) {
</li>
<li role="presentation" class="my-link" >
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
<a target="_blank" href="{{$.userInfo.BlogUrl}}">
<i class="fa fa-bold"></i>
{{msg . "myBlog"}}</a>
</li>
<li role="presentation" class="divider my-link writing-mobile-hide"></li>
<li role="presentation" class="my-link toggle-editor-mode writing-mobile-hide" >
<a href="#writing"><i class="fa fa-rocket"></i>
<a href="#writing" title="{{msg . "writingMode"}} ctrl/cmd+e"><i class="fa fa-rocket"></i>
{{msg . "writingMode"}}</a>
</li>
@@ -186,13 +196,11 @@ function log(o) {
</div>
</div>
<!--
<div class="pull-right top-nav writting-hide lea-blog">
<a target="_blank" href="http://lea.leanote.com">
lea++
</a>
</div>
-->
<div class="pull-right top-nav writting-hide" id="myBlog">
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
@@ -269,7 +277,7 @@ function log(o) {
</div>
<div class="folderBody">
<input type="text" class="form-control" id="searchNotebookForList" placeholder="Search notebook"/>
<input type="text" class="form-control" id="searchNotebookForList" placeholder="{{msg . "SearchNotebook"}}"/>
<ul class="ztree" id="notebookList"></ul>
<ul class="ztree" id="notebookListForSearch"></ul>
</div>
@@ -416,37 +424,17 @@ function log(o) {
<div class="noteSplit" id="noteSplitter"></div>
<div id="note">
<!-- 只读模式 -->
<div id="noteRead">
<div id="noteReadContainer">
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix" id="noteReadInfo">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i>{{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i>{{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
<div id="noteReadContent">
</div>
</div>
</div>
<!-- 遮罩, 为了resize3Columns用 -->
<div id="noteMask" class="note-mask"></div>
<div id="noteMaskForLoading" class="note-mask">
<!-- for loading -->
<div id="noteMaskForLoading" class="note-mask"> <!-- z-index: 11 -->
<img src="/images/loading-24.gif"/>
<br />
loading...
</div>
<div id="editorMask">
<!-- for tips -->
<div id="editorMask"> <!-- z-index: 10 -->
{{msg . "noNoteNewNoteTips"}}
<br />
<br />
@@ -458,6 +446,16 @@ function log(o) {
{{msg . "canntNewNoteTips"}}
</div>
</div>
<div id="batchMask" class="note-mask"> <!-- z-index: 99 -->
<div class="batch-ctn" id="batchCtn"></div>
<div class="batch-info">
{{leaMsg . "<span></span> notes selected"}}
<p><i class="fa fa-cog"></i></p>
</div>
</div>
<div id="noteTop">
<!-- 左侧放tag, 右侧放按钮, 比如save, info, move, delete -->
<div id="tool" class="clearfix">
@@ -496,34 +494,36 @@ function log(o) {
</div>
<ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s"
data-toggle="dropdown">
<span class="fa fa-save"></span>
{{msg . "save"}}</a></li>
<li><a class="ios7-a " id="saveBtn"
data-toggle="dropdown" title="ctrl+s {{msg . "save"}}">
<span class="fa fa-save"></span></a></li>
<li class="dropdown" id="noteInfoDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" title="{{msg . "Information"}}">
<span class="fa fa-info"></span>
</a>
<div class="dropdown-menu" id="noteInfo"></div>
</li>
<li class="dropdown" id="attachDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach" title="{{msg . "attachments"}}">
<span class="fa fa-paperclip"></span>
{{msg . "attachments"}}<span id="attachNum"></span>
<span id="attachNum"></span>
</a>
<div class="dropdown-menu" id="attachMenu">
<ul id="attachList">
</ul>
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data">
<div id="dropAttach" class="dropzone">
<a class="btn btn-success btn-choose-file">
<i class="fa fa-upload"></i>
<span>Choose File</span>
<span>{{msg . "Choose File"}}</span>
</a>
<a class="btn btn-default" id="downloadAllBtn">
<i class="fa fa-download"></i>
<span>Download All</span>
</a>
<a class="btn btn-default" id="linkAllBtn">
<i class="fa fa-link"></i>
<span>Link All</span>
<span>{{msg . "Download All"}}</span>
</a>
<input type="file" name="file" multiple/>
</div>
<div id="attachUploadMsg">
@@ -532,31 +532,50 @@ function log(o) {
</div>
</li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown">
<span class="fa fa-question"></span>
{{msg . "editorTips"}}</a></li>
<li><a class="ios7-a " id="contentHistory"
data-toggle="dropdown">
<span class="fa fa-history"></span>
{{msg . "history"}}</a></li>
data-toggle="dropdown" title="{{msg . "history"}}">
<span class="fa fa-history"></span></a></li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown" title="{{msg . "editorTips"}}">
<span class="fa fa-question"></span></a></li>
</ul>
</div>
<div id="noteTitleDiv">
<input name="noteTitle" id="noteTitle" value="" placeholder="{{msg . "unTitled"}}" tabindex="1" />
</div>
<!-- 只读时 -->
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix" id="noteReadInfo">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i> {{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i> {{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
</div>
<div id="editor">
<div id="editor" class="read-only">
<!-- 编辑器 -->
<div id="mceToolbar">
<div id="popularToolbar"
style="position: absolute; right: 30px; left: 0"></div>
<a
style="dispaly: block; position: absolute; cursor: pointer; right: 10px; padding: 3px 10px"
id="moreBtn"> <i class="fa fa-angle-down"></i>
</a>
<div id="mceToolbarContainer">
<div id="popularToolbar" style="position: absolute; right: 30px; left: 0"></div>
<a id="moreBtn"> <i class="fa more-fa"></i></a>
</div>
<!-- 查看信息 -->
<div id="infoToolbar" class="info-toolbar invisible">
<a class="toolbar-update" title="{{msg . "modify"}} ctrl/cmd+e"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
</div>
</div>
<div class="editorBg"></div>
<div id="leanoteNav" class="leanoteNav">
@@ -571,7 +590,7 @@ function log(o) {
<!-- leaui image drop image to editor-->
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
<div id="drop">
Drop images to here
{{leaMsg . "Drop images to here"}}
<input type="file" name="file" multiple style="display: none"/>
</div>
<ul id="uploadMsg">
@@ -584,50 +603,56 @@ function log(o) {
</div>
</div>
<div id="mdEditor">
<div id="mdEditor" class="read-only">
<div class="layout-wrapper-l1">
<div class="layout-wrapper-l2">
<div class="navbar navbar-default">
<div class="navbar-inner" id="wmd-button-bar">
<ul class="nav left-buttons">
<li class="wmd-button-group1 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group2 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group3 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group5 btn-group"></li>
</ul>
<!-- 帮助 -->
<ul class="nav left-buttons">
<li class="wmd-button-group6 btn-group">
<li class="wmd-button btn btn-success" id="wmd-help-button" title="Markdown syntax" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
</li>
</ul>
<!--
<ul class="nav pull-right right-buttons">
<li class="offline-status hide">
<div class="text-danger">
<i class="icon-attention-circled"></i>offline
</div>
</li>
<li class="extension-buttons"></li>
</ul>
<ul class="nav pull-right title-container">
<li><div class="working-indicator"></div></li>
<li><a class="btn btn-success file-title-navbar" href="#"
title="Rename document"> </a></li>
<li><div class="input-file-title-container"><input type="text"
class="col-sm-4 form-control hide input-file-title"
placeholder="Document title" /></div></li>
</ul>
-->
<div id="mdInfoToolbar" class="info-toolbar invisible">
<a class="toolbar-update" title="{{msg . "modify"}} ctrl/cmd+e"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
</div>
<div class="wmd-button-bar-inner">
<ul class="nav left-buttons">
<li class="wmd-button-group1 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group2 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group3 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group5 btn-group"></li>
</ul>
<!-- 帮助 -->
<ul class="nav left-buttons">
<li class="wmd-button-group6 btn-group">
<li class="wmd-button btn btn-success" id="wmd-help-button" title="{{msg . "Markdown syntax"}}" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
</li>
</ul>
<!--
<ul class="nav pull-right right-buttons">
<li class="offline-status hide">
<div class="text-danger">
<i class="icon-attention-circled"></i>offline
</div>
</li>
<li class="extension-buttons"></li>
</ul>
<ul class="nav pull-right title-container">
<li><div class="working-indicator"></div></li>
<li><a class="btn btn-success file-title-navbar" href="#"
title="Rename document"> </a></li>
<li><div class="input-file-title-container"><input type="text"
class="col-sm-4 form-control hide input-file-title"
placeholder="Document title" /></div></li>
</ul>
-->
</div>
</div>
<div class="editorBg"></div>
</div>
@@ -685,32 +710,6 @@ function log(o) {
</div>
<!-- mdEditor -->
<!-- v2 -->
<div class="modal fade modal-insert-link">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h4 class="modal-title">Hyperlink</h4>
</div>
<div class="modal-body">
<p>Please provide the link URL and an optional title:</p>
<div class="input-group">
<span class="input-group-addon"><i class="icon-globe"></i></span><input
id="input-insert-link" type="text" class="col-sm-5 form-control"
placeholder='http://example.com/ "optional title"' />
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a>
<a href="#" class="btn btn-primary action-insert-link"
data-dismiss="modal">OK</a>
</div>
</div>
</div>
</div>
<!-- 插入图片 -->
<div class="modal fade modal-insert-image">
<div class="modal-dialog" style="width: 840px;max-width:100%;">
@@ -718,15 +717,15 @@ function log(o) {
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h4 class="modal-title">Image</h4>
<h4 class="modal-title">{{msg . "Image"}}</h4>
</div>
<div class="modal-body" style="padding-top: 0; padding-bottom: 0">
<iframe name="mdImageManager" style="width: 100%; height: 350px" scrolling="no" id="leauiIfrForMD" src="" frameborder="0"></iframe>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-default"
data-dismiss="modal">Cancel</a> <a href="#"
class="btn btn-primary action-insert-image" data-dismiss="modal">Insert Image</a>
data-dismiss="modal">{{msg . "Cancel"}}</a> <a href="#"
class="btn btn-primary action-insert-image" data-dismiss="modal">{{msg . "Insert Image"}}</a>
</div>
</div>
</div>
@@ -741,7 +740,7 @@ function log(o) {
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="editorDialog-title"></h4>
</div>
<div class="modal-body">
<p></p>
<div class="input-group">
@@ -851,24 +850,7 @@ function log(o) {
</div><!-- /.modal -->
<!-- 编辑器提示 -->
<div class="modal fade bs-modal-sm" id="tipsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "editorTips"}}</h4>
</div>
<div class="modal-body">
{{msg . "editorTipsInfo"}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- modal 静态区域 -->
<div class="display: hidden">
@@ -876,12 +858,11 @@ function log(o) {
</div>
</div>
</div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/jquery.ztree.all-3.5-min.js"></script>
<script src="/js/i18n/msg.{{.locale}}.js"></script>
<script src="/js/common-min.js"></script>
<script>
var UrlPrefix = '{{.siteUrl}}'; // 为了发weibo
var LEA = {};
LEA.locale = "{{.locale}}";
var UrlPrefix = '{{.siteUrl}}';
var UserInfo = {{.userInfo|jsonJs}};
var notebooks = {{.notebooks|jsonJs}};
var shareNotebooks = {{.shareNotebooks|jsonJs}};
@@ -894,38 +875,33 @@ var notes = {{.notes|jsonJs}};
var latestNotes = {{if .latestNotes}}{{.latestNotes|jsonJs}}{{else}}[]{{end}};
var noteContentJson = {{.noteContentJson|jsonJs}};
var tagsJson = {{.tags|jsonJs}};
LEA.locale = "{{.locale}}";
var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2
var GlobalConfigs = {{.globalConfigs|jsonJs}};
var tinyMCEPreInit = {base: '/public/tinymce', suffix: '.min'};
</script>
<!-- 渲染view -->
<script src="/tinymce/tinymce.min.js"></script>
<script src="/js/i18n/msg.{{.locale}}.js"></script>
<script src="/js/dep.min.js"></script>
<script src="/public/libs/ace/ace.js"></script>
<script src="/js/app/page-min.js"></script>
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll-min.js"></script>
<script src="/js/contextmenu/jquery.contextmenu-min.js"></script>
<script src="/js/bootstrap-min.js"></script>
<script src="/js/app/note-min.js"></script>
<script src="/js/app/tag-min.js"></script>
<script src="/js/app/notebook-min.js"></script>
<script src="/js/app/share-min.js"></script>
<script src="/js/object_id-min.js"></script>
<script src="/js/app.min.js"></script>
<script>
initPage();
</script>
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
<!-- v2 use require.js, mdeditor -->
<script>
window.baseDir = '/public/dist';
// 当tinymce.dev.js时, 请注释require
window.require = {
baseUrl: window.baseDir,
deps: ['main']
baseUrl: '/public',
};
</script>
<script src="/js/require.js"></script>
</script>
<script src="/js/markdown.min.js"></script>
<script src="/public/js/plugins/main.min.js"></script>
</body>
</html>

View File

@@ -1,32 +0,0 @@
{{template "home/header_box.html" .}}
<section id="box" class="animated fadeInUp">
<div>
<h1 id="logo">leanote</h1>
<div id="boxForm">
<div id="boxHeader">We got a error</div>
<form>
<div class="alert alert-danger" id="loginMsg" style="display: block">
Sorry, we can't get your infomation.
<br />
Please <a href="/login">{{msg . "login"}}</a> Or <a href="/register?email={{.email}}">{{msg . "register"}}</a>
</div>
</form>
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
</body>
</html>

View File

@@ -2,40 +2,85 @@
# release leanote
# 当前路径
SP=$(cd "$(dirname "$0")"; pwd)
cd $SP
cd ../
# tmp path to store leanote release files
tmp="/Users/life/Desktop/leanote_release"
version=x86_64.v1.0-beta.2
# version
V="v1.2"
##=================================
# 1. 先build 成 3个平台, 2种bit = 6种
##=================================
# cd /Documents/Go/package2/src/github.com/leanote/leanote/bin
# GOOS=darwin GOARCH=amd64 go build -o leanote-darwin-amd64 ../app/tmp
cd $SP
# $1 = darwin, linux
# $2 = amd64
function build()
{
echo build-$1-$2
if [ $1 = "linux" -o $1 = "darwin" ]
then
suffix=""
else
suffix=".exe"
fi
GOOS=$1 GOARCH=$2 go build -o leanote-$1-$2$suffix ../app/tmp
}
build "linux" "386";
build "linux" "amd64";
build "windows" "386";
build "windows" "amd64";
# build "darwin" "386";
build "darwin" "amd64";
##======================
# 2. release目录准备工作
##======================
rm -rf $tmp/leanote
mkdir -p $tmp/leanote/app
mkdir -p $tmp/leanote/conf
mkdir -p $tmp/leanote/bin
##==================
# 3. 复制
##==================
cd $SP
cd ../
# bin
cp -r ./bin/src $tmp/leanote/bin/
cp ./bin/run.sh $tmp/leanote/bin/
# LICENSE
cp ./LICENSE $tmp/leanote/
cp ./bin/run.bat $tmp/leanote/bin/
# views
cp -r ./app/views $tmp/leanote/app
# 可不要
cp -r ./app/service $tmp/leanote/app/service
cp -r ./app/controllers $tmp/leanote/app/controllers
cp -r ./app/db $tmp/leanote/app/db
cp -r ./app/info $tmp/leanote/app/info
cp -r ./app/lea $tmp/leanote/app/lea
# 可不要, 源码
#cp -r ./app/service $tmp/leanote/app/service
#cp -r ./app/controllers $tmp/leanote/app/controllers
#cp -r ./app/db $tmp/leanote/app/db
#cp -r ./app/info $tmp/leanote/app/info
#cp -r ./app/lea $tmp/leanote/app/lea
# conf
cp ./conf/app.conf-default $tmp/leanote/conf/app.conf
cp ./conf/routes-default $tmp/leanote/conf/routes
cp ./conf/app.conf $tmp/leanote/conf/app.conf
cp ./conf/routes $tmp/leanote/conf/routes
# 处理app.conf, 还原配置
cat $tmp/leanote/conf/app.conf | sed 's/db.dbname=leanote.*#/db.dbname=leanote #/' > $tmp/leanote/conf/app.conf2 # 不能直接覆盖
rm $tmp/leanote/conf/app.conf
mv $tmp/leanote/conf/app.conf2 $tmp/leanote/conf/app.conf
# others
cp -r ./messages ./public ./mongodb_backup $tmp/leanote/
@@ -44,40 +89,57 @@ cp -r ./messages ./public ./mongodb_backup $tmp/leanote/
rm -r $tmp/leanote/public/tinymce/classes
rm -r $tmp/leanote/public/upload
mkdir $tmp/leanote/public/upload
mkdir $tmp/leanote/files
rm -r $tmp/leanote/public/.codekit-cache
rm $tmp/leanote/public/.DS_Store
rm $tmp/leanote/public/config.codekit
# make link
cd $tmp/leanote/bin
mkdir ./src/github.com/leanote
# cd $tmp/leanote/bin
# ln -s ../../../../ ./src/github.com/leanote/leanote
# archieve
# << 'BLOCK
# linux
cd $SP
cd ../
cp ./bin/leanote-linux $tmp/leanote/bin/
cd $tmp
tar -cvf $tmp/leanote-linux-$version.bin.tar leanote
gzip $tmp/leanote-linux-$version.bin.tar
# mac
rm $tmp/leanote/bin/leanote-linux
cd $SP
cd ../
cp ./bin/leanote-mac $tmp/leanote/bin/
cd $tmp
tar -cvf $tmp/leanote-mac-$version.bin.tar leanote
gzip $tmp/leanote-mac-$version.bin.tar
##===========
# 4. 打包
##===========
# $1 = linux
# $2 = 386, amd64
cd $SP
cd ../
cp ./bin/leanote-linux $tmp/leanote/bin/
# 创建一个$V的目录存放之
rm -rf $tmp/$V
mkdir $tmp/$V
rm $tmp/bin/src/github.com/leanote/leanote
cp -r $tmp/leanote/* $tmp/leanote_release_github
function tarRelease()
{
echo tar-$1-$2
cd $SP
cd ../
rm $tmp/leanote/bin/leanote-* # 删除之前的bin文件
rm $tmp/leanote/bin/run* # 删除之前的run.sh 或 run.bat
if [ $1 = "linux" -o $1 = "darwin" ]
then
suffix=""
cp ./bin/run.sh $tmp/leanote/bin/
else
cp ./bin/run.bat $tmp/leanote/bin/
suffix=".exe"
fi
cp ./bin/leanote-$1-$2$suffix $tmp/leanote/bin/
cd $tmp
tar -cf $tmp/$V/leanote-$1-$2-$V.bin.tar leanote
gzip $tmp/$V/leanote-$1-$2-$V.bin.tar
}
# BLOCK'
tarRelease "linux" "386";
tarRelease "linux" "amd64";
tarRelease "windows" "386";
tarRelease "windows" "amd64";
# tarRelease "darwin" "386";
tarRelease "darwin" "amd64";
# BLOCK'

23
bin/run.bat Normal file
View File

@@ -0,0 +1,23 @@
@echo off
cd..
set SCRIPTPATH=%cd%
: top src directory
set path="%SCRIPTPATH%\bin\src\github.com\leanote"
if not exist "%path%" mkdir "%path%"
: create software link
if exist "%path%\leanote" del /Q "%path%\leanote"
mklink /D "%path%\leanote" %SCRIPTPATH%
: set GOPATH
set GOPATH="%SCRIPTPATH%\bin"
: run
if %processor_architecture%==x86 (
"%SCRIPTPATH%\bin\leanote-windows-386.exe" -importPath github.com/leanote/leanote
) else (
"%SCRIPTPATH%\bin\leanote-windows-amd64.exe" -importPath github.com/leanote/leanote
)

View File

@@ -7,17 +7,23 @@ path="$SCRIPTPATH/src/github.com/leanote"
if [ ! -d "$path" ]; then
mkdir -p "$path"
fi
rm -rf $SCRIPTPATH/src/github.com/leanote/leanote # 先删除
ln -s ../../../../ $SCRIPTPATH/src/github.com/leanote/leanote
# set GOPATH
export GOPATH=$GOPATH:$SCRIPTPATH
export GOPATH=$SCRIPTPATH
# run
osName=`uname`
if [ $osName == "Darwin" ]; then
chmod 777 "$SCRIPTPATH/leanote-mac"
"$SCRIPTPATH/leanote-mac" -importPath github.com/leanote/leanote
osName=`uname` # Darwin or Linux
osName=`tr '[A-Z]' '[a-z]' <<<"$osName"` # toLowerCase
bit=`getconf LONG_BIT` # 32, 64
if [ $bit = "64" ]
then
bit="amd64"
else
chmod 777 "$SCRIPTPATH/leanote-linux"
"$SCRIPTPATH/leanote-linux" -importPath github.com/leanote/leanote
bit="386"
fi
script="$SCRIPTPATH/leanote-$osName-$bit"
chmod 777 $script
$script -importPath github.com/leanote/leanote

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

File diff suppressed because one or more lines are too long

View File

@@ -6,15 +6,19 @@ http.port=9000
site.url=http://localhost:9000 # or http://x.com:8080, http://www.xx.com:9000
# admin username
adminUsername=admin
# mongdb
db.host=localhost
db.port=27017
db.dbname=leanote # 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 for more complex needs the format is:
# or you can set the mongodb url for more complex needs the format is:
# mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
# db.url=mongodb://root:root123@localhost:27017/leanote
# db.urlEnv=${MONGODB_URL} # set url from env. eg. mongodb://root:root123@localhost:27017/leanote
# You Must Change It !! About Security!!
app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y
@@ -28,7 +32,7 @@ http.addr=
http.ssl=false
cookie.httponly=false
cookie.prefix=LEANOTE
cookie.domain= # for share cookie with subdomain, 默认为空, 即不设置 不能设置为localhost, 要么就为空
cookie.domain= # for share cookie with sub-domain
cookie.secure=false
format.date=01/02/2006
format.datetime=01/02/2006 15:04
@@ -42,14 +46,14 @@ log.error.prefix = "ERROR "
# The default language of this application.
i18n.default_language=en
module.static=github.com/revel/revel/modules/static
module.static=github.com/revel/modules/static
[dev]
mode.dev=true
results.pretty=true
watch=true
module.testrunner = github.com/revel/revel/modules/testrunner
module.testrunner = # github.com/revel/modules/testrunner
log.trace.output = stderr
log.info.output = stderr

Some files were not shown because too many files have changed in this diff Show More