Compare commits
409 Commits
1.0-beta.2
...
2.2.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
000dfea92d | ||
|
|
a53398ebb4 | ||
|
|
b23592229a | ||
|
|
584bde247e | ||
|
|
6774392807 | ||
|
|
964cf1a750 | ||
|
|
52128e6453 | ||
|
|
032470c3df | ||
|
|
a8dd578624 | ||
|
|
f49624d3eb | ||
|
|
816af11db2 | ||
|
|
eda03f0aa4 | ||
|
|
6a06511405 | ||
|
|
15d8ebdc0f | ||
|
|
5dc929c4c0 | ||
|
|
a4d6a99fee | ||
|
|
15d3b847cb | ||
|
|
065ac99942 | ||
|
|
08ec359b93 | ||
|
|
2f81c04f41 | ||
|
|
e8bff79425 | ||
|
|
4ca8e8cd83 | ||
|
|
c0cc08a3c2 | ||
|
|
d32a6dd277 | ||
|
|
8bacc15309 | ||
|
|
8a3f24b9c6 | ||
|
|
2a7d1fe7b5 | ||
|
|
9a05ffcd8f | ||
|
|
aa95e680f9 | ||
|
|
874ab69c34 | ||
|
|
d43f5fd7ab | ||
|
|
6a048f0d9a | ||
|
|
cb3681f651 | ||
|
|
dfd7159401 | ||
|
|
929ec975da | ||
|
|
e826dc7b93 | ||
|
|
629a49e54b | ||
|
|
6ebedf94f0 | ||
|
|
a07bb25128 | ||
|
|
a007b89ebb | ||
|
|
cc4b003503 | ||
|
|
091922175a | ||
|
|
371c93912d | ||
|
|
27221acda7 | ||
|
|
5db724e80b | ||
|
|
14265d9de4 | ||
|
|
5c9e95ed60 | ||
|
|
2fc97ed42d | ||
|
|
45cca30cec | ||
|
|
31167b6aaf | ||
|
|
139906b9d6 | ||
|
|
a9d49369e2 | ||
|
|
303e0da5c0 | ||
|
|
ff5523bfaa | ||
|
|
063dd0140d | ||
|
|
c52d388d5d | ||
|
|
18bcdb48af | ||
|
|
db8f132507 | ||
|
|
439d5212b0 | ||
|
|
88e2320027 | ||
|
|
659a4e590a | ||
|
|
ab7f2386e0 | ||
|
|
ec82ff18f2 | ||
|
|
f3f00626e6 | ||
|
|
80fbb56426 | ||
|
|
a5c952882f | ||
|
|
973b452a7e | ||
|
|
25bcd21ea9 | ||
|
|
09de5e1e83 | ||
|
|
15b0ef21ff | ||
|
|
9cf9ded855 | ||
|
|
7af433ebaf | ||
|
|
67b608b324 | ||
|
|
2aa3f5a654 | ||
|
|
f5538218bd | ||
|
|
0e3bdd177c | ||
|
|
12520cc09d | ||
|
|
cb67892124 | ||
|
|
45c09c9a26 | ||
|
|
20951ea2dd | ||
|
|
530f2368da | ||
|
|
e685a99628 | ||
|
|
bfc5acc3ee | ||
|
|
551e3d965e | ||
|
|
7c479b5595 | ||
|
|
b135f9717f | ||
|
|
08ae37cae0 | ||
|
|
315a7bbe62 | ||
|
|
e173b18ddc | ||
|
|
dcc25df679 | ||
|
|
c561dec14c | ||
|
|
43e8f2e701 | ||
|
|
dd9952c453 | ||
|
|
19517dd634 | ||
|
|
a5b006aae7 | ||
|
|
90bd07cdcc | ||
|
|
92576e9239 | ||
|
|
c58e6874c2 | ||
|
|
cfebf00cf1 | ||
|
|
19be0990da | ||
|
|
3f27e4b0d4 | ||
|
|
4710fb6686 | ||
|
|
28324c0178 | ||
|
|
f270ade8d7 | ||
|
|
3b3e665a9f | ||
|
|
bfa85e2d34 | ||
|
|
fcf270a521 | ||
|
|
eba113c73c | ||
|
|
41f63156ea | ||
|
|
f54fe397c3 | ||
|
|
6f157289d3 | ||
|
|
65792d020c | ||
|
|
926882dce6 | ||
|
|
f834f469fc | ||
|
|
06d42c36f0 | ||
|
|
17d513a9de | ||
|
|
8cc00c1f66 | ||
|
|
cb433d9392 | ||
|
|
86ca1efd11 | ||
|
|
263f978c54 | ||
|
|
c8af7eda5d | ||
|
|
3724811802 | ||
|
|
10ff48267f | ||
|
|
cd0913aec0 | ||
|
|
f8d4c2ef20 | ||
|
|
f16b5e816e | ||
|
|
9a9968760a | ||
|
|
b1f36dfbf1 | ||
|
|
bc0e09f222 | ||
|
|
0dab71e72b | ||
|
|
10f23c19ab | ||
|
|
bba57ea302 | ||
|
|
f052117989 | ||
|
|
df4ec53647 | ||
|
|
a7eaf6114a | ||
|
|
b25bf0e16c | ||
|
|
d13447e6d0 | ||
|
|
6a1e84da0c | ||
|
|
51aeb7d46e | ||
|
|
2dbd025768 | ||
|
|
d52d80d529 | ||
|
|
2000bc585b | ||
|
|
8301a1e515 | ||
|
|
bee65eb36d | ||
|
|
78199583b0 | ||
|
|
28d5678349 | ||
|
|
e97c468f6e | ||
|
|
5d18554542 | ||
|
|
3ff0a9e09c | ||
|
|
6a753682b7 | ||
|
|
ce491e62c3 | ||
|
|
e4b847b731 | ||
|
|
a12b285a35 | ||
|
|
7f0ada8016 | ||
|
|
bac5ab4d79 | ||
|
|
b362406819 | ||
|
|
9e2edbb987 | ||
|
|
7809d00787 | ||
|
|
cba69444a8 | ||
|
|
5e02816c09 | ||
|
|
20b085aeae | ||
|
|
0f5e683a4f | ||
|
|
e134ca7244 | ||
|
|
6818574605 | ||
|
|
6eea03f81e | ||
|
|
1242834b7f | ||
|
|
1844513f08 | ||
|
|
b0be7a14d2 | ||
|
|
7e17c68dba | ||
|
|
6259f15ffa | ||
|
|
f65d027a0d | ||
|
|
e9e2ca50ee | ||
|
|
a64ef8517e | ||
|
|
c9bda60554 | ||
|
|
c3478520c0 | ||
|
|
8346ffe915 | ||
|
|
f49b046601 | ||
|
|
263c03291e | ||
|
|
29244c247e | ||
|
|
320de8637f | ||
|
|
33bc6bf84b | ||
|
|
0cd2c42b68 | ||
|
|
1716acf9e0 | ||
|
|
45477119d4 | ||
|
|
716fbeae0c | ||
|
|
103891e0c0 | ||
|
|
638b5b51e0 | ||
|
|
90fea722aa | ||
|
|
90ce35327f | ||
|
|
60e3b9446a | ||
|
|
16a726223c | ||
|
|
dfe45f39bb | ||
|
|
413be4a6d0 | ||
|
|
6a35f98f89 | ||
|
|
ad295d97c8 | ||
|
|
cf8171c9cd | ||
|
|
c616a4e9a6 | ||
|
|
04d641a5ad | ||
|
|
ec68570a38 | ||
|
|
407b44382c | ||
|
|
71a2bcc7f0 | ||
|
|
a2dd09725c | ||
|
|
f757e00f03 | ||
|
|
b7652bb321 | ||
|
|
e6018d32ec | ||
|
|
cb7e272bbe | ||
|
|
a4d9d8e0d3 | ||
|
|
c844c8b188 | ||
|
|
320263eefa | ||
|
|
274875c6c4 | ||
|
|
1ea35324c1 | ||
|
|
3e20ad3cdd | ||
|
|
711e6b3d17 | ||
|
|
0438bbb414 | ||
|
|
70ee362cc8 | ||
|
|
0479f1a433 | ||
|
|
7e01cb8227 | ||
|
|
ae0eb3b918 | ||
|
|
097d2709e2 | ||
|
|
f1e56272ef | ||
|
|
db4cfbf605 | ||
|
|
da7d31fa00 | ||
|
|
62bc74d3c6 | ||
|
|
4467689ec3 | ||
|
|
69a874d90d | ||
|
|
b259a00c94 | ||
|
|
a0088ead9e | ||
|
|
228fd80abd | ||
|
|
8b3a1a646a | ||
|
|
c4954b94b8 | ||
|
|
2b56ebd620 | ||
|
|
a78d95ddad | ||
|
|
938362154a | ||
|
|
8bff2b7000 | ||
|
|
c56f228646 | ||
|
|
98f0313e3e | ||
|
|
bde2e891ef | ||
|
|
7acdaede0d | ||
|
|
45d1b4bee3 | ||
|
|
4d0a170f01 | ||
|
|
5e37e5a37d | ||
|
|
a60da3d592 | ||
|
|
9a88fab84e | ||
|
|
4981d6aad4 | ||
|
|
7193ac6833 | ||
|
|
3d1a32764d | ||
|
|
2b8df3ed76 | ||
|
|
e94b004a5a | ||
|
|
6cc6f26fa1 | ||
|
|
120cdd53c3 | ||
|
|
435aac4a9c | ||
|
|
f234cf285d | ||
|
|
54953b69c6 | ||
|
|
60878b7a65 | ||
|
|
dada88d8a2 | ||
|
|
7167705409 | ||
|
|
4217abca6e | ||
|
|
49a97f2285 | ||
|
|
4f2d7b8cd0 | ||
|
|
afbda7bfb2 | ||
|
|
c568756d16 | ||
|
|
5794f76b0a | ||
|
|
74a768c0fb | ||
|
|
4136060825 | ||
|
|
e249a953cd | ||
|
|
eddc2e6356 | ||
|
|
1604474d6e | ||
|
|
bbaf71481c | ||
|
|
952117818c | ||
|
|
a31432f1bf | ||
|
|
b0afe25730 | ||
|
|
024e0e9bae | ||
|
|
dbabc3a5a8 | ||
|
|
a30aec8254 | ||
|
|
7440218974 | ||
|
|
b3c771a87f | ||
|
|
8f8451cdff | ||
|
|
6d7fe83469 | ||
|
|
05a5280162 | ||
|
|
8dd55eb949 | ||
|
|
5838cc218f | ||
|
|
2e52c957e0 | ||
|
|
9486c58d78 | ||
|
|
6e7c1e0d41 | ||
|
|
6a0a60a644 | ||
|
|
94f9c2c3cb | ||
|
|
37e7ed8d14 | ||
|
|
4227c80ec7 | ||
|
|
6ec6ef698b | ||
|
|
0543dd015e | ||
|
|
b88f895acf | ||
|
|
fc517700cc | ||
|
|
b5e0cd31dd | ||
|
|
ab4b8b77a3 | ||
|
|
04c339896b | ||
|
|
e37056737d | ||
|
|
89f8bb934c | ||
|
|
b564989214 | ||
|
|
03aa8edd58 | ||
|
|
803fc90dd9 | ||
|
|
6987a38820 | ||
|
|
7e458bb433 | ||
|
|
b391375008 | ||
|
|
3f89e1cf83 | ||
|
|
9f5dc8a0e2 | ||
|
|
12ea17e89f | ||
|
|
1fd6610fa3 | ||
|
|
1ec28d80c8 | ||
|
|
fcbc23e964 | ||
|
|
6fba2a6416 | ||
|
|
0e5edb16f6 | ||
|
|
5726df1a81 | ||
|
|
f3a0a786c5 | ||
|
|
fa08b3393a | ||
|
|
5db148be9f | ||
|
|
d03cce2761 | ||
|
|
3a0bdd803d | ||
|
|
d1b6dddfd0 | ||
|
|
2676c5a85d | ||
|
|
bde731d84c | ||
|
|
46bc714b45 | ||
|
|
7269226422 | ||
|
|
c390d12dd2 | ||
|
|
68eaac9622 | ||
|
|
a089292461 | ||
|
|
b59eba4ca4 | ||
|
|
8e52074346 | ||
|
|
4fb7e8dbaa | ||
|
|
8ed260976a | ||
|
|
d8dac67913 | ||
|
|
ade77bba58 | ||
|
|
849118f9b8 | ||
|
|
f0b45deaec | ||
|
|
5ebfe301f8 | ||
|
|
801263f50c | ||
|
|
6bd6f70cbf | ||
|
|
f38a86cb07 | ||
|
|
f755574576 | ||
|
|
d568c286d5 | ||
|
|
cbf5c7f941 | ||
|
|
ce0e099bbd | ||
|
|
fbf819f4d2 | ||
|
|
bb65ef992a | ||
|
|
ab0ee68f39 | ||
|
|
f959694b0f | ||
|
|
5fae5dd10e | ||
|
|
9aacba4c31 | ||
|
|
e47dc0dc18 | ||
|
|
0b3d69e73c | ||
|
|
decf580ed3 | ||
|
|
e186cf984c | ||
|
|
3b1d9e6a73 | ||
|
|
cb218086f3 | ||
|
|
0f7ebb6c53 | ||
|
|
257a141519 | ||
|
|
12bc62e990 | ||
|
|
fbeecdf062 | ||
|
|
1e0fd166db | ||
|
|
2484abe7cf | ||
|
|
cbaa52e57b | ||
|
|
407e4c3ac4 | ||
|
|
8a61b64575 | ||
|
|
419585ff7d | ||
|
|
ffaaa8c11a | ||
|
|
3417e71d04 | ||
|
|
014a141808 | ||
|
|
9803249ab1 | ||
|
|
ac99e20aa4 | ||
|
|
d5dabecb81 | ||
|
|
df9bc37e1d | ||
|
|
4277caa571 | ||
|
|
1d18908cd0 | ||
|
|
916ee52cdb | ||
|
|
289e7b16d9 | ||
|
|
016ef5de2d | ||
|
|
52d0a7ed21 | ||
|
|
ddf3236c4f | ||
|
|
9515f1e58f | ||
|
|
c514d0bc1c | ||
|
|
d24531dc78 | ||
|
|
2cfc89ca5f | ||
|
|
b0d4005ad6 | ||
|
|
c0cd433c3f | ||
|
|
792c8cfd40 | ||
|
|
4f102ff883 | ||
|
|
40973c4615 | ||
|
|
58bf623d05 | ||
|
|
46e97abe91 | ||
|
|
be01c9c3f7 | ||
|
|
17718732cc | ||
|
|
e2e90f8618 | ||
|
|
25d5df6bfc | ||
|
|
b781f8318c | ||
|
|
0e07cbab8b | ||
|
|
ed75704032 | ||
|
|
c49593171c | ||
|
|
45d72051d0 | ||
|
|
10b387faa6 | ||
|
|
30ebc95abf | ||
|
|
9bc65bc099 | ||
|
|
39809dc4c6 | ||
|
|
8c4a203d96 | ||
|
|
034ad821ca | ||
|
|
c894b7b308 | ||
|
|
c0979be9f3 | ||
|
|
b45e9eacb0 | ||
|
|
16d47418ad | ||
|
|
5d48d1853d | ||
|
|
f503da30b7 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -2,14 +2,12 @@
|
||||
pkg
|
||||
bin2
|
||||
bin/i18n
|
||||
bin/leanote-linux
|
||||
bin/leanote-mac
|
||||
bin/leanote*
|
||||
bin/release
|
||||
bin/test.sh
|
||||
bin/tmp
|
||||
bin/test
|
||||
bin/src
|
||||
conf/app.conf
|
||||
conf/routes
|
||||
public/upload
|
||||
app/routes/routes.go
|
||||
app/tmp/main.go
|
||||
@@ -18,3 +16,4 @@ app/tmp/main.go
|
||||
.project
|
||||
public/config.codekit
|
||||
files
|
||||
/node_modules
|
||||
|
||||
35
.travis.yml
Normal file
35
.travis.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
language: go
|
||||
go: 1.4
|
||||
|
||||
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/
|
||||
|
||||
# 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
|
||||
409
Gulpfile.js
Normal file
409
Gulpfile.js
Normal file
@@ -0,0 +1,409 @@
|
||||
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';
|
||||
|
||||
var messagesPath = leanoteBase + 'messages';
|
||||
|
||||
// 合并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'));
|
||||
});
|
||||
|
||||
// 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';
|
||||
}
|
||||
jss.push(base + '/js/plugins/libs-min/fileupload.js');
|
||||
|
||||
return gulp.src(jss)
|
||||
.pipe(uglify()) // 压缩
|
||||
.pipe(concat('main.min.js'))
|
||||
.pipe(gulp.dest(base + '/js/plugins'));
|
||||
});
|
||||
|
||||
// 合并requirejs和markdown为一个文件
|
||||
gulp.task('concatMarkdownJs', function() {
|
||||
var jss = [
|
||||
'js/require.js',
|
||||
'md/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'));
|
||||
});
|
||||
|
||||
// / 合并requirejs和markdown为一个文件
|
||||
gulp.task('concatMarkdownJsV2', function() {
|
||||
var jss = [
|
||||
'js/require.js',
|
||||
'md/main-v2.min.js',
|
||||
];
|
||||
|
||||
for(var i in jss) {
|
||||
jss[i] = base + '/' + jss[i];
|
||||
}
|
||||
|
||||
return gulp
|
||||
.src(jss)
|
||||
.pipe(uglify()) // 压缩
|
||||
.pipe(concat('markdown-v2.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_markdown_js -->/, '<script src="/js/markdown-v2.min.js"></script>')) // 替换
|
||||
.pipe(replace('/tinymce/tinymce.js', '/tinymce/tinymce.full.min.js')) // 替换
|
||||
.pipe(replace(/<!-- pro_tinymce_init_js -->/, "var tinyMCEPreInit = {base: '/public/tinymce', suffix: '.min'};")) // 替换
|
||||
.pipe(replace(/plugins\/main.js/, "plugins/main.min.js")) // 替换
|
||||
// 连续两个空行换成一个空行
|
||||
.pipe(replace(/\r\n\r\n/g, '\r\n'))
|
||||
.pipe(replace(/\r\n\r\n/g, '\r\n'))
|
||||
.pipe(replace(/\r\n\r\n/g, '\r\n'))
|
||||
.pipe(replace(/\r\n\r\n/g, '\r\n'))
|
||||
.pipe(replace(/\r\n\r\n/g, '\r\n'))
|
||||
.pipe(replace(/\r\n\r\n/g, '\r\n'))
|
||||
.pipe(replace('console.log(o);', ''))
|
||||
.pipe(replace('console.trace(o);', ''))
|
||||
// .pipe(minifyHtml()) // 不行, 压缩后golang报错
|
||||
.pipe(rename('note.html'))
|
||||
.pipe(gulp.dest(noteProBase));
|
||||
});
|
||||
|
||||
// 只获取需要js i18n的key
|
||||
var path = require('path');
|
||||
gulp.task('i18n', function() {
|
||||
var keys = {};
|
||||
var reg = /getMsg\(["']+(.+?)["']+/g;
|
||||
// {rule: "required", msg: "inputNewPassword"},
|
||||
var reg2 = /msg: ?"?([0-9a-zA-Z]*)"?/g;
|
||||
function getKey(data) {
|
||||
while(ret = reg.exec(data)) {
|
||||
keys[ret[1]] = 1;
|
||||
}
|
||||
|
||||
while(ret2 = reg2.exec(data)) {
|
||||
keys[ret2[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 + '/md');
|
||||
ls(base + '/js');
|
||||
ls(base + '/album');
|
||||
ls(base + '/libs');
|
||||
ls(base + '/member');
|
||||
ls(base + '/tinymce');
|
||||
|
||||
ls(leanoteBase + '/app/views');
|
||||
|
||||
console.log('parsed');
|
||||
var langs = {}; // zh-cn: 1
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 得到所有的语言的后缀
|
||||
// 返回{en-us: 1, }
|
||||
function getAllLangs() {
|
||||
var langs = {};
|
||||
var files = fs.readdirSync(messagesPath);
|
||||
for(fn in files) {
|
||||
var fname = files[fn];
|
||||
if (fname.indexOf('-') > 0) {
|
||||
langs[fname] = 1;
|
||||
}
|
||||
}
|
||||
return langs;
|
||||
}
|
||||
|
||||
// msg.zh, msg.js
|
||||
function genI18nJsFile(targetFilename, lang, fromFilenames, keys) {
|
||||
var msgs = {};
|
||||
fromFilenames.forEach(function (name) {
|
||||
var tmpMsgs = getAllMsgs(leanoteBase + '/messages/' + lang + '/' + name + '.conf');
|
||||
for (var i in tmpMsgs) {
|
||||
msgs[i] = tmpMsgs[i];
|
||||
}
|
||||
});
|
||||
|
||||
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;' +
|
||||
'}';
|
||||
|
||||
// 写入到文件中
|
||||
var toFilename = targetFilename + '.' + lang + '.js';
|
||||
fs.writeFile(base + '/js/i18n/' + toFilename, str);
|
||||
}
|
||||
|
||||
function genTinymceLang(lang) {
|
||||
var msgs = getAllMsgs(leanoteBase + 'messages/' + lang + '/tinymce_editor.conf');
|
||||
var str = 'tinymce.addI18n("' + lang + '",' + JSON.stringify(msgs) + ');';
|
||||
fs.writeFile(base + '/tinymce/langs/' + lang + '.js', str);
|
||||
}
|
||||
|
||||
var langs = getAllLangs();
|
||||
for (var lang in langs) {
|
||||
genI18nJsFile('blog', lang, ['blog'], keys);
|
||||
genI18nJsFile('msg', lang, ['msg', 'member', 'markdown', 'album'], keys);
|
||||
|
||||
genTinymceLang(lang);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// 合并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'));
|
||||
});
|
||||
|
||||
// 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));
|
||||
});
|
||||
|
||||
// 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 + '/md/themes/default.css')
|
||||
.pipe(rename({suffix: '-min'}))
|
||||
.pipe(minifycss())
|
||||
.pipe(gulp.dest(base + '/md/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'));
|
||||
}
|
||||
*/
|
||||
});
|
||||
|
||||
|
||||
gulp.task('concat', ['concatDepJs', 'concatAppJs', /* 'concatMarkdownJs', */'concatMarkdownJsV2']);
|
||||
gulp.task('html', ['devToProHtml']);
|
||||
gulp.task('default', ['concat', 'plugins', 'minifycss', 'i18n', 'concatAlbumJs', 'html']);
|
||||
6
LICENSE
6
LICENSE
@@ -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)
|
||||
|
||||
|
||||
277
README.md
277
README.md
@@ -1,194 +1,189 @@
|
||||
[中文](https://github.com/leanote/leanote#1-介绍)
|
||||
# Leanote
|
||||
|
||||
[](https://travis-ci.org/leanote/leanote)
|
||||
[](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
Leanote, note just a notebook!
|
||||
Leanote, not just a notepad!
|
||||

|
||||
|
||||
**Some Features**
|
||||
**Highlighted 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.
|
||||
* Cooperation: Collaborate with friends to improve your skills.
|
||||
* Blog: Publish your knowledge and make leanote your blog.
|
||||
* Note-taking made easy: Leanote incorporates a clean and intuitive interface, the `tinymce` rich-text editor and a dedicated *markdown* editor, making your writing/typing more efficient and enjoyable. For more advanced users, we even offer `Vim` and `Emacs` writing modes to help boost your writing speed to another level.
|
||||
* Knowledge management: The flexible and versatile notebook-note-tagging system of Leanote makes it an ideal tool for knowledge management.
|
||||
* Sharing: Share your knowledge, thoughts and experiences with friends via Leanote. Invite your friends to join your notepad in the cloud.
|
||||
* Cooperating: Collaborate with colleagues to improve skills, fertilize ideas and brainstorm on the fly.
|
||||
* Blogging: Publish your work and make Leanote your personal 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!
|
||||
**Other Features**
|
||||
|
||||
* Markdown syntax support
|
||||
* Distraction-free writing mode
|
||||
* `Vim` and `Emacs` editing mode
|
||||
* Export notes to PDFs
|
||||
* Batch note operation
|
||||
* Customizable themes for blogging
|
||||
|
||||
## 2. Why we create Leanote
|
||||
|
||||
We have been using the popular note-taking software/service `Evernote` as our knowledge management tool on a daily basis. Benefited from and inspired by `Evernote`, we decided to create a brand-new tool that provides everything `Evernote` has to offer, plus a bunch of new features that `Evernote` failed to deliver, such as:
|
||||
|
||||
* A more powerful editor: `Evernote`'s editor lacks the functionalities of **document navigation**, **syntax based code rendering** (as a programmer, syntax highlighted code rendering is a necessity), **image resizing** and so forth.
|
||||
* Everybody loves *markdown*, however `Evernote` simply wouldn't add it despite of years' of requests from users. So we will do the favor and bring a *markdown* enabled editor to you, guess what, it is also rendered in real-time!
|
||||
* If you a developer and miss the `Vim` or `Emacs` ways of writing, we offer you the choice of `Vim` and `Emacs` editting modes. Equipped with *markdown* syntax for text formatting, you will never need to touch your mouse while writing.
|
||||
* We love managing knowledge and thoughts as much as sharing them, so everybody has their own note account (`Evernote`, `Onenote`, `Google doc`, `Wiz note` etc.) and social media account (`Facebook`, `Wordpress`, blogs, etc.). But why can’t those two be one? Leanote makes this first step to bridge the private note-taking and public knowledge sharing seamlessly.
|
||||
* A complete and all-platform (sorry Windows phone) covering software suite: that includes 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 they are all open source!
|
||||
* ......
|
||||
|
||||
## 3. How to install leanote
|
||||
## 3. How to get Leanote
|
||||
|
||||
### 3.1. Download leanote
|
||||
The Leanote software suite 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).
|
||||
|
||||
Leanote V1.0-beta has been released. Binaries:
|
||||
Interested in our product and want to try it out from your web browser? Welcome to sign up on https://leanote.com.
|
||||
|
||||
* Linux: [leanote-linux-x86_64.v1.0-beta.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-linux-x86_64.v1.0-beta.bin.tar.gz)
|
||||
* MacOS X: [leanote-mac-x86_64.v1.0-beta.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-mac-x86_64.v1.0-beta.bin.tar.gz)
|
||||
Feeling suspicious about how those note-taking companies treat your personal data? You can install Leanote on your server, and use Leanote App (Desktop, iOS, Android) to sync notes with your self-hosted server.
|
||||
|
||||
### 3.2. Install MongoDB
|
||||
More information about how to install Leanote please see:
|
||||
|
||||
Leanote is written in go using [revel](https://revel.github.io/) and [MongoDB](https://www.mongodb.org). Thus, you need to first install MongoDB.
|
||||
* Leanote binary installation tutorial:
|
||||
* [Windows](https://github.com/leanote/leanote/wiki/leanote-source-installation-on-Windows-(En))
|
||||
* [Mac and Linux](https://github.com/leanote/leanote/wiki/leanote-binary-installation-on-Mac-and-Linux-(En))
|
||||
* Leanote source installation tutorial:
|
||||
* [Windows](https://github.com/leanote/leanote/wiki/leanote-source-installation-on-Windows-(En))
|
||||
* [Mac and Linux](https://github.com/leanote/leanote/wiki/Leanote-source-installation-on-Mac-and-Linux-(En))
|
||||
|
||||
For more tips please have a look at [our wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
|
||||
## 4. Documentation
|
||||
|
||||
### 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
|
||||
|
||||
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)
|
||||
Please see [wiki](https://github.com/leanote/leanote/wiki) for detailed instruction on how to install Leanote on various platforms, trouble shooting and configuration explanations.
|
||||
|
||||
|
||||
## 5. Contributors
|
||||
Thank you to all the [contributors](https://github.com/leanote/leanote/graphs/contributors) on
|
||||
this project. Your help is much appreciated.
|
||||
## 5. How to develop Leanote
|
||||
|
||||
## 6. Contributing
|
||||
If you are a developer yourself and feel like to build on top of Leanote, please refer to [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).
|
||||
|
||||
Please fork this repository and contribute back using [pull requests](https://github.com/leanote/leanote/pulls).
|
||||
|
||||
## Discussion
|
||||
* [leanote bbs](http://bbs.leanote.com)
|
||||
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)
|
||||
* QQ Group: 158716820
|
||||
## 6. Contributions
|
||||
|
||||
Like or dislike Leanote, please leave your comments and suggestions to help us improve it.
|
||||
If you encounter any issue, we suggest you first do a search in the issues section to see whether a solution already exists, or open up a new one otherwise.
|
||||
|
||||
We’d like to acknowledge the contributions made by our [developers and contributors](https://github.com/leanote/leanote/graphs/contributors) to
|
||||
this project. Leanote won’t exist without your hard work. Your help is much appreciated.
|
||||
|
||||
## 7. Join us
|
||||
|
||||
Please feel free to fork this repository and contribute back using [pull requests](https://github.com/leanote/leanote/pulls).
|
||||
|
||||
If you find any problems or have any good ideas, feature requests, please submit here [issues](https://github.com/leanote/leanote/issues).
|
||||
|
||||
|
||||
## 8. Donation
|
||||
|
||||
If you like our product, consider supporting us via [donate us](http://leanote.org/#donate).
|
||||
We acknowledge the donations make by all the [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
|
||||
|
||||
You are welcome to join us.
|
||||
|
||||
## 10. Contacts
|
||||
|
||||
* Email: leanote@leanote.com
|
||||
* [Leanote BBS](http://bbs.leanote.com)
|
||||
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
|
||||
* QQ Groups: 326073529, 256076853, 158716820
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
# Leanote
|
||||
|
||||
## 1. 介绍
|
||||
|
||||
Leanote, 不只是笔记!
|
||||
|
||||
**特性**
|
||||
|
||||
* 知识管理: 通过leanote来管理知识, leanote有易操作的界面, 包含两款编辑器tinymce和markdown. 在leanote, 你可以尽情享受写作.
|
||||
* 分享: 你也可以通过分享知识给好友, 让好友拥有你的知识.
|
||||
* 协作: 在分享的同时也可以与好友一起协作知识.
|
||||
* 博客: leanote也可以作为你的博客, 将知识公开成博客, 让leanote把你的知识传播的更远!
|
||||
* 高效笔记:Leanote 有易操作的界面, 包含一款富文本编辑器和Markdown编辑器,让您的笔记记录更轻松和高效。对高阶用户,我们还提供`Vim` 和`Emacs` 编辑模式,助推你的写作速度更上层楼。
|
||||
* 知识管理: Leanote 灵活而强大的“笔记本-笔记-标签”系统,让它成为你个人知识管理的利器。
|
||||
* 分享: 你可以通过Leanote同好友分享知识、想法和经历, 邀请好友加入你的笔记簿,通过云端交流信息。
|
||||
* 协作: Leanote协助你与同事之间相互协作,激荡新思路,随时随地头脑风暴。
|
||||
* 博客: Leanote也可以作为你的个人博客, 把你的知识传播的更远!
|
||||
|
||||
## 2. 为什么我们要创建leanote?
|
||||
说实话, 我们曾是evernote的忠实粉丝, 但是我们也发现evernote的不足:
|
||||
* evernote的编辑器不能满足我们的需求, 不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放.
|
||||
* 我们是markdown的爱好者, 可是evernote竟然没有.
|
||||
* 我们也想将知识公开, 所以我们有自己的博客, 如wordpress, 但为什么这两者不能合二为一呢?
|
||||
**其它特性**
|
||||
|
||||
* 支持Markdown编辑
|
||||
* 写作模式
|
||||
* `Vim` 及 `Emacs` 编辑模式
|
||||
* 支持PDF导出
|
||||
* 支持批量操作
|
||||
* 博客自定义主题, 实现高度定制化
|
||||
|
||||
## 2. 为什么我们要创建Leanote?
|
||||
我们都曾是`Evernote`的忠实粉丝, 一直以来`Evernote`都是我们日常知识管理的有效工具。于是我们决定重新创造一款工具,提供`Evernote`所能提供的功能,同时弥补`Evernote`的不足,比如:
|
||||
* 功能更强的文本编辑器:`Evernote`的编辑器不能满足我们的需求, 不能实现文档导航、不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放等。
|
||||
* `Evernote` 不支持所有人都喜爱的markdown语法,于是我们为Leanote配备了一款可以实时渲染的markdown编辑器。
|
||||
* 如果你是一名开发者,觉得手指怀念`Vim` 或 `Emacs` 了,那么我们还提供给你`Vim` 和 `Emacs` 写作模式,配合*markdown*的格式编辑,写作的时候再也不用去碰鼠标了。
|
||||
* 知识积累和知识分享同样重要,因此大家都有自己的笔记账号和社交账号。但为什么这两者不能合二为一呢? Leanote 做到了将二者无缝衔接。
|
||||
* 一套完整的、全平台覆盖的软件套装,包括了web、桌面、安卓、IOS设备,而且全部开源!
|
||||
* 还有...
|
||||
|
||||
## 3.安装leanote
|
||||
leanote是一款私有云笔记, 你可以下载它安装在自己的服务器上, 当然也可以在 http://leanote.com 上注册.
|
||||
## 3. 获取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)
|
||||
Leanote云笔记产品包括: Leanote Web & Server(即本仓库), 桌面客户端, IOS, android. 4端全部开源!
|
||||
|
||||
### 3.1. 下载leanote
|
||||
如果想试用我们的产品,欢迎在 https://leanote.com 上注册, Leanote团队为你提供稳定可靠的服务。
|
||||
担心服务厂商如何处理你的个人数据吗?你可以下载Leanote安装在自己的服务器上, 通过Leanote客户端连接与自建服务同步数据。
|
||||
|
||||
Leanote V1.0-beta 已发布, 二进制文件(暂时没有windows版的):
|
||||
这里详细整理了Leanote二进版和Leanote开发版的安装教程, 请移步至:
|
||||
|
||||
* Linux: [leanote-linux-x86_64.v1.0-beta.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-linux-x86_64.v1.0-beta.bin.tar.gz)
|
||||
* MacOS X: [leanote-mac-x86_64.v1.0-beta.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-mac-x86_64.v1.0-beta.bin.tar.gz)
|
||||
* Leanote二进制详细安装教程:
|
||||
* [Windows](https://github.com/leanote/leanote/wiki/Leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B---Windows)
|
||||
* [Mac, Linux](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源码详细安装教程:
|
||||
* [Windows](https://github.com/leanote/leanote/wiki/Leanote-%E6%BA%90%E7%A0%81%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B----Windows)
|
||||
* [Mac, Linux](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)
|
||||
|
||||
### 3.2. 安装 MongodbDB
|
||||
## 4. 相关文档
|
||||
|
||||
Leanote是由golang(使用[revel](https://revel.github.io/)框架 和 [MongoDB](https://www.mongodb.org)数据库), 你需要先安装Mongodb.
|
||||
更多详细的安装说明、问题处理和配置说明文档,请查看 [wiki](https://github.com/leanote/leanote/wiki)。
|
||||
|
||||
安装MongodbDB, 导入数据更多细节请查看: [wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
|
||||
## 5. 如何对Leanote进行二次开发
|
||||
|
||||
### 3.3. 导入初始数据
|
||||
如果您有兴趣基于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)。
|
||||
|
||||
MongodbDB初始数据在 `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
|
||||
## 6. 贡献者
|
||||
|
||||
```
|
||||
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
|
||||
```
|
||||
在此对向Leanote贡献力量的[贡献者们](https://github.com/leanote/leanote/graphs/contributors) 表示感谢。Leanote因有你们而更完美!
|
||||
|
||||
初始数据包含两个用户:
|
||||
## 7. 加入我们
|
||||
|
||||
```
|
||||
user2 username: admin, password: abc123 (管理员, 重要!)
|
||||
user3 username: demo@leanote.com, password: demo@leanote.com (为体验使用)
|
||||
```
|
||||
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到Leanote。
|
||||
|
||||
### 3.4. 配置
|
||||
有任何问题或建议, 请先搜索[issue](https://github.com/leanote/leanote/issues)区是否已经有解决方法。如果没有,欢迎提交新issue。
|
||||
|
||||
修改 `[PATH_TO_LEANOTE]/conf/app.conf`. 有以下选项:
|
||||
Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善Leanote。
|
||||
|
||||
``mongodb`` **必须配置!**
|
||||
## 8. 捐赠
|
||||
|
||||
```Shell
|
||||
db.host=localhost
|
||||
db.port=27017
|
||||
db.dbname=leanote
|
||||
db.username=
|
||||
db.password=
|
||||
```
|
||||
如果您喜欢我们的产品,请考虑支持我们, [捐赠Leanote](http://leanote.org/#donate)。
|
||||
|
||||
``app.secret`` **重要**
|
||||
请随意修改一个, app的密钥, 不能使用默认的, 不然会有安全问题
|
||||
感谢[这些捐赠者](http://leanote.leanote.com/post/leanote-donation-list), 谢谢你们的鼓励, Leanote会一直坚持!
|
||||
|
||||
更多配置请查看 `app/app.conf` 和 [revel 手册](https://revel.github.io/)
|
||||
## 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), 开发阶段
|
||||
|
||||
### 3.5. 运行leanote
|
||||
欢迎加入我们!
|
||||
|
||||
```
|
||||
$> cd PATH_TO_LEANOTE/bin
|
||||
$> sudo sh run.sh
|
||||
```
|
||||
## 联系&加入我们
|
||||
* Email: leanote@leanote.com
|
||||
* [Leanote 社区](http://bbs.leanote.com)
|
||||
* [QQ群](http://leanote.leanote.com/post/Leanote-groups)
|
||||
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
|
||||
|
||||
## 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. 贡献者
|
||||
多谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, leanote因有你们而更完美!
|
||||
|
||||
## 6. 加入我们
|
||||
|
||||
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到leanote.
|
||||
|
||||
leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善leanote.
|
||||
|
||||
## 讨论
|
||||
* [leanote 社区](http://bbs.leanote.com)
|
||||
* QQ群: 158716820
|
||||
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)
|
||||
|
||||
19
app/cmd/main.go
Normal file
19
app/cmd/main.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/revel/cmd/harness"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "encoding/json"
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
"io"
|
||||
"fmt"
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
)
|
||||
|
||||
// 附件
|
||||
@@ -31,44 +31,46 @@ func (c Attach) uploadAttach(noteId string) (re info.Re) {
|
||||
var resultMsg = "error" // 错误信息
|
||||
var Ok = false
|
||||
var fileInfo info.Attach
|
||||
|
||||
|
||||
re = info.NewRe()
|
||||
|
||||
|
||||
defer func() {
|
||||
re.Id = fileId // 只是id, 没有其它信息
|
||||
re.Msg = resultMsg
|
||||
re.Ok = Ok
|
||||
re.Item = fileInfo
|
||||
}()
|
||||
|
||||
|
||||
// 判断是否有权限为笔记添加附件
|
||||
if !shareService.HasUpdateNotePerm(noteId, c.GetUserId()) {
|
||||
return re
|
||||
}
|
||||
|
||||
|
||||
file, handel, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
return re
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return re
|
||||
}
|
||||
// > 5M?
|
||||
maxFileSize := configService.GetUploadSize("uploadAttachSize");
|
||||
maxFileSize := configService.GetUploadSize("uploadAttachSize")
|
||||
if maxFileSize <= 0 {
|
||||
maxFileSize = 1000
|
||||
}
|
||||
if(float64(len(data)) > maxFileSize * float64(1024*1024)) {
|
||||
resultMsg = fmt.Sprintf("附件大于%vM", maxFileSize)
|
||||
if float64(len(data)) > maxFileSize*float64(1024*1024) {
|
||||
resultMsg = fmt.Sprintf("The file's size is bigger than %vM", maxFileSize)
|
||||
return re
|
||||
}
|
||||
|
||||
|
||||
// 生成上传路径
|
||||
filePath := "files/" + c.GetUserId() + "/attachs"
|
||||
dir := revel.BasePath + "/" + filePath
|
||||
// filePath := "files/" + c.GetUserId() + "/attachs"
|
||||
newGuid := NewGuid()
|
||||
filePath := "files/" + GetRandomFilePath(c.GetUserId(), newGuid) + "/attachs"
|
||||
dir := revel.BasePath + "/" + filePath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return re
|
||||
@@ -76,13 +78,13 @@ func (c Attach) uploadAttach(noteId string) (re info.Re) {
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
_, ext := SplitFilename(filename) // .doc
|
||||
filename = NewGuid() + ext
|
||||
toPath := dir + "/" + filename;
|
||||
filename = newGuid + ext
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
return re
|
||||
}
|
||||
|
||||
|
||||
// add File to db
|
||||
fileType := ""
|
||||
if ext != "" {
|
||||
@@ -90,21 +92,25 @@ func (c Attach) uploadAttach(noteId string) (re info.Re) {
|
||||
}
|
||||
filesize := GetFilesize(toPath)
|
||||
fileInfo = info.Attach{Name: filename,
|
||||
Title: handel.Filename,
|
||||
NoteId: bson.ObjectIdHex(noteId),
|
||||
Title: handel.Filename,
|
||||
NoteId: bson.ObjectIdHex(noteId),
|
||||
UploadUserId: c.GetObjectUserId(),
|
||||
Path: filePath + "/" + filename,
|
||||
Type: fileType,
|
||||
Size: filesize}
|
||||
|
||||
id := bson.NewObjectId();
|
||||
Path: filePath + "/" + filename,
|
||||
Type: fileType,
|
||||
Size: filesize}
|
||||
|
||||
id := bson.NewObjectId()
|
||||
fileInfo.AttachId = id
|
||||
fileId = id.Hex()
|
||||
Ok = attachService.AddAttach(fileInfo)
|
||||
|
||||
fileInfo.Path = ""; // 不要返回
|
||||
resultMsg = "success"
|
||||
|
||||
Ok, resultMsg = attachService.AddAttach(fileInfo, false)
|
||||
if resultMsg != "" {
|
||||
resultMsg = c.Message(resultMsg)
|
||||
}
|
||||
|
||||
fileInfo.Path = "" // 不要返回
|
||||
if Ok {
|
||||
resultMsg = "success"
|
||||
}
|
||||
return re
|
||||
}
|
||||
|
||||
@@ -126,15 +132,15 @@ func (c Attach) GetAttachs(noteId string) revel.Result {
|
||||
// 下载附件
|
||||
// 权限判断
|
||||
func (c Attach) Download(attachId string) revel.Result {
|
||||
attach := attachService.GetAttach(attachId, c.GetUserId()); // 得到路径
|
||||
attach := attachService.GetAttach(attachId, c.GetUserId()) // 得到路径
|
||||
path := attach.Path
|
||||
if path == "" {
|
||||
return c.RenderText("")
|
||||
}
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
file, _ := os.Open(fn)
|
||||
return c.RenderBinary(file, attach.Title, revel.Attachment, time.Now()) // revel.Attachment
|
||||
// return c.RenderFile(file, revel.Attachment) // revel.Attachment
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
file, _ := os.Open(fn)
|
||||
return c.RenderBinary(file, attach.Title, revel.Attachment, time.Now()) // revel.Attachment
|
||||
// return c.RenderFile(file, revel.Attachment) // revel.Attachment
|
||||
}
|
||||
|
||||
func (c Attach) DownloadAll(noteId string) revel.Result {
|
||||
@@ -147,69 +153,75 @@ func (c Attach) DownloadAll(noteId string) revel.Result {
|
||||
if attachs == nil || len(attachs) == 0 {
|
||||
return c.RenderText("")
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
dir := revel.BasePath + "/files/tmp"
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
dir := revel.BasePath + "/files/tmp"
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
filename := note.Title + ".tar.gz"
|
||||
if note.Title == "" {
|
||||
filename = "all.tar.gz"
|
||||
}
|
||||
|
||||
|
||||
dir := revel.BasePath + "/files/attach_all"
|
||||
|
||||
if !MkdirAll(dir) {
|
||||
return c.RenderText("error")
|
||||
}
|
||||
|
||||
// file write
|
||||
fw, err := os.Create(revel.BasePath + "/files/" + filename)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
|
||||
|
||||
// gzip write
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
|
||||
// tar write
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
// 遍历文件列表
|
||||
for _, attach := range attachs {
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(attach.Path, "/")
|
||||
fr, err := os.Open(fn)
|
||||
fileInfo, _ := fr.Stat()
|
||||
if err != nil {
|
||||
fw, err := os.Create(dir + "/" + filename)
|
||||
if err != nil {
|
||||
return c.RenderText("error")
|
||||
}
|
||||
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
|
||||
|
||||
// gzip write
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
|
||||
// tar write
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
// 遍历文件列表
|
||||
for _, attach := range attachs {
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(attach.Path, "/")
|
||||
fr, err := os.Open(fn)
|
||||
fileInfo, _ := fr.Stat()
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// 信息头
|
||||
h := new(tar.Header)
|
||||
h.Name = attach.Title
|
||||
h.Size = fileInfo.Size()
|
||||
h.Mode = int64(fileInfo.Mode())
|
||||
h.ModTime = fileInfo.ModTime()
|
||||
|
||||
// 写信息头
|
||||
err = tw.WriteHeader(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 写文件
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} // for
|
||||
|
||||
// tw.Close()
|
||||
// gw.Close()
|
||||
// fw.Close()
|
||||
// file, _ := os.Open(dir + "/" + filename)
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// 信息头
|
||||
h := new(tar.Header)
|
||||
h.Name = attach.Title
|
||||
h.Size = fileInfo.Size()
|
||||
h.Mode = int64(fileInfo.Mode())
|
||||
h.ModTime = fileInfo.ModTime()
|
||||
|
||||
// 写信息头
|
||||
err = tw.WriteHeader(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 写文件
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} // for
|
||||
|
||||
// tw.Close()
|
||||
// gw.Close()
|
||||
// fw.Close()
|
||||
// file, _ := os.Open(dir + "/" + filename)
|
||||
// fw.Seek(0, 0)
|
||||
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "strconv"
|
||||
"github.com/revel/revel"
|
||||
"strings"
|
||||
// "strconv"
|
||||
)
|
||||
|
||||
// 用户登录/注销/找回密码
|
||||
@@ -21,14 +22,14 @@ func (c Auth) Login(email, from string) revel.Result {
|
||||
c.RenderArgs["email"] = email
|
||||
c.RenderArgs["from"] = from
|
||||
c.RenderArgs["openRegister"] = configService.IsOpenRegister()
|
||||
|
||||
|
||||
sessionId := c.Session.Id()
|
||||
if sessionService.LoginTimesIsOver(sessionId) {
|
||||
c.RenderArgs["needCaptcha"] = true
|
||||
}
|
||||
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
|
||||
if c.Has("demo") {
|
||||
c.RenderArgs["demo"] = true
|
||||
c.RenderArgs["email"] = "demo@leanote.com"
|
||||
@@ -40,41 +41,42 @@ 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)})
|
||||
|
||||
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId), Msg: c.Message(msg)})
|
||||
}
|
||||
func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result {
|
||||
sessionId := c.Session.Id()
|
||||
var msg = ""
|
||||
|
||||
|
||||
// > 5次需要验证码, 直到登录成功
|
||||
if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha {
|
||||
msg = "captchaError"
|
||||
} else {
|
||||
userInfo := authService.Login(email, pwd)
|
||||
if userInfo.Email != "" {
|
||||
c.SetSession(userInfo)
|
||||
sessionService.ClearLoginTimes(sessionId)
|
||||
return c.RenderJson(info.Re{Ok: true})
|
||||
} else {
|
||||
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)})
|
||||
|
||||
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId), Msg: c.Message(msg)})
|
||||
}
|
||||
|
||||
// 注销
|
||||
func (c Auth) Logout() revel.Result {
|
||||
sessionId := c.Session.Id()
|
||||
@@ -85,51 +87,64 @@ 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
|
||||
}
|
||||
|
||||
//--------
|
||||
// 注册
|
||||
func (c Auth) Register(from string) revel.Result {
|
||||
func (c Auth) Register(from, iu string) revel.Result {
|
||||
if !configService.IsOpenRegister() {
|
||||
return c.Redirect("/index")
|
||||
}
|
||||
c.SetLocale()
|
||||
c.RenderArgs["from"] = from
|
||||
|
||||
c.RenderArgs["iu"] = iu
|
||||
|
||||
c.RenderArgs["title"] = c.Message("register")
|
||||
c.RenderArgs["subTitle"] = c.Message("register")
|
||||
return c.RenderTemplate("home/register.html")
|
||||
}
|
||||
func (c Auth) DoRegister(email, pwd string) revel.Result {
|
||||
func (c Auth) DoRegister(email, pwd, iu string) revel.Result {
|
||||
if !configService.IsOpenRegister() {
|
||||
return c.Redirect("/index")
|
||||
}
|
||||
|
||||
re := info.NewRe();
|
||||
|
||||
|
||||
re := info.NewRe()
|
||||
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
|
||||
email = strings.ToLower(email)
|
||||
|
||||
// 注册
|
||||
re.Ok, re.Msg = authService.Register(email, pwd)
|
||||
|
||||
re.Ok, re.Msg = authService.Register(email, pwd, iu)
|
||||
|
||||
// 注册成功, 则立即登录之
|
||||
if re.Ok {
|
||||
c.doLogin(email, pwd)
|
||||
}
|
||||
|
||||
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
//--------
|
||||
// 找回密码
|
||||
func (c Auth) FindPassword() revel.Result {
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("findPassword")
|
||||
c.RenderArgs["subTitle"] = c.Message("findPassword")
|
||||
return c.RenderTemplate("home/find_password.html")
|
||||
@@ -140,30 +155,33 @@ func (c Auth) DoFindPassword(email string) revel.Result {
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 点击链接后, 先验证之
|
||||
func (c Auth) FindPassword2(token string) revel.Result {
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = c.Message("findPassword")
|
||||
c.RenderArgs["subTitle"] = c.Message("findPassword")
|
||||
if token == "" {
|
||||
return c.RenderTemplate("find_password2_timeout.html")
|
||||
}
|
||||
ok, _, findPwd := tokenService.VerifyToken(token, info.TokenPwd);
|
||||
ok, _, findPwd := tokenService.VerifyToken(token, info.TokenPwd)
|
||||
if !ok {
|
||||
return c.RenderTemplate("home/find_password2_timeout.html")
|
||||
}
|
||||
c.RenderArgs["findPwd"] = findPwd
|
||||
|
||||
|
||||
c.RenderArgs["title"] = c.Message("updatePassword")
|
||||
c.RenderArgs["subTitle"] = c.Message("updatePassword")
|
||||
|
||||
|
||||
return c.RenderTemplate("home/find_password2.html")
|
||||
}
|
||||
|
||||
// 找回密码修改密码
|
||||
func (c Auth) FindPasswordUpdate(token, pwd string) revel.Result {
|
||||
re := info.NewRe();
|
||||
re := info.NewRe()
|
||||
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
// 修改之
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
"github.com/leanote/leanote/app/lea/i18n"
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
"bytes"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// 公用Controller, 其它Controller继承它
|
||||
@@ -19,6 +20,11 @@ type BaseController struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// 覆盖revel.Message
|
||||
func (c *BaseController) Message(message string, args ...interface{}) (value string) {
|
||||
return i18n.Message(c.Request.Locale, message, args...)
|
||||
}
|
||||
|
||||
func (c BaseController) GetUserId() string {
|
||||
if userId, ok := c.Session["UserId"]; ok {
|
||||
return userId
|
||||
@@ -56,34 +62,41 @@ func (c BaseController) GetUsername() string {
|
||||
// 得到用户信息
|
||||
func (c BaseController) GetUserInfo() info.User {
|
||||
if userId, ok := c.Session["UserId"]; ok && userId != "" {
|
||||
return userService.GetUserInfo(userId);
|
||||
return userService.GetUserInfo(userId)
|
||||
/*
|
||||
notebookWidth, _ := strconv.Atoi(c.Session["NotebookWidth"])
|
||||
noteListWidth, _ := strconv.Atoi(c.Session["NoteListWidth"])
|
||||
mdEditorWidth, _ := strconv.Atoi(c.Session["MdEditorWidth"])
|
||||
LogJ(c.Session)
|
||||
user := info.User{UserId: bson.ObjectIdHex(userId),
|
||||
Email: c.Session["Email"],
|
||||
Logo: c.Session["Logo"],
|
||||
Username: c.Session["Username"],
|
||||
UsernameRaw: c.Session["UsernameRaw"],
|
||||
Theme: c.Session["Theme"],
|
||||
NotebookWidth: notebookWidth,
|
||||
NoteListWidth: noteListWidth,
|
||||
MdEditorWidth: mdEditorWidth,
|
||||
notebookWidth, _ := strconv.Atoi(c.Session["NotebookWidth"])
|
||||
noteListWidth, _ := strconv.Atoi(c.Session["NoteListWidth"])
|
||||
mdEditorWidth, _ := strconv.Atoi(c.Session["MdEditorWidth"])
|
||||
LogJ(c.Session)
|
||||
user := info.User{UserId: bson.ObjectIdHex(userId),
|
||||
Email: c.Session["Email"],
|
||||
Logo: c.Session["Logo"],
|
||||
Username: c.Session["Username"],
|
||||
UsernameRaw: c.Session["UsernameRaw"],
|
||||
Theme: c.Session["Theme"],
|
||||
NotebookWidth: notebookWidth,
|
||||
NoteListWidth: noteListWidth,
|
||||
MdEditorWidth: mdEditorWidth,
|
||||
}
|
||||
if c.Session["Verified"] == "1" {
|
||||
user.Verified = true
|
||||
}
|
||||
if c.Session["Verified"] == "1" {
|
||||
user.Verified = true
|
||||
}
|
||||
if c.Session["LeftIsMin"] == "1" {
|
||||
user.LeftIsMin = true
|
||||
}
|
||||
return user
|
||||
if c.Session["LeftIsMin"] == "1" {
|
||||
user.LeftIsMin = true
|
||||
}
|
||||
return 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]
|
||||
@@ -100,16 +113,16 @@ func (c BaseController) SetSession(userInfo info.User) {
|
||||
c.Session["UsernameRaw"] = userInfo.UsernameRaw
|
||||
c.Session["Theme"] = userInfo.Theme
|
||||
c.Session["Logo"] = userInfo.Logo
|
||||
|
||||
|
||||
c.Session["NotebookWidth"] = strconv.Itoa(userInfo.NotebookWidth)
|
||||
c.Session["NoteListWidth"] = strconv.Itoa(userInfo.NoteListWidth)
|
||||
|
||||
|
||||
if userInfo.Verified {
|
||||
c.Session["Verified"] = "1"
|
||||
} else {
|
||||
c.Session["Verified"] = "0"
|
||||
}
|
||||
|
||||
|
||||
if userInfo.LeftIsMin {
|
||||
c.Session["LeftIsMin"] = "1"
|
||||
} else {
|
||||
@@ -132,8 +145,8 @@ func (c BaseController) UpdateSession(key, value string) {
|
||||
|
||||
// 返回json
|
||||
func (c BaseController) Json(i interface{}) string {
|
||||
// b, _ := json.MarshalIndent(i, "", " ")
|
||||
b, _ := json.Marshal(i)
|
||||
// b, _ := json.MarshalIndent(i, "", " ")
|
||||
b, _ := json.Marshal(i)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
@@ -150,9 +163,9 @@ func (c BaseController) GetPage() int {
|
||||
// 判断是否含有某参数
|
||||
func (c BaseController) Has(key string) bool {
|
||||
if _, ok := c.Params.Values[key]; ok {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -162,43 +175,48 @@ func (c Blog) GetPage(page, count int, list interface{}) info.Page {
|
||||
*/
|
||||
|
||||
func (c BaseController) GetTotalPage(page, count int) int {
|
||||
return int(math.Ceil(float64(count)/float64(page)))
|
||||
return int(math.Ceil(float64(count) / float64(page)))
|
||||
}
|
||||
|
||||
//-------------
|
||||
func (c BaseController) E404() revel.Result {
|
||||
c.RenderArgs["title"] = "404";
|
||||
c.RenderArgs["title"] = "404"
|
||||
return c.NotFound("", nil)
|
||||
}
|
||||
|
||||
// 设置本地
|
||||
func (c BaseController) SetLocale() string {
|
||||
locale := string(c.Request.Locale) // zh-CN
|
||||
// lang := locale
|
||||
// if strings.Contains(locale, "-") {
|
||||
// pos := strings.Index(locale, "-")
|
||||
// lang = locale[0:pos]
|
||||
// }
|
||||
// if lang != "zh" && lang != "en" {
|
||||
// lang = "en"
|
||||
// }
|
||||
lang := locale
|
||||
if strings.Contains(locale, "-") {
|
||||
pos := strings.Index(locale, "-")
|
||||
lang = locale[0:pos]
|
||||
if !i18n.HasLang(locale) {
|
||||
lang = i18n.GetDefaultLang()
|
||||
}
|
||||
if lang != "zh" && lang != "en" {
|
||||
lang = "en";
|
||||
}
|
||||
c.RenderArgs["locale"] = lang;
|
||||
c.RenderArgs["siteUrl"] = configService.GetSiteUrl();
|
||||
|
||||
c.RenderArgs["locale"] = lang
|
||||
c.RenderArgs["siteUrl"] = configService.GetSiteUrl()
|
||||
|
||||
c.RenderArgs["blogUrl"] = configService.GetBlogUrl()
|
||||
c.RenderArgs["leaUrl"] = configService.GetLeaUrl()
|
||||
c.RenderArgs["noteUrl"] = configService.GetNoteUrl()
|
||||
|
||||
|
||||
return lang
|
||||
}
|
||||
|
||||
// 设置userInfo
|
||||
func (c BaseController) SetUserInfo() {
|
||||
func (c BaseController) SetUserInfo() info.User {
|
||||
userInfo := c.GetUserInfo()
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
if(userInfo.Username == configService.GetAdminUsername()) {
|
||||
if userInfo.Username == configService.GetAdminUsername() {
|
||||
c.RenderArgs["isAdmin"] = true
|
||||
}
|
||||
return userInfo
|
||||
}
|
||||
|
||||
// life
|
||||
@@ -214,18 +232,19 @@ func (c BaseController) RenderTemplateStr(templatePath string) string {
|
||||
Template: template,
|
||||
RenderArgs: c.RenderArgs, // 把args给它
|
||||
}
|
||||
|
||||
|
||||
var buffer bytes.Buffer
|
||||
tpl.Template.Render(&buffer, c.RenderArgs)
|
||||
return buffer.String();
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// json, result
|
||||
// 为了msg
|
||||
// msg-v1-v2-v3
|
||||
func (c BaseController) RenderRe(re info.Re) revel.Result {
|
||||
oldMsg := re.Msg
|
||||
if re.Msg != "" {
|
||||
if(strings.Contains(re.Msg, "-")) {
|
||||
if strings.Contains(re.Msg, "-") {
|
||||
msgAndValues := strings.Split(re.Msg, "-")
|
||||
if len(msgAndValues) == 2 {
|
||||
re.Msg = c.Message(msgAndValues[0], msgAndValues[1])
|
||||
@@ -241,5 +260,8 @@ func (c BaseController) RenderRe(re info.Re) revel.Result {
|
||||
re.Msg = c.Message(re.Msg)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(re.Msg, "???") {
|
||||
re.Msg = oldMsg
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,9 @@ func (c Blog) render(templateName string, themePath string) revel.Result {
|
||||
isPreview = true
|
||||
themePath = themePath2.(string)
|
||||
c.setPreviewUrl()
|
||||
|
||||
// 因为common的themeInfo是从UserBlog.ThemeId来取的, 所以这里要fugai下
|
||||
c.RenderArgs["themeInfo"] = c.RenderArgs["themeInfoPreview"]
|
||||
}
|
||||
return blog.RenderTemplate(templateName, c.RenderArgs, revel.BasePath+"/"+themePath, isPreview)
|
||||
}
|
||||
@@ -102,19 +105,23 @@ func (c Blog) setPreviewUrl() {
|
||||
var indexUrl, postUrl, searchUrl, cateUrl, singleUrl, tagsUrl, archiveUrl string
|
||||
|
||||
userId := c.GetUserId()
|
||||
userIdOrEmail := userId
|
||||
username := c.GetUsername()
|
||||
if username != "" {
|
||||
userIdOrEmail = username
|
||||
}
|
||||
themeId := c.Session["themeId"]
|
||||
theme := themeService.GetTheme(userId, themeId)
|
||||
|
||||
siteUrl := configService.GetSiteUrl()
|
||||
blogUrl := siteUrl + "/preview" // blog.leanote.com
|
||||
userIdOrEmail := userId
|
||||
// siteUrl := configService.GetSiteUrl()
|
||||
blogUrl := "/preview" // blog.leanote.com
|
||||
|
||||
indexUrl = blogUrl + "/" + userIdOrEmail
|
||||
cateUrl = blogUrl + "/cate" // /notebookId
|
||||
cateUrl = blogUrl + "/cate/" + userIdOrEmail // /notebookId
|
||||
|
||||
postUrl = blogUrl + "/post" // /xxxxx
|
||||
postUrl = blogUrl + "/post/" + userIdOrEmail // /xxxxx
|
||||
searchUrl = blogUrl + "/search/" + userIdOrEmail // blog.leanote.com/search/userId
|
||||
singleUrl = blogUrl + "/single" // blog.leanote.com/single/singleId
|
||||
singleUrl = blogUrl + "/single/" + userIdOrEmail // blog.leanote.com/single/singleId
|
||||
archiveUrl = blogUrl + "/archives/" + userIdOrEmail // blog.leanote.com/archive/userId
|
||||
tagsUrl = blogUrl + "/tags/" + userIdOrEmail // blog.leanote.com/archive/userId
|
||||
|
||||
@@ -136,8 +143,8 @@ func (c Blog) setPreviewUrl() {
|
||||
// 各种地址设置
|
||||
func (c Blog) setUrl(userBlog info.UserBlog, userInfo info.User) {
|
||||
// 主页 http://leanote.com/blog/life or http://blog.leanote.com/life or http:// xxxx.leanote.com or aa.com
|
||||
host := c.Request.Request.Host
|
||||
var staticUrl = configService.GetUserUrl(strings.Split(host, ":")[0])
|
||||
// host := c.Request.Request.Host
|
||||
// var staticUrl = configService.GetUserUrl(strings.Split(host, ":")[0])
|
||||
// staticUrl == host, 为保证同源!!! 只有host, http://leanote.com, http://blog/leanote.com
|
||||
// life.leanote.com, lealife.com
|
||||
siteUrl := configService.GetSiteUrl()
|
||||
@@ -161,20 +168,20 @@ func (c Blog) setUrl(userBlog info.UserBlog, userInfo info.User) {
|
||||
c.RenderArgs["themeBaseUrl"] = "/" + userBlog.ThemePath
|
||||
|
||||
// 其它static js
|
||||
c.RenderArgs["jQueryUrl"] = siteUrl + "/js/jquery-1.9.0.min.js"
|
||||
c.RenderArgs["jQueryUrl"] = "/js/jquery-1.9.0.min.js"
|
||||
|
||||
c.RenderArgs["prettifyJsUrl"] = siteUrl + "/js/google-code-prettify/prettify.js"
|
||||
c.RenderArgs["prettifyCssUrl"] = siteUrl + "/js/google-code-prettify/prettify.css"
|
||||
c.RenderArgs["prettifyJsUrl"] = "/js/google-code-prettify/prettify.js"
|
||||
c.RenderArgs["prettifyCssUrl"] = "/js/google-code-prettify/prettify.css"
|
||||
|
||||
c.RenderArgs["blogCommonJsUrl"] = siteUrl + "/public/blog/js/common.js"
|
||||
c.RenderArgs["blogCommonJsUrl"] = "/public/blog/js/common.js"
|
||||
|
||||
c.RenderArgs["shareCommentCssUrl"] = siteUrl + "/public/blog/css/share_comment.css"
|
||||
c.RenderArgs["shareCommentJsUrl"] = siteUrl + "/public/blog/js/share_comment.js"
|
||||
c.RenderArgs["shareCommentCssUrl"] = "/public/blog/css/share_comment.css"
|
||||
c.RenderArgs["shareCommentJsUrl"] = "/public/blog/js/share_comment.js"
|
||||
|
||||
c.RenderArgs["fontAwesomeUrl"] = staticUrl + "/css/font-awesome-4.2.0/css/font-awesome.css"
|
||||
c.RenderArgs["fontAwesomeUrl"] = "/css/font-awesome-4.2.0/css/font-awesome.css"
|
||||
|
||||
c.RenderArgs["bootstrapCssUrl"] = siteUrl + "/css/bootstrap.css"
|
||||
c.RenderArgs["bootstrapJsUrl"] = siteUrl + "/js/bootstrap-min.js"
|
||||
c.RenderArgs["bootstrapCssUrl"] = "/css/bootstrap.css"
|
||||
c.RenderArgs["bootstrapJsUrl"] = "/js/bootstrap-min.js"
|
||||
}
|
||||
|
||||
// 笔记本分类
|
||||
@@ -193,29 +200,73 @@ func (c Blog) getCates(userBlog info.UserBlog) {
|
||||
}
|
||||
|
||||
var i = 0
|
||||
cates := make([]map[string]string, len(notebooks))
|
||||
cates := make([]*info.Cate, len(notebooks))
|
||||
|
||||
// 先要保证已有的是正确的排序
|
||||
cateIds := userBlog.CateIds
|
||||
has := map[string]bool{} // cateIds中有的
|
||||
cateMap := map[string]*info.Cate{}
|
||||
if cateIds != nil && len(cateIds) > 0 {
|
||||
for _, cateId := range cateIds {
|
||||
if n, ok := notebooksMap[cateId]; ok {
|
||||
cates[i] = map[string]string{"Title": n.Title, "UrlTitle": c.getCateUrlTitle(&n), "CateId": n.NotebookId.Hex()}
|
||||
parentNotebookId := ""
|
||||
if n.ParentNotebookId != "" {
|
||||
parentNotebookId = n.ParentNotebookId.Hex()
|
||||
}
|
||||
cates[i] = &info.Cate{Title: n.Title, UrlTitle: c.getCateUrlTitle(&n), CateId: n.NotebookId.Hex(), ParentCateId: parentNotebookId}
|
||||
cateMap[cates[i].CateId] = cates[i]
|
||||
i++
|
||||
has[cateId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// 之后
|
||||
|
||||
// 之后添加没有排序的
|
||||
for _, n := range notebooks {
|
||||
id := n.NotebookId.Hex()
|
||||
if !has[id] {
|
||||
cates[i] = map[string]string{"Title": n.Title, "UrlTitle": c.getCateUrlTitle(&n), "CateId": id}
|
||||
parentNotebookId := ""
|
||||
if n.ParentNotebookId != "" {
|
||||
parentNotebookId = n.ParentNotebookId.Hex()
|
||||
}
|
||||
cates[i] = &info.Cate{Title: n.Title, UrlTitle: c.getCateUrlTitle(&n), CateId: id, ParentCateId: parentNotebookId}
|
||||
cateMap[cates[i].CateId] = cates[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// LogJ(">>")
|
||||
// LogJ(cates)
|
||||
|
||||
// 建立层级
|
||||
hasParent := map[string]bool{} // 有父的cate
|
||||
for _, cate := range cates {
|
||||
parentCateId := cate.ParentCateId
|
||||
if parentCateId != "" {
|
||||
if parentCate, ok := cateMap[parentCateId]; ok {
|
||||
// Log("________")
|
||||
// LogJ(parentCate)
|
||||
// LogJ(cate)
|
||||
if parentCate.Children == nil {
|
||||
parentCate.Children = []*info.Cate{cate}
|
||||
} else {
|
||||
parentCate.Children = append(parentCate.Children, cate)
|
||||
}
|
||||
hasParent[cate.CateId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 得到没有父的cate, 作为第一级cate
|
||||
catesTree := []*info.Cate{}
|
||||
for _, cate := range cates {
|
||||
if !hasParent[cate.CateId] {
|
||||
catesTree = append(catesTree, cate)
|
||||
}
|
||||
}
|
||||
|
||||
c.RenderArgs["cates"] = cates
|
||||
c.RenderArgs["catesTree"] = catesTree
|
||||
}
|
||||
|
||||
// 单页
|
||||
@@ -297,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
|
||||
}
|
||||
@@ -455,6 +507,7 @@ func (c Blog) Archives(userIdOrEmail string, cateId string, year, month int) (re
|
||||
// 进入某个用户的博客
|
||||
var blogPageSize = 5
|
||||
var searchBlogPageSize = 30
|
||||
|
||||
// 分类 /cate/xxxxxxxx?notebookId=1212
|
||||
func (c Blog) Cate(userIdOrEmail string, notebookId string) (re revel.Result) {
|
||||
// 自定义域名
|
||||
@@ -577,7 +630,8 @@ func (c Blog) Post(userIdOrEmail, noteId string) (re revel.Result) {
|
||||
return c.e404(userBlog.ThemePath) // 404 TODO 使用用户的404
|
||||
}
|
||||
|
||||
c.RenderArgs["post"] = blogService.FixBlog(blogInfo)
|
||||
post := blogService.FixBlog(blogInfo)
|
||||
c.RenderArgs["post"] = post
|
||||
// c.RenderArgs["userInfo"] = userInfo
|
||||
c.RenderArgs["curIsPost"] = true
|
||||
|
||||
@@ -593,7 +647,7 @@ func (c Blog) Post(userIdOrEmail, noteId string) (re revel.Result) {
|
||||
baseTime = blogInfo.Title
|
||||
}
|
||||
|
||||
prePost, nextPost := blogService.PreNextBlog(userId, userBlog.SortField, userBlog.IsAsc, baseTime)
|
||||
prePost, nextPost := blogService.PreNextBlog(userId, userBlog.SortField, userBlog.IsAsc, post.NoteId, baseTime)
|
||||
if prePost.NoteId != "" {
|
||||
c.RenderArgs["prePost"] = prePost
|
||||
}
|
||||
@@ -768,7 +822,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()
|
||||
@@ -781,6 +835,10 @@ func (c Blog) GetComments(noteId string) revel.Result {
|
||||
result["commentUserInfo"] = commentUserInfo
|
||||
re.Item = result
|
||||
|
||||
if callback != "" {
|
||||
return c.RenderJsonP(callback, result)
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/lea/captcha"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
// "strconv"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
// "strconv"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -22,22 +22,23 @@ type Captcha struct {
|
||||
}
|
||||
|
||||
type Ca string
|
||||
|
||||
func (r Ca) Apply(req *revel.Request, resp *revel.Response) {
|
||||
resp.WriteHeader(http.StatusOK, "image/png")
|
||||
resp.WriteHeader(http.StatusOK, "image/png")
|
||||
}
|
||||
|
||||
func (c Captcha) Get() revel.Result {
|
||||
c.Response.ContentType = "image/png"
|
||||
image, str := captcha.Fetch()
|
||||
image.WriteTo(c.Response.Out)
|
||||
|
||||
|
||||
sessionId := c.Session["_ID"]
|
||||
// LogJ(c.Session)
|
||||
// Log("------")
|
||||
// Log(str)
|
||||
// Log(sessionId)
|
||||
Log("..")
|
||||
// LogJ(c.Session)
|
||||
// Log("------")
|
||||
// Log(str)
|
||||
// Log(sessionId)
|
||||
Log("..")
|
||||
sessionService.SetCaptcha(sessionId, str)
|
||||
|
||||
|
||||
return c.Render()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "encoding/json"
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/lea/netutil"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"fmt"
|
||||
"strconv"
|
||||
// "strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -22,8 +22,8 @@ type File struct {
|
||||
// 上传的是博客logo
|
||||
// TODO logo不要设置权限, 另外的目录
|
||||
func (c File) UploadBlogLogo() revel.Result {
|
||||
re := c.uploadImage("blogLogo", "");
|
||||
|
||||
re := c.uploadImage("blogLogo", "")
|
||||
|
||||
c.RenderArgs["fileUrlPath"] = re.Id
|
||||
c.RenderArgs["resultCode"] = re.Code
|
||||
c.RenderArgs["resultMsg"] = re.Msg
|
||||
@@ -34,48 +34,50 @@ func (c File) UploadBlogLogo() revel.Result {
|
||||
// 拖拉上传, pasteImage
|
||||
// noteId 是为了判断是否是协作的note, 如果是则需要复制一份到note owner中
|
||||
func (c File) PasteImage(noteId string) revel.Result {
|
||||
re := c.uploadImage("pasteImage", "");
|
||||
|
||||
userId := c.GetUserId()
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.UserId != "" {
|
||||
noteUserId := note.UserId.Hex()
|
||||
if noteUserId != userId {
|
||||
// 是否是有权限协作的
|
||||
if shareService.HasUpdatePerm(noteUserId, userId, noteId) {
|
||||
// 复制图片之, 图片复制给noteUserId
|
||||
_, re.Id = fileService.CopyImage(userId, re.Id, noteUserId)
|
||||
} else {
|
||||
// 怎么可能在这个笔记下paste图片呢?
|
||||
// 正常情况下不会
|
||||
re := c.uploadImage("pasteImage", "")
|
||||
|
||||
if noteId != "" {
|
||||
userId := c.GetUserId()
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.UserId != "" {
|
||||
noteUserId := note.UserId.Hex()
|
||||
if noteUserId != userId {
|
||||
// 是否是有权限协作的
|
||||
if shareService.HasUpdatePerm(noteUserId, userId, noteId) {
|
||||
// 复制图片之, 图片复制给noteUserId
|
||||
_, re.Id = fileService.CopyImage(userId, re.Id, noteUserId)
|
||||
} else {
|
||||
// 怎么可能在这个笔记下paste图片呢?
|
||||
// 正常情况下不会
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 头像设置
|
||||
func (c File) UploadAvatar() revel.Result {
|
||||
re := c.uploadImage("logo", "");
|
||||
|
||||
re := c.uploadImage("logo", "")
|
||||
|
||||
c.RenderArgs["fileUrlPath"] = re.Id
|
||||
c.RenderArgs["resultCode"] = re.Code
|
||||
c.RenderArgs["resultMsg"] = re.Msg
|
||||
|
||||
|
||||
if re.Ok {
|
||||
re.Ok = userService.UpdateAvatar(c.GetUserId(), re.Id)
|
||||
if re.Ok {
|
||||
c.UpdateSession("Logo", re.Id);
|
||||
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 +86,82 @@ 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 resultMsg = "内部错误" // 错误信息
|
||||
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()
|
||||
|
||||
// 生成上传路径
|
||||
if(from == "logo" || from == "blogLogo") {
|
||||
fileUrlPath = "public/upload/" + c.GetUserId() + "/images/logo"
|
||||
newGuid := NewGuid()
|
||||
|
||||
userId := c.GetUserId()
|
||||
|
||||
if from == "logo" || from == "blogLogo" {
|
||||
fileUrlPath = "public/upload/" + Digest3(userId) + "/" + userId + "/images/logo"
|
||||
} else {
|
||||
fileUrlPath = "files/" + c.GetUserId() + "/images"
|
||||
// fileUrlPath = "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
|
||||
fileUrlPath = "files/" + GetRandomFilePath(userId, 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类型
|
||||
handel.Filename = c.Message("unTitled")
|
||||
ext = ".png" // TODO 可能不是png类型
|
||||
} else {
|
||||
_, ext = SplitFilename(filename)
|
||||
if(ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg") {
|
||||
resultMsg = "不是图片"
|
||||
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("图片大于%vM", maxFileSize)
|
||||
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)
|
||||
@@ -163,26 +173,28 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
|
||||
filesize := GetFilesize(toPathGif)
|
||||
fileUrlPath += "/" + filename
|
||||
resultCode = 1
|
||||
resultMsg = "上传成功!"
|
||||
|
||||
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 = fileService.AddImage(fileInfo, albumId, c.GetUserId())
|
||||
|
||||
fileInfo.Path = ""; // 不要返回
|
||||
|
||||
Ok, resultMsg = fileService.AddImage(fileInfo, albumId, c.GetUserId(), from == "" || from == "pasteImage")
|
||||
resultMsg = c.Message(resultMsg)
|
||||
|
||||
fileInfo.Path = "" // 不要返回
|
||||
re.Item = fileInfo
|
||||
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
@@ -204,127 +216,62 @@ func (c File) DeleteImage(fileId string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// update image uploader to leaui image,
|
||||
// scan all user's images and insert into db
|
||||
func (c File) UpgradeLeauiImage() revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
if ok, _ := revel.Config.Bool("upgradeLeauiImage"); !ok {
|
||||
re.Msg = "Not allowed"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
uploadPath := revel.BasePath + "/public/upload";
|
||||
userIds := ListDir(uploadPath)
|
||||
if userIds == nil {
|
||||
re.Msg = "no user"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
msg := "";
|
||||
|
||||
for _, userId := range userIds {
|
||||
dirPath := uploadPath + "/" + userId + "/images"
|
||||
images := ListDir(dirPath)
|
||||
if images == nil {
|
||||
msg += userId + " no images "
|
||||
continue;
|
||||
}
|
||||
|
||||
hadImages := fileService.GetAllImageNamesMap(userId)
|
||||
|
||||
i := 0
|
||||
for _, filename := range images {
|
||||
if _, ok := hadImages[filename]; !ok {
|
||||
fileUrlPath := "/upload/" + userId + "/images/" + filename
|
||||
fileInfo := info.File{Name: filename,
|
||||
Title: filename,
|
||||
Path: fileUrlPath,
|
||||
Size: GetFilesize(dirPath + "/" + filename)}
|
||||
fileService.AddImage(fileInfo, "", userId)
|
||||
i++
|
||||
}
|
||||
}
|
||||
msg += userId + ": " + strconv.Itoa(len(images)) + " -- " + strconv.Itoa(i) + " images "
|
||||
}
|
||||
|
||||
re.Msg = msg
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
//-----------
|
||||
|
||||
// 输出image
|
||||
// 权限判断
|
||||
func (c File) OutputImage(noteId, fileId string) revel.Result {
|
||||
path := fileService.GetFile(c.GetUserId(), fileId); // 得到路径
|
||||
path := fileService.GetFile(c.GetUserId(), fileId) // 得到路径
|
||||
if path == "" {
|
||||
return c.RenderText("")
|
||||
}
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
file, _ := os.Open(fn)
|
||||
return c.RenderFile(file, revel.Inline) // revel.Attachment
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
file, _ := os.Open(fn)
|
||||
return c.RenderFile(file, revel.Inline) // revel.Attachment
|
||||
}
|
||||
|
||||
// 协作时复制图片到owner
|
||||
// 需要计算对方大小
|
||||
func (c File) CopyImage(userId, fileId, toUserId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
re.Ok, re.Id = fileService.CopyImage(userId, fileId, toUserId)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 复制外网的图片, 成公共图片 放在/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"
|
||||
fileUrlPath := "files/" + GetRandomFilePath(userId, 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.Ok = fileService.AddImage(fileInfo, "", c.GetUserId())
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
//------------
|
||||
// 过时 已弃用!
|
||||
func (c File) UploadImage(renderHtml string) revel.Result {
|
||||
if renderHtml == "" {
|
||||
renderHtml = "file/image.html"
|
||||
}
|
||||
|
||||
re := c.uploadImage("", "");
|
||||
|
||||
c.RenderArgs["fileUrlPath"] = configService.GetSiteUrl() + re.Id
|
||||
c.RenderArgs["resultCode"] = re.Code
|
||||
c.RenderArgs["resultMsg"] = re.Msg
|
||||
|
||||
return c.RenderTemplate(renderHtml)
|
||||
}
|
||||
|
||||
// 已弃用
|
||||
func (c File) UploadImageJson(from, noteId string) revel.Result {
|
||||
re := c.uploadImage(from, "");
|
||||
// re.Item = fileInfo.Path
|
||||
re.Ok, re.Msg = fileService.AddImage(fileInfo, "", c.GetUserId(), true)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
)
|
||||
|
||||
// 首页
|
||||
@@ -13,32 +13,32 @@ type Index struct {
|
||||
}
|
||||
|
||||
func (c Index) Default() revel.Result {
|
||||
if configService.HomePageIsAdminsBlog(){
|
||||
if configService.HomePageIsAdminsBlog() {
|
||||
blog := Blog{c.BaseController}
|
||||
return blog.Index(configService.GetAdminUsername());
|
||||
return blog.Index(configService.GetAdminUsername())
|
||||
}
|
||||
return c.Index()
|
||||
}
|
||||
|
||||
// leanote展示页, 没有登录的, 或已登录明确要进该页的
|
||||
func (c Index) Index() revel.Result {
|
||||
|
||||
c.SetUserInfo()
|
||||
c.RenderArgs["title"] = "leanote"
|
||||
c.RenderArgs["openRegister"] = configService.GlobalStringConfigs["openRegister"]
|
||||
lang := c.SetLocale()
|
||||
|
||||
return c.RenderTemplate("home/index_" + lang + ".html");
|
||||
c.SetLocale()
|
||||
|
||||
return c.RenderTemplate("home/index.html")
|
||||
}
|
||||
|
||||
// 建议
|
||||
func (c Index) Suggestion(addr, suggestion string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = suggestionService.AddSuggestion(info.Suggestion{Addr: addr, UserId: c.GetObjectUserId(), Suggestion: suggestion})
|
||||
|
||||
|
||||
// 发给我
|
||||
go func() {
|
||||
emailService.SendEmail("leanote@leanote.com", "建议", "UserId: " + c.GetUserId() + " <br /> Suggestions: " + suggestion)
|
||||
}();
|
||||
|
||||
emailService.SendEmail("leanote@leanote.com", "建议", "UserId: "+c.GetUserId()+" <br /> Suggestions: "+suggestion)
|
||||
}()
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ type NoteContentHistory struct {
|
||||
// 得到list
|
||||
func (c NoteContentHistory) ListHistories(noteId string) revel.Result {
|
||||
histories := noteContentHistoryService.ListHistories(noteId, c.GetUserId())
|
||||
|
||||
|
||||
return c.RenderJson(histories)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +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"
|
||||
// "time"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "bytes"
|
||||
// "os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
"fmt"
|
||||
// "bytes"
|
||||
// "os"
|
||||
)
|
||||
|
||||
type Note struct {
|
||||
@@ -22,54 +25,112 @@ type Note struct {
|
||||
// 笔记首页, 判断是否已登录
|
||||
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
|
||||
// 否则, 转向登录页面
|
||||
func (c Note) Index() revel.Result {
|
||||
func (c Note) Index(noteId, online string) revel.Result {
|
||||
c.SetLocale()
|
||||
|
||||
userInfo := c.GetUserInfo()
|
||||
|
||||
userInfo := c.GetUserAndBlogUrl()
|
||||
|
||||
userId := userInfo.UserId.Hex()
|
||||
|
||||
|
||||
// 没有登录
|
||||
if userId == "" {
|
||||
return c.Redirect("/login")
|
||||
}
|
||||
|
||||
|
||||
c.RenderArgs["openRegister"] = configService.IsOpenRegister()
|
||||
|
||||
|
||||
// 已登录了, 那么得到所有信息
|
||||
notebooks := notebookService.GetNotebooks(userId)
|
||||
shareNotebooks, sharedUserInfos := shareService.GetShareNotebooks(userId)
|
||||
|
||||
|
||||
// 还需要按时间排序(DESC)得到notes
|
||||
notes := []info.Note{}
|
||||
noteContent := info.NoteContent{}
|
||||
|
||||
if len(notebooks) > 0 {
|
||||
// _, notes = noteService.ListNotes(c.GetUserId(), "", false, c.GetPage(), pageSize, defaultSortField, false, false);
|
||||
// 变成最新
|
||||
_, notes = noteService.ListNotes(c.GetUserId(), "", false, c.GetPage(), 50, defaultSortField, false, false);
|
||||
if len(notes) > 0 {
|
||||
noteContent = noteService.GetNoteContent(notes[0].NoteId.Hex(), userId)
|
||||
// noteId是否存在
|
||||
// 是否传入了正确的noteId
|
||||
hasRightNoteId := false
|
||||
if IsObjectId(noteId) {
|
||||
note := noteService.GetNoteById(noteId)
|
||||
|
||||
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()
|
||||
|
||||
// 打开的是共享的笔记, 那么判断是否是共享给我的默认笔记
|
||||
if noteOwner != c.GetUserId() {
|
||||
if shareService.HasReadPerm(noteOwner, c.GetUserId(), noteId) {
|
||||
// 不要获取notebook下的笔记
|
||||
// 在前端下发请求
|
||||
c.RenderArgs["curSharedNoteNotebookId"] = note.NotebookId.Hex()
|
||||
c.RenderArgs["curSharedUserId"] = noteOwner
|
||||
// 没有读写权限
|
||||
} else {
|
||||
hasRightNoteId = false
|
||||
}
|
||||
} else {
|
||||
_, notes = noteService.ListNotes(c.GetUserId(), note.NotebookId.Hex(), false, c.GetPage(), 50, defaultSortField, false, false)
|
||||
|
||||
// 如果指定了某笔记, 则该笔记放在首位
|
||||
lenNotes := len(notes)
|
||||
if lenNotes > 1 {
|
||||
notes2 := make([]info.Note, len(notes))
|
||||
notes2[0] = note
|
||||
i := 1
|
||||
for _, note := range notes {
|
||||
if note.NoteId.Hex() != noteId {
|
||||
if i == lenNotes { // 防止越界
|
||||
break
|
||||
}
|
||||
notes2[i] = note
|
||||
i++
|
||||
}
|
||||
}
|
||||
notes = notes2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 得到最近的笔记
|
||||
_, latestNotes := noteService.ListNotes(c.GetUserId(), "", false, c.GetPage(), 50, defaultSortField, false, false)
|
||||
c.RenderArgs["latestNotes"] = latestNotes
|
||||
}
|
||||
|
||||
// 没有传入笔记
|
||||
// 那么得到最新笔记
|
||||
if !hasRightNoteId {
|
||||
_, notes = noteService.ListNotes(c.GetUserId(), "", false, c.GetPage(), 50, defaultSortField, false, false)
|
||||
if len(notes) > 0 {
|
||||
noteContent = noteService.GetNoteContent(notes[0].NoteId.Hex(), userId)
|
||||
c.RenderArgs["curNoteId"] = notes[0].NoteId.Hex()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 当然, 还需要得到第一个notes的content
|
||||
//...
|
||||
Log(configService.GetAdminUsername())
|
||||
c.RenderArgs["isAdmin"] = configService.GetAdminUsername() == userInfo.Username
|
||||
|
||||
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
c.RenderArgs["notebooks"] = notebooks
|
||||
c.RenderArgs["shareNotebooks"] = shareNotebooks
|
||||
c.RenderArgs["shareNotebooks"] = shareNotebooks // note信息在notes列表中
|
||||
c.RenderArgs["sharedUserInfos"] = sharedUserInfos
|
||||
|
||||
|
||||
c.RenderArgs["notes"] = notes
|
||||
c.RenderArgs["noteContentJson"] = noteContent
|
||||
c.RenderArgs["noteContent"] = noteContent.Content
|
||||
|
||||
|
||||
c.RenderArgs["tags"] = tagService.GetTags(c.GetUserId())
|
||||
|
||||
|
||||
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
|
||||
|
||||
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")
|
||||
@@ -80,13 +141,13 @@ func (c Note) Index() 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)
|
||||
}
|
||||
|
||||
@@ -101,116 +162,139 @@ func (c Note) GetNoteContent(noteId string) revel.Result {
|
||||
return c.RenderJson(noteContent)
|
||||
}
|
||||
|
||||
// 更新note或content
|
||||
// 肯定会传userId(谁的), NoteId
|
||||
// 会传Title, Content, Tags, 一种或几种
|
||||
type NoteOrContent struct {
|
||||
NotebookId string
|
||||
NoteId string
|
||||
UserId string
|
||||
Title string
|
||||
Desc string
|
||||
ImgSrc string
|
||||
Tags []string
|
||||
Content string
|
||||
Abstract string
|
||||
IsNew bool
|
||||
IsMarkdown bool
|
||||
FromUserId string // 为共享而新建
|
||||
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
|
||||
}
|
||||
// 这里不能用json, 要用post
|
||||
func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
|
||||
func (c Note) UpdateNoteOrContent(noteOrContent info.NoteOrContent) revel.Result {
|
||||
// 新添加note
|
||||
if noteOrContent.IsNew {
|
||||
userId := c.GetObjectUserId();
|
||||
// myUserId := userId
|
||||
userId := c.GetObjectUserId()
|
||||
// myUserId := userId
|
||||
// 为共享新建?
|
||||
if noteOrContent.FromUserId != "" {
|
||||
userId = bson.ObjectIdHex(noteOrContent.FromUserId)
|
||||
}
|
||||
|
||||
note := info.Note{UserId: userId,
|
||||
NoteId: bson.ObjectIdHex(noteOrContent.NoteId),
|
||||
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
|
||||
Title: noteOrContent.Title,
|
||||
Tags: noteOrContent.Tags,
|
||||
Desc: noteOrContent.Desc,
|
||||
ImgSrc: noteOrContent.ImgSrc,
|
||||
IsBlog: noteOrContent.IsBlog,
|
||||
|
||||
note := info.Note{UserId: userId,
|
||||
NoteId: bson.ObjectIdHex(noteOrContent.NoteId),
|
||||
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
|
||||
Title: noteOrContent.Title,
|
||||
Tags: strings.Split(noteOrContent.Tags, ","),
|
||||
Desc: noteOrContent.Desc,
|
||||
ImgSrc: noteOrContent.ImgSrc,
|
||||
IsBlog: noteOrContent.IsBlog,
|
||||
IsMarkdown: noteOrContent.IsMarkdown,
|
||||
};
|
||||
noteContent := info.NoteContent{NoteId: note.NoteId,
|
||||
UserId: userId,
|
||||
IsBlog: note.IsBlog,
|
||||
Content: noteOrContent.Content,
|
||||
Abstract: noteOrContent.Abstract};
|
||||
|
||||
}
|
||||
noteContent := info.NoteContent{NoteId: note.NoteId,
|
||||
UserId: userId,
|
||||
IsBlog: note.IsBlog,
|
||||
Content: noteOrContent.Content,
|
||||
Abstract: noteOrContent.Abstract}
|
||||
|
||||
note = noteService.AddNoteAndContentForController(note, noteContent, c.GetUserId())
|
||||
return c.RenderJson(note)
|
||||
}
|
||||
|
||||
|
||||
noteUpdate := bson.M{}
|
||||
needUpdateNote := false
|
||||
|
||||
|
||||
// Desc前台传来
|
||||
if c.Has("Desc") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Desc"] = noteOrContent.Desc;
|
||||
noteUpdate["Desc"] = noteOrContent.Desc
|
||||
}
|
||||
if c.Has("ImgSrc") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["ImgSrc"] = noteOrContent.ImgSrc;
|
||||
noteUpdate["ImgSrc"] = noteOrContent.ImgSrc
|
||||
}
|
||||
if c.Has("Title") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Title"] = noteOrContent.Title;
|
||||
noteUpdate["Title"] = noteOrContent.Title
|
||||
}
|
||||
|
||||
if c.Has("Tags[]") {
|
||||
|
||||
if c.Has("Tags") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Tags"] = noteOrContent.Tags;
|
||||
noteUpdate["Tags"] = strings.Split(noteOrContent.Tags, ",")
|
||||
}
|
||||
|
||||
if needUpdateNote {
|
||||
noteService.UpdateNote(noteOrContent.UserId, c.GetUserId(),
|
||||
noteOrContent.NoteId, noteUpdate)
|
||||
|
||||
// web端不控制
|
||||
if needUpdateNote {
|
||||
noteService.UpdateNote(c.GetUserId(),
|
||||
noteOrContent.NoteId, noteUpdate, -1)
|
||||
}
|
||||
|
||||
|
||||
//-------------
|
||||
|
||||
afterContentUsn := 0
|
||||
contentOk := false
|
||||
contentMsg := ""
|
||||
if c.Has("Content") {
|
||||
noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(),
|
||||
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract)
|
||||
// noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(),
|
||||
// noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract)
|
||||
contentOk, contentMsg, afterContentUsn = noteService.UpdateNoteContent(c.GetUserId(),
|
||||
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract,
|
||||
needUpdateNote, -1, time.Now())
|
||||
}
|
||||
|
||||
|
||||
Log(afterContentUsn)
|
||||
Log(contentOk)
|
||||
Log(contentMsg)
|
||||
|
||||
return c.RenderJson(true)
|
||||
}
|
||||
|
||||
// 删除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)
|
||||
}
|
||||
|
||||
//------------
|
||||
@@ -220,143 +304,178 @@ 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("auth error")
|
||||
}
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
return c.RenderText("")
|
||||
return c.RenderText("no note")
|
||||
}
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
|
||||
noteUserId := note.UserId.Hex()
|
||||
content := noteService.GetNoteContent(noteId, noteUserId)
|
||||
userInfo := userService.GetUserInfo(noteUserId);
|
||||
|
||||
userInfo := userService.GetUserInfo(noteUserId)
|
||||
|
||||
//------------------
|
||||
// 将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)
|
||||
}
|
||||
|
||||
siteUrlPattern = "(?:" + siteUrlPattern + ")*"
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// markdown
|
||||
if note.IsMarkdown {
|
||||
// 
|
||||
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 := ""
|
||||
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if note.Tags != nil && len(note.Tags) > 0 && note.Tags[0] != "" {
|
||||
} else {
|
||||
note.Tags = nil
|
||||
}
|
||||
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
|
||||
|
||||
return c.RenderTemplate("html2Image/index.html")
|
||||
|
||||
return c.RenderTemplate("file/pdf.html")
|
||||
}
|
||||
|
||||
func (c Note) Html2Image(noteId string) revel.Result {
|
||||
// 导出成PDF
|
||||
func (c Note) ExportPdf(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)
|
||||
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("No Perm")
|
||||
}
|
||||
}
|
||||
|
||||
// path 判断是否需要重新生成之
|
||||
fileUrlPath := "/upload/" + noteUserId + "/images/weibo"
|
||||
dir := revel.BasePath + "/public/" + fileUrlPath
|
||||
if !ClearDir(dir) {
|
||||
re.Msg = "No Dir"
|
||||
return c.RenderJson(re)
|
||||
guid := NewGuid()
|
||||
fileUrlPath := "files/export_pdf"
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
if !MkdirAll(dir) {
|
||||
return c.RenderText("error, no dir")
|
||||
}
|
||||
|
||||
filename := note.NoteId.Hex() + ".png";
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
|
||||
// 默认路径
|
||||
if binPath == "" {
|
||||
return c.RenderJson(re);
|
||||
binPath = "/usr/local/bin/wkhtmltopdf"
|
||||
}
|
||||
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 + " --lowquality --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
} else {
|
||||
cc = binPath + " --lowquality \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
}
|
||||
|
||||
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||
Log(cc);
|
||||
b, err := cmd.Output()
|
||||
if err == nil {
|
||||
re.Ok = true
|
||||
re.Id = fileUrlPath + "/" + filename
|
||||
} else {
|
||||
re.Msg = string(b)
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
}
|
||||
|
||||
return c.RenderJson(re)
|
||||
|
||||
/*
|
||||
// 这里速度慢, 生成不完全(图片和内容都不全)
|
||||
content := noteService.GetNoteContent(noteId, noteUserId)
|
||||
userInfo := userService.GetUserInfo(noteUserId);
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
c.RenderArgs["blog"] = note
|
||||
c.RenderArgs["content"] = content.Content
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
userBlog := blogService.GetUserBlog(noteUserId)
|
||||
c.RenderArgs["userBlog"] = userBlog
|
||||
|
||||
html := c.RenderTemplateStr("html2Image/index.html") // Result类型的
|
||||
contentFile := dir + "/html";
|
||||
fout, err := os.Create(contentFile)
|
||||
if err != nil {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
fout.WriteString(html);
|
||||
fout.Close()
|
||||
|
||||
cc := "/Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/c.js \"" + contentFile + "\" \"" + path + "\""
|
||||
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||
b, err := cmd.Output()
|
||||
if err == nil {
|
||||
re.Ok = true
|
||||
re.Id = fileUrlPath + "/" + filename
|
||||
} else {
|
||||
Log(string(b))
|
||||
}
|
||||
*/
|
||||
|
||||
_, 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)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
)
|
||||
|
||||
type Notebook struct {
|
||||
@@ -30,21 +30,23 @@ func (c Notebook) DeleteNotebook(notebookId string) revel.Result {
|
||||
|
||||
// 添加notebook
|
||||
func (c Notebook) AddNotebook(notebookId, title, parentNotebookId string) revel.Result {
|
||||
notebook := info.Notebook{NotebookId: bson.ObjectIdHex(notebookId),
|
||||
Title: title,
|
||||
Seq: -1,
|
||||
notebook := info.Notebook{NotebookId: bson.ObjectIdHex(notebookId),
|
||||
Title: title,
|
||||
Seq: -1,
|
||||
UserId: c.GetObjectUserId()}
|
||||
if(parentNotebookId != "") {
|
||||
if parentNotebookId != "" {
|
||||
notebook.ParentNotebookId = bson.ObjectIdHex(parentNotebookId)
|
||||
}
|
||||
re := notebookService.AddNotebook(notebook)
|
||||
|
||||
if(re) {
|
||||
|
||||
re, notebook := notebookService.AddNotebook(notebook)
|
||||
|
||||
if re {
|
||||
return c.RenderJson(notebook)
|
||||
} else {
|
||||
return c.RenderJson(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 修改标题
|
||||
func (c Notebook) UpdateNotebookTitle(notebookId, title string) revel.Result {
|
||||
return c.RenderJson(notebookService.UpdateNotebookTitle(notebookId, c.GetUserId(), title))
|
||||
@@ -52,21 +54,22 @@ func (c Notebook) UpdateNotebookTitle(notebookId, title string) revel.Result {
|
||||
|
||||
// 排序
|
||||
// 无用
|
||||
func (c Notebook) SortNotebooks(notebookId2Seqs map[string]int) revel.Result {
|
||||
return c.RenderJson(notebookService.SortNotebooks(c.GetUserId(), notebookId2Seqs))
|
||||
}
|
||||
// func (c Notebook) SortNotebooks(notebookId2Seqs map[string]int) revel.Result {
|
||||
// return c.RenderJson(notebookService.SortNotebooks(c.GetUserId(), notebookId2Seqs))
|
||||
// }
|
||||
|
||||
// 调整notebooks, 可能是排序, 可能是移动到其它笔记本下
|
||||
type DragNotebooksInfo struct {
|
||||
CurNotebookId string
|
||||
CurNotebookId string
|
||||
ParentNotebookId string
|
||||
Siblings []string
|
||||
Siblings []string
|
||||
}
|
||||
|
||||
// 传过来的data是JSON.stringfy数据
|
||||
func (c Notebook) DragNotebooks(data string) revel.Result {
|
||||
info := DragNotebooksInfo{}
|
||||
json.Unmarshal([]byte(data), &info)
|
||||
|
||||
|
||||
return c.RenderJson(notebookService.DragNotebooks(c.GetUserId(), info.CurNotebookId, info.ParentNotebookId, info.Siblings))
|
||||
}
|
||||
|
||||
@@ -74,4 +77,4 @@ func (c Notebook) DragNotebooks(data string) revel.Result {
|
||||
func (c Notebook) SetNotebook2Blog(notebookId string, isBlog bool) revel.Result {
|
||||
re := notebookService.ToBlog(c.GetUserId(), notebookId, isBlog)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -2,13 +2,13 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "strings"
|
||||
// "time"
|
||||
// "strings"
|
||||
// "time"
|
||||
// "encoding/json"
|
||||
// "github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/leanote/leanote/app/lea/blog"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/leanote/leanote/app/lea/blog"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "math"
|
||||
@@ -23,7 +23,7 @@ type Preview struct {
|
||||
// 得到要预览的主题绝对路径
|
||||
func (c Preview) getPreviewThemeAbsolutePath(themeId string) bool {
|
||||
if themeId != "" {
|
||||
c.Session["themeId"] = themeId // 存到session中, 下次的url就不能带了
|
||||
c.Session["themeId"] = themeId // 存到session中, 下次的url就不能带了, 待优化, 有时会取不到
|
||||
} else {
|
||||
themeId = c.Session["themeId"] // 直接从session中获取
|
||||
}
|
||||
@@ -31,8 +31,10 @@ func (c Preview) getPreviewThemeAbsolutePath(themeId string) bool {
|
||||
return false
|
||||
}
|
||||
theme := themeService.GetTheme(c.GetUserId(), themeId)
|
||||
|
||||
|
||||
c.RenderArgs["isPreview"] = true
|
||||
c.RenderArgs["themeId"] = themeId
|
||||
c.RenderArgs["themeInfoPreview"] = theme.Info
|
||||
c.RenderArgs["themePath"] = theme.Path
|
||||
if theme.Path == "" {
|
||||
return false
|
||||
@@ -45,7 +47,7 @@ func (c Preview) Index(userIdOrEmail string, themeId string) revel.Result {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Index(c.GetUserId())
|
||||
// return blog.RenderTemplate("index.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(themeId))
|
||||
// return blog.RenderTemplate("index.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(themeId))
|
||||
}
|
||||
|
||||
func (c Preview) Tag(userIdOrEmail, tag string) revel.Result {
|
||||
@@ -59,43 +61,43 @@ func (c Preview) Tags(userIdOrEmail string) revel.Result {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Tags(c.GetUserId())
|
||||
// if tag == "" {
|
||||
// return blog.RenderTemplate("tags.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// }
|
||||
// return blog.RenderTemplate("tag_posts.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// if tag == "" {
|
||||
// return blog.RenderTemplate("tags.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// }
|
||||
// return blog.RenderTemplate("tag_posts.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Archives(userIdOrEmail string, notebookId string, year, month int) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Archives(c.GetUserId(), notebookId, year, month)
|
||||
// return blog.RenderTemplate("archive.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("archive.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Cate(userIdOrEmail, notebookId string) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Cate(userIdOrEmail, notebookId)
|
||||
// return blog.RenderTemplate("cate.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("cate.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Post(userIdOrEmail, noteId string) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Post(userIdOrEmail, noteId)
|
||||
// return blog.RenderTemplate("view.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("view.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Single(userIdOrEmail, singleId string) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Single(userIdOrEmail, singleId)
|
||||
// return blog.RenderTemplate("single.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
// return blog.RenderTemplate("single.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
func (c Preview) Search(userIdOrEmail, keywords string) revel.Result {
|
||||
if !c.getPreviewThemeAbsolutePath("") {
|
||||
return c.E404()
|
||||
}
|
||||
return c.Blog.Search(c.GetUserId(), keywords)
|
||||
// return blog.RenderTemplate("search.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
// return blog.RenderTemplate("search.html", c.RenderArgs, c.getPreviewThemeAbsolutePath(""))
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
)
|
||||
|
||||
type Share struct {
|
||||
@@ -19,20 +19,20 @@ type Share struct {
|
||||
func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Result {
|
||||
status := make(map[string]info.Re, len(emails))
|
||||
// 自己不能给自己添加共享
|
||||
myEmail := c.GetEmail();
|
||||
myEmail := c.GetEmail()
|
||||
for _, email := range emails {
|
||||
if email == "" {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
if(myEmail != email) {
|
||||
ok, msg, userId := shareService.AddShareNote(noteId, perm, c.GetUserId(), email);
|
||||
if myEmail != email {
|
||||
ok, msg, userId := shareService.AddShareNote(noteId, perm, c.GetUserId(), email)
|
||||
status[email] = info.Re{Ok: ok, Msg: msg, Id: userId}
|
||||
|
||||
|
||||
} else {
|
||||
status[email] = info.Re{Ok: false, Msg: "不能分享给自己"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return c.RenderJson(status)
|
||||
}
|
||||
|
||||
@@ -40,19 +40,19 @@ func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Resu
|
||||
func (c Share) AddShareNotebook(notebookId string, emails []string, perm int) revel.Result {
|
||||
status := make(map[string]info.Re, len(emails))
|
||||
// 自己不能给自己添加共享
|
||||
myEmail := c.GetEmail();
|
||||
myEmail := c.GetEmail()
|
||||
for _, email := range emails {
|
||||
if email == "" {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
if(myEmail != email) {
|
||||
ok, msg, userId := shareService.AddShareNotebook(notebookId, perm, c.GetUserId(), email);
|
||||
if myEmail != email {
|
||||
ok, msg, userId := shareService.AddShareNotebook(notebookId, perm, c.GetUserId(), email)
|
||||
status[email] = info.Re{Ok: ok, Msg: msg, Id: userId}
|
||||
} else {
|
||||
status[email] = info.Re{Ok: false, Msg: "不能分享给自己"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return c.RenderJson(status)
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@ func (c Share) ListShareNotes(notebookId, userId string) revel.Result {
|
||||
// 表示是默认笔记本, 不是某个特定notebook的共享
|
||||
var notes []info.ShareNoteWithPerm
|
||||
if notebookId == "" {
|
||||
notes = shareService.ListShareNotes(c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false);
|
||||
notes = shareService.ListShareNotes(c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false)
|
||||
return c.RenderJson(notes)
|
||||
} else {
|
||||
// 有notebookId的
|
||||
return c.RenderJson(shareService.ListShareNotesByNotebookId(notebookId, c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false));
|
||||
return c.RenderJson(shareService.ListShareNotesByNotebookId(notebookId, c.GetUserId(), userId, c.GetPage(), pageSize, defaultSortField, false))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,106 +83,110 @@ func (c Share) GetShareNoteContent(noteId, sharedUserId string) revel.Result {
|
||||
// 还要查看该note的notebookId分享的信息
|
||||
func (c Share) ListNoteShareUserInfo(noteId string) revel.Result {
|
||||
note := noteService.GetNote(noteId, c.GetUserId())
|
||||
|
||||
|
||||
noteShareUserInfos := shareService.ListNoteShareUserInfo(noteId, c.GetUserId())
|
||||
c.RenderArgs["noteOrNotebookShareUserInfos"] = noteShareUserInfos
|
||||
|
||||
c.RenderArgs["noteOrNotebookShareUserInfos"] = noteShareUserInfos
|
||||
|
||||
c.RenderArgs["noteOrNotebookShareGroupInfos"] = shareService.GetNoteShareGroups(noteId, c.GetUserId())
|
||||
|
||||
|
||||
c.RenderArgs["isNote"] = true
|
||||
c.RenderArgs["noteOrNotebookId"] = note.NoteId.Hex();
|
||||
c.RenderArgs["noteOrNotebookId"] = note.NoteId.Hex()
|
||||
c.RenderArgs["title"] = note.Title
|
||||
|
||||
|
||||
return c.RenderTemplate("share/note_notebook_share_user_infos.html")
|
||||
}
|
||||
func (c Share) ListNotebookShareUserInfo(notebookId string) revel.Result {
|
||||
notebook := notebookService.GetNotebook(notebookId, c.GetUserId())
|
||||
|
||||
|
||||
notebookShareUserInfos := shareService.ListNotebookShareUserInfo(notebookId, c.GetUserId())
|
||||
c.RenderArgs["noteOrNotebookShareUserInfos"] = notebookShareUserInfos
|
||||
|
||||
c.RenderArgs["noteOrNotebookShareUserInfos"] = notebookShareUserInfos
|
||||
|
||||
c.RenderArgs["noteOrNotebookShareGroupInfos"] = shareService.GetNotebookShareGroups(notebookId, c.GetUserId())
|
||||
LogJ(c.RenderArgs["noteOrNotebookShareGroupInfos"])
|
||||
|
||||
|
||||
c.RenderArgs["isNote"] = false
|
||||
c.RenderArgs["noteOrNotebookId"] = notebook.NotebookId.Hex();
|
||||
c.RenderArgs["noteOrNotebookId"] = notebook.NotebookId.Hex()
|
||||
c.RenderArgs["title"] = notebook.Title
|
||||
|
||||
|
||||
return c.RenderTemplate("share/note_notebook_share_user_infos.html")
|
||||
}
|
||||
|
||||
//------------
|
||||
// 改变share note 权限
|
||||
func (c Share) UpdateShareNotePerm(noteId string, perm int, toUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.UpdateShareNotePerm(noteId, perm, c.GetUserId(), toUserId));
|
||||
return c.RenderJson(shareService.UpdateShareNotePerm(noteId, perm, c.GetUserId(), toUserId))
|
||||
}
|
||||
|
||||
// 改变share notebook 权限
|
||||
func (c Share) UpdateShareNotebookPerm(notebookId string, perm int, toUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.UpdateShareNotebookPerm(notebookId, perm, c.GetUserId(), toUserId));
|
||||
return c.RenderJson(shareService.UpdateShareNotebookPerm(notebookId, perm, c.GetUserId(), toUserId))
|
||||
}
|
||||
|
||||
//---------------
|
||||
// 删除share note
|
||||
func (c Share) DeleteShareNote(noteId string, toUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteShareNote(noteId, c.GetUserId(), toUserId));
|
||||
return c.RenderJson(shareService.DeleteShareNote(noteId, c.GetUserId(), toUserId))
|
||||
}
|
||||
|
||||
// 删除share notebook
|
||||
func (c Share) DeleteShareNotebook(notebookId string, toUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteShareNotebook(notebookId, c.GetUserId(), toUserId));
|
||||
return c.RenderJson(shareService.DeleteShareNotebook(notebookId, c.GetUserId(), toUserId))
|
||||
}
|
||||
|
||||
// 删除share note, 被共享方删除
|
||||
func (c Share) DeleteShareNoteBySharedUser(noteId string, fromUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteShareNote(noteId, fromUserId, c.GetUserId()));
|
||||
return c.RenderJson(shareService.DeleteShareNote(noteId, fromUserId, c.GetUserId()))
|
||||
}
|
||||
|
||||
// 删除share notebook, 被共享方删除
|
||||
func (c Share) DeleteShareNotebookBySharedUser(notebookId string, fromUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteShareNotebook(notebookId, fromUserId, c.GetUserId()));
|
||||
return c.RenderJson(shareService.DeleteShareNotebook(notebookId, fromUserId, c.GetUserId()))
|
||||
}
|
||||
|
||||
// 删除fromUserId分享给我的所有note, notebook
|
||||
func (c Share) DeleteUserShareNoteAndNotebook(fromUserId string) revel.Result {
|
||||
return c.RenderJson(shareService.DeleteUserShareNoteAndNotebook(fromUserId, c.GetUserId()));
|
||||
return c.RenderJson(shareService.DeleteUserShareNoteAndNotebook(fromUserId, c.GetUserId()))
|
||||
}
|
||||
|
||||
//-------------
|
||||
// 用户组
|
||||
|
||||
|
||||
// 将笔记分享给分组
|
||||
func (c Share) AddShareNoteGroup(noteId, groupId string, perm int) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = shareService.AddShareNoteGroup(c.GetUserId(), noteId, groupId, perm);
|
||||
return c.RenderJson(re);
|
||||
re.Ok = shareService.AddShareNoteGroup(c.GetUserId(), noteId, groupId, perm)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 删除
|
||||
func (c Share) DeleteShareNoteGroup(noteId, groupId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = shareService.DeleteShareNoteGroup(c.GetUserId(), noteId, groupId);
|
||||
return c.RenderJson(re);
|
||||
re.Ok = shareService.DeleteShareNoteGroup(c.GetUserId(), noteId, groupId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 更新, 也是一样, 先删后加
|
||||
func (c Share) UpdateShareNoteGroupPerm(noteId, groupId string, perm int) revel.Result {
|
||||
return c.AddShareNoteGroup(noteId, groupId, perm)
|
||||
}
|
||||
|
||||
|
||||
//------
|
||||
|
||||
// 将笔记分享给分组
|
||||
func (c Share) AddShareNotebookGroup(notebookId, groupId string, perm int) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = shareService.AddShareNotebookGroup(c.GetUserId(), notebookId, groupId, perm);
|
||||
return c.RenderJson(re);
|
||||
re.Ok = shareService.AddShareNotebookGroup(c.GetUserId(), notebookId, groupId, perm)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 删除
|
||||
func (c Share) DeleteShareNotebookGroup(notebookId, groupId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = shareService.DeleteShareNotebookGroup(c.GetUserId(), notebookId, groupId);
|
||||
return c.RenderJson(re);
|
||||
re.Ok = shareService.DeleteShareNotebookGroup(c.GetUserId(), notebookId, groupId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 更新, 也是一样, 先删后加
|
||||
func (c Share) UpdateShareNotebookGroupPerm(notebookId, groupId string, perm int) revel.Result {
|
||||
return c.AddShareNotebookGroup(notebookId, groupId, perm)
|
||||
}
|
||||
}
|
||||
|
||||
30
app/controllers/TagController.go
Normal file
30
app/controllers/TagController.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// "os/exec"
|
||||
)
|
||||
|
||||
type Tag struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// 更新Tag
|
||||
func (c Tag) UpdateTag(tag string) revel.Result {
|
||||
ret := info.NewRe()
|
||||
ret.Ok = true
|
||||
ret.Item = tagService.AddOrUpdateTag(c.GetUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
func (c Tag) DeleteTag(tag string) revel.Result {
|
||||
ret := info.Re{}
|
||||
ret.Ok = true
|
||||
ret.Item = tagService.DeleteTag(c.GetUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
}
|
||||
@@ -2,16 +2,16 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -29,59 +29,59 @@ 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);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
|
||||
if re.Ok, re.Msg = Vd("username", username); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
|
||||
re.Ok, re.Msg = userService.UpdateUsername(c.GetUserId(), username)
|
||||
if(re.Ok) {
|
||||
if re.Ok {
|
||||
c.UpdateSession("Username", username)
|
||||
}
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
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);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", oldPwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
re.Ok, re.Msg = userService.UpdatePwd(c.GetUserId(), oldPwd, pwd)
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
// 更新主题
|
||||
func (c User) UpdateTheme(theme string) revel.Result {
|
||||
re := info.NewRe();
|
||||
re := info.NewRe()
|
||||
re.Ok = userService.UpdateTheme(c.GetUserId(), theme)
|
||||
if re.Ok {
|
||||
c.UpdateSession("Theme", theme)
|
||||
}
|
||||
return c.RenderJson(re);
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 发送邀请链接
|
||||
func (c User) SendRegisterEmail(content, toEmail string) revel.Result {
|
||||
re := info.NewRe()
|
||||
if content == "" || !IsEmail(toEmail) {
|
||||
return c.RenderJson(re);
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
re.Ok = emailService.SendInviteEmail(c.GetUserInfo(), toEmail, content)
|
||||
return c.RenderJson(re);
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
@@ -93,79 +93,43 @@ func (c User) ReSendActiveEmail() revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 修改Email发送激活邮箱
|
||||
func (c User) UpdateEmailSendActiveEmail(email string) revel.Result {
|
||||
re := info.NewRe()
|
||||
if(c.GetUsername() == "demo") {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re);
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = emailService.UpdateEmailSendActiveEmail(c.GetUserInfo(), email)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
// 通过点击链接
|
||||
// 修改邮箱
|
||||
func (c User) UpdateEmail(token string) revel.Result {
|
||||
c.SetUserInfo();
|
||||
|
||||
c.SetUserInfo()
|
||||
|
||||
ok, msg, email := userService.UpdateEmail(token)
|
||||
|
||||
|
||||
c.RenderArgs["title"] = "验证邮箱"
|
||||
c.RenderArgs["ok"] = ok
|
||||
c.RenderArgs["msg"] = msg
|
||||
c.RenderArgs["email"] = email
|
||||
|
||||
|
||||
// 修改session
|
||||
if ok {
|
||||
c.UpdateSession("Email", email)
|
||||
}
|
||||
|
||||
|
||||
return c.RenderTemplate("user/update_email.html")
|
||||
}
|
||||
|
||||
// 注册后激活邮箱
|
||||
func (c User) ActiveEmail(token string) revel.Result {
|
||||
c.SetUserInfo();
|
||||
|
||||
c.SetUserInfo()
|
||||
|
||||
ok, msg, email := userService.ActiveEmail(token)
|
||||
|
||||
|
||||
// 需要修改session
|
||||
if ok {
|
||||
c.UpdateSession("Verified", "1");
|
||||
c.UpdateSession("Verified", "1")
|
||||
}
|
||||
|
||||
|
||||
c.RenderArgs["title"] = "验证邮箱"
|
||||
c.RenderArgs["ok"] = ok
|
||||
c.RenderArgs["msg"] = msg
|
||||
c.RenderArgs["email"] = email
|
||||
|
||||
return c.RenderTemplate("user/active_email.html")
|
||||
}
|
||||
|
||||
//------------
|
||||
// 第三方账号添加leanote账号
|
||||
func (c User) AddAccount(email, pwd string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = userService.ThirdAddUser(c.GetUserId(), email, pwd)
|
||||
|
||||
if re.Ok {
|
||||
c.UpdateSession("Email", email);
|
||||
}
|
||||
|
||||
return c.RenderRe(re)
|
||||
return c.RenderTemplate("user/active_email.html")
|
||||
}
|
||||
|
||||
//-----------------
|
||||
@@ -174,10 +138,10 @@ func (c User) UpdateColumnWidth(notebookWidth, noteListWidth, mdEditorWidth int)
|
||||
re := info.NewRe()
|
||||
re.Ok = userService.UpdateColumnWidth(c.GetUserId(), notebookWidth, noteListWidth, mdEditorWidth)
|
||||
if re.Ok {
|
||||
c.UpdateSession("NotebookWidth", strconv.Itoa(notebookWidth));
|
||||
c.UpdateSession("NoteListWidth", strconv.Itoa(noteListWidth));
|
||||
c.UpdateSession("MdEditorWidth", strconv.Itoa(mdEditorWidth));
|
||||
|
||||
c.UpdateSession("NotebookWidth", strconv.Itoa(notebookWidth))
|
||||
c.UpdateSession("NoteListWidth", strconv.Itoa(noteListWidth))
|
||||
c.UpdateSession("MdEditorWidth", strconv.Itoa(mdEditorWidth))
|
||||
|
||||
LogJ(c.Session)
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
@@ -187,10 +151,10 @@ func (c User) UpdateLeftIsMin(leftIsMin bool) revel.Result {
|
||||
re.Ok = userService.UpdateLeftIsMin(c.GetUserId(), leftIsMin)
|
||||
if re.Ok {
|
||||
if leftIsMin {
|
||||
c.UpdateSession("LeftIsMin", "1");
|
||||
c.UpdateSession("LeftIsMin", "1")
|
||||
} else {
|
||||
c.UpdateSession("LeftIsMin", "0");
|
||||
c.UpdateSession("LeftIsMin", "0")
|
||||
}
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
// "github.com/revel/revel"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "encoding/json"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/revel/revel"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "strconv"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -20,26 +20,26 @@ type AdminBaseController struct {
|
||||
|
||||
// 得到sorterField 和 isAsc
|
||||
// okSorter = ['email', 'username']
|
||||
func (c AdminBaseController) getSorter(sorterField string, isAsc bool, okSorter []string) (string, bool){
|
||||
func (c AdminBaseController) getSorter(sorterField string, isAsc bool, okSorter []string) (string, bool) {
|
||||
sorter := ""
|
||||
c.Params.Bind(&sorter, "sorter")
|
||||
if sorter == "" {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
|
||||
// sorter形式 email-up, email-down
|
||||
s2 := strings.Split(sorter, "-")
|
||||
if len(s2) != 2 {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
|
||||
// 必须是可用的sorter
|
||||
if okSorter != nil && len(okSorter) > 0 {
|
||||
if !InArray(okSorter, s2[0]) {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sorterField = strings.Title(s2[0])
|
||||
if s2[1] == "up" {
|
||||
isAsc = true
|
||||
@@ -47,7 +47,7 @@ func (c AdminBaseController) getSorter(sorterField string, isAsc bool, okSorter
|
||||
isAsc = false
|
||||
}
|
||||
c.RenderArgs["sorter"] = sorter
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
func (c AdminBaseController) updateConfig(keys []string) {
|
||||
@@ -56,4 +56,4 @@ func (c AdminBaseController) updateConfig(keys []string) {
|
||||
v := c.Params.Values.Get(key)
|
||||
configService.UpdateGlobalStringConfig(userId, key, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
)
|
||||
|
||||
@@ -15,16 +15,16 @@ type AdminBlog struct {
|
||||
// admin 主页
|
||||
func (c AdminBlog) Index(sorter, keywords string) revel.Result {
|
||||
pageNumber := c.GetPage()
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"title", "userId", "isRecommed", "createdTime"});
|
||||
pageInfo, blogs := blogService.ListAllBlogs("", "", keywords, false, pageNumber, userPageSize, sorterField, isAsc);
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"title", "userId", "isRecommed", "createdTime"})
|
||||
pageInfo, blogs := blogService.ListAllBlogs("", "", keywords, false, pageNumber, userPageSize, sorterField, isAsc)
|
||||
c.RenderArgs["pageInfo"] = pageInfo
|
||||
c.RenderArgs["blogs"] = blogs
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
return c.RenderTemplate("admin/blog/list.html");
|
||||
return c.RenderTemplate("admin/blog/list.html")
|
||||
}
|
||||
|
||||
func (c AdminBlog) SetRecommend(noteId string, recommend bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.SetRecommend(noteId, recommend);
|
||||
re.Ok = blogService.SetRecommend(noteId, recommend)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -13,15 +13,15 @@ type Admin struct {
|
||||
// admin 主页
|
||||
func (c Admin) Index() revel.Result {
|
||||
c.SetUserInfo()
|
||||
|
||||
|
||||
c.RenderArgs["title"] = "leanote"
|
||||
c.SetLocale()
|
||||
|
||||
|
||||
c.RenderArgs["countUser"] = userService.CountUser()
|
||||
c.RenderArgs["countNote"] = noteService.CountNote("")
|
||||
c.RenderArgs["countBlog"] = noteService.CountBlog("")
|
||||
|
||||
return c.RenderTemplate("admin/index.html");
|
||||
|
||||
return c.RenderTemplate("admin/index.html")
|
||||
}
|
||||
|
||||
// 模板
|
||||
@@ -35,5 +35,5 @@ func (c Admin) T(t string) revel.Result {
|
||||
}
|
||||
|
||||
func (c Admin) GetView(view string) revel.Result {
|
||||
return c.RenderTemplate("admin/" + view);
|
||||
}
|
||||
return c.RenderTemplate("admin/" + view)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"os"
|
||||
"io"
|
||||
"time"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 数据管理, 备份和恢复
|
||||
@@ -22,18 +22,18 @@ func (c AdminData) Index() revel.Result {
|
||||
// 逆序之
|
||||
backups2 := make([]map[string]string, len(backups))
|
||||
j := 0
|
||||
for i := len(backups)-1; i >= 0; i-- {
|
||||
for i := len(backups) - 1; i >= 0; i-- {
|
||||
backups2[j] = backups[i]
|
||||
j++
|
||||
}
|
||||
c.RenderArgs["backups"] = backups2
|
||||
return c.RenderTemplate("admin/data/index.html");
|
||||
return c.RenderTemplate("admin/data/index.html")
|
||||
}
|
||||
|
||||
func (c AdminData) Backup() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = configService.Backup("")
|
||||
return c.RenderJson(re)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 还原
|
||||
@@ -51,7 +51,7 @@ func (c AdminData) Delete(createdTime string) revel.Result {
|
||||
func (c AdminData) UpdateRemark(createdTime, remark string) revel.Result {
|
||||
re := info.Re{}
|
||||
re.Ok, re.Msg = configService.UpdateBackupRemark(createdTime, remark)
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c AdminData) Download(createdTime string) revel.Result {
|
||||
@@ -59,56 +59,56 @@ func (c AdminData) Download(createdTime string) revel.Result {
|
||||
if !ok {
|
||||
return c.RenderText("")
|
||||
}
|
||||
|
||||
|
||||
dbname, _ := revel.Config.String("db.dbname")
|
||||
path := backup["path"] + "/" + dbname
|
||||
allFiles := ListDir(path)
|
||||
|
||||
allFiles := ListDir(path)
|
||||
|
||||
filename := "backup_" + dbname + "_" + backup["createdTime"] + ".tar.gz"
|
||||
|
||||
|
||||
// file write
|
||||
fw, err := os.Create(revel.BasePath + "/files/" + filename)
|
||||
if err != nil {
|
||||
fw, err := os.Create(revel.BasePath + "/files/" + filename)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
|
||||
// gzip write
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
|
||||
// tar write
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
// 遍历文件列表
|
||||
for _, file := range allFiles {
|
||||
}
|
||||
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
|
||||
// gzip write
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
|
||||
// tar write
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
// 遍历文件列表
|
||||
for _, file := range allFiles {
|
||||
fn := path + "/" + file
|
||||
fr, err := os.Open(fn)
|
||||
fileInfo, _ := fr.Stat()
|
||||
if err != nil {
|
||||
fr, err := os.Open(fn)
|
||||
fileInfo, _ := fr.Stat()
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// 信息头
|
||||
h := new(tar.Header)
|
||||
h.Name = file
|
||||
h.Size = fileInfo.Size()
|
||||
h.Mode = int64(fileInfo.Mode())
|
||||
h.ModTime = fileInfo.ModTime()
|
||||
|
||||
// 写信息头
|
||||
err = tw.WriteHeader(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 写文件
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} // for
|
||||
|
||||
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachm
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// 信息头
|
||||
h := new(tar.Header)
|
||||
h.Name = file
|
||||
h.Size = fileInfo.Size()
|
||||
h.Mode = int64(fileInfo.Mode())
|
||||
h.ModTime = fileInfo.ModTime()
|
||||
|
||||
// 写信息头
|
||||
err = tw.WriteHeader(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 写文件
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} // for
|
||||
|
||||
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachm
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"strings"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// admin 首页
|
||||
@@ -25,14 +25,14 @@ func (c AdminEmail) Blog() revel.Result {
|
||||
newTags := configService.GetGlobalArrayConfig("newTags")
|
||||
c.RenderArgs["recommendTags"] = strings.Join(recommendTags, ",")
|
||||
c.RenderArgs["newTags"] = strings.Join(newTags, ",")
|
||||
return c.RenderTemplate("admin/setting/blog.html");
|
||||
return c.RenderTemplate("admin/setting/blog.html")
|
||||
}
|
||||
func (c AdminEmail) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -41,21 +41,24 @@ func (c AdminEmail) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
func (c AdminEmail) Demo() revel.Result {
|
||||
c.RenderArgs["demoUsername"] = configService.GetGlobalStringConfig("demoUsername")
|
||||
c.RenderArgs["demoPassword"] = configService.GetGlobalStringConfig("demoPassword")
|
||||
return c.RenderTemplate("admin/setting/demo.html");
|
||||
return c.RenderTemplate("admin/setting/demo.html")
|
||||
}
|
||||
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";
|
||||
re.Msg = "The User is Not Exists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUserId", userInfo.UserId.Hex())
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUsername", demoUsername)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoPassword", demoPassword)
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -63,7 +66,7 @@ func (c AdminEmail) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
// 长微博的bin路径phantomJs
|
||||
func (c AdminEmail) ToImage() revel.Result {
|
||||
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
|
||||
return c.RenderTemplate("admin/setting/toImage.html");
|
||||
return c.RenderTemplate("admin/setting/toImage.html")
|
||||
}
|
||||
func (c AdminEmail) DoToImage(toImageBinPath string) revel.Result {
|
||||
re := info.NewRe()
|
||||
@@ -71,19 +74,20 @@ func (c AdminEmail) DoToImage(toImageBinPath string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c AdminEmail) Set(emailHost, emailPort, emailUsername, emailPassword string) revel.Result {
|
||||
func (c AdminEmail) Set(emailHost, emailPort, emailUsername, emailPassword, emailSSL string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailHost", emailHost)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailPort", emailPort)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailUsername", emailUsername)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailPassword", emailPassword)
|
||||
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "emailSSL", emailSSL)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c AdminEmail) Template() revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
keys := []string{"emailTemplateHeader", "emailTemplateFooter",
|
||||
|
||||
keys := []string{"emailTemplateHeader", "emailTemplateFooter",
|
||||
"emailTemplateRegisterSubject",
|
||||
"emailTemplateRegister",
|
||||
"emailTemplateFindPasswordSubject",
|
||||
@@ -95,7 +99,7 @@ func (c AdminEmail) Template() revel.Result {
|
||||
"emailTemplateCommentSubject",
|
||||
"emailTemplateComment",
|
||||
}
|
||||
|
||||
|
||||
userId := c.GetUserId()
|
||||
for _, key := range keys {
|
||||
v := c.Params.Values.Get(key)
|
||||
@@ -110,7 +114,7 @@ func (c AdminEmail) Template() revel.Result {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
@@ -118,100 +122,99 @@ func (c AdminEmail) Template() revel.Result {
|
||||
// 发送Email
|
||||
func (c AdminEmail) SendEmailToEmails(sendEmails, latestEmailSubject, latestEmailBody string, verified, saveAsOldEmail bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
|
||||
c.updateConfig([]string{"sendEmails", "latestEmailSubject", "latestEmailBody"})
|
||||
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
if saveAsOldEmail {
|
||||
oldEmails := configService.GetGlobalMapConfig("oldEmails")
|
||||
oldEmails[latestEmailSubject] = latestEmailBody
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails);
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails)
|
||||
}
|
||||
|
||||
|
||||
sendEmails = strings.Replace(sendEmails, "\r", "", -1)
|
||||
emails := strings.Split(sendEmails, "\n")
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToEmails(emails, latestEmailSubject, latestEmailBody);
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToEmails(emails, latestEmailSubject, latestEmailBody)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 发送Email
|
||||
func (c AdminEmail) SendToUsers2(emails, latestEmailSubject, latestEmailBody string, verified, saveAsOldEmail bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
|
||||
c.updateConfig([]string{"sendEmails", "latestEmailSubject", "latestEmailBody"})
|
||||
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
if saveAsOldEmail {
|
||||
oldEmails := configService.GetGlobalMapConfig("oldEmails")
|
||||
oldEmails[latestEmailSubject] = latestEmailBody
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails);
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails)
|
||||
}
|
||||
|
||||
|
||||
emails = strings.Replace(emails, "\r", "", -1)
|
||||
emailsArr := strings.Split(emails, "\n")
|
||||
|
||||
|
||||
users := userService.ListUserInfosByEmails(emailsArr)
|
||||
LogJ(emailsArr)
|
||||
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToUsers(users, latestEmailSubject, latestEmailBody);
|
||||
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToUsers(users, latestEmailSubject, latestEmailBody)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// send Email dialog
|
||||
func (c AdminEmail) SendEmailDialog(emails string) revel.Result{
|
||||
func (c AdminEmail) SendEmailDialog(emails string) revel.Result {
|
||||
emailsArr := strings.Split(emails, ",")
|
||||
emailsNl := strings.Join(emailsArr, "\n")
|
||||
|
||||
|
||||
c.RenderArgs["emailsNl"] = emailsNl
|
||||
c.RenderArgs["str"] = configService.GlobalStringConfigs
|
||||
c.RenderArgs["map"] = configService.GlobalMapConfigs
|
||||
|
||||
return c.RenderTemplate("admin/email/emailDialog.html");
|
||||
|
||||
return c.RenderTemplate("admin/email/emailDialog.html")
|
||||
}
|
||||
|
||||
func (c AdminEmail) SendToUsers(userFilterEmail, userFilterWhiteList, userFilterBlackList, latestEmailSubject, latestEmailBody string, verified, saveAsOldEmail bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
|
||||
c.updateConfig([]string{"userFilterEmail", "userFilterWhiteList", "userFilterBlackList", "latestEmailSubject", "latestEmailBody"})
|
||||
|
||||
|
||||
if latestEmailSubject == "" || latestEmailBody == "" {
|
||||
re.Msg = "subject or body is blank"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
if saveAsOldEmail {
|
||||
oldEmails := configService.GetGlobalMapConfig("oldEmails")
|
||||
oldEmails[latestEmailSubject] = latestEmailBody
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails);
|
||||
configService.UpdateGlobalMapConfig(c.GetUserId(), "oldEmails", oldEmails)
|
||||
}
|
||||
|
||||
|
||||
users := userService.GetAllUserByFilter(userFilterEmail, userFilterWhiteList, userFilterBlackList, verified)
|
||||
|
||||
if(users == nil || len(users) == 0) {
|
||||
|
||||
if users == nil || len(users) == 0 {
|
||||
re.Ok = false
|
||||
re.Msg = "no users"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToUsers(users, latestEmailSubject, latestEmailBody);
|
||||
if(!re.Ok) {
|
||||
|
||||
re.Ok, re.Msg = emailService.SendEmailToUsers(users, latestEmailSubject, latestEmailBody)
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
re.Ok = true
|
||||
re.Msg = "users:" + strconv.Itoa(len(users))
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -224,10 +227,10 @@ func (c AdminEmail) DeleteEmails(ids string) revel.Result {
|
||||
|
||||
func (c AdminEmail) List(sorter, keywords string) revel.Result {
|
||||
pageNumber := c.GetPage()
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"email", "ok", "subject", "createdTime"});
|
||||
pageInfo, emails := emailService.ListEmailLogs(pageNumber, userPageSize, sorterField, isAsc, keywords);
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"email", "ok", "subject", "createdTime"})
|
||||
pageInfo, emails := emailService.ListEmailLogs(pageNumber, userPageSize, sorterField, isAsc, keywords)
|
||||
c.RenderArgs["pageInfo"] = pageInfo
|
||||
c.RenderArgs["emails"] = emails
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
return c.RenderTemplate("admin/email/list.html");
|
||||
}
|
||||
return c.RenderTemplate("admin/email/list.html")
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// admin 首页
|
||||
@@ -25,24 +25,24 @@ func (c AdminSetting) Blog() revel.Result {
|
||||
newTags := configService.GetGlobalArrayConfig("newTags")
|
||||
c.RenderArgs["recommendTags"] = strings.Join(recommendTags, ",")
|
||||
c.RenderArgs["newTags"] = strings.Join(newTags, ",")
|
||||
return c.RenderTemplate("admin/setting/blog.html");
|
||||
return c.RenderTemplate("admin/setting/blog.html")
|
||||
}
|
||||
func (c AdminSetting) DoBlogTag(recommendTags, newTags string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 共享设置
|
||||
func (c AdminSetting) ShareNote(registerSharedUserId string,
|
||||
registerSharedNotebookPerms, registerSharedNotePerms []int,
|
||||
func (c AdminSetting) ShareNote(registerSharedUserId string,
|
||||
registerSharedNotebookPerms, registerSharedNotePerms []int,
|
||||
registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds []string) revel.Result {
|
||||
|
||||
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = configService.UpdateShareNoteConfig(registerSharedUserId, registerSharedNotebookPerms, registerSharedNotePerms, registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds);
|
||||
re.Ok, re.Msg = configService.UpdateShareNoteConfig(registerSharedUserId, registerSharedNotebookPerms, registerSharedNotePerms, registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -51,33 +51,37 @@ func (c AdminSetting) ShareNote(registerSharedUserId string,
|
||||
func (c AdminSetting) Demo() revel.Result {
|
||||
c.RenderArgs["demoUsername"] = configService.GetGlobalStringConfig("demoUsername")
|
||||
c.RenderArgs["demoPassword"] = configService.GetGlobalStringConfig("demoPassword")
|
||||
return c.RenderTemplate("admin/setting/demo.html");
|
||||
return c.RenderTemplate("admin/setting/demo.html")
|
||||
}
|
||||
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";
|
||||
re.Msg = "The User is Not Exists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUserId", userInfo.UserId.Hex())
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoUsername", demoUsername)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "demoPassword", demoPassword)
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// ToImage
|
||||
// 长微博的bin路径phantomJs
|
||||
func (c 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)
|
||||
}
|
||||
|
||||
func (c AdminSetting) DoSiteUrl(siteUrl string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "siteUrl", siteUrl)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -85,23 +89,23 @@ func (c AdminSetting) DoToImage(toImageBinPath string) revel.Result {
|
||||
func (c AdminSetting) SubDomain() revel.Result {
|
||||
c.RenderArgs["str"] = configService.GlobalStringConfigs
|
||||
c.RenderArgs["arr"] = configService.GlobalArrayConfigs
|
||||
|
||||
|
||||
c.RenderArgs["noteSubDomain"] = configService.GetGlobalStringConfig("noteSubDomain")
|
||||
c.RenderArgs["blogSubDomain"] = configService.GetGlobalStringConfig("blogSubDomain")
|
||||
c.RenderArgs["leaSubDomain"] = configService.GetGlobalStringConfig("leaSubDomain")
|
||||
|
||||
return c.RenderTemplate("admin/setting/subDomain.html");
|
||||
|
||||
return c.RenderTemplate("admin/setting/subDomain.html")
|
||||
}
|
||||
func (c AdminSetting) DoSubDomain(noteSubDomain, blogSubDomain, leaSubDomain, blackSubDomains, allowCustomDomain, blackCustomDomains string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "noteSubDomain", noteSubDomain)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "blogSubDomain", blogSubDomain)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "leaSubDomain", leaSubDomain)
|
||||
|
||||
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "allowCustomDomain", allowCustomDomain)
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "blackSubDomains", strings.Split(blackSubDomains, ","))
|
||||
re.Ok = configService.UpdateGlobalArrayConfig(c.GetUserId(), "blackCustomDomains", strings.Split(blackCustomDomains, ","))
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -135,4 +139,4 @@ func (c AdminSetting) UploadSize(uploadImageSize, uploadAvatarSize, uploadBlogLo
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "uploadBlogLogoSize", fmt.Sprintf("%v", uploadBlogLogoSize))
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "uploadAttachSize", fmt.Sprintf("%v", uploadAttachSize))
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// "io/ioutil"
|
||||
// "io/ioutil"
|
||||
)
|
||||
|
||||
// Upgrade controller
|
||||
@@ -14,11 +14,17 @@ type AdminUpgrade struct {
|
||||
|
||||
func (c AdminUpgrade) UpgradeBlog() revel.Result {
|
||||
upgradeService.UpgradeBlog()
|
||||
return nil;
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c AdminUpgrade) UpgradeBetaToBeta2() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = upgradeService.UpgradeBetaToBeta2(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
func (c AdminUpgrade) UpgradeBeta3ToBeta4() revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = upgradeService.Api(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "time"
|
||||
"github.com/revel/revel"
|
||||
// "time"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
)
|
||||
|
||||
@@ -15,37 +15,38 @@ type AdminUser struct {
|
||||
|
||||
// admin 主页
|
||||
var userPageSize = 10
|
||||
|
||||
func (c AdminUser) Index(sorter, keywords string, pageSize int) revel.Result {
|
||||
pageNumber := c.GetPage()
|
||||
if userPageSize == 0 {
|
||||
pageSize = userPageSize
|
||||
}
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"email", "username", "verified", "createdTime", "accountType"});
|
||||
pageInfo, users := userService.ListUsers(pageNumber, pageSize, sorterField, isAsc, keywords);
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"email", "username", "verified", "createdTime", "accountType"})
|
||||
pageInfo, users := userService.ListUsers(pageNumber, pageSize, sorterField, isAsc, keywords)
|
||||
c.RenderArgs["pageInfo"] = pageInfo
|
||||
c.RenderArgs["users"] = users
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
return c.RenderTemplate("admin/user/list.html");
|
||||
return c.RenderTemplate("admin/user/list.html")
|
||||
}
|
||||
|
||||
func (c AdminUser) Add() revel.Result {
|
||||
return c.RenderTemplate("admin/user/add.html");
|
||||
return c.RenderTemplate("admin/user/add.html")
|
||||
}
|
||||
|
||||
// 添加
|
||||
func (c AdminUser) Register(email, pwd string) revel.Result {
|
||||
re := info.NewRe();
|
||||
|
||||
re := info.NewRe()
|
||||
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
|
||||
// 注册
|
||||
re.Ok, re.Msg = authService.Register(email, pwd)
|
||||
|
||||
re.Ok, re.Msg = authService.Register(email, pwd, "")
|
||||
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
@@ -53,14 +54,14 @@ func (c AdminUser) Register(email, pwd string) revel.Result {
|
||||
func (c AdminUser) ResetPwd(userId string) revel.Result {
|
||||
userInfo := userService.GetUserInfo(userId)
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
return c.RenderTemplate("admin/user/reset_pwd.html");
|
||||
return c.RenderTemplate("admin/user/reset_pwd.html")
|
||||
}
|
||||
|
||||
func (c AdminUser) DoResetPwd(userId, pwd string) revel.Result {
|
||||
re := info.NewRe();
|
||||
re := info.NewRe()
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderRe(re);
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
re.Ok, re.Msg = userService.ResetPwd(c.GetUserId(), userId, pwd)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/service"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
// "strings"
|
||||
// "strings"
|
||||
)
|
||||
|
||||
var userService *service.UserService
|
||||
@@ -19,9 +19,9 @@ var blogService *service.BlogService
|
||||
var tagService *service.TagService
|
||||
var pwdService *service.PwdService
|
||||
var tokenService *service.TokenService
|
||||
var suggestionService *service.SuggestionService
|
||||
var albumService *service.AlbumService
|
||||
var noteImageService *service.NoteImageService
|
||||
var suggestionService *service.SuggestionService
|
||||
var albumService *service.AlbumService
|
||||
var noteImageService *service.NoteImageService
|
||||
var fileService *service.FileService
|
||||
var attachService *service.AttachService
|
||||
var configService *service.ConfigService
|
||||
@@ -31,31 +31,32 @@ var upgradeService *service.UpgradeService
|
||||
// 拦截器
|
||||
// 不需要拦截的url
|
||||
// Index 除了Note之外都不需要
|
||||
var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": true,
|
||||
"Login": true,
|
||||
"DoLogin": true,
|
||||
"Logout": true,
|
||||
"Register": true,
|
||||
"DoRegister": true,
|
||||
"FindPasswword": true,
|
||||
"DoFindPassword": true,
|
||||
"FindPassword2": true,
|
||||
"FindPasswordUpdate": true,
|
||||
"Suggestion": true,
|
||||
},
|
||||
var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": true,
|
||||
"Login": true,
|
||||
"DoLogin": true,
|
||||
"Logout": true,
|
||||
"Register": true,
|
||||
"DoRegister": true,
|
||||
"FindPasswword": true,
|
||||
"DoFindPassword": true,
|
||||
"FindPassword2": true,
|
||||
"FindPasswordUpdate": true,
|
||||
"Suggestion": true,
|
||||
},
|
||||
"Blog": map[string]bool{"Index": true,
|
||||
"View": true,
|
||||
"AboutMe": true,
|
||||
"View": true,
|
||||
"AboutMe": true,
|
||||
"SearchBlog": true,
|
||||
},
|
||||
},
|
||||
// 用户的激活与修改邮箱都不需要登录, 通过链接地址
|
||||
"User": map[string]bool{"UpdateEmail": true,
|
||||
"ActiveEmail":true,
|
||||
},
|
||||
"Oauth": map[string]bool{"GithubCallback": true},
|
||||
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
|
||||
"ActiveEmail": true,
|
||||
},
|
||||
"Oauth": map[string]bool{"GithubCallback": true},
|
||||
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
|
||||
"Attach": map[string]bool{"Download": true, "DownloadAll": true},
|
||||
}
|
||||
|
||||
func needValidate(controller, method string) bool {
|
||||
// 在里面
|
||||
if v, ok := commonUrl[controller]; ok {
|
||||
@@ -66,33 +67,33 @@ func needValidate(controller, method string) bool {
|
||||
return true
|
||||
} else {
|
||||
// controller不在这里的, 肯定要验证
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
// 全部变成首字大写
|
||||
/*
|
||||
var controller = strings.Title(c.Name)
|
||||
var method = strings.Title(c.MethodName)
|
||||
// 是否需要验证?
|
||||
if !needValidate(controller, method) {
|
||||
return nil
|
||||
}
|
||||
var controller = strings.Title(c.Name)
|
||||
var method = strings.Title(c.MethodName)
|
||||
// 是否需要验证?
|
||||
if !needValidate(controller, method) {
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// 验证是否已登录
|
||||
// 必须是管理员
|
||||
if username, ok := c.Session["Username"]; ok && username == configService.GetAdminUsername() {
|
||||
return nil // 已登录
|
||||
}
|
||||
|
||||
|
||||
// 没有登录, 判断是否是ajax操作
|
||||
if c.Request.Header.Get("X-Requested-With") == "XMLHttpRequest" {
|
||||
re := info.NewRe()
|
||||
re.Msg = "NOTLOGIN"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
return c.Redirect("/login")
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ func InitService() {
|
||||
tokenService = service.TokenS
|
||||
noteImageService = service.NoteImageS
|
||||
fileService = service.FileS
|
||||
albumService= service.AlbumS
|
||||
albumService = service.AlbumS
|
||||
attachService = service.AttachS
|
||||
pwdService = service.PwdS
|
||||
suggestionService = service.SuggestionS
|
||||
|
||||
465
app/controllers/api/API列表-v0.1.md
Normal file
465
app/controllers/api/API列表-v0.1.md
Normal file
@@ -0,0 +1,465 @@
|
||||
# API 列表
|
||||
|
||||
## 前言
|
||||
|
||||
### api url
|
||||
|
||||
所有api的url前面带/api/, 如:
|
||||
|
||||
`/api/user/info?userId=xxxx&token=xxxx`
|
||||
|
||||
除了/auth/login, /auth/register外其它的都需要另外带参数token=xxxx
|
||||
|
||||
### 文件目录结构
|
||||
* 所有API的Controller都在app/api文件夹下
|
||||
* 文件命名: Api功能Controller.go, 如ApiUserController.go
|
||||
* 结构体命名为 Api功能, 如ApiUser
|
||||
* API公用Controller: ApiBaseController
|
||||
* init.go 注入service和定义拦截器
|
||||
|
||||
### 流程
|
||||
用户登录后返回一个token, 以后所有的请求都携带该token.
|
||||
在init.go中的拦截器会得到token并调用sessionService判断是否登录了
|
||||
|
||||
## 返回值结构
|
||||
* 全部返回JSON, JSON, 除二进制文件(图片, 附件外), 如果返回其它非JSON格式的值, 肯定是出错了
|
||||
* 错误信息全部返回 {Ok: false, Msg: "相应的错误信息"}
|
||||
* 正确信息返回分两种:
|
||||
1. 一些操作型的api, 比如updateUsername, updateLogo之类的, 成功后返回 {Ok: true, Msg:""}
|
||||
2. 一些获取型api, 如getNote, 全部返回真实的返回数据, 如返回笔记:
|
||||
```
|
||||
{
|
||||
"NoteId": "54bdc7e305fcd13ea3000000",
|
||||
"NotebookId": "54bdc65599c37b0da9000003",
|
||||
"UserId": "54bdc65599c37b0da9000002",
|
||||
"Title": "笔记标题",
|
||||
"Desc": "",
|
||||
"Tags": null,
|
||||
"Abstract": "",
|
||||
"Content": "",
|
||||
"IsMarkdown": false,
|
||||
"IsBlog": false,
|
||||
"IsTrash": true,
|
||||
"IsDeleted": false,
|
||||
"Usn": 15,
|
||||
"Files": [],
|
||||
"CreatedTime": "2015-01-20T11:13:41.34+08:00",
|
||||
"UpdatedTime": "2015-01-20T11:13:41.34+08:00",
|
||||
"PublicTime": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
```
|
||||
* 时间类型全是返回如 "2015-01-20T11:13:41.34+08:00" 的格式, (因为golang的time转成Json就是这样, 历史原因)
|
||||
|
||||
-----------------
|
||||
|
||||
## Auth 登录与注册
|
||||
|
||||
### /auth/login 登录
|
||||
|
||||
```
|
||||
参数: email, pwd
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {"Ok":false, "Msg":"用户名或密码有误"}
|
||||
正确: 比如:
|
||||
{
|
||||
"Ok":true,
|
||||
"Token":"5500830738f41138e90003232",
|
||||
"UserId":"52d26b4e99c37b609a000001",
|
||||
"Email":"leanote@leanote.com",
|
||||
"Username":"leanote"
|
||||
}
|
||||
```
|
||||
|
||||
登录成功后将使用token作为之后的请求
|
||||
|
||||
### /auth/logout 注销
|
||||
```
|
||||
参数: token
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
### /auth/register 注册
|
||||
```
|
||||
参数: email, pwd
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
## User 用户
|
||||
### /user/info 获取用户信息
|
||||
```
|
||||
参数: userId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.User
|
||||
```
|
||||
|
||||
### /user/updateUsername 修改用户名
|
||||
```
|
||||
参数: username(新用户名)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
### /user/updatePwd 修改密码
|
||||
```
|
||||
参数: oldPwd(旧密码), pwd(新密码)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
### /user/updateLogo 修改头像
|
||||
```
|
||||
参数: file(文件)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {Ok: true, Msg: ""}
|
||||
```
|
||||
|
||||
### /user/getSyncState 获取最新同步状态
|
||||
```
|
||||
参数: 无
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: {LastSyncUsn: 3232, LastSyncTime: "上次同步时间"(暂时无用)}
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## Notebook 笔记本
|
||||
|
||||
### /notebook/getSyncNotebooks 得到需要同步的笔记本
|
||||
```
|
||||
参数: afterUsn(int, 在此usn后的笔记本是需要同步的), maxEntry(int, 最大要同步的量)
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Notebook] 数组
|
||||
```
|
||||
|
||||
### /notebook/getNotebooks 得到所有笔记本
|
||||
```
|
||||
无参数
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Notebook] 数组
|
||||
```
|
||||
|
||||
### /notebook/addNotebook 添加笔记本
|
||||
```
|
||||
参数: title(string), parentNotebookId(string, 父notebookId, 可空), seq(int) 排列
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg:""}
|
||||
成功: type.Notebook
|
||||
```
|
||||
|
||||
### /notebook/updateNotebook 修改笔记本
|
||||
```
|
||||
参数: notebookId, title, parentNotebookId, seq(int), usn(int)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, msg: ""} msg == "conflict" 表示冲突
|
||||
成功: type.Notebook
|
||||
```
|
||||
|
||||
### /notebook/deleteNotebook 删除笔记本
|
||||
```
|
||||
参数: notebookId, usn(int)
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, msg: ""} msg == "conflict" 表示冲突
|
||||
成功: {Ok: true}
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## Note 笔记
|
||||
|
||||
|
||||
### /note/getSyncNotes 获取需要同步的笔记
|
||||
```
|
||||
参数: afterUsn(int, 在此usn后的笔记是需要同步的), maxEntry(int, 最大要同步的量)
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Note] 数组, 笔记不包含Abstract和Content
|
||||
```
|
||||
|
||||
|
||||
### /note/getNotes 获得某笔记本下的笔记(无内容)
|
||||
```
|
||||
参数: notebookId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Note] 数组, 笔记不包含Abstract和Content
|
||||
```
|
||||
|
||||
### /note/getNoteAndContent 获得笔记与内容
|
||||
```
|
||||
参数: noteId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.Note
|
||||
```
|
||||
|
||||
### /note/getNoteContent 获得笔记内容
|
||||
```
|
||||
参数: noteId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.NoteContent
|
||||
```
|
||||
|
||||
### /note/addNote 添加笔记
|
||||
```
|
||||
参数: (注意首字大写)
|
||||
NotebookId string 必传
|
||||
Title string 必传
|
||||
Tags []string 可选
|
||||
Content string 必传
|
||||
Abstract string 可选, 当是markdown笔记时必须传
|
||||
IsMarkdown bool 可选
|
||||
Files []type.NoteFiles 数组 可选
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.Note, 不包含Abstract, Content
|
||||
```
|
||||
|
||||
** 关于笔记中的图片/附件**
|
||||
客户端应该添加一个"图片/附件表"来存元数据, 图片应该要缓存到本地, 附件可在需要的时候再调用相应api获取.
|
||||
|
||||
Content中的数据, 图片,附件在Leanote的链接是, 所以, 不管你在本地的笔记中是以什么形式来保存图片,附件的链接的,请addNote和updateNote时务必将链接修改成leanote服务器上的链接.
|
||||
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, 和相关的图片/附件一起传到服务器中
|
||||
其中Files(文件的元数据)和其它字段以POST方式传出, 而真正数据则以http的multipart传入, 每个文件的name为"FileDatas[LocalFileId]"
|
||||
|
||||
图片在笔记内的链接必须为: http://leanote.com/api/file/getImage?fileId=LocalFileId或FileId
|
||||
附件如果插入到了笔记内容内, 其链接必须为: http://leanote.com/api/file/getAttach?fileId=LocalFileId或FileId
|
||||
其中, fileId为文件在本地的LocalFileId或服务器上的FileId
|
||||
|
||||
服务器端会生成FileId传给Client. Client在本地必须要建立LocalFileId与FileId的关联.
|
||||
|
||||
如果笔记内容传了, 且笔记内有图片, 则必须要传Files 文件元数据, 因为Server端还要对内容内的图片, 附件链接进行修改, 可能你传过去的是LocalFileId, 服务器端会将LocalFileId替换成FileId存到数据库中.
|
||||
|
||||
同样适用于 updateNote
|
||||
|
||||
http://leanote.com 不绝对, 因为用户可以自建服务, 所以在开发时需要可配置
|
||||
|
||||
### /note/updateNote 更新笔记
|
||||
|
||||
当更新了笔记某个属性时, 只要传某个属性就行, 其它不用传, 比如把笔记拉入了trash, 那么就传IsTrash: true
|
||||
|
||||
```
|
||||
参数: (注意首字大写)
|
||||
NoteId string 必传
|
||||
Usn int 必传
|
||||
NotebookId string 可选
|
||||
Title string 可选
|
||||
Tags []string 可选
|
||||
Content string 可选
|
||||
Abstract string 可选, 当是markdown笔记时必须传
|
||||
IsMarkdown bool 可选
|
||||
IsTrash bool 是否是trash 可选
|
||||
Files []type.NoteFiles 数组 可选
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, msg: ''} msg == 'conflict' 表示冲突
|
||||
成功: type.Note, 不包含Abstract和Content
|
||||
```
|
||||
|
||||
### /note/deleteTrash 彻底删除笔记
|
||||
```
|
||||
参数: noteId, usn
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, msg: ''} msg == 'conflict' 表示冲突
|
||||
成功: type.UpdateRet
|
||||
```
|
||||
|
||||
-------
|
||||
|
||||
## Tag 标签
|
||||
|
||||
### /tag/getSyncTags 获取需要同步的标签
|
||||
```
|
||||
参数: afterUsn(int, 在此usn后的标签是需要同步的), maxEntry(int, 最大要同步的量)
|
||||
Method: GET
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: [type.Tag] 数组
|
||||
```
|
||||
|
||||
### /tag/addTag 添加标签
|
||||
```
|
||||
参数: tag(string)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.Tag
|
||||
```
|
||||
|
||||
### /tag/deleteTag 删除标签
|
||||
```
|
||||
参数: tag(string)
|
||||
Method: POST
|
||||
返回:
|
||||
错误: {Ok: false, Msg: ""}
|
||||
成功: type.UpdateRet
|
||||
```
|
||||
|
||||
### File 文件(获取图片, 附件)
|
||||
|
||||
### /file/getImage 获取图片
|
||||
```
|
||||
参数: fileId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: 非二进制文件数据
|
||||
成功: 二进制文件
|
||||
```
|
||||
|
||||
### /file/getAttach 获取附件
|
||||
```
|
||||
参数: fileId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: 非二进制文件数据
|
||||
成功: 二进制文件
|
||||
```
|
||||
|
||||
### /file/getAllAttachs 获取所有附件
|
||||
```
|
||||
参数: noteId
|
||||
Method: GET
|
||||
返回:
|
||||
错误: 非二进制文件数据
|
||||
成功: 二进制文件
|
||||
```
|
||||
|
||||
--------
|
||||
|
||||
## 数据类型
|
||||
|
||||
### type.User 用户信息
|
||||
|
||||
```
|
||||
User {
|
||||
UserId string
|
||||
Username string
|
||||
Email string
|
||||
Verified bool
|
||||
Logo string
|
||||
}
|
||||
```
|
||||
|
||||
### type.Notebook 笔记本
|
||||
|
||||
```
|
||||
Notebook {
|
||||
NotebookId
|
||||
UserId
|
||||
ParentNotebookId // 上级
|
||||
Seq int // 排序
|
||||
Title string
|
||||
IsBlog bool
|
||||
IsDeleted bool
|
||||
CreatedTime time.Time
|
||||
UpdatedTime time.Time
|
||||
|
||||
// 更新序号
|
||||
Usn int // UpdateSequenceNum
|
||||
}
|
||||
```
|
||||
|
||||
### type.Note 笔记
|
||||
```
|
||||
Note {
|
||||
NoteId string
|
||||
NotebookId string
|
||||
UserId string
|
||||
Title string
|
||||
Tags []string
|
||||
Content string
|
||||
IsMarkdown bool
|
||||
IsBlog bool
|
||||
IsTrash bool
|
||||
Files []NoteFile // 图片, 附件
|
||||
CreatedTime time.Time
|
||||
UpdatedTime time.Time
|
||||
PublicTime time.Time
|
||||
|
||||
// 更新序号
|
||||
Usn int
|
||||
}
|
||||
```
|
||||
|
||||
### type.NoteContent 笔记内容
|
||||
```
|
||||
NoteContent {
|
||||
NoteId string
|
||||
UserId string
|
||||
Content string
|
||||
}
|
||||
```
|
||||
|
||||
### type.NoteFile 笔记文件(图片,附件)
|
||||
```
|
||||
NoteFile {
|
||||
FileId string // 服务器端Id
|
||||
LocalFileId string // 客户端Id
|
||||
Type string // images/png, doc, xls, 根据fileName确定
|
||||
Title string
|
||||
HasBody bool // 传过来的值是否要更新内容, 如果有true, 则必须传文件
|
||||
IsAttach bool // 是否是附件, 不是附件就是图片
|
||||
}
|
||||
```
|
||||
|
||||
### type.Tag 标签
|
||||
```
|
||||
Tag {
|
||||
TagId string
|
||||
UserId string
|
||||
Tag string
|
||||
CreatedTime
|
||||
UpdatedTime
|
||||
IsDeleted bool // 删除位
|
||||
// 更新序号
|
||||
Usn
|
||||
}
|
||||
```
|
||||
|
||||
### type.UpdateRe 更新后返回的值, 包含Usn
|
||||
```
|
||||
ReUpdate {
|
||||
Ok bool
|
||||
Msg string
|
||||
|
||||
// 更新序号
|
||||
Usn int
|
||||
}
|
||||
```
|
||||
47
app/controllers/api/API设计-v0.1.md
Normal file
47
app/controllers/api/API设计-v0.1.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# API设计
|
||||
|
||||
By life (life@leanote.com)
|
||||
|
||||
## api url
|
||||
|
||||
所有api的url前面带/api/, 如:
|
||||
|
||||
`/api/user/info?userId=xxxx&token=xxxx`
|
||||
|
||||
## 文件目录结构
|
||||
* 所有API的Controller都在app/api文件夹下
|
||||
* 文件命名: Api功能Controller.go, 如ApiUserController.go
|
||||
* 结构体命名为 Api功能, 如ApiUser
|
||||
* API公用Controller: ApiBaseController
|
||||
* init.go 注入service和定义拦截器
|
||||
|
||||
## 流程
|
||||
用户登录后返回一个token, 以后所有的请求都携带该token.
|
||||
在init.go中的拦截器会得到token并调用sessionService判断是否登录了
|
||||
|
||||
## 返回值结构
|
||||
* 全部返回JSON, JSON, 除二进制文件(图片, 附件外), 如果返回其它非JSON格式的值, 肯定是出错了
|
||||
* 错误信息全部返回 {Ok: false, Msg: ""}
|
||||
* 正确信息全部返回真实的返回数据, 如返回笔记:
|
||||
```
|
||||
{
|
||||
"NoteId": "54bdc7e305fcd13ea3000000",
|
||||
"NotebookId": "54bdc65599c37b0da9000003",
|
||||
"UserId": "54bdc65599c37b0da9000002",
|
||||
"Title": "asdfads",
|
||||
"Desc": "",
|
||||
"Tags": null,
|
||||
"Abstract": "",
|
||||
"Content": "",
|
||||
"IsMarkdown": false,
|
||||
"IsBlog": false,
|
||||
"IsTrash": true,
|
||||
"IsDeleted": false,
|
||||
"Usn": 15,
|
||||
"Files": [],
|
||||
"CreatedTime": "2015-01-20T11:13:41.34+08:00",
|
||||
"UpdatedTime": "2015-01-20T11:13:41.34+08:00",
|
||||
"PublicTime": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
```
|
||||
* 时间类型全是返回如 "2015-01-20T11:13:41.34+08:00" 的格式, (因为golang的time转成Json就是这样, 历史原因)
|
||||
69
app/controllers/api/ApiAuthController.go
Normal file
69
app/controllers/api/ApiAuthController.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "strconv"
|
||||
)
|
||||
|
||||
// 用户登录后生成一个token, 将这个token保存到session中
|
||||
// 以后每次的请求必须带这个token, 并从session中获取userId
|
||||
|
||||
// 用户登录/注销/找回密码
|
||||
|
||||
type ApiAuth struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 登录
|
||||
// [ok]
|
||||
// 成功返回 {Ok: true, Item: token }
|
||||
// 失败返回 {Ok: false, Msg: ""}
|
||||
func (c ApiAuth) Login(email, pwd string) revel.Result {
|
||||
var msg = ""
|
||||
|
||||
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})
|
||||
} else {
|
||||
// 登录错误, 则错误次数++
|
||||
msg = "wrongUsernameOrPassword"
|
||||
}
|
||||
return c.RenderJson(info.ApiRe{Ok: false, Msg: c.Message(msg)})
|
||||
}
|
||||
|
||||
// 注销
|
||||
// [Ok]
|
||||
func (c ApiAuth) Logout() revel.Result {
|
||||
token := c.getToken()
|
||||
sessionService.Clear(token)
|
||||
re := info.NewApiRe()
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 注册
|
||||
// [Ok]
|
||||
// 成功后并不返回用户ID, 需要用户重新登录
|
||||
func (c ApiAuth) Register(email, pwd string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if !configService.IsOpenRegister() {
|
||||
re.Msg = "notOpenRegister" // 未开放注册
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 注册
|
||||
re.Ok, re.Msg = authService.Register(email, pwd, "")
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
189
app/controllers/api/ApiBaseController.go
Normal file
189
app/controllers/api/ApiBaseController.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"os"
|
||||
// "fmt"
|
||||
"io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 公用Controller, 其它Controller继承它
|
||||
type ApiBaseContrller struct {
|
||||
controllers.BaseController // 不能用*BaseController
|
||||
}
|
||||
|
||||
// 得到token, 这个token是在AuthInterceptor设到Session中的
|
||||
func (c ApiBaseContrller) getToken() string {
|
||||
return c.Session["_token"]
|
||||
}
|
||||
|
||||
// userId
|
||||
// _userId是在AuthInterceptor设置的
|
||||
func (c ApiBaseContrller) getUserId() string {
|
||||
return c.Session["_userId"]
|
||||
}
|
||||
|
||||
// 得到用户信息
|
||||
func (c ApiBaseContrller) getUserInfo() info.User {
|
||||
userId := c.Session["_userId"]
|
||||
if userId == "" {
|
||||
return info.User{}
|
||||
}
|
||||
return userService.GetUserInfo(userId)
|
||||
}
|
||||
|
||||
// 上传附件
|
||||
func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg string, id string) {
|
||||
userId := c.getUserId()
|
||||
|
||||
// 判断是否有权限为笔记添加附件
|
||||
// 如果笔记还没有添加是不是会有问题
|
||||
/*
|
||||
if !shareService.HasUpdateNotePerm(noteId, userId) {
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
file, handel, err := c.Request.FormFile(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// > 5M?
|
||||
maxFileSize := configService.GetUploadSize("uploadAttachSize")
|
||||
if maxFileSize <= 0 {
|
||||
maxFileSize = 1000
|
||||
}
|
||||
if float64(len(data)) > maxFileSize*float64(1024*1024) {
|
||||
msg = "fileIsTooLarge"
|
||||
return
|
||||
}
|
||||
|
||||
// 生成上传路径
|
||||
newGuid := NewGuid()
|
||||
// filePath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/attachs"
|
||||
filePath := "files/" + GetRandomFilePath(userId, newGuid) + "/attachs"
|
||||
|
||||
dir := revel.BasePath + "/" + filePath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
_, ext := SplitFilename(filename) // .doc
|
||||
filename = newGuid + ext
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// add File to db
|
||||
fileType := ""
|
||||
if ext != "" {
|
||||
fileType = strings.ToLower(ext[1:])
|
||||
}
|
||||
filesize := GetFilesize(toPath)
|
||||
fileInfo := info.Attach{AttachId: bson.NewObjectId(),
|
||||
Name: filename,
|
||||
Title: handel.Filename,
|
||||
NoteId: bson.ObjectIdHex(noteId),
|
||||
UploadUserId: bson.ObjectIdHex(userId),
|
||||
Path: filePath + "/" + filename,
|
||||
Type: fileType,
|
||||
Size: filesize}
|
||||
|
||||
ok, msg = attachService.AddAttach(fileInfo, true)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id = fileInfo.AttachId.Hex()
|
||||
return
|
||||
}
|
||||
|
||||
// 上传图片
|
||||
func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok bool, msg string, id string) {
|
||||
if isAttach {
|
||||
return c.uploadAttach(name, noteId)
|
||||
}
|
||||
file, handel, err := c.Request.FormFile(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
newGuid := NewGuid()
|
||||
// 生成上传路径
|
||||
userId := c.getUserId()
|
||||
// fileUrlPath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
|
||||
fileUrlPath := "files/" + GetRandomFilePath(userId, newGuid) + "/images"
|
||||
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
_, ext := SplitFilename(filename)
|
||||
if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
|
||||
msg = "notImage"
|
||||
return
|
||||
}
|
||||
|
||||
filename = newGuid + ext
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
maxFileSize := configService.GetUploadSize("uploadImageSize")
|
||||
if maxFileSize <= 0 {
|
||||
maxFileSize = 1000
|
||||
}
|
||||
|
||||
// > 2M?
|
||||
if float64(len(data)) > maxFileSize*float64(1024*1024) {
|
||||
msg = "fileIsTooLarge"
|
||||
return
|
||||
}
|
||||
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 改变成gif图片
|
||||
_, toPathGif := TransToGif(toPath, 0, true)
|
||||
filename = GetFilename(toPathGif)
|
||||
filesize := GetFilesize(toPathGif)
|
||||
fileUrlPath += "/" + filename
|
||||
|
||||
// File
|
||||
fileInfo := info.File{FileId: bson.NewObjectId(),
|
||||
Name: filename,
|
||||
Title: handel.Filename,
|
||||
Path: fileUrlPath,
|
||||
Size: filesize}
|
||||
ok, msg = fileService.AddImage(fileInfo, "", c.getUserId(), true)
|
||||
if ok {
|
||||
id = fileInfo.FileId.Hex()
|
||||
}
|
||||
return
|
||||
}
|
||||
163
app/controllers/api/ApiFileController.go
Normal file
163
app/controllers/api/ApiFileController.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "github.com/leanote/leanote/app/lea/netutil"
|
||||
// "github.com/leanote/leanote/app/info"
|
||||
// "io/ioutil"
|
||||
"os"
|
||||
// "strconv"
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 文件操作, 图片, 头像上传, 输出
|
||||
|
||||
type ApiFile struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
/*
|
||||
// 协作时复制图片到owner
|
||||
func (c ApiFile) CopyImage(userId, fileId, toUserId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
re.Ok, re.Id = fileService.CopyImage(userId, fileId, toUserId)
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// get all images by userId with page
|
||||
func (c ApiFile) GetImages(albumId, key string, page int) revel.Result {
|
||||
imagesPage := fileService.ListImagesWithPage(c.getUserId(), albumId, key, page, 12)
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.Item = imagesPage
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c ApiFile) UpdateImageTitle(fileId, title string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = fileService.UpdateImageTitle(c.getUserId(), fileId, title)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c ApiFile) DeleteImage(fileId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = fileService.DeleteImage(c.getUserId(), fileId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
//-----------
|
||||
|
||||
// 输出image
|
||||
// [OK]
|
||||
func (c ApiFile) GetImage(fileId string) revel.Result {
|
||||
path := fileService.GetFile(c.getUserId(), fileId) // 得到路径
|
||||
if path == "" {
|
||||
return c.RenderText("")
|
||||
}
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
file, _ := os.Open(fn)
|
||||
return c.RenderFile(file, revel.Inline) // revel.Attachment
|
||||
}
|
||||
|
||||
// 下载附件
|
||||
// [OK]
|
||||
func (c ApiFile) GetAttach(fileId string) revel.Result {
|
||||
attach := attachService.GetAttach(fileId, c.getUserId()) // 得到路径
|
||||
path := attach.Path
|
||||
if path == "" {
|
||||
return c.RenderText("No Such File")
|
||||
}
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
file, _ := os.Open(fn)
|
||||
return c.RenderBinary(file, attach.Title, revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
|
||||
// 下载所有附件
|
||||
// [OK]
|
||||
func (c ApiFile) GetAllAttachs(noteId string) revel.Result {
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
return c.RenderText("")
|
||||
}
|
||||
// 得到文件列表
|
||||
attachs := attachService.ListAttachs(noteId, c.getUserId())
|
||||
if attachs == nil || len(attachs) == 0 {
|
||||
return c.RenderText("")
|
||||
}
|
||||
|
||||
/*
|
||||
dir := revel.BasePath + "/files/tmp"
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
*/
|
||||
|
||||
filename := note.Title + ".tar.gz"
|
||||
if note.Title == "" {
|
||||
filename = "all.tar.gz"
|
||||
}
|
||||
|
||||
// file write
|
||||
fw, err := os.Create(revel.BasePath + "/files/" + filename)
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
|
||||
|
||||
// gzip write
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
|
||||
// tar write
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
// 遍历文件列表
|
||||
for _, attach := range attachs {
|
||||
fn := revel.BasePath + "/" + strings.TrimLeft(attach.Path, "/")
|
||||
fr, err := os.Open(fn)
|
||||
fileInfo, _ := fr.Stat()
|
||||
if err != nil {
|
||||
return c.RenderText("")
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// 信息头
|
||||
h := new(tar.Header)
|
||||
h.Name = attach.Title
|
||||
h.Size = fileInfo.Size()
|
||||
h.Mode = int64(fileInfo.Mode())
|
||||
h.ModTime = fileInfo.ModTime()
|
||||
|
||||
// 写信息头
|
||||
err = tw.WriteHeader(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 写文件
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} // for
|
||||
|
||||
// tw.Close()
|
||||
// gw.Close()
|
||||
// fw.Close()
|
||||
// file, _ := os.Open(dir + "/" + filename)
|
||||
// fw.Seek(0, 0)
|
||||
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
642
app/controllers/api/ApiNoteController.go
Normal file
642
app/controllers/api/ApiNoteController.go
Normal file
@@ -0,0 +1,642 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"os"
|
||||
"os/exec"
|
||||
// "strings"
|
||||
"time"
|
||||
"regexp"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "bytes"
|
||||
// "os"
|
||||
)
|
||||
|
||||
// 笔记API
|
||||
|
||||
type ApiNote struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 获取同步的笔记
|
||||
// > afterUsn的笔记
|
||||
// 无Desc, Abstract, 有Files
|
||||
/*
|
||||
{
|
||||
"NoteId": "55195fa199c37b79be000005",
|
||||
"NotebookId": "55195fa199c37b79be000002",
|
||||
"UserId": "55195fa199c37b79be000001",
|
||||
"Title": "Leanote语法Leanote语法Leanote语法Leanote语法Leanote语法",
|
||||
"Desc": "",
|
||||
"Tags": null,
|
||||
"Abstract": "",
|
||||
"Content": "",
|
||||
"IsMarkdown": true,
|
||||
"IsBlog": false,
|
||||
"IsTrash": false,
|
||||
"Usn": 5,
|
||||
"Files": [],
|
||||
"CreatedTime": "2015-03-30T22:37:21.695+08:00",
|
||||
"UpdatedTime": "2015-03-30T22:37:21.724+08:00",
|
||||
"PublicTime": "2015-03-30T22:37:21.695+08:00"
|
||||
}
|
||||
*/
|
||||
func (c ApiNote) GetSyncNotes(afterUsn, maxEntry int) revel.Result {
|
||||
if maxEntry == 0 {
|
||||
maxEntry = 100
|
||||
}
|
||||
notes := noteService.GetSyncNotes(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(notes)
|
||||
}
|
||||
|
||||
// 得到笔记本下的笔记
|
||||
// [OK]
|
||||
func (c ApiNote) GetNotes(notebookId string) revel.Result {
|
||||
if notebookId != "" && !bson.IsObjectIdHex(notebookId) {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "notebookIdInvalid"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
_, notes := noteService.ListNotes(c.getUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(noteService.ToApiNotes(notes))
|
||||
}
|
||||
|
||||
// 得到trash
|
||||
// [OK]
|
||||
func (c ApiNote) GetTrashNotes() revel.Result {
|
||||
_, notes := noteService.ListNotes(c.getUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(noteService.ToApiNotes(notes))
|
||||
|
||||
}
|
||||
|
||||
// get Note
|
||||
// [OK]
|
||||
/*
|
||||
{
|
||||
"NoteId": "550c0bee2ec82a2eb5000000",
|
||||
"NotebookId": "54a1676399c37b1c77000004",
|
||||
"UserId": "54a1676399c37b1c77000002",
|
||||
"Title": "asdfadsf--=",
|
||||
"Desc": "",
|
||||
"Tags": [
|
||||
],
|
||||
"Abstract": "",
|
||||
"Content": "",
|
||||
"IsMarkdown": false,
|
||||
"IsBlog": false,
|
||||
"IsTrash": false,
|
||||
"Usn": 8,
|
||||
"Files": [
|
||||
{
|
||||
"FileId": "551975d599c37b970f000000",
|
||||
"LocalFileId": "",
|
||||
"Type": "",
|
||||
"Title": "",
|
||||
"HasBody": false,
|
||||
"IsAttach": false
|
||||
},
|
||||
{
|
||||
"FileId": "551975de99c37b970f000001",
|
||||
"LocalFileId": "",
|
||||
"Type": "doc",
|
||||
"Title": "李铁-print-en.doc",
|
||||
"HasBody": false,
|
||||
"IsAttach": true
|
||||
},
|
||||
{
|
||||
"FileId": "551975de99c37b970f000002",
|
||||
"LocalFileId": "",
|
||||
"Type": "doc",
|
||||
"Title": "李铁-print.doc",
|
||||
"HasBody": false,
|
||||
"IsAttach": true
|
||||
}
|
||||
],
|
||||
"CreatedTime": "2015-03-20T20:00:52.463+08:00",
|
||||
"UpdatedTime": "2015-03-31T00:12:44.967+08:00",
|
||||
"PublicTime": "2015-03-20T20:00:52.463+08:00"
|
||||
}
|
||||
*/
|
||||
func (c ApiNote) GetNote(noteId string) revel.Result {
|
||||
if !bson.IsObjectIdHex(noteId) {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "noteIdInvalid"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
note := noteService.GetNote(noteId, c.getUserId())
|
||||
if note.NoteId == "" {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "notExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
apiNotes := noteService.ToApiNotes([]info.Note{note})
|
||||
return c.RenderJson(apiNotes[0])
|
||||
}
|
||||
|
||||
// 得到note和内容
|
||||
// [OK]
|
||||
func (c ApiNote) GetNoteAndContent(noteId string) revel.Result {
|
||||
noteAndContent := noteService.GetNoteAndContent(noteId, c.getUserId())
|
||||
|
||||
apiNotes := noteService.ToApiNotes([]info.Note{noteAndContent.Note})
|
||||
apiNote := apiNotes[0]
|
||||
apiNote.Content = noteService.FixContent(noteAndContent.Content, noteAndContent.IsMarkdown)
|
||||
return c.RenderJson(apiNote)
|
||||
}
|
||||
|
||||
// content里的image, attach链接是
|
||||
// https://leanote.com/api/file/getImage?fileId=xx
|
||||
// https://leanote.com/api/file/getAttach?fileId=xx
|
||||
// 将fileId=映射成ServerFileId, 这里的fileId可能是本地的FileId
|
||||
func (c ApiNote) fixPostNotecontent(noteOrContent *info.ApiNote) {
|
||||
if noteOrContent.Content == "" {
|
||||
return
|
||||
}
|
||||
|
||||
files := noteOrContent.Files
|
||||
if files != nil && len(files) > 0 {
|
||||
for _, file := range files {
|
||||
if file.LocalFileId != "" {
|
||||
LogJ(file)
|
||||
if !file.IsAttach {
|
||||
// <img src="https://"
|
||||
// 
|
||||
reg, _ := regexp.Compile(`https*://[^/]*?/api/file/getImage\?fileId=`+file.LocalFileId)
|
||||
// Log(reg)
|
||||
noteOrContent.Content = reg.ReplaceAllString(noteOrContent.Content, `/api/file/getImage?fileId=`+file.FileId)
|
||||
|
||||
// // "http://a.com/api/file/getImage?fileId=localId" => /api/file/getImage?fileId=serverId
|
||||
// noteOrContent.Content = strings.Replace(noteOrContent.Content,
|
||||
// baseUrl + "/api/file/getImage?fileId="+file.LocalFileId,
|
||||
// "/api/file/getImage?fileId="+file.FileId, -1)
|
||||
} else {
|
||||
reg, _ := regexp.Compile(`https*://[^/]*?/api/file/getAttach\?fileId=`+file.LocalFileId)
|
||||
Log(reg)
|
||||
noteOrContent.Content = reg.ReplaceAllString(noteOrContent.Content, `/api/file/getAttach?fileId=`+file.FileId)
|
||||
/*
|
||||
noteOrContent.Content = strings.Replace(noteOrContent.Content,
|
||||
baseUrl + "/api/file/getAttach?fileId="+file.LocalFileId,
|
||||
"/api/file/getAttach?fileId="+file.FileId, -1)
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 得到内容
|
||||
func (c ApiNote) GetNoteContent(noteId string) revel.Result {
|
||||
userId := c.getUserId()
|
||||
note := noteService.GetNote(noteId, userId)
|
||||
// re := info.NewRe()
|
||||
noteContent := noteService.GetNoteContent(noteId, userId)
|
||||
if noteContent.Content != "" {
|
||||
noteContent.Content = noteService.FixContent(noteContent.Content, note.IsMarkdown)
|
||||
}
|
||||
|
||||
apiNoteContent := info.ApiNoteContent{
|
||||
NoteId: noteContent.NoteId,
|
||||
UserId: noteContent.UserId,
|
||||
Content: noteContent.Content,
|
||||
}
|
||||
|
||||
return c.RenderJson(apiNoteContent)
|
||||
}
|
||||
|
||||
// 添加笔记
|
||||
// [OK]
|
||||
func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
|
||||
userId := bson.ObjectIdHex(c.getUserId())
|
||||
re := info.NewRe()
|
||||
myUserId := userId
|
||||
// 为共享新建?
|
||||
/*
|
||||
if noteOrContent.FromUserId != "" {
|
||||
userId = bson.ObjectIdHex(noteOrContent.FromUserId)
|
||||
}
|
||||
*/
|
||||
// Log(noteOrContent.Title)
|
||||
// LogJ(noteOrContent)
|
||||
|
||||
/*
|
||||
LogJ(c.Params)
|
||||
for name, _ := range c.Params.Files {
|
||||
Log(name)
|
||||
file, _, _ := c.Request.FormFile(name)
|
||||
LogJ(file)
|
||||
}
|
||||
*/
|
||||
// return c.RenderJson(re)
|
||||
if noteOrContent.NotebookId == "" || !bson.IsObjectIdHex(noteOrContent.NotebookId) {
|
||||
re.Msg = "notebookIdNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
noteId := bson.NewObjectId()
|
||||
// TODO 先上传图片/附件, 如果不成功, 则返回false
|
||||
//
|
||||
attachNum := 0
|
||||
if noteOrContent.Files != nil && len(noteOrContent.Files) > 0 {
|
||||
for i, file := range noteOrContent.Files {
|
||||
if file.HasBody {
|
||||
if file.LocalFileId != "" {
|
||||
// FileDatas[54c7ae27d98d0329dd000000]
|
||||
ok, msg, fileId := c.upload("FileDatas["+file.LocalFileId+"]", noteId.Hex(), file.IsAttach)
|
||||
|
||||
if !ok {
|
||||
re.Ok = false
|
||||
if msg != "" {
|
||||
Log(msg)
|
||||
Log(file.LocalFileId)
|
||||
re.Msg = "fileUploadError"
|
||||
}
|
||||
// 报不是图片的错误没关系, 证明客户端传来非图片的数据
|
||||
if msg != "notImage" {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
} else {
|
||||
// 建立映射
|
||||
file.FileId = fileId
|
||||
noteOrContent.Files[i] = file
|
||||
|
||||
if file.IsAttach {
|
||||
attachNum++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.fixPostNotecontent(¬eOrContent)
|
||||
|
||||
// Log("Add")
|
||||
// LogJ(noteOrContent)
|
||||
|
||||
// return c.RenderJson(re)
|
||||
|
||||
note := info.Note{UserId: userId,
|
||||
NoteId: noteId,
|
||||
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
|
||||
Title: noteOrContent.Title,
|
||||
Tags: noteOrContent.Tags,
|
||||
Desc: noteOrContent.Desc,
|
||||
// ImgSrc: noteOrContent.ImgSrc,
|
||||
IsBlog: noteOrContent.IsBlog,
|
||||
IsMarkdown: noteOrContent.IsMarkdown,
|
||||
AttachNum: attachNum,
|
||||
CreatedTime: noteOrContent.CreatedTime,
|
||||
UpdatedTime: noteOrContent.UpdatedTime,
|
||||
}
|
||||
noteContent := info.NoteContent{NoteId: note.NoteId,
|
||||
UserId: userId,
|
||||
IsBlog: note.IsBlog,
|
||||
Content: noteOrContent.Content,
|
||||
Abstract: noteOrContent.Abstract,
|
||||
CreatedTime: noteOrContent.CreatedTime,
|
||||
UpdatedTime: noteOrContent.UpdatedTime,
|
||||
}
|
||||
|
||||
// 通过内容得到Desc, abstract
|
||||
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)
|
||||
|
||||
if note.NoteId == "" {
|
||||
re.Ok = false
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 添加需要返回的
|
||||
noteOrContent.NoteId = note.NoteId.Hex()
|
||||
noteOrContent.Usn = note.Usn
|
||||
noteOrContent.CreatedTime = note.CreatedTime
|
||||
noteOrContent.UpdatedTime = note.UpdatedTime
|
||||
noteOrContent.UserId = c.getUserId()
|
||||
noteOrContent.IsMarkdown = note.IsMarkdown
|
||||
// 删除一些不要返回的, 删除Desc?
|
||||
noteOrContent.Content = ""
|
||||
noteOrContent.Abstract = ""
|
||||
// apiNote := info.NoteToApiNote(note, noteOrContent.Files)
|
||||
return c.RenderJson(noteOrContent)
|
||||
}
|
||||
|
||||
// 更新笔记
|
||||
// [OK]
|
||||
func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
|
||||
re := info.NewReUpdate()
|
||||
|
||||
noteUpdate := bson.M{}
|
||||
needUpdateNote := false
|
||||
|
||||
noteId := noteOrContent.NoteId
|
||||
|
||||
if noteOrContent.NoteId == "" {
|
||||
re.Msg = "noteIdNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if noteOrContent.Usn <= 0 {
|
||||
re.Msg = "usnNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// Log("_____________")
|
||||
// LogJ(noteOrContent)
|
||||
/*
|
||||
LogJ(c.Params.Files)
|
||||
LogJ(c.Request.Header)
|
||||
LogJ(c.Params.Values)
|
||||
*/
|
||||
|
||||
// 先判断USN的问题, 因为很可能添加完附件后, 会有USN冲突, 这时附件就添错了
|
||||
userId := c.getUserId()
|
||||
note := noteService.GetNote(noteId, userId)
|
||||
if note.NoteId == "" {
|
||||
re.Msg = "notExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
if note.Usn != noteOrContent.Usn {
|
||||
re.Msg = "conflict"
|
||||
Log("conflict")
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
Log("没有冲突")
|
||||
|
||||
// 如果传了files
|
||||
// TODO 测试
|
||||
/*
|
||||
for key, v := range c.Params.Values {
|
||||
Log(key)
|
||||
Log(v)
|
||||
}
|
||||
*/
|
||||
// Log(c.Has("Files[0]"))
|
||||
if c.Has("Files[0][LocalFileId]") {
|
||||
// LogJ(c.Params.Files)
|
||||
if noteOrContent.Files != nil && len(noteOrContent.Files) > 0 {
|
||||
for i, file := range noteOrContent.Files {
|
||||
if file.HasBody {
|
||||
if file.LocalFileId != "" {
|
||||
// FileDatas[54c7ae27d98d0329dd000000]
|
||||
ok, msg, fileId := c.upload("FileDatas["+file.LocalFileId+"]", noteId, file.IsAttach)
|
||||
if !ok {
|
||||
Log("upload file error")
|
||||
re.Ok = false
|
||||
if msg == "" {
|
||||
re.Msg = "fileUploadError"
|
||||
} else {
|
||||
re.Msg = msg
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
} else {
|
||||
// 建立映射
|
||||
file.FileId = fileId
|
||||
noteOrContent.Files[i] = file
|
||||
}
|
||||
} else {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log("after upload")
|
||||
// LogJ(noteOrContent.Files)
|
||||
|
||||
}
|
||||
|
||||
// 移到外面来, 删除最后一个file时也要处理, 不然总删不掉
|
||||
// 附件问题, 根据Files, 有些要删除的, 只留下这些
|
||||
attachService.UpdateOrDeleteAttachApi(noteId, userId, noteOrContent.Files)
|
||||
|
||||
// Desc前台传来
|
||||
if c.Has("Desc") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Desc"] = noteOrContent.Desc
|
||||
}
|
||||
/*
|
||||
if c.Has("ImgSrc") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["ImgSrc"] = noteOrContent.ImgSrc
|
||||
}
|
||||
*/
|
||||
if c.Has("Title") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Title"] = noteOrContent.Title
|
||||
}
|
||||
if c.Has("IsTrash") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["IsTrash"] = noteOrContent.IsTrash
|
||||
}
|
||||
|
||||
// 是否是博客
|
||||
if c.Has("IsBlog") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["IsBlog"] = noteOrContent.IsBlog
|
||||
}
|
||||
|
||||
/*
|
||||
Log(c.Has("tags[0]"))
|
||||
Log(c.Has("Tags[]"))
|
||||
for key, v := range c.Params.Values {
|
||||
Log(key)
|
||||
Log(v)
|
||||
}
|
||||
*/
|
||||
|
||||
if c.Has("Tags[0]") {
|
||||
needUpdateNote = true
|
||||
noteUpdate["Tags"] = noteOrContent.Tags
|
||||
}
|
||||
|
||||
if c.Has("NotebookId") {
|
||||
if bson.IsObjectIdHex(noteOrContent.NotebookId) {
|
||||
needUpdateNote = true
|
||||
noteUpdate["NotebookId"] = bson.ObjectIdHex(noteOrContent.NotebookId)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Has("Content") {
|
||||
// 通过内容得到Desc, 如果有Abstract, 则用Abstract生成Desc
|
||||
if noteOrContent.Abstract == "" {
|
||||
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Content, 200)
|
||||
} else {
|
||||
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Abstract, 200)
|
||||
}
|
||||
}
|
||||
|
||||
noteUpdate["UpdatedTime"] = noteOrContent.UpdatedTime
|
||||
|
||||
afterNoteUsn := 0
|
||||
noteOk := false
|
||||
noteMsg := ""
|
||||
if needUpdateNote {
|
||||
noteOk, noteMsg, afterNoteUsn = noteService.UpdateNote(c.getUserId(), noteOrContent.NoteId, noteUpdate, noteOrContent.Usn)
|
||||
if !noteOk {
|
||||
re.Ok = false
|
||||
re.Msg = noteMsg
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
//-------------
|
||||
afterContentUsn := 0
|
||||
contentOk := false
|
||||
contentMsg := ""
|
||||
if c.Has("Content") {
|
||||
// 把fileId替换下
|
||||
c.fixPostNotecontent(¬eOrContent)
|
||||
// 如果传了Abstract就用之
|
||||
if noteOrContent.Abstract == "" {
|
||||
noteOrContent.Abstract = SubStringHTML(noteOrContent.Content, 200, "")
|
||||
}
|
||||
|
||||
// Log("--------> afte fixed")
|
||||
// Log(noteOrContent.Content)
|
||||
contentOk, contentMsg, afterContentUsn = noteService.UpdateNoteContent(c.getUserId(),
|
||||
noteOrContent.NoteId,
|
||||
noteOrContent.Content,
|
||||
noteOrContent.Abstract,
|
||||
needUpdateNote,
|
||||
noteOrContent.Usn,
|
||||
noteOrContent.UpdatedTime)
|
||||
}
|
||||
|
||||
if needUpdateNote {
|
||||
re.Ok = noteOk
|
||||
re.Msg = noteMsg
|
||||
re.Usn = afterNoteUsn
|
||||
} else {
|
||||
re.Ok = contentOk
|
||||
re.Msg = contentMsg
|
||||
re.Usn = afterContentUsn
|
||||
}
|
||||
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
noteOrContent.Content = ""
|
||||
noteOrContent.Usn = re.Usn
|
||||
noteOrContent.UpdatedTime = time.Now()
|
||||
|
||||
// Log("after upload")
|
||||
// LogJ(noteOrContent.Files)
|
||||
noteOrContent.UserId = c.getUserId()
|
||||
|
||||
return c.RenderJson(noteOrContent)
|
||||
}
|
||||
|
||||
// 删除trash
|
||||
func (c ApiNote) DeleteTrash(noteId string, usn int) revel.Result {
|
||||
re := info.NewReUpdate()
|
||||
re.Ok, re.Msg, re.Usn = trashService.DeleteTrashApi(noteId, c.getUserId(), usn)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 得到历史列表
|
||||
/*
|
||||
func (c ApiNote) GetHistories(noteId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
histories := noteContentHistoryService.ListHistories(noteId, c.getUserId())
|
||||
if len(histories) > 0 {
|
||||
re.Ok = true
|
||||
re.Item = histories
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
*/
|
||||
|
||||
// 0.2 新增
|
||||
// 导出成PDF
|
||||
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/export_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 + " --lowquality --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
} else {
|
||||
cc = binPath + " --lowquality \"" + 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
|
||||
}
|
||||
106
app/controllers/api/ApiNotebookController.go
Normal file
106
app/controllers/api/ApiNotebookController.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "io/ioutil"
|
||||
)
|
||||
|
||||
// 笔记本API
|
||||
|
||||
type ApiNotebook struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 从Notebook -> ApiNotebook
|
||||
func (c ApiNotebook) fixNotebooks(notebooks []info.Notebook) []info.ApiNotebook {
|
||||
if notebooks == nil {
|
||||
return nil
|
||||
}
|
||||
apiNotebooks := make([]info.ApiNotebook, len(notebooks))
|
||||
for i, notebook := range notebooks {
|
||||
apiNotebooks[i] = c.fixNotebook(¬ebook)
|
||||
}
|
||||
return apiNotebooks
|
||||
}
|
||||
func (c ApiNotebook) fixNotebook(notebook *info.Notebook) info.ApiNotebook {
|
||||
if notebook == nil {
|
||||
return info.ApiNotebook{}
|
||||
}
|
||||
return info.ApiNotebook{
|
||||
NotebookId: notebook.NotebookId,
|
||||
UserId: notebook.UserId,
|
||||
ParentNotebookId: notebook.ParentNotebookId,
|
||||
Seq: notebook.Seq,
|
||||
Title: notebook.Title,
|
||||
UrlTitle: notebook.UrlTitle,
|
||||
IsBlog: notebook.IsBlog,
|
||||
CreatedTime: notebook.CreatedTime,
|
||||
UpdatedTime: notebook.UpdatedTime,
|
||||
Usn: notebook.Usn,
|
||||
IsDeleted: notebook.IsDeleted,
|
||||
}
|
||||
}
|
||||
|
||||
// 获取同步的笔记本
|
||||
// [OK]
|
||||
// > afterUsn的笔记
|
||||
// 返回 {ChunkHighUsn: 本下最大的usn, 借此可以知道是否还有, Notebooks: []}
|
||||
func (c ApiNotebook) GetSyncNotebooks(afterUsn, maxEntry int) revel.Result {
|
||||
if maxEntry == 0 {
|
||||
maxEntry = 100
|
||||
}
|
||||
notebooks := notebookService.GeSyncNotebooks(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(c.fixNotebooks(notebooks))
|
||||
}
|
||||
|
||||
// 得到用户的所有笔记本
|
||||
// [OK]
|
||||
// info.SubNotebooks
|
||||
func (c ApiNotebook) GetNotebooks() revel.Result {
|
||||
notebooks := notebookService.GeSyncNotebooks(c.getUserId(), 0, 99999)
|
||||
return c.RenderJson(c.fixNotebooks(notebooks))
|
||||
}
|
||||
|
||||
// 添加notebook
|
||||
// [OK]
|
||||
func (c ApiNotebook) AddNotebook(title, parentNotebookId string, seq int) revel.Result {
|
||||
notebook := info.Notebook{NotebookId: bson.NewObjectId(),
|
||||
Title: title,
|
||||
Seq: seq,
|
||||
UserId: bson.ObjectIdHex(c.getUserId())}
|
||||
if parentNotebookId != "" && bson.IsObjectIdHex(parentNotebookId) {
|
||||
notebook.ParentNotebookId = bson.ObjectIdHex(parentNotebookId)
|
||||
}
|
||||
re := info.NewRe()
|
||||
re.Ok, notebook = notebookService.AddNotebook(notebook)
|
||||
if !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
return c.RenderJson(c.fixNotebook(¬ebook))
|
||||
}
|
||||
|
||||
// 修改笔记
|
||||
// [OK]
|
||||
func (c ApiNotebook) UpdateNotebook(notebookId, title, parentNotebookId string, seq, usn int) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
|
||||
ok, msg, notebook := notebookService.UpdateNotebookApi(c.getUserId(), notebookId, title, parentNotebookId, seq, usn)
|
||||
if !ok {
|
||||
re.Ok = false
|
||||
re.Msg = msg
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
LogJ(notebook)
|
||||
return c.RenderJson(c.fixNotebook(¬ebook))
|
||||
}
|
||||
|
||||
// 删除笔记本
|
||||
// [OK]
|
||||
func (c ApiNotebook) DeleteNotebook(notebookId string, usn int) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
re.Ok, re.Msg = notebookService.DeleteNotebookForce(c.getUserId(), notebookId, usn)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
56
app/controllers/api/ApiTagController.go
Normal file
56
app/controllers/api/ApiTagController.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/revel/revel"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
)
|
||||
|
||||
// 标签API
|
||||
|
||||
type ApiTag struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 获取同步的标签
|
||||
// [OK]
|
||||
// > afterUsn的笔记
|
||||
// 返回 {ChunkHighUsn: 本下最大的usn, 借此可以知道是否还有, Notebooks: []}
|
||||
func (c ApiTag) GetSyncTags(afterUsn, maxEntry int) revel.Result {
|
||||
if maxEntry == 0 {
|
||||
maxEntry = 100
|
||||
}
|
||||
tags := tagService.GeSyncTags(c.getUserId(), afterUsn, maxEntry)
|
||||
return c.RenderJson(tags)
|
||||
}
|
||||
|
||||
// 添加Tag
|
||||
// [OK]
|
||||
// 不会产生冲突, 即使里面有
|
||||
// 返回
|
||||
/*
|
||||
{
|
||||
"TagId": "551978dd99c37b9bc5000001",
|
||||
"UserId": "54a1676399c37b1c77000002",
|
||||
"Tag": "32",
|
||||
"Usn": 25,
|
||||
"Count": 1,
|
||||
"CreatedTime": "2015-03-31T00:25:01.149312407+08:00",
|
||||
"UpdatedTime": "2015-03-31T00:25:01.149312407+08:00",
|
||||
"IsDeleted": false
|
||||
}
|
||||
*/
|
||||
func (c ApiTag) AddTag(tag string) revel.Result {
|
||||
ret := tagService.AddOrUpdateTag(c.getUserId(), tag)
|
||||
return c.RenderJson(ret)
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
// [OK]
|
||||
func (c ApiTag) DeleteTag(tag string, usn int) revel.Result {
|
||||
re := info.NewReUpdate()
|
||||
re.Ok, re.Msg, re.Usn = tagService.DeleteTagApi(c.getUserId(), tag, usn)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
154
app/controllers/api/ApiUserController.go
Normal file
154
app/controllers/api/ApiUserController.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
"io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
"os"
|
||||
|
||||
// "path"
|
||||
// "strconv"
|
||||
)
|
||||
|
||||
type ApiUser struct {
|
||||
ApiBaseContrller
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
// [OK]
|
||||
func (c ApiUser) Info() revel.Result {
|
||||
re := info.NewApiRe()
|
||||
|
||||
userInfo := c.getUserInfo()
|
||||
if userInfo.UserId == "" {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
apiUser := info.ApiUser{
|
||||
UserId: userInfo.UserId.Hex(),
|
||||
Username: userInfo.Username,
|
||||
Email: userInfo.Email,
|
||||
Logo: userInfo.Logo,
|
||||
Verified: userInfo.Verified,
|
||||
}
|
||||
return c.RenderJson(apiUser)
|
||||
}
|
||||
|
||||
// 修改用户名
|
||||
// [OK]
|
||||
func (c ApiUser) UpdateUsername(username string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if c.GetUsername() == "demo" {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
if re.Ok, re.Msg = Vd("username", username); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
re.Ok, re.Msg = userService.UpdateUsername(c.getUserId(), username)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
// [OK]
|
||||
func (c ApiUser) UpdatePwd(oldPwd, pwd string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
if c.GetUsername() == "demo" {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", oldPwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
re.Ok, re.Msg = userService.UpdatePwd(c.getUserId(), oldPwd, pwd)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 获得同步状态
|
||||
// [OK]
|
||||
func (c ApiUser) GetSyncState() revel.Result {
|
||||
ret := bson.M{"LastSyncUsn": userService.GetUsn(c.getUserId()), "LastSyncTime": time.Now().Unix()}
|
||||
return c.RenderJson(ret)
|
||||
}
|
||||
|
||||
// 头像设置
|
||||
// 参数file=文件
|
||||
// 成功返回{Logo: url} 头像新url
|
||||
// [OK]
|
||||
func (c ApiUser) UpdateLogo() revel.Result {
|
||||
ok, msg, url := c.uploadImage()
|
||||
|
||||
if ok {
|
||||
ok = userService.UpdateAvatar(c.getUserId(), url)
|
||||
return c.RenderJson(map[string]string{"Logo": url})
|
||||
} else {
|
||||
re := info.NewApiRe()
|
||||
re.Msg = msg
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
// 上传图片
|
||||
func (c ApiUser) uploadImage() (ok bool, msg, url string) {
|
||||
var fileUrlPath = ""
|
||||
ok = false
|
||||
|
||||
file, handel, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
// 生成上传路径
|
||||
fileUrlPath = "public/upload/" + c.getUserId() + "/images/logo"
|
||||
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
|
||||
var ext string
|
||||
|
||||
_, ext = SplitFilename(filename)
|
||||
if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
|
||||
msg = "notImage"
|
||||
return
|
||||
}
|
||||
|
||||
filename = NewGuid() + ext
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
LogJ(err)
|
||||
return
|
||||
}
|
||||
|
||||
// > 5M?
|
||||
if len(data) > 5*1024*1024 {
|
||||
msg = "fileIsTooLarge"
|
||||
return
|
||||
}
|
||||
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
LogJ(err)
|
||||
return
|
||||
}
|
||||
|
||||
ok = true
|
||||
url = configService.GetSiteUrl() + "/" + fileUrlPath + "/" + filename
|
||||
return
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/leanote/leanote/app/info"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "os"
|
||||
// "path"
|
||||
// "strconv"
|
||||
)
|
||||
|
||||
type ApiUser struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// 修改用户名, 需要重置session
|
||||
func (c ApiUser) Info() revel.Result {
|
||||
Log("APIUser");
|
||||
return c.RenderTemplate("home/index.html");
|
||||
// return nil;
|
||||
}
|
||||
144
app/controllers/api/init.go
Normal file
144
app/controllers/api/init.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var userService *service.UserService
|
||||
var noteService *service.NoteService
|
||||
var trashService *service.TrashService
|
||||
var notebookService *service.NotebookService
|
||||
var noteContentHistoryService *service.NoteContentHistoryService
|
||||
var authService *service.AuthService
|
||||
var shareService *service.ShareService
|
||||
var blogService *service.BlogService
|
||||
var tagService *service.TagService
|
||||
var pwdService *service.PwdService
|
||||
var tokenService *service.TokenService
|
||||
var suggestionService *service.SuggestionService
|
||||
var albumService *service.AlbumService
|
||||
var noteImageService *service.NoteImageService
|
||||
var fileService *service.FileService
|
||||
var attachService *service.AttachService
|
||||
var configService *service.ConfigService
|
||||
var emailService *service.EmailService
|
||||
var sessionService *service.SessionService
|
||||
|
||||
var pageSize = 1000
|
||||
var defaultSortField = "UpdatedTime"
|
||||
var leanoteUserId = "admin" // 不能更改
|
||||
|
||||
// 状态
|
||||
const (
|
||||
S_DEFAULT = iota // 0
|
||||
S_NOT_LOGIN // 1
|
||||
S_WRONG_USERNAME_PASSWORD // 2
|
||||
S_WRONG_CAPTCHA // 3
|
||||
S_NEED_CAPTCHA // 4
|
||||
S_NOT_OPEN_REGISTER // 4
|
||||
)
|
||||
|
||||
// 拦截器
|
||||
// 不需要拦截的url
|
||||
var commonUrl = map[string]map[string]bool{"ApiAuth": map[string]bool{"Login": true,
|
||||
"Register": true,
|
||||
},
|
||||
// 文件的操作也不用登录, userId会从session中获取
|
||||
"ApiFile": map[string]bool{"GetImage": true,
|
||||
"GetAttach": true,
|
||||
"GetAllAttachs": true,
|
||||
},
|
||||
}
|
||||
|
||||
func needValidate(controller, method string) bool {
|
||||
// 在里面
|
||||
if v, ok := commonUrl[controller]; ok {
|
||||
// 在commonUrl里
|
||||
if _, ok2 := v[method]; ok2 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
// controller不在这里的, 肯定要验证
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 这里得到token, 若不是login, logout等公用操作, 必须验证是否已登录
|
||||
func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
// 得到token /api/user/info?userId=xxx&token=xxxxx
|
||||
token := c.Params.Values.Get("token")
|
||||
noToken := false
|
||||
if token == "" {
|
||||
// 若无, 则取sessionId
|
||||
token = c.Session.Id()
|
||||
noToken = true
|
||||
}
|
||||
c.Session["_token"] = token
|
||||
|
||||
// 全部变成首字大写
|
||||
var controller = strings.Title(c.Name)
|
||||
var method = strings.Title(c.MethodName)
|
||||
|
||||
// 验证是否已登录
|
||||
// 通过sessionService判断该token下是否有userId, 并返回userId
|
||||
userId := sessionService.GetUserId(token)
|
||||
if noToken && userId == "" {
|
||||
// 从session中获取, api/file/getImage, api/file/getAttach, api/file/getAllAttach
|
||||
// 客户端
|
||||
userId, _ = c.Session["UserId"]
|
||||
}
|
||||
c.Session["_userId"] = userId
|
||||
|
||||
// 是否需要验证?
|
||||
if !needValidate(controller, method) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if userId != "" {
|
||||
return nil // 已登录
|
||||
}
|
||||
|
||||
// 没有登录, 返回错误的信息, 需要登录
|
||||
re := info.NewApiRe()
|
||||
re.Msg = "NOTLOGIN"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// interceptors
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiAuth{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiUser{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiFile{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiNote{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiTag{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiNotebook{})
|
||||
}
|
||||
|
||||
// 最外层init.go调用
|
||||
// 获取service, 单例
|
||||
func InitService() {
|
||||
notebookService = service.NotebookS
|
||||
noteService = service.NoteS
|
||||
noteContentHistoryService = service.NoteContentHistoryS
|
||||
trashService = service.TrashS
|
||||
shareService = service.ShareS
|
||||
userService = service.UserS
|
||||
tagService = service.TagS
|
||||
blogService = service.BlogS
|
||||
tokenService = service.TokenS
|
||||
noteImageService = service.NoteImageS
|
||||
fileService = service.FileS
|
||||
albumService = service.AlbumS
|
||||
attachService = service.AttachS
|
||||
pwdService = service.PwdS
|
||||
suggestionService = service.SuggestionS
|
||||
authService = service.AuthS
|
||||
configService = service.ConfigS
|
||||
emailService = service.EmailS
|
||||
sessionService = service.SessionS
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/service"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/lea/blog"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"strings"
|
||||
)
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package member
|
||||
|
||||
import (
|
||||
// "github.com/revel/revel"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "encoding/json"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/revel/revel"
|
||||
// "gopkg.in/mgo.v2/bson"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "strconv"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "math"
|
||||
// "strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -20,26 +20,26 @@ type MemberBaseController struct {
|
||||
|
||||
// 得到sorterField 和 isAsc
|
||||
// okSorter = ['email', 'username']
|
||||
func (c MemberBaseController) getSorter(sorterField string, isAsc bool, okSorter []string) (string, bool){
|
||||
func (c MemberBaseController) getSorter(sorterField string, isAsc bool, okSorter []string) (string, bool) {
|
||||
sorter := ""
|
||||
c.Params.Bind(&sorter, "sorter")
|
||||
if sorter == "" {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
|
||||
// sorter形式 email-up, email-down
|
||||
s2 := strings.Split(sorter, "-")
|
||||
if len(s2) != 2 {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
|
||||
// 必须是可用的sorter
|
||||
if okSorter != nil && len(okSorter) > 0 {
|
||||
if !InArray(okSorter, s2[0]) {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sorterField = strings.Title(s2[0])
|
||||
if s2[1] == "up" {
|
||||
isAsc = true
|
||||
@@ -47,7 +47,7 @@ func (c MemberBaseController) getSorter(sorterField string, isAsc bool, okSorter
|
||||
isAsc = false
|
||||
}
|
||||
c.RenderArgs["sorter"] = sorter
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
func (c MemberBaseController) updateConfig(keys []string) {
|
||||
@@ -56,4 +56,4 @@ func (c MemberBaseController) updateConfig(keys []string) {
|
||||
v := c.Params.Values.Get(key)
|
||||
configService.UpdateGlobalStringConfig(userId, key, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package member
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
// "github.com/leanote/leanote/app/lea/blog"
|
||||
"time"
|
||||
// "github.com/leanote/leanote/app/lea/blog"
|
||||
)
|
||||
|
||||
// 博客管理
|
||||
@@ -28,35 +28,34 @@ func (c MemberBlog) common() info.UserBlog {
|
||||
|
||||
userBlog := blogService.GetUserBlog(userId)
|
||||
c.RenderArgs["userBlog"] = userBlog
|
||||
|
||||
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
return userBlog
|
||||
}
|
||||
|
||||
|
||||
// 得到sorterField 和 isAsc
|
||||
// okSorter = ['email', 'username']
|
||||
func (c MemberBlog) getSorter(sorterField string, isAsc bool, okSorter []string) (string, bool){
|
||||
func (c MemberBlog) getSorter(sorterField string, isAsc bool, okSorter []string) (string, bool) {
|
||||
sorter := ""
|
||||
c.Params.Bind(&sorter, "sorter")
|
||||
if sorter == "" {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
|
||||
// sorter形式 email-up, email-down
|
||||
s2 := strings.Split(sorter, "-")
|
||||
if len(s2) != 2 {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
|
||||
// 必须是可用的sorter
|
||||
if okSorter != nil && len(okSorter) > 0 {
|
||||
if !InArray(okSorter, s2[0]) {
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sorterField = strings.Title(s2[0])
|
||||
if s2[1] == "up" {
|
||||
isAsc = true
|
||||
@@ -64,24 +63,31 @@ func (c MemberBlog) getSorter(sorterField string, isAsc bool, okSorter []string)
|
||||
isAsc = false
|
||||
}
|
||||
c.RenderArgs["sorter"] = sorter
|
||||
return sorterField, isAsc;
|
||||
return sorterField, isAsc
|
||||
}
|
||||
|
||||
// 博客列表
|
||||
var userPageSize = 15
|
||||
|
||||
func (c MemberBlog) Index(sorter, keywords string) revel.Result {
|
||||
c.RenderArgs["title"] = "Posts"
|
||||
userId := c.GetUserId()
|
||||
userInfo := userService.GetUserInfo(userId)
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
|
||||
c.RenderArgs["title"] = c.Message("Posts")
|
||||
pageNumber := c.GetPage()
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"title", "urlTitle", "updatedTime", "publicTime", "createdTime"});
|
||||
pageInfo, blogs := blogService.ListAllBlogs(c.GetUserId(), "", keywords, false, pageNumber, userPageSize, sorterField, isAsc);
|
||||
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"title", "urlTitle", "updatedTime", "publicTime", "createdTime"})
|
||||
pageInfo, blogs := blogService.ListAllBlogs(c.GetUserId(), "", keywords, false, pageNumber, userPageSize, sorterField, isAsc)
|
||||
c.RenderArgs["pageInfo"] = pageInfo
|
||||
c.RenderArgs["blogs"] = blogs
|
||||
c.RenderArgs["keywords"] = keywords
|
||||
|
||||
|
||||
userAndBlog := userService.GetUserAndBlog(c.GetUserId())
|
||||
c.RenderArgs["userAndBlog"] = userAndBlog
|
||||
|
||||
return c.RenderTemplate("member/blog/list.html");
|
||||
|
||||
c.common()
|
||||
|
||||
return c.RenderTemplate("member/blog/list.html")
|
||||
}
|
||||
|
||||
// 修改笔记的urlTitle
|
||||
@@ -91,20 +97,19 @@ func (c MemberBlog) UpdateBlogUrlTitle(noteId, urlTitle string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
// 修改笔记的urlTitle
|
||||
func (c MemberBlog) UpdateBlogAbstract(noteId string) revel.Result {
|
||||
c.RenderArgs["title"] = "Update Post Abstract"
|
||||
note := noteService.GetNoteAndContent(noteId, c.GetUserId());
|
||||
c.common()
|
||||
c.RenderArgs["title"] = c.Message("Update Post Abstract")
|
||||
note := noteService.GetNoteAndContent(noteId, c.GetUserId())
|
||||
if !note.Note.IsBlog {
|
||||
return c.E404();
|
||||
return c.E404()
|
||||
}
|
||||
c.RenderArgs["note"] = note
|
||||
c.RenderArgs["noteId"] = noteId
|
||||
return c.RenderTemplate("member/blog/update_abstract.html");
|
||||
return c.RenderTemplate("member/blog/update_abstract.html")
|
||||
}
|
||||
func (c MemberBlog) DoUpdateBlogAbstract(noteId, imgSrc, desc, abstract string) revel.Result {
|
||||
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.UpateBlogAbstract(c.GetUserId(), noteId, imgSrc, desc, abstract)
|
||||
return c.RenderJson(re)
|
||||
@@ -113,39 +118,34 @@ func (c MemberBlog) DoUpdateBlogAbstract(noteId, imgSrc, desc, abstract string)
|
||||
// 基本信息设置
|
||||
func (c MemberBlog) Base() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = "Blog Base Info"
|
||||
return c.RenderTemplate("member/blog/base.html");
|
||||
c.RenderArgs["title"] = c.Message("Blog Base Info")
|
||||
return c.RenderTemplate("member/blog/base.html")
|
||||
}
|
||||
func (c MemberBlog) Comment() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = "Comment"
|
||||
return c.RenderTemplate("member/blog/comment.html");
|
||||
}
|
||||
func (c MemberBlog) Domain() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = "Domain"
|
||||
return c.RenderTemplate("member/blog/domain.html");
|
||||
c.RenderArgs["title"] = c.Message("Comment")
|
||||
return c.RenderTemplate("member/blog/comment.html")
|
||||
}
|
||||
|
||||
func (c MemberBlog) Paging() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = "Paging"
|
||||
return c.RenderTemplate("member/blog/paging.html");
|
||||
c.RenderArgs["title"] = c.Message("Paging")
|
||||
return c.RenderTemplate("member/blog/paging.html")
|
||||
}
|
||||
|
||||
func (c MemberBlog) Cate() revel.Result {
|
||||
userBlog := c.common()
|
||||
c.RenderArgs["title"] = "Cate"
|
||||
|
||||
c.RenderArgs["title"] = c.Message("Category")
|
||||
|
||||
notebooks := blogService.ListBlogNotebooks(c.GetUserId())
|
||||
notebooksMap := map[string]info.Notebook{}
|
||||
for _, each := range notebooks {
|
||||
notebooksMap[each.NotebookId.Hex()] = each
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
|
||||
var i = 0
|
||||
notebooks2 := make([]info.Notebook, len(notebooks))
|
||||
|
||||
|
||||
// 先要保证已有的是正确的排序
|
||||
cateIds := userBlog.CateIds
|
||||
has := map[string]bool{} // cateIds中有的
|
||||
@@ -167,8 +167,8 @@ func (c MemberBlog) Cate() revel.Result {
|
||||
}
|
||||
}
|
||||
c.RenderArgs["notebooks"] = notebooks2
|
||||
|
||||
return c.RenderTemplate("member/blog/cate.html");
|
||||
|
||||
return c.RenderTemplate("member/blog/cate.html")
|
||||
}
|
||||
|
||||
// 修改分类排序
|
||||
@@ -192,12 +192,13 @@ func (c MemberBlog) DoAddOrUpdateSingle(singleId, title, content string) revel.R
|
||||
}
|
||||
func (c MemberBlog) AddOrUpdateSingle(singleId string) revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = "Add Single"
|
||||
c.RenderArgs["title"] = c.Message("Add Single")
|
||||
c.RenderArgs["singleId"] = singleId
|
||||
if singleId != "" {
|
||||
c.RenderArgs["title"] = c.Message("Update Single")
|
||||
c.RenderArgs["single"] = blogService.GetSingle(singleId)
|
||||
}
|
||||
return c.RenderTemplate("member/blog/add_single.html");
|
||||
return c.RenderTemplate("member/blog/add_single.html")
|
||||
}
|
||||
func (c MemberBlog) SortSingles(singleIds []string) revel.Result {
|
||||
re := info.NewRe()
|
||||
@@ -220,10 +221,10 @@ func (c MemberBlog) UpdateSingleUrlTitle(singleId, urlTitle string) revel.Result
|
||||
|
||||
func (c MemberBlog) Single() revel.Result {
|
||||
c.common()
|
||||
c.RenderArgs["title"] = "Cate"
|
||||
c.RenderArgs["title"] = c.Message("Single")
|
||||
c.RenderArgs["singles"] = blogService.GetSingles(c.GetUserId())
|
||||
|
||||
return c.RenderTemplate("member/blog/single.html");
|
||||
|
||||
return c.RenderTemplate("member/blog/single.html")
|
||||
}
|
||||
|
||||
// 主题
|
||||
@@ -232,15 +233,16 @@ func (c MemberBlog) Theme() revel.Result {
|
||||
activeTheme, otherThemes := themeService.GetUserThemes(c.GetUserId())
|
||||
c.RenderArgs["activeTheme"] = activeTheme
|
||||
c.RenderArgs["otherThemes"] = otherThemes
|
||||
|
||||
|
||||
c.RenderArgs["optionThemes"] = themeService.GetDefaultThemes()
|
||||
|
||||
c.RenderArgs["title"] = "Theme"
|
||||
return c.RenderTemplate("member/blog/theme.html");
|
||||
|
||||
c.RenderArgs["title"] = c.Message("Theme")
|
||||
return c.RenderTemplate("member/blog/theme.html")
|
||||
}
|
||||
|
||||
// 编辑主题
|
||||
var baseTpls = []string{"header.html", "footer.html", "index.html", "cate.html", "search.html", "post.html", "single.html", "tags.html", "tag_posts.html", "archive.html", "share_comment.html", "404.html", "theme.json", "style.css", "blog.js"}
|
||||
|
||||
func (c MemberBlog) UpdateTheme(themeId string, isNew int) revel.Result {
|
||||
// 查看用户是否有该theme, 若没有则复制default之
|
||||
// 得到主题的文件列表
|
||||
@@ -249,22 +251,25 @@ func (c MemberBlog) UpdateTheme(themeId string, isNew int) revel.Result {
|
||||
_, themeId = themeService.NewThemeForFirst(userBlog)
|
||||
return c.Redirect("/member/blog/updateTheme?themeId=" + themeId)
|
||||
}
|
||||
|
||||
|
||||
c.common()
|
||||
c.RenderArgs["title"] = "Upate Theme"
|
||||
c.RenderArgs["title"] = c.Message("Update Theme")
|
||||
c.RenderArgs["isNew"] = isNew
|
||||
|
||||
|
||||
// 先复制之
|
||||
c.RenderArgs["themeId"] = themeId
|
||||
|
||||
|
||||
// 得到脚本目录
|
||||
userId := c.GetUserId()
|
||||
|
||||
theme := themeService.GetTheme(userId, themeId)
|
||||
|
||||
theme := themeService.GetTheme(userId, themeId)
|
||||
if theme.ThemeId == "" {
|
||||
return c.E404()
|
||||
}
|
||||
c.RenderArgs["theme"] = theme
|
||||
|
||||
|
||||
path := revel.BasePath + "/" + theme.Path
|
||||
|
||||
|
||||
tpls := ListDir(path)
|
||||
myTpls := make([]string, len(baseTpls))
|
||||
tplMap := map[string]bool{}
|
||||
@@ -275,16 +280,16 @@ func (c MemberBlog) UpdateTheme(themeId string, isNew int) revel.Result {
|
||||
// 得到没有的tpls
|
||||
for _, t := range tpls {
|
||||
if t == "images" {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
if !tplMap[t] {
|
||||
myTpls = append(myTpls, t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
c.RenderArgs["myTpls"] = myTpls
|
||||
|
||||
return c.RenderTemplate("member/blog/update_theme.html");
|
||||
|
||||
return c.RenderTemplate("member/blog/update_theme.html")
|
||||
}
|
||||
|
||||
// 得到文件内容
|
||||
@@ -292,13 +297,13 @@ func (c MemberBlog) GetTplContent(themeId string, filename string) revel.Result
|
||||
re := info.NewRe()
|
||||
re.Ok = true
|
||||
re.Item = themeService.GetTplContent(c.GetUserId(), themeId, filename)
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
func (c MemberBlog) UpdateTplContent(themeId, filename, content string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = themeService.UpdateTplContent(c.GetUserId(), themeId, filename, content)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
func (c MemberBlog) DeleteTpl(themeId, filename string) revel.Result {
|
||||
@@ -327,7 +332,7 @@ func (c MemberBlog) DeleteThemeImage(themeId, filename string) revel.Result {
|
||||
|
||||
// 上传主题图片
|
||||
func (c MemberBlog) UploadThemeImage(themeId string) revel.Result {
|
||||
re := c.uploadImage(themeId);
|
||||
re := c.uploadImage(themeId)
|
||||
c.RenderArgs["fileUrlPath"] = re.Id
|
||||
c.RenderArgs["resultCode"] = re.Code
|
||||
c.RenderArgs["resultMsg"] = re.Msg
|
||||
@@ -335,17 +340,17 @@ func (c MemberBlog) UploadThemeImage(themeId string) revel.Result {
|
||||
}
|
||||
func (c MemberBlog) uploadImage(themeId string) (re info.Re) {
|
||||
var fileId = ""
|
||||
var resultCode = 0 // 1表示正常
|
||||
var resultCode = 0 // 1表示正常
|
||||
var resultMsg = "内部错误" // 错误信息
|
||||
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
|
||||
@@ -359,11 +364,11 @@ func (c MemberBlog) uploadImage(themeId string) (re info.Re) {
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
|
||||
var ext string;
|
||||
|
||||
|
||||
var ext string
|
||||
|
||||
_, 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 = "不是图片"
|
||||
return re
|
||||
}
|
||||
@@ -374,15 +379,15 @@ func (c MemberBlog) uploadImage(themeId string) (re info.Re) {
|
||||
LogJ(err)
|
||||
return re
|
||||
}
|
||||
|
||||
|
||||
// > 2M?
|
||||
if(len(data) > 5 * 1024 * 1024) {
|
||||
if len(data) > 5*1024*1024 {
|
||||
resultCode = 0
|
||||
resultMsg = "图片大于2M"
|
||||
return re
|
||||
}
|
||||
|
||||
toPath := dir + "/" + filename;
|
||||
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
LogJ(err)
|
||||
@@ -391,7 +396,7 @@ func (c MemberBlog) uploadImage(themeId string) (re info.Re) {
|
||||
TransToGif(toPath, 0, true)
|
||||
resultCode = 1
|
||||
resultMsg = "上传成功!"
|
||||
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
@@ -402,18 +407,21 @@ func (c MemberBlog) ActiveTheme(themeId string) revel.Result {
|
||||
re.Ok = themeService.ActiveTheme(c.GetUserId(), themeId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 删除主题
|
||||
func (c MemberBlog) DeleteTheme(themeId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = themeService.DeleteTheme(c.GetUserId(), themeId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 管理员公开主题
|
||||
func (c MemberBlog) PublicTheme(themeId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = themeService.PublicTheme(c.GetUserId(), themeId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 导出
|
||||
func (c MemberBlog) ExportTheme(themeId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
@@ -423,22 +431,22 @@ func (c MemberBlog) ExportTheme(themeId string) revel.Result {
|
||||
return c.RenderText("error...")
|
||||
}
|
||||
fw, err := os.Open(path)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return c.RenderText("error")
|
||||
}
|
||||
return c.RenderBinary(fw, GetFilename(path), revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
return c.RenderBinary(fw, GetFilename(path), revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
|
||||
// 导入主题
|
||||
func (c MemberBlog) ImportTheme() revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
|
||||
file, handel, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
re.Msg = fmt.Sprintf("%v", err)
|
||||
return c.RenderJson(re)
|
||||
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
// 生成上传路径
|
||||
userId := c.GetUserId()
|
||||
@@ -450,11 +458,11 @@ func (c MemberBlog) ImportTheme() revel.Result {
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
|
||||
var ext string;
|
||||
|
||||
var ext string
|
||||
_, ext = SplitFilename(filename)
|
||||
if(ext != ".zip") {
|
||||
re.Msg = "请上传zip文件"
|
||||
if ext != ".zip" {
|
||||
re.Msg = "Please upload zip file"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -463,40 +471,40 @@ func (c MemberBlog) ImportTheme() revel.Result {
|
||||
if err != nil {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
// > 10M?
|
||||
if(len(data) > 10 * 1024 * 1024) {
|
||||
re.Msg = "文件大于10M"
|
||||
if len(data) > 10*1024*1024 {
|
||||
re.Msg = "File is big than 10M"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
toPath := dir + "/" + filename;
|
||||
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
re.Msg = fmt.Sprintf("%v", err)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
// 上传好后, 增加之
|
||||
themeService.ImportTheme(c.GetUserId(), toPath)
|
||||
|
||||
re.Ok = true
|
||||
return c.RenderJson(re)
|
||||
re.Ok, re.Msg = themeService.ImportTheme(c.GetUserId(), toPath)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
|
||||
// 安装
|
||||
func (c MemberBlog) InstallTheme(themeId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = themeService.InstallTheme(c.GetUserId(), themeId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 新建主题
|
||||
func (c MemberBlog) NewTheme() revel.Result {
|
||||
_, themeId := themeService.NewTheme(c.GetUserId())
|
||||
return c.Redirect("/member/blog/updateTheme?isNew=1&themeId=" + themeId)
|
||||
}
|
||||
|
||||
//-----------
|
||||
//
|
||||
//
|
||||
func (c MemberBlog) SetUserBlogBase(userBlog info.UserBlogBase) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = blogService.UpdateUserBlogBase(c.GetUserId(), userBlog)
|
||||
@@ -512,6 +520,7 @@ func (c MemberBlog) SetUserBlogStyle(userBlog info.UserBlogStyle) revel.Result {
|
||||
re.Ok = blogService.UpdateUserBlogStyle(c.GetUserId(), userBlog)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c MemberBlog) SetUserBlogPaging(perPageSize int, sortField string, isAsc bool) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = blogService.UpdateUserBlogPaging(c.GetUserId(), perPageSize, sortField, isAsc)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package member
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
// 分组管理
|
||||
@@ -14,9 +14,9 @@ type MemberGroup struct {
|
||||
func (c MemberGroup) Index() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = "My Group"
|
||||
c.RenderArgs["title"] = c.Message("My Group")
|
||||
c.RenderArgs["groups"] = groupService.GetGroupsAndUsers(c.GetUserId())
|
||||
return c.RenderTemplate("member/group/index.html");
|
||||
return c.RenderTemplate("member/group/index.html")
|
||||
}
|
||||
|
||||
// 添加分组
|
||||
@@ -35,7 +35,7 @@ func (c MemberGroup) UpdateGroupTitle(groupId, title string) revel.Result {
|
||||
func (c MemberGroup) DeleteGroup(groupId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok, re.Msg = groupService.DeleteGroup(c.GetUserId(), groupId)
|
||||
return c.RenderJson(re)
|
||||
return c.RenderRe(re)
|
||||
}
|
||||
|
||||
// 添加用户
|
||||
|
||||
@@ -13,14 +13,14 @@ type MemberIndex struct {
|
||||
// admin 主页
|
||||
func (c MemberIndex) Index() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.RenderArgs["title"] = "Leanote Member Center"
|
||||
|
||||
c.RenderArgs["title"] = c.Message("Leanote Member Center")
|
||||
|
||||
c.RenderArgs["countNote"] = noteService.CountNote(c.GetUserId())
|
||||
c.RenderArgs["countBlog"] = noteService.CountBlog(c.GetUserId())
|
||||
|
||||
|
||||
c.SetLocale()
|
||||
|
||||
return c.RenderTemplate("member/index.html");
|
||||
|
||||
return c.RenderTemplate("member/index.html")
|
||||
}
|
||||
|
||||
// 模板
|
||||
@@ -33,5 +33,5 @@ func (c MemberIndex) T(t string) revel.Result {
|
||||
}
|
||||
|
||||
func (c MemberIndex) GetView(view string) revel.Result {
|
||||
return c.RenderTemplate("admin/" + view);
|
||||
}
|
||||
return c.RenderTemplate("admin/" + view)
|
||||
}
|
||||
|
||||
@@ -13,34 +13,28 @@ type MemberUser struct {
|
||||
func (c MemberUser) Username() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = "Username"
|
||||
return c.RenderTemplate("member/user/username.html");
|
||||
c.RenderArgs["title"] = c.Message("Username")
|
||||
return c.RenderTemplate("member/user/username.html")
|
||||
}
|
||||
|
||||
func (c MemberUser) Email() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = "Email"
|
||||
return c.RenderTemplate("member/user/email.html");
|
||||
c.RenderArgs["title"] = c.Message("Email")
|
||||
return c.RenderTemplate("member/user/email.html")
|
||||
}
|
||||
|
||||
func (c MemberUser) Password() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = "Password"
|
||||
return c.RenderTemplate("member/user/password.html");
|
||||
c.RenderArgs["title"] = c.Message("Password")
|
||||
return c.RenderTemplate("member/user/password.html")
|
||||
}
|
||||
|
||||
func (c MemberUser) Avatar() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = "Avatar"
|
||||
c.RenderArgs["title"] = c.Message("Avatar")
|
||||
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
|
||||
return c.RenderTemplate("member/user/avatar.html");
|
||||
return c.RenderTemplate("member/user/avatar.html")
|
||||
}
|
||||
func (c MemberUser) AddAccount() revel.Result {
|
||||
c.SetUserInfo()
|
||||
c.SetLocale()
|
||||
c.RenderArgs["title"] = "Add Account"
|
||||
return c.RenderTemplate("member/user/add_account.html");
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package member
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/service"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
// "strings"
|
||||
// "strings"
|
||||
)
|
||||
|
||||
var userService *service.UserService
|
||||
@@ -20,9 +20,9 @@ var blogService *service.BlogService
|
||||
var tagService *service.TagService
|
||||
var pwdService *service.PwdService
|
||||
var tokenService *service.TokenService
|
||||
var suggestionService *service.SuggestionService
|
||||
var albumService *service.AlbumService
|
||||
var noteImageService *service.NoteImageService
|
||||
var suggestionService *service.SuggestionService
|
||||
var albumService *service.AlbumService
|
||||
var noteImageService *service.NoteImageService
|
||||
var fileService *service.FileService
|
||||
var attachService *service.AttachService
|
||||
var configService *service.ConfigService
|
||||
@@ -33,31 +33,32 @@ var themeService *service.ThemeService
|
||||
// 拦截器
|
||||
// 不需要拦截的url
|
||||
// Index 除了Note之外都不需要
|
||||
var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": true,
|
||||
"Login": true,
|
||||
"DoLogin": true,
|
||||
"Logout": true,
|
||||
"Register": true,
|
||||
"DoRegister": true,
|
||||
"FindPasswword": true,
|
||||
"DoFindPassword": true,
|
||||
"FindPassword2": true,
|
||||
"FindPasswordUpdate": true,
|
||||
"Suggestion": true,
|
||||
},
|
||||
var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": true,
|
||||
"Login": true,
|
||||
"DoLogin": true,
|
||||
"Logout": true,
|
||||
"Register": true,
|
||||
"DoRegister": true,
|
||||
"FindPasswword": true,
|
||||
"DoFindPassword": true,
|
||||
"FindPassword2": true,
|
||||
"FindPasswordUpdate": true,
|
||||
"Suggestion": true,
|
||||
},
|
||||
"Blog": map[string]bool{"Index": true,
|
||||
"View": true,
|
||||
"AboutMe": true,
|
||||
"View": true,
|
||||
"AboutMe": true,
|
||||
"SearchBlog": true,
|
||||
},
|
||||
},
|
||||
// 用户的激活与修改邮箱都不需要登录, 通过链接地址
|
||||
"User": map[string]bool{"UpdateEmail": true,
|
||||
"ActiveEmail":true,
|
||||
},
|
||||
"Oauth": map[string]bool{"GithubCallback": true},
|
||||
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
|
||||
"ActiveEmail": true,
|
||||
},
|
||||
"Oauth": map[string]bool{"GithubCallback": true},
|
||||
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
|
||||
"Attach": map[string]bool{"Download": true, "DownloadAll": true},
|
||||
}
|
||||
|
||||
func needValidate(controller, method string) bool {
|
||||
// 在里面
|
||||
if v, ok := commonUrl[controller]; ok {
|
||||
@@ -68,33 +69,33 @@ func needValidate(controller, method string) bool {
|
||||
return true
|
||||
} else {
|
||||
// controller不在这里的, 肯定要验证
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
func AuthInterceptor(c *revel.Controller) revel.Result {
|
||||
// 全部变成首字大写
|
||||
/*
|
||||
var controller = strings.Title(c.Name)
|
||||
var method = strings.Title(c.MethodName)
|
||||
// 是否需要验证?
|
||||
if !needValidate(controller, method) {
|
||||
return nil
|
||||
}
|
||||
var controller = strings.Title(c.Name)
|
||||
var method = strings.Title(c.MethodName)
|
||||
// 是否需要验证?
|
||||
if !needValidate(controller, method) {
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// 验证是否已登录
|
||||
// 必须是管理员
|
||||
if _, ok := c.Session["Username"]; ok {
|
||||
return nil // 已登录
|
||||
}
|
||||
|
||||
|
||||
// 没有登录, 判断是否是ajax操作
|
||||
if c.Request.Header.Get("X-Requested-With") == "XMLHttpRequest" {
|
||||
re := info.NewRe()
|
||||
re.Msg = "NOTLOGIN"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
return c.Redirect("/login")
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ func InitService() {
|
||||
tokenService = service.TokenS
|
||||
noteImageService = service.NoteImageS
|
||||
fileService = service.FileS
|
||||
albumService= service.AlbumS
|
||||
albumService = service.AlbumS
|
||||
attachService = service.AttachS
|
||||
pwdService = service.PwdS
|
||||
suggestionService = service.SuggestionS
|
||||
@@ -128,6 +129,7 @@ func init() {
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &MemberIndex{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &MemberUser{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &MemberBlog{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &MemberGroup{})
|
||||
revel.OnAppStart(func() {
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
包括基本信息设置
|
||||
博客设置
|
||||
@@ -2,9 +2,11 @@ package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"gopkg.in/mgo.v2"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Init mgo and the common DAO
|
||||
@@ -27,7 +29,7 @@ var Groups *mgo.Collection
|
||||
var GroupUsers *mgo.Collection
|
||||
|
||||
var Tags *mgo.Collection
|
||||
//var TagNotes *mgo.Collection
|
||||
var NoteTags *mgo.Collection
|
||||
var TagCounts *mgo.Collection
|
||||
|
||||
var UserBlogs *mgo.Collection
|
||||
@@ -56,12 +58,31 @@ var Themes *mgo.Collection
|
||||
var Sessions *mgo.Collection
|
||||
|
||||
// 初始化时连接数据库
|
||||
func Init() {
|
||||
var url string
|
||||
var ok bool
|
||||
func Init(url, dbname string) {
|
||||
ok := true
|
||||
config := revel.Config
|
||||
url, ok = config.String("db.url")
|
||||
dbname, _ := config.String("db.dbname")
|
||||
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")
|
||||
@@ -73,6 +94,7 @@ func Init() {
|
||||
}
|
||||
url = "mongodb://" + usernameAndPassword + host + ":" + port + "/" + dbname
|
||||
}
|
||||
Log(url)
|
||||
|
||||
// [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options]
|
||||
// mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
|
||||
@@ -102,7 +124,7 @@ func Init() {
|
||||
|
||||
// user
|
||||
Users = Session.DB(dbname).C("users")
|
||||
// group
|
||||
// group
|
||||
Groups = Session.DB(dbname).C("groups")
|
||||
GroupUsers = Session.DB(dbname).C("group_users")
|
||||
|
||||
@@ -111,7 +133,7 @@ func Init() {
|
||||
|
||||
// tag
|
||||
Tags = Session.DB(dbname).C("tags")
|
||||
// TagNotes = Session.DB(dbname).C("tag_notes")
|
||||
NoteTags = Session.DB(dbname).C("note_tags")
|
||||
TagCounts = Session.DB(dbname).C("tag_count")
|
||||
|
||||
// blog
|
||||
@@ -333,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("重连失败!!!! 警告")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"bufio"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// 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/"
|
||||
func parse(filename string) {
|
||||
file, err := os.Open(msgBasePath + filename)
|
||||
reader := bufio.NewReader(file)
|
||||
msg := map[string]string{}
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
for true {
|
||||
line, _, err := reader.ReadLine()
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
// 对每一行进行处理
|
||||
if line[0] == '#' || line[1] == '#' {
|
||||
continue;
|
||||
}
|
||||
lineStr := string(line)
|
||||
|
||||
// 找到第一个=位置
|
||||
pos := strings.Index(lineStr, "=")
|
||||
|
||||
if pos < 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
key := string(line[0:pos])
|
||||
value := string(line[pos+1:])
|
||||
|
||||
// fmt.Println(lineStr)
|
||||
// fmt.Println(value)
|
||||
|
||||
msg[key] = value
|
||||
}
|
||||
|
||||
// JSON
|
||||
b, _ := json.Marshal(msg)
|
||||
str := string(b)
|
||||
fmt.Println(str);
|
||||
|
||||
targetName := targetBasePath + filename + ".js"
|
||||
file2, err2 := os.OpenFile(targetName, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err2 != nil {
|
||||
file2, err2 = os.Create(targetName)
|
||||
}
|
||||
file2.WriteString("var MSG = " + str + ";" + `
|
||||
function getMsg(key, data) {
|
||||
var msg = MSG[key]
|
||||
if(msg) {
|
||||
if(data) {
|
||||
if(!isArray(data)) {
|
||||
data = [data];
|
||||
}
|
||||
for(var i = 0; i < data.length; ++i) {
|
||||
msg = msg.replace("%s", data[i]);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
return key;
|
||||
}`)
|
||||
}
|
||||
|
||||
// 生成js的i18n文件
|
||||
func main() {
|
||||
parse("msg.en")
|
||||
parse("msg.zh")
|
||||
parse("blog.zh")
|
||||
parse("blog.en")
|
||||
}
|
||||
119
app/info/Api.go
Normal file
119
app/info/Api.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
//---------
|
||||
// 数据结构
|
||||
//---------
|
||||
type NoteFile struct {
|
||||
FileId string // 服务器端Id
|
||||
LocalFileId string // 客户端Id
|
||||
Type string // images/png, doc, xls, 根据fileName确定
|
||||
Title string
|
||||
HasBody bool // 传过来的值是否要更新内容
|
||||
IsAttach bool // 是否是附件, 不是附件就是图片
|
||||
}
|
||||
type ApiNote struct {
|
||||
NoteId string
|
||||
NotebookId string
|
||||
UserId string
|
||||
Title string
|
||||
Desc string
|
||||
// ImgSrc string
|
||||
Tags []string
|
||||
Abstract string
|
||||
Content string
|
||||
IsMarkdown bool
|
||||
// FromUserId string // 为共享而新建
|
||||
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
|
||||
IsTrash bool
|
||||
IsDeleted bool
|
||||
Usn int
|
||||
Files []NoteFile
|
||||
CreatedTime time.Time
|
||||
UpdatedTime time.Time
|
||||
PublicTime time.Time
|
||||
}
|
||||
|
||||
// 内容
|
||||
type ApiNoteContent struct {
|
||||
NoteId bson.ObjectId `bson:"_id,omitempty"`
|
||||
UserId bson.ObjectId `bson:"UserId"`
|
||||
|
||||
Content string `Content`
|
||||
|
||||
// CreatedTime time.Time `CreatedTime`
|
||||
// UpdatedTime time.Time `UpdatedTime`
|
||||
}
|
||||
|
||||
// 转换
|
||||
func NoteToApiNote(note Note, files []NoteFile) ApiNote {
|
||||
apiNote := ApiNote{}
|
||||
return apiNote
|
||||
}
|
||||
|
||||
//----------
|
||||
// 用户信息
|
||||
//----------
|
||||
|
||||
type ApiUser struct {
|
||||
UserId string
|
||||
Username string
|
||||
Email string
|
||||
Verified bool
|
||||
Logo string
|
||||
}
|
||||
|
||||
//----------
|
||||
// Notebook
|
||||
//----------
|
||||
type ApiNotebook struct {
|
||||
NotebookId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
|
||||
UserId bson.ObjectId `bson:"UserId"`
|
||||
ParentNotebookId bson.ObjectId `bson:"ParentNotebookId,omitempty"` // 上级
|
||||
Seq int `Seq` // 排序
|
||||
Title string `Title` // 标题
|
||||
UrlTitle string `UrlTitle` // Url标题 2014/11.11加
|
||||
IsBlog bool `IsBlog,omitempty` // 是否是Blog 2013/12/29 新加
|
||||
CreatedTime time.Time `CreatedTime,omitempty`
|
||||
UpdatedTime time.Time `UpdatedTime,omitempty`
|
||||
Usn int `Usn` // UpdateSequenceNum
|
||||
IsDeleted bool `IsDeleted`
|
||||
}
|
||||
|
||||
//---------
|
||||
// api 返回
|
||||
//---------
|
||||
|
||||
// 一般返回
|
||||
type ApiRe struct {
|
||||
Ok bool
|
||||
Msg string
|
||||
}
|
||||
|
||||
func NewApiRe() ApiRe {
|
||||
return ApiRe{Ok: false}
|
||||
}
|
||||
|
||||
// auth
|
||||
type AuthOk struct {
|
||||
Ok bool
|
||||
Token string
|
||||
UserId bson.ObjectId
|
||||
Email string
|
||||
Username string
|
||||
}
|
||||
|
||||
// 供notebook, note, tag更新的返回数据用
|
||||
type ReUpdate struct {
|
||||
Ok bool
|
||||
Msg string
|
||||
Usn int
|
||||
}
|
||||
|
||||
func NewReUpdate() ReUpdate {
|
||||
return ReUpdate{Ok: false}
|
||||
}
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
|
||||
// Attach belongs to note
|
||||
type Attach struct {
|
||||
AttachId bson.ObjectId `bson:"_id,omitempty"` //
|
||||
NoteId bson.ObjectId `bson:"NoteId"` //
|
||||
UploadUserId bson.ObjectId `bson:"UploadUserId"` // 可以不是note owner, 协作者userId
|
||||
Name string `Name` // file name, md5, such as 13232312.doc
|
||||
Title string `Title` // raw file name
|
||||
Size int64 `Size` // file size (byte)
|
||||
Type string `Type` // file type, "doc" = word
|
||||
Path string `Path` // the file path such as: files/userId/attachs/adfadf.doc
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
|
||||
AttachId bson.ObjectId `bson:"_id,omitempty"` //
|
||||
NoteId bson.ObjectId `bson:"NoteId"` //
|
||||
UploadUserId bson.ObjectId `bson:"UploadUserId"` // 可以不是note owner, 协作者userId
|
||||
Name string `Name` // file name, md5, such as 13232312.doc
|
||||
Title string `Title` // raw file name
|
||||
Size int64 `Size` // file size (byte)
|
||||
Type string `Type` // file type, "doc" = word
|
||||
Path string `Path` // the file path such as: files/userId/attachs/adfadf.doc
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
|
||||
// FromFileId bson.ObjectId `bson:"FromFileId,omitempty"` // copy from fileId, for collaboration
|
||||
}
|
||||
|
||||
@@ -7,43 +7,52 @@ import (
|
||||
// 仅仅为了博客的主题
|
||||
|
||||
type BlogInfoCustom struct {
|
||||
UserId string
|
||||
Username string
|
||||
UserLogo string
|
||||
Title string
|
||||
SubTitle string
|
||||
Logo string
|
||||
UserId string
|
||||
Username string
|
||||
UserLogo string
|
||||
Title string
|
||||
SubTitle string
|
||||
Logo string
|
||||
OpenComment bool
|
||||
CommentType string
|
||||
ThemeId string
|
||||
SubDomain string
|
||||
Domain string
|
||||
ThemeId string
|
||||
SubDomain string
|
||||
Domain string
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
NoteId string
|
||||
Title string
|
||||
UrlTitle string
|
||||
ImgSrc string
|
||||
NoteId string
|
||||
Title string
|
||||
UrlTitle string
|
||||
ImgSrc string
|
||||
CreatedTime time.Time
|
||||
UpdatedTime time.Time
|
||||
PublicTime time.Time
|
||||
Desc string
|
||||
Abstract string
|
||||
Content string
|
||||
Tags []string
|
||||
CommentNum int
|
||||
ReadNum int
|
||||
LikeNum int
|
||||
IsMarkdown bool
|
||||
PublicTime time.Time
|
||||
Desc string
|
||||
Abstract string
|
||||
Content string
|
||||
Tags []string
|
||||
CommentNum int
|
||||
ReadNum int
|
||||
LikeNum int
|
||||
IsMarkdown bool
|
||||
}
|
||||
|
||||
// 归档
|
||||
type ArchiveMonth struct {
|
||||
Month int
|
||||
Posts []*Post
|
||||
}
|
||||
type Archive struct {
|
||||
Year int
|
||||
Year int
|
||||
MonthAchives []ArchiveMonth
|
||||
Posts []*Post
|
||||
Posts []*Post
|
||||
}
|
||||
|
||||
type Cate struct {
|
||||
CateId string
|
||||
ParentCateId string
|
||||
Title string
|
||||
UrlTitle string
|
||||
Children []*Cate
|
||||
}
|
||||
|
||||
@@ -9,17 +9,17 @@ import (
|
||||
|
||||
type BlogItem struct {
|
||||
Note
|
||||
Abstract string
|
||||
Content string // 可能是content的一部分, 截取. 点击more后就是整个信息了
|
||||
HasMore bool // 是否是否还有
|
||||
User User // 用户信息
|
||||
Abstract string
|
||||
Content string // 可能是content的一部分, 截取. 点击more后就是整个信息了
|
||||
HasMore bool // 是否是否还有
|
||||
User User // 用户信息
|
||||
}
|
||||
|
||||
type UserBlogBase struct {
|
||||
Logo string `Logo`
|
||||
Title string `Title` // 标题
|
||||
SubTitle string `SubTitle` // 副标题
|
||||
// AboutMe string `AboutMe` // 关于我
|
||||
// AboutMe string `AboutMe` // 关于我
|
||||
}
|
||||
|
||||
type UserBlogComment struct {
|
||||
@@ -49,32 +49,32 @@ type UserBlog struct {
|
||||
Style string `Style` // 风格
|
||||
Css string `Css` // 自定义css
|
||||
|
||||
ThemeId bson.ObjectId `ThemeId,omitempty` // 主题Id
|
||||
ThemePath string `bson:"ThemePath" json:"-"` // 不存值, 从Theme中获取, 相对路径 public/
|
||||
ThemeId bson.ObjectId `ThemeId,omitempty` // 主题Id
|
||||
ThemePath string `bson:"ThemePath" json:"-"` // 不存值, 从Theme中获取, 相对路径 public/
|
||||
|
||||
CateIds []string `CateIds,omitempty` // 分类Id, 排序好的
|
||||
Singles []map[string]string `Singles,omitempty` // 单页, 排序好的, map包含: ["Title"], ["SingleId"]
|
||||
|
||||
PerPageSize int `PerPageSize,omitempty`
|
||||
SortField string `SortField` // 排序字段
|
||||
IsAsc bool `IsAsc,omitempty` // 排序类型, 降序, 升序, 默认是false, 表示降序
|
||||
Singles []map[string]string `Singles,omitempty` // 单页, 排序好的, map包含: ["Title"], ["SingleId"]
|
||||
|
||||
PerPageSize int `PerPageSize,omitempty`
|
||||
SortField string `SortField` // 排序字段
|
||||
IsAsc bool `IsAsc,omitempty` // 排序类型, 降序, 升序, 默认是false, 表示降序
|
||||
|
||||
SubDomain string `SubDomain` // 二级域名
|
||||
Domain string `Domain` // 自定义域名
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 博客统计信息
|
||||
type BlogStat struct {
|
||||
NoteId bson.ObjectId `bson:"_id,omitempty"`
|
||||
ReadNum int `ReadNum,omitempty` // 阅读次数 2014/9/28
|
||||
LikeNum int `LikeNum,omitempty` // 点赞次数 2014/9/28
|
||||
CommentNum int `CommentNum,omitempty` // 评论次数 2014/9/28
|
||||
NoteId bson.ObjectId `bson:"_id,omitempty"`
|
||||
ReadNum int `ReadNum,omitempty` // 阅读次数 2014/9/28
|
||||
LikeNum int `LikeNum,omitempty` // 点赞次数 2014/9/28
|
||||
CommentNum int `CommentNum,omitempty` // 评论次数 2014/9/28
|
||||
}
|
||||
|
||||
// 单页
|
||||
type BlogSingle struct {
|
||||
SingleId bson.ObjectId `bson:"_id,omitempty"`
|
||||
SingleId bson.ObjectId `bson:"_id,omitempty"`
|
||||
UserId bson.ObjectId `UserId`
|
||||
Title string `Title`
|
||||
UrlTitle string `UrlTitle` // 2014/11/11
|
||||
@@ -117,12 +117,12 @@ type BlogCommentPublic struct {
|
||||
}
|
||||
|
||||
type BlogUrls struct {
|
||||
IndexUrl string
|
||||
CateUrl string
|
||||
SearchUrl string
|
||||
SingleUrl string
|
||||
PostUrl string
|
||||
ArchiveUrl string
|
||||
TagsUrl string
|
||||
IndexUrl string
|
||||
CateUrl string
|
||||
SearchUrl string
|
||||
SingleUrl string
|
||||
PostUrl string
|
||||
ArchiveUrl string
|
||||
TagsUrl string
|
||||
TagPostsUrl string
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ type Config struct {
|
||||
ConfigId bson.ObjectId `bson:"_id"`
|
||||
UserId bson.ObjectId `UserId`
|
||||
Key string `Key`
|
||||
ValueStr string `ValueStr,omitempty` // "1"
|
||||
ValueArr []string `ValueArr,omitempty` // ["1","b","c"]
|
||||
ValueMap map[string]string `ValueMap,omitempty` // {"a":"bb", "CC":"xx"}
|
||||
ValueStr string `ValueStr,omitempty` // "1"
|
||||
ValueArr []string `ValueArr,omitempty` // ["1","b","c"]
|
||||
ValueMap map[string]string `ValueMap,omitempty` // {"a":"bb", "CC":"xx"}
|
||||
ValueArrMap []map[string]string `ValueArrMap,omitempty` // [{"a":"B"}, {}, {}]
|
||||
IsArr bool `IsArr` // 是否是数组
|
||||
IsMap bool `IsMap` // 是否是Map
|
||||
|
||||
@@ -9,4 +9,4 @@ type NoteImage struct {
|
||||
NoteImageId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
|
||||
NoteId bson.ObjectId `bson:"NoteId"` // 笔记
|
||||
ImageId bson.ObjectId `bson:"ImageId"` // 图片fileId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ type Note struct {
|
||||
RecommendTime time.Time `RecommendTime,omitempty` // 推荐时间
|
||||
PublicTime time.Time `PublicTime,omitempty` // 发表时间, 公开为博客则设置
|
||||
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了
|
||||
|
||||
// 2015/1/15, 更新序号
|
||||
Usn int `Usn` // UpdateSequenceNum
|
||||
|
||||
IsDeleted bool `IsDeleted` // 删除位
|
||||
}
|
||||
|
||||
// 内容
|
||||
@@ -75,3 +80,24 @@ type NoteContentHistory struct {
|
||||
UserId bson.ObjectId `bson:"UserId"` // 所属者
|
||||
Histories []EachHistory `Histories`
|
||||
}
|
||||
|
||||
// 为了NoteController接收参数
|
||||
|
||||
// 更新note或content
|
||||
// 肯定会传userId(谁的), NoteId
|
||||
// 会传Title, Content, Tags, 一种或几种
|
||||
type NoteOrContent struct {
|
||||
NotebookId string
|
||||
NoteId string
|
||||
UserId string
|
||||
Title string
|
||||
Desc string
|
||||
ImgSrc string
|
||||
Tags string
|
||||
Content string
|
||||
Abstract string
|
||||
IsNew bool
|
||||
IsMarkdown bool
|
||||
FromUserId string // 为共享而新建
|
||||
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ type Notebook struct {
|
||||
IsBlog bool `IsBlog,omitempty` // 是否是Blog 2013/12/29 新加
|
||||
CreatedTime time.Time `CreatedTime,omitempty`
|
||||
UpdatedTime time.Time `UpdatedTime,omitempty`
|
||||
|
||||
// 2015/1/15, 更新序号
|
||||
Usn int `Usn` // UpdateSequenceNum
|
||||
IsDeleted bool `IsDeleted`
|
||||
}
|
||||
|
||||
// 仅仅是为了返回前台
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
)
|
||||
import ()
|
||||
|
||||
// controller ajax返回
|
||||
type Re struct {
|
||||
Ok bool
|
||||
Ok bool
|
||||
Code int
|
||||
Msg string
|
||||
Id string
|
||||
Msg string
|
||||
Id string
|
||||
List interface{}
|
||||
Item interface{}
|
||||
}
|
||||
|
||||
func NewRe() Re {
|
||||
return Re{Ok: false}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ type Session struct {
|
||||
LoginTimes int `LoginTimes` // 登录错误时间
|
||||
Captcha string `Captcha` // 验证码
|
||||
|
||||
UserId string `UserId` // API时有值UserId
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
UpdatedTime time.Time `UpdatedTime` // 更新时间, expire这个时间会自动清空
|
||||
}
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -2,6 +2,7 @@ package info
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 这里主要是为了统计每个tag的note数目
|
||||
@@ -17,17 +18,30 @@ type TagNote struct {
|
||||
|
||||
// 每个用户一条记录, 存储用户的所有tags
|
||||
type Tag struct {
|
||||
UserId bson.ObjectId `bson:"_id"`
|
||||
Tags []string `Tags`
|
||||
UserId bson.ObjectId `bson:"_id"`
|
||||
Tags []string `Tags`
|
||||
}
|
||||
|
||||
// v2 版标签
|
||||
type NoteTag struct {
|
||||
TagId bson.ObjectId `bson:"_id"`
|
||||
UserId bson.ObjectId `UserId` // 谁的
|
||||
Tag string `Tag` // UserId, Tag是唯一索引
|
||||
Usn int `Usn` // Update Sequence Number
|
||||
Count int `Count` // 笔记数
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
UpdatedTime time.Time `UpdatedTime`
|
||||
IsDeleted bool `IsDeleted` // 删除位
|
||||
}
|
||||
|
||||
type TagCount struct {
|
||||
TagCountId bson.ObjectId `bson:"_id,omitempty"`
|
||||
UserId bson.ObjectId `UserId` // 谁的
|
||||
Tag string `Tag`
|
||||
IsBlog bool `IsBlog` // 是否是博客的tag统计
|
||||
Count int `Count` // 统计数量
|
||||
UserId bson.ObjectId `UserId` // 谁的
|
||||
Tag string `Tag`
|
||||
IsBlog bool `IsBlog` // 是否是博客的tag统计
|
||||
Count int `Count` // 统计数量
|
||||
}
|
||||
|
||||
/*
|
||||
type TagsCounts []TagCount
|
||||
func (this TagsCounts) Len() int {
|
||||
@@ -39,4 +53,4 @@ func (this TagsCounts) Less(i, j int) bool {
|
||||
func (this TagsCounts) Swap(i, j int) {
|
||||
this[i], this[j] = this[j], this[i]
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
@@ -14,11 +14,11 @@ 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` // 是否在用
|
||||
|
||||
IsDefault bool `IsDefault` // leanote默认主题, 如果用户修改了默认主题, 则先copy之. 也是admin用户的主题
|
||||
IsDefault bool `IsDefault` // leanote默认主题, 如果用户修改了默认主题, 则先copy之. 也是admin用户的主题
|
||||
Style string `Style,omitempty` // 之前的, 只有default的用户才有blog_default, blog_daqi, blog_left_fixed
|
||||
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
|
||||
@@ -18,7 +18,7 @@ const (
|
||||
|
||||
// 过期时间
|
||||
const (
|
||||
PwdOverHours = 2.0
|
||||
PwdOverHours = 2.0
|
||||
ActiveEmailOverHours = 48.0
|
||||
UpdateEmailOverHours = 2.0
|
||||
)
|
||||
@@ -29,4 +29,4 @@ type Token struct {
|
||||
Token string `Token`
|
||||
Type int `Type`
|
||||
CreatedTime time.Time `CreatedTime`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,22 +36,26 @@ type User struct {
|
||||
ThirdType int `ThirdType` // 第三方类型
|
||||
|
||||
// 用户的帐户类型
|
||||
|
||||
ImageNum int `bson:"ImageNum" json:"-"` // 图片数量
|
||||
ImageSize int `bson:"ImageSize" json:"-"` // 图片大小
|
||||
AttachNum int `bson:"AttachNum" json:"-"` // 附件数量
|
||||
AttachSize int `bson:"AttachSize" json:"-"` // 附件大小
|
||||
PerAttachSize int `bson:"PerAttachSize" json:"-"` // 单个附件大小
|
||||
|
||||
ImageNum int `bson:"ImageNum" json:"-"` // 图片数量
|
||||
ImageSize int `bson:"ImageSize" json:"-"` // 图片大小
|
||||
AttachNum int `bson:"AttachNum" json:"-"` // 附件数量
|
||||
AttachSize int `bson:"AttachSize" json:"-"` // 附件大小
|
||||
FromUserId bson.ObjectId `FromUserId,omitempty` // 邀请的用户
|
||||
|
||||
AccountType string `bson:"AccountType" json:"-"` // normal(为空), premium
|
||||
AccountStartTime time.Time `bson:"AccountStartTime" json:"-"` // 开始日期
|
||||
AccountEndTime time.Time `bson:"AccountEndTime" json:"-"` // 结束日期
|
||||
// 阈值
|
||||
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
|
||||
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
|
||||
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
|
||||
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
|
||||
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
|
||||
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
|
||||
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
|
||||
// 2015/1/15, 更新序号
|
||||
Usn int `Usn` // UpdateSequenceNum , 全局的
|
||||
FullSyncBefore time.Time `bson:"FullSyncBefore"` // 需要全量同步的时间, 如果 > 客户端的LastSyncTime, 则需要全量更新
|
||||
}
|
||||
|
||||
type UserAccount struct {
|
||||
@@ -59,11 +63,18 @@ type UserAccount struct {
|
||||
AccountStartTime time.Time `bson:"AccountStartTime" json:"-"` // 开始日期
|
||||
AccountEndTime time.Time `bson:"AccountEndTime" json:"-"` // 结束日期
|
||||
// 阈值
|
||||
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
|
||||
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
|
||||
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
|
||||
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
|
||||
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
|
||||
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
|
||||
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
}
|
||||
|
||||
// note主页需要
|
||||
type UserAndBlogUrl struct {
|
||||
User
|
||||
BlogUrl string `BlogUrl`
|
||||
PostUrl string `PostUrl`
|
||||
}
|
||||
|
||||
// 用户与博客信息结合, 公开
|
||||
@@ -75,6 +86,6 @@ type UserAndBlog struct {
|
||||
BlogTitle string `BlogTitle` // 博客标题
|
||||
BlogLogo string `BlogLogo` // 博客Logo
|
||||
BlogUrl string `BlogUrl` // 博客链接, 主页
|
||||
|
||||
|
||||
BlogUrls // 各个页面
|
||||
}
|
||||
|
||||
@@ -4,14 +4,13 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
|
||||
// 分页数据
|
||||
type Page struct {
|
||||
CurPage int // 当前页码
|
||||
TotalPage int // 总页
|
||||
CurPage int // 当前页码
|
||||
TotalPage int // 总页
|
||||
PerPageSize int
|
||||
Count int // 总记录数
|
||||
List interface{}
|
||||
Count int // 总记录数
|
||||
List interface{}
|
||||
}
|
||||
|
||||
func NewPage(page, perPageSize, count int, list interface{}) Page {
|
||||
|
||||
344
app/init.go
344
app/init.go
@@ -1,66 +1,79 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
"github.com/leanote/leanote/app/controllers/admin"
|
||||
"github.com/leanote/leanote/app/controllers/api"
|
||||
"github.com/leanote/leanote/app/controllers/member"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
_ "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/i18n"
|
||||
"github.com/leanote/leanote/app/lea/route"
|
||||
"reflect"
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
"github.com/revel/revel"
|
||||
"html/template"
|
||||
"math"
|
||||
"strings"
|
||||
"strconv"
|
||||
"time"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Filters is the default set of global filters.
|
||||
revel.Filters = []revel.Filter{
|
||||
revel.PanicFilter, // Recover from panics and display an error page instead.
|
||||
revel.PanicFilter, // Recover from panics and display an error page instead.
|
||||
route.RouterFilter,
|
||||
// revel.RouterFilter, // Use the routing table to select the right Action
|
||||
// AuthFilter, // Invoke the action.
|
||||
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
|
||||
revel.ParamsFilter, // Parse parameters into Controller.Params.
|
||||
// revel.SessionFilter, // Restore and write the session cookie.
|
||||
|
||||
revel.SessionFilter, // Restore and write the session cookie.
|
||||
|
||||
// 使用SessionFilter标准版从cookie中得到sessionID, 然后通过MssessionFilter从Memcache中得到
|
||||
// session, 之后MSessionFilter将session只存sessionID然后返回给SessionFilter返回到web
|
||||
session.SessionFilter, // leanote session
|
||||
// session.MSessionFilter, // leanote memcache session
|
||||
|
||||
revel.FlashFilter, // Restore and write the flash cookie.
|
||||
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
|
||||
revel.I18nFilter, // Resolve the requested language
|
||||
revel.InterceptorFilter, // Run interceptors around the action.
|
||||
revel.CompressFilter, // Compress the result.
|
||||
revel.ActionInvoker, // Invoke the action.
|
||||
// session.SessionFilter, // leanote session
|
||||
// session.MSessionFilter, // leanote memcache session
|
||||
|
||||
revel.FlashFilter, // Restore and write the flash cookie.
|
||||
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
|
||||
// revel.I18nFilter, // Resolve the requested language
|
||||
i18n.I18nFilter, // Resolve the requested language by leanote
|
||||
revel.InterceptorFilter, // Run interceptors around the action.
|
||||
revel.CompressFilter, // Compress the result.
|
||||
revel.ActionInvoker, // Invoke the action.
|
||||
}
|
||||
|
||||
|
||||
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, " ")
|
||||
|
||||
// 以下两个空格不一样
|
||||
str = strings.Trim(str, " ")
|
||||
str = strings.Trim(str, " ")
|
||||
return str
|
||||
}
|
||||
revel.TemplateFuncs["add"] = func(i int) string {
|
||||
i = i + 1;
|
||||
i = i + 1
|
||||
return fmt.Sprintf("%v", i)
|
||||
}
|
||||
revel.TemplateFuncs["sub"] = func(i int) int {
|
||||
i = i - 1;
|
||||
i = i - 1
|
||||
return i
|
||||
}
|
||||
// 增加或减少
|
||||
revel.TemplateFuncs["incr"] = func(n, i int) int {
|
||||
n = n + i;
|
||||
n = n + i
|
||||
return n
|
||||
}
|
||||
revel.TemplateFuncs["join"] = func(arr []string) template.HTML {
|
||||
@@ -84,11 +97,11 @@ func init() {
|
||||
return v.Get("a")
|
||||
}
|
||||
revel.TemplateFuncs["json"] = func(i interface{}) string {
|
||||
b, _ := json.Marshal(i)
|
||||
b, _ := json.Marshal(i)
|
||||
return string(b)
|
||||
}
|
||||
revel.TemplateFuncs["jsonJs"] = func(i interface{}) template.JS {
|
||||
b, _ := json.Marshal(i)
|
||||
b, _ := json.Marshal(i)
|
||||
return template.JS(string(b))
|
||||
}
|
||||
revel.TemplateFuncs["datetime"] = func(t time.Time) template.HTML {
|
||||
@@ -102,15 +115,16 @@ func init() {
|
||||
t := time.Unix(int64(sec), 0)
|
||||
return template.HTML(t.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
|
||||
// interface是否有该字段
|
||||
revel.TemplateFuncs["has"] = func(i interface{}, key string) bool {
|
||||
t := reflect.TypeOf(i)
|
||||
t := reflect.TypeOf(i)
|
||||
_, ok := t.FieldByName(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
// tags
|
||||
// 2014/12/30 标签添加链接
|
||||
revel.TemplateFuncs["blogTags"] = func(renderArgs map[string]interface{}, tags []string) template.HTML {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return ""
|
||||
@@ -118,70 +132,165 @@ func init() {
|
||||
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
tagStr := ""
|
||||
lenTags := len(tags)
|
||||
|
||||
tagPostUrl, _ := renderArgs["tagPostsUrl"].(string)
|
||||
|
||||
for i, tag := range tags {
|
||||
str := revel.Message(locale, tag)
|
||||
var classes = "label"
|
||||
if strings.HasPrefix(str, "???") {
|
||||
str = tag
|
||||
}
|
||||
tagStr += str
|
||||
if i != lenTags - 1 {
|
||||
tagStr += ","
|
||||
if InArray([]string{"red", "blue", "yellow", "green"}, tag) {
|
||||
classes += " label-" + tag
|
||||
} else {
|
||||
classes += " label-default"
|
||||
}
|
||||
|
||||
classes += " label-post"
|
||||
var url = tagPostUrl + "/" + url.QueryEscape(tag)
|
||||
tagStr += "<a class=\"" + classes + "\" href=\"" + url + "\">" + str + "</a>"
|
||||
if i != lenTags-1 {
|
||||
tagStr += " "
|
||||
}
|
||||
}
|
||||
return template.HTML(tagStr)
|
||||
}
|
||||
/*
|
||||
revel.TemplateFuncs["blogTags"] = func(tags []string) template.HTML {
|
||||
|
||||
revel.TemplateFuncs["blogTagsForExport"] = func(renderArgs map[string]interface{}, tags []string) template.HTML {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
// TODO 这里判断语言, 从语言包中拿
|
||||
tagMap := map[string]string{"red": "红色", "yellow": "黄色", "blue": "蓝色", "green": "绿色"}
|
||||
tagStr := ""
|
||||
lenTags := len(tags)
|
||||
|
||||
for i, tag := range tags {
|
||||
if text, ok := tagMap[tag]; ok {
|
||||
tagStr += text
|
||||
str := tag
|
||||
var classes = "label"
|
||||
if InArray([]string{"red", "blue", "yellow", "green"}, tag) {
|
||||
classes += " label-" + tag
|
||||
} else {
|
||||
tagStr += tag
|
||||
classes += " label-default"
|
||||
}
|
||||
if i != lenTags - 1 {
|
||||
tagStr += ","
|
||||
|
||||
classes += " label-post"
|
||||
tagStr += "<span class=\"" + classes + "\" >" + str + "</span>"
|
||||
if i != lenTags-1 {
|
||||
tagStr += " "
|
||||
}
|
||||
}
|
||||
return template.HTML(tagStr)
|
||||
}
|
||||
|
||||
revel.TemplateFuncs["msg"] = func(renderArgs map[string]interface{}, message string, args ...interface{}) template.HTML {
|
||||
str, ok := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return template.HTML(i18n.Message(str, message, args...))
|
||||
}
|
||||
|
||||
// 不用revel的msg
|
||||
revel.TemplateFuncs["leaMsg"] = func(renderArgs map[string]interface{}, key string) template.HTML {
|
||||
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
str := i18n.Message(locale, key)
|
||||
if strings.HasPrefix(str, "???") {
|
||||
str = key
|
||||
}
|
||||
return template.HTML(str)
|
||||
}
|
||||
|
||||
// lea++
|
||||
revel.TemplateFuncs["blogTagsLea"] = func(renderArgs map[string]interface{}, tags []string, typeStr string) template.HTML {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
tagStr := ""
|
||||
lenTags := len(tags)
|
||||
|
||||
tagPostUrl := "http://lea.leanote.com/"
|
||||
if typeStr == "recommend" {
|
||||
tagPostUrl += "?tag="
|
||||
} else if typeStr == "latest" {
|
||||
tagPostUrl += "latest?tag="
|
||||
} else {
|
||||
tagPostUrl += "subscription?tag="
|
||||
}
|
||||
|
||||
for i, tag := range tags {
|
||||
str := revel.Message(locale, tag)
|
||||
var classes = "label"
|
||||
if strings.HasPrefix(str, "???") {
|
||||
str = tag
|
||||
}
|
||||
if InArray([]string{"red", "blue", "yellow", "green"}, tag) {
|
||||
classes += " label-" + tag
|
||||
} else {
|
||||
classes += " label-default"
|
||||
}
|
||||
classes += " label-post"
|
||||
var url = tagPostUrl + url.QueryEscape(tag)
|
||||
tagStr += "<a class=\"" + classes + "\" href=\"" + url + "\">" + str + "</a>"
|
||||
if i != lenTags-1 {
|
||||
tagStr += " "
|
||||
}
|
||||
}
|
||||
return template.HTML(tagStr)
|
||||
}
|
||||
|
||||
/*
|
||||
revel.TemplateFuncs["blogTags"] = func(tags []string) template.HTML {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
// TODO 这里判断语言, 从语言包中拿
|
||||
tagMap := map[string]string{"red": "红色", "yellow": "黄色", "blue": "蓝色", "green": "绿色"}
|
||||
tagStr := ""
|
||||
lenTags := len(tags)
|
||||
for i, tag := range tags {
|
||||
if text, ok := tagMap[tag]; ok {
|
||||
tagStr += text
|
||||
} else {
|
||||
tagStr += tag
|
||||
}
|
||||
if i != lenTags - 1 {
|
||||
tagStr += ","
|
||||
}
|
||||
}
|
||||
return template.HTML(tagStr)
|
||||
}
|
||||
*/
|
||||
revel.TemplateFuncs["li"] = func(a string) string {
|
||||
return ""
|
||||
}
|
||||
// str连接
|
||||
revel.TemplateFuncs["urlConcat"] = func(url string, v... interface{}) string {
|
||||
revel.TemplateFuncs["urlConcat"] = func(url string, v ...interface{}) string {
|
||||
html := ""
|
||||
for i := 0; i < len(v); i = i + 2 {
|
||||
item := v[i]
|
||||
if i+1 == len(v) {
|
||||
break;
|
||||
break
|
||||
}
|
||||
value := v[i+1]
|
||||
if item != nil && value != nil {
|
||||
keyStr, _ := item.(string)
|
||||
valueStr, err := value.(string)
|
||||
if !err {
|
||||
valueInt, _ := value.(int)
|
||||
valueStr = strconv.Itoa(valueInt)
|
||||
}
|
||||
if keyStr != "" && valueStr != "" {
|
||||
s := keyStr + "=" + valueStr
|
||||
if html != "" {
|
||||
html += "&" + s
|
||||
} else {
|
||||
html += s
|
||||
}
|
||||
}
|
||||
}
|
||||
keyStr, _ := item.(string)
|
||||
valueStr, err := value.(string)
|
||||
if !err {
|
||||
valueInt, _ := value.(int)
|
||||
valueStr = strconv.Itoa(valueInt)
|
||||
}
|
||||
if keyStr != "" && valueStr != "" {
|
||||
s := keyStr + "=" + valueStr
|
||||
if html != "" {
|
||||
html += "&" + s
|
||||
} else {
|
||||
html += s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if html != "" {
|
||||
if strings.Index(url, "?") >= 0 {
|
||||
return url + "&" + html
|
||||
@@ -191,11 +300,11 @@ func init() {
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
|
||||
revel.TemplateFuncs["urlCond"] = func(url string, sorterI, keyords interface{}) template.HTML {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
// http://stackoverflow.com/questions/14226416/go-lang-templates-always-quotes-a-string-and-removes-comments
|
||||
revel.TemplateFuncs["rawMsg"] = func(renderArgs map[string]interface{}, message string, args ...interface{}) template.JS {
|
||||
str, ok := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
@@ -204,38 +313,38 @@ func init() {
|
||||
}
|
||||
return template.JS(revel.Message(str, message, args...))
|
||||
}
|
||||
|
||||
|
||||
// 为后台管理sorter th使用
|
||||
// 必须要返回HTMLAttr, 返回html, golang 会执行安全检查返回ZgotmplZ
|
||||
// sorterI 可能是nil, 所以用interfalce{}来接收
|
||||
/*
|
||||
data-url="/adminUser/index"
|
||||
data-sorter="email"
|
||||
class="th-sortable {{if eq .sorter "email-up"}}th-sort-up{{else}}{{if eq .sorter "email-down"}}th-sort-down{{end}}{{end}}"
|
||||
data-url="/adminUser/index"
|
||||
data-sorter="email"
|
||||
class="th-sortable {{if eq .sorter "email-up"}}th-sort-up{{else}}{{if eq .sorter "email-down"}}th-sort-down{{end}}{{end}}"
|
||||
*/
|
||||
revel.TemplateFuncs["sorterTh"] = func(url, sorterField string, sorterI interface{}) template.HTMLAttr {
|
||||
sorter := ""
|
||||
if sorterI != nil {
|
||||
sorter, _ = sorterI.(string)
|
||||
}
|
||||
html := "data-url=\"" + url + "\" data-sorter=\"" + sorterField + "\"";
|
||||
html += " class=\"th-sortable ";
|
||||
if sorter == sorterField + "-up" {
|
||||
html += "th-sort-up\"";
|
||||
} else if(sorter == sorterField + "-down") {
|
||||
html += "th-sort-down";
|
||||
html := "data-url=\"" + url + "\" data-sorter=\"" + sorterField + "\""
|
||||
html += " class=\"th-sortable "
|
||||
if sorter == sorterField+"-up" {
|
||||
html += "th-sort-up\""
|
||||
} else if sorter == sorterField+"-down" {
|
||||
html += "th-sort-down"
|
||||
}
|
||||
html += "\"";
|
||||
html += "\""
|
||||
return template.HTMLAttr(html)
|
||||
}
|
||||
|
||||
|
||||
// pagination
|
||||
revel.TemplateFuncs["page"] = func(urlBase string, page, pageSize, count int) template.HTML {
|
||||
if count == 0 {
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
totalPage := int(math.Ceil(float64(count)/float64(pageSize)))
|
||||
|
||||
totalPage := int(math.Ceil(float64(count) / float64(pageSize)))
|
||||
|
||||
preClass := ""
|
||||
prePage := page - 1
|
||||
if prePage == 0 {
|
||||
@@ -244,10 +353,10 @@ func init() {
|
||||
nextClass := ""
|
||||
nextPage := page + 1
|
||||
var preUrl, nextUrl string
|
||||
|
||||
preUrl = urlBase + "?page=" + strconv.Itoa(prePage)
|
||||
|
||||
preUrl = urlBase + "?page=" + strconv.Itoa(prePage)
|
||||
nextUrl = urlBase + "?page=" + strconv.Itoa(nextPage)
|
||||
|
||||
|
||||
// 没有上一页了
|
||||
if page == 1 {
|
||||
preClass = "disabled"
|
||||
@@ -265,62 +374,63 @@ func init() {
|
||||
// http://play.golang.org/p/snygrVpQva
|
||||
// http://grokbase.com/t/gg/golang-nuts/142a6dhfh3/go-nuts-text-template-using-comparison-operators-eq-gt-etc-on-non-existent-variable-causes-the-template-to-stop-outputting-but-with-no-error-correct-behaviour
|
||||
/*
|
||||
revel.TemplateFuncs["gt"] = func(a1, a2 interface{}) bool {
|
||||
switch a1.(type) {
|
||||
case string:
|
||||
switch a2.(type) {
|
||||
revel.TemplateFuncs["gt"] = func(a1, a2 interface{}) bool {
|
||||
switch a1.(type) {
|
||||
case string:
|
||||
return reflect.ValueOf(a1).String() > reflect.ValueOf(a2).String()
|
||||
}
|
||||
case int, int8, int16, int32, int64:
|
||||
switch a2.(type) {
|
||||
switch a2.(type) {
|
||||
case string:
|
||||
return reflect.ValueOf(a1).String() > reflect.ValueOf(a2).String()
|
||||
}
|
||||
case int, int8, int16, int32, int64:
|
||||
return reflect.ValueOf(a1).Int() > reflect.ValueOf(a2).Int()
|
||||
}
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
switch a2.(type) {
|
||||
switch a2.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
return reflect.ValueOf(a1).Int() > reflect.ValueOf(a2).Int()
|
||||
}
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
return reflect.ValueOf(a1).Uint() > reflect.ValueOf(a2).Uint()
|
||||
}
|
||||
case float32, float64:
|
||||
switch a2.(type) {
|
||||
switch a2.(type) {
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
return reflect.ValueOf(a1).Uint() > reflect.ValueOf(a2).Uint()
|
||||
}
|
||||
case float32, float64:
|
||||
return reflect.ValueOf(a1).Float() > reflect.ValueOf(a2).Float()
|
||||
switch a2.(type) {
|
||||
case float32, float64:
|
||||
return reflect.ValueOf(a1).Float() > reflect.ValueOf(a2).Float()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
{{range $i := N 1 10}}
|
||||
<div>{{$i}}</div>
|
||||
{{end}}
|
||||
*/
|
||||
{{range $i := N 1 10}}
|
||||
<div>{{$i}}</div>
|
||||
{{end}}
|
||||
*/
|
||||
revel.TemplateFuncs["N"] = func(start, end int) (stream chan int) {
|
||||
stream = make(chan int)
|
||||
go func() {
|
||||
for i := start; i <= end; i++ {
|
||||
stream <- i
|
||||
}
|
||||
close(stream)
|
||||
}()
|
||||
return
|
||||
stream = make(chan int)
|
||||
go func() {
|
||||
for i := start; i <= end; i++ {
|
||||
stream <- i
|
||||
}
|
||||
close(stream)
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// init Email
|
||||
revel.OnAppStart(func() {
|
||||
// 数据库
|
||||
db.Init()
|
||||
db.Init("", "")
|
||||
// email配置
|
||||
InitEmail()
|
||||
InitVd()
|
||||
memcache.InitMemcache() // session服务
|
||||
// memcache.InitMemcache() // session服务
|
||||
// 其它service
|
||||
service.InitService()
|
||||
controllers.InitService()
|
||||
admin.InitService()
|
||||
member.InitService()
|
||||
service.ConfigS.InitGlobalConfigs()
|
||||
api.InitService()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,15 +1,34 @@
|
||||
package lea
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/revel/revel"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
func Log(i interface{}) {
|
||||
revel.INFO.Println(i)
|
||||
func Log(i ...interface{}) {
|
||||
revel.INFO.Println(i...)
|
||||
}
|
||||
|
||||
func LogW(i ...interface{}) {
|
||||
revel.WARN.Println(i...)
|
||||
}
|
||||
|
||||
func Log1(key, i interface{}) {
|
||||
revel.INFO.Println(key, i)
|
||||
}
|
||||
|
||||
func LogJ(i interface{}) {
|
||||
b, _ := json.MarshalIndent(i, "", " ")
|
||||
revel.INFO.Println(string(b))
|
||||
}
|
||||
b, _ := json.MarshalIndent(i, "", " ")
|
||||
revel.INFO.Println(string(b))
|
||||
}
|
||||
|
||||
// 为test用
|
||||
func L(i interface{}) {
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
func LJ(i interface{}) {
|
||||
b, _ := json.MarshalIndent(i, "", " ")
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package lea
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
// 发送邮件
|
||||
var host = "smtp.ym.163.com"
|
||||
var port = "25"
|
||||
@@ -12,7 +13,7 @@ var username = "noreply@leanote.com"
|
||||
var password = "---"
|
||||
|
||||
func InitEmail() {
|
||||
config := revel.Config;
|
||||
config := revel.Config
|
||||
host, _ = config.String("email.host")
|
||||
port, _ = config.String("email.port")
|
||||
username, _ = config.String("email.username")
|
||||
@@ -56,26 +57,27 @@ var bodyTpl = `
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
func SendEmailOld(to, subject, body string) bool {
|
||||
hp := strings.Split(host, ":")
|
||||
auth := smtp.PlainAuth("", username, password, hp[0])
|
||||
|
||||
|
||||
var content_type string
|
||||
|
||||
|
||||
mailtype := "html"
|
||||
if mailtype == "html" {
|
||||
content_type = "Content-Type: text/"+ mailtype + "; charset=UTF-8"
|
||||
} else{
|
||||
content_type = "Content-Type: text/" + mailtype + "; charset=UTF-8"
|
||||
} else {
|
||||
content_type = "Content-Type: text/plain" + "; charset=UTF-8"
|
||||
}
|
||||
|
||||
|
||||
//body = strings.Replace(bodyTpl, "$body", body, 1)
|
||||
//body = strings.Replace(body, "$title", title, 1)
|
||||
|
||||
msg := []byte("To: " + to + "\r\nFrom: " + username + "<"+ username +">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body)
|
||||
msg := []byte("To: " + to + "\r\nFrom: " + username + "<" + username + ">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body)
|
||||
send_to := strings.Split(to, ";")
|
||||
err := smtp.SendMail(host+":"+port, auth, username, send_to, msg)
|
||||
|
||||
|
||||
if err != nil {
|
||||
Log(err)
|
||||
return false
|
||||
@@ -85,5 +87,5 @@ func SendEmailOld(to, subject, body string) bool {
|
||||
|
||||
func SendToLeanoteOld(subject, title, body string) {
|
||||
to := "leanote@leanote.com"
|
||||
SendEmailOld(to, subject, body);
|
||||
}
|
||||
SendEmailOld(to, subject, body)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -108,13 +116,13 @@ func CopyDir(source string, dest string) (err error) {
|
||||
// create sub-directories - recursively
|
||||
err = CopyDir(sourcefilepointer, destinationfilepointer)
|
||||
if err != nil {
|
||||
// fmt.Println(err)
|
||||
// fmt.Println(err)
|
||||
}
|
||||
} else {
|
||||
// perform copy
|
||||
_, err = CopyFile(sourcefilepointer, destinationfilepointer)
|
||||
if err != nil {
|
||||
// fmt.Println(err)
|
||||
// fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func TransToGif(path string, maxWidth uint, afterDelete bool) (ok bool, transPat
|
||||
transPath = path
|
||||
wand.Genesis()
|
||||
defer wand.Terminus()
|
||||
|
||||
|
||||
w := wand.NewMagickWand()
|
||||
defer w.Destroy()
|
||||
|
||||
@@ -24,7 +24,7 @@ func TransToGif(path string, maxWidth uint, afterDelete bool) (ok bool, transPat
|
||||
fmt.Println(err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
width := w.ImageWidth()
|
||||
height := w.ImageHeight()
|
||||
if maxWidth != 0 {
|
||||
@@ -34,14 +34,14 @@ func TransToGif(path string, maxWidth uint, afterDelete bool) (ok bool, transPat
|
||||
width = maxWidth
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
w.SetImageFormat("GIF");
|
||||
|
||||
if err := paint.Thumbnail(w, width, height); err != nil {
|
||||
fmt.Println(err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 判断是否是gif图片, 是就不用转换了
|
||||
baseName, ext := SplitFilename(path)
|
||||
var toPath string
|
||||
@@ -50,19 +50,19 @@ func TransToGif(path string, maxWidth uint, afterDelete bool) (ok bool, transPat
|
||||
} else {
|
||||
toPath = TransferExt(path, ".gif");
|
||||
}
|
||||
|
||||
|
||||
if err := w.WriteImage(toPath); err != nil {
|
||||
fmt.Println(err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if afterDelete {
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
|
||||
ok = true
|
||||
transPath = toPath
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func TransToGif(path string, maxWidth uint, afterDelete bool) (ok bool, transPat
|
||||
func Reset(path string, maxWidth uint) (ok bool, transPath string){
|
||||
wand.Genesis()
|
||||
defer wand.Terminus()
|
||||
|
||||
|
||||
w := wand.NewMagickWand()
|
||||
defer w.Destroy()
|
||||
|
||||
@@ -79,7 +79,7 @@ func Reset(path string, maxWidth uint) (ok bool, transPath string){
|
||||
fmt.Println(err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
width := w.ImageWidth()
|
||||
height := w.ImageHeight()
|
||||
if maxWidth != 0 {
|
||||
@@ -93,20 +93,20 @@ func Reset(path string, maxWidth uint) (ok bool, transPath string){
|
||||
fmt.Println(err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
toPath := TransferExt(path, ".gif");
|
||||
if err := w.WriteImage(toPath); err != nil {
|
||||
fmt.Println(err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ok = true
|
||||
transPath = toPath
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
func TransToGif(path string, maxWidth uint, afterDelete bool) (ok bool, transPath string) {
|
||||
return ok, path
|
||||
}
|
||||
}
|
||||
|
||||
22
app/lea/Pwd.go
Normal file
22
app/lea/Pwd.go
Normal 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)
|
||||
}
|
||||
230
app/lea/Util.go
230
app/lea/Util.go
@@ -1,16 +1,19 @@
|
||||
package lea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"fmt"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"io"
|
||||
math_rand "math/rand"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
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)
|
||||
@@ -58,7 +77,7 @@ func Substr(str string, start, length int) string {
|
||||
func substr(str string, start, length int, isRune bool) string {
|
||||
rs := []rune(str)
|
||||
rs2 := []byte(str)
|
||||
|
||||
|
||||
rl := len(rs)
|
||||
if !isRune {
|
||||
rl = len(rs2)
|
||||
@@ -122,39 +141,49 @@ func ReplaceAll(oldStr, pattern, newStr string) string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func SubStringHTML(param string, length int, end string) string {
|
||||
// 先取出<pre></pre>占位..
|
||||
|
||||
result := ""
|
||||
|
||||
// 1
|
||||
// 获取纯文本
|
||||
func SubStringHTMLToRaw(param string, length int) string {
|
||||
if param == "" {
|
||||
return param
|
||||
}
|
||||
n := 0
|
||||
var temp rune // 中文问题, 用rune来解决
|
||||
isCode := false //是不是HTML代码
|
||||
isHTML := false //是不是HTML特殊字符,如
|
||||
rStr := []rune(param)
|
||||
for i := 0; i < len(rStr); i++ {
|
||||
lenStr := len(rStr)
|
||||
isCode := false
|
||||
|
||||
resultRune := make([]rune, length)
|
||||
// s := ""
|
||||
for i := 0; i < lenStr; i++ {
|
||||
temp = rStr[i]
|
||||
if temp == '<' {
|
||||
isCode = true
|
||||
} else if temp == '&' {
|
||||
isHTML = true
|
||||
} else if temp == '>' && isCode {
|
||||
n = n - 1
|
||||
continue
|
||||
} else if temp == '>' {
|
||||
isCode = false
|
||||
} else if temp == ';' && isHTML {
|
||||
isHTML = false
|
||||
resultRune[n] = ' '
|
||||
|
||||
n++
|
||||
if n >= length {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !isCode && !isHTML {
|
||||
n = n + 1
|
||||
}
|
||||
result += string(temp)
|
||||
if n >= length {
|
||||
break
|
||||
if !isCode {
|
||||
resultRune[n] = temp
|
||||
// s += string(temp)
|
||||
n++
|
||||
if n >= length {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
result += end
|
||||
|
||||
result := string(resultRune[0:n])
|
||||
return strings.Trim(result, " ")
|
||||
}
|
||||
|
||||
// 自带方法补全html
|
||||
func fixHtml(result string) string {
|
||||
// 取出所有标签
|
||||
tempResult := ReplaceAll(result, "(>)[^<>]*(<?)", "$1$2") // 把标签中间的所有内容都去掉了
|
||||
|
||||
@@ -163,13 +192,13 @@ func SubStringHTML(param string, length int, end string) string {
|
||||
|
||||
// 把<div class=xxx的class=xxx去掉
|
||||
tempResult = ReplaceAll(tempResult, "<(/?[a-zA-Z]+)[^<>]*>", "<$1>")
|
||||
|
||||
|
||||
// 3 只能用正则,+stack来去有结束的
|
||||
// golang的正则暂不支持back reference, 以后可以用它来去掉重复的标签
|
||||
p, _ := regexp.Compile("<(/?[a-zA-Z]+)[^<>]*>") // 得到所有的<div>, </div>...
|
||||
strs := p.FindAllString(tempResult, -1)
|
||||
|
||||
// fmt.Println(strs)
|
||||
// fmt.Println(strs)
|
||||
stack := make([]string, len(strs))
|
||||
stackP := -1
|
||||
for _, each := range strs {
|
||||
@@ -186,16 +215,82 @@ func SubStringHTML(param string, length int, end string) string {
|
||||
// 补全tag
|
||||
if stackP != -1 {
|
||||
fmt.Println(stack[0 : stackP+1])
|
||||
|
||||
|
||||
for _, each := range stack[0 : stackP+1] {
|
||||
if each[1] != '/' {
|
||||
result += "</" + each[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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特殊字符,如
|
||||
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 == "" {
|
||||
@@ -210,16 +305,16 @@ func IsGoodPwd(pwd string) (bool, string) {
|
||||
// 是否是email
|
||||
func IsEmail(email string) bool {
|
||||
if email == "" {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-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
|
||||
}
|
||||
|
||||
// 是否只包含数字, 字母 -, _
|
||||
func IsUsername(username string) bool {
|
||||
if username == "" {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
ok, _ := regexp.MatchString(`[^0-9a-zA-Z_\-]`, username)
|
||||
return !ok
|
||||
@@ -256,15 +351,15 @@ func RandomPwd(num int) string {
|
||||
chars[j] = byte(i)
|
||||
j++
|
||||
}
|
||||
j--;
|
||||
|
||||
j--
|
||||
|
||||
str := ""
|
||||
math_rand.Seed(time.Now().UnixNano())
|
||||
for i := 0; i < num; i++ {
|
||||
x := math_rand.Intn(j)
|
||||
str += string(chars[x])
|
||||
}
|
||||
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
@@ -278,4 +373,61 @@ 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
|
||||
}
|
||||
|
||||
// 是否是合法的时间
|
||||
// 不是, 0001-01-01T00:00:00Z, 且比今天小
|
||||
func IsValidTime(t time.Time) bool {
|
||||
if t.Year() > 20 {
|
||||
now := time.Now()
|
||||
Log("------")
|
||||
Log(t)
|
||||
Log(now)
|
||||
if t.Before(now) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// url传过来的时间没有时区信息, 转到本地时间
|
||||
func ToLocalTime(t time.Time) time.Time {
|
||||
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, time.Local)
|
||||
}
|
||||
|
||||
// 修复传过来的时间, 如果比今天大, 则设为现在
|
||||
func FixUrlTime(t time.Time) time.Time {
|
||||
localTime := ToLocalTime(t)
|
||||
if IsValidTime(localTime) {
|
||||
return localTime
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// 得到用户的随机文件路径 3位/userId/2位
|
||||
func GetRandomFilePath(userId, uuid string) string {
|
||||
if uuid == "" {
|
||||
uuid = NewGuid()
|
||||
}
|
||||
return Digest3(userId) + "/" + userId + "/" + Digest2(uuid)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package lea
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// 验证
|
||||
@@ -38,15 +38,15 @@ var rulesStr = `{
|
||||
`
|
||||
var rulesMap map[string][]map[string]string
|
||||
|
||||
var rules = map[string]func(string, map[string]string)(bool, string) {
|
||||
"required": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
var rules = map[string]func(string, map[string]string) (bool, string){
|
||||
"required": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
return
|
||||
},
|
||||
"minLength": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
"minLength": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
@@ -55,7 +55,7 @@ var rules = map[string]func(string, map[string]string)(bool, string) {
|
||||
ok = len(value) >= dataI
|
||||
return
|
||||
},
|
||||
"min": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
"min": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
@@ -65,8 +65,8 @@ var rules = map[string]func(string, map[string]string)(bool, string) {
|
||||
ok = vI >= dataI
|
||||
return
|
||||
},
|
||||
|
||||
"sortField": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
|
||||
"sortField": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
@@ -74,22 +74,22 @@ var rules = map[string]func(string, map[string]string)(bool, string) {
|
||||
ok = InArray(sortFields, value)
|
||||
return
|
||||
},
|
||||
|
||||
"password": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
|
||||
"password": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
ok = len(value) >= 6
|
||||
return
|
||||
},
|
||||
"email": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
"email": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
ok = IsEmail(value)
|
||||
return
|
||||
},
|
||||
"noSpecialChars": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
"noSpecialChars": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
@@ -97,8 +97,8 @@ var rules = map[string]func(string, map[string]string)(bool, string) {
|
||||
return
|
||||
},
|
||||
// www.baidu.com
|
||||
//
|
||||
"domain": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
//
|
||||
"domain": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
ok = true
|
||||
return // 可为空
|
||||
@@ -106,16 +106,16 @@ var rules = map[string]func(string, map[string]string)(bool, string) {
|
||||
ok2, _ := regexp.MatchString(`[^0-9a-zA-Z_\.\-]`, value)
|
||||
ok = !ok2
|
||||
if !ok {
|
||||
return
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
},
|
||||
// abcd
|
||||
"subDomain": func(value string, rule map[string]string)(ok bool, msg string) {
|
||||
"subDomain": func(value string, rule map[string]string) (ok bool, msg string) {
|
||||
if value == "" {
|
||||
ok = true
|
||||
return // 可为空
|
||||
return // 可为空
|
||||
}
|
||||
if len(value) < 4 {
|
||||
ok = false
|
||||
@@ -137,7 +137,7 @@ func InitVd() {
|
||||
|
||||
func Vd(name, value string) (ok bool, msg string) {
|
||||
rs, _ := rulesMap[name]
|
||||
|
||||
|
||||
for _, rule := range rs {
|
||||
ruleFunc, _ := rules[rule["rule"]]
|
||||
if ok2, msg2 := ruleFunc(value, rule); !ok2 {
|
||||
@@ -151,11 +151,11 @@ func Vd(name, value string) (ok bool, msg string) {
|
||||
if msgData != "" {
|
||||
msg += "-" + msgData
|
||||
}
|
||||
return
|
||||
return
|
||||
}
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
func Vds(m map[string]string) (ok bool, msg string) {
|
||||
@@ -166,5 +166,5 @@ func Vds(m map[string]string) (ok bool, msg string) {
|
||||
}
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// main functions shows how to TarGz a directory/file and
|
||||
@@ -16,66 +16,66 @@ import (
|
||||
|
||||
func main() {
|
||||
/*
|
||||
os.Mkdir("/home/ty4z2008/tar", 0777)
|
||||
w, err := CopyFile("/home/ty4z2008/tar/1.pdf", "/home/ty4z2008/src/1.pdf")
|
||||
//targetfile,sourcefile
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
fmt.Println(w)
|
||||
os.Mkdir("/home/ty4z2008/tar", 0777)
|
||||
w, err := CopyFile("/home/ty4z2008/tar/1.pdf", "/home/ty4z2008/src/1.pdf")
|
||||
//targetfile,sourcefile
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
fmt.Println(w)
|
||||
|
||||
TarGz("/home/ty4z2008/tar/1.pdf", "/home/ty4z2008/test.tar.gz") //压缩
|
||||
//UnTarGz("/home/ty4z2008/1.tar.gz", "/home/ty4z2008") //解压
|
||||
os.RemoveAll("/home/ty4z2008/tar")
|
||||
*/
|
||||
// TaZip("/Users/life/Desktop/j", "/Users/life/Desktop/aaa.tar.gz")
|
||||
Zip("/Users/life/Desktop/j", "/Users/life/Desktop/aaa.zip")
|
||||
fmt.Println("ok")
|
||||
TarGz("/home/ty4z2008/tar/1.pdf", "/home/ty4z2008/test.tar.gz") //压缩
|
||||
//UnTarGz("/home/ty4z2008/1.tar.gz", "/home/ty4z2008") //解压
|
||||
os.RemoveAll("/home/ty4z2008/tar")
|
||||
*/
|
||||
// TaZip("/Users/life/Desktop/j", "/Users/life/Desktop/aaa.tar.gz")
|
||||
Zip("/Users/life/Desktop/j", "/Users/life/Desktop/aaa.zip")
|
||||
fmt.Println("ok")
|
||||
}
|
||||
|
||||
func TarGz(srcDirPath string, destFilePath string) (ok bool) {
|
||||
defer func() { //必须要先声明defer,否则不能捕获到panic异常
|
||||
defer func() { //必须要先声明defer,否则不能捕获到panic异常
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
}
|
||||
|
||||
|
||||
}()
|
||||
|
||||
fw, err := os.Create(destFilePath)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fw.Close()
|
||||
fw, err := os.Create(destFilePath)
|
||||
|
||||
// Gzip writer
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
// Tar writer
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
// Gzip writer
|
||||
gw := gzip.NewWriter(fw)
|
||||
defer gw.Close()
|
||||
|
||||
// Check if it's a file or a directory
|
||||
f, err := os.Open(srcDirPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if fi.IsDir() {
|
||||
// handle source directory
|
||||
// fmt.Println("Cerating tar.gz from directory...")
|
||||
tarGzDir(srcDirPath, path.Base(srcDirPath), tw)
|
||||
} else {
|
||||
// handle file directly
|
||||
// fmt.Println("Cerating tar.gz from " + fi.Name() + "...")
|
||||
tarGzFile(srcDirPath, fi.Name(), tw, fi)
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
// Tar writer
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
// Check if it's a file or a directory
|
||||
f, err := os.Open(srcDirPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if fi.IsDir() {
|
||||
// handle source directory
|
||||
// fmt.Println("Cerating tar.gz from directory...")
|
||||
tarGzDir(srcDirPath, path.Base(srcDirPath), tw)
|
||||
} else {
|
||||
// handle file directly
|
||||
// fmt.Println("Cerating tar.gz from " + fi.Name() + "...")
|
||||
tarGzFile(srcDirPath, fi.Name(), tw, fi)
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// Deal with directories
|
||||
@@ -83,148 +83,148 @@ func TarGz(srcDirPath string, destFilePath string) (ok bool) {
|
||||
// Every recurrence append the base path to the recPath
|
||||
// recPath is the path inside of tar.gz
|
||||
func tarGzDir(srcDirPath string, recPath string, tw *tar.Writer) {
|
||||
// Open source diretory
|
||||
dir, err := os.Open(srcDirPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer dir.Close()
|
||||
// Open source diretory
|
||||
dir, err := os.Open(srcDirPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
// Get file info slice
|
||||
fis, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, fi := range fis {
|
||||
// Append path
|
||||
curPath := srcDirPath + "/" + fi.Name()
|
||||
// Check it is directory or file
|
||||
if fi.IsDir() {
|
||||
// Directory
|
||||
// (Directory won't add unitl all subfiles are added)
|
||||
// fmt.Printf("Adding path...%s\n", curPath)
|
||||
tarGzDir(curPath, recPath+"/"+fi.Name(), tw)
|
||||
} else {
|
||||
// File
|
||||
// fmt.Printf("Adding file...%s\n", curPath)
|
||||
}
|
||||
// Get file info slice
|
||||
fis, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, fi := range fis {
|
||||
// Append path
|
||||
curPath := srcDirPath + "/" + fi.Name()
|
||||
// Check it is directory or file
|
||||
if fi.IsDir() {
|
||||
// Directory
|
||||
// (Directory won't add unitl all subfiles are added)
|
||||
// fmt.Printf("Adding path...%s\n", curPath)
|
||||
tarGzDir(curPath, recPath+"/"+fi.Name(), tw)
|
||||
} else {
|
||||
// File
|
||||
// fmt.Printf("Adding file...%s\n", curPath)
|
||||
}
|
||||
|
||||
tarGzFile(curPath, recPath+"/"+fi.Name(), tw, fi)
|
||||
}
|
||||
tarGzFile(curPath, recPath+"/"+fi.Name(), tw, fi)
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with files
|
||||
func tarGzFile(srcFile string, recPath string, tw *tar.Writer, fi os.FileInfo) {
|
||||
if fi.IsDir() {
|
||||
// fmt.Println("??")
|
||||
// Create tar header
|
||||
hdr := new(tar.Header)
|
||||
// if last character of header name is '/' it also can be directory
|
||||
// but if you don't set Typeflag, error will occur when you untargz
|
||||
hdr.Name = recPath // + "/"
|
||||
// fmt.Println(hdr.Name)
|
||||
hdr.Typeflag = tar.TypeDir
|
||||
// hdr.Size = 0
|
||||
//hdr.Mode = 0755 | c_ISDIR
|
||||
// hdr.Mode = int64(fi.Mode()) // 加这个会有错误!!!
|
||||
// hdr.ModTime = fi.ModTime() // 加这个会有错误!!
|
||||
if fi.IsDir() {
|
||||
// fmt.Println("??")
|
||||
// Create tar header
|
||||
hdr := new(tar.Header)
|
||||
// if last character of header name is '/' it also can be directory
|
||||
// but if you don't set Typeflag, error will occur when you untargz
|
||||
hdr.Name = recPath // + "/"
|
||||
// fmt.Println(hdr.Name)
|
||||
hdr.Typeflag = tar.TypeDir
|
||||
// hdr.Size = 0
|
||||
//hdr.Mode = 0755 | c_ISDIR
|
||||
// hdr.Mode = int64(fi.Mode()) // 加这个会有错误!!!
|
||||
// hdr.ModTime = fi.ModTime() // 加这个会有错误!!
|
||||
|
||||
// Write hander
|
||||
err := tw.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
// File reader
|
||||
fr, err := os.Open(srcFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fr.Close()
|
||||
// Write hander
|
||||
err := tw.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
// File reader
|
||||
fr, err := os.Open(srcFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// Create tar header
|
||||
hdr := new(tar.Header)
|
||||
hdr.Name = recPath
|
||||
// fmt.Println(hdr.Name)
|
||||
hdr.Size = fi.Size()
|
||||
hdr.Mode = int64(fi.Mode())
|
||||
hdr.ModTime = fi.ModTime()
|
||||
// Create tar header
|
||||
hdr := new(tar.Header)
|
||||
hdr.Name = recPath
|
||||
// fmt.Println(hdr.Name)
|
||||
hdr.Size = fi.Size()
|
||||
hdr.Mode = int64(fi.Mode())
|
||||
hdr.ModTime = fi.ModTime()
|
||||
|
||||
// Write hander
|
||||
err = tw.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Write hander
|
||||
err = tw.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Write file data
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// Write file data
|
||||
_, err = io.Copy(tw, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ungzip and untar from source file to destination directory
|
||||
// you need check file exist before you call this function
|
||||
func UnTarGz(srcFilePath string, destDirPath string) {
|
||||
// fmt.Println("UnTarGzing " + srcFilePath + "...")
|
||||
// Create destination directory
|
||||
os.Mkdir(destDirPath, os.ModePerm)
|
||||
// fmt.Println("UnTarGzing " + srcFilePath + "...")
|
||||
// Create destination directory
|
||||
os.Mkdir(destDirPath, os.ModePerm)
|
||||
|
||||
fr, err := os.Open(srcFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fr.Close()
|
||||
fr, err := os.Open(srcFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// Gzip reader
|
||||
gr, err := gzip.NewReader(fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer gr.Close()
|
||||
// Gzip reader
|
||||
gr, err := gzip.NewReader(fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer gr.Close()
|
||||
|
||||
// Tar reader
|
||||
tr := tar.NewReader(gr)
|
||||
// Tar reader
|
||||
tr := tar.NewReader(gr)
|
||||
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
// End of tar archive
|
||||
break
|
||||
}
|
||||
//handleError(err)
|
||||
// fmt.Println("UnTarGzing file..." + hdr.Name)
|
||||
// Check if it is diretory or file
|
||||
if hdr.Typeflag != tar.TypeDir {
|
||||
// Get files from archive
|
||||
// Create diretory before create file
|
||||
os.MkdirAll(destDirPath+"/"+path.Dir(hdr.Name), os.ModePerm)
|
||||
// Write data to file
|
||||
fw, _ := os.Create(destDirPath + "/" + hdr.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(fw, tr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// fmt.Println("Well done!")
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
// End of tar archive
|
||||
break
|
||||
}
|
||||
//handleError(err)
|
||||
// fmt.Println("UnTarGzing file..." + hdr.Name)
|
||||
// Check if it is diretory or file
|
||||
if hdr.Typeflag != tar.TypeDir {
|
||||
// Get files from archive
|
||||
// Create diretory before create file
|
||||
os.MkdirAll(destDirPath+"/"+path.Dir(hdr.Name), os.ModePerm)
|
||||
// Write data to file
|
||||
fw, _ := os.Create(destDirPath + "/" + hdr.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(fw, tr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// fmt.Println("Well done!")
|
||||
}
|
||||
|
||||
// Copyfile
|
||||
func CopyFile(dstName, srcName string) (written int64, err error) {
|
||||
src, err := os.Open(srcName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer src.Close()
|
||||
dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer dst.Close()
|
||||
return io.Copy(dst, src)
|
||||
}
|
||||
src, err := os.Open(srcName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer src.Close()
|
||||
dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer dst.Close()
|
||||
return io.Copy(dst, src)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"github.com/leanote/leanote/app/lea"
|
||||
)
|
||||
|
||||
// main functions shows how to TarGz a directory/file and
|
||||
@@ -15,43 +16,43 @@ import (
|
||||
// you need check file exist before you call this function
|
||||
|
||||
func Zip(srcDirPath string, destFilePath string) (ok bool) {
|
||||
defer func() { //必须要先声明defer,否则不能捕获到panic异常
|
||||
defer func() { //必须要先声明defer,否则不能捕获到panic异常
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
fw, err := os.Create(destFilePath)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fw.Close()
|
||||
fw, err := os.Create(destFilePath)
|
||||
|
||||
// Tar writer
|
||||
tw := zip.NewWriter(fw)
|
||||
defer tw.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
// Check if it's a file or a directory
|
||||
f, err := os.Open(srcDirPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if fi.IsDir() {
|
||||
// handle source directory
|
||||
// fmt.Println("Cerating tar.gz from directory...")
|
||||
zipDir(srcDirPath, path.Base(srcDirPath), tw)
|
||||
} else {
|
||||
// handle file directly
|
||||
// fmt.Println("Cerating tar.gz from " + fi.Name() + "...")
|
||||
zipFile(srcDirPath, fi.Name(), tw, fi)
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
// Tar writer
|
||||
tw := zip.NewWriter(fw)
|
||||
defer tw.Close()
|
||||
|
||||
// Check if it's a file or a directory
|
||||
f, err := os.Open(srcDirPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if fi.IsDir() {
|
||||
// handle source directory
|
||||
// fmt.Println("Cerating tar.gz from directory...")
|
||||
zipDir(srcDirPath, path.Base(srcDirPath), tw)
|
||||
} else {
|
||||
// handle file directly
|
||||
// fmt.Println("Cerating tar.gz from " + fi.Name() + "...")
|
||||
zipFile(srcDirPath, fi.Name(), tw, fi)
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// Deal with directories
|
||||
@@ -59,69 +60,69 @@ func Zip(srcDirPath string, destFilePath string) (ok bool) {
|
||||
// Every recurrence append the base path to the recPath
|
||||
// recPath is the path inside of tar.gz
|
||||
func zipDir(srcDirPath string, recPath string, tw *zip.Writer) {
|
||||
// Open source diretory
|
||||
dir, err := os.Open(srcDirPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer dir.Close()
|
||||
// Open source diretory
|
||||
dir, err := os.Open(srcDirPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
// Get file info slice
|
||||
fis, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, fi := range fis {
|
||||
// Append path
|
||||
curPath := srcDirPath + "/" + fi.Name()
|
||||
// Check it is directory or file
|
||||
if fi.IsDir() {
|
||||
// Directory
|
||||
// (Directory won't add unitl all subfiles are added)
|
||||
// fmt.Printf("Adding path...%s\n", curPath)
|
||||
zipDir(curPath, recPath+"/"+fi.Name(), tw)
|
||||
} else {
|
||||
// File
|
||||
// fmt.Printf("Adding file...%s\n", curPath)
|
||||
}
|
||||
// Get file info slice
|
||||
fis, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, fi := range fis {
|
||||
// Append path
|
||||
curPath := srcDirPath + "/" + fi.Name()
|
||||
// Check it is directory or file
|
||||
if fi.IsDir() {
|
||||
// Directory
|
||||
// (Directory won't add unitl all subfiles are added)
|
||||
// fmt.Printf("Adding path...%s\n", curPath)
|
||||
zipDir(curPath, recPath+"/"+fi.Name(), tw)
|
||||
} else {
|
||||
// File
|
||||
// fmt.Printf("Adding file...%s\n", curPath)
|
||||
}
|
||||
|
||||
zipFile(curPath, recPath+"/"+fi.Name(), tw, fi)
|
||||
}
|
||||
zipFile(curPath, recPath+"/"+fi.Name(), tw, fi)
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with files
|
||||
func zipFile(srcFile string, recPath string, tw *zip.Writer, fi os.FileInfo) {
|
||||
if fi.IsDir() {
|
||||
// fmt.Println("??")
|
||||
// Create tar header
|
||||
/*
|
||||
fh, err := zip.FileInfoHeader(fi)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fh.Name = recPath // + "/"
|
||||
err = tw.WriteHeader(hdr)
|
||||
tw.Create(recPath)
|
||||
*/
|
||||
} else {
|
||||
// File reader
|
||||
fr, err := os.Open(srcFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fr.Close()
|
||||
if fi.IsDir() {
|
||||
// fmt.Println("??")
|
||||
// Create tar header
|
||||
/*
|
||||
fh, err := zip.FileInfoHeader(fi)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fh.Name = recPath // + "/"
|
||||
err = tw.WriteHeader(hdr)
|
||||
tw.Create(recPath)
|
||||
*/
|
||||
} else {
|
||||
// File reader
|
||||
fr, err := os.Open(srcFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
// Write hander
|
||||
w, err2 := tw.Create(recPath)
|
||||
if err2 != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Write file data
|
||||
_, err = io.Copy(w, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// Write hander
|
||||
w, err2 := tw.Create(recPath)
|
||||
if err2 != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Write file data
|
||||
_, err = io.Copy(w, fr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ungzip and untar from source file to destination directory
|
||||
@@ -129,33 +130,39 @@ func zipFile(srcFile string, recPath string, tw *zip.Writer, fi os.FileInfo) {
|
||||
func Unzip(srcFilePath string, destDirPath string) (ok bool, msg string) {
|
||||
ok = false
|
||||
msg = ""
|
||||
|
||||
defer func() { //必须要先声明defer,否则不能捕获到panic异常
|
||||
|
||||
defer func() { //必须要先声明defer,否则不能捕获到panic异常
|
||||
if err := recover(); err != nil {
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
os.Mkdir(destDirPath, os.ModePerm)
|
||||
r, err := zip.OpenReader(srcFilePath);
|
||||
r, err := zip.OpenReader(srcFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer r.Close();
|
||||
defer r.Close()
|
||||
for _, f := range r.File {
|
||||
// fmt.Println("FileName : ", f.Name); // j/aaa.zip
|
||||
rc, err := f.Open();
|
||||
if err!=nil {
|
||||
// fmt.Println("FileName : ", f.Name); // j/aaa.zip
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
// 包含恶意目录
|
||||
if strings.Contains(f.Name, "../") {
|
||||
lea.LogW("恶意文件", f.Name);
|
||||
continue
|
||||
}
|
||||
|
||||
// 把首文件夹去掉, 即j去掉, 分离出文件夹和文件名
|
||||
paths := strings.Split(f.Name, "/")
|
||||
prePath := ""
|
||||
filename := ""
|
||||
l := len(paths)
|
||||
// fmt.Println(l)
|
||||
// fmt.Println(l)
|
||||
if l > 1 {
|
||||
// 去掉第1个文件夹
|
||||
if l == 2 {
|
||||
@@ -167,26 +174,26 @@ func Unzip(srcFilePath string, destDirPath string) (ok bool, msg string) {
|
||||
} else {
|
||||
filename = f.Name
|
||||
}
|
||||
// fmt.Println(prePath)
|
||||
|
||||
// fmt.Println(prePath)
|
||||
|
||||
// 相对于目标文件件下的路径
|
||||
destPath := destDirPath + "/" + filename
|
||||
destPath := destDirPath + "/" + filename
|
||||
if prePath != "" {
|
||||
os.MkdirAll(destDirPath + "/" + prePath, os.ModePerm)
|
||||
os.MkdirAll(destDirPath+"/"+prePath, os.ModePerm)
|
||||
destPath = destDirPath + "/" + prePath + "/" + filename
|
||||
}
|
||||
// Write data to file
|
||||
// fmt.Println(destPath)
|
||||
fw, _ := os.Create(destPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(fw, rc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Write data to file
|
||||
// fmt.Println(destPath)
|
||||
fw, _ := os.Create(destPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(fw, rc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package binder
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/controllers"
|
||||
"github.com/revel/revel"
|
||||
// "github.com/leanote/leanote/app/controllers/api"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// leanote binder struct
|
||||
// rewrite revel struct binder
|
||||
// not need the struct name as prefix,
|
||||
// not need the struct name as prefix,
|
||||
// eg:
|
||||
// type Note struct {Name}
|
||||
// func (c Controller) List(note Note) revel.Result {}
|
||||
@@ -33,13 +33,13 @@ var MSSBinder = revel.Binder{
|
||||
}
|
||||
return result
|
||||
},
|
||||
|
||||
|
||||
Unbind: func(output map[string]string, name string, val interface{}) {
|
||||
mapValue := reflect.ValueOf(val)
|
||||
for _, key := range mapValue.MapKeys() {
|
||||
revel.Unbind(output, fmt.Sprintf("%v", key.Interface()),
|
||||
mapValue.MapIndex(key).Interface())
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -53,31 +53,56 @@ func nextKey(key string) string {
|
||||
}
|
||||
return key[:fieldLen]
|
||||
}
|
||||
|
||||
var leanoteStructBinder = revel.Binder{
|
||||
// name == "noteOrContent"
|
||||
Bind: func(params *revel.Params, name string, typ reflect.Type) reflect.Value {
|
||||
result := reflect.New(typ).Elem()
|
||||
result := reflect.New(typ).Elem() // 创建一个该类型的, 然后其field从所有的param去取
|
||||
fieldValues := make(map[string]reflect.Value)
|
||||
for key, _ := range params.Values {
|
||||
// fmt.Println(name)
|
||||
// fmt.Println(typ) // api.NoteFiles
|
||||
// name = files[0], files[1], noteContent
|
||||
// fmt.Println(params.Values)
|
||||
/*
|
||||
map[Title:[test1] METHOD:[POST] NotebookId:[54c4f51705fcd14031000002]
|
||||
files[1][FileId]:[]
|
||||
controller:[note]
|
||||
files[1][LocalFileId]:[54c7ae27d98d0329dd000000] files[1][HasBody]:[true] files[0][FileId]:[] files[0][LocalFileId]:[54c7ae855e94ea2dba000000] action:[addNote] Content:[<p>lifedddddd</p><p><img src="app://leanote/data/54bdc65599c37b0da9000002/images/1422368307147_2.png" alt="" data-mce-src="app://leanote/data/54bdc65599c37b0da9000002/images/1422368307147_2.png" style="display: block; margin-left: auto; margin-right: auto;"></p><p><img src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae27d98d0329dd000000" alt="" data-mce-src="http://127.0.0.1:8008/api/file/getImg?fileId=54c7ae27d98d0329dd000000"></p><p><br></p><p><img src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae855e94ea2dba000000" alt="" data-mce-src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae855e94ea2dba000000" style="display: block; margin-left: auto; margin-right: auto;"></p><p><br></p><p><br></p>] IsBlog:[false] token:[user1]
|
||||
files[0][HasBody]:[true]]
|
||||
*/
|
||||
nameIsSlice := strings.Contains(name, "[")
|
||||
// fmt.Println(params.Values["files[1]"])
|
||||
// fmt.Println(params.Values["Title"])
|
||||
for key, _ := range params.Values { // Title, Content, Files
|
||||
// 这里, 如果没有点, 默认就是a.
|
||||
// life
|
||||
// fmt.Println("key:" + key); // files[0][LocalFileId]
|
||||
// fmt.Println("name:" + name); // files[0][LocalFileId]
|
||||
var suffix string
|
||||
var noPrefix = false
|
||||
if !strings.HasPrefix(key, name + ".") {
|
||||
if nameIsSlice && strings.HasPrefix(key, name) {
|
||||
suffix = key[len(name)+1 : len(key)-1] // files[0][LocalFileId] 去掉 => LocalFileId
|
||||
} else if !strings.HasPrefix(key, name+".") {
|
||||
noPrefix = true
|
||||
suffix = key
|
||||
// continue
|
||||
// continue
|
||||
} else {
|
||||
// Get the name of the struct property.
|
||||
// Strip off the prefix. e.g. foo.bar.baz => bar.baz
|
||||
suffix = key[len(name)+1:]
|
||||
}
|
||||
|
||||
// fmt.Println(suffix);
|
||||
|
||||
fieldName := nextKey(suffix) // e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar"
|
||||
// fmt.Println(fieldName);
|
||||
fieldLen := len(fieldName)
|
||||
|
||||
|
||||
if _, ok := fieldValues[fieldName]; !ok {
|
||||
// Time to bind this field. Get it and make sure we can set it.
|
||||
fieldName = strings.Title(fieldName) // 传过来title, 但struct是Title
|
||||
// fmt.Println("xx: " + fieldName)
|
||||
fieldValue := result.FieldByName(fieldName)
|
||||
// fmt.Println(fieldValue)
|
||||
if !fieldValue.IsValid() {
|
||||
continue
|
||||
}
|
||||
@@ -86,9 +111,17 @@ var leanoteStructBinder = revel.Binder{
|
||||
}
|
||||
var boundVal reflect.Value
|
||||
// 没有name前缀
|
||||
if(noPrefix) {
|
||||
if noPrefix {
|
||||
// life
|
||||
// fmt.Println("<<")
|
||||
// fmt.Println(strings.Title(key[:fieldLen]));
|
||||
boundVal = revel.Bind(params, key[:fieldLen], fieldValue.Type())
|
||||
} else {
|
||||
// fmt.Println("final")
|
||||
// fmt.Println(key[:len(name)+1+fieldLen]) // files[0][HasBody
|
||||
if nameIsSlice {
|
||||
fieldLen += 1
|
||||
}
|
||||
boundVal = revel.Bind(params, key[:len(name)+1+fieldLen], fieldValue.Type())
|
||||
}
|
||||
fieldValue.Set(boundVal)
|
||||
@@ -116,5 +149,8 @@ func init() {
|
||||
revel.TypeBinders[reflect.TypeOf(info.UserBlogComment{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.UserBlogStyle{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.Notebook{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(controllers.NoteOrContent{})] = leanoteStructBinder
|
||||
}
|
||||
revel.TypeBinders[reflect.TypeOf(info.UserAccount{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.NoteOrContent{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.ApiNote{})] = leanoteStructBinder
|
||||
revel.TypeBinders[reflect.TypeOf(info.NoteFile{})] = leanoteStructBinder
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
// "os"
|
||||
"fmt"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@@ -40,8 +40,8 @@ type RenderTemplateResult struct {
|
||||
Template *template.Template
|
||||
PathContent map[string]string
|
||||
RenderArgs map[string]interface{}
|
||||
|
||||
IsPreview bool // 是否是预览
|
||||
|
||||
IsPreview bool // 是否是预览
|
||||
CurBlogTpl *BlogTpl
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func (r *RenderTemplateResult) render(req *revel.Request, resp *revel.Response,
|
||||
Line: line,
|
||||
SourceLines: templateContent,
|
||||
}
|
||||
|
||||
|
||||
// 这里, 错误!!
|
||||
// 这里应该导向到本主题的错误页面
|
||||
resp.Status = 500
|
||||
@@ -117,7 +117,7 @@ func (r *RenderTemplateResult) Apply(req *revel.Request, resp *revel.Response) {
|
||||
r.render(req, resp, out) // 这里!!!
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Render the template into a temporary buffer, to see if there was an error
|
||||
// rendering the template. If not, then copy it into the response buffer.
|
||||
// Otherwise, template render errors may result in unpredictable HTML (and
|
||||
@@ -139,7 +139,7 @@ func Init() {
|
||||
fileBytes, _ := ioutil.ReadFile(revel.ViewsPath + "/Blog/" + path)
|
||||
fileStr := string(fileBytes)
|
||||
path := "blog/" + path
|
||||
// path := path
|
||||
// path := path
|
||||
BlogTplObject.PathContent[path] = fileStr
|
||||
BlogTplObject.Template.New(path).Parse(fileStr) // 以blog为根
|
||||
}
|
||||
@@ -157,7 +157,7 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
// 都不会为空的
|
||||
if basePath == "" {
|
||||
path := "blog/" + name
|
||||
// path := name
|
||||
// path := name
|
||||
t := BlogTplObject.Template.Lookup(path)
|
||||
r = &RenderTemplateResult{
|
||||
Template: t,
|
||||
@@ -179,11 +179,9 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
|
||||
// 将该basePath下的所有文件提出
|
||||
files := ListDir(basePath)
|
||||
Log(basePath)
|
||||
LogJ(files);
|
||||
for _, t := range files {
|
||||
if !strings.Contains(t, ".html") {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
fileBytes, err := ioutil.ReadFile(basePath + "/" + t)
|
||||
if err != nil {
|
||||
@@ -196,7 +194,7 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
|
||||
// 如果本主题下没有, 则用系统的
|
||||
t := newBlogTplObject.Template.Lookup(name)
|
||||
|
||||
|
||||
if t == nil {
|
||||
path := "blog/" + name
|
||||
t = BlogTplObject.Template.Lookup(path)
|
||||
@@ -205,22 +203,21 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
|
||||
Template: t,
|
||||
PathContent: newBlogTplObject.PathContent, // 为了显示错误
|
||||
RenderArgs: args,
|
||||
CurBlogTpl: newBlogTplObject,
|
||||
IsPreview: isPreview,
|
||||
CurBlogTpl: newBlogTplObject,
|
||||
IsPreview: isPreview,
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// 错误显示
|
||||
//
|
||||
|
||||
type ErrorResult struct {
|
||||
RenderArgs map[string]interface{}
|
||||
Error error
|
||||
IsPreview bool
|
||||
IsPreview bool
|
||||
CurBlogTpl *BlogTpl
|
||||
}
|
||||
|
||||
@@ -241,7 +238,7 @@ func (r ErrorResult) Apply(req *revel.Request, resp *revel.Response) {
|
||||
var err error
|
||||
templatePath := fmt.Sprintf("errors/%d.%s", status, format)
|
||||
err = nil
|
||||
// tmpl, err := revel.MainTemplateLoader.Template("index.html") // 这里找到错误页面主题
|
||||
// tmpl, err := revel.MainTemplateLoader.Template("index.html") // 这里找到错误页面主题
|
||||
|
||||
// This func shows a plaintext error message, in case the template rendering
|
||||
// doesn't work.
|
||||
@@ -290,13 +287,12 @@ func (r ErrorResult) Apply(req *revel.Request, resp *revel.Response) {
|
||||
r.RenderArgs["Router"] = revel.MainRouter
|
||||
|
||||
// 不是preview就不要显示错误了
|
||||
LogJ(revelError)
|
||||
// if r.IsPreview {
|
||||
if r.IsPreview {
|
||||
var b bytes.Buffer
|
||||
out := io.Writer(resp.Out)
|
||||
// out = ioutil.Discard
|
||||
err = tmpl.Execute(&b, r.RenderArgs)
|
||||
resp.WriteHeader(http.StatusOK, "text/html; charset=utf-8")
|
||||
b.WriteTo(out)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,399 +1,401 @@
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"math/rand"
|
||||
crand "crypto/rand"
|
||||
"time"
|
||||
"strconv"
|
||||
)
|
||||
const (
|
||||
stdWidth = 100
|
||||
stdHeight = 40
|
||||
maxSkew = 2
|
||||
crand "crypto/rand"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
fontWidth = 5
|
||||
fontHeight = 8
|
||||
blackChar = 1
|
||||
stdWidth = 100
|
||||
stdHeight = 40
|
||||
maxSkew = 2
|
||||
)
|
||||
|
||||
|
||||
const (
|
||||
fontWidth = 5
|
||||
fontHeight = 8
|
||||
blackChar = 1
|
||||
)
|
||||
|
||||
var font = [][]byte{
|
||||
{ // 0
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 1
|
||||
0, 0, 1, 0, 0,
|
||||
0, 1, 1, 0, 0,
|
||||
1, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
},
|
||||
{ // 2
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 1,
|
||||
0, 1, 1, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
},
|
||||
{ // 3
|
||||
1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 4
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
},
|
||||
{ // 5
|
||||
1, 1, 1, 1, 1,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 6
|
||||
0, 0, 1, 1, 1,
|
||||
0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 7
|
||||
1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
},
|
||||
{ // 8
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 9
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 1, 0, 0, 1,
|
||||
0, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 0
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 1
|
||||
0, 0, 1, 0, 0,
|
||||
0, 1, 1, 0, 0,
|
||||
1, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
},
|
||||
{ // 2
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 1,
|
||||
0, 1, 1, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
},
|
||||
{ // 3
|
||||
1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 4
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
},
|
||||
{ // 5
|
||||
1, 1, 1, 1, 1,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 6
|
||||
0, 0, 1, 1, 1,
|
||||
0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 7
|
||||
1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
},
|
||||
{ // 8
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 0,
|
||||
},
|
||||
{ // 9
|
||||
0, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 0, 0, 0, 1,
|
||||
1, 1, 0, 0, 1,
|
||||
0, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
type Image struct {
|
||||
*image.NRGBA
|
||||
color *color.NRGBA
|
||||
width int //a digit width
|
||||
height int //a digit height
|
||||
dotsize int
|
||||
*image.NRGBA
|
||||
color *color.NRGBA
|
||||
width int //a digit width
|
||||
height int //a digit height
|
||||
dotsize int
|
||||
}
|
||||
func init(){
|
||||
rand.Seed(int64(time.Second))
|
||||
|
||||
func init() {
|
||||
rand.Seed(int64(time.Second))
|
||||
}
|
||||
|
||||
|
||||
func NewImage(digits []byte, width, height int) *Image {
|
||||
img := new(Image)
|
||||
r := image.Rect(img.width, img.height, stdWidth, stdHeight)
|
||||
img.NRGBA = image.NewNRGBA(r)
|
||||
|
||||
img.color = &color.NRGBA{
|
||||
uint8(rand.Intn(129)),
|
||||
uint8(rand.Intn(129)),
|
||||
uint8(rand.Intn(129)),
|
||||
0xFF,
|
||||
}
|
||||
// Draw background (10 random circles of random brightness)
|
||||
img.calculateSizes(width, height, len(digits))
|
||||
img.fillWithCircles(10, img.dotsize)
|
||||
|
||||
maxx := width - (img.width+img.dotsize)*len(digits) - img.dotsize
|
||||
maxy := height - img.height - img.dotsize*2
|
||||
|
||||
x := rnd(img.dotsize*2, maxx)
|
||||
y := rnd(img.dotsize*2, maxy)
|
||||
|
||||
// Draw digits.
|
||||
for _, n := range digits {
|
||||
img.drawDigit(font[n], x, y)
|
||||
x += img.width + img.dotsize
|
||||
}
|
||||
|
||||
// Draw strike-through line.
|
||||
// 中间线不要
|
||||
//img.strikeThrough()
|
||||
|
||||
return img
|
||||
img := new(Image)
|
||||
r := image.Rect(img.width, img.height, stdWidth, stdHeight)
|
||||
img.NRGBA = image.NewNRGBA(r)
|
||||
|
||||
img.color = &color.NRGBA{
|
||||
uint8(rand.Intn(129)),
|
||||
uint8(rand.Intn(129)),
|
||||
uint8(rand.Intn(129)),
|
||||
0xFF,
|
||||
}
|
||||
// Draw background (10 random circles of random brightness)
|
||||
img.calculateSizes(width, height, len(digits))
|
||||
img.fillWithCircles(10, img.dotsize)
|
||||
|
||||
maxx := width - (img.width+img.dotsize)*len(digits) - img.dotsize
|
||||
maxy := height - img.height - img.dotsize*2
|
||||
|
||||
x := rnd(img.dotsize*2, maxx)
|
||||
y := rnd(img.dotsize*2, maxy)
|
||||
|
||||
// Draw digits.
|
||||
for _, n := range digits {
|
||||
img.drawDigit(font[n], x, y)
|
||||
x += img.width + img.dotsize
|
||||
}
|
||||
|
||||
// Draw strike-through line.
|
||||
// 中间线不要
|
||||
//img.strikeThrough()
|
||||
|
||||
return img
|
||||
}
|
||||
|
||||
|
||||
func (img *Image) WriteTo(w io.Writer) (int64, error) {
|
||||
return 0, png.Encode(w, img)
|
||||
return 0, png.Encode(w, img)
|
||||
}
|
||||
|
||||
|
||||
func (img *Image) calculateSizes(width, height, ncount int) {
|
||||
|
||||
// Goal: fit all digits inside the image.
|
||||
var border int
|
||||
if width > height {
|
||||
border = height / 5
|
||||
} else {
|
||||
border = width / 5
|
||||
}
|
||||
// Convert everything to floats for calculations.
|
||||
w := float64(width - border*2) //268
|
||||
h := float64(height - border*2) //48
|
||||
// fw takes into account 1-dot spacing between digits.
|
||||
|
||||
fw := float64(fontWidth) + 1 //6
|
||||
|
||||
fh := float64(fontHeight) //8
|
||||
nc := float64(ncount) //7
|
||||
|
||||
// Calculate the width of a single digit taking into account only the
|
||||
// width of the image.
|
||||
nw := w / nc //38
|
||||
// Calculate the height of a digit from this width.
|
||||
nh := nw * fh / fw //51
|
||||
|
||||
// Digit too high?
|
||||
|
||||
if nh > h {
|
||||
// Fit digits based on height.
|
||||
nh = h //nh = 44
|
||||
nw = fw / fh * nh
|
||||
}
|
||||
// Calculate dot size.
|
||||
img.dotsize = int(nh / fh)
|
||||
// Save everything, making the actual width smaller by 1 dot to account
|
||||
// for spacing between digits.
|
||||
img.width = int(nw)
|
||||
img.height = int(nh) - img.dotsize
|
||||
|
||||
// Goal: fit all digits inside the image.
|
||||
var border int
|
||||
if width > height {
|
||||
border = height / 5
|
||||
} else {
|
||||
border = width / 5
|
||||
}
|
||||
// Convert everything to floats for calculations.
|
||||
w := float64(width - border*2) //268
|
||||
h := float64(height - border*2) //48
|
||||
// fw takes into account 1-dot spacing between digits.
|
||||
|
||||
fw := float64(fontWidth) + 1 //6
|
||||
|
||||
fh := float64(fontHeight) //8
|
||||
nc := float64(ncount) //7
|
||||
|
||||
// Calculate the width of a single digit taking into account only the
|
||||
// width of the image.
|
||||
nw := w / nc //38
|
||||
// Calculate the height of a digit from this width.
|
||||
nh := nw * fh / fw //51
|
||||
|
||||
// Digit too high?
|
||||
|
||||
if nh > h {
|
||||
// Fit digits based on height.
|
||||
nh = h //nh = 44
|
||||
nw = fw / fh * nh
|
||||
}
|
||||
// Calculate dot size.
|
||||
img.dotsize = int(nh / fh)
|
||||
// Save everything, making the actual width smaller by 1 dot to account
|
||||
// for spacing between digits.
|
||||
img.width = int(nw)
|
||||
img.height = int(nh) - img.dotsize
|
||||
}
|
||||
|
||||
|
||||
func (img *Image) fillWithCircles(n, maxradius int) {
|
||||
color := img.color
|
||||
maxx := img.Bounds().Max.X
|
||||
maxy := img.Bounds().Max.Y
|
||||
for i := 0; i < n; i++ {
|
||||
setRandomBrightness(color, 255)
|
||||
r := rnd(1, maxradius)
|
||||
img.drawCircle(color, rnd(r, maxx-r), rnd(r, maxy-r), r)
|
||||
}
|
||||
color := img.color
|
||||
maxx := img.Bounds().Max.X
|
||||
maxy := img.Bounds().Max.Y
|
||||
for i := 0; i < n; i++ {
|
||||
setRandomBrightness(color, 255)
|
||||
r := rnd(1, maxradius)
|
||||
img.drawCircle(color, rnd(r, maxx-r), rnd(r, maxy-r), r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (img *Image) drawHorizLine(color color.Color, fromX, toX, y int) {
|
||||
for x := fromX; x <= toX; x++ {
|
||||
img.Set(x, y, color)
|
||||
}
|
||||
for x := fromX; x <= toX; x++ {
|
||||
img.Set(x, y, color)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (img *Image) drawCircle(color color.Color, x, y, radius int) {
|
||||
f := 1 - radius
|
||||
dfx := 1
|
||||
dfy := -2 * radius
|
||||
xx := 0
|
||||
yy := radius
|
||||
|
||||
img.Set(x, y+radius, color)
|
||||
img.Set(x, y-radius, color)
|
||||
img.drawHorizLine(color, x-radius, x+radius, y)
|
||||
|
||||
for xx < yy {
|
||||
if f >= 0 {
|
||||
yy--
|
||||
dfy += 2
|
||||
f += dfy
|
||||
}
|
||||
xx++
|
||||
dfx += 2
|
||||
f += dfx
|
||||
img.drawHorizLine(color, x-xx, x+xx, y+yy)
|
||||
img.drawHorizLine(color, x-xx, x+xx, y-yy)
|
||||
img.drawHorizLine(color, x-yy, x+yy, y+xx)
|
||||
img.drawHorizLine(color, x-yy, x+yy, y-xx)
|
||||
}
|
||||
f := 1 - radius
|
||||
dfx := 1
|
||||
dfy := -2 * radius
|
||||
xx := 0
|
||||
yy := radius
|
||||
|
||||
img.Set(x, y+radius, color)
|
||||
img.Set(x, y-radius, color)
|
||||
img.drawHorizLine(color, x-radius, x+radius, y)
|
||||
|
||||
for xx < yy {
|
||||
if f >= 0 {
|
||||
yy--
|
||||
dfy += 2
|
||||
f += dfy
|
||||
}
|
||||
xx++
|
||||
dfx += 2
|
||||
f += dfx
|
||||
img.drawHorizLine(color, x-xx, x+xx, y+yy)
|
||||
img.drawHorizLine(color, x-xx, x+xx, y-yy)
|
||||
img.drawHorizLine(color, x-yy, x+yy, y+xx)
|
||||
img.drawHorizLine(color, x-yy, x+yy, y-xx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (img *Image) strikeThrough() {
|
||||
r := 0
|
||||
maxx := img.Bounds().Max.X
|
||||
maxy := img.Bounds().Max.Y
|
||||
y := rnd(maxy/3, maxy-maxy/3)
|
||||
for x := 0; x < maxx; x += r {
|
||||
r = rnd(1, img.dotsize/3)
|
||||
y += rnd(-img.dotsize/2, img.dotsize/2)
|
||||
if y <= 0 || y >= maxy {
|
||||
y = rnd(maxy/3, maxy-maxy/3)
|
||||
}
|
||||
img.drawCircle(img.color, x, y, r)
|
||||
}
|
||||
r := 0
|
||||
maxx := img.Bounds().Max.X
|
||||
maxy := img.Bounds().Max.Y
|
||||
y := rnd(maxy/3, maxy-maxy/3)
|
||||
for x := 0; x < maxx; x += r {
|
||||
r = rnd(1, img.dotsize/3)
|
||||
y += rnd(-img.dotsize/2, img.dotsize/2)
|
||||
if y <= 0 || y >= maxy {
|
||||
y = rnd(maxy/3, maxy-maxy/3)
|
||||
}
|
||||
img.drawCircle(img.color, x, y, r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (img *Image) drawDigit(digit []byte, x, y int) {
|
||||
skf := rand.Float64() * float64(rnd(-maxSkew, maxSkew))
|
||||
xs := float64(x)
|
||||
minr := img.dotsize / 2 // minumum radius
|
||||
maxr := img.dotsize/2 + img.dotsize/4 // maximum radius
|
||||
y += rnd(-minr, minr)
|
||||
for yy := 0; yy < fontHeight; yy++ {
|
||||
for xx := 0; xx < fontWidth; xx++ {
|
||||
if digit[yy*fontWidth+xx] != blackChar {
|
||||
continue
|
||||
}
|
||||
// Introduce random variations.
|
||||
or := rnd(minr, maxr)
|
||||
ox := x + (xx * img.dotsize) + rnd(0, or/2)
|
||||
oy := y + (yy * img.dotsize) + rnd(0, or/2)
|
||||
|
||||
img.drawCircle(img.color, ox, oy, or)
|
||||
}
|
||||
xs += skf
|
||||
x = int(xs)
|
||||
}
|
||||
skf := rand.Float64() * float64(rnd(-maxSkew, maxSkew))
|
||||
xs := float64(x)
|
||||
minr := img.dotsize / 2 // minumum radius
|
||||
maxr := img.dotsize/2 + img.dotsize/4 // maximum radius
|
||||
y += rnd(-minr, minr)
|
||||
for yy := 0; yy < fontHeight; yy++ {
|
||||
for xx := 0; xx < fontWidth; xx++ {
|
||||
if digit[yy*fontWidth+xx] != blackChar {
|
||||
continue
|
||||
}
|
||||
// Introduce random variations.
|
||||
or := rnd(minr, maxr)
|
||||
ox := x + (xx * img.dotsize) + rnd(0, or/2)
|
||||
oy := y + (yy * img.dotsize) + rnd(0, or/2)
|
||||
|
||||
img.drawCircle(img.color, ox, oy, or)
|
||||
}
|
||||
xs += skf
|
||||
x = int(xs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func setRandomBrightness(c *color.NRGBA, max uint8) {
|
||||
minc := min3(c.R, c.G, c.B)
|
||||
maxc := max3(c.R, c.G, c.B)
|
||||
if maxc > max {
|
||||
return
|
||||
}
|
||||
n := rand.Intn(int(max-maxc)) - int(minc)
|
||||
c.R = uint8(int(c.R) + n)
|
||||
c.G = uint8(int(c.G) + n)
|
||||
c.B = uint8(int(c.B) + n)
|
||||
minc := min3(c.R, c.G, c.B)
|
||||
maxc := max3(c.R, c.G, c.B)
|
||||
if maxc > max {
|
||||
return
|
||||
}
|
||||
n := rand.Intn(int(max-maxc)) - int(minc)
|
||||
c.R = uint8(int(c.R) + n)
|
||||
c.G = uint8(int(c.G) + n)
|
||||
c.B = uint8(int(c.B) + n)
|
||||
}
|
||||
|
||||
|
||||
func min3(x, y, z uint8) (o uint8) {
|
||||
o = x
|
||||
if y < o {
|
||||
o = y
|
||||
}
|
||||
if z < o {
|
||||
o = z
|
||||
}
|
||||
return
|
||||
o = x
|
||||
if y < o {
|
||||
o = y
|
||||
}
|
||||
if z < o {
|
||||
o = z
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func max3(x, y, z uint8) (o uint8) {
|
||||
o = x
|
||||
if y > o {
|
||||
o = y
|
||||
}
|
||||
if z > o {
|
||||
o = z
|
||||
}
|
||||
return
|
||||
o = x
|
||||
if y > o {
|
||||
o = y
|
||||
}
|
||||
if z > o {
|
||||
o = z
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// rnd returns a random number in range [from, to].
|
||||
func rnd(from, to int) int {
|
||||
//println(to+1-from)
|
||||
return rand.Intn(to+1-from) + from
|
||||
//println(to+1-from)
|
||||
return rand.Intn(to+1-from) + from
|
||||
}
|
||||
|
||||
|
||||
const (
|
||||
// Standard length of uniuri string to achive ~95 bits of entropy.
|
||||
StdLen = 16
|
||||
// Length of uniurl string to achive ~119 bits of entropy, closest
|
||||
// to what can be losslessly converted to UUIDv4 (122 bits).
|
||||
UUIDLen = 20
|
||||
// Standard length of uniuri string to achive ~95 bits of entropy.
|
||||
StdLen = 16
|
||||
// Length of uniurl string to achive ~119 bits of entropy, closest
|
||||
// to what can be losslessly converted to UUIDv4 (122 bits).
|
||||
UUIDLen = 20
|
||||
)
|
||||
|
||||
|
||||
// Standard characters allowed in uniuri string.
|
||||
var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
||||
|
||||
|
||||
// New returns a new random string of the standard length, consisting of
|
||||
// standard characters.
|
||||
func New() string {
|
||||
return NewLenChars(StdLen, StdChars)
|
||||
return NewLenChars(StdLen, StdChars)
|
||||
}
|
||||
|
||||
|
||||
// NewLen returns a new random string of the provided length, consisting of
|
||||
// standard characters.
|
||||
func NewLen(length int) string {
|
||||
return NewLenChars(length, StdChars)
|
||||
return NewLenChars(length, StdChars)
|
||||
}
|
||||
|
||||
|
||||
// NewLenChars returns a new random string of the provided length, consisting
|
||||
// of the provided byte slice of allowed characters (maximum 256).
|
||||
func NewLenChars(length int, chars []byte) string {
|
||||
b := make([]byte, length)
|
||||
r := make([]byte, length+(length/4)) // storage for random bytes.
|
||||
clen := byte(len(chars))
|
||||
maxrb := byte(256 - (256 % len(chars)))
|
||||
i := 0
|
||||
for {
|
||||
if _, err := io.ReadFull(crand.Reader, r); err != nil {
|
||||
panic("error reading from random source: " + err.Error())
|
||||
}
|
||||
for _, c := range r {
|
||||
if c >= maxrb {
|
||||
// Skip this number to avoid modulo bias.
|
||||
continue
|
||||
}
|
||||
b[i] = chars[c%clen]
|
||||
i++
|
||||
if i == length {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
b := make([]byte, length)
|
||||
r := make([]byte, length+(length/4)) // storage for random bytes.
|
||||
clen := byte(len(chars))
|
||||
maxrb := byte(256 - (256 % len(chars)))
|
||||
i := 0
|
||||
for {
|
||||
if _, err := io.ReadFull(crand.Reader, r); err != nil {
|
||||
panic("error reading from random source: " + err.Error())
|
||||
}
|
||||
for _, c := range r {
|
||||
if c >= maxrb {
|
||||
// Skip this number to avoid modulo bias.
|
||||
continue
|
||||
}
|
||||
b[i] = chars[c%clen]
|
||||
i++
|
||||
if i == length {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func Fetch() (*Image, string) {
|
||||
d := make([]byte, 4)
|
||||
s := NewLen(4)
|
||||
ss := ""
|
||||
d = []byte(s)
|
||||
for v := range d {
|
||||
d[v] %= 10
|
||||
ss += strconv.FormatInt(int64(d[v]), 32)
|
||||
}
|
||||
return NewImage(d, 100, 40), ss
|
||||
}
|
||||
d := make([]byte, 4)
|
||||
s := NewLen(4)
|
||||
ss := ""
|
||||
d = []byte(s)
|
||||
for v := range d {
|
||||
d[v] %= 10
|
||||
ss += strconv.FormatInt(int64(d[v]), 32)
|
||||
}
|
||||
return NewImage(d, 100, 40), ss
|
||||
}
|
||||
|
||||
26
app/lea/crypto.go
Normal file
26
app/lea/crypto.go
Normal 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
|
||||
}
|
||||
@@ -24,29 +24,29 @@ import (
|
||||
type Html2Image struct {
|
||||
image *image.RGBA
|
||||
gc *draw2d.ImageGraphicContext
|
||||
|
||||
|
||||
// 试探
|
||||
gc2 *draw2d.ImageGraphicContext
|
||||
|
||||
|
||||
width float64 // 图片宽度
|
||||
height float64
|
||||
|
||||
|
||||
painWidth float64 // 画布宽度
|
||||
|
||||
|
||||
startX float64
|
||||
x float64
|
||||
y float64
|
||||
|
||||
|
||||
isFirstP bool // 是否是第一个段落?
|
||||
|
||||
|
||||
// 换行和段落的高度
|
||||
brY float64
|
||||
pY float64
|
||||
|
||||
|
||||
// 字体
|
||||
normalFontFamily draw2d.FontData
|
||||
boldFontFamily draw2d.FontData
|
||||
|
||||
|
||||
// preTag 之前的标签
|
||||
preTag *html.Node
|
||||
}
|
||||
@@ -58,23 +58,23 @@ func NewHtml2Image() *Html2Image {
|
||||
i, gc := h.InitGc(h.width, h.height)
|
||||
h.gc = gc;
|
||||
h.image = i
|
||||
|
||||
|
||||
// 试探
|
||||
_, h.gc2 = h.InitGc(h.width, 100)
|
||||
|
||||
h.startX = 10
|
||||
|
||||
|
||||
h.startX = 10
|
||||
|
||||
// 最初位置
|
||||
h.x = h.startX
|
||||
h.y = 80
|
||||
|
||||
|
||||
h.isFirstP = true
|
||||
|
||||
|
||||
h.normalFontFamily = draw2d.FontData{"xihei", 4, draw2d.FontStyleNormal};
|
||||
h.boldFontFamily = draw2d.FontData{"heiti", 5, draw2d.FontStyleNormal};
|
||||
|
||||
|
||||
h.SetNormalFont()
|
||||
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ func (this *Html2Image) InitGc(w, h float64) (* image.RGBA, *draw2d.ImageGraphic
|
||||
gc.SetFillColor(image.White)
|
||||
// fill the background
|
||||
// gc.Clear()
|
||||
|
||||
|
||||
draw2d.SetFontFolder(revel.BasePath + "/public/fonts/weibo")
|
||||
draw2d.Rect(gc, 0, 0, w, h) // 设置背景
|
||||
gc.FillStroke()
|
||||
gc.SetFillColor(image.Black)
|
||||
|
||||
|
||||
// 这个很耗时
|
||||
// gc.Translate(0, 0)
|
||||
return i, gc
|
||||
@@ -101,7 +101,7 @@ func (this *Html2Image) SaveToPngFile(filePath string) bool {
|
||||
// m := this.image;
|
||||
m := this.image.SubImage(image.Rect(0, 0, int(this.width), int(this.y + 20)))
|
||||
// 需要截断之
|
||||
|
||||
|
||||
f, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -124,14 +124,14 @@ func (this *Html2Image) SaveToPngFile(filePath string) bool {
|
||||
func (this *Html2Image) SetSmallFont() {
|
||||
this.gc.SetFontData(this.normalFontFamily)
|
||||
this.gc2.SetFontData(this.normalFontFamily)
|
||||
|
||||
|
||||
this.gc.SetFillColor(color.NRGBA{60, 60, 60, 255})
|
||||
this.gc.SetFontSize(12)
|
||||
this.gc2.SetFontSize(12)
|
||||
|
||||
|
||||
this.brY = 16
|
||||
this.pY = 30
|
||||
|
||||
|
||||
this.painWidth = this.width - 10
|
||||
}
|
||||
|
||||
@@ -139,13 +139,13 @@ func (this *Html2Image) SetNormalFont() {
|
||||
this.gc.SetFillColor(image.Black)
|
||||
this.gc.SetFontData(this.normalFontFamily)
|
||||
this.gc2.SetFontData(this.normalFontFamily)
|
||||
|
||||
|
||||
this.gc.SetFontSize(14)
|
||||
this.gc2.SetFontSize(14)
|
||||
|
||||
|
||||
this.brY = 20
|
||||
this.pY = 30
|
||||
|
||||
|
||||
this.painWidth = this.width - 10
|
||||
}
|
||||
func (this *Html2Image) SetAColor() {
|
||||
@@ -155,16 +155,16 @@ func (this *Html2Image) SetAColor() {
|
||||
// 标题
|
||||
func (this *Html2Image) SetTitleFont() {
|
||||
this.gc.SetFillColor(image.Black)
|
||||
|
||||
|
||||
this.gc.SetFontData(this.boldFontFamily)
|
||||
this.gc2.SetFontData(this.boldFontFamily)
|
||||
|
||||
|
||||
this.gc.SetFontSize(24)
|
||||
this.gc2.SetFontSize(24)
|
||||
|
||||
|
||||
this.brY = 30
|
||||
this.pY = 60
|
||||
|
||||
|
||||
this.painWidth = this.width - 100
|
||||
}
|
||||
|
||||
@@ -175,9 +175,9 @@ func (this *Html2Image) SetHeadFont(h string) {
|
||||
|
||||
this.gc.SetFontData(this.boldFontFamily)
|
||||
this.gc2.SetFontData(this.boldFontFamily)
|
||||
|
||||
|
||||
this.painWidth = this.width - 50
|
||||
|
||||
|
||||
if h == "h1" {
|
||||
this.gc.SetFontSize(20)
|
||||
this.gc2.SetFontSize(20)
|
||||
@@ -229,7 +229,7 @@ func (this *Html2Image) IsOver(r []rune) bool {
|
||||
// 以下的方法可以极大节约时间
|
||||
// a, b, c, d := this.gc2.GetStringBounds(string(r))
|
||||
// width2 := c - a + 2
|
||||
|
||||
|
||||
// fmt.Println(width2)
|
||||
// fmt.Println(c - a)
|
||||
|
||||
@@ -268,7 +268,7 @@ func (this *Html2Image) InsertText(text string, needTest bool, prefix string) {
|
||||
this.InsertText(text, true, prefix)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
r := []rune(text)
|
||||
// 试探吧, 可能需要截取
|
||||
if !needTest || !this.IsOver(r) {
|
||||
@@ -316,17 +316,17 @@ func (this *Html2Image) InsertText(text string, needTest bool, prefix string) {
|
||||
end = i
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 这一段写上
|
||||
// println("------>" + string(r[0:end]))
|
||||
|
||||
|
||||
// 这里, 判断后面一个是否是标点符号
|
||||
end = this.includePunctuation(r, end)
|
||||
this.InsertText(string(r[0:end]), false, prefix)
|
||||
this.NewBr()
|
||||
// 之后的
|
||||
this.InsertText(string(r[end:]), true, prefix)
|
||||
|
||||
|
||||
return;
|
||||
} else {
|
||||
// 没超出, 不用计算, 但出要看是否是结尾了
|
||||
@@ -351,17 +351,17 @@ func (this *Html2Image) InsertText(text string, needTest bool, prefix string) {
|
||||
end = maxRI + 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 这一段写上
|
||||
// println("-e----->" + string(r[0:end]))
|
||||
|
||||
|
||||
// 这里, 判断后面一个是否是标点符号
|
||||
end = this.includePunctuation(r, end)
|
||||
this.InsertText(string(r[0:end]), false, prefix)
|
||||
this.NewBr()
|
||||
// 之后的
|
||||
this.InsertText(string(r[end:]), true, prefix)
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -376,9 +376,9 @@ func (this *Html2Image) SetBottom(username, url string) {
|
||||
this.gc.SetStrokeColor(color.NRGBA{200, 0, 0, 255})
|
||||
this.gc.SetLineWidth(2)
|
||||
this.gc.FillStroke()
|
||||
|
||||
|
||||
this.SetSmallFont()
|
||||
|
||||
|
||||
// 左侧写字
|
||||
this.NewP()
|
||||
this.InsertText("本文来自 " + username + " 的leanote笔记", true, " ")
|
||||
@@ -389,11 +389,11 @@ func (this *Html2Image) SetBottom(username, url string) {
|
||||
siteUrl = "http://leanote.com"
|
||||
}
|
||||
this.InsertA(siteUrl + "/blog/" + username, false)
|
||||
|
||||
|
||||
this.setLogo()
|
||||
// this.painWidth = this.width - 100
|
||||
// this.NewP()
|
||||
// this.InsertText("leanote, 不一样的笔记.", false, " ")
|
||||
// this.InsertText("leanote, 不一样的笔记.", false, " ")
|
||||
// this.NewBr()
|
||||
// this.InsertText("在这里你可以管理自己的知识", false, " ")
|
||||
// this.NewBr()
|
||||
@@ -401,7 +401,7 @@ func (this *Html2Image) SetBottom(username, url string) {
|
||||
// this.NewBr()
|
||||
// this.InsertText("并且还可以将笔记设为博客公开", false, " ")
|
||||
// this.InsertText(". 赶紧加入吧! leanote.com", false, "")
|
||||
//
|
||||
//
|
||||
// Logo
|
||||
}
|
||||
|
||||
@@ -411,7 +411,7 @@ func (this *Html2Image) setImage(path string, x, y float64) {
|
||||
return;
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
var m1 image.Image
|
||||
_, ext := lea.SplitFilename(path)
|
||||
if ext == ".png" {
|
||||
@@ -424,8 +424,8 @@ func (this *Html2Image) setImage(path string, x, y float64) {
|
||||
if err != nil {
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.gc.Translate(x, y)
|
||||
this.gc.DrawImage(m1)
|
||||
this.gc.Translate(-x, -y)
|
||||
@@ -448,10 +448,10 @@ func (this *Html2Image) InsertA(text string, isNormal bool) {
|
||||
if text == "" {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
this.SetAColor()
|
||||
this.InsertText(text, true, "")
|
||||
|
||||
|
||||
// 还原
|
||||
if isNormal {
|
||||
this.SetNormalFont()
|
||||
@@ -464,17 +464,17 @@ func (this *Html2Image) InsertA(text string, isNormal bool) {
|
||||
func (this *Html2Image) InsertTitle(title string) {
|
||||
oldX := this.x
|
||||
oldY := this.y - 35
|
||||
|
||||
|
||||
// 插入之
|
||||
this.SetTitleFont()
|
||||
|
||||
|
||||
this.InsertText(title, true, " ")
|
||||
|
||||
|
||||
// 还原字体大小
|
||||
this.SetNormalFont()
|
||||
|
||||
|
||||
this.NewBr()
|
||||
|
||||
|
||||
this.gc.MoveTo(oldX, oldY)
|
||||
this.gc.LineTo(this.x, this.y - 10)
|
||||
this.gc.SetStrokeColor(color.NRGBA{200, 0, 0, 255})
|
||||
@@ -512,7 +512,7 @@ func (this *Html2Image) InsertCode(n *html.Node) {
|
||||
}
|
||||
}
|
||||
this.NewBr()
|
||||
|
||||
|
||||
this.gc.MoveTo(oldX, oldY)
|
||||
this.gc.LineTo(this.x, this.y - 20)
|
||||
this.gc.SetStrokeColor(color.NRGBA{0, 200, 0, 255})
|
||||
@@ -521,14 +521,14 @@ func (this *Html2Image) InsertCode(n *html.Node) {
|
||||
}
|
||||
|
||||
// 插入图片
|
||||
// 这个path应该是url,
|
||||
// 这个path应该是url,
|
||||
// http://abc.com/a.gif 需要先下载
|
||||
// 或 /upload/a.gif
|
||||
func (this *Html2Image) InsertImage(path string, needTrans bool, width uint) {
|
||||
if path == "" {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 是url, 那么取网络图片之
|
||||
var ok bool
|
||||
if strings.HasPrefix(path, "http") || strings.HasPrefix(path, "//") {
|
||||
@@ -539,7 +539,7 @@ func (this *Html2Image) InsertImage(path string, needTrans bool, width uint) {
|
||||
} else {
|
||||
path = revel.BasePath + "/public/" + path
|
||||
}
|
||||
|
||||
|
||||
// 需要转换, logo不需要转换
|
||||
if(needTrans) {
|
||||
painWidth := uint(this.painWidth - 10)
|
||||
@@ -557,7 +557,7 @@ func (this *Html2Image) InsertImage(path string, needTrans bool, width uint) {
|
||||
return;
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
var m1 image.Image
|
||||
_, ext := lea.SplitFilename(path)
|
||||
if ext == ".png" {
|
||||
@@ -568,8 +568,8 @@ func (this *Html2Image) InsertImage(path string, needTrans bool, width uint) {
|
||||
if err != nil {
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 如果之前是p, 那么不要有<br>
|
||||
if this.preTag.Data != "p" {
|
||||
this.NewBr()
|
||||
@@ -580,9 +580,9 @@ func (this *Html2Image) InsertImage(path string, needTrans bool, width uint) {
|
||||
this.gc.Translate(-this.x, -this.y) // 这个有用些
|
||||
this.y += float64(m1.Bounds().Dy()) - 20
|
||||
this.NewP()
|
||||
|
||||
|
||||
os.Remove(path)
|
||||
|
||||
|
||||
// 如果图片是文章第一个的话, 之后的需要p
|
||||
this.isFirstP = false
|
||||
}
|
||||
@@ -605,7 +605,7 @@ func (this *Html2Image) InsertBody(htmlStr string) (ok bool) {
|
||||
this.preTag = n
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
// 标签
|
||||
if n.Type == html.ElementNode {
|
||||
if n.Data == "p" {
|
||||
@@ -616,7 +616,7 @@ func (this *Html2Image) InsertBody(htmlStr string) (ok bool) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 也是一个段落, 只是要缩进
|
||||
if n.Data == "ul" || n.Data == "ol" {
|
||||
this.NewP()
|
||||
@@ -637,7 +637,7 @@ func (this *Html2Image) InsertBody(htmlStr string) (ok bool) {
|
||||
} else {
|
||||
f(c, n, "")
|
||||
}
|
||||
|
||||
|
||||
if c.Type == html.ElementNode {
|
||||
if c.Data == "br" || c.Data == "p" {
|
||||
needPrefix = true
|
||||
@@ -650,19 +650,19 @@ func (this *Html2Image) InsertBody(htmlStr string) (ok bool) {
|
||||
this.NewBr()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 标题
|
||||
if n.Data == "h1" || n.Data == "h2" || n.Data == "h3" || n.Data == "h4" {
|
||||
this.InsertHead(n)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if n.Data == "pre" {
|
||||
// 把之后的全拿过来
|
||||
this.InsertCode(n)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 图片
|
||||
// 得到src
|
||||
if n.Data == "img" {
|
||||
@@ -680,22 +680,22 @@ func (this *Html2Image) InsertBody(htmlStr string) (ok bool) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 链接
|
||||
// 如果链接里只有文本, 那么单独处理, 如果还有其它的, 不作链接处理
|
||||
if n.Data == "a" {
|
||||
if n.FirstChild == n.LastChild {
|
||||
this.InsertA(n.FirstChild.Data, true)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 空行
|
||||
if n.Data == "br" { // || n.Data == "div"
|
||||
this.NewBr()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 是文本, 输出之
|
||||
if n.Type == html.TextNode {
|
||||
data := strings.TrimSpace(n.Data);
|
||||
@@ -705,36 +705,36 @@ func (this *Html2Image) InsertBody(htmlStr string) (ok bool) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 其余的
|
||||
|
||||
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
f(c, n, prefix)
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
f(doc, nil, "")
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 主函数
|
||||
func ToImage(uid, username, noteId, title, htmlStr, toPath string) (ok bool) {
|
||||
h := NewHtml2Image()
|
||||
|
||||
|
||||
// 标题
|
||||
h.InsertTitle(title)
|
||||
|
||||
|
||||
// 主体
|
||||
ok = h.InsertBody(htmlStr)
|
||||
if(!ok) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 页眉与页脚
|
||||
h.SetBottom(username, "")
|
||||
|
||||
|
||||
// 保存成png图片
|
||||
ok = h.SaveToPngFile(toPath)
|
||||
return
|
||||
@@ -757,4 +757,4 @@ func TestFillString() {
|
||||
|
||||
func ToImage(uid, username, noteId, title, htmlStr, toPath string) (ok bool) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,3 @@ import (
|
||||
func Html2Image(userInfo info.User, note info.Note, content, toPath string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
223
app/lea/i18n/i18n.go
Normal file
223
app/lea/i18n/i18n.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/revel/revel"
|
||||
"github.com/robfig/config"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
CurrentLocaleRenderArg = "currentLocale" // The key for the current locale render arg value
|
||||
|
||||
messageFilesDirectory = "messages"
|
||||
messageFilePattern = `^\w+\.conf$`
|
||||
unknownValueFormat = "??? %s ???"
|
||||
defaultLanguageOption = "i18n.default_language"
|
||||
localeCookieConfigKey = "i18n.cookie"
|
||||
)
|
||||
|
||||
var (
|
||||
// All currently loaded message configs.
|
||||
// en-us, zh-cn, zh-hk ->
|
||||
messages map[string]*config.Config
|
||||
)
|
||||
|
||||
func GetAllLangMessages() map[string]*config.Config {
|
||||
return messages
|
||||
}
|
||||
|
||||
func HasLang(lang string) bool {
|
||||
_, ok := messages[lang]
|
||||
return ok
|
||||
}
|
||||
|
||||
func GetDefaultLang() string {
|
||||
lang, _ := revel.Config.String(defaultLanguageOption)
|
||||
return lang
|
||||
}
|
||||
|
||||
// Return all currently loaded message languages.
|
||||
func MessageLanguages() []string {
|
||||
languages := make([]string, len(messages))
|
||||
i := 0
|
||||
for language, _ := range messages {
|
||||
languages[i] = language
|
||||
i++
|
||||
}
|
||||
return languages
|
||||
}
|
||||
|
||||
// Perform a message look-up for the given locale and message using the given arguments.
|
||||
//
|
||||
// When either an unknown locale or message is detected, a specially formatted string is returned.
|
||||
func Message(locale, message string, args ...interface{}) string {
|
||||
language, region := parseLocale(locale)
|
||||
|
||||
langAndRegion := language + "-" + region
|
||||
// revel.TRACE.Println(langAndRegion + " 怎么回事")
|
||||
|
||||
messageConfig, knownLanguage := messages[langAndRegion]
|
||||
if !knownLanguage {
|
||||
// revel.TRACE.Printf("Unsupported language for locale '%s' and message '%s', trying default language", locale, message)
|
||||
|
||||
if defaultLanguage, found := revel.Config.String(defaultLanguageOption); found {
|
||||
// revel.TRACE.Printf("Using default language '%s'", defaultLanguage)
|
||||
|
||||
messageConfig, knownLanguage = messages[defaultLanguage]
|
||||
if !knownLanguage {
|
||||
// WARN.Printf("Unsupported default language for locale '%s' and message '%s'", defaultLanguage, message)
|
||||
return fmt.Sprintf(unknownValueFormat, message)
|
||||
}
|
||||
} else {
|
||||
// WARN.Printf("Unable to find default language option (%s); messages for unsupported locales will never be translated", defaultLanguageOption)
|
||||
return fmt.Sprintf(unknownValueFormat, message)
|
||||
}
|
||||
}
|
||||
|
||||
// This works because unlike the goconfig documentation suggests it will actually
|
||||
// try to resolve message in DEFAULT if it did not find it in the given section.
|
||||
value, error := messageConfig.String(region, message)
|
||||
if error != nil {
|
||||
// WARN.Printf("Unknown message '%s' for locale '%s'", message, locale)
|
||||
return fmt.Sprintf(unknownValueFormat, message)
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
// revel.TRACE.Printf("Arguments detected, formatting '%s' with %v", value, args)
|
||||
value = fmt.Sprintf(value, args...)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func parseLocale(locale string) (language, region string) {
|
||||
if strings.Contains(locale, "-") {
|
||||
languageAndRegion := strings.Split(locale, "-")
|
||||
return languageAndRegion[0], languageAndRegion[1]
|
||||
}
|
||||
|
||||
return locale, ""
|
||||
}
|
||||
|
||||
// Recursively read and cache all available messages from all message files on the given path.
|
||||
func loadMessages(path string) {
|
||||
messages = make(map[string]*config.Config)
|
||||
|
||||
if error := filepath.Walk(path, loadEachMessageLang); error != nil && !os.IsNotExist(error) {
|
||||
// ERROR.Println("Error reading messages files:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载每一个文件夹
|
||||
func loadEachMessageLang(parentPath string, parentInfo os.FileInfo, osError error) (err error) {
|
||||
if !parentInfo.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := filepath.Walk(parentPath, func(path string, info os.FileInfo, osError error) error {
|
||||
return loadMessageFile(parentInfo.Name(), path, info, osError)
|
||||
|
||||
}); err != nil && !os.IsNotExist(err) {
|
||||
// ERROR.Println("Error reading messages files:", error)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Load a single message file
|
||||
func loadMessageFile(locale string, path string, info os.FileInfo, osError error) error {
|
||||
if osError != nil {
|
||||
return osError
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if matched, _ := regexp.MatchString(messageFilePattern, info.Name()); matched {
|
||||
if config, error := parseMessagesFile(path); error != nil {
|
||||
return error
|
||||
} else {
|
||||
// locale := parseLocaleFromFileName(info.Name())
|
||||
// revel.TRACE.Print(locale + "----locale")
|
||||
|
||||
// If we have already parsed a message file for this locale, merge both
|
||||
if _, exists := messages[locale]; exists {
|
||||
messages[locale].Merge(config)
|
||||
revel.TRACE.Printf("Successfully merged messages for locale '%s'", locale)
|
||||
} else {
|
||||
messages[locale] = config
|
||||
}
|
||||
|
||||
revel.TRACE.Println("Successfully loaded messages from file", info.Name())
|
||||
}
|
||||
} else {
|
||||
revel.TRACE.Printf("Ignoring file %s because it did not have a valid extension", info.Name())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseMessagesFile(path string) (messageConfig *config.Config, error error) {
|
||||
messageConfig, error = config.ReadDefault(path)
|
||||
return
|
||||
}
|
||||
|
||||
func parseLocaleFromFileName(file string) string {
|
||||
extension := filepath.Ext(file)[1:]
|
||||
return strings.ToLower(extension)
|
||||
}
|
||||
|
||||
func init() {
|
||||
revel.OnAppStart(func() {
|
||||
loadMessages(filepath.Join(revel.BasePath, messageFilesDirectory))
|
||||
})
|
||||
}
|
||||
|
||||
func I18nFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
if foundCookie, cookieValue := hasLocaleCookie(c.Request); foundCookie {
|
||||
revel.TRACE.Printf("Found locale cookie value: %s", cookieValue)
|
||||
setCurrentLocaleControllerArguments(c, cookieValue)
|
||||
} else if foundHeader, headerValue := hasAcceptLanguageHeader(c.Request); foundHeader {
|
||||
revel.TRACE.Printf("Found Accept-Language header value: %s", headerValue)
|
||||
setCurrentLocaleControllerArguments(c, headerValue)
|
||||
} else {
|
||||
revel.TRACE.Println("Unable to find locale in cookie or header, using empty string")
|
||||
setCurrentLocaleControllerArguments(c, "")
|
||||
}
|
||||
fc[0](c, fc[1:])
|
||||
}
|
||||
|
||||
// Set the current locale controller argument (CurrentLocaleControllerArg) with the given locale.
|
||||
func setCurrentLocaleControllerArguments(c *revel.Controller, locale string) {
|
||||
c.Request.Locale = locale
|
||||
c.RenderArgs[CurrentLocaleRenderArg] = locale
|
||||
}
|
||||
|
||||
// Determine whether the given request has valid Accept-Language value.
|
||||
//
|
||||
// Assumes that the accept languages stored in the request are sorted according to quality, with top
|
||||
// quality first in the slice.
|
||||
func hasAcceptLanguageHeader(request *revel.Request) (bool, string) {
|
||||
if request.AcceptLanguages != nil && len(request.AcceptLanguages) > 0 {
|
||||
return true, request.AcceptLanguages[0].Language
|
||||
}
|
||||
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// Determine whether the given request has a valid language cookie value.
|
||||
func hasLocaleCookie(request *revel.Request) (bool, string) {
|
||||
if request != nil && request.Cookies() != nil {
|
||||
name := revel.Config.StringDefault(localeCookieConfigKey, revel.CookiePrefix+"_LANG")
|
||||
if cookie, error := request.Cookie(name); error == nil {
|
||||
return true, cookie.Value
|
||||
} else {
|
||||
revel.TRACE.Printf("Unable to read locale cookie with name '%s': %s", name, error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return false, ""
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"os"
|
||||
// "path/filepath"
|
||||
"strings"
|
||||
// "path/filepath"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"io/ioutil"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
)
|
||||
|
||||
// net的util
|
||||
@@ -16,33 +17,33 @@ import (
|
||||
// 返回文件的完整目录
|
||||
func WriteUrl(url string, toPath string) (length int64, newFilename, path string, ok bool) {
|
||||
if url == "" {
|
||||
return;
|
||||
return
|
||||
}
|
||||
content, err := GetContent(url)
|
||||
if err != nil {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
length = int64(len(content))
|
||||
|
||||
|
||||
// a.html?a=a11&xxx
|
||||
url = trimQueryParams(url)
|
||||
_, ext := SplitFilename(url)
|
||||
if toPath == "" {
|
||||
toPath = "/tmp"
|
||||
}
|
||||
// dir := filepath.Dir(toPath)
|
||||
// dir := filepath.Dir(toPath)
|
||||
newFilename = NewGuid() + ext
|
||||
fullPath := toPath + "/" + newFilename
|
||||
|
||||
|
||||
// 写到文件中
|
||||
file, err := os.Create(fullPath)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
file.Write(content)
|
||||
|
||||
|
||||
path = fullPath
|
||||
ok = true
|
||||
return
|
||||
@@ -53,43 +54,43 @@ func GetContent(url string) (content []byte, err error) {
|
||||
var resp *http.Response
|
||||
resp, err = http.Get(url)
|
||||
Log(err)
|
||||
if(resp != nil && resp.Body != nil) {
|
||||
if resp != nil && resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
} else {
|
||||
}
|
||||
if resp == nil || resp.Body == nil || err != nil || resp.StatusCode != http.StatusOK {
|
||||
return
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
buf, err = ioutil.ReadAll(resp.Body)
|
||||
if(err != nil) {
|
||||
Log(err)
|
||||
if resp == nil || resp.Body == nil || err != nil || resp.StatusCode != http.StatusOK {
|
||||
return
|
||||
}
|
||||
|
||||
content = buf;
|
||||
err = nil
|
||||
return
|
||||
|
||||
var buf []byte
|
||||
buf, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
Log(err)
|
||||
return
|
||||
}
|
||||
|
||||
content = buf
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// 将url ?, #后面的字符串去掉
|
||||
func trimQueryParams(url string) string {
|
||||
pos := strings.Index(url, "?");
|
||||
pos := strings.Index(url, "?")
|
||||
if pos != -1 {
|
||||
url = Substr(url, 0, pos);
|
||||
url = Substr(url, 0, pos)
|
||||
}
|
||||
|
||||
pos = strings.Index(url, "#");
|
||||
|
||||
pos = strings.Index(url, "#")
|
||||
if pos != -1 {
|
||||
url = Substr(url, 0, pos);
|
||||
url = Substr(url, 0, pos)
|
||||
}
|
||||
|
||||
pos = strings.Index(url, "!");
|
||||
|
||||
pos = strings.Index(url, "!")
|
||||
if pos != -1 {
|
||||
url = Substr(url, 0, pos);
|
||||
url = Substr(url, 0, pos)
|
||||
}
|
||||
return url;
|
||||
return url
|
||||
}
|
||||
|
||||
// 通过domain得到ip
|
||||
@@ -99,4 +100,4 @@ func GetIpFromDomain(domain string) string {
|
||||
return ip[0].String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/revel/revel"
|
||||
// "github.com/leanote/leanote/app/service"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
@@ -11,10 +11,11 @@ import (
|
||||
// overwite revel RouterFilter
|
||||
// /api/user/Info => ApiUser.Info()
|
||||
var staticPrefix = []string{"/public", "/favicon.ico", "/css", "/js", "/images", "/tinymce", "/upload", "/fonts"}
|
||||
|
||||
func RouterFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
// 补全controller部分
|
||||
path := c.Request.Request.URL.Path
|
||||
|
||||
|
||||
// Figure out the Controller/Action
|
||||
var route *revel.RouteMatch = revel.MainRouter.Route(c.Request.Request)
|
||||
if route == nil {
|
||||
@@ -27,25 +28,29 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
c.Result = c.NotFound("(intentionally)")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
//----------
|
||||
// life start
|
||||
/*
|
||||
type URL struct {
|
||||
Scheme string
|
||||
Opaque string // encoded opaque data
|
||||
User *Userinfo // username and password information
|
||||
Host string // host or host:port
|
||||
Path string
|
||||
RawQuery string // encoded query values, without '?'
|
||||
Fragment string // fragment for references, without '#'
|
||||
}
|
||||
type URL struct {
|
||||
Scheme string
|
||||
Opaque string // encoded opaque data
|
||||
User *Userinfo // username and password information
|
||||
Host string // host or host:port
|
||||
Path string
|
||||
RawQuery string // encoded query values, without '?'
|
||||
Fragment string // fragment for references, without '#'
|
||||
}
|
||||
*/
|
||||
if route.ControllerName != "Static" {
|
||||
|
||||
// 检查mongodb 是否lost
|
||||
db.CheckMongoSessionLost()
|
||||
|
||||
// api设置
|
||||
// leanote.com/api/user/get => ApiUser::Get
|
||||
//* /api/login ApiAuth.Login, 这里的设置, 其实已经转成了ApiAuth了
|
||||
if strings.HasPrefix(path, "/api") && !strings.HasPrefix(route.ControllerName, "Api"){
|
||||
if strings.HasPrefix(path, "/api") && !strings.HasPrefix(route.ControllerName, "Api") {
|
||||
route.ControllerName = "Api" + route.ControllerName
|
||||
} else if strings.HasPrefix(path, "/member") && !strings.HasPrefix(route.ControllerName, "Member") {
|
||||
// member设置
|
||||
@@ -53,7 +58,7 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
}
|
||||
// end
|
||||
}
|
||||
|
||||
|
||||
// Set the action.
|
||||
if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
|
||||
c.Result = c.NotFound(err.Error())
|
||||
@@ -80,4 +85,3 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
|
||||
fc[0](c, fc[1:])
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -2,11 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
// "time"
|
||||
)
|
||||
|
||||
/*
|
||||
用golang exec 总是说找不到uglifyjs命令, 需要全部路径
|
||||
而且node, npm要在/usr/bin下, 已建ln
|
||||
@@ -29,10 +31,10 @@ 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",
|
||||
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/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/"
|
||||
var cmdPath = "/usr/local/bin/uglifyjs"
|
||||
@@ -52,26 +54,26 @@ func compressJs(filename string) {
|
||||
to := base + filename + "-min.js"
|
||||
cmd := exec.Command(cmdPath, source, "-o", to)
|
||||
_, err := cmd.CombinedOutput()
|
||||
|
||||
fmt.Println(source)
|
||||
cmdError(err)
|
||||
}
|
||||
|
||||
|
||||
func combineJs() {
|
||||
// 生成一个总文件
|
||||
cmd := exec.Command("rm", base + "js/all.js")
|
||||
cmd := exec.Command("rm", base+"js/all.js")
|
||||
_, err := cmd.CombinedOutput()
|
||||
cmdError(err)
|
||||
|
||||
|
||||
for _, js := range jss {
|
||||
to := base + js + "-min.js"
|
||||
fmt.Println(to)
|
||||
compressJs(js)
|
||||
|
||||
|
||||
// 每个压缩后的文件放入之
|
||||
cmd2 := exec.Command("/bin/sh", "-c", "cat " + to + " >> " + base + "js/all.js")
|
||||
cmd2 := exec.Command("/bin/sh", "-c", "cat "+to+" >> "+base+"js/all.js")
|
||||
_, err := cmd2.CombinedOutput()
|
||||
cmdError(err)
|
||||
cmd2 = exec.Command("/bin/sh", "-c", "cat \n >> " + base + "js/all.js")
|
||||
cmd2 = exec.Command("/bin/sh", "-c", "cat \n >> "+base+"js/all.js")
|
||||
_, err = cmd2.CombinedOutput()
|
||||
cmdError(err)
|
||||
}
|
||||
@@ -80,68 +82,78 @@ func combineJs() {
|
||||
// 改note-dev->note
|
||||
func dev() {
|
||||
// 即替换note.js->note-min.js
|
||||
m := map[string]string{"note.js": "note-min.js",
|
||||
"app.js": "app-min.js",
|
||||
"page.js": "page-min.js",
|
||||
"common.js": "common-min.js",
|
||||
"notebook.js": "notebook-min.js",
|
||||
"share.js": "share-min.js",
|
||||
"tag.js": "tag-min.js",
|
||||
"main.js": "main-min.js",
|
||||
"jquery.contextmenu.js": "jquery.contextmenu-min.js",
|
||||
"editor/editor.js": "editor/editor-min.js",
|
||||
m := map[string]string{"tinymce.dev.js": "tinymce.min.js",
|
||||
"tinymce.js": "tinymce.min.js",
|
||||
"jquery.ztree.all-3.5.js": "jquery.ztree.all-3.5-min.js",
|
||||
"note.js": "note-min.js",
|
||||
"app.js": "app-min.js",
|
||||
"page.js": "page-min.js",
|
||||
"common.js": "common-min.js",
|
||||
"notebook.js": "notebook-min.js",
|
||||
"share.js": "share-min.js",
|
||||
"tag.js": "tag-min.js",
|
||||
"jquery.slimscroll.js": "jquery.slimscroll-min.js",
|
||||
"jquery.contextmenu.js": "jquery.contextmenu-min.js",
|
||||
"editor/editor.js": "editor/editor-min.js",
|
||||
"/public/mdeditor/editor/scrollLink.js": "/public/mdeditor/editor/scrollLink-min.js",
|
||||
"console.log(o);": "",
|
||||
}
|
||||
"console.log(o);": "",
|
||||
}
|
||||
path := base1 + "/src/views/note/note-dev.html"
|
||||
target := base1 + "/src/views/note/note.html"
|
||||
|
||||
|
||||
bs, _ := ioutil.ReadFile(path)
|
||||
content := string(bs)
|
||||
print(content)
|
||||
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)
|
||||
}
|
||||
|
||||
// 压缩js成一块
|
||||
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"
|
||||
c, err := cmd.CombinedOutput()
|
||||
// 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("/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")
|
||||
cmd2.CombinedOutput()
|
||||
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))
|
||||
cmdError(err)
|
||||
}
|
||||
|
||||
func main() {
|
||||
dev();
|
||||
|
||||
// 压缩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",
|
||||
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",
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, js := range otherJss {
|
||||
compressJs(js)
|
||||
}
|
||||
|
||||
|
||||
// 先压缩后合并
|
||||
combineJs()
|
||||
|
||||
// 压缩tinymce
|
||||
tinymce()
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
@@ -30,9 +30,9 @@ func (this *AlbumService) GetAlbums(userId string) []info.Album {
|
||||
// delete album
|
||||
// presupposition: has no images under this ablum
|
||||
func (this *AlbumService) DeleteAlbum(userId, albumId string) (bool, string) {
|
||||
if db.Count(db.Files, bson.M{"AlbumId": bson.ObjectIdHex(albumId),
|
||||
if db.Count(db.Files, bson.M{"AlbumId": bson.ObjectIdHex(albumId),
|
||||
"UserId": bson.ObjectIdHex(userId),
|
||||
}) == 0 {
|
||||
}) == 0 {
|
||||
return db.DeleteByIdAndUserId(db.Albums, albumId, userId), ""
|
||||
}
|
||||
return false, "has images"
|
||||
@@ -41,4 +41,4 @@ func (this *AlbumService) DeleteAlbum(userId, albumId string) (bool, string) {
|
||||
// update album name
|
||||
func (this *AlbumService) UpdateAlbum(albumId, userId, name string) bool {
|
||||
return db.UpdateByIdAndUserIdField(db.Albums, albumId, userId, "Name", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,47 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"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"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AttachService struct {
|
||||
}
|
||||
|
||||
// add attach
|
||||
func (this *AttachService) AddAttach(attach info.Attach) bool {
|
||||
// api调用时, 添加attach之前是没有note的
|
||||
// fromApi表示是api添加的, updateNote传过来的, 此时不要incNote's usn, 因为updateNote会inc的
|
||||
func (this *AttachService) AddAttach(attach info.Attach, fromApi bool) (ok bool, msg string) {
|
||||
attach.CreatedTime = time.Now()
|
||||
ok := db.Insert(db.Attachs, attach)
|
||||
|
||||
ok = db.Insert(db.Attachs, attach)
|
||||
|
||||
note := noteService.GetNoteById(attach.NoteId.Hex())
|
||||
|
||||
// api调用时, 添加attach之前是没有note的
|
||||
var userId string
|
||||
if note.NoteId != "" {
|
||||
userId = note.UserId.Hex()
|
||||
} else {
|
||||
userId = attach.UploadUserId.Hex()
|
||||
}
|
||||
|
||||
if ok {
|
||||
// 更新笔记的attachs num
|
||||
this.updateNoteAttachNum(attach.NoteId, 1)
|
||||
}
|
||||
|
||||
return ok
|
||||
|
||||
if !fromApi {
|
||||
// 增长note's usn
|
||||
noteService.IncrNoteUsn(attach.NoteId.Hex(), userId)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 更新笔记的附件个数
|
||||
@@ -32,13 +49,13 @@ func (this *AttachService) AddAttach(attach info.Attach) bool {
|
||||
func (this *AttachService) updateNoteAttachNum(noteId bson.ObjectId, addNum int) bool {
|
||||
num := db.Count(db.Attachs, bson.M{"NoteId": noteId})
|
||||
/*
|
||||
note := info.Note{}
|
||||
note = noteService.GetNoteById(noteId.Hex())
|
||||
note.AttachNum += addNum
|
||||
if note.AttachNum < 0 {
|
||||
note.AttachNum = 0
|
||||
}
|
||||
Log(note.AttachNum)
|
||||
note := info.Note{}
|
||||
note = noteService.GetNoteById(noteId.Hex())
|
||||
note.AttachNum += addNum
|
||||
if note.AttachNum < 0 {
|
||||
note.AttachNum = 0
|
||||
}
|
||||
Log(note.AttachNum)
|
||||
*/
|
||||
return db.UpdateByQField(db.Notes, bson.M{"_id": noteId}, "AttachNum", num)
|
||||
}
|
||||
@@ -46,21 +63,45 @@ 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)
|
||||
|
||||
|
||||
return attachs
|
||||
}
|
||||
|
||||
// api调用, 通过noteIds得到note's attachs, 通过noteId归类返回
|
||||
func (this *AttachService) getAttachsByNoteIds(noteIds []bson.ObjectId) map[string][]info.Attach {
|
||||
attachs := []info.Attach{}
|
||||
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.M{"$in": noteIds}}, &attachs)
|
||||
noteAttchs := make(map[string][]info.Attach)
|
||||
for _, attach := range attachs {
|
||||
noteId := attach.NoteId.Hex()
|
||||
if itAttachs, ok := noteAttchs[noteId]; ok {
|
||||
noteAttchs[noteId] = append(itAttachs, attach)
|
||||
} else {
|
||||
noteAttchs[noteId] = []info.Attach{attach}
|
||||
}
|
||||
}
|
||||
return noteAttchs
|
||||
}
|
||||
|
||||
func (this *AttachService) UpdateImageTitle(userId, fileId, title string) bool {
|
||||
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
|
||||
}
|
||||
|
||||
|
||||
// Delete note to delete attas firstly
|
||||
func (this *AttachService) DeleteAllAttachs(noteId, userId string) bool {
|
||||
note := noteService.GetNoteById(noteId)
|
||||
@@ -73,27 +114,32 @@ func (this *AttachService) DeleteAllAttachs(noteId, userId string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// delete attach
|
||||
// 删除附件为什么要incrNoteUsn ? 因为可能没有内容要修改的
|
||||
func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string) {
|
||||
attach := info.Attach{}
|
||||
db.Get(db.Attachs, attachId, &attach)
|
||||
|
||||
if(attach.AttachId != "") {
|
||||
|
||||
if attach.AttachId != "" {
|
||||
// 判断是否有权限为笔记添加附件
|
||||
if !shareService.HasUpdateNotePerm(attach.NoteId.Hex(), userId) {
|
||||
return false, "No Perm"
|
||||
}
|
||||
|
||||
|
||||
if db.Delete(db.Attachs, bson.M{"_id": bson.ObjectIdHex(attachId)}) {
|
||||
this.updateNoteAttachNum(attach.NoteId, -1)
|
||||
attach.Path = strings.TrimLeft(attach.Path, "/")
|
||||
err := os.Remove(revel.BasePath + "/" + attach.Path)
|
||||
if err == nil {
|
||||
return true, "delete file error"
|
||||
// userService.UpdateAttachSize(note.UserId.Hex(), -attach.Size)
|
||||
// 修改note Usn
|
||||
noteService.IncrNoteUsn(attach.NoteId.Hex(), userId)
|
||||
|
||||
return true, "delete file success"
|
||||
}
|
||||
return false, "delete file error"
|
||||
}
|
||||
@@ -107,37 +153,37 @@ func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string)
|
||||
// userId是否具有attach的访问权限
|
||||
func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attach) {
|
||||
if attachId == "" {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
attach = info.Attach{}
|
||||
db.Get(db.Attachs, attachId, &attach)
|
||||
path := attach.Path
|
||||
if path == "" {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
note := noteService.GetNoteById(attach.NoteId.Hex())
|
||||
|
||||
|
||||
// 判断权限
|
||||
|
||||
|
||||
// 笔记是否是公开的
|
||||
if note.IsBlog {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 笔记是否是我的
|
||||
if note.UserId.Hex() == userId {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 我是否有权限查看或协作
|
||||
if shareService.HasReadNotePerm(attach.NoteId.Hex(), userId) {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
attach = info.Attach{}
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// 复制笔记时需要复制附件
|
||||
@@ -145,31 +191,58 @@ func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attac
|
||||
func (this *AttachService) CopyAttachs(noteId, toNoteId, toUserId string) bool {
|
||||
attachs := []info.Attach{}
|
||||
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)
|
||||
|
||||
|
||||
// 复制之
|
||||
toNoteIdO := bson.ObjectIdHex(toNoteId)
|
||||
for _, attach := range attachs {
|
||||
attach.AttachId = ""
|
||||
attach.NoteId = toNoteIdO
|
||||
|
||||
|
||||
// 文件复制一份
|
||||
_, ext := SplitFilename(attach.Name)
|
||||
newFilename := NewGuid() + ext
|
||||
dir := "files/" + toUserId + "/attachs"
|
||||
filePath := dir + "/" + newFilename
|
||||
err := os.MkdirAll(revel.BasePath + "/" + dir, 0755)
|
||||
err := os.MkdirAll(revel.BasePath+"/"+dir, 0755)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = CopyFile(revel.BasePath + "/" + attach.Path, revel.BasePath + "/" + filePath)
|
||||
_, err = CopyFile(revel.BasePath+"/"+attach.Path, revel.BasePath+"/"+filePath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
attach.Name = newFilename
|
||||
attach.Path = filePath
|
||||
|
||||
this.AddAttach(attach)
|
||||
|
||||
this.AddAttach(attach, false)
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 只留下files的数据, 其它的都删除
|
||||
func (this *AttachService) UpdateOrDeleteAttachApi(noteId, userId string, files []info.NoteFile) bool {
|
||||
// 现在数据库内的
|
||||
attachs := this.ListAttachs(noteId, userId)
|
||||
|
||||
nowAttachs := map[string]bool{}
|
||||
if files != nil {
|
||||
for _, file := range files {
|
||||
if file.IsAttach && file.FileId != "" {
|
||||
nowAttachs[file.FileId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, attach := range attachs {
|
||||
fileId := attach.AttachId.Hex()
|
||||
if !nowAttachs[fileId] {
|
||||
// 需要删除的
|
||||
// TODO 权限验证去掉
|
||||
this.DeleteAttach(fileId, userId)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
@@ -2,23 +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"
|
||||
. "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 {
|
||||
userInfo := userService.LoginGetUserInfo(emailOrUsername, Md5(pwd))
|
||||
return userInfo
|
||||
// 使用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.GetUserInfoByName(emailOrUsername)
|
||||
if userInfo.UserId == "" || !ComparePwd(pwd, userInfo.Pwd) {
|
||||
return userInfo, errors.New("wrong username or password")
|
||||
}
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
// 注册
|
||||
@@ -32,70 +40,79 @@ func (this *AuthService) Login(emailOrUsername, pwd string) info.User {
|
||||
// 1. 添加用户
|
||||
// 2. 将leanote共享给我
|
||||
// [ok]
|
||||
func (this *AuthService) Register(email, pwd string) (bool, string) {
|
||||
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)
|
||||
}
|
||||
return this.register(user)
|
||||
}
|
||||
|
||||
func (this *AuthService) register(user info.User) (bool, string) {
|
||||
if userService.AddUser(user) {
|
||||
// 添加笔记本, 生活, 学习, 工作
|
||||
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)
|
||||
}
|
||||
|
||||
email := user.Email
|
||||
|
||||
|
||||
// 添加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.AddShareNotebook(notebook["notebookId"], perm, registerSharedUserId, email);
|
||||
shareService.AddShareNotebookToUserId(notebook["notebookId"], perm, registerSharedUserId, userId)
|
||||
}
|
||||
|
||||
|
||||
// 添加共享笔记
|
||||
for _, note := range registerSharedNotes {
|
||||
perm, _ := strconv.Atoi(note["perm"])
|
||||
shareService.AddShareNote(note["noteId"], perm, registerSharedUserId, email);
|
||||
shareService.AddShareNoteToUserId(note["noteId"], perm, registerSharedUserId, userId)
|
||||
}
|
||||
|
||||
|
||||
// 复制笔记
|
||||
for _, noteId := range registerCopyNoteIds {
|
||||
note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex());
|
||||
noteUpdate := bson.M{"IsBlog": true}
|
||||
noteService.UpdateNote(user.UserId.Hex(), user.UserId.Hex(), note.NoteId.Hex(), noteUpdate)
|
||||
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, ""
|
||||
}
|
||||
|
||||
@@ -106,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
|
||||
}
|
||||
@@ -122,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}, ¬e)
|
||||
db.GetByQ(db.Notes, bson.M{"UserId": bson.ObjectIdHex(userId), "UrlTitle": encodeValue(noteIdOrUrlTitle),
|
||||
"IsBlog": true,
|
||||
"IsTrash": false, "IsDeleted": false}, ¬e)
|
||||
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{}
|
||||
}
|
||||
|
||||
// 内容
|
||||
@@ -69,9 +71,14 @@ func (this *BlogService) GetBlogItem(note info.Note) (blog info.BlogItem) {
|
||||
}
|
||||
|
||||
// 得到用户共享的notebooks
|
||||
// 3/19 博客不是deleted
|
||||
func (this *BlogService) ListBlogNotebooks(userId string) []info.Notebook {
|
||||
notebooks := []info.Notebook{}
|
||||
db.ListByQ(db.Notebooks, bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true}, ¬ebooks)
|
||||
orQ := []bson.M{
|
||||
bson.M{"IsDeleted": false},
|
||||
bson.M{"IsDeleted": bson.M{"$exists": false}},
|
||||
}
|
||||
db.ListByQ(db.Notebooks, bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "$or": orQ}, ¬ebooks)
|
||||
return notebooks
|
||||
}
|
||||
|
||||
@@ -126,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
|
||||
}
|
||||
@@ -137,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"}, ¬es)
|
||||
|
||||
db.DeleteAll(db.TagCounts, bson.M{"UserId": userIdO, "IsBlog": true})
|
||||
@@ -179,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)
|
||||
}
|
||||
@@ -286,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)
|
||||
|
||||
@@ -353,7 +362,7 @@ func (this *BlogService) SearchBlog(key, userId string, page, pageSize int, sort
|
||||
// 上一篇文章, 下一篇文章
|
||||
// sorterField, baseTime是基准, sorterField=PublicTime, title
|
||||
// isAsc是用户自定义的排序方式
|
||||
func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bool, baseTime interface{}) (info.Post, info.Post) {
|
||||
func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bool, noteId string, baseTime interface{}) (info.Post, info.Post) {
|
||||
userIdO := bson.ObjectIdHex(userId)
|
||||
|
||||
var sortFieldT1, sortFieldT2 bson.M
|
||||
@@ -361,16 +370,16 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
|
||||
if !isAsc {
|
||||
// 降序
|
||||
/*
|
||||
------- pre
|
||||
----- now
|
||||
--- next
|
||||
--
|
||||
------- pre
|
||||
----- now
|
||||
--- next
|
||||
--
|
||||
*/
|
||||
// 上一篇时间要比它大, 找最小的
|
||||
sortFieldT1 = bson.M{"$gt": baseTime}
|
||||
sortFieldT1 = bson.M{"$gte": baseTime} // 为什么要相等, 因为将notebook发布成博客, 会统一修改note的publicTime, 此时所有notes都一样
|
||||
sortFieldR1 = sorterField
|
||||
// 下一篇时间要比它小
|
||||
sortFieldT2 = bson.M{"$lt": baseTime}
|
||||
sortFieldT2 = bson.M{"$lte": baseTime}
|
||||
sortFieldR2 = "-" + sorterField
|
||||
} else {
|
||||
// 升序
|
||||
@@ -381,22 +390,30 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
|
||||
---------
|
||||
*/
|
||||
// 上一篇要比它小, 找最大的
|
||||
sortFieldT1 = bson.M{"$lt": baseTime}
|
||||
sortFieldT1 = bson.M{"$lte": baseTime}
|
||||
sortFieldR1 = "-" + sorterField
|
||||
// 下一篇, 找最小的
|
||||
sortFieldT2 = bson.M{"$gt": baseTime}
|
||||
sortFieldT2 = bson.M{"$gte": baseTime}
|
||||
sortFieldR2 = sorterField
|
||||
}
|
||||
|
||||
// 1
|
||||
// 上一篇, 比基时间要小, 但是是最后一篇, 所以是降序
|
||||
note := info.Note{}
|
||||
query := bson.M{"UserId": userIdO, "IsTrash": false, "IsBlog": true,
|
||||
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)
|
||||
q.Sort(sortFieldR1).Limit(1).One(¬e)
|
||||
|
||||
// 下一篇, 比基时间要大, 但是是第一篇, 所以是升序
|
||||
if note.NoteId != "" {
|
||||
query["_id"] = bson.M{"$nin": []bson.ObjectId{bson.ObjectIdHex(noteId), note.NoteId}}
|
||||
}
|
||||
note2 := info.Note{}
|
||||
query[sorterField] = sortFieldT2
|
||||
// Log(isAsc)
|
||||
@@ -404,7 +421,7 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
|
||||
// Log(sortFieldR2)
|
||||
q = db.Notes.Find(query)
|
||||
q.Sort(sortFieldR2).Limit(1).One(¬e2)
|
||||
|
||||
|
||||
return this.FixNote(note), this.FixNote(note2)
|
||||
}
|
||||
|
||||
@@ -419,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}}
|
||||
}
|
||||
@@ -485,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)
|
||||
@@ -498,7 +518,7 @@ func (this *BlogService) fixUserBlog(userBlog *info.UserBlog) {
|
||||
// Logo路径问题, 有些有http: 有些没有
|
||||
if userBlog.Logo != "" && !strings.HasPrefix(userBlog.Logo, "http") {
|
||||
userBlog.Logo = strings.Trim(userBlog.Logo, "/")
|
||||
userBlog.Logo = configService.GetSiteUrl() + "/" + userBlog.Logo
|
||||
userBlog.Logo = "/" + userBlog.Logo
|
||||
}
|
||||
|
||||
if userBlog.SortField == "" {
|
||||
@@ -899,7 +919,7 @@ func (this *BlogService) UpateCateUrlTitle(userId string, cateId, urlTitle strin
|
||||
"UrlTitle": "",
|
||||
})
|
||||
*/
|
||||
url = GetUrTitle(userId, urlTitle, "notebook")
|
||||
url = GetUrTitle(userId, urlTitle, "notebook", cateId)
|
||||
ok = db.UpdateByIdAndUserIdMap(db.Notebooks, cateId, userId, bson.M{
|
||||
"UrlTitle": url,
|
||||
})
|
||||
@@ -915,7 +935,7 @@ func (this *BlogService) UpateBlogUrlTitle(userId string, noteId, urlTitle strin
|
||||
ok = db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, bson.M{
|
||||
"UrlTitle": "",
|
||||
})
|
||||
url = GetUrTitle(userId, urlTitle, "note")
|
||||
url = GetUrTitle(userId, urlTitle, "note", noteId)
|
||||
ok = db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, bson.M{
|
||||
"UrlTitle": url,
|
||||
})
|
||||
@@ -1016,7 +1036,7 @@ func (this *BlogService) UpdateSingleUrlTitle(userId, singleId, urlTitle string)
|
||||
"UrlTitle": "",
|
||||
})
|
||||
*/
|
||||
url = GetUrTitle(userId, urlTitle, "single")
|
||||
url = GetUrTitle(userId, urlTitle, "single", singleId)
|
||||
ok = db.UpdateByIdAndUserIdMap(db.BlogSingles, singleId, userId, bson.M{
|
||||
"UrlTitle": url,
|
||||
})
|
||||
@@ -1050,7 +1070,7 @@ func (this *BlogService) AddOrUpdateSingle(userId, singleId, title, content stri
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
Title: title,
|
||||
Content: content,
|
||||
UrlTitle: GetUrTitle(userId, title, "single"),
|
||||
UrlTitle: GetUrTitle(userId, title, "single", singleId),
|
||||
CreatedTime: time.Now(),
|
||||
}
|
||||
page.UpdatedTime = page.CreatedTime
|
||||
@@ -1088,20 +1108,26 @@ func (this *BlogService) SortSingles(userId string, singleIds []string) (ok bool
|
||||
|
||||
// 得到用户的博客url
|
||||
func (this *BlogService) GetUserBlogUrl(userBlog *info.UserBlog, username string) string {
|
||||
if userBlog.Domain != "" && configService.AllowCustomDomain() {
|
||||
return configService.GetUserUrl(userBlog.Domain)
|
||||
} else if userBlog.SubDomain != "" {
|
||||
return configService.GetUserSubUrl(userBlog.SubDomain)
|
||||
}
|
||||
if username == "" {
|
||||
username = userBlog.UserId.Hex()
|
||||
/*
|
||||
if userBlog != nil {
|
||||
if userBlog.Domain != "" && configService.AllowCustomDomain() {
|
||||
return configService.GetUserUrl(userBlog.Domain)
|
||||
} else if userBlog.SubDomain != "" {
|
||||
return configService.GetUserSubUrl(userBlog.SubDomain)
|
||||
}
|
||||
if username == "" {
|
||||
username = userBlog.UserId.Hex()
|
||||
}
|
||||
}
|
||||
*/
|
||||
return configService.GetBlogUrl() + "/" + username
|
||||
}
|
||||
|
||||
// 得到所有url
|
||||
func (this *BlogService) GetBlogUrls(userBlog *info.UserBlog, userInfo *info.User) info.BlogUrls {
|
||||
var indexUrl, postUrl, searchUrl, cateUrl, singleUrl, tagsUrl, archiveUrl, tagPostsUrl string
|
||||
|
||||
/*
|
||||
if userBlog.Domain != "" && configService.AllowCustomDomain() { // http://demo.com
|
||||
// ok
|
||||
indexUrl = configService.GetUserUrl(userBlog.Domain)
|
||||
@@ -1122,6 +1148,7 @@ func (this *BlogService) GetBlogUrls(userBlog *info.UserBlog, userInfo *info.Use
|
||||
tagsUrl = indexUrl + "/tags"
|
||||
tagPostsUrl = indexUrl + "/tag"
|
||||
} else {
|
||||
*/
|
||||
// ok
|
||||
blogUrl := configService.GetBlogUrl() // blog.leanote.com
|
||||
userIdOrEmail := ""
|
||||
@@ -1140,7 +1167,7 @@ func (this *BlogService) GetBlogUrls(userBlog *info.UserBlog, userInfo *info.Use
|
||||
archiveUrl = blogUrl + "/archives/" + userIdOrEmail // blog.leanote.com/archive/username
|
||||
tagsUrl = blogUrl + "/tags/" + userIdOrEmail
|
||||
tagPostsUrl = blogUrl + "/tag/" + userIdOrEmail // blog.leanote.com/archive/username
|
||||
}
|
||||
// }
|
||||
|
||||
return info.BlogUrls{
|
||||
IndexUrl: indexUrl,
|
||||
@@ -1184,6 +1211,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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"github.com/revel/revel"
|
||||
"time"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"os"
|
||||
"os/exec"
|
||||
"fmt"
|
||||
"strings"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 配置服务
|
||||
// 只是全局的, 用户的配置没有
|
||||
type ConfigService struct {
|
||||
adminUserId string
|
||||
siteUrl string
|
||||
adminUserId string
|
||||
siteUrl string
|
||||
adminUsername string
|
||||
// 全局的
|
||||
GlobalAllConfigs map[string]interface{}
|
||||
GlobalAllConfigs map[string]interface{}
|
||||
GlobalStringConfigs map[string]string
|
||||
GlobalArrayConfigs map[string][]string
|
||||
GlobalMapConfigs map[string]map[string]string
|
||||
GlobalArrayConfigs map[string][]string
|
||||
GlobalMapConfigs map[string]map[string]string
|
||||
GlobalArrMapConfigs map[string][]map[string]string
|
||||
}
|
||||
|
||||
// appStart时 将全局的配置从数据库中得到作为全局
|
||||
func (this *ConfigService) InitGlobalConfigs() bool {
|
||||
this.GlobalAllConfigs = map[string]interface{}{}
|
||||
@@ -34,22 +35,23 @@ func (this *ConfigService) InitGlobalConfigs() bool {
|
||||
this.GlobalArrayConfigs = map[string][]string{}
|
||||
this.GlobalMapConfigs = map[string]map[string]string{}
|
||||
this.GlobalArrMapConfigs = map[string][]map[string]string{}
|
||||
|
||||
|
||||
this.adminUsername, _ = revel.Config.String("adminUsername")
|
||||
if this.adminUsername == "" {
|
||||
this.adminUsername = "admin"
|
||||
}
|
||||
this.siteUrl, _ = revel.Config.String("site.url")
|
||||
|
||||
|
||||
userInfo := userService.GetUserInfoByAny(this.adminUsername)
|
||||
if userInfo.UserId == "" {
|
||||
return false
|
||||
}
|
||||
this.adminUserId = userInfo.UserId.Hex()
|
||||
|
||||
|
||||
configs := []info.Config{}
|
||||
db.ListByQ(db.Configs, bson.M{"UserId": userInfo.UserId}, &configs)
|
||||
|
||||
// db.ListByQ(db.Configs, bson.M{"UserId": userInfo.UserId}, &configs)
|
||||
db.ListByQ(db.Configs, bson.M{}, &configs)
|
||||
|
||||
for _, config := range configs {
|
||||
if config.IsArr {
|
||||
this.GlobalArrayConfigs[config.Key] = config.ValueArr
|
||||
@@ -65,12 +67,22 @@ func (this *ConfigService) InitGlobalConfigs() bool {
|
||||
this.GlobalAllConfigs[config.Key] = config.ValueStr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// site URL
|
||||
if s, ok := this.GlobalStringConfigs["siteUrl"]; !ok || s != "" {
|
||||
this.GlobalStringConfigs["siteUrl"] = this.siteUrl
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *ConfigService) GetSiteUrl() string {
|
||||
return this.siteUrl;
|
||||
s := this.GetGlobalStringConfig("siteUrl")
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
|
||||
return this.siteUrl
|
||||
}
|
||||
func (this *ConfigService) GetAdminUsername() string {
|
||||
return this.adminUsername
|
||||
@@ -84,15 +96,15 @@ func (this *ConfigService) updateGlobalConfig(userId, key string, value interfac
|
||||
// 判断是否存在
|
||||
if _, ok := this.GlobalAllConfigs[key]; !ok {
|
||||
// 需要添加
|
||||
config := info.Config{ConfigId: bson.NewObjectId(),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
Key: key,
|
||||
IsArr: isArr,
|
||||
IsMap: isMap,
|
||||
IsArrMap: isArrMap,
|
||||
config := info.Config{ConfigId: bson.NewObjectId(),
|
||||
UserId: bson.ObjectIdHex(userId), // 没用
|
||||
Key: key,
|
||||
IsArr: isArr,
|
||||
IsMap: isMap,
|
||||
IsArrMap: isArrMap,
|
||||
UpdatedTime: time.Now(),
|
||||
}
|
||||
if(isArr) {
|
||||
if isArr {
|
||||
v, _ := value.([]string)
|
||||
config.ValueArr = v
|
||||
this.GlobalArrayConfigs[key] = v
|
||||
@@ -113,7 +125,7 @@ func (this *ConfigService) updateGlobalConfig(userId, key string, value interfac
|
||||
} else {
|
||||
i := bson.M{"UpdatedTime": time.Now()}
|
||||
this.GlobalAllConfigs[key] = value
|
||||
if(isArr) {
|
||||
if isArr {
|
||||
v, _ := value.([]string)
|
||||
i["ValueArr"] = v
|
||||
this.GlobalArrayConfigs[key] = v
|
||||
@@ -130,7 +142,8 @@ func (this *ConfigService) updateGlobalConfig(userId, key string, value interfac
|
||||
i["ValueStr"] = v
|
||||
this.GlobalStringConfigs[key] = v
|
||||
}
|
||||
return db.UpdateByQMap(db.Configs, bson.M{"UserId": bson.ObjectIdHex(userId), "Key": key}, i)
|
||||
// return db.UpdateByQMap(db.Configs, bson.M{"UserId": bson.ObjectIdHex(userId), "Key": key}, i)
|
||||
return db.UpdateByQMap(db.Configs, bson.M{"Key": key}, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,25 +190,26 @@ func (this *ConfigService) GetGlobalArrMapConfig(key string) []map[string]string
|
||||
func (this *ConfigService) IsOpenRegister() bool {
|
||||
return this.GetGlobalStringConfig("openRegister") != ""
|
||||
}
|
||||
|
||||
//-------
|
||||
// 修改共享笔记的配置
|
||||
func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
registerSharedNotebookPerms, registerSharedNotePerms []int,
|
||||
func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
registerSharedNotebookPerms, registerSharedNotePerms []int,
|
||||
registerSharedNotebookIds, registerSharedNoteIds, registerCopyNoteIds []string) (ok bool, msg string) {
|
||||
|
||||
defer func() {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprint(err)
|
||||
}
|
||||
}();
|
||||
|
||||
}()
|
||||
|
||||
// 用户是否存在?
|
||||
if registerSharedUserId == "" {
|
||||
ok = true
|
||||
msg = "share userId is blank, So it share nothing to register"
|
||||
this.UpdateGlobalStringConfig(this.adminUserId, "registerSharedUserId", "")
|
||||
return
|
||||
return
|
||||
} else {
|
||||
user := userService.GetUserInfo(registerSharedUserId)
|
||||
if user.UserId == "" {
|
||||
@@ -206,7 +220,7 @@ func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
this.UpdateGlobalStringConfig(this.adminUserId, "registerSharedUserId", registerSharedUserId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
notebooks := []map[string]string{}
|
||||
// 共享笔记本
|
||||
if len(registerSharedNotebookIds) > 0 {
|
||||
@@ -222,7 +236,7 @@ func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
msg = "The user has no such notebook: " + notebookId
|
||||
return
|
||||
} else {
|
||||
perm := "0";
|
||||
perm := "0"
|
||||
if registerSharedNotebookPerms[i] == 1 {
|
||||
perm = "1"
|
||||
}
|
||||
@@ -231,7 +245,7 @@ func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
}
|
||||
}
|
||||
this.UpdateGlobalArrMapConfig(this.adminUserId, "registerSharedNotebooks", notebooks)
|
||||
|
||||
|
||||
notes := []map[string]string{}
|
||||
// 共享笔记
|
||||
if len(registerSharedNoteIds) > 0 {
|
||||
@@ -247,7 +261,7 @@ func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
msg = "The user has no such note: " + noteId
|
||||
return
|
||||
} else {
|
||||
perm := "0";
|
||||
perm := "0"
|
||||
if registerSharedNotePerms[i] == 1 {
|
||||
perm = "1"
|
||||
}
|
||||
@@ -256,7 +270,7 @@ func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
}
|
||||
}
|
||||
this.UpdateGlobalArrMapConfig(this.adminUserId, "registerSharedNotes", notes)
|
||||
|
||||
|
||||
// 复制
|
||||
noteIds := []string{}
|
||||
if len(registerCopyNoteIds) > 0 {
|
||||
@@ -277,7 +291,7 @@ func (this *ConfigService) UpdateShareNoteConfig(registerSharedUserId string,
|
||||
}
|
||||
}
|
||||
this.UpdateGlobalArrayConfig(this.adminUserId, "registerCopyNoteIds", noteIds)
|
||||
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
@@ -298,14 +312,14 @@ func (this *ConfigService) getBackupDirname() string {
|
||||
}
|
||||
func (this *ConfigService) Backup(remark string) (ok bool, msg string) {
|
||||
binPath := configService.GetGlobalStringConfig("mongodumpPath")
|
||||
config := revel.Config;
|
||||
config := revel.Config
|
||||
dbname, _ := config.String("db.dbname")
|
||||
host, _ := revel.Config.String("db.host")
|
||||
port, _ := revel.Config.String("db.port")
|
||||
username, _ := revel.Config.String("db.username")
|
||||
password, _ := revel.Config.String("db.password")
|
||||
// mongodump -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
|
||||
binPath = binPath + " -h " + host + " -d " + dbname + " -port " + port
|
||||
binPath = binPath + " -h " + host + " -d " + dbname + " --port " + port
|
||||
if username != "" {
|
||||
binPath += " -u " + username + " -p " + password
|
||||
}
|
||||
@@ -318,20 +332,21 @@ func (this *ConfigService) Backup(remark string) (ok bool, msg string) {
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
cmd := exec.Command("/bin/sh", "-c", binPath)
|
||||
Log(binPath);
|
||||
Log(binPath)
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
ok = false
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
return
|
||||
}
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
return
|
||||
}
|
||||
ok = configService.AddBackup(dir, remark)
|
||||
return ok, msg
|
||||
return ok, msg
|
||||
}
|
||||
|
||||
// 还原
|
||||
func (this *ConfigService) Restore(createdTime string) (ok bool, msg string) {
|
||||
backups := this.GetGlobalArrMapConfig("backups") // [{}, {}]
|
||||
@@ -339,52 +354,52 @@ func (this *ConfigService) Restore(createdTime string) (ok bool, msg string) {
|
||||
var backup map[string]string
|
||||
for i, backup = range backups {
|
||||
if backup["createdTime"] == createdTime {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(backups) {
|
||||
return false, "Backup Not Found"
|
||||
}
|
||||
|
||||
|
||||
// 先备份当前
|
||||
ok, msg = this.Backup("Auto backup when restore from " + backup["createdTime"] )
|
||||
ok, msg = this.Backup("Auto backup when restore from " + backup["createdTime"])
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// mongorestore -h localhost -d leanote --directoryperdb /home/user1/gopackage/src/github.com/leanote/leanote/mongodb_backup/leanote_install_data/
|
||||
binPath := configService.GetGlobalStringConfig("mongorestorePath")
|
||||
config := revel.Config;
|
||||
config := revel.Config
|
||||
dbname, _ := config.String("db.dbname")
|
||||
host, _ := revel.Config.String("db.host")
|
||||
port, _ := revel.Config.String("db.port")
|
||||
username, _ := revel.Config.String("db.username")
|
||||
password, _ := revel.Config.String("db.password")
|
||||
// mongorestore -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
|
||||
binPath = binPath + " --drop -h " + host + " -d " + dbname + " -port " + port
|
||||
binPath = binPath + " --drop -h " + host + " -d " + dbname + " --port " + port
|
||||
if username != "" {
|
||||
binPath += " -u " + username + " -p " + password
|
||||
}
|
||||
|
||||
|
||||
path := backup["path"] + "/" + dbname
|
||||
// 判断路径是否存在
|
||||
if !IsDirExists(path) {
|
||||
return false, path + " Is Not Exists"
|
||||
}
|
||||
|
||||
binPath += " --directoryperdb " + path
|
||||
|
||||
|
||||
binPath += " " + path
|
||||
|
||||
cmd := exec.Command("/bin/sh", "-c", binPath)
|
||||
Log(binPath);
|
||||
Log(binPath)
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("%v", err)
|
||||
ok = false
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
return
|
||||
}
|
||||
|
||||
Log("error:......")
|
||||
Log(string(b))
|
||||
return
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
func (this *ConfigService) DeleteBackup(createdTime string) (bool, string) {
|
||||
@@ -393,22 +408,22 @@ func (this *ConfigService) DeleteBackup(createdTime string) (bool, string) {
|
||||
var backup map[string]string
|
||||
for i, backup = range backups {
|
||||
if backup["createdTime"] == createdTime {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(backups) {
|
||||
return false, "Backup Not Found"
|
||||
}
|
||||
|
||||
|
||||
// 删除文件夹之
|
||||
err := os.RemoveAll(backups[i]["path"])
|
||||
if err != nil {
|
||||
return false, fmt.Sprintf("%v", err)
|
||||
}
|
||||
|
||||
|
||||
// 删除之
|
||||
backups = append(backups[0:i], backups[i+1:]...)
|
||||
|
||||
|
||||
ok := this.UpdateGlobalArrMapConfig(this.adminUserId, "backups", backups)
|
||||
return ok, ""
|
||||
}
|
||||
@@ -419,14 +434,14 @@ func (this *ConfigService) UpdateBackupRemark(createdTime, remark string) (bool,
|
||||
var backup map[string]string
|
||||
for i, backup = range backups {
|
||||
if backup["createdTime"] == createdTime {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(backups) {
|
||||
return false, "Backup Not Found"
|
||||
}
|
||||
backup["remark"] = remark;
|
||||
|
||||
backup["remark"] = remark
|
||||
|
||||
ok := this.UpdateGlobalArrMapConfig(this.adminUserId, "backups", backups)
|
||||
return ok, ""
|
||||
}
|
||||
@@ -438,7 +453,7 @@ func (this *ConfigService) GetBackup(createdTime string) (map[string]string, boo
|
||||
var backup map[string]string
|
||||
for i, backup = range backups {
|
||||
if backup["createdTime"] == createdTime {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(backups) {
|
||||
@@ -455,13 +470,16 @@ var port string
|
||||
|
||||
func init() {
|
||||
revel.OnAppStart(func() {
|
||||
port = strconv.Itoa(revel.HttpPort)
|
||||
if port != "80" {
|
||||
port = ":" + port
|
||||
} else {
|
||||
port = "";
|
||||
}
|
||||
|
||||
/*
|
||||
不用配置的, 因为最终通过命令可以改, 而且有的使用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://") {
|
||||
defaultDomain = siteUrl[len("http://"):]
|
||||
@@ -469,53 +487,51 @@ 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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (this *ConfigService) GetSchema() string {
|
||||
return schema;
|
||||
return schema
|
||||
}
|
||||
|
||||
// 默认
|
||||
func (this *ConfigService) GetDefaultDomain() string {
|
||||
return defaultDomain
|
||||
}
|
||||
// 包含http://
|
||||
func (this *ConfigService) GetDefaultUrl() string {
|
||||
return schema + defaultDomain
|
||||
}
|
||||
|
||||
// note
|
||||
func (this *ConfigService) GetNoteDomain() string {
|
||||
subDomain := this.GetGlobalStringConfig("noteSubDomain");
|
||||
if subDomain != "" {
|
||||
return subDomain + port
|
||||
}
|
||||
return this.GetDefaultDomain() + "/note"
|
||||
return "/note"
|
||||
}
|
||||
func (this *ConfigService) GetNoteUrl() string {
|
||||
return schema + this.GetNoteDomain();
|
||||
return this.GetNoteDomain()
|
||||
}
|
||||
|
||||
// blog
|
||||
func (this *ConfigService) GetBlogDomain() string {
|
||||
subDomain := this.GetGlobalStringConfig("blogSubDomain");
|
||||
if subDomain != "" {
|
||||
return subDomain + port
|
||||
}
|
||||
return this.GetDefaultDomain() + "/blog"
|
||||
return "/blog"
|
||||
}
|
||||
func (this *ConfigService) GetBlogUrl() string {
|
||||
return schema + this.GetBlogDomain();
|
||||
return this.GetBlogDomain()
|
||||
}
|
||||
|
||||
// lea
|
||||
func (this *ConfigService) GetLeaDomain() string {
|
||||
subDomain := this.GetGlobalStringConfig("leaSubDomain");
|
||||
if subDomain != "" {
|
||||
return subDomain + port
|
||||
}
|
||||
return this.GetDefaultDomain() + "/lea"
|
||||
return "/lea"
|
||||
}
|
||||
func (this *ConfigService) GetLeaUrl() string {
|
||||
return schema + this.GetLeaDomain();
|
||||
return schema + this.GetLeaDomain()
|
||||
}
|
||||
|
||||
func (this *ConfigService) GetUserUrl(domain string) string {
|
||||
@@ -529,6 +545,7 @@ func (this *ConfigService) GetUserSubUrl(subDomain string) string {
|
||||
func (this *ConfigService) AllowCustomDomain() bool {
|
||||
return configService.GetGlobalStringConfig("allowCustomDomain") != ""
|
||||
}
|
||||
|
||||
// 是否是好的自定义域名
|
||||
func (this *ConfigService) IsGoodCustomDomain(domain string) bool {
|
||||
blacks := this.GetGlobalArrayConfig("blackCustomDomains")
|
||||
@@ -553,20 +570,29 @@ func (this *ConfigService) IsGoodSubDomain(domain string) bool {
|
||||
// 上传大小
|
||||
func (this *ConfigService) GetUploadSize(key string) float64 {
|
||||
f, _ := strconv.ParseFloat(this.GetGlobalStringConfig(key), 64)
|
||||
return f;
|
||||
return f
|
||||
}
|
||||
func (this *ConfigService) GetInt64(key string) int64 {
|
||||
f, _ := strconv.ParseInt(this.GetGlobalStringConfig(key), 10, 64)
|
||||
return f
|
||||
}
|
||||
func (this *ConfigService) GetInt32(key string) int32 {
|
||||
f, _ := strconv.ParseInt(this.GetGlobalStringConfig(key), 10, 32)
|
||||
return int32(f)
|
||||
}
|
||||
func (this *ConfigService) GetUploadSizeLimit() map[string]float64 {
|
||||
return map[string]float64{
|
||||
"uploadImageSize": this.GetUploadSize("uploadImageSize"),
|
||||
"uploadBlogLogoSize":this.GetUploadSize("uploadBlogLogoSize"),
|
||||
"uploadAttachSize":this.GetUploadSize("uploadAttachSize"),
|
||||
"uploadAvatarSize":this.GetUploadSize("uploadAvatarSize"),
|
||||
"uploadImageSize": this.GetUploadSize("uploadImageSize"),
|
||||
"uploadBlogLogoSize": this.GetUploadSize("uploadBlogLogoSize"),
|
||||
"uploadAttachSize": this.GetUploadSize("uploadAttachSize"),
|
||||
"uploadAvatarSize": this.GetUploadSize("uploadAvatarSize"),
|
||||
}
|
||||
}
|
||||
|
||||
// 为用户得到全局的配置
|
||||
// NoteController调用
|
||||
func (this *ConfigService) GetGlobalConfigForUser() map[string]interface{} {
|
||||
uploadSizeConfigs := this.GetUploadSizeLimit();
|
||||
uploadSizeConfigs := this.GetUploadSizeLimit()
|
||||
config := map[string]interface{}{}
|
||||
for k, v := range uploadSizeConfigs {
|
||||
config[k] = v
|
||||
@@ -580,5 +606,5 @@ func (this *ConfigService) HomePageIsAdminsBlog() bool {
|
||||
}
|
||||
|
||||
func (this *ConfigService) GetVersion() string {
|
||||
return "1.0-beta2"
|
||||
return "2.2.2"
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
"strings"
|
||||
"html/template"
|
||||
"net/smtp"
|
||||
"strconv"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"bytes"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 发送邮件
|
||||
@@ -20,7 +22,7 @@ type EmailService struct {
|
||||
tpls map[string]*template.Template
|
||||
}
|
||||
|
||||
func NewEmailService() (*EmailService) {
|
||||
func NewEmailService() *EmailService {
|
||||
return &EmailService{tpls: map[string]*template.Template{}}
|
||||
}
|
||||
|
||||
@@ -29,42 +31,111 @@ var host = ""
|
||||
var emailPort = ""
|
||||
var username = ""
|
||||
var password = ""
|
||||
var ssl = false
|
||||
|
||||
func InitEmailFromDb() {
|
||||
host = configService.GetGlobalStringConfig("emailHost")
|
||||
emailPort = configService.GetGlobalStringConfig("emailPort")
|
||||
username = configService.GetGlobalStringConfig("emailUsername")
|
||||
password = configService.GetGlobalStringConfig("emailPassword")
|
||||
if configService.GetGlobalStringConfig("emailSSL") == "1" {
|
||||
ssl = true
|
||||
}
|
||||
}
|
||||
|
||||
//return a smtp client
|
||||
func dial(addr string) (*smtp.Client, error) {
|
||||
conn, err := tls.Dial("tcp", addr, nil)
|
||||
if err != nil {
|
||||
LogW("Dialing Error:", err)
|
||||
return nil, err
|
||||
}
|
||||
//分解主机端口字符串
|
||||
host, _, _ := net.SplitHostPort(addr)
|
||||
return smtp.NewClient(conn, host)
|
||||
}
|
||||
|
||||
func SendEmailWithSSL (auth smtp.Auth, to []string, msg []byte) (err error) {
|
||||
//create smtp client
|
||||
c, err := dial(host + ":" + emailPort)
|
||||
if err != nil {
|
||||
LogW("Create smpt client error:", err)
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if auth != nil {
|
||||
if ok, _ := c.Extension("AUTH"); ok {
|
||||
if err = c.Auth(auth); err != nil {
|
||||
LogW("Error during AUTH", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = c.Mail(username); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, addr := range to {
|
||||
if err = c.Rcpt(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Quit()
|
||||
}
|
||||
|
||||
func (this *EmailService) SendEmail(to, subject, body string) (ok bool, e string) {
|
||||
InitEmailFromDb()
|
||||
|
||||
|
||||
if host == "" || emailPort == "" || username == "" || password == "" {
|
||||
return
|
||||
return
|
||||
}
|
||||
hp := strings.Split(host, ":")
|
||||
auth := smtp.PlainAuth("", username, password, hp[0])
|
||||
|
||||
|
||||
var content_type string
|
||||
|
||||
|
||||
mailtype := "html"
|
||||
if mailtype == "html" {
|
||||
content_type = "Content-Type: text/"+ mailtype + "; charset=UTF-8"
|
||||
} else{
|
||||
content_type = "Content-Type: text/" + mailtype + "; charset=UTF-8"
|
||||
} else {
|
||||
content_type = "Content-Type: text/plain" + "; charset=UTF-8"
|
||||
}
|
||||
|
||||
msg := []byte("To: " + to + "\r\nFrom: " + username + "<"+ username +">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body)
|
||||
|
||||
msg := []byte("To: " + to + "\r\nFrom: " + username + "<" + username + ">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body)
|
||||
send_to := strings.Split(to, ";")
|
||||
err := smtp.SendMail(host+":"+emailPort, auth, username, send_to, msg)
|
||||
|
||||
|
||||
var err error
|
||||
if ssl {
|
||||
err = SendEmailWithSSL(auth, send_to, msg)
|
||||
} else {
|
||||
Log("no ssl")
|
||||
err = smtp.SendMail(host + ":" + emailPort, auth, username, send_to, msg)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
e = fmt.Sprint(err)
|
||||
return
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// AddUser调用
|
||||
@@ -74,20 +145,20 @@ func (this *EmailService) RegisterSendActiveEmail(userInfo info.User, email stri
|
||||
if token == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateRegisterSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateRegister");
|
||||
|
||||
if(tpl == "") {
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateRegisterSubject")
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateRegister")
|
||||
|
||||
if tpl == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
tokenUrl := configService.GetSiteUrl() + "/user/activeEmail?token=" + token
|
||||
// {siteUrl} {tokenUrl} {token} {tokenTimeout} {user.id} {user.email} {user.username}
|
||||
token2Value := map[string]interface{}{"siteUrl": configService.GetSiteUrl(), "tokenUrl": tokenUrl, "token": token, "tokenTimeout": strconv.Itoa(int(tokenService.GetOverHours(info.TokenActiveEmail))),
|
||||
"user": map[string]interface{}{
|
||||
"userId": userInfo.UserId.Hex(),
|
||||
"email": userInfo.Email,
|
||||
"userId": userInfo.UserId.Hex(),
|
||||
"email": userInfo.Email,
|
||||
"username": userInfo.Username,
|
||||
},
|
||||
}
|
||||
@@ -97,7 +168,7 @@ func (this *EmailService) RegisterSendActiveEmail(userInfo info.User, email stri
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// 发送邮件
|
||||
ok, _ = this.SendEmail(email, subject, tpl)
|
||||
return ok
|
||||
@@ -113,66 +184,66 @@ func (this *EmailService) UpdateEmailSendActiveEmail(userInfo info.User, email s
|
||||
}
|
||||
|
||||
token := tokenService.NewToken(userInfo.UserId.Hex(), email, info.TokenUpdateEmail)
|
||||
|
||||
|
||||
if token == "" {
|
||||
return
|
||||
}
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateUpdateEmailSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateUpdateEmail");
|
||||
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateUpdateEmailSubject")
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateUpdateEmail")
|
||||
|
||||
// 发送邮件
|
||||
tokenUrl := configService.GetSiteUrl() + "/user/updateEmail?token=" + token
|
||||
// {siteUrl} {tokenUrl} {token} {tokenTimeout} {user.userId} {user.email} {user.username}
|
||||
token2Value := map[string]interface{}{"siteUrl": configService.GetSiteUrl(), "tokenUrl": tokenUrl, "token": token, "tokenTimeout": strconv.Itoa(int(tokenService.GetOverHours(info.TokenActiveEmail))),
|
||||
"newEmail": email,
|
||||
"user": map[string]interface{}{
|
||||
"userId": userInfo.UserId.Hex(),
|
||||
"email": userInfo.Email,
|
||||
"userId": userInfo.UserId.Hex(),
|
||||
"email": userInfo.Email,
|
||||
"username": userInfo.Username,
|
||||
},
|
||||
}
|
||||
|
||||
ok, msg, subject, tpl = this.renderEmail(subject, tpl, token2Value)
|
||||
if !ok {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 发送邮件
|
||||
ok, msg = this.SendEmail(email, subject, tpl)
|
||||
return
|
||||
}
|
||||
|
||||
func (this *EmailService) FindPwdSendEmail(token, email string) (ok bool, msg string) {
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateFindPasswordSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateFindPassword");
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateFindPasswordSubject")
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateFindPassword")
|
||||
|
||||
// 发送邮件
|
||||
tokenUrl := configService.GetSiteUrl() + "/findPassword/" + token
|
||||
// {siteUrl} {tokenUrl} {token} {tokenTimeout} {user.id} {user.email} {user.username}
|
||||
token2Value := map[string]interface{}{"siteUrl": configService.GetSiteUrl(), "tokenUrl": tokenUrl,
|
||||
token2Value := map[string]interface{}{"siteUrl": configService.GetSiteUrl(), "tokenUrl": tokenUrl,
|
||||
"token": token, "tokenTimeout": strconv.Itoa(int(tokenService.GetOverHours(info.TokenActiveEmail)))}
|
||||
|
||||
|
||||
ok, msg, subject, tpl = this.renderEmail(subject, tpl, token2Value)
|
||||
if !ok {
|
||||
return
|
||||
return
|
||||
}
|
||||
// 发送邮件
|
||||
ok, msg = this.SendEmail(email, subject, tpl)
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// 发送邀请链接
|
||||
func (this *EmailService) SendInviteEmail(userInfo info.User, email, content string) bool {
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateInviteSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateInvite");
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateInviteSubject")
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateInvite")
|
||||
|
||||
token2Value := map[string]interface{}{"siteUrl": configService.GetSiteUrl(),
|
||||
"registerUrl": configService.GetSiteUrl() + "/register?from=" + userInfo.Username,
|
||||
"content": content,
|
||||
"content": content,
|
||||
"user": map[string]interface{}{
|
||||
"username": userInfo.Username,
|
||||
"email": userInfo.Email,
|
||||
"email": userInfo.Email,
|
||||
},
|
||||
}
|
||||
var ok bool
|
||||
@@ -187,32 +258,32 @@ func (this *EmailService) SendInviteEmail(userInfo info.User, email, content str
|
||||
|
||||
// 发送评论
|
||||
func (this *EmailService) SendCommentEmail(note info.Note, comment info.BlogComment, userId, content string) bool {
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateCommentSubject");
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateComment");
|
||||
|
||||
subject := configService.GetGlobalStringConfig("emailTemplateCommentSubject")
|
||||
tpl := configService.GetGlobalStringConfig("emailTemplateComment")
|
||||
|
||||
// title := "评论提醒"
|
||||
|
||||
|
||||
/*
|
||||
toUserId := note.UserId.Hex()
|
||||
// title := "评论提醒"
|
||||
|
||||
// 表示回复回复的内容, 那么发送给之前回复的
|
||||
if comment.CommentId != "" {
|
||||
toUserId = comment.UserId.Hex()
|
||||
}
|
||||
toUserInfo := userService.GetUserInfo(toUserId)
|
||||
sendUserInfo := userService.GetUserInfo(userId)
|
||||
|
||||
subject := note.Title + " 收到 " + sendUserInfo.Username + " 的评论";
|
||||
if comment.CommentId != "" {
|
||||
subject = "您在 " + note.Title + " 发表的评论收到 " + sendUserInfo.Username;
|
||||
if userId == note.UserId.Hex() {
|
||||
subject += "(作者)";
|
||||
toUserId := note.UserId.Hex()
|
||||
// title := "评论提醒"
|
||||
|
||||
// 表示回复回复的内容, 那么发送给之前回复的
|
||||
if comment.CommentId != "" {
|
||||
toUserId = comment.UserId.Hex()
|
||||
}
|
||||
toUserInfo := userService.GetUserInfo(toUserId)
|
||||
sendUserInfo := userService.GetUserInfo(userId)
|
||||
|
||||
subject := note.Title + " 收到 " + sendUserInfo.Username + " 的评论";
|
||||
if comment.CommentId != "" {
|
||||
subject = "您在 " + note.Title + " 发表的评论收到 " + sendUserInfo.Username;
|
||||
if userId == note.UserId.Hex() {
|
||||
subject += "(作者)";
|
||||
}
|
||||
subject += " 的评论";
|
||||
}
|
||||
subject += " 的评论";
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
toUserId := note.UserId.Hex()
|
||||
// 表示回复回复的内容, 那么发送给之前回复的
|
||||
if comment.CommentId != "" {
|
||||
@@ -220,28 +291,28 @@ func (this *EmailService) SendCommentEmail(note info.Note, comment info.BlogComm
|
||||
}
|
||||
toUserInfo := userService.GetUserInfo(toUserId) // 被评论者
|
||||
sendUserInfo := userService.GetUserInfo(userId) // 评论者
|
||||
|
||||
// {siteUrl} {blogUrl}
|
||||
|
||||
// {siteUrl} {blogUrl}
|
||||
// {blog.id} {blog.title} {blog.url}
|
||||
// {commentUser.userId} {commentUser.username} {commentUser.email}
|
||||
// {commentUser.userId} {commentUser.username} {commentUser.email}
|
||||
// {commentedUser.userId} {commentedUser.username} {commentedUser.email}
|
||||
token2Value := map[string]interface{}{"siteUrl": configService.GetSiteUrl(), "blogUrl": configService.GetBlogUrl(),
|
||||
"blog": map[string]string{
|
||||
"id": note.NoteId.Hex(),
|
||||
"id": note.NoteId.Hex(),
|
||||
"title": note.Title,
|
||||
"url": configService.GetBlogUrl() + "/view/" + note.NoteId.Hex(),
|
||||
"url": configService.GetBlogUrl() + "/view/" + note.NoteId.Hex(),
|
||||
},
|
||||
"commentContent": content,
|
||||
// 评论者信息
|
||||
"commentUser": map[string]interface{}{"userId": sendUserInfo.UserId.Hex(),
|
||||
"username": sendUserInfo.Username,
|
||||
"email": sendUserInfo.Email,
|
||||
"commentUser": map[string]interface{}{"userId": sendUserInfo.UserId.Hex(),
|
||||
"username": sendUserInfo.Username,
|
||||
"email": sendUserInfo.Email,
|
||||
"isBlogAuthor": userId == note.UserId.Hex(),
|
||||
},
|
||||
// 被评论者信息
|
||||
"commentedUser": map[string]interface{}{"userId": toUserId,
|
||||
"username": toUserInfo.Username,
|
||||
"email": toUserInfo.Email,
|
||||
"username": toUserInfo.Username,
|
||||
"email": toUserInfo.Email,
|
||||
"isBlogAuthor": toUserId == note.UserId.Hex(),
|
||||
},
|
||||
}
|
||||
@@ -251,53 +322,52 @@ func (this *EmailService) SendCommentEmail(note info.Note, comment info.BlogComm
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// 发送邮件
|
||||
ok, _ = this.SendEmail(toUserInfo.Email, subject, tpl)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
||||
// 验证模板是否正确
|
||||
func (this *EmailService) ValidTpl(str string) (ok bool, msg string){
|
||||
defer func() {
|
||||
func (this *EmailService) ValidTpl(str string) (ok bool, msg string) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprint(err)
|
||||
}
|
||||
}();
|
||||
header := configService.GetGlobalStringConfig("emailTemplateHeader");
|
||||
footer := configService.GetGlobalStringConfig("emailTemplateFooter");
|
||||
}()
|
||||
header := configService.GetGlobalStringConfig("emailTemplateHeader")
|
||||
footer := configService.GetGlobalStringConfig("emailTemplateFooter")
|
||||
str = strings.Replace(str, "{{header}}", header, -1)
|
||||
str = strings.Replace(str, "{{footer}}", footer, -1)
|
||||
_, err := template.New("tpl name").Parse(str)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
msg = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// ok, msg, subject, tpl
|
||||
func (this *EmailService) getTpl(str string) (ok bool, msg string, tpl *template.Template){
|
||||
defer func() {
|
||||
func (this *EmailService) getTpl(str string) (ok bool, msg string, tpl *template.Template) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprint(err)
|
||||
}
|
||||
}();
|
||||
|
||||
}()
|
||||
|
||||
var err error
|
||||
var has bool
|
||||
|
||||
|
||||
if tpl, has = this.tpls[str]; !has {
|
||||
tpl, err = template.New("tpl name").Parse(str)
|
||||
if err != nil {
|
||||
tpl, err = template.New("tpl name").Parse(str)
|
||||
if err != nil {
|
||||
msg = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
this.tpls[str] = tpl
|
||||
return
|
||||
}
|
||||
this.tpls[str] = tpl
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
@@ -310,67 +380,67 @@ func (this *EmailService) renderEmail(subject, body string, values map[string]in
|
||||
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
|
||||
if err := recover(); err != nil {
|
||||
ok = false
|
||||
msg = fmt.Sprint(err) // 这里的err其实就是panic传入的内容,
|
||||
msg = fmt.Sprint(err) // 这里的err其实就是panic传入的内容,
|
||||
}
|
||||
}();
|
||||
|
||||
}()
|
||||
|
||||
var tpl *template.Template
|
||||
|
||||
values["siteUrl"] = configService.GetSiteUrl();
|
||||
|
||||
|
||||
values["siteUrl"] = configService.GetSiteUrl()
|
||||
|
||||
// subject
|
||||
if subject != "" {
|
||||
ok, msg, tpl = this.getTpl(subject)
|
||||
if(!ok) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
err := tpl.Execute(&buffer, values)
|
||||
if err != nil {
|
||||
err := tpl.Execute(&buffer, values)
|
||||
if err != nil {
|
||||
msg = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
o = buffer.String()
|
||||
} else {
|
||||
o = ""
|
||||
}
|
||||
|
||||
// content
|
||||
header := configService.GetGlobalStringConfig("emailTemplateHeader");
|
||||
footer := configService.GetGlobalStringConfig("emailTemplateFooter");
|
||||
}
|
||||
o = buffer.String()
|
||||
} else {
|
||||
o = ""
|
||||
}
|
||||
|
||||
// content
|
||||
header := configService.GetGlobalStringConfig("emailTemplateHeader")
|
||||
footer := configService.GetGlobalStringConfig("emailTemplateFooter")
|
||||
body = strings.Replace(body, "{{header}}", header, -1)
|
||||
body = strings.Replace(body, "{{footer}}", footer, -1)
|
||||
values["subject"] = o
|
||||
ok, msg, tpl = this.getTpl(body)
|
||||
if(!ok) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var buffer2 bytes.Buffer
|
||||
err := tpl.Execute(&buffer2, values)
|
||||
if err != nil {
|
||||
err := tpl.Execute(&buffer2, values)
|
||||
if err != nil {
|
||||
msg = fmt.Sprint(err)
|
||||
return
|
||||
}
|
||||
b = buffer2.String()
|
||||
|
||||
return
|
||||
}
|
||||
b = buffer2.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 发送email给用户
|
||||
// 需要记录
|
||||
func (this *EmailService) SendEmailToUsers(users []info.User, subject, body string) (ok bool, msg string) {
|
||||
if(users == nil || len(users) == 0) {
|
||||
if users == nil || len(users) == 0 {
|
||||
msg = "no users"
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 尝试renderHtml
|
||||
ok, msg, _, _ = this.renderEmail(subject, body, map[string]interface{}{})
|
||||
if(!ok) {
|
||||
if !ok {
|
||||
Log(msg)
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
go func() {
|
||||
for _, user := range users {
|
||||
LogJ(user)
|
||||
@@ -381,8 +451,8 @@ func (this *EmailService) SendEmailToUsers(users []info.User, subject, body stri
|
||||
ok2, msg2, subject2, body2 := this.renderEmail(subject, body, m)
|
||||
ok = ok2
|
||||
msg = msg2
|
||||
if(ok2) {
|
||||
sendOk, msg := this.SendEmail(user.Email, subject2, body2);
|
||||
if ok2 {
|
||||
sendOk, msg := this.SendEmail(user.Email, subject2, body2)
|
||||
this.AddEmailLog(user.Email, subject, body, sendOk, msg) // 把模板记录下
|
||||
// 记录到Email Log
|
||||
if sendOk {
|
||||
@@ -395,47 +465,47 @@ func (this *EmailService) SendEmailToUsers(users []info.User, subject, body stri
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *EmailService) SendEmailToEmails(emails []string, subject, body string) (ok bool, msg string) {
|
||||
if(emails == nil || len(emails) == 0) {
|
||||
if emails == nil || len(emails) == 0 {
|
||||
msg = "no emails"
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 尝试renderHtml
|
||||
ok, msg, _, _ = this.renderEmail(subject, body, map[string]interface{}{})
|
||||
if(!ok) {
|
||||
if !ok {
|
||||
Log(msg)
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// go func() {
|
||||
for _, email := range emails {
|
||||
if email == "" {
|
||||
continue
|
||||
}
|
||||
m := map[string]interface{}{}
|
||||
m["email"] = email
|
||||
ok, msg, subject, body = this.renderEmail(subject, body, m)
|
||||
if(ok) {
|
||||
sendOk, msg := this.SendEmail(email, subject, body);
|
||||
this.AddEmailLog(email, subject, body, sendOk, msg)
|
||||
// 记录到Email Log
|
||||
if sendOk {
|
||||
Log("ok " + email)
|
||||
} else {
|
||||
Log("no " + email)
|
||||
}
|
||||
} else {
|
||||
Log(msg);
|
||||
}
|
||||
|
||||
// go func() {
|
||||
for _, email := range emails {
|
||||
if email == "" {
|
||||
continue
|
||||
}
|
||||
// }()
|
||||
|
||||
return
|
||||
m := map[string]interface{}{}
|
||||
m["email"] = email
|
||||
ok, msg, subject, body = this.renderEmail(subject, body, m)
|
||||
if ok {
|
||||
sendOk, msg := this.SendEmail(email, subject, body)
|
||||
this.AddEmailLog(email, subject, body, sendOk, msg)
|
||||
// 记录到Email Log
|
||||
if sendOk {
|
||||
Log("ok " + email)
|
||||
} else {
|
||||
Log("no " + email)
|
||||
}
|
||||
} else {
|
||||
Log(msg)
|
||||
}
|
||||
}
|
||||
// }()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 添加邮件日志
|
||||
@@ -443,6 +513,7 @@ func (this *EmailService) AddEmailLog(email, subject, body string, ok bool, msg
|
||||
log := info.EmailLog{LogId: bson.NewObjectId(), Email: email, Subject: subject, Body: body, Ok: ok, Msg: msg, CreatedTime: time.Now()}
|
||||
db.Insert(db.EmailLogs, log)
|
||||
}
|
||||
|
||||
// 展示邮件日志
|
||||
|
||||
func (this *EmailService) DeleteEmails(ids []string) bool {
|
||||
@@ -451,7 +522,7 @@ func (this *EmailService) DeleteEmails(ids []string) bool {
|
||||
idsO[i] = bson.ObjectIdHex(id)
|
||||
}
|
||||
db.DeleteAll(db.EmailLogs, bson.M{"_id": bson.M{"$in": idsO}})
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
func (this *EmailService) ListEmailLogs(pageNumber, pageSize int, sortField string, isAsc bool, email string) (page info.Page, emailLogs []info.EmailLog) {
|
||||
@@ -461,7 +532,7 @@ func (this *EmailService) ListEmailLogs(pageNumber, pageSize int, sortField stri
|
||||
if email != "" {
|
||||
query["Email"] = bson.M{"$regex": bson.RegEx{".*?" + email + ".*", "i"}}
|
||||
}
|
||||
q := db.EmailLogs.Find(query);
|
||||
q := db.EmailLogs.Find(query)
|
||||
// 总记录数
|
||||
count, _ := q.Count()
|
||||
// 列表
|
||||
@@ -471,4 +542,4 @@ func (this *EmailService) ListEmailLogs(pageNumber, pageSize int, sortField stri
|
||||
All(&emailLogs)
|
||||
page = info.NewPage(pageNumber, pageSize, count, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -17,7 +21,7 @@ type FileService struct {
|
||||
}
|
||||
|
||||
// add Image
|
||||
func (this *FileService) AddImage(image info.File, albumId, userId string) bool {
|
||||
func (this *FileService) AddImage(image info.File, albumId, userId string, needCheckSize bool) (ok bool, msg string) {
|
||||
image.CreatedTime = time.Now()
|
||||
if albumId != "" {
|
||||
image.AlbumId = bson.ObjectIdHex(albumId)
|
||||
@@ -26,8 +30,9 @@ func (this *FileService) AddImage(image info.File, albumId, userId string) bool
|
||||
image.IsDefaultAlbum = true
|
||||
}
|
||||
image.UserId = bson.ObjectIdHex(userId)
|
||||
|
||||
return db.Insert(db.Files, image)
|
||||
|
||||
ok = db.Insert(db.Files, image)
|
||||
return
|
||||
}
|
||||
|
||||
// list images
|
||||
@@ -35,28 +40,28 @@ func (this *FileService) AddImage(image info.File, albumId, userId string) bool
|
||||
func (this *FileService) ListImagesWithPage(userId, albumId, key string, pageNumber, pageSize int) info.Page {
|
||||
skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, "CreatedTime", false)
|
||||
files := []info.File{}
|
||||
|
||||
|
||||
q := bson.M{"UserId": bson.ObjectIdHex(userId), "Type": ""} // life
|
||||
if albumId != "" {
|
||||
q["AlbumId"] = bson.ObjectIdHex(albumId);
|
||||
q["AlbumId"] = bson.ObjectIdHex(albumId)
|
||||
} else {
|
||||
q["IsDefaultAlbum"] = true
|
||||
}
|
||||
if key != "" {
|
||||
q["Title"] = bson.M{"$regex": bson.RegEx{".*?" + key + ".*", "i"}}
|
||||
q["Title"] = bson.M{"$regex": bson.RegEx{".*?" + key + ".*", "i"}}
|
||||
}
|
||||
|
||||
// LogJ(q)
|
||||
|
||||
count := db.Count(db.Files, q);
|
||||
|
||||
|
||||
// LogJ(q)
|
||||
|
||||
count := db.Count(db.Files, q)
|
||||
|
||||
db.Files.
|
||||
Find(q).
|
||||
Sort(sortFieldR).
|
||||
Skip(skipNum).
|
||||
Limit(pageSize).
|
||||
All(&files)
|
||||
|
||||
|
||||
return info.Page{Count: count, List: files}
|
||||
}
|
||||
|
||||
@@ -70,12 +75,12 @@ func (this *FileService) GetAllImageNamesMap(userId string) (m map[string]bool)
|
||||
q := bson.M{"UserId": bson.ObjectIdHex(userId)}
|
||||
files := []info.File{}
|
||||
db.ListByQWithFields(db.Files, q, []string{"Name"}, &files)
|
||||
|
||||
|
||||
m = make(map[string]bool)
|
||||
if len(files) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
for _, file := range files {
|
||||
m[file.Name] = true
|
||||
}
|
||||
@@ -86,8 +91,8 @@ func (this *FileService) GetAllImageNamesMap(userId string) (m map[string]bool)
|
||||
func (this *FileService) DeleteImage(userId, fileId string) (bool, string) {
|
||||
file := info.File{}
|
||||
db.GetByIdAndUserId(db.Files, fileId, userId, &file)
|
||||
|
||||
if(file.FileId != "") {
|
||||
|
||||
if file.FileId != "" {
|
||||
if db.DeleteByIdAndUserId(db.Files, fileId, userId) {
|
||||
// delete image
|
||||
// TODO
|
||||
@@ -114,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的访问权限
|
||||
@@ -121,53 +173,63 @@ func (this *FileService) GetFile(userId, fileId string) string {
|
||||
if fileId == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
file := info.File{}
|
||||
db.Get(db.Files, fileId, &file)
|
||||
path := file.Path
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
// 1. 判断权限
|
||||
|
||||
|
||||
// 是否是我的文件
|
||||
if userId != "" && file.UserId.Hex() == userId {
|
||||
return path
|
||||
}
|
||||
|
||||
|
||||
// 得到使用过该fileId的所有笔记NoteId
|
||||
// 这些笔记是否有public的, 若有则ok
|
||||
// 这些笔记(笔记本)是否有共享给我的, 若有则ok
|
||||
|
||||
|
||||
noteIds := noteImageService.GetNoteIds(fileId)
|
||||
if noteIds != nil && len(noteIds) > 0 {
|
||||
// 这些笔记是否有public的
|
||||
if db.Has(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}, "IsBlog": true}) {
|
||||
return path
|
||||
}
|
||||
|
||||
// 若有共享给我的笔记?
|
||||
if db.Has(db.ShareNotes, bson.M{"ToUserId": bson.ObjectIdHex(userId), "NoteId": bson.M{"$in": noteIds}}) {
|
||||
return path
|
||||
}
|
||||
|
||||
// 笔记本是否共享给我?
|
||||
// 通过笔记得到笔记本
|
||||
notes := []info.Note{}
|
||||
db.ListByQWithFields(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}}, []string{"NotebookId"}, ¬es)
|
||||
if notes != nil && len(notes) > 0 {
|
||||
notebookIds := make([]bson.ObjectId, len(notes))
|
||||
for i := 0; i < len(notes); i++ {
|
||||
notebookIds[i] = notes[i].NotebookId
|
||||
}
|
||||
|
||||
if db.Has(db.ShareNotebooks, bson.M{"ToUserId": bson.ObjectIdHex(userId), "NotebookId": bson.M{"$in": notebookIds}}) {
|
||||
|
||||
// 2014/12/28 修复, 如果是分享给用户组, 那就不行, 这里可以实现
|
||||
for _, noteId := range noteIds {
|
||||
note := noteService.GetNoteById(noteId.Hex())
|
||||
if shareService.HasReadPerm(note.UserId.Hex(), userId, noteId.Hex()) {
|
||||
return path
|
||||
}
|
||||
}
|
||||
/*
|
||||
// 若有共享给我的笔记?
|
||||
// 对该笔记可读?
|
||||
if db.Has(db.ShareNotes, bson.M{"ToUserId": bson.ObjectIdHex(userId), "NoteId": bson.M{"$in": noteIds}}) {
|
||||
return path
|
||||
}
|
||||
|
||||
// 笔记本是否共享给我?
|
||||
// 通过笔记得到笔记本
|
||||
notes := []info.Note{}
|
||||
db.ListByQWithFields(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}}, []string{"NotebookId"}, ¬es)
|
||||
if notes != nil && len(notes) > 0 {
|
||||
notebookIds := make([]bson.ObjectId, len(notes))
|
||||
for i := 0; i < len(notes); i++ {
|
||||
notebookIds[i] = notes[i].NotebookId
|
||||
}
|
||||
|
||||
if db.Has(db.ShareNotebooks, bson.M{"ToUserId": bson.ObjectIdHex(userId), "NotebookId": bson.M{"$in": notebookIds}}) {
|
||||
return path
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// 可能是刚复制到owner上, 但内容又没有保存, 所以没有note->imageId的映射, 此时看是否有fromFileId
|
||||
if file.FromFileId != "" {
|
||||
fromFile := info.File{}
|
||||
@@ -176,54 +238,56 @@ func (this *FileService) GetFile(userId, fileId string) string {
|
||||
return fromFile.Path
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// 复制共享的笔记时, 复制其中的图片到我本地
|
||||
// 复制图片
|
||||
func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, string) {
|
||||
// 是否已经复制过了
|
||||
file2 := info.File{}
|
||||
db.GetByQ(db.Files, bson.M{"UserId": bson.ObjectIdHex(toUserId), "FromFileId": bson.ObjectIdHex(fileId)}, &file2)
|
||||
if file2.FileId != "" {
|
||||
return true, file2.FileId.Hex();
|
||||
return true, file2.FileId.Hex()
|
||||
}
|
||||
|
||||
// 复制之
|
||||
|
||||
file := info.File{}
|
||||
db.GetByIdAndUserId(db.Files, fileId, userId, &file)
|
||||
|
||||
|
||||
if file.FileId == "" || file.UserId.Hex() != userId {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
|
||||
_, ext := SplitFilename(file.Name)
|
||||
newFilename := NewGuid() + ext
|
||||
|
||||
dir := "files/" + toUserId + "/images"
|
||||
guid := NewGuid()
|
||||
newFilename := guid + ext
|
||||
|
||||
// TODO 统一目录格式
|
||||
// dir := "files/" + toUserId + "/images"
|
||||
dir := "files/" + GetRandomFilePath(toUserId, guid) + "/images"
|
||||
filePath := dir + "/" + newFilename
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
err := os.MkdirAll(revel.BasePath+dir, 0755)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
_, err = CopyFile(revel.BasePath + "/" + file.Path, revel.BasePath + "/" + filePath)
|
||||
|
||||
_, err = CopyFile(revel.BasePath+"/"+file.Path, revel.BasePath+"/"+filePath)
|
||||
if err != nil {
|
||||
Log(err)
|
||||
return false, ""
|
||||
}
|
||||
|
||||
|
||||
fileInfo := info.File{Name: newFilename,
|
||||
Title: file.Title,
|
||||
Path: filePath,
|
||||
Size: file.Size,
|
||||
Title: file.Title,
|
||||
Path: filePath,
|
||||
Size: file.Size,
|
||||
FromFileId: file.FileId}
|
||||
id := bson.NewObjectId();
|
||||
id := bson.NewObjectId()
|
||||
fileInfo.FileId = id
|
||||
fileId = id.Hex()
|
||||
Ok := this.AddImage(fileInfo, "", toUserId)
|
||||
|
||||
Ok, _ := this.AddImage(fileInfo, "", toUserId, false)
|
||||
|
||||
if Ok {
|
||||
return Ok, id.Hex()
|
||||
}
|
||||
@@ -232,5 +296,9 @@ func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, strin
|
||||
|
||||
// 是否是我的文件
|
||||
func (this *FileService) IsMyFile(userId, fileId string) bool {
|
||||
// 如果有问题会panic
|
||||
if !bson.IsObjectIdHex(fileId) || !bson.IsObjectIdHex(userId) {
|
||||
return false
|
||||
}
|
||||
return db.Has(db.Files, bson.M{"UserId": bson.ObjectIdHex(userId), "_id": bson.ObjectIdHex(fileId)})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
// "strings"
|
||||
// "strings"
|
||||
)
|
||||
|
||||
// 用户组, 用户组用户管理
|
||||
@@ -16,34 +16,53 @@ type GroupService struct {
|
||||
|
||||
// 添加分组
|
||||
func (this *GroupService) AddGroup(userId, title string) (bool, info.Group) {
|
||||
group := info.Group {
|
||||
GroupId: bson.NewObjectId(),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
Title: title,
|
||||
group := info.Group{
|
||||
GroupId: bson.NewObjectId(),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
Title: title,
|
||||
CreatedTime: time.Now(),
|
||||
}
|
||||
return db.Insert(db.Groups, group), group
|
||||
}
|
||||
|
||||
// 删除分组
|
||||
// 判断是否有好友
|
||||
func (this *GroupService) DeleteGroup(userId, groupId string) (ok bool, msg string) {
|
||||
if db.Has(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId)}) {
|
||||
return false, "hasUsers"
|
||||
/*
|
||||
if db.Has(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId)}) {
|
||||
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)
|
||||
}
|
||||
|
||||
// 得到用户的所有分组(包括下的所有用户)
|
||||
func (this *GroupService) GetGroupsAndUsers(userId string) ([]info.Group) {
|
||||
// 得到分组s
|
||||
groups := []info.Group{}
|
||||
db.ListByQ(db.Groups, bson.M{"UserId": bson.ObjectIdHex(userId)}, &groups)
|
||||
func (this *GroupService) GetGroupsAndUsers(userId string) []info.Group {
|
||||
/*
|
||||
// 得到我的分组
|
||||
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())
|
||||
@@ -51,15 +70,65 @@ func (this *GroupService) GetGroupsAndUsers(userId string) ([]info.Group) {
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// 仅仅得到所有分组
|
||||
func (this *GroupService) GetGroups(userId string) ([]info.Group) {
|
||||
func (this *GroupService) GetGroups(userId string) []info.Group {
|
||||
// 得到分组s
|
||||
groups := []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) {
|
||||
func (this *GroupService) GetGroup(userId, groupId string) info.Group {
|
||||
// 得到分组s
|
||||
group := info.Group{}
|
||||
db.GetByIdAndUserId(db.Groups, groupId, userId, &group)
|
||||
@@ -67,7 +136,7 @@ func (this *GroupService) GetGroup(userId, groupId string) (info.Group) {
|
||||
}
|
||||
|
||||
// 得到某分组下的用户
|
||||
func (this *GroupService) GetUsers(groupId string) ([]info.User) {
|
||||
func (this *GroupService) GetUsers(groupId string) []info.User {
|
||||
// 得到UserIds
|
||||
groupUsers := []info.GroupUser{}
|
||||
db.ListByQWithFields(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId)}, []string{"UserId"}, &groupUsers)
|
||||
@@ -83,7 +152,7 @@ func (this *GroupService) GetUsers(groupId string) ([]info.User) {
|
||||
}
|
||||
|
||||
// 得到我所属的所有分组ids
|
||||
func (this *GroupService) GetBelongToGroupIds(userId string) ([]bson.ObjectId) {
|
||||
func (this *GroupService) GetBelongToGroupIds(userId string) []bson.ObjectId {
|
||||
// 得到UserIds
|
||||
groupUsers := []info.GroupUser{}
|
||||
db.ListByQWithFields(db.GroupUsers, bson.M{"UserId": bson.ObjectIdHex(userId)}, []string{"GroupId"}, &groupUsers)
|
||||
@@ -99,32 +168,58 @@ 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, "forbiddenNotMyGroup"
|
||||
}
|
||||
*/
|
||||
if !this.isMyGroup(ownUserId, groupId) {
|
||||
return false, "forbidden"
|
||||
return false, "forbiddenNotMyGroup"
|
||||
}
|
||||
|
||||
|
||||
// 是否已存在
|
||||
if db.Has(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId), "UserId": bson.ObjectIdHex(userId)}) {
|
||||
return false, "hasUsers"
|
||||
return false, "userExistsInGroup"
|
||||
}
|
||||
|
||||
|
||||
return db.Insert(db.GroupUsers, info.GroupUser{
|
||||
GroupUserId: bson.NewObjectId(),
|
||||
GroupId: bson.ObjectIdHex(groupId),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
GroupId: bson.ObjectIdHex(groupId),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
CreatedTime: time.Now(),
|
||||
}), ""
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
func (this *GroupService) DeleteUser(ownUserId, groupId, userId string) (ok bool, msg string) {
|
||||
// groupId是否是ownUserId的?
|
||||
/*
|
||||
if !this.IsExistsGroupUser(ownUserId, groupId) {
|
||||
return false, "forbiddenNotMyGroup"
|
||||
}
|
||||
*/
|
||||
if !this.isMyGroup(ownUserId, groupId) {
|
||||
return false, "forbidden"
|
||||
return false, "forbiddenNotMyGroup"
|
||||
}
|
||||
|
||||
// 删除该用户分享到本组的笔记本, 笔记
|
||||
shareService.DeleteShareNotebookGroupWhenDeleteGroupUser(userId, groupId)
|
||||
shareService.DeleteShareNoteGroupWhenDeleteGroupUser(userId, groupId)
|
||||
|
||||
return db.Delete(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId), "UserId": bson.ObjectIdHex(userId)}), ""
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "time"
|
||||
// "time"
|
||||
)
|
||||
|
||||
// 历史记录
|
||||
@@ -22,7 +22,7 @@ func (this *NoteContentHistoryService) AddHistory(noteId, userId string, eachHis
|
||||
if eachHistory.Content == "" {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 先查是否存在历史记录, 没有则添加之
|
||||
history := info.NoteContentHistory{}
|
||||
db.GetByIdAndUserId(db.NoteContentHistories, noteId, userId, &history)
|
||||
@@ -33,12 +33,13 @@ func (this *NoteContentHistoryService) AddHistory(noteId, userId string, eachHis
|
||||
// TODO
|
||||
l := len(history.Histories)
|
||||
if l >= maxSize {
|
||||
history.Histories = history.Histories[l-maxSize:]
|
||||
// history.Histories = history.Histories[l-maxSize:] // BUG, 致使都是以前的
|
||||
history.Histories = history.Histories[:maxSize]
|
||||
}
|
||||
newHistory := []info.EachHistory{eachHistory}
|
||||
newHistory = append(newHistory, history.Histories...) // 在开头加了, 最近的在最前
|
||||
history.Histories = newHistory
|
||||
|
||||
history.Histories = newHistory
|
||||
|
||||
// 更新之
|
||||
db.UpdateByIdAndUserId(db.NoteContentHistories, noteId, userId, history)
|
||||
}
|
||||
@@ -47,13 +48,13 @@ func (this *NoteContentHistoryService) AddHistory(noteId, userId string, eachHis
|
||||
|
||||
// 新建历史
|
||||
func (this *NoteContentHistoryService) newHistory(noteId, userId string, eachHistory info.EachHistory) {
|
||||
history := info.NoteContentHistory{NoteId: bson.ObjectIdHex(noteId),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
history := info.NoteContentHistory{NoteId: bson.ObjectIdHex(noteId),
|
||||
UserId: bson.ObjectIdHex(userId),
|
||||
Histories: []info.EachHistory{eachHistory},
|
||||
}
|
||||
|
||||
|
||||
// 保存之
|
||||
db.Insert(db.NoteContentHistories, history)
|
||||
db.Insert(db.NoteContentHistories, history)
|
||||
}
|
||||
|
||||
// 列表展示
|
||||
@@ -61,4 +62,4 @@ func (this *NoteContentHistoryService) ListHistories(noteId, userId string) []in
|
||||
histories := info.NoteContentHistory{}
|
||||
db.GetByIdAndUserId(db.NoteContentHistories, noteId, userId, &histories)
|
||||
return histories.Histories
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user