Compare commits

..
151 Commits
Author SHA1 Message Date
lealife 7e17c68dba update readme 2015-10-27 23:44:53 +08:00
lealife 6259f15ffa update readme 2015-10-27 23:42:19 +08:00
lealife f65d027a0d update readme 2015-10-27 23:25:17 +08:00
lealife e9e2ca50ee update readme 2015-10-27 23:19:56 +08:00
lealife a64ef8517e update readme 2015-10-27 23:17:15 +08:00
lealife c9bda60554 v1.3 release 2015-10-27 23:14:02 +08:00
lealife c3478520c0 batch release 2015-10-27 23:08:00 +08:00
lealife 8346ffe915 batch release
https://github.com/leanote/leanote/issues/238
2015-10-27 23:07:49 +08:00
lealife f49b046601 Merge branch 'feature-batch' 2015-10-27 23:06:51 +08:00
lealife 263c03291e batch ok 2015-10-27 23:04:39 +08:00
lealife 29244c247e lit indent 2015-10-20 09:28:03 +08:00
lealife 320de8637f v1.2 update initial db data 2015-10-18 20:34:44 +08:00
lealife 33bc6bf84b v1.2 update initial data 2015-10-18 20:34:31 +08:00
lealife 0cd2c42b68 v1.2 release 2015-10-18 00:16:54 +08:00
lealife 1716acf9e0 release v1.2 for export pdf 2015-10-17 23:46:09 +08:00
lealife 45477119d4 Merge branch 'feature-pdf' 2015-10-17 23:23:36 +08:00
lealife 716fbeae0c update wkhtmltopdf link 2015-10-17 23:22:40 +08:00
lealife 103891e0c0 wkhtmltopdf configuration 2015-10-17 19:36:49 +08:00
lealife 638b5b51e0 export pdf ok 2015-10-17 17:15:33 +08:00
lealife 90fea722aa 笔记内拖动图片, release 2015-10-17 15:55:14 +08:00
lealife 90ce35327f 笔记内拖动图片, 拖动上传图片
https://github.com/leanote/leanote/issues/231
2015-10-17 15:53:35 +08:00
lealife 60e3b9446a 空白笔记前有一个空格 2015-10-17 15:51:21 +08:00
lealife 16a726223c 富文本编辑器不支持 多层级列表
https://github.com/leanote/leanote/issues/234
2015-10-17 15:42:40 +08:00
lealife dfe45f39bb i18n 2015-10-14 19:03:41 +08:00
lealife 413be4a6d0 markdown editor, image manager, [ok] 2015-10-14 18:53:29 +08:00
lealife 6a35f98f89 image manager optimization, i18n 2015-10-14 18:47:01 +08:00
lealife ad295d97c8 markdown editor i18n 2015-10-14 18:38:08 +08:00
lealife cf8171c9cd member upload avatar 2015-10-12 21:18:01 +08:00
lealife c616a4e9a6 download attach 2015-10-12 21:08:29 +08:00
lealife 04d641a5ad download attach 2015-10-12 21:03:51 +08:00
lealife ec68570a38 attach list 2015-10-12 20:55:08 +08:00
lealife 407b44382c avatar upload 2015-10-12 19:44:43 +08:00
lealife 71a2bcc7f0 v1.1 Proudly powered by <a href="https://leanote.com">Leanote</a> 2015-10-12 12:15:53 +08:00
lealife a2dd09725c v1.1 update theme <div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div> 2015-10-12 12:09:44 +08:00
lealife f757e00f03 Merge branch 'sync-from-leanote.com' 2015-10-10 18:10:41 +08:00
lealife b7652bb321 opt, fix 1.1 2015-10-10 18:10:26 +08:00
lealife e6018d32ec tinymce 复制纯文本bug 2015-10-10 17:41:18 +08:00
lealife cb7e272bbe leanote v1.1 release 2015-10-10 17:23:27 +08:00
lealife a4d9d8e0d3 leanote v1.1 build 2015-10-10 16:55:36 +08:00
lealife c844c8b188 复制外链图片, 复制到leanote 2015-10-10 16:18:26 +08:00
lealife 320263eefa 笔记信息, 历史记录, 提示, 附件优化
https://github.com/leanote/leanote/issues/224
2015-10-10 16:10:54 +08:00
lealife 274875c6c4 note dev 优化 2015-10-10 15:24:26 +08:00
lealife 1ea35324c1 内容的IsBlog没有note IsBlog一致, 导致前端判断有问题 2015-10-10 15:20:34 +08:00
lealife 3e20ad3cdd CPU性能过高优化, 字符串多次连接导致
https://github.com/leanote/leanote/issues/223
2015-10-10 15:06:48 +08:00
lealife 711e6b3d17 注册邮箱支持多个"_", 如 a__a@a.com 2015-10-10 15:02:55 +08:00
lealife 0438bbb414 email 不让修改! 2015-10-10 14:53:50 +08:00
lealife 70ee362cc8 db connection lost #222 2015-10-10 14:49:02 +08:00
lealife 0479f1a433 attach bug 2015-10-10 14:42:49 +08:00
lealife 7e01cb8227 防止blog urlTitle 无限循环 2015-10-10 14:40:52 +08:00
lealife ae0eb3b918 get all attachs 权限未验证 2015-10-10 14:38:18 +08:00
lealife 097d2709e2 tag标题不能为空 2015-10-10 14:33:40 +08:00
lealife f1e56272ef 修改email只能小写 2015-10-10 14:32:30 +08:00
lealife db4cfbf605 已删除的笔记还显示 #226 2015-10-10 14:28:53 +08:00
lealife da7d31fa00 files, upload下文件过多
https://github.com/leanote/leanote/issues/225
2015-10-10 14:12:22 +08:00
lealife 62bc74d3c6 update msg 2015-10-10 13:51:42 +08:00
lealife 4467689ec3 add debug mode 2015-10-10 13:46:58 +08:00
life 69a874d90d Merge pull request #214 from nosqldb/master
compress images
2015-10-07 00:48:30 +08:00
lealife b259a00c94 update default conf 2015-10-07 00:29:29 +08:00
lealife a0088ead9e update default conf 2015-10-07 00:28:35 +08:00
duoyun 228fd80abd compress images 2015-09-21 21:39:28 +08:00
lealife 8b3a1a646a update travis 2015-09-18 13:48:06 +08:00
lealife c4954b94b8 rmove testrunner 2015-09-18 13:44:25 +08:00
lealife 2b56ebd620 update travis 2015-09-18 13:28:51 +08:00
lealife a78d95ddad update travis 2015-09-18 13:10:23 +08:00
lealife 938362154a update travis 2015-09-18 13:06:04 +08:00
lealife 8bff2b7000 delete session 2015-09-18 12:55:57 +08:00
lealife c56f228646 delete memcache 2015-09-18 12:21:32 +08:00
lealife 98f0313e3e update travis 2015-09-18 12:17:07 +08:00
lealife bde2e891ef update travis 2015-09-18 11:38:55 +08:00
lealife 7acdaede0d update travis 2015-09-18 11:35:17 +08:00
lealife 45d1b4bee3 add cmd to gen routes.go, main.go 2015-09-18 11:30:38 +08:00
lealife 4d0a170f01 update travis 2015-09-18 11:23:00 +08:00
lealife 5e37e5a37d add adminUsername on app.conf 2015-09-17 18:52:10 +08:00
lealife a60da3d592 1.0 版在网页端删除笔记本出错
https://github.com/leanote/leanote/issues/207
2015-09-17 17:14:57 +08:00
lealife 9a88fab84e fix demo 2015-09-17 12:30:57 +08:00
lealife 4981d6aad4 move tests to app/tests 2015-09-16 11:14:37 +08:00
life 7193ac6833 Merge pull request #206 from duoyun/patch-1
use english  double quote
2015-09-14 21:57:36 +08:00
Cyrus.Chu 3d1a32764d use english double quote 2015-09-14 09:03:02 +08:00
lealife 2b8df3ed76 update travis 2015-09-13 21:53:01 +08:00
lealife e94b004a5a update travis 2015-09-13 21:43:47 +08:00
lealife 6cc6f26fa1 travis 2015-09-13 21:40:50 +08:00
lealife 120cdd53c3 update travis 2015-09-13 21:34:30 +08:00
lealife 435aac4a9c https://github.com/leanote/leanote/issues/205 2015-09-13 21:23:58 +08:00
lealife f234cf285d update travis 2015-09-11 18:41:47 +08:00
lealife 54953b69c6 add travis status 2015-09-11 18:40:08 +08:00
lealife 60878b7a65 ad travis 2015-09-11 18:36:51 +08:00
lealife dada88d8a2 add travis 2015-09-11 18:34:38 +08:00
lealife 7167705409 add db/auth test 2015-09-11 18:31:37 +08:00
life 4217abca6e Merge pull request #203 from duoyun/master
remove unused func LoginGetUserInfo
2015-09-11 17:40:16 +08:00
duoyun 49a97f2285 remove unused func LoginGetUserInfo 2015-09-10 20:36:22 +08:00
lealife 4f2d7b8cd0 get mongodb configuration from env 2015-09-09 10:10:24 +08:00
lealife afbda7bfb2 reset conf 2015-09-07 22:20:53 +08:00
lealife c568756d16 使用Crypto加密, 找回密码, 修改密码修复 2015-09-07 15:39:46 +08:00
life 5794f76b0a Merge pull request #195 from duoyun/master
change md5 to bcrypt to strengthen  password hash and keep md5
2015-09-07 15:02:25 +08:00
life 74a768c0fb Merge pull request #196 from bencevans/patch-1
English Translation Updates
2015-09-07 14:58:13 +08:00
Ben Evans 4136060825 English Translation UnTitled -> Untitled 2015-09-07 00:16:50 +01:00
Ben Evans e249a953cd English Translation Update 2015-09-07 00:04:56 +01:00
Ben Evans eddc2e6356 English Translation Updates 2015-09-07 00:04:18 +01:00
duoyun 1604474d6e mv crypto to lea 2015-09-07 00:29:02 +08:00
duoyun bbaf71481c use bcrypt and keep Md5 2015-09-06 23:16:56 +08:00
lealife 952117818c delete unused 2015-09-06 10:36:10 +08:00
lealife a31432f1bf update package.json 2015-09-05 23:56:12 +08:00
lealife b0afe25730 gulp build 2015-09-05 23:48:41 +08:00
lealife 024e0e9bae ace editor 最后没有元素, 或者元素不是p, 则在最后插入之 2015-09-05 23:46:02 +08:00
lealife dbabc3a5a8 use gulp to build leanote front-end 2015-09-05 23:42:10 +08:00
lealife a30aec8254 界面优化 2015-09-05 23:30:31 +08:00
lealife 7440218974 界面优化 2015-09-05 23:30:24 +08:00
life b3c771a87f Merge pull request #182 from ZhuangER/yu-huang
fix a nav bar's bug
2015-09-05 22:47:34 +08:00
ZhuangEr 8f8451cdff fix iss183 2015-08-31 14:15:52 -04:00
yu huang 6d7fe83469 move script from footer.js
have fixed bug of multiple active of email and send to users.
also make URL like "http://localhost:9000/admin/t?p=0&t=email/sendToUsers" work.
2015-08-29 19:19:11 -04:00
yu huang 05a5280162 move inline script code to admin.js 2015-08-29 19:15:08 -04:00
yu huang 8dd55eb949 Update footer.html 2015-08-29 19:13:54 -04:00
life 5838cc218f Merge pull request #180 from ZhuangER/yu-huang
Fix a nav bar's bug
2015-08-29 13:23:09 +08:00
yu huang 2e52c957e0 Update nav.html 2015-08-28 20:10:05 -04:00
yu huang 9486c58d78 Update nav.html 2015-08-28 20:04:57 -04:00
lealife 6e7c1e0d41 add chat room 2015-08-27 10:25:05 +08:00
lealife 6a0a60a644 update readme 2015-08-21 23:52:28 +08:00
lealife 94f9c2c3cb update readme 2015-08-20 23:42:12 +08:00
lealife 37e7ed8d14 update readme 2015-08-20 23:37:55 +08:00
lealife 4227c80ec7 Merge branch 'master' of github.com:leanote/leanote
merge it
2015-08-20 23:34:46 +08:00
lealife 6ec6ef698b add qq group2 2015-08-20 23:30:46 +08:00
life 0543dd015e Merge pull request #159 from alexsourcerer/patch-6
Update msg.fr
2015-07-13 18:22:28 +08:00
alexsourcerer b88f895acf Update msg.fr
I made some translation mistakes I did not see before.
2015-06-16 12:52:56 +02:00
lealife fc517700cc v1.0 release.sh 2015-06-15 19:47:22 +08:00
lealife b5e0cd31dd v1.0 reset app.conf 2015-06-15 19:13:59 +08:00
lealife ab4b8b77a3 v1.0 use revel 0.12 2015-06-15 19:13:00 +08:00
lealife 04c339896b v1.0 preview 2015-06-15 18:55:33 +08:00
lealife e37056737d v1.0 preview 2015-06-15 18:55:07 +08:00
lealife 89f8bb934c v1.0 initial db data 2015-06-15 18:53:10 +08:00
lealife b564989214 v1.0 initial db data 2015-06-15 18:53:02 +08:00
lealife 03aa8edd58 v1.0 french lang 2015-06-15 18:24:28 +08:00
lealife 803fc90dd9 v1.0 remove unused code 2015-06-15 18:07:52 +08:00
lealife 6987a38820 v1.0
只读模式
group, 分享
评论更多问题
博客标签总是存在一个
2015-06-15 18:01:48 +08:00
lealife 7e458bb433 分享给项目组无法输出已设置项目组 #143 2015-05-21 20:49:58 +08:00
lealife b391375008 font-awesome.css找不到 #140 2015-05-14 19:23:56 +08:00
lealife 3f89e1cf83 update readme 2015-05-10 15:10:56 +08:00
lealife 9f5dc8a0e2 update readme 2015-05-10 15:07:51 +08:00
lealife 12ea17e89f 编译之前修改的 2015-05-07 00:13:04 +08:00
lealife 1fd6610fa3 #133 修改默认主题, 更新markdown解析器 2015-05-07 00:02:54 +08:00
lealife 1ec28d80c8 添加markdown解析器 for blog 博客与note的markdown解析器不一致 #133 2015-05-06 23:54:44 +08:00
lealife fcbc23e964 Ace编辑器出现XXXXXXX #132 2015-05-06 23:54:10 +08:00
lealife 6fba2a6416 test for commit 2015-05-06 23:39:56 +08:00
life 0e5edb16f6 #131 2015-05-06 23:32:45 +08:00
life 5726df1a81 update read 2015-05-03 12:32:16 +08:00
life f3a0a786c5 release 2015-04-20 20:51:03 +08:00
life fa08b3393a restore dbname 2015-04-20 19:52:13 +08:00
life 5db148be9f release for windows 2015-04-20 19:51:20 +08:00
life d03cce2761 initial db data fixed 2015-04-08 14:01:34 +08:00
life 3a0bdd803d initial data fixed 2015-04-08 14:01:24 +08:00
life d1b6dddfd0 readme 2015-04-07 20:34:09 +08:00
life 2676c5a85d Readme 2015-04-07 20:32:50 +08:00
1728 changed files with 156475 additions and 106848 deletions

No files matched your search

+3 -2
View File
@@ -2,9 +2,9 @@
pkg pkg
bin2 bin2
bin/i18n bin/i18n
bin/leanote-linux bin/leanote*
bin/leanote-mac
bin/release bin/release
bin/test.sh
bin/tmp bin/tmp
bin/test bin/test
bin/src bin/src
@@ -16,3 +16,4 @@ app/tmp/main.go
.project .project
public/config.codekit public/config.codekit
files files
/node_modules
+37
View File
@@ -0,0 +1,37 @@
language: go
go: 1.3
services:
- mongodb # 2.4.12
install:
- export PATH=$PATH:$HOME/gopath/bin
- go get -v github.com/leanote/leanote/app
- go get -u github.com/revel/cmd/revel
- ls $GOPATH/src/github.com/revel/
# - go get github.com/revel/moudle/revel
# - go install github.com/revel/cmd/revel
- pwd
- ls
script:
- mongo --version
- mongorestore -h localhost -d leanote --directoryperdb ./mongodb_backup/leanote_install_data/
- go test github.com/leanote/leanote/app/tests
# gen tmp/main.go, routes/routes.go
- go run app/cmd/main.go
# build
- go build -o leanote github.com/leanote/leanote/app/tmp
# run with port 9000
- ./leanote -importPath=github.com/leanote/leanote -runMode=dev -port=9000 &
- sleep 10s;
# test
- curl http://localhost:9000
- curl http://localhost:9000/blog
- curl http://localhost:9000/login
- curl http://localhost:9000/demo
# - revel build github.com/leanote/leanote tmp
# OK
+345
View File
@@ -0,0 +1,345 @@
var gulp = require('gulp');
var clean = require('gulp-clean');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var minifyHtml = require("gulp-minify-html");
var concat = require('gulp-concat');
var replace = require('gulp-replace');
var inject = require('gulp-inject');
var gulpSequence = require('gulp-sequence');
var fs = require('fs');
var leanoteBase = './';
var base = leanoteBase + '/public'; // public base
var noteDev = leanoteBase + '/app/views/note/note-dev.html';
var noteProBase = leanoteBase + '/app/views/note';
// 合并Js, 这些js都是不怎么修改, 且是依赖
// 840kb, 非常耗时!!
gulp.task('concatDepJs', function() {
var jss = [
'js/jquery-1.9.0.min.js',
'js/jquery.ztree.all-3.5-min.js',
'tinymce/tinymce.full.min.js', // 使用打成的包, 加载速度快
// 'libs/ace/ace.js',
'js/jQuery-slimScroll-1.3.0/jquery.slimscroll-min.js',
'js/contextmenu/jquery.contextmenu-min.js',
'js/bootstrap-min.js',
'js/object_id-min.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
// .pipe(uglify()) // 压缩
.pipe(concat('dep.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// 合并app js 这些js会经常变化 90kb
gulp.task('concatAppJs', function() {
var jss = [
'js/common.js',
'js/app/note.js',
'js/app/page.js', // 写作模式下, page依赖note
'js/app/tag.js',
'js/app/notebook.js',
'js/app/share.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('app.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// 合并requirejs和markdown为一个文件
gulp.task('concatMarkdownJs', function() {
var jss = [
'js/require.js',
'dist/main.min.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('markdown.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// note-dev.html -> note.html, 替换css, js
// TODO 加?t=2323232, 强制浏览器更新, 一般只需要把app.min.js上加
gulp.task('devToProHtml', function() {
return gulp
.src(noteDev)
.pipe(replace(/<!-- dev -->[.\s\S]+?<!-- \/dev -->/g, '')) // 把dev 去掉
.pipe(replace(/<!-- pro_dep_js -->/, '<script src="/js/dep.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_app_js -->/, '<script src="/js/app.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_markdown_js -->/, '<script src="/js/markdown.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_tinymce_init_js -->/, "var tinyMCEPreInit = {base: '/public/tinymce', suffix: '.min'};")) // 替换
.pipe(replace(/plugins\/main.js/, "plugins/main.min.js")) // 替换
// 连续两个空行换成一个空行, 没用
.pipe(replace(/\n\n/g, '\n'))
.pipe(replace(/\n\n/g, '\n'))
.pipe(replace('console.log(o);', ''))
// .pipe(minifyHtml()) // 不行, 压缩后golang报错
.pipe(rename('note.html'))
.pipe(gulp.dest(noteProBase));
});
// Get used keys
// 只获取需要js i18n的key
var path = require('path');
gulp.task('i18n', function() {
var keys = {};
var reg = /getMsg\(["']+(.+?)["']+/g;
function getKey(data) {
while(ret = reg.exec(data)) {
keys[ret[1]] = 1;
}
}
// 先获取需要的key
function ls(ff) {
var files = fs.readdirSync(ff);
for(fn in files) {
var fname = ff + path.sep + files[fn];
var stat = fs.lstatSync(fname);
if(stat.isDirectory() == true) {
ls(fname);
}
else {
if ((fname.indexOf('.html') > 0 || fname.indexOf('.js') > 0)) {
// console.log(fname);
// if (fname.indexOf('min.js') < 0) {
var data = fs.readFileSync(fname, "utf-8");
// 得到getMsg里的key
getKey(data);
// }
}
}
}
}
console.log('parsing used keys');
ls(base + '/admin');
ls(base + '/blog');
ls(base + '/dist');
ls(base + '/js');
ls(base + '/album');
ls(base + '/libs');
ls(base + '/member');
ls(base + '/tinymce');
console.log('parsed');
// msg.zh
function getAllMsgs(fname) {
var msg = {};
var data = fs.readFileSync(fname, "utf-8");
var lines = data.split('\n');
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];
// 忽略注释
if (line[0] == '#' || line[1] == '#') {
continue;
}
var lineArr = line.split('=');
if (lineArr.length >= 2) {
var key = lineArr[0];
lineArr.shift();
msg[key] = lineArr.join('=');
// msg[lineArr[0]] = lineArr[1];
}
}
return msg;
}
// msg.zh, msg.js
function genI18nJsFile(fromFilename, keys) {
var msgs = getAllMsgs(leanoteBase + '/messages/' + fromFilename);
var toFilename = fromFilename + '.js';
var toMsgs = {};
for (var i in msgs) {
// 只要需要的
if (i in keys) {
toMsgs[i] = msgs[i];
}
}
var str = 'var MSG=' + JSON.stringify(toMsgs) + ';';
str += 'function getMsg(key, data) {var msg = MSG[key];if(msg) {if(data) {if(!isArray(data)) {data = [data];}' +
'for(var i = 0; i < data.length; ++i) {' +
'msg = msg.replace("%s", data[i]);' +
'}' +
'}' +
'return msg;' +
'}' +
'return key;' +
'}';
// 写入到文件中
fs.writeFile(base + '/js/i18n/' + toFilename, str);
}
genI18nJsFile('msg.zh', keys);
genI18nJsFile('msg.en', keys);
genI18nJsFile('msg.fr', keys);
genI18nJsFile('blog.zh', keys);
genI18nJsFile('blog.en', keys);
genI18nJsFile('blog.fr', keys);
});
// 合并album需要的js
gulp.task('concatAlbumJs', function() {
/*
gulp.src(base + '/album/js/main.js')
.pipe(uglify()) // 压缩
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest(base + '/album/js/'));
*/
gulp.src(base + '/album/css/style.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/album/css'));
var jss = [
'js/jquery-1.9.0.min.js',
'js/bootstrap-min.js',
'js/plugins/libs-min/fileupload.js',
'js/jquery.pagination.js',
'album/js/main.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('main.all.js'))
.pipe(gulp.dest(base + '/album/js'));
});
// plugins压缩
gulp.task('plugins', function() {
gulp.src(base + '/js/plugins/libs/*.js')
.pipe(uglify()) // 压缩
// .pipe(concat('main.min.js'))
.pipe(gulp.dest(base + '/js/plugins/libs-min'));
// 所有js合并成一个
var jss = [
'note_info',
'tips',
'history',
'attachment_upload',
'editor_drop_paste',
'main'
];
for(var i in jss) {
jss[i] = base + '/js/plugins/' + jss[i] + '.js';
}
gulp.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('main.min.js'))
.pipe(gulp.dest(base + '/js/plugins'));
});
// mincss
var minifycss = require('gulp-minify-css');
gulp.task('minifycss', function() {
gulp.src(base + '/css/bootstrap.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css'));
gulp.src(base + '/css/font-awesome-4.2.0/css/font-awesome.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/font-awesome-4.2.0/css'));
gulp.src(base + '/css/zTreeStyle/zTreeStyle.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/zTreeStyle'));
gulp.src(base + '/dist/themes/default.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/dist/themes'));
gulp.src(base + '/js/contextmenu/css/contextmenu.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/js/contextmenu/css'));
// theme
// 用codekit
var as = ['default', 'simple', 'writting', /*'writting-overwrite', */ 'mobile'];
/*
for(var i = 0; i < as.length; ++i) {
gulp.src(base + '/css/theme/' + as[i] + '.css')
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/theme'));
}
*/
});
// tinymce
// !! You must has tinymce_dev on public/
var tinymceBase = base + '/tinymce_dev';
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,leaui_mind,lists,hr,paste,searchreplace,leanote_nav,leanote_code,tabfocus,table,directionality,textcolor';
// build
cp.exec('grunt minify', {cwd: tinymceBase}, function(err, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
// 将所有都合并成一起
cp.exec(bundleCmd, {cwd: tinymceBase}, function(err, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});
});
});
// 合并css, 无用
// Deprecated
gulp.task('concatCss', function() {
return gulp
.src([markdownRaw + '/css/default.css', markdownRaw + '/css/md.css'])
.pipe(concat('all.css'))
.pipe(gulp.dest(markdownMin));
});
gulp.task('concat', ['concatDepJs', 'concatAppJs', 'concatMarkdownJs']);
gulp.task('html', ['devToProHtml']);
gulp.task('default', ['concat', 'plugins', 'minifycss', 'i18n', 'concatAlbumJs', 'html']);
+3 -3
View File
@@ -1,6 +1,6 @@
leanote - you own cloud note! LEANOTE - NOT JUST A NOTEPAD!
Copyright 2014 by the contributors Copyright 2014-2015 by the contributors
This program is free software; you can redistribute it and/or modify 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 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. 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) life(life@leanote.com, lifephp@gmail.com)
+130 -76
View File
@@ -1,113 +1,167 @@
# Leanote # Leanote
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.org/leanote/leanote.svg)](https://travis-ci.org/leanote/leanote)
## 1. Introduction ## 1. Introduction
Leanote, not just a notebook! Leanote, Not Just A Notepad!
![leanote.png](leanote.png "") ![leanote.png](leanote.png "")
**Some Features** **Some Features**
* Knowledge: Manage your knowledge in leanote. leanote contains the tinymce editor and a markdown editor, just enjoy yourself writing. * 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. * 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. * Cooperation: Collaborate with friends to improve your skills.
* Blog: Publish your knowledge and make leanote your blog. * Blog: Publish your knowledge and make Leanote your blog.
## 2. Why we created leanote **Other Features**
* Support Markdown
* Writting mode
* Export note to PDF
* Note batch operation
* Custom theme for blog
## 2. Why we created Leanote
To be honest, our inspiration comes from Evernote. We use Evernote to manage our knowledge everyday. But we find that: 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 * 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 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! * We want to share our knowledge, so all of us have our blogs (e.g. on Wordpress) and our Evernote accounts, but why can not those two be one!
* ...... * ......
## 3. How to install leanote ## 3. How to install Leanote
More information about how to install leanote please see: Leanote contains: Leanote Web & Server (this repository), [Desktop app](https://github.com/leanote/desktop-app), [iOS](https://github.com/leanote/leanote-ios), [Android](https://github.com/leanote/leanote-android). And all the products are open source!
* [leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
### 3.1. Download leanote You can install Leanote on your server, and use Leanote App (Desktop, iOs, Android) to sync notes with your self-hosted server.
Leanote V1.0.3-beta has been released. Binaries: Welcome to sign up on https://leanote.com, Leanote team provide a stable and reliable service for you.
* Linux: [leanote-linux-x86_64.v1.0-beta.3.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta.3/leanote-linux-x86_64.v1.0-beta.3.bin.tar.gz) More information about how to install Leanote please see:
* MacOS X: [leanote-mac-x86_64.v1.0-beta.3.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta.3/leanote-mac-x86_64.v1.0-beta.3.bin.tar.gz)
Or you can clone [Leanote bin repository](https://github.com/leanote/leanote-bin) (Recommend) * [Leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [Leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
### 3.2. Install MongoDB ## 4. How to develop Leanote
Leanote is written in go using [revel](https://revel.github.io/) and [MongoDB](https://www.mongodb.org). Thus, you need to first install MongoDB.
For more tips please have a look at [our wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
### 3.3. Import initial MongoDB data
The mongodb data is in `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
```
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
```
The initial database contains two users:
```
user2 username: admin, password: abc123 (administrator)
user3 username: demo, password: demo@leanote.com (this user is for demo)
```
### 3.4. Configuration
Modify `[PATH_TO_LEANOTE]/conf/app.conf`. Available configuration options are:
``mongodb`` **required**
```Shell
db.host=localhost
db.port=27017
db.dbname=leanote
db.username=
db.password=
```
``app.secret`` **required** **important**
The secret key used for cryptographic operations (revel.Sign).
FOR SECURITY, YOU MUST CHANGE IT!!
For more infomation please see `app/app.conf` and the [revel manuals](https://revel.github.io/)
### 3.5. Run leanote
```
$> cd PATH_TO_LEANOTE/bin
$> sudo sh run.sh
```
## 4. How to develop leanote
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 [How-to-develop-leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-%E5%A6%82%E4%BD%95%E5%BC%80%E5%8F%91leanote)
## 5. Docs
* [Leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [Leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
## 5. Contributors More docs please see [wiki](https://github.com/leanote/leanote/wiki).
## 6. Contributors
Thank you to all the [contributors](https://github.com/leanote/leanote/graphs/contributors) on Thank you to all the [contributors](https://github.com/leanote/leanote/graphs/contributors) on
this project. Your help is much appreciated. this project. Your help is much appreciated.
## 6. Contributing ## 7.Join us
Please fork this repository and contribute back using [pull requests](https://github.com/leanote/leanote/pulls). Please fork this repository and contribute back using [pull requests](https://github.com/leanote/leanote/pulls).
## Docs If you find some problems or has some good ideas, please submit [issues](https://github.com/leanote/leanote/issues).
* [leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
* [leanote blog theme api](https://github.com/leanote/leanote/wiki/leanote-blog-theme-api_en)
## Discussion You are always welcomed!
* [leanote bbs](http://bbs.leanote.com)
* [leanote google group](https://groups.google.com/forum/#!forum/leanote) ## 8. Donation
* QQ Group: 158716820 Support us, [donate us](http://leanote.org/#donate). And thanks [donators](http://leanote.leanote.com/post/leanote-donation-list).
## 9. Related projects
* [Leanote Desktop App](https://github.com/leanote/desktop-app), [Download](http://app.leanote.com)
* [Leanote iOS](https://github.com/leanote/leanote-ios), [Download From App Store](https://itunes.apple.com/en/app/leanote/id1022302858?mt=8)
* [Leanote Android](https://github.com/leanote/leanote-android), development phase
Yare are welcome to join us.
## 9. Support & Join us
* Email: leanote@leanote.com
* [Leanote BBS](http://bbs.leanote.com)
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
* QQ Group: 256076853, 158716820
----------------------------------------------------------------------- -----------------------------------------------------------------------
[中文](README_zh.md)
# Leanote
## 1. 介绍
Leanote, 不只是笔记!
**特性**
* 知识管理: 通过Leanote来管理知识, Leanote有易操作的界面, 包含两款编辑器富文本编辑器和Markdown编辑器. 在Leanote, 你可以尽情享受写作.
* 博客: Leanote也可以作为你的博客, 将知识公开成博客, 让Leanote把你的知识传播的更远!
* 分享: 你也可以通过分享知识给好友, 让好友拥有你的知识.
* 协作: 在分享的同时也可以与好友一起协作知识.
**一些其它特性**
* 支持Markdown编辑
* 写作模式
* 支持PDF导出
* 支持批量操作
* 博客自定义主题, 实现高度定制化
## 2. 为什么我们要创建Leanote?
说实话, 我们曾是evernote的忠实粉丝, 但是我们也发现evernote的不足:
* evernote的编辑器不能满足我们的需求, 不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放.
* 我们是markdown的爱好者, 可是evernote竟然没有.
* 我们也想将知识公开, 所以我们有自己的博客, 如wordpress, 但为什么这两者不能合二为一呢?
* 还有...
## 3.安装Leanote
Leanote云笔记产品包括: Leanote Web & Server(即本仓库), 桌面客户端, ios, android. 4端全部开源! 你可以下载它安装在自己的服务器上, Leanote的客户端可以连接自建的服务.
欢迎在 https://leanote.com 上注册, Leanote团队为你提供稳定可靠的服务.
这里详细整理了Leanote二进版和Leanote开发版的安装教程, 请移步至:
* [Leanote二进制详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
* [Leanote开发版详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E5%BC%80%E5%8F%91%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
## 4. 如何对Leanote进行二次开发
请查看 [How-to-develop-Leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-%E5%A6%82%E4%BD%95%E5%BC%80%E5%8F%91leanote)
## 5 相关文档
* [Leanote二进制版详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
* [Leanote开发版详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E5%BC%80%E5%8F%91%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
* [Leanote source Leanote源码导读](https://github.com/leanote/leanote/wiki/Leanote-source-leanote源码导读)
* [Leanote blog theme api(中文版)](https://github.com/leanote/leanote/wiki/leanote-blog-theme-api)
* [How to develop leanote 如何开发Leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-如何开发leanote)
更多文档请查看 [wiki](https://github.com/leanote/leanote/wiki).
## 6. 贡献者
谢谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, Leanote因有你们而更完美!
## 7. 加入我们
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到Leanote.
有任何问题或建议, 欢迎提交[issue](https://github.com/leanote/leanote/issues).
Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善Leanote.
## 8. 捐赠
支持我们, [捐赠Leanote](http://leanote.org/#donate). 感谢[捐赠者](http://leanote.leanote.com/post/leanote-donation-list), 谢谢你们的鼓励, Leanote会一直坚持!
## 9. 其它相关项目
* [Leanote Desktop App](https://github.com/leanote/desktop-app), [下载地址](http://app.leanote.com)
* [Leanote iOS](https://github.com/leanote/leanote-ios), [从App Store下载](https://itunes.apple.com/zn/app/leanote/id1022302858?mt=8)
* [Leanote Android](https://github.com/leanote/leanote-android), 开发阶段
欢迎加入我们!
## 联系&加入我们
* Email: leanote@leanote.com
* [Leanote 社区](http://bbs.leanote.com)
* QQ群: 256076853, 158716820(已满)
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
+31 -71
View File
@@ -1,6 +1,7 @@
# Leanote
# Leanote产品 [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.org/leanote/leanote.svg)](https://travis-ci.org/leanote/leanote)
## 1. 介绍 ## 1. 介绍
Leanote, 不只是笔记! Leanote, 不只是笔记!
@@ -24,90 +25,49 @@ Leanote, 不只是笔记!
leanote是一款私有云笔记, 你可以下载它安装在自己的服务器上, 当然也可以在 http://leanote.com 上注册. leanote是一款私有云笔记, 你可以下载它安装在自己的服务器上, 当然也可以在 http://leanote.com 上注册.
这里详细整理了leanote二进版和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%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开发版详细安装教程](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.1. 下载leanote
Leanote V1.0-beta.3 已发布, 二进制文件(暂时没有windows版的):
* Linux: [leanote-linux-x86_64.v1.0-beta.3.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta.3/leanote-linux-x86_64.v1.0-beta.3.bin.tar.gz)
* MacOS X: [leanote-mac-x86_64.v1.0-beta.3.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta.3/leanote-mac-x86_64.v1.0-beta.3.bin.tar.gz)
或者直接检出[Leanote bin repository](https://github.com/leanote/leanote-bin) (推荐, 因为为最新版本)
### 3.2. 安装 MongodbDB
Leanote是由golang(使用[revel](https://revel.github.io/)框架 和 [MongoDB](https://www.mongodb.org)数据库), 你需要先安装Mongodb.
安装MongodbDB, 导入数据更多细节请查看: [wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
### 3.3. 导入初始数据
MongodbDB初始数据在 `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
```
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
```
初始数据包含两个用户:
```
user2 username: admin, password: abc123 (管理员, 重要!)
user3 username: demo@leanote.com, password: demo@leanote.com (为体验使用)
```
### 3.4. 配置
修改 `[PATH_TO_LEANOTE]/conf/app.conf`. 有以下选项:
``mongodb`` **必须配置!**
```Shell
db.host=localhost
db.port=27017
db.dbname=leanote
db.username=
db.password=
```
``app.secret`` **重要**
请随意修改一个, app的密钥, 不能使用默认的, 不然会有安全问题
更多配置请查看 `app/app.conf` 和 [revel 手册](https://revel.github.io/)
### 3.5. 运行leanote
```
$> cd PATH_TO_LEANOTE/bin
$> sudo sh run.sh
```
## 4. 如何对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) 请查看 [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. 贡献者 ## 5 相关文档
多谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, leanote因有你们而更完美!
## 6. 加入我们
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到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%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开发版详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E5%BC%80%E5%8F%91%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
* [Leanote source leanote源码导读](https://github.com/leanote/leanote/wiki/Leanote-source-leanote源码导读) * [Leanote source leanote源码导读](https://github.com/leanote/leanote/wiki/Leanote-source-leanote源码导读)
* [leanote blog theme api(中文版)](https://github.com/leanote/leanote/wiki/leanote-blog-theme-api) * [leanote blog theme api(中文版)](https://github.com/leanote/leanote/wiki/leanote-blog-theme-api)
* [How to develop leanote 如何开发leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-如何开发leanote) * [How to develop leanote 如何开发leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-如何开发leanote)
更多文档请查看 [wiki](https://github.com/leanote/leanote/wiki).
## 讨论 ## 6. 贡献者
多谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, leanote因有你们而更完美!
## 7. 加入我们
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到leanote.
有任何问题或建议, 欢迎提交[issue](https://github.com/leanote/leanote/issues).
Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善leanote.
## 8. 捐赠
支持我们, [捐赠Leanote](http://leanote.org/#donate). 感谢[捐赠者](http://leanote.leanote.com/post/leanote-donation-list), 谢谢你们的鼓励, Leanote会一直坚持!
## 9. 其它相关项目
* [Leanote Desktop App](https://github.com/leanote/desktop-app), [下载地址](http://app.leanote.com)
* [Leanote IOS](https://github.com/leanote/leanote-ios), [从App Store下载](https://itunes.apple.com/zn/app/leanote/id1022302858?mt=8)
* [Leanote Android](https://github.com/Dminter/leanote-android-client), 开发阶段
欢迎加入我们!
## 联系&加入我们
* Email: leanote@leanote.com
* [leanote 社区](http://bbs.leanote.com) * [leanote 社区](http://bbs.leanote.com)
* QQ群: 158716820 * QQ群: 158716820, 256076853
* [leanote google group](https://groups.google.com/forum/#!forum/leanote) * [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
---------------------------------------------------------------- ----------------------------------------------------------------
[English](README.md) [English](README.md)
+19
View File
@@ -0,0 +1,19 @@
package main
import (
"github.com/revel/revel"
"github.com/revel/cmd/harness"
"fmt"
)
func main() {
// go run main.go
// 生成routes.go, main.go
revel.Init("", "github.com/leanote/leanote", "")
_, err := harness.Build() // ok, err
if err != nil {
panic(err)
}
fmt.Println("Ok")
// panicOnError(reverr, "Failed to build")
}
+13 -8
View File
@@ -2,11 +2,11 @@ package controllers
import ( import (
"github.com/revel/revel" "github.com/revel/revel"
// "encoding/json" // "encoding/json"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
// . "github.com/leanote/leanote/app/lea" // . "github.com/leanote/leanote/app/lea"
// "io/ioutil" // "io/ioutil"
) )
// Album controller // Album controller
@@ -14,12 +14,17 @@ type Album struct {
BaseController BaseController
} }
// 图片管理, iframe
func (c Album) Index() revel.Result {
c.SetLocale();
return c.RenderTemplate("album/index.html")
}
// all albums by userId // all albums by userId
func (c Album) GetAlbums() revel.Result { func (c Album) GetAlbums() revel.Result {
re := albumService.GetAlbums(c.GetUserId()) re := albumService.GetAlbums(c.GetUserId())
return c.RenderJson(re) return c.RenderJson(re)
} }
func (c Album) DeleteAlbum(albumId string) revel.Result { func (c Album) DeleteAlbum(albumId string) revel.Result {
re, msg := albumService.DeleteAlbum(c.GetUserId(), albumId) re, msg := albumService.DeleteAlbum(c.GetUserId(), albumId)
return c.RenderJson(info.Re{Ok: re, Msg: msg}) return c.RenderJson(info.Re{Ok: re, Msg: msg})
@@ -29,12 +34,12 @@ func (c Album) DeleteAlbum(albumId string) revel.Result {
func (c Album) AddAlbum(name string) revel.Result { func (c Album) AddAlbum(name string) revel.Result {
album := info.Album{ album := info.Album{
AlbumId: bson.NewObjectId(), AlbumId: bson.NewObjectId(),
Name: name, Name: name,
Seq: -1, Seq: -1,
UserId: c.GetObjectUserId()} UserId: c.GetObjectUserId()}
re := albumService.AddAlbum(album) re := albumService.AddAlbum(album)
if(re) { if re {
return c.RenderJson(album) return c.RenderJson(album)
} else { } else {
return c.RenderJson(false) return c.RenderJson(false)
+22 -13
View File
@@ -41,14 +41,14 @@ func (c Auth) doLogin(email, pwd string) revel.Result {
sessionId := c.Session.Id() sessionId := c.Session.Id()
var msg = "" var msg = ""
userInfo := authService.Login(email, pwd) userInfo, err := authService.Login(email, pwd)
if userInfo.Email != "" { if err != nil {
// 登录错误, 则错误次数++
msg = "wrongUsernameOrPassword"
} else {
c.SetSession(userInfo) c.SetSession(userInfo)
sessionService.ClearLoginTimes(sessionId) sessionService.ClearLoginTimes(sessionId)
return c.RenderJson(info.Re{Ok: true}) 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)})
@@ -61,15 +61,15 @@ func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result {
if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha { if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha {
msg = "captchaError" msg = "captchaError"
} else { } else {
userInfo := authService.Login(email, pwd) userInfo, err := authService.Login(email, pwd)
if userInfo.Email != "" { if err != nil {
c.SetSession(userInfo)
sessionService.ClearLoginTimes(sessionId)
return c.RenderJson(info.Re{Ok: true})
} else {
// 登录错误, 则错误次数++ // 登录错误, 则错误次数++
msg = "wrongUsernameOrPassword" msg = "wrongUsernameOrPassword"
sessionService.IncrLoginTimes(sessionId) sessionService.IncrLoginTimes(sessionId)
} else {
c.SetSession(userInfo)
sessionService.ClearLoginTimes(sessionId)
return c.RenderJson(info.Re{Ok: true})
} }
} }
@@ -85,8 +85,17 @@ func (c Auth) Logout() revel.Result {
// 体验一下 // 体验一下
func (c Auth) Demo() revel.Result { func (c Auth) Demo() revel.Result {
c.doLogin(configService.GetGlobalStringConfig("demoUsername"), configService.GetGlobalStringConfig("demoPassword")) email := configService.GetGlobalStringConfig("demoUsername")
return c.Redirect("/note") 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
} }
//-------- //--------
+7
View File
@@ -84,6 +84,13 @@ func (c BaseController) GetUserInfo() info.User {
return info.User{} return info.User{}
} }
func (c BaseController) GetUserAndBlogUrl() info.UserAndBlogUrl {
if userId, ok := c.Session["UserId"]; ok && userId != "" {
return userService.GetUserAndBlogUrl(userId);
}
return info.UserAndBlogUrl{}
}
// 这里的session都是cookie中的, 与数据库session无关 // 这里的session都是cookie中的, 与数据库session无关
func (c BaseController) GetSession(key string) string { func (c BaseController) GetSession(key string) string {
v, ok := c.Session[key] v, ok := c.Session[key]
+9 -8
View File
@@ -265,10 +265,6 @@ func (c Blog) getCates(userBlog info.UserBlog) {
} }
} }
Log("cates")
LogJ(cates)
LogJ(catesTree);
c.RenderArgs["cates"] = cates c.RenderArgs["cates"] = cates
c.RenderArgs["catesTree"] = catesTree c.RenderArgs["catesTree"] = catesTree
} }
@@ -352,9 +348,10 @@ func (c Blog) blogCommon(userId string, userBlog info.UserBlog, userInfo info.Us
// 得到主题信息 // 得到主题信息
themeInfo := themeService.GetThemeInfo(userBlog.ThemeId.Hex(), userBlog.Style) themeInfo := themeService.GetThemeInfo(userBlog.ThemeId.Hex(), userBlog.Style)
c.RenderArgs["themeInfo"] = themeInfo c.RenderArgs["themeInfo"] = themeInfo
Log(">>")
Log(userBlog.Style) // Log(">>")
Log(userBlog.ThemeId.Hex()) // Log(userBlog.Style)
// Log(userBlog.ThemeId.Hex())
return true, userBlog return true, userBlog
} }
@@ -824,7 +821,7 @@ func (c Blog) LikePost(noteId string, callback string) revel.Result {
re.Ok, re.Item = blogService.LikeBlog(noteId, userId) re.Ok, re.Item = blogService.LikeBlog(noteId, userId)
return c.RenderJsonP(callback, re) 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() userId := c.GetUserId()
page := c.GetPage() page := c.GetPage()
@@ -837,6 +834,10 @@ func (c Blog) GetComments(noteId string) revel.Result {
result["commentUserInfo"] = commentUserInfo result["commentUserInfo"] = commentUserInfo
re.Item = result re.Item = result
if callback != "" {
return c.RenderJsonP(callback, result)
}
return c.RenderJson(re) return c.RenderJson(re)
} }
+39 -27
View File
@@ -75,7 +75,7 @@ func (c File) UploadAvatar() revel.Result {
// leaui image plugin upload image // leaui image plugin upload image
func (c File) UploadImageLeaui(albumId string) revel.Result { func (c File) UploadImageLeaui(albumId string) revel.Result {
re := c.uploadImage("", albumId); re := c.uploadImage("", albumId)
return c.RenderJson(re) return c.RenderJson(re)
} }
@@ -84,7 +84,7 @@ func (c File) UploadImageLeaui(albumId string) revel.Result {
func (c File) uploadImage(from, albumId string) (re info.Re) { func (c File) uploadImage(from, albumId string) (re info.Re) {
var fileUrlPath = "" var fileUrlPath = ""
var fileId = "" var fileId = ""
var resultCode = 0 // 1表示正常 var resultCode = 0 // 1表示正常
var resultMsg = "error" // 错误信息 var resultMsg = "error" // 错误信息
var Ok = false var Ok = false
@@ -100,13 +100,19 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
return re return re
} }
defer file.Close() defer file.Close()
// 生成上传路径 // 生成上传路径
newGuid := NewGuid()
userId := c.GetUserId()
if(from == "logo" || from == "blogLogo") { if(from == "logo" || from == "blogLogo") {
fileUrlPath = "public/upload/" + c.GetUserId() + "/images/logo" fileUrlPath = "public/upload/" + Digest3(userId) + "/" + userId + "/images/logo"
} else { } else {
fileUrlPath = "files/" + c.GetUserId() + "/images" fileUrlPath = "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
} }
dir := revel.BasePath + "/" + fileUrlPath
dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755) err = os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return re return re
@@ -114,18 +120,18 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
// 生成新的文件名 // 生成新的文件名
filename := handel.Filename filename := handel.Filename
var ext string; var ext string
if from == "pasteImage" { if from == "pasteImage" {
ext = ".png"; // TODO 可能不是png类型 ext = ".png" // TODO 可能不是png类型
} else { } else {
_, ext = SplitFilename(filename) _, ext = SplitFilename(filename)
if(ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg") { if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
resultMsg = "Please upload image" resultMsg = "Please upload image"
return re return re
} }
} }
filename = NewGuid() + ext filename = newGuid + ext
data, err := ioutil.ReadAll(file) data, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
LogJ(err) LogJ(err)
@@ -133,25 +139,25 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
} }
var maxFileSize float64 var maxFileSize float64
if(from == "logo") { if from == "logo" {
maxFileSize = configService.GetUploadSize("uploadAvatarSize"); maxFileSize = configService.GetUploadSize("uploadAvatarSize")
} else if from == "blogLogo" { } else if from == "blogLogo" {
maxFileSize = configService.GetUploadSize("uploadBlogLogoSize"); maxFileSize = configService.GetUploadSize("uploadBlogLogoSize")
} else { } else {
maxFileSize = configService.GetUploadSize("uploadImageSize"); maxFileSize = configService.GetUploadSize("uploadImageSize")
} }
if maxFileSize <= 0 { if maxFileSize <= 0 {
maxFileSize = 1000 maxFileSize = 1000
} }
// > 2M? // > 2M?
if(float64(len(data)) > maxFileSize * float64(1024*1024)) { if float64(len(data)) > maxFileSize*float64(1024*1024) {
resultCode = 0 resultCode = 0
resultMsg = fmt.Sprintf("The file Size is bigger than %vM", maxFileSize) resultMsg = fmt.Sprintf("The file Size is bigger than %vM", maxFileSize)
return re return re
} }
toPath := dir + "/" + filename; toPath := dir + "/" + filename
err = ioutil.WriteFile(toPath, data, 0777) err = ioutil.WriteFile(toPath, data, 0777)
if err != nil { if err != nil {
LogJ(err) LogJ(err)
@@ -168,20 +174,21 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
// File // File
fileInfo := info.File{Name: filename, fileInfo := info.File{Name: filename,
Title: handel.Filename, Title: handel.Filename,
Path: fileUrlPath, Path: fileUrlPath,
Size: filesize} Size: filesize}
id := bson.NewObjectId(); id := bson.NewObjectId()
fileInfo.FileId = id fileInfo.FileId = id
fileId = id.Hex() fileId = id.Hex()
if(from == "logo" || from == "blogLogo") { if(from == "logo" || from == "blogLogo") {
fileId = "public/upload/" + c.GetUserId() + "/images/logo/" + filename fileId = fileUrlPath
} }
Ok, resultMsg = fileService.AddImage(fileInfo, albumId, c.GetUserId(), from == "" || from == "pasteImage") Ok, resultMsg = fileService.AddImage(fileInfo, albumId, c.GetUserId(), from == "" || from == "pasteImage")
resultMsg = c.Message(resultMsg) resultMsg = c.Message(resultMsg)
fileInfo.Path = ""; // 不要返回 fileInfo.Path = "" // 不要返回
re.Item = fileInfo re.Item = fileInfo
return re return re
@@ -204,6 +211,7 @@ func (c File) DeleteImage(fileId string) revel.Result {
re.Ok, re.Msg = fileService.DeleteImage(c.GetUserId(), fileId) re.Ok, re.Msg = fileService.DeleteImage(c.GetUserId(), fileId)
return c.RenderJson(re) return c.RenderJson(re)
} }
//----------- //-----------
// 输出image // 输出image
@@ -226,12 +234,16 @@ func (c File) CopyImage(userId, fileId, toUserId string) revel.Result {
return c.RenderJson(re) return c.RenderJson(re)
} }
// 复制外网的图片, 成公共图片 放在/upload下 // 复制外网的图片
// 都要好好的计算大小 // 都要好好的计算大小
func (c File) CopyHttpImage(src string) revel.Result { func (c File) CopyHttpImage(src string) revel.Result {
re := info.NewRe() re := info.NewRe()
fileUrlPath := "upload/" + c.GetUserId() + "/images"
dir := revel.BasePath + "/public/" + fileUrlPath // 生成上传路径
newGuid := NewGuid()
userId := c.GetUserId()
fileUrlPath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
dir := revel.BasePath + "/" + fileUrlPath
err := os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return c.RenderJson(re) return c.RenderJson(re)
@@ -246,14 +258,14 @@ func (c File) CopyHttpImage(src string) revel.Result {
// File // File
fileInfo := info.File{Name: filename, fileInfo := info.File{Name: filename,
Title: filename, Title: filename,
Path: fileUrlPath + "/" + filename, Path: fileUrlPath + "/" + filename,
Size: filesize} Size: filesize}
id := bson.NewObjectId(); id := bson.NewObjectId()
fileInfo.FileId = id fileInfo.FileId = id
re.Id = id.Hex() re.Id = id.Hex()
re.Item = fileInfo.Path // re.Item = fileInfo.Path
re.Ok, re.Msg = fileService.AddImage(fileInfo, "", c.GetUserId(), true) re.Ok, re.Msg = fileService.AddImage(fileInfo, "", c.GetUserId(), true)
return c.RenderJson(re) return c.RenderJson(re)
+2 -3
View File
@@ -21,13 +21,12 @@ func (c Index) Default() revel.Result {
} }
// leanote展示页, 没有登录的, 或已登录明确要进该页的 // leanote展示页, 没有登录的, 或已登录明确要进该页的
func (c Index) Index() revel.Result { func (c Index) Index() revel.Result {
c.SetUserInfo() c.SetUserInfo()
c.RenderArgs["title"] = "leanote" c.RenderArgs["title"] = "leanote"
c.RenderArgs["openRegister"] = configService.GlobalStringConfigs["openRegister"] c.RenderArgs["openRegister"] = configService.GlobalStringConfigs["openRegister"]
lang := c.SetLocale() c.SetLocale()
return c.RenderTemplate("home/index_" + lang + ".html"); return c.RenderTemplate("home/index.html");
} }
// 建议 // 建议
+230 -32
View File
@@ -2,17 +2,20 @@ package controllers
import ( import (
"github.com/revel/revel" "github.com/revel/revel"
// "encoding/json" // "encoding/json"
"gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson"
"os"
"os/exec"
"regexp"
"strings" "strings"
// "time" "time"
// "github.com/leanote/leanote/app/types" "fmt"
// "io/ioutil" // "github.com/leanote/leanote/app/types"
// "fmt" // "io/ioutil"
// "bytes" // "bytes"
// "os" // "os"
) )
type Note struct { type Note struct {
@@ -22,9 +25,9 @@ type Note struct {
// 笔记首页, 判断是否已登录 // 笔记首页, 判断是否已登录
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中 // 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
// 否则, 转向登录页面 // 否则, 转向登录页面
func (c Note) Index(noteId string) revel.Result { func (c Note) Index(noteId, online string) revel.Result {
c.SetLocale() c.SetLocale()
userInfo := c.GetUserInfo() userInfo := c.GetUserAndBlogUrl()
userId := userInfo.UserId.Hex() userId := userInfo.UserId.Hex()
@@ -49,10 +52,11 @@ func (c Note) Index(noteId string) revel.Result {
hasRightNoteId := false hasRightNoteId := false
if IsObjectId(noteId) { if IsObjectId(noteId) {
note := noteService.GetNoteById(noteId) note := noteService.GetNoteById(noteId)
var noteOwner = note.UserId.Hex()
noteContent = noteService.GetNoteContent(noteId, noteOwner)
if note.NoteId != "" { if note.NoteId != "" {
var noteOwner = note.UserId.Hex()
noteContent = noteService.GetNoteContent(noteId, noteOwner)
hasRightNoteId = true hasRightNoteId = true
c.RenderArgs["curNoteId"] = noteId c.RenderArgs["curNoteId"] = noteId
c.RenderArgs["curNotebookId"] = note.NotebookId.Hex() c.RenderArgs["curNotebookId"] = note.NotebookId.Hex()
@@ -124,8 +128,9 @@ func (c Note) Index(noteId string) revel.Result {
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser() c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
// return c.RenderTemplate("note/note.html") // return c.RenderTemplate("note/note.html")
if isDev, _ := revel.Config.Bool("mode.dev"); isDev {
if isDev, _ := revel.Config.Bool("mode.dev"); isDev && online == "" {
return c.RenderTemplate("note/note-dev.html") return c.RenderTemplate("note/note-dev.html")
} else { } else {
return c.RenderTemplate("note/note.html") return c.RenderTemplate("note/note.html")
@@ -136,13 +141,13 @@ func (c Note) Index(noteId string) revel.Result {
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中 // 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
// 否则, 转向登录页面 // 否则, 转向登录页面
func (c Note) ListNotes(notebookId string) revel.Result { 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) return c.RenderJson(notes)
} }
// 得到trash // 得到trash
func (c Note) ListTrashNotes() revel.Result { 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) return c.RenderJson(notes)
} }
@@ -254,28 +259,59 @@ func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
// 删除note/ 删除别人共享给我的笔记 // 删除note/ 删除别人共享给我的笔记
// userId 是note.UserId // userId 是note.UserId
func (c Note) DeleteNote(noteId, userId string, isShared bool) revel.Result { func (c Note) DeleteNote(noteIds []string, isShared bool) revel.Result {
if(!isShared) { if !isShared {
return c.RenderJson(trashService.DeleteNote(noteId, c.GetUserId())); 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 { 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 // 移动note
func (c Note) MoveNote(noteId, notebookId string) revel.Result { func (c Note) MoveNote(noteIds []string, notebookId string) revel.Result {
return c.RenderJson(noteService.MoveNote(noteId, notebookId, c.GetUserId())); userId := c.GetUserId()
for _, noteId := range noteIds {
noteService.MoveNote(noteId, notebookId, userId)
}
return c.RenderJson(true)
} }
// 复制note // 复制note
func (c Note) CopyNote(noteId, notebookId string) revel.Result { func (c Note) CopyNote(noteIds []string, notebookId string) revel.Result {
return c.RenderJson(noteService.CopyNote(noteId, notebookId, c.GetUserId())); 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 { func (c Note) CopySharedNote(noteIds []string, notebookId, fromUserId string) revel.Result {
return c.RenderJson(noteService.CopySharedNote(noteId, notebookId, fromUserId, c.GetUserId())); 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)
} }
//------------ //------------
@@ -285,14 +321,176 @@ func (c Note) SearchNote(key string) revel.Result {
_, blogs := noteService.SearchNote(key, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false, false) _, blogs := noteService.SearchNote(key, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false, false)
return c.RenderJson(blogs) return c.RenderJson(blogs)
} }
// 通过tags搜索 // 通过tags搜索
func (c Note) SearchNoteByTags(tags []string) revel.Result { func (c Note) SearchNoteByTags(tags []string) revel.Result {
_, blogs := noteService.SearchNoteByTags(tags, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false) _, blogs := noteService.SearchNoteByTags(tags, c.GetUserId(), c.GetPage(), pageSize, "UpdatedTime", false)
return c.RenderJson(blogs) return c.RenderJson(blogs)
} }
// 生成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("error")
}
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
return c.RenderText("error")
}
noteUserId := note.UserId.Hex()
content := noteService.GetNoteContent(noteId, 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)
}
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 {
// ![enter image description here](url)
regImageMarkdown, _ := regexp.Compile(`!\[.*?\]\(` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})\)`)
findsImageMarkdown := regImageMarkdown.FindAllStringSubmatch(contentStr, -1) // 查找所有的
for _, eachFind := range findsImageMarkdown {
if len(eachFind) == 3 {
fileId := eachFind[2]
// 得到base64编码文件
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
if fileBase64 == "" {
continue
}
// 1
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
allFixed := "![](" + fileBase64 + ")"
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
}
}
}
if note.Tags != nil && len(note.Tags) > 0 && note.Tags[0] != "" {
} else {
note.Tags = nil
}
c.RenderArgs["blog"] = note
c.RenderArgs["content"] = contentStr
c.RenderArgs["userInfo"] = userInfo
userBlog := blogService.GetUserBlog(noteUserId)
c.RenderArgs["userBlog"] = userBlog
return c.RenderTemplate("file/pdf.html")
}
// 导出成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.RenderText("error")
}
noteUserId := note.UserId.Hex()
// 是否有权限
if noteUserId != userId {
// 是否是有权限协作的
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
re.Msg = "No Perm"
return c.RenderText("error")
}
}
// path 判断是否需要重新生成之
guid := NewGuid()
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
dir := revel.BasePath + "/" + fileUrlPath
if !MkdirAll(dir) {
return c.RenderText("error, no dir")
}
filename := guid + ".pdf"
path := dir + "/" + filename
// leanote.com的secret
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
// cc := binPath + " --no-stop-slow-scripts --javascript-delay 10000 \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// cc := binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// 等待--window-status为done的状态
// http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html
// wkhtmltopdf参数大全
var cc string
if note.IsMarkdown {
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
} else {
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
}
cmd := exec.Command("/bin/sh", "-c", cc)
_, 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; 置顶 // 设置/取消Blog; 置顶
func (c Note) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result { func (c Note) SetNote2Blog(noteIds []string, isBlog, isTop bool) revel.Result {
re := noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop) for _, noteId := range noteIds {
return c.RenderJson(re) noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
}
return c.RenderJson(true)
} }
-70
View File
@@ -1,70 +0,0 @@
package controllers
import (
"code.google.com/p/goauth2/oauth"
"github.com/revel/revel"
"github.com/leanote/leanote/app/lea/netutil"
. "github.com/leanote/leanote/app/lea"
"encoding/json"
"fmt"
)
type Oauth struct {
BaseController
}
var oauthCfg = &oauth.Config{
ClientId: "3790fbf1fc14bc6c5d85",
ClientSecret: "e9dadfe601c7caa6df9b33db3e7539945c60dfa2",
AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token",
RedirectURL: "http://leanote.com/oauth/githubCallback",
Scope: "user",
}
// 用户允许后, github返回到leanote
// 通过code得到token
// https://github.com/login/oauth/authorize?access_type=&approval_prompt=&client_id=3790fbf1fc14bc6c5d85&redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Foauth2callback&response_type=code&scope=user&state=
func (c Oauth) GithubCallback(code string) revel.Result {
t := &oauth.Transport{Config: oauthCfg}
// Exchange the received code for a token
tok, err := t.Exchange(code)
token := tok.AccessToken
if err != nil || token == "" {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
// 得到用户信息
profileInfoURL := "https://api.github.com/user"
url := fmt.Sprintf("%s?access_token=%s", profileInfoURL, token)
content, err2 := netutil.GetContent(url)
if err2 != nil {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
// 转成map
profileInfo := map[string]interface{}{}
Log(string(content))
err2 = json.Unmarshal(content, &profileInfo)
if err2 != nil {
c.RenderArgs["title"] = "error"
return c.RenderTemplate("oauth/oauth_callback_error.html")
}
usernameI := profileInfo["login"]
username, _ := usernameI.(string)
userId := username
// 注册
isExists, userInfo := authService.ThirdRegister("github", userId, username)
c.RenderArgs["isExists"] = isExists
c.RenderArgs["userInfo"] = userInfo
// 登录之
c.SetSession(userInfo)
return c.Redirect("/note")
}
+6 -6
View File
@@ -29,8 +29,8 @@ func (c User) Account(tab int) revel.Result {
// 修改用户名, 需要重置session // 修改用户名, 需要重置session
func (c User) UpdateUsername(username string) revel.Result { func (c User) UpdateUsername(username string) revel.Result {
re := info.NewRe(); re := info.NewRe()
if(c.GetUsername() == "demo") { if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
re.Msg = "cannotUpdateDemo" re.Msg = "cannotUpdateDemo"
return c.RenderRe(re); return c.RenderRe(re);
} }
@@ -48,8 +48,8 @@ func (c User) UpdateUsername(username string) revel.Result {
// 修改密码 // 修改密码
func (c User) UpdatePwd(oldPwd, pwd string) revel.Result { func (c User) UpdatePwd(oldPwd, pwd string) revel.Result {
re := info.NewRe(); re := info.NewRe()
if(c.GetUsername() == "demo") { if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
re.Msg = "cannotUpdateDemo" re.Msg = "cannotUpdateDemo"
return c.RenderRe(re); return c.RenderRe(re);
} }
@@ -94,9 +94,9 @@ func (c User) ReSendActiveEmail() revel.Result {
} }
// 修改Email发送激活邮箱 // 修改Email发送激活邮箱
func (c User) UpdateEmailSendActiveEmail(email string) revel.Result { func (c User) updateEmailSendActiveEmail(email, pwd string) revel.Result {
re := info.NewRe() re := info.NewRe()
if(c.GetUsername() == "demo") { if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
re.Msg = "cannotUpdateDemo" re.Msg = "cannotUpdateDemo"
return c.RenderJson(re); return c.RenderJson(re);
} }
@@ -46,7 +46,10 @@ func (c AdminEmail) Demo() revel.Result {
func (c AdminEmail) DoDemo(demoUsername, demoPassword string) revel.Result { func (c AdminEmail) DoDemo(demoUsername, demoPassword string) revel.Result {
re := info.NewRe() 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 == "" { if userInfo.UserId == "" {
re.Msg = "The User is Not Exists"; re.Msg = "The User is Not Exists";
return c.RenderJson(re) return c.RenderJson(re)
@@ -56,7 +56,11 @@ func (c AdminSetting) Demo() revel.Result {
func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result { func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
re := info.NewRe() 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 == "" { if userInfo.UserId == "" {
re.Msg = "The User is Not Exists"; re.Msg = "The User is Not Exists";
return c.RenderJson(re) return c.RenderJson(re)
@@ -69,15 +73,9 @@ func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
return c.RenderJson(re) return c.RenderJson(re)
} }
// ToImage func (c AdminSetting) ExportPdf(path string) revel.Result {
// 长微博的bin路径phantomJs
func (c AdminSetting) ToImage() revel.Result {
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
return c.RenderTemplate("admin/setting/toImage.html");
}
func (c AdminSetting) DoToImage(toImageBinPath string) revel.Result {
re := info.NewRe() re := info.NewRe()
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "toImageBinPath", toImageBinPath) re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "exportPdfBinPath", path)
return c.RenderJson(re) return c.RenderJson(re)
} }
+2 -2
View File
@@ -24,8 +24,8 @@ type ApiAuth struct {
func (c ApiAuth) Login(email, pwd string) revel.Result { func (c ApiAuth) Login(email, pwd string) revel.Result {
var msg = "" var msg = ""
userInfo := authService.Login(email, pwd) userInfo, err := authService.Login(email, pwd)
if userInfo.Email != "" { if err == nil {
token := bson.NewObjectId().Hex() token := bson.NewObjectId().Hex()
sessionService.SetUserId(token, userInfo.UserId.Hex()) sessionService.SetUserId(token, userInfo.UserId.Hex())
return c.RenderJson(info.AuthOk{Ok: true, Token: token, UserId: userInfo.UserId, Email: userInfo.Email, Username: userInfo.Username}) return c.RenderJson(info.AuthOk{Ok: true, Token: token, UserId: userInfo.UserId, Email: userInfo.Email, Username: userInfo.Username})
+10 -4
View File
@@ -74,7 +74,9 @@ func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg
} }
// 生成上传路径 // 生成上传路径
filePath := "files/" + userId + "/attachs" newGuid := NewGuid()
filePath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/attachs"
dir := revel.BasePath + "/" + filePath dir := revel.BasePath + "/" + filePath
err = os.MkdirAll(dir, 0755) err = os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
@@ -83,7 +85,7 @@ func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg
// 生成新的文件名 // 生成新的文件名
filename := handel.Filename filename := handel.Filename
_, ext := SplitFilename(filename) // .doc _, ext := SplitFilename(filename) // .doc
filename = NewGuid() + ext filename = newGuid + ext
toPath := dir + "/" + filename; toPath := dir + "/" + filename;
err = ioutil.WriteFile(toPath, data, 0777) err = ioutil.WriteFile(toPath, data, 0777)
if err != nil { if err != nil {
@@ -124,8 +126,12 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
return return
} }
defer file.Close() defer file.Close()
newGuid := NewGuid()
// 生成上传路径 // 生成上传路径
fileUrlPath := "files/" + c.getUserId() + "/images" userId := c.getUserId()
fileUrlPath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
dir := revel.BasePath + "/" + fileUrlPath dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755) err = os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
@@ -139,7 +145,7 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
return return
} }
filename = NewGuid() + ext filename = newGuid + ext
data, err := ioutil.ReadAll(file) data, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
return return
+96 -5
View File
@@ -9,6 +9,8 @@ import (
"regexp" "regexp"
"strings" "strings"
"time" "time"
"os/exec"
"os"
// "github.com/leanote/leanote/app/types" // "github.com/leanote/leanote/app/types"
// "io/ioutil" // "io/ioutil"
// "fmt" // "fmt"
@@ -70,6 +72,7 @@ func (c ApiNote) GetNotes(notebookId string) revel.Result {
func (c ApiNote) GetTrashNotes() revel.Result { func (c ApiNote) GetTrashNotes() 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(noteService.ToApiNotes(notes)) return c.RenderJson(noteService.ToApiNotes(notes))
} }
// get Note // get Note
@@ -335,8 +338,12 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
Abstract: noteOrContent.Abstract} Abstract: noteOrContent.Abstract}
// 通过内容得到Desc, abstract // 通过内容得到Desc, abstract
note.Desc = SubStringHTMLToRaw(noteContent.Content, 50) if noteOrContent.Abstract == "" {
noteContent.Abstract = SubStringHTML(noteContent.Content, 200, "") 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) note = noteService.AddNoteAndContentApi(note, noteContent, myUserId)
@@ -352,7 +359,7 @@ func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
noteOrContent.UpdatedTime = note.UpdatedTime noteOrContent.UpdatedTime = note.UpdatedTime
noteOrContent.UserId = c.getUserId() noteOrContent.UserId = c.getUserId()
noteOrContent.IsMarkdown = note.IsMarkdown noteOrContent.IsMarkdown = note.IsMarkdown
// 删除一些不要返回的 // 删除一些不要返回的, 删除Desc?
noteOrContent.Content = "" noteOrContent.Content = ""
noteOrContent.Abstract = "" noteOrContent.Abstract = ""
// apiNote := info.NoteToApiNote(note, noteOrContent.Files) // apiNote := info.NoteToApiNote(note, noteOrContent.Files)
@@ -494,8 +501,12 @@ func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
} }
if c.Has("Content") { if c.Has("Content") {
// 通过内容得到Desc, abstract // 通过内容得到Desc, 如果有Abstract, 则用Abstract生成Desc
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Content, 50) if noteOrContent.Abstract == "" {
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Content, 200)
} else {
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Abstract, 200)
}
} }
afterNoteUsn := 0 afterNoteUsn := 0
@@ -571,3 +582,83 @@ func (c ApiNote) GetHistories(noteId string) revel.Result {
return c.RenderJson(re) return c.RenderJson(re)
} }
*/ */
// 0.2 新增
// 导出成PDF
// test localhost:9000/api/note/exportPdf?noteId=554f07bf05fcd15fa9000000&token=562211dc99c37ba6a7000001
func (c ApiNote) ExportPdf(noteId string) revel.Result {
re := info.NewApiRe()
userId := c.getUserId()
if noteId == "" {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
noteUserId := note.UserId.Hex()
// 是否有权限
if noteUserId != userId {
// 是否是有权限协作的
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
}
// path 判断是否需要重新生成之
guid := NewGuid()
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
dir := revel.BasePath + "/" + fileUrlPath
if !MkdirAll(dir) {
re.Msg = "noDir"
return c.RenderJson(re)
}
filename := guid + ".pdf"
path := dir + "/" + filename
appKey, _ := revel.Config.String("app.secretLeanote")
if appKey == "" {
appKey, _ = revel.Config.String("app.secret")
}
// 生成之
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
// 默认路径
if binPath == "" {
binPath = "/usr/local/bin/wkhtmltopdf"
}
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
var cc string
if(note.IsMarkdown) {
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
} else {
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
}
cmd := exec.Command("/bin/sh", "-c", cc)
_, err := cmd.Output()
if err != nil {
re.Msg = "sysError"
return c.RenderJson(re)
}
file, err := os.Open(path)
if err != nil {
re.Msg = "sysError"
return c.RenderJson(re)
}
filenameReturn := note.Title
filenameReturn = FixFilename(filenameReturn)
if filenameReturn == "" {
filenameReturn = "Untitled.pdf"
} else {
filenameReturn += ".pdf"
}
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
}
+4 -3
View File
@@ -48,7 +48,7 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
"FindPasswordUpdate": true, "FindPasswordUpdate": true,
"Suggestion": true, "Suggestion": true,
}, },
"Note": map[string]bool{"ToImage": true}, "Note": map[string]bool{"ToPdf": true},
"Blog": map[string]bool{"Index": true, "Blog": map[string]bool{"Index": true,
"View": true, "View": true,
"AboutMe": 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}, "Oauth": map[string]bool{"GithubCallback": true},
"File": map[string]bool{"OutputImage": true, "OutputFile": 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 { func needValidate(controller, method string) bool {
@@ -146,9 +146,10 @@ func init() {
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Note{}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Note{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Share{}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Share{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &User{}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &User{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Album{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &File{}) revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &File{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Attach{}) 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.InterceptFunc(AuthInterceptor, revel.BEFORE, &NoteContentHistory{})
revel.OnAppStart(func() { revel.OnAppStart(func() {
@@ -460,7 +460,7 @@ func (c MemberBlog) ImportTheme() revel.Result {
var ext string var ext string
_, ext = SplitFilename(filename) _, ext = SplitFilename(filename)
if ext != ".zip" { if ext != ".zip" {
re.Msg = "请上传zip文件" re.Msg = "Please upload zip file"
return c.RenderJson(re) return c.RenderJson(re)
} }
@@ -472,7 +472,7 @@ func (c MemberBlog) ImportTheme() revel.Result {
// > 10M? // > 10M?
if len(data) > 10*1024*1024 { if len(data) > 10*1024*1024 {
re.Msg = "文件大于10M" re.Msg = "File is big than 10M"
return c.RenderJson(re) return c.RenderJson(re)
} }
+34
View File
@@ -6,6 +6,7 @@ import (
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2" "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"strings"
) )
// Init mgo and the common DAO // Init mgo and the common DAO
@@ -62,10 +63,26 @@ func Init(url, dbname string) {
config := revel.Config config := revel.Config
if url == "" { if url == "" {
url, ok = config.String("db.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 == "" { if dbname == "" {
dbname, _ = config.String("db.dbname") dbname, _ = config.String("db.dbname")
} }
// get db config from host, port, username, password
if !ok { if !ok {
host, _ := revel.Config.String("db.host") host, _ := revel.Config.String("db.host")
port, _ := revel.Config.String("db.port") port, _ := revel.Config.String("db.port")
@@ -338,3 +355,20 @@ func Err(err error) bool {
} }
return true 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("重连失败!!!! 警告")
}
}
}
+4 -2
View File
@@ -10,8 +10,8 @@ import (
// convert revel msg to js msg // convert revel msg to js msg
var msgBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/messages/" var msgBasePath = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/messages/"
var targetBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/public/js/i18n/" var targetBasePath = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/public/js/i18n/"
func parse(filename string) { func parse(filename string) {
file, err := os.Open(msgBasePath + filename) file, err := os.Open(msgBasePath + filename)
reader := bufio.NewReader(file) reader := bufio.NewReader(file)
@@ -84,6 +84,8 @@ function getMsg(key, data) {
func main() { func main() {
parse("msg.en") parse("msg.en")
parse("msg.zh") parse("msg.zh")
parse("msg.fr")
parse("blog.zh") parse("blog.zh")
parse("blog.en") parse("blog.en")
parse("blog.fr")
} }
+2 -2
View File
@@ -78,7 +78,7 @@ type ShareNotebook struct {
UserId bson.ObjectId `bson:"UserId"` UserId bson.ObjectId `bson:"UserId"`
ToUserId bson.ObjectId `bson:"ToUserId,omitempty"` ToUserId bson.ObjectId `bson:"ToUserId,omitempty"`
ToGroupId bson.ObjectId `bson:"ToGroupId,omitempty"` // 分享给的用户组 ToGroupId bson.ObjectId `bson:"ToGroupId,omitempty"` // 分享给的用户组
ToGroup Group `ToGroup,omitempty` // 仅仅为了显示, 不存储, 分组信息 ToGroup Group `ToGroup,omitempty` // 仅仅为了显示, 不存储, 分组信息
NotebookId bson.ObjectId `bson:"NotebookId"` NotebookId bson.ObjectId `bson:"NotebookId"`
Seq int `bson:"Seq"` // 排序 Seq int `bson:"Seq"` // 排序
Perm int `bson:"Perm"` // 权限, 其下所有notes 0只读, 1可修改 Perm int `bson:"Perm"` // 权限, 其下所有notes 0只读, 1可修改
@@ -138,7 +138,7 @@ type ShareNote struct {
UserId bson.ObjectId `bson:"UserId"` UserId bson.ObjectId `bson:"UserId"`
ToUserId bson.ObjectId `bson:"ToUserId,omitempty"` ToUserId bson.ObjectId `bson:"ToUserId,omitempty"`
ToGroupId bson.ObjectId `bson:"ToGroupId,omitempty"` // 分享给的用户组 ToGroupId bson.ObjectId `bson:"ToGroupId,omitempty"` // 分享给的用户组
ToGroup Group `ToGroup,omitempty` // 仅仅为了显示, 不存储, 分组信息 ToGroup Group `ToGroup,omitempty` // 仅仅为了显示, 不存储, 分组信息
NoteId bson.ObjectId `bson:"NoteId"` NoteId bson.ObjectId `bson:"NoteId"`
Perm int `bson:"Perm"` // 权限, 0只读, 1可修改 Perm int `bson:"Perm"` // 权限, 0只读, 1可修改
CreatedTime time.Time `CreatedTime` CreatedTime time.Time `CreatedTime`
+1 -1
View File
@@ -14,7 +14,7 @@ type Theme struct {
Version string `Version` Version string `Version`
Author string `Author` Author string `Author`
AuthorUrl string `AuthorUrl` AuthorUrl string `AuthorUrl`
Path string `Path` // 文件夹路径 Path string `Path` // 文件夹路径, public/upload/54d7620d99c37b030600002c/themes/54d867c799c37b533e000001
Info map[string]interface{} `Info` // 所有信息 Info map[string]interface{} `Info` // 所有信息
IsActive bool `IsActive` // 是否在用 IsActive bool `IsActive` // 是否在用
+7
View File
@@ -70,6 +70,13 @@ type UserAccount struct {
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小 MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
} }
// note主页需要
type UserAndBlogUrl struct {
User
BlogUrl string `BlogUrl`
PostUrl string `PostUrl`
}
// 用户与博客信息结合, 公开 // 用户与博客信息结合, 公开
type UserAndBlog struct { type UserAndBlog struct {
UserId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键 UserId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
+55 -8
View File
@@ -10,8 +10,6 @@ import (
"github.com/leanote/leanote/app/controllers/admin" "github.com/leanote/leanote/app/controllers/admin"
"github.com/leanote/leanote/app/controllers/member" "github.com/leanote/leanote/app/controllers/member"
_ "github.com/leanote/leanote/app/lea/binder" _ "github.com/leanote/leanote/app/lea/binder"
"github.com/leanote/leanote/app/lea/session"
"github.com/leanote/leanote/app/lea/memcache"
"github.com/leanote/leanote/app/lea/route" "github.com/leanote/leanote/app/lea/route"
"reflect" "reflect"
"fmt" "fmt"
@@ -33,11 +31,11 @@ func init() {
// AuthFilter, // Invoke the action. // AuthFilter, // Invoke the action.
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters. revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params. revel.ParamsFilter, // Parse parameters into Controller.Params.
// revel.SessionFilter, // Restore and write the session cookie. revel.SessionFilter, // Restore and write the session cookie.
// 使用SessionFilter标准版从cookie中得到sessionID, 然后通过MssessionFilter从Memcache中得到 // 使用SessionFilter标准版从cookie中得到sessionID, 然后通过MssessionFilter从Memcache中得到
// session, 之后MSessionFilter将session只存sessionID然后返回给SessionFilter返回到web // session, 之后MSessionFilter将session只存sessionID然后返回给SessionFilter返回到web
session.SessionFilter, // leanote session // session.SessionFilter, // leanote session
// session.MSessionFilter, // leanote memcache session // session.MSessionFilter, // leanote memcache session
revel.FlashFilter, // Restore and write the flash cookie. revel.FlashFilter, // Restore and write the flash cookie.
@@ -51,6 +49,18 @@ func init() {
revel.TemplateFuncs["raw"] = func(str string) template.HTML { revel.TemplateFuncs["raw"] = func(str string) template.HTML {
return template.HTML(str) return template.HTML(str)
} }
revel.TemplateFuncs["trim"] = func(str string) string {
str = strings.Trim(str, " ")
str = strings.Trim(str, " ")
str = strings.Trim(str, "\n")
str = strings.Trim(str, "&nbsp;")
// 以下两个空格不一样
str = strings.Trim(str, " ")
str = strings.Trim(str, " ")
return str
}
revel.TemplateFuncs["add"] = func(i int) string { revel.TemplateFuncs["add"] = func(i int) string {
i = i + 1; i = i + 1;
return fmt.Sprintf("%v", i) return fmt.Sprintf("%v", i)
@@ -145,8 +155,43 @@ func init() {
return template.HTML(tagStr) return template.HTML(tagStr)
} }
revel.TemplateFuncs["blogTagsForExport"] = func(renderArgs map[string]interface{}, tags []string) template.HTML {
if tags == nil || len(tags) == 0 {
return ""
}
tagStr := ""
lenTags := len(tags)
for i, tag := range tags {
str := tag
var classes = "label"
if InArray([]string{"red", "blue", "yellow", "green"}, tag) {
classes += " label-" + tag
} else {
classes += " label-default"
}
classes += " label-post"
tagStr += "<span class=\"" + classes + "\" >" + str + "</span>";
if i != lenTags - 1 {
tagStr += " "
}
}
return template.HTML(tagStr)
}
// 不用revel的msg
revel.TemplateFuncs["leaMsg"] = func(renderArgs map[string]interface{}, key string) template.HTML {
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
str := revel.Message(locale, key)
if strings.HasPrefix(str, "???") {
str = key
}
return template.HTML(str);
}
// lea++ // lea++
revel.TemplateFuncs["blogTagsLea"] = func(renderArgs map[string]interface{}, tags []string, isRecommend bool) template.HTML { revel.TemplateFuncs["blogTagsLea"] = func(renderArgs map[string]interface{}, tags []string, typeStr string) template.HTML {
if tags == nil || len(tags) == 0 { if tags == nil || len(tags) == 0 {
return "" return ""
} }
@@ -155,10 +200,12 @@ func init() {
lenTags := len(tags) lenTags := len(tags)
tagPostUrl := "http://lea.leanote.com/" tagPostUrl := "http://lea.leanote.com/"
if isRecommend { if typeStr == "recommend" {
tagPostUrl += "?tag="; tagPostUrl += "?tag=";
} else { } else if typeStr == "latest" {
tagPostUrl += "latest?tag="; tagPostUrl += "latest?tag=";
} else {
tagPostUrl += "subscription?tag=";
} }
for i, tag := range tags { for i, tag := range tags {
@@ -367,7 +414,7 @@ func init() {
// email配置 // email配置
InitEmail() InitEmail()
InitVd() InitVd()
memcache.InitMemcache() // session服务 // memcache.InitMemcache() // session服务
// 其它service // 其它service
service.InitService() service.InitService()
controllers.InitService() controllers.InitService()
+8
View File
@@ -56,6 +56,14 @@ func ClearDir(dir string) bool {
return true 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 // list dir's all file, return filenames
func ListDir(dir string) []string { func ListDir(dir string) []string {
f, err := os.Open(dir) f, err := os.Open(dir)
+22
View File
@@ -0,0 +1,22 @@
package lea
// 对比密码是否一致
// 因为之前密码是用md5加密的, 所以通过密码长度来判断
// rawPwd 原始, 用户输入的密码
func ComparePwd(rawPwd, dbPwd string) bool {
if len(dbPwd) == 32 {
return Md5(rawPwd) == dbPwd
}
hex := []byte(dbPwd)
return CompareHash(hex, rawPwd)
}
// 加密
func GenPwd(rawPwd string) string {
digest, err := GenerateHash(rawPwd)
if err != nil {
return ""
}
return string(digest)
}
+124 -45
View File
@@ -10,6 +10,9 @@ import (
"io" "io"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time" "time"
"strings"
"github.com/PuerkitoBio/goquery"
"bytes"
math_rand "math/rand" math_rand "math/rand"
) )
@@ -22,6 +25,22 @@ func Md5(s string) string {
return hex.EncodeToString(h.Sum(nil)) 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 // Guid
func NewGuid() string { func NewGuid() string {
b := make([]byte, 48) b := make([]byte, 48)
@@ -123,73 +142,48 @@ func ReplaceAll(oldStr, pattern, newStr string) string {
} }
// 获取纯文本 // 获取纯文本
func SubStringHTMLToRaw(param string, length int) (result string) { func SubStringHTMLToRaw(param string, length int) string {
if param == "" { if param == "" {
return "" return param
} }
result = ""
n := 0 n := 0
var temp rune // 中文问题, 用rune来解决 var temp rune // 中文问题, 用rune来解决
rStr := []rune(param) rStr := []rune(param)
lenStr := len(rStr)
isCode := false isCode := false
for i := 0; i < len(rStr); i++ {
resultRune := make([]rune, length)
// s := ""
for i := 0; i < lenStr; i++ {
temp = rStr[i] temp = rStr[i]
if temp == '<' { if temp == '<' {
isCode = true isCode = true
continue continue
} else if temp == '>' { } else if temp == '>' {
isCode = false isCode = false
result += " "; // 空格 resultRune[n] = ' ';
n++
if n >= length {
break
}
continue continue
} }
if !isCode { if !isCode {
result += string(temp) resultRune[n] = temp;
// s += string(temp)
n++ n++
if n >= length { if n >= length {
break break
} }
} }
} }
return result := string(resultRune[0:n])
return strings.Trim(result, " ")
} }
// 获取摘要, HTML // 自带方法补全html
func SubStringHTML(param string, length int, end string) string { func fixHtml(result string) string {
if param == "" {
return ""
}
// 先取出<pre></pre>占位..
result := ""
// 1
n := 0
var temp rune // 中文问题, 用rune来解决
isCode := false //是不是HTML代码
isHTML := false //是不是HTML特殊字符,如&nbsp;
rStr := []rune(param)
for i := 0; i < len(rStr); i++ {
temp = rStr[i]
if temp == '<' {
isCode = true
} else if temp == '&' {
isHTML = true
} else if temp == '>' && isCode {
n = n - 1
isCode = false
} else if temp == ';' && isHTML {
isHTML = false
}
if !isCode && !isHTML {
n = n + 1
}
result += string(temp)
if n >= length {
break
}
}
result += end
// 取出所有标签 // 取出所有标签
tempResult := ReplaceAll(result, "(>)[^<>]*(<?)", "$1$2") // 把标签中间的所有内容都去掉了 tempResult := ReplaceAll(result, "(>)[^<>]*(<?)", "$1$2") // 把标签中间的所有内容都去掉了
@@ -232,6 +226,71 @@ func SubStringHTML(param string, length int, end string) string {
return result return result
} }
// 获取摘要, HTML
func SubStringHTML(param string, length int, end string) string {
if param == "" {
return param
}
result := ""
rStr := []rune(param)
lenStr := len(rStr)
if lenStr <= length {
result = param
} else {
// 1
n := 0
var temp rune // 中文问题, 用rune来解决
isCode := false //是不是HTML代码
isHTML := false //是不是HTML特殊字符,如&nbsp;
var i = 0;
for ; i < lenStr; i++ {
temp = rStr[i]
if temp == '<' {
isCode = true
} else if temp == '&' {
isHTML = true
} else if temp == '>' && isCode {
// n = n - 1
isCode = false
} else if temp == ';' && isHTML {
isHTML = false
}
if !isCode && !isHTML {
n = n + 1
}
// 每一次都相加, 速度非常慢!, 重新分配内存, 7倍的差距
// result += string(temp)
if n >= length {
break
}
}
result = string(rStr[0:i])
if end != "" {
result += end
}
}
// 使用goquery来取出html, 为了补全html
htmlReader := bytes.NewBufferString(result)
dom, err1 := goquery.NewDocumentFromReader(htmlReader)
if err1 == nil {
html, _ := dom.Html()
html = strings.Replace(html, "<html><head></head><body>", "", 1)
html = strings.Replace(html, "</body></html>", "", 1)
// TODO 把style="float: left"去掉
return html
// 如果有错误, 则使用自己的方法补全, 有风险
} else {
return fixHtml(result)
}
}
// 是否是合格的密码 // 是否是合格的密码
func IsGoodPwd(pwd string) (bool, string) { func IsGoodPwd(pwd string) (bool, string) {
if pwd == "" { if pwd == "" {
@@ -248,7 +307,7 @@ func IsEmail(email string) bool {
if email == "" { if email == "" {
return false; return false;
} }
ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-z\-A-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,3}$`, email) ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[_a-z\-A-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,6}$`, email)
return ok return ok
} }
@@ -315,3 +374,23 @@ func InArray(arr []string, str string) bool {
} }
return false return false
} }
// 将名称的特殊字符去掉
func FixFilename(filename string) string {
if filename != "" {
// 把特殊字段给替换掉
// str := `life "%&()+,/:;<>=?@\|`
// . == \\.
// $ === \\$
reg, _ := regexp.Compile("\\.|/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\")
filename = reg.ReplaceAllString(filename, "-")
filename = strings.Trim(filename, "-") // 左右单独的-去掉
// 把空格替换成-
// filename = strings.Replace(filename, " ", "-", -1)
for strings.Index(filename, "--") >= 0 { // 防止出现连续的--
filename = strings.Replace(filename, "--", "-", -1)
}
return filename
}
return filename
}
+26
View File
@@ -0,0 +1,26 @@
// contains two cryptographic functions for both storing and comparing passwords.
package lea
import (
"golang.org/x/crypto/bcrypt"
)
// GenerateHash generates bcrypt hash from plaintext password
func GenerateHash(password string) ([]byte, error) {
hex := []byte(password)
hashedPassword, err := bcrypt.GenerateFromPassword(hex, 10)
if err != nil {
return hashedPassword, err
}
return hashedPassword, nil
}
// CompareHash compares bcrypt password with a plaintext one. Returns true if passwords match
// and false if they do not.
func CompareHash(digest []byte, password string) bool {
hex := []byte(password)
if err := bcrypt.CompareHashAndPassword(digest, hex); err == nil {
return true
}
return false
}
-66
View File
@@ -1,66 +0,0 @@
package memcache
import (
"github.com/robfig/gomemcache/memcache"
"encoding/json"
"strconv"
)
var client *memcache.Client
// onAppStart后调用
func InitMemcache() {
client = memcache.New("localhost:11211")
}
//------------
// map
func SetMap(key string, value map[string]string, expiration int32) {
// 把value转成byte
bytes, _ := json.Marshal(value)
if expiration == -1 {
expiration = 30 * 24 * 60 * 60 // 30天
}
client.Set(&memcache.Item{Key: key, Value: bytes, Expiration: expiration})
}
func GetMap(key string) map[string]string {
item, err := client.Get(key)
if err != nil {
return nil
}
m := map[string]string{}
json.Unmarshal(item.Value, &m)
return m
}
//------------
// string
func GetString(key string) string {
item, err := client.Get(key)
if err != nil {
return ""
}
return string(item.Value)
}
func SetString(key string, value string, expiration int32) {
if expiration == -1 {
expiration = 30 * 24 * 60 * 60 // 30天
}
client.Set(&memcache.Item{Key: key, Value: []byte(value), Expiration: expiration})
}
//-------------------------
// int, 是通过转成string来存的
func GetInt(key string) int {
str := GetString(key)
i, _ := strconv.Atoi(str)
return i
}
func SetInt(key string, value int, expiration int32) {
str := strconv.Itoa(value)
SetString(key, str, expiration)
}
+5 -1
View File
@@ -2,7 +2,7 @@ package route
import ( import (
"github.com/revel/revel" "github.com/revel/revel"
// "github.com/leanote/leanote/app/service" "github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea" // . "github.com/leanote/leanote/app/lea"
"net/url" "net/url"
"strings" "strings"
@@ -42,6 +42,10 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
} }
*/ */
if route.ControllerName != "Static" { if route.ControllerName != "Static" {
// 检查mongodb 是否lost
db.CheckMongoSessionLost()
// api设置 // api设置
// leanote.com/api/user/get => ApiUser::Get // leanote.com/api/user/get => ApiUser::Get
//* /api/login ApiAuth.Login, 这里的设置, 其实已经转成了ApiAuth了 //* /api/login ApiAuth.Login, 这里的设置, 其实已经转成了ApiAuth了
-38
View File
@@ -1,38 +0,0 @@
package session
import (
"github.com/revel/revel"
"github.com/leanote/leanote/app/lea/memcache"
. "github.com/leanote/leanote/app/lea"
)
// 使用filter
// 很巧妙就使用了memcache来处理session
// revel的session(cookie)只存sessionId, 其它信息存在memcache中
func MSessionFilter(c *revel.Controller, fc []revel.Filter) {
sessionId := c.Session.Id()
// 从memcache中得到cache, 赋给session
cache := revel.Session(memcache.GetMap(sessionId))
Log("memcache")
LogJ(cache)
if cache == nil {
cache = revel.Session{}
cache.Id()
}
c.Session = cache
// Make session vars available in templates as {{.session.xyz}}
c.RenderArgs["session"] = c.Session
fc[0](c, fc[1:])
// 再把session保存之
LogJ(c.Session)
memcache.SetMap(sessionId, c.Session, -1)
// 只留下sessionId
c.Session = revel.Session{revel.SESSION_ID_KEY: sessionId}
}
-208
View File
@@ -1,208 +0,0 @@
package session
import (
"github.com/revel/revel"
// . "github.com/leanote/leanote/app/lea"
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
// 主要修改revel的cookie, 设置Domain
// 为了使sub domain共享cookie
// cookie.domain = leanote.com
// A signed cookie (and thus limited to 4kb in size).
// Restriction: Keys may not have a colon in them.
type Session map[string]string
const (
SESSION_ID_KEY = "_ID"
TIMESTAMP_KEY = "_TS"
)
// expireAfterDuration is the time to live, in seconds, of a session cookie.
// It may be specified in config as "session.expires". Values greater than 0
// set a persistent cookie with a time to live as specified, and the value 0
// sets a session cookie.
var expireAfterDuration time.Duration
var cookieDomain = "" // life
func init() {
// Set expireAfterDuration, default to 30 days if no value in config
revel.OnAppStart(func() {
var err error
if expiresString, ok := revel.Config.String("session.expires"); !ok {
expireAfterDuration = 30 * 24 * time.Hour
} else if expiresString == "session" {
expireAfterDuration = 0
} else if expireAfterDuration, err = time.ParseDuration(expiresString); err != nil {
panic(fmt.Errorf("session.expires invalid: %s", err))
}
cookieDomain, _ = revel.Config.String("cookie.domain")
})
}
// Id retrieves from the cookie or creates a time-based UUID identifying this
// session.
func (s Session) Id() string {
if sessionIdStr, ok := s[SESSION_ID_KEY]; ok {
return sessionIdStr
}
buffer := make([]byte, 32)
if _, err := rand.Read(buffer); err != nil {
panic(err)
}
s[SESSION_ID_KEY] = hex.EncodeToString(buffer)
return s[SESSION_ID_KEY]
}
// getExpiration return a time.Time with the session's expiration date.
// If previous session has set to "session", remain it
func (s Session) getExpiration() time.Time {
if expireAfterDuration == 0 || s[TIMESTAMP_KEY] == "session" {
// Expire after closing browser
return time.Time{}
}
return time.Now().Add(expireAfterDuration)
}
// cookie returns an http.Cookie containing the signed session.
func (s Session) cookie() *http.Cookie {
var sessionValue string
ts := s.getExpiration()
s[TIMESTAMP_KEY] = getSessionExpirationCookie(ts)
for key, value := range s {
if strings.ContainsAny(key, ":\x00") {
panic("Session keys may not have colons or null bytes")
}
if strings.Contains(value, "\x00") {
panic("Session values may not have null bytes")
}
sessionValue += "\x00" + key + ":" + value + "\x00"
}
sessionData := url.QueryEscape(sessionValue)
cookie := http.Cookie{
Name: revel.CookiePrefix + "_SESSION",
Value: revel.Sign(sessionData) + "-" + sessionData,
Path: "/",
HttpOnly: revel.CookieHttpOnly,
Secure: revel.CookieSecure,
Expires: ts.UTC(),
}
if cookieDomain != "" {
cookie.Domain = cookieDomain
}
return &cookie
}
// sessionTimeoutExpiredOrMissing returns a boolean of whether the session
// cookie is either not present or present but beyond its time to live; i.e.,
// whether there is not a valid session.
func sessionTimeoutExpiredOrMissing(session Session) bool {
if exp, present := session[TIMESTAMP_KEY]; !present {
return true
} else if exp == "session" {
return false
} else if expInt, _ := strconv.Atoi(exp); int64(expInt) < time.Now().Unix() {
return true
}
return false
}
// getSessionFromCookie returns a Session struct pulled from the signed
// session cookie.
func getSessionFromCookie(cookie *http.Cookie) Session {
session := make(Session)
// Separate the data from the signature.
hyphen := strings.Index(cookie.Value, "-")
if hyphen == -1 || hyphen >= len(cookie.Value)-1 {
return session
}
sig, data := cookie.Value[:hyphen], cookie.Value[hyphen+1:]
// Verify the signature.
if !revel.Verify(data, sig) {
revel.INFO.Println("Session cookie signature failed")
return session
}
revel.ParseKeyValueCookie(data, func(key, val string) {
session[key] = val
})
if sessionTimeoutExpiredOrMissing(session) {
session = make(Session)
}
return session
}
// SessionFilter is a Revel Filter that retrieves and sets the session cookie.
// Within Revel, it is available as a Session attribute on Controller instances.
// The name of the Session cookie is set as CookiePrefix + "_SESSION".
func SessionFilter(c *revel.Controller, fc []revel.Filter) {
session := restoreSession(c.Request.Request)
// c.Session, 重新生成一个revel.Session给controller!!!
// Log("sessoin--------")
// LogJ(session)
revelSession := revel.Session(session) // 强制转换 还是同一个对象, 但有个问题, 这样Session.Id()方法是用revel的了
c.Session = revelSession
// 生成sessionId
c.Session.Id()
sessionWasEmpty := len(c.Session) == 0
// Make session vars available in templates as {{.session.xyz}}
c.RenderArgs["session"] = c.Session
fc[0](c, fc[1:])
// Store the signed session if it could have changed.
if len(c.Session) > 0 || !sessionWasEmpty {
// 转换成lea.Session
session = Session(c.Session)
c.SetCookie(session.cookie())
}
}
// restoreSession returns either the current session, retrieved from the
// session cookie, or a new session.
func restoreSession(req *http.Request) Session {
cookie, err := req.Cookie(revel.CookiePrefix + "_SESSION")
if err != nil {
return make(Session)
} else {
return getSessionFromCookie(cookie)
}
}
// getSessionExpirationCookie retrieves the cookie's time to live as a
// string of either the number of seconds, for a persistent cookie, or
// "session".
func getSessionExpirationCookie(t time.Time) string {
if t.IsZero() {
return "session"
}
return strconv.FormatInt(t.Unix(), 10)
}
// SetNoExpiration sets session to expire when browser session ends
func (s Session) SetNoExpiration() {
s[TIMESTAMP_KEY] = "session"
}
// SetDefaultExpiration sets session to expire after default duration
func (s Session) SetDefaultExpiration() {
delete(s, TIMESTAMP_KEY)
}
+16 -20
View File
@@ -6,6 +6,7 @@ import (
"os/exec" "os/exec"
"io/ioutil" "io/ioutil"
"strings" "strings"
// "time"
) )
/* /*
@@ -32,7 +33,7 @@ import (
//var jss = []string{"js/jquery-cookie", "js/bootstrap"} //var jss = []string{"js/jquery-cookie", "js/bootstrap"}
var jss = []string{"js/jquery-cookie", "js/bootstrap", var jss = []string{"js/jquery-cookie", "js/bootstrap",
"js/common", "js/app/note", "js/app/tag", "js/app/notebook", "js/app/share", "js/common", "js/app/note", "js/app/tag", "js/app/notebook", "js/app/share",
"js/object_id"} "js/object_id", "js/ZeroClipboard/ZeroClipboard"}
var base1 = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/" var 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 base = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/public/"
@@ -91,7 +92,6 @@ func dev() {
"notebook.js": "notebook-min.js", "notebook.js": "notebook-min.js",
"share.js": "share-min.js", "share.js": "share-min.js",
"tag.js": "tag-min.js", "tag.js": "tag-min.js",
"main.js": "main-min.js",
"jquery.slimscroll.js": "jquery.slimscroll-min.js", "jquery.slimscroll.js": "jquery.slimscroll-min.js",
"jquery.contextmenu.js": "jquery.contextmenu-min.js", "jquery.contextmenu.js": "jquery.contextmenu-min.js",
"editor/editor.js": "editor/editor-min.js", "editor/editor.js": "editor/editor-min.js",
@@ -107,6 +107,13 @@ func dev() {
for key, value := range m { for key, value := range m {
content = strings.Replace(content, key, value, -1) 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) ioutil.WriteFile(target, []byte(content), os.ModeAppend)
} }
@@ -114,8 +121,10 @@ func dev() {
func tinymce() { 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]" // 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", "bundle[themes:modern,plugins:table,paste,advlist,autolink,link,image,lists,charmap,hr,searchreplace,visualblocks,visualchars,code,nav,tabfocus,contextmenu,directionality,codemirror,codesyntax,textcolor,fullpage]")
cmd := exec.Command("/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master/node_modules/jake/bin/cli.js", "minify") cmd := exec.Command("/bin/sh", "-c", "grunt minify");
cmd.Dir = "/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master" 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 := exec.Command("/bin/sh", "-c", "rm " + cmd.Dir + "/js/tinymce/tinymce.dev.js")
@@ -123,32 +132,19 @@ func tinymce() {
cmd2 = exec.Command("/bin/sh", "-c", "rm " + cmd.Dir + "/js/tinymce/tinymce.jquery.dev.js") cmd2 = exec.Command("/bin/sh", "-c", "rm " + cmd.Dir + "/js/tinymce/tinymce.jquery.dev.js")
c, _ := cmd2.CombinedOutput() c, _ := cmd2.CombinedOutput()
fmt.Println(string(c)) fmt.Println(string(c))
c, _ = cmd.CombinedOutput() c, _ = cmd.CombinedOutput()
fmt.Println(string(c)) fmt.Println(string(c))
} }
func main() { func main() {
// 压缩tinymce // 压缩tinymce
tinymce() // tinymce()
dev(); dev();
// 其它零散的需要压缩的js // 其它零散的需要压缩的js
otherJss := []string{"tinymce/tinymce", "js/main", "js/app/page", "js/contextmenu/jquery.contextmenu", otherJss := []string{"js/main", "js/app/page", "js/contextmenu/jquery.contextmenu",
"mdeditor/editor/scrollLink",
"mdeditor/editor/editor",
"mdeditor/editor/jquery.waitforimages",
"mdeditor/editor/pagedown/local/Markdown.local.zh",
"mdeditor/editor/pagedown/local/Markdown.local.en",
"mdeditor/editor/pagedown/Markdown.Editor",
"mdeditor/editor/pagedown/Markdown.Sanitizer",
"mdeditor/editor/pagedown/Markdown.Converter",
"mdeditor/editor/Markdown.Extra",
"mdeditor/editor/underscore",
"mdeditor/editor/mathJax",
"js/jQuery-slimScroll-1.3.0/jquery.slimscroll",
"js/app/editor_drop_paste",
"js/app/attachment_upload",
"js/jquery.ztree.all-3.5", "js/jquery.ztree.all-3.5",
"js/jQuery-slimScroll-1.3.0/jquery.slimscroll", "js/jQuery-slimScroll-1.3.0/jquery.slimscroll",
} }
+11 -2
View File
@@ -63,11 +63,20 @@ func (this *AttachService) updateNoteAttachNum(noteId bson.ObjectId, addNum int)
// list attachs // list attachs
func (this *AttachService) ListAttachs(noteId, userId string) []info.Attach { func (this *AttachService) ListAttachs(noteId, userId string) []info.Attach {
attachs := []info.Attach{} attachs := []info.Attach{}
// 判断是否有权限为笔记添加附件
if !shareService.HasUpdateNotePerm(noteId, userId) { // 判断是否有权限为笔记添加附件, userId为空时表示是分享笔记的附件
if userId != "" && !shareService.HasUpdateNotePerm(noteId, userId) {
return attachs return attachs
} }
// 笔记是否是自己的
note := noteService.GetNoteByIdAndUserId(noteId, userId)
if note.NoteId == "" {
return attachs
}
// TODO 这里, 优化权限控制
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs) db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)
return attachs return attachs
+38 -30
View File
@@ -2,26 +2,31 @@ package service
import ( import (
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
// "github.com/leanote/leanote/app/db" // "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
// "github.com/revel/revel" // "github.com/revel/revel"
"strings" "errors"
. "github.com/leanote/leanote/app/lea"
"fmt" "fmt"
. "github.com/leanote/leanote/app/lea"
"strconv" "strconv"
"strings"
) )
// 登录与权限 // 登录与权限 Login & Register
type AuthService struct { type AuthService struct {
} }
// pwd已md5了 // 使用bcrypt认证或者Md5认证
func (this *AuthService) Login(emailOrUsername, pwd string) info.User { // Use bcrypt (Md5 depreciated)
func (this *AuthService) Login(emailOrUsername, pwd string) (info.User, error) {
emailOrUsername = strings.Trim(emailOrUsername, " ") emailOrUsername = strings.Trim(emailOrUsername, " ")
// pwd = strings.Trim(pwd, " ") // pwd = strings.Trim(pwd, " ")
userInfo := userService.LoginGetUserInfo(emailOrUsername, Md5(pwd)) userInfo := userService.GetUserInfoByName(emailOrUsername)
return userInfo if userInfo.UserId == "" || !ComparePwd(pwd, userInfo.Pwd) {
return userInfo, errors.New("wrong username or password")
}
return userInfo, nil
} }
// 注册 // 注册
@@ -40,32 +45,35 @@ func (this *AuthService) Register(email, pwd, fromUserId string) (bool, string)
if userService.IsExistsUser(email) { if userService.IsExistsUser(email) {
return false, "userHasBeenRegistered-" + 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) { if fromUserId != "" && IsObjectId(fromUserId) {
user.FromUserId = bson.ObjectIdHex(fromUserId) user.FromUserId = bson.ObjectIdHex(fromUserId)
} }
LogJ(user)
return this.register(user) return this.register(user)
} }
func (this *AuthService) register(user info.User) (bool, string) { func (this *AuthService) register(user info.User) (bool, string) {
if userService.AddUser(user) { if userService.AddUser(user) {
// 添加笔记本, 生活, 学习, 工作 // 添加笔记本, 生活, 学习, 工作
userId := user.UserId.Hex(); userId := user.UserId.Hex()
notebook := info.Notebook{ notebook := info.Notebook{
Seq: -1, Seq: -1,
UserId: user.UserId} UserId: user.UserId}
title2Id := map[string]bson.ObjectId{"life": bson.NewObjectId(), "study": bson.NewObjectId(), "work": bson.NewObjectId()} title2Id := map[string]bson.ObjectId{"life": bson.NewObjectId(), "study": bson.NewObjectId(), "work": bson.NewObjectId()}
for title, objectId := range title2Id { for title, objectId := range title2Id {
notebook.Title = title notebook.Title = title
notebook.NotebookId = objectId notebook.NotebookId = objectId
notebook.UserId = user.UserId notebook.UserId = user.UserId
notebookService.AddNotebook(notebook); notebookService.AddNotebook(notebook)
} }
// 添加leanote -> 该用户的共享 // 添加leanote -> 该用户的共享
registerSharedUserId := configService.GetGlobalStringConfig("registerSharedUserId") registerSharedUserId := configService.GetGlobalStringConfig("registerSharedUserId")
if(registerSharedUserId != "") { if registerSharedUserId != "" {
registerSharedNotebooks := configService.GetGlobalArrMapConfig("registerSharedNotebooks") registerSharedNotebooks := configService.GetGlobalArrMapConfig("registerSharedNotebooks")
registerSharedNotes := configService.GetGlobalArrMapConfig("registerSharedNotes") registerSharedNotes := configService.GetGlobalArrMapConfig("registerSharedNotes")
registerCopyNoteIds := configService.GetGlobalArrayConfig("registerCopyNoteIds") registerCopyNoteIds := configService.GetGlobalArrayConfig("registerCopyNoteIds")
@@ -73,21 +81,21 @@ func (this *AuthService) register(user info.User) (bool, string) {
// 添加共享笔记本 // 添加共享笔记本
for _, notebook := range registerSharedNotebooks { for _, notebook := range registerSharedNotebooks {
perm, _ := strconv.Atoi(notebook["perm"]) perm, _ := strconv.Atoi(notebook["perm"])
shareService.AddShareNotebookToUserId(notebook["notebookId"], perm, registerSharedUserId, userId); shareService.AddShareNotebookToUserId(notebook["notebookId"], perm, registerSharedUserId, userId)
} }
// 添加共享笔记 // 添加共享笔记
for _, note := range registerSharedNotes { for _, note := range registerSharedNotes {
perm, _ := strconv.Atoi(note["perm"]) perm, _ := strconv.Atoi(note["perm"])
shareService.AddShareNoteToUserId(note["noteId"], perm, registerSharedUserId, userId); shareService.AddShareNoteToUserId(note["noteId"], perm, registerSharedUserId, userId)
} }
// 复制笔记 // 复制笔记
for _, noteId := range registerCopyNoteIds { for _, noteId := range registerCopyNoteIds {
note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex()); note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex())
// Log(noteId) // Log(noteId)
// Log("Copy") // Log("Copy")
// LogJ(note) // LogJ(note)
noteUpdate := bson.M{"IsBlog": false} // 不要是博客 noteUpdate := bson.M{"IsBlog": false} // 不要是博客
noteService.UpdateNote(user.UserId.Hex(), note.NoteId.Hex(), noteUpdate, -1) noteService.UpdateNote(user.UserId.Hex(), note.NoteId.Hex(), noteUpdate, -1)
} }
@@ -96,11 +104,11 @@ func (this *AuthService) register(user info.User) (bool, string) {
//--------------- //---------------
// 添加一条userBlog // 添加一条userBlog
blogService.UpdateUserBlog(info.UserBlog{UserId: user.UserId, blogService.UpdateUserBlog(info.UserBlog{UserId: user.UserId,
Title: user.Username + " 's Blog", Title: user.Username + " 's Blog",
SubTitle: "Love Leanote!", SubTitle: "Love Leanote!",
AboutMe: "Hello, I am (^_^)", AboutMe: "Hello, I am (^_^)",
CanComment: true, CanComment: true,
}) })
// 添加一个单页面 // 添加一个单页面
blogService.AddOrUpdateSingle(user.UserId.Hex(), "", "About Me", "Hello, I am (^_^)") blogService.AddOrUpdateSingle(user.UserId.Hex(), "", "About Me", "Hello, I am (^_^)")
} }
@@ -115,7 +123,7 @@ func (this *AuthService) register(user info.User) (bool, string) {
func (this *AuthService) getUsername(thirdType, thirdUsername string) (username string) { func (this *AuthService) getUsername(thirdType, thirdUsername string) (username string) {
username = thirdType + "-" + thirdUsername username = thirdType + "-" + thirdUsername
i := 1 i := 1
for ;; { for {
if !userService.IsExistsUserByUsername(username) { if !userService.IsExistsUserByUsername(username) {
return return
} }
@@ -132,10 +140,10 @@ func (this *AuthService) ThirdRegister(thirdType, thirdUserId, thirdUsername str
username := this.getUsername(thirdType, thirdUsername) username := this.getUsername(thirdType, thirdUsername)
userInfo = info.User{UserId: bson.NewObjectId(), userInfo = info.User{UserId: bson.NewObjectId(),
Username: username, Username: username,
ThirdUserId: thirdUserId, ThirdUserId: thirdUserId,
ThirdUsername: thirdUsername, ThirdUsername: thirdUsername,
} }
_, _ = this.register(userInfo) _, _ = this.register(userInfo)
return return
} }
+29 -16
View File
@@ -45,7 +45,9 @@ func (this *BlogService) GetBlogByIdAndUrlTitle(userId string, noteIdOrUrlTitle
return this.GetBlog(noteIdOrUrlTitle) return this.GetBlog(noteIdOrUrlTitle)
} }
note := info.Note{} note := info.Note{}
db.GetByQ(db.Notes, bson.M{"UserId": bson.ObjectIdHex(userId), "UrlTitle": encodeValue(noteIdOrUrlTitle), "IsBlog": true, "IsTrash": false}, &note) db.GetByQ(db.Notes, bson.M{"UserId": bson.ObjectIdHex(userId), "UrlTitle": encodeValue(noteIdOrUrlTitle),
"IsBlog": true,
"IsTrash": false, "IsDeleted": false}, &note)
return this.GetBlogItem(note) 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) { func (this *BlogService) GetBlogItem(note info.Note) (blog info.BlogItem) {
if note.NoteId == "" || !note.IsBlog { if note.NoteId == "" || !note.IsBlog {
return return info.BlogItem{}
} }
// 内容 // 内容
@@ -131,7 +133,8 @@ func (this *BlogService) ListBlogs(userId, notebookId string, page, pageSize int
func (this *BlogService) GetBlogTags(userId string) []info.TagCount { func (this *BlogService) GetBlogTags(userId string) []info.TagCount {
// 得到所有博客 // 得到所有博客
tagCounts := []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) db.TagCounts.Find(query).Sort("-Count").All(&tagCounts)
return tagCounts return tagCounts
} }
@@ -142,7 +145,7 @@ func (this *BlogService) ReCountBlogTags(userId string) bool {
// 得到所有博客 // 得到所有博客
notes := []info.Note{} notes := []info.Note{}
userIdO := bson.ObjectIdHex(userId) userIdO := bson.ObjectIdHex(userId)
query := bson.M{"UserId": userIdO, "IsTrash": false, "IsBlog": true} query := bson.M{"UserId": userIdO, "IsTrash": false, "IsDeleted": false, "IsBlog": true}
db.ListByQWithFields(db.Notes, query, []string{"Tags"}, &notes) db.ListByQWithFields(db.Notes, query, []string{"Tags"}, &notes)
db.DeleteAll(db.TagCounts, bson.M{"UserId": userIdO, "IsBlog": true}) db.DeleteAll(db.TagCounts, bson.M{"UserId": userIdO, "IsBlog": true})
@@ -184,7 +187,7 @@ Posts: []
*/ */
func (this *BlogService) ListBlogsArchive(userId, notebookId string, year, month int, sortField string, isAsc bool) []info.Archive { 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); // _, 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 != "" { if notebookId != "" {
q["NotebookId"] = bson.ObjectIdHex(notebookId) q["NotebookId"] = bson.ObjectIdHex(notebookId)
} }
@@ -291,9 +294,10 @@ func (this *BlogService) SearchBlogByTags(tags []string, userId string, pageNumb
// 不是trash的 // 不是trash的
query := bson.M{"UserId": bson.ObjectIdHex(userId), query := bson.M{"UserId": bson.ObjectIdHex(userId),
"IsTrash": false, "IsTrash": false,
"IsBlog": true, "IsDeleted": false,
"Tags": bson.M{"$all": tags}} "IsBlog": true,
"Tags": bson.M{"$all": tags}}
q := db.Notes.Find(query) q := db.Notes.Find(query)
@@ -366,10 +370,10 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
if !isAsc { if !isAsc {
// 降序 // 降序
/* /*
------- pre ------- pre
----- now ----- now
--- next --- next
-- --
*/ */
// 上一篇时间要比它大, 找最小的 // 上一篇时间要比它大, 找最小的
sortFieldT1 = bson.M{"$gte": baseTime} // 为什么要相等, 因为将notebook发布成博客, 会统一修改note的publicTime, 此时所有notes都一样 sortFieldT1 = bson.M{"$gte": baseTime} // 为什么要相等, 因为将notebook发布成博客, 会统一修改note的publicTime, 此时所有notes都一样
@@ -393,12 +397,14 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
sortFieldR2 = sorterField sortFieldR2 = sorterField
} }
// 1
// 上一篇, 比基时间要小, 但是是最后一篇, 所以是降序 // 上一篇, 比基时间要小, 但是是最后一篇, 所以是降序
note := info.Note{} note := info.Note{}
query := bson.M{"UserId": userIdO, query := bson.M{"UserId": userIdO,
"IsTrash": false, "IsTrash": false,
"IsBlog": true, "IsDeleted": false,
"_id": bson.M{"$ne": bson.ObjectIdHex(noteId)}, "IsBlog": true,
"_id": bson.M{"$ne": bson.ObjectIdHex(noteId)},
sorterField: sortFieldT1, sorterField: sortFieldT1,
} }
q := db.Notes.Find(query) q := db.Notes.Find(query)
@@ -430,7 +436,7 @@ func (this *BlogService) ListAllBlogs(userId, tag string, keywords string, isRec
skipNum, sortFieldR := parsePageAndSort(page, pageSize, sorterField, isAsc) skipNum, sortFieldR := parsePageAndSort(page, pageSize, sorterField, isAsc)
// 不是trash的 // 不是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 != "" { if tag != "" {
query["Tags"] = bson.M{"$in": []string{tag}} query["Tags"] = bson.M{"$in": []string{tag}}
} }
@@ -496,6 +502,9 @@ func (this *BlogService) ListAllBlogs(userId, tag string, keywords string, isRec
content = noteContent.Abstract content = noteContent.Abstract
} }
*/ */
if len(note.Tags) == 1 && note.Tags[0] == "" {
note.Tags = nil
}
blogs[i] = info.BlogItem{note, "", content, hasMore, userMap[note.UserId]} blogs[i] = info.BlogItem{note, "", content, hasMore, userMap[note.UserId]}
} }
pageInfo = info.NewPage(page, pageSize, count, nil) pageInfo = info.NewPage(page, pageSize, count, nil)
@@ -1197,6 +1206,10 @@ func (this *BlogService) FixBlog(blog info.BlogItem) info.Post {
LikeNum: blog.LikeNum, LikeNum: blog.LikeNum,
IsMarkdown: blog.IsMarkdown, IsMarkdown: blog.IsMarkdown,
} }
if blog2.Tags != nil && len(blog2.Tags) > 0 && blog2.Tags[0] != "" {
} else {
blog2.Tags = nil
}
return blog2 return blog2
} }
+15 -1
View File
@@ -455,12 +455,15 @@ var port string
func init() { func init() {
revel.OnAppStart(func() { revel.OnAppStart(func() {
/*
不用配置的, 因为最终通过命令可以改, 而且有的使用nginx代理
port = strconv.Itoa(revel.HttpPort) port = strconv.Itoa(revel.HttpPort)
if port != "80" { if port != "80" {
port = ":" + port port = ":" + port
} else { } else {
port = ""; port = "";
} }
*/
siteUrl, _ := revel.Config.String("site.url") // 已包含:9000, http, 去掉成 leanote.com siteUrl, _ := revel.Config.String("site.url") // 已包含:9000, http, 去掉成 leanote.com
if strings.HasPrefix(siteUrl, "http://") { if strings.HasPrefix(siteUrl, "http://") {
@@ -469,6 +472,17 @@ func init() {
defaultDomain = siteUrl[len("https://"):] defaultDomain = siteUrl[len("https://"):]
schema = "https://" schema = "https://"
} }
// port localhost:9000
ports := strings.Split(defaultDomain, ":")
if len(ports) == 2 {
port = ports[1]
}
if port == "80" {
port = ""
} else {
port = ":" + port
}
}) })
} }
@@ -580,5 +594,5 @@ func (this *ConfigService) HomePageIsAdminsBlog() bool {
} }
func (this *ConfigService) GetVersion() string { func (this *ConfigService) GetVersion() string {
return "1.0-beta.4" return "1.3"
} }
+54 -3
View File
@@ -1,14 +1,18 @@
package service package service
import ( import (
"encoding/base64"
"fmt"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"github.com/revel/revel" "github.com/revel/revel"
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time" "io/ioutil"
"net/http"
"os" "os"
"strings" "strings"
"time"
) )
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001" const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
@@ -115,6 +119,53 @@ func (this *FileService) UpdateImage(userId, fileId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title) 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的访问权限 // userId是否具有fileId的访问权限
+82 -23
View File
@@ -32,11 +32,21 @@ func (this *GroupService) DeleteGroup(userId, groupId string) (ok bool, msg stri
return false, "groupHasUsers" 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)}) db.DeleteAll(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId)})
return db.DeleteByIdAndUserId(db.Groups, groupId, userId), "" return db.DeleteByIdAndUserId(db.Groups, groupId, userId), ""
// TODO 删除分组后, 在shareNote, shareNotebook中也要删除 // TODO 删除分组后, 在shareNote, shareNotebook中也要删除
} }
// 修改group标题 // 修改group标题
func (this *GroupService) UpdateGroupTitle(userId, groupId, title string) (ok bool) { func (this *GroupService) UpdateGroupTitle(userId, groupId, title string) (ok bool) {
return db.UpdateByIdAndUserIdField(db.Groups, groupId, userId, "Title", title) return db.UpdateByIdAndUserIdField(db.Groups, groupId, userId, "Title", title)
@@ -44,9 +54,14 @@ func (this *GroupService) UpdateGroupTitle(userId, groupId, title string) (ok bo
// 得到用户的所有分组(包括下的所有用户) // 得到用户的所有分组(包括下的所有用户)
func (this *GroupService) GetGroupsAndUsers(userId string) ([]info.Group) { func (this *GroupService) GetGroupsAndUsers(userId string) ([]info.Group) {
// 得到分组s /*
// 得到我的分组
groups := []info.Group{} groups := []info.Group{}
db.ListByQ(db.Groups, bson.M{"UserId": bson.ObjectIdHex(userId)}, &groups) db.ListByQ(db.Groups, bson.M{"UserId": bson.ObjectIdHex(userId)}, &groups)
*/
// 我的分组, 及我所属的分组
groups := this.GetGroupsContainOf(userId);
// 得到其下的用户 // 得到其下的用户
for i, group := range groups { for i, group := range groups {
group.Users = this.GetUsers(group.GroupId.Hex()) group.Users = this.GetUsers(group.GroupId.Hex())
@@ -62,31 +77,54 @@ func (this *GroupService) GetGroups(userId string) ([]info.Group) {
return groups return groups
} }
// 得到我的和我所属组的ids
func (this *GroupService) GetMineAndBelongToGroupIds(userId string) ([]bson.ObjectId) {
// 所属组
groupIds := this.GetBelongToGroupIds(userId)
// 获取包含此用户的组对象数组 m := map[bson.ObjectId]bool{}
func (this *GroupService) GetGroupsContainOf(userId string) []info.Group { for _, groupId := range groupIds {
groupIds := this.GetGroupIdsContainOf(userId) m[groupId] = true
groups := []info.Group{}
db.ListByQ(db.Groups, bson.M{"_id": bson.M{"$in": groupIds}}, &groups)
return groups
}
// 获取包含此用户的组Id数组
func (this *GroupService) GetGroupIdsContainOf(userId string) []bson.ObjectId {
groupUsers := []info.GroupUser{}
db.ListByQWithFields(db.GroupUsers, bson.M{"UserId": bson.ObjectIdHex(userId)}, []string{"GroupId"}, &groupUsers)
if len(groupUsers) == 0 {
return nil
} }
groupIds := make([]bson.ObjectId, len(groupUsers)) // 我的组
for i, each := range groupUsers { myGroups := this.GetGroups(userId)
groupIds[i] = each.GroupId
for _, group := range myGroups {
if !m[group.GroupId] {
groupIds = append(groupIds, group.GroupId)
}
} }
return groupIds 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用 // 得到分组, shareService用
func (this *GroupService) GetGroup(userId, groupId string) (info.Group) { func (this *GroupService) GetGroup(userId, groupId string) (info.Group) {
// 得到分组s // 得到分组s
@@ -129,10 +167,25 @@ func (this *GroupService) GetBelongToGroupIds(userId string) ([]bson.ObjectId) {
func (this *GroupService) isMyGroup(ownUserId, groupId string) (ok bool) { func (this *GroupService) isMyGroup(ownUserId, groupId string) (ok bool) {
return db.Has(db.Groups, bson.M{"_id": bson.ObjectIdHex(groupId), "UserId": bson.ObjectIdHex(ownUserId)}) 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添加用户 // 为group添加用户
// 用户是否已存在? // 用户是否已存在?
func (this *GroupService) AddUser(ownUserId, groupId, userId string) (ok bool, msg string) { func (this *GroupService) AddUser(ownUserId, groupId, userId string) (ok bool, msg string) {
// groupId是否是ownUserId的? // groupId是否是ownUserId的?
/*
if !this.IsExistsGroupUser(ownUserId, groupId) {
return false, "forbidden"
}
*/
if !this.isMyGroup(ownUserId, groupId) { if !this.isMyGroup(ownUserId, groupId) {
return false, "forbidden" return false, "forbidden"
} }
@@ -149,16 +202,22 @@ func (this *GroupService) AddUser(ownUserId, groupId, userId string) (ok bool, m
CreatedTime: time.Now(), CreatedTime: time.Now(),
}), "" }), ""
} }
// 删除用户 // 删除用户
func (this *GroupService) DeleteUser(ownUserId, groupId, userId string) (ok bool, msg string) { func (this *GroupService) DeleteUser(ownUserId, groupId, userId string) (ok bool, msg string) {
// groupId是否是ownUserId的? // groupId是否是ownUserId的?
/*
if !this.IsExistsGroupUser(ownUserId, groupId) {
return false, "forbidden"
}
*/
if !this.isMyGroup(ownUserId, groupId) { if !this.isMyGroup(ownUserId, groupId) {
return false, "forbidden" return false, "forbidden"
} }
// 删除该用户分享到本组的笔记本, 笔记
shareService.DeleteShareNotebookGroupWhenDeleteGroupUser(userId, groupId);
shareService.DeleteShareNoteGroupWhenDeleteGroupUser(userId, groupId);
return db.Delete(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId), "UserId": bson.ObjectIdHex(userId)}), "" return db.Delete(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId), "UserId": bson.ObjectIdHex(userId)}), ""
} }
// 判断组中是否包含指定用户
func (this *GroupService) IsExistsGroupUser(userId, groupId string) (ok bool) {
return db.Has(db.GroupUsers, bson.M{"UserId": bson.ObjectIdHex(userId), "GroupId": bson.ObjectIdHex(groupId)})
}
+35 -15
View File
@@ -73,17 +73,13 @@ func (this *NoteImageService) UpdateNoteImages(userId, noteId, imgSrc, content s
// 复制图片, 把note的图片都copy给我, 且修改noteContent图片路径 // 复制图片, 把note的图片都copy给我, 且修改noteContent图片路径
func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId, content, toUserId string) string { func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId, content, toUserId string) string {
/* 弃用之
// 得到fromNoteId的noteImages, 如果为空, 则直接返回content // 得到fromNoteId的noteImages, 如果为空, 则直接返回content
noteImages := []info.NoteImage{} noteImages := []info.NoteImage{}
db.ListByQWithFields(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(fromNoteId)}, []string{"ImageId"}, &noteImages) db.ListByQWithFields(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(fromNoteId)}, []string{"ImageId"}, &noteImages)
if len(noteImages) == 0 { if len(noteImages) == 0 {
return content; return content;
} }
// <img src="/file/outputImage?fileId=12323232" />
// 把fileId=1232替换成新的
replaceMap := map[string]string{}
for _, noteImage := range noteImages { for _, noteImage := range noteImages {
imageId := noteImage.ImageId.Hex() imageId := noteImage.ImageId.Hex()
ok, newImageId := fileService.CopyImage(fromUserId, imageId, toUserId) ok, newImageId := fileService.CopyImage(fromUserId, imageId, toUserId)
@@ -91,19 +87,43 @@ func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId,
replaceMap[imageId] = newImageId replaceMap[imageId] = newImageId
} }
} }
*/
if len(replaceMap) > 0 { // 因为很多图片上传就会删除, 所以直接从内容中查看图片id进行复制
// 替换之
reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})") // <img src="/file/outputImage?fileId=12323232" />
content = reg.ReplaceAllStringFunc(content, func(each string) string { // 把fileId=1232替换成新的
// each=outputImage?fileId=541bd2f599c37b4f3r000003 replaceMap := map[string]string{}
fileId := each[len(each)-24:] // 得到后24位, 也即id
if replaceFileId, ok := replaceMap[fileId]; ok { reg, _ := regexp.Compile("(outputImage|getImage)\\?fileId=([a-z0-9A-Z]{24})")
content = reg.ReplaceAllStringFunc(content, func(each string) string {
// each = outputImage?fileId=541bd2f599c37b4f3r000003
// each = getImage?fileId=541bd2f599c37b4f3r000003
fileId := each[len(each)-24:] // 得到后24位, 也即id
if _, ok := replaceMap[fileId]; !ok {
if bson.IsObjectIdHex(fileId) {
ok2, newImageId := fileService.CopyImage(fromUserId, fileId, toUserId)
if ok2 {
replaceMap[fileId] = newImageId
} else {
replaceMap[fileId] = ""
}
} else {
replaceMap[fileId] = ""
}
}
replaceFileId := replaceMap[fileId]
if replaceFileId != "" {
if each[0] == 'o' {
return "outputImage?fileId=" + replaceFileId return "outputImage?fileId=" + replaceFileId
} }
return each return "getImage?fileId=" + replaceFileId
}); }
} return each
});
return content; return content;
} }
+29 -10
View File
@@ -18,16 +18,29 @@ func (this *NoteService) GetNote(noteId, userId string) (note info.Note) {
return return
} }
// fileService调用 // fileService调用
// 不能是已经删除了的, life bug, 客户端删除后, 竟然还能在web上打开
func (this *NoteService) GetNoteById(noteId string) (note info.Note) { func (this *NoteService) GetNoteById(noteId string) (note info.Note) {
note = info.Note{} note = info.Note{}
db.Get(db.Notes, noteId, &note) if noteId == "" {
return
}
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsDeleted": false}, &note)
return
}
func (this *NoteService) GetNoteByIdAndUserId(noteId, userId string) (note info.Note) {
note = info.Note{}
if noteId == "" || userId == "" {
return
}
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "UserId": bson.ObjectIdHex(userId), "IsDeleted": false}, &note)
return return
} }
// 得到blog, blogService用 // 得到blog, blogService用
// 不要传userId, 因为是公开的 // 不要传userId, 因为是公开的
func (this *NoteService) GetBlogNote(noteId string) (note info.Note) { func (this *NoteService) GetBlogNote(noteId string) (note info.Note) {
note = info.Note{} note = info.Note{}
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsBlog": true, "IsTrash": false}, &note) db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId),
"IsBlog": true, "IsTrash": false, "IsDeleted": false}, &note)
return return
} }
// 通过id, userId得到noteContent // 通过id, userId得到noteContent
@@ -238,7 +251,6 @@ func (this *NoteService) AddNote(note info.Note, fromApi bool) info.Note {
// api会传IsBlog, web不会传 // api会传IsBlog, web不会传
if !fromApi { if !fromApi {
note.PublicTime = note.UpdatedTime
// 设为blog // 设为blog
note.IsBlog = notebookService.IsBlog(notebookId) note.IsBlog = notebookService.IsBlog(notebookId)
} }
@@ -418,7 +430,7 @@ func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bso
if isBlog, ok := needUpdate["IsBlog"]; ok { if isBlog, ok := needUpdate["IsBlog"]; ok {
isBlog2 := isBlog.(bool) isBlog2 := isBlog.(bool)
if note.IsBlog != isBlog2 { if note.IsBlog != isBlog2 {
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog2}) this.UpdateNoteContentIsBlog(noteId, userId, isBlog2);
// 重新发布成博客 // 重新发布成博客
if !note.IsBlog { if !note.IsBlog {
@@ -459,6 +471,11 @@ func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bso
return true, "", afterUsn return true, "", afterUsn
} }
// 当设置/取消了笔记为博客
func (this *NoteService) UpdateNoteContentIsBlog(noteId, userId string, isBlog bool) {
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog})
}
// 附件修改, 增加noteIncr // 附件修改, 增加noteIncr
func (this *NoteService) IncrNoteUsn(noteId, userId string) int { func (this *NoteService) IncrNoteUsn(noteId, userId string) int {
afterUsn := userService.IncrUsn(userId) afterUsn := userService.IncrUsn(userId)
@@ -572,6 +589,8 @@ func (this *NoteService) ToBlog(userId, noteId string, isBlog, isTop bool) bool
ok := db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, noteUpdate) ok := db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, noteUpdate)
// 重新计算tags // 重新计算tags
go (func() { go (func() {
this.UpdateNoteContentIsBlog(noteId, userId, isBlog);
blogService.ReCountBlogTags(userId) blogService.ReCountBlogTags(userId)
})() })()
return ok return ok
@@ -645,7 +664,7 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
note.NotebookId = bson.ObjectIdHex(notebookId) note.NotebookId = bson.ObjectIdHex(notebookId)
noteContent.NoteId = note.NoteId noteContent.NoteId = note.NoteId
this.AddNoteAndContent(note, noteContent, note.UserId); note = this.AddNoteAndContent(note, noteContent, note.UserId);
// 更新blog状态 // 更新blog状态
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId) isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId)
@@ -664,10 +683,9 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
// 复制别人的共享笔记给我 // 复制别人的共享笔记给我
// 将别人可用的图片转为我的图片, 复制图片 // 将别人可用的图片转为我的图片, 复制图片
func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId string) info.Note { func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId string) info.Note {
// Log(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId))
// 判断是否共享了给我 // 判断是否共享了给我
if notebookService.IsMyNotebook(notebookId, myUserId) && // Log(notebookService.IsMyNotebook(notebookId, myUserId))
(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) { if notebookService.IsMyNotebook(notebookId, myUserId) && shareService.HasReadPerm(fromUserId, myUserId, noteId) {
note := this.GetNote(noteId, fromUserId) note := this.GetNote(noteId, fromUserId)
if note.NoteId == "" { if note.NoteId == "" {
return info.Note{} return info.Note{}
@@ -734,6 +752,7 @@ func (this *NoteService) SearchNote(key, userId string, pageNumber, pageSize int
// 不是trash的 // 不是trash的
query := bson.M{"UserId": bson.ObjectIdHex(userId), query := bson.M{"UserId": bson.ObjectIdHex(userId),
"IsTrash": false, "IsTrash": false,
"IsDeleted": false, // 不能搜索已删除了的
"$or": orQ, "$or": orQ,
} }
if isBlog { if isBlog {
@@ -819,14 +838,14 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb
//------------ //------------
// 统计 // 统计
func (this *NoteService) CountNote(userId string) int { func (this *NoteService) CountNote(userId string) int {
q := bson.M{"IsTrash": false} q := bson.M{"IsTrash": false, "IsDeleted": false}
if userId != "" { if userId != "" {
q["UserId"] = bson.ObjectIdHex(userId) q["UserId"] = bson.ObjectIdHex(userId)
} }
return db.Count(db.Notes, q) return db.Count(db.Notes, q)
} }
func (this *NoteService) CountBlog(userId string) int { func (this *NoteService) CountBlog(userId string) int {
q := bson.M{"IsBlog": true, "IsTrash": false} q := bson.M{"IsBlog": true, "IsTrash": false, "IsDeleted": false}
if userId != "" { if userId != "" {
q["UserId"] = bson.ObjectIdHex(userId) q["UserId"] = bson.ObjectIdHex(userId)
} }
+12 -4
View File
@@ -8,6 +8,8 @@ import (
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"sort" "sort"
"time" "time"
"strings"
// "html"
) )
// 笔记本 // 笔记本
@@ -40,7 +42,9 @@ func ParseAndSortNotebooks(userNotebooks []info.Notebook, noParentDelete, needSo
for _, each := range userNotebooks { for _, each := range userNotebooks {
newNotebooks := info.Notebooks{Subs: info.SubNotebooks{}} newNotebooks := info.Notebooks{Subs: info.SubNotebooks{}}
newNotebooks.NotebookId = each.NotebookId newNotebooks.NotebookId = each.NotebookId
newNotebooks.Title = each.Title newNotebooks.Title = each.Title;
// newNotebooks.Title = html.EscapeString(each.Title)
newNotebooks.Title = strings.Replace(strings.Replace(each.Title, "<script>", "", -1), "</script", "", -1)
newNotebooks.Seq = each.Seq newNotebooks.Seq = each.Seq
newNotebooks.UserId = each.UserId newNotebooks.UserId = each.UserId
newNotebooks.ParentNotebookId = each.ParentNotebookId newNotebooks.ParentNotebookId = each.ParentNotebookId
@@ -271,11 +275,15 @@ func (this *NotebookService) ToBlog(userId, notebookId string, isBlog bool) (boo
// 查看是否有子notebook // 查看是否有子notebook
// 先查看该notebookId下是否有notes, 没有则删除 // 先查看该notebookId下是否有notes, 没有则删除
func (this *NotebookService) DeleteNotebook(userId, notebookId string) (bool, string) { func (this *NotebookService) DeleteNotebook(userId, notebookId string) (bool, string) {
if db.Count(db.Notebooks, bson.M{"ParentNotebookId": bson.ObjectIdHex(notebookId), if db.Count(db.Notebooks, bson.M{
"UserId": bson.ObjectIdHex(userId)}) == 0 { // 无 "ParentNotebookId": bson.ObjectIdHex(notebookId),
"UserId": bson.ObjectIdHex(userId),
"IsDeleted": false,
}) == 0 { // 无
if db.Count(db.Notes, bson.M{"NotebookId": bson.ObjectIdHex(notebookId), if db.Count(db.Notes, bson.M{"NotebookId": bson.ObjectIdHex(notebookId),
"UserId": bson.ObjectIdHex(userId), "UserId": bson.ObjectIdHex(userId),
"IsTrash": false}) == 0 { // 不包含trash "IsTrash": false,
"IsDeleted": false}) == 0 { // 不包含trash
// 不是真删除 1/20, 为了同步笔记本 // 不是真删除 1/20, 为了同步笔记本
ok := db.UpdateByQMap(db.Notebooks, bson.M{"_id": bson.ObjectIdHex(notebookId)}, bson.M{"IsDeleted": true, "Usn": userService.IncrUsn(userId)}) ok := db.UpdateByQMap(db.Notebooks, bson.M{"_id": bson.ObjectIdHex(notebookId)}, bson.M{"IsDeleted": true, "Usn": userService.IncrUsn(userId)})
return ok, "" return ok, ""
+6 -2
View File
@@ -45,9 +45,13 @@ func (this *PwdService) UpdatePwd(token, pwd string) (bool, string) {
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenPwd); !ok { if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenPwd); !ok {
return ok, msg return ok, msg
} }
digest, err := GenerateHash(pwd)
if err != nil {
return false,"GenerateHash error"
}
passwd := string(digest)
// 修改密码之 // 修改密码之
ok = db.UpdateByQField(db.Users, bson.M{"_id": tokenInfo.UserId}, "Pwd", Md5(pwd)) ok = db.UpdateByQField(db.Users, bson.M{"_id": tokenInfo.UserId}, "Pwd", passwd)
// 删除token // 删除token
tokenService.DeleteToken(tokenInfo.UserId.Hex(), info.TokenPwd) tokenService.DeleteToken(tokenInfo.UserId.Hex(), info.TokenPwd)
+51 -2
View File
@@ -33,8 +33,9 @@ type ShareService struct {
// 谁共享给了我的Query // 谁共享给了我的Query
func (this *ShareService) getOrQ(userId string) bson.M { func (this *ShareService) getOrQ(userId string) bson.M {
// 得到我参与的组织 // 得到我和和我参与的组织
groupIds := groupService.GetBelongToGroupIds(userId) groupIds := groupService.GetMineAndBelongToGroupIds(userId)
q := bson.M{} q := bson.M{}
if len(groupIds) > 0 { if len(groupIds) > 0 {
orQ := []bson.M{ orQ := []bson.M{
@@ -51,6 +52,8 @@ func (this *ShareService) getOrQ(userId string) bson.M {
// 得到共享给我的笔记本和用户(谁共享给了我) // 得到共享给我的笔记本和用户(谁共享给了我)
func (this *ShareService) GetShareNotebooks(userId string) (info.ShareNotebooksByUser, []info.User) { func (this *ShareService) GetShareNotebooks(userId string) (info.ShareNotebooksByUser, []info.User) {
myUserId := userId
// 得到共享给我的用户s信息 // 得到共享给我的用户s信息
// 得到我参与的组织 // 得到我参与的组织
q := this.getOrQ(userId) q := this.getOrQ(userId)
@@ -64,7 +67,15 @@ func (this *ShareService) GetShareNotebooks(userId string) (info.ShareNotebooksB
db.Distinct(db.ShareNotebooks, q, "UserId", &userIds2) // BUG之前是userId1, 2014/12/29 db.Distinct(db.ShareNotebooks, q, "UserId", &userIds2) // BUG之前是userId1, 2014/12/29
userIds := append(userIds1, userIds2...) userIds := append(userIds1, userIds2...)
userInfos := userService.GetUserInfosOrderBySeq(userIds); userInfos := userService.GetUserInfosOrderBySeq(userIds);
// 不要我的id
for i, userInfo := range userInfos {
if userInfo.UserId.Hex() == myUserId {
userInfos = append(userInfos[:i], userInfos[i+1:]...)
break;
}
}
//-------------------- //--------------------
// 得到他们共享给我的notebooks // 得到他们共享给我的notebooks
@@ -102,6 +113,11 @@ func (this *ShareService) GetShareNotebooks(userId string) (info.ShareNotebooksB
// 先建立userId => [] // 先建立userId => []
for _, eachSub := range subShareNotebooks { for _, eachSub := range subShareNotebooks {
userId := eachSub.Notebook.UserId userId := eachSub.Notebook.UserId
// 我自己的, 算了
if userId.Hex() == myUserId {
continue;
}
if _, ok := shareNotebooksByUsersMap[userId]; ok { if _, ok := shareNotebooksByUsersMap[userId]; ok {
shareNotebooksByUsersMap[userId] = append(shareNotebooksByUsersMap[userId], eachSub) shareNotebooksByUsersMap[userId] = append(shareNotebooksByUsersMap[userId], eachSub)
} else { } else {
@@ -779,3 +795,36 @@ func (this *ShareService) DeleteShareNotebookGroup(userId, notebookId, groupId s
"ToGroupId": bson.ObjectIdHex(groupId), "ToGroupId": bson.ObjectIdHex(groupId),
}); });
} }
//--------------------
// 删除组时, 删除所有的
//--------------------
func (this *ShareService) DeleteAllShareNotebookGroup(groupId string) bool {
return db.Delete(db.ShareNotebooks, bson.M{
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
func (this *ShareService) DeleteAllShareNoteGroup(groupId string) bool {
return db.Delete(db.ShareNotes, bson.M{
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
//--------------------
// 删除组内用户时, 删除其分享的
//--------------------
func (this *ShareService) DeleteShareNotebookGroupWhenDeleteGroupUser(userId, groupId string) bool {
return db.Delete(db.ShareNotebooks, bson.M{
"UserId": bson.ObjectIdHex(userId),
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
func (this *ShareService) DeleteShareNoteGroupWhenDeleteGroupUser(userId, groupId string) bool {
return db.Delete(db.ShareNotes, bson.M{
"UserId": bson.ObjectIdHex(userId),
"ToGroupId": bson.ObjectIdHex(groupId),
});
}
+12 -4
View File
@@ -85,13 +85,13 @@ func (this *ThemeService) getDefaultTheme(style string) info.Theme {
// 用户的主题路径设置 // 用户的主题路径设置
func (this *ThemeService) getUserThemeBasePath(userId string) string { func (this *ThemeService) getUserThemeBasePath(userId string) string {
return revel.BasePath + "/public/upload/" + userId + "/themes" return revel.BasePath + "/public/upload/" + Digest3(userId) + "/" + userId + "/themes"
} }
func (this *ThemeService) getUserThemePath(userId, themeId string) string { func (this *ThemeService) getUserThemePath(userId, themeId string) string {
return this.getUserThemeBasePath(userId) + "/" + themeId return this.getUserThemeBasePath(userId) + "/" + themeId
} }
func (this *ThemeService) getUserThemePath2(userId, themeId string) string { func (this *ThemeService) getUserThemePath2(userId, themeId string) string {
return "public/upload/" + userId + "/themes/" + themeId return "public/upload/" + Digest3(userId) + "/" + userId + "/themes/" + themeId
} }
// 新建主题 // 新建主题
@@ -412,10 +412,18 @@ func (this *ThemeService) ImportTheme(userId, path string) (ok bool, msg string)
themeIdO := bson.NewObjectId() themeIdO := bson.NewObjectId()
themeId := themeIdO.Hex() themeId := themeIdO.Hex()
targetPath := this.getUserThemePath(userId, themeId) // revel.BasePath + "/public/upload/" + userId + "/themes/" + themeId targetPath := this.getUserThemePath(userId, themeId) // revel.BasePath + "/public/upload/" + userId + "/themes/" + themeId
if ok, msg = archive.Unzip(path, targetPath); !ok {
DeleteFile(targetPath) err := os.MkdirAll(targetPath, 0755)
if err != nil {
msg = "error"
return return
} }
if ok, msg = archive.Unzip(path, targetPath); !ok {
DeleteFile(targetPath)
Log("oh no")
return
}
// 主题验证 // 主题验证
if ok, msg = this.ValidateTheme(targetPath, "", ""); !ok { if ok, msg = this.ValidateTheme(targetPath, "", ""); !ok {
DeleteFile(targetPath) DeleteFile(targetPath)
+10 -2
View File
@@ -25,6 +25,12 @@ type TrashService struct {
// 应该放在回收站里 // 应该放在回收站里
// 有trashService // 有trashService
func (this *TrashService) DeleteNote(noteId, userId string) bool { func (this *TrashService) DeleteNote(noteId, userId string) bool {
note := noteService.GetNote(noteId, userId);
// 如果是垃圾, 则彻底删除
if (note.IsTrash) {
return this.DeleteTrash(noteId, userId)
}
// 首先删除其共享 // 首先删除其共享
if shareService.DeleteShareNoteAll(noteId, userId) { if shareService.DeleteShareNoteAll(noteId, userId) {
// 更新note isTrash = true // 更新note isTrash = true
@@ -36,13 +42,15 @@ func (this *TrashService) DeleteNote(noteId, userId string) bool {
return true return true
} }
} }
return false return false
} }
// 删除别人共享给我的笔记 // 删除别人共享给我的笔记
// 先判断我是否有权限, 笔记是否是我创建的 // 先判断我是否有权限, 笔记是否是我创建的
func (this *TrashService) DeleteSharedNote(noteId, userId, myUserId string) bool { func (this *TrashService) DeleteSharedNote(noteId, myUserId string) bool {
note := noteService.GetNote(noteId, userId) note := noteService.GetNoteById(noteId)
userId := note.UserId.Hex()
if shareService.HasUpdatePerm(userId, myUserId, noteId) && note.CreatedUserId.Hex() == myUserId { if shareService.HasUpdatePerm(userId, myUserId, noteId) && note.CreatedUserId.Hex() == myUserId {
return db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true, "Usn": userService.IncrUsn(userId)}}) return db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true, "Usn": userService.IncrUsn(userId)}})
} }
+54 -15
View File
@@ -69,6 +69,13 @@ func (this *UserService) GetUsername(userId string) string {
return user.Username return user.Username
} }
// 得到用户名
func (this *UserService) GetUsernameById(userId bson.ObjectId) string {
user := info.User{}
db.GetByQWithFields(db.Users, bson.M{"_id": userId}, []string{"Username"}, &user)
return user.Username
}
// 是否存在该用户 email // 是否存在该用户 email
func (this *UserService) IsExistsUser(email string) bool { func (this *UserService) IsExistsUser(email string) bool {
if this.GetUserId(email) == "" { if this.GetUserId(email) == "" {
@@ -107,6 +114,13 @@ func (this *UserService) setUserLogo(user *info.User) {
} }
} }
// 仅得到用户
func (this *UserService) GetUser(userId string) info.User {
user := info.User{}
db.Get(db.Users, userId, &user)
return user
}
// 得到用户信息 userId // 得到用户信息 userId
func (this *UserService) GetUserInfo(userId string) info.User { func (this *UserService) GetUserInfo(userId string) info.User {
user := info.User{} user := info.User{}
@@ -202,19 +216,33 @@ func (this *UserService) MapUserAndBlogByUserIds(userIds []bson.ObjectId) map[st
return userAndBlogMap return userAndBlogMap
} }
// 得到用户信息+博客主页
func (this *UserService) GetUserAndBlogUrl(userId string) info.UserAndBlogUrl {
user := this.GetUserInfo(userId)
userBlog := blogService.GetUserBlog(userId)
blogUrls := blogService.GetBlogUrls(&userBlog, &user)
return info.UserAndBlogUrl{
User: user,
BlogUrl: blogUrls.IndexUrl,
PostUrl: blogUrls.PostUrl,
}
}
// 得到userAndBlog公开信息 // 得到userAndBlog公开信息
func (this *UserService) GetUserAndBlog(userId string) info.UserAndBlog { func (this *UserService) GetUserAndBlog(userId string) info.UserAndBlog {
user := this.GetUserInfo(userId) user := this.GetUserInfo(userId)
userBlog := blogService.GetUserBlog(userId) userBlog := blogService.GetUserBlog(userId)
return info.UserAndBlog{ return info.UserAndBlog{
UserId: user.UserId, UserId: user.UserId,
Username: user.Username, Username: user.Username,
Email: user.Email, Email: user.Email,
Logo: user.Logo, Logo: user.Logo,
BlogTitle: userBlog.Title, BlogTitle: userBlog.Title,
BlogLogo: userBlog.Logo, BlogLogo: userBlog.Logo,
BlogUrl: blogService.GetUserBlogUrl(&userBlog, user.Username), BlogUrl: blogService.GetUserBlogUrl(&userBlog, user.Username),
BlogUrls: blogService.GetBlogUrls(&userBlog, &user), BlogUrls: blogService.GetBlogUrls(&userBlog, &user),
} }
} }
@@ -239,15 +267,15 @@ func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.
return users2 return users2
} }
// 使用email(username), pwd得到用户信息 // 使用email(username), 得到用户信息
func (this *UserService) LoginGetUserInfo(emailOrUsername, md5Pwd string) info.User { func (this *UserService) GetUserInfoByName(emailOrUsername string) info.User {
emailOrUsername = strings.ToLower(emailOrUsername) emailOrUsername = strings.ToLower(emailOrUsername)
user := info.User{} user := info.User{}
if strings.Contains(emailOrUsername, "@") { if strings.Contains(emailOrUsername, "@") {
db.GetByQ(db.Users, bson.M{"Email": emailOrUsername, "Pwd": md5Pwd}, &user) db.GetByQ(db.Users, bson.M{"Email": emailOrUsername}, &user)
} else { } else {
db.GetByQ(db.Users, bson.M{"Username": emailOrUsername, "Pwd": md5Pwd}, &user) db.GetByQ(db.Users, bson.M{"Username": emailOrUsername}, &user)
} }
this.setUserLogo(&user) this.setUserLogo(&user)
return user return user
@@ -281,10 +309,16 @@ func (this *UserService) UpdateAvatar(userId, avatarPath string) (bool) {
// 已经登录了的用户修改密码 // 已经登录了的用户修改密码
func (this *UserService) UpdatePwd(userId, oldPwd, pwd string) (bool, string) { func (this *UserService) UpdatePwd(userId, oldPwd, pwd string) (bool, string) {
userInfo := this.GetUserInfo(userId) userInfo := this.GetUserInfo(userId)
if userInfo.Pwd != Md5(oldPwd) { if !ComparePwd(oldPwd, userInfo.Pwd) {
return false, "oldPasswordError" return false, "oldPasswordError"
} }
ok := db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", Md5(pwd))
passwd := GenPwd(pwd)
if passwd == "" {
return false, "GenerateHash error"
}
ok := db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", passwd)
return ok, "" return ok, ""
} }
@@ -293,7 +327,12 @@ func (this *UserService) ResetPwd(adminUserId, userId, pwd string) (ok bool, msg
if configService.GetAdminUserId() != adminUserId { if configService.GetAdminUserId() != adminUserId {
return return
} }
ok = db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", Md5(pwd))
passwd := GenPwd(pwd)
if passwd == "" {
return false, "GenerateHash error"
}
ok = db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", passwd)
return return
} }
@@ -351,7 +390,7 @@ func (this *UserService) UpdateEmail(token string) (ok bool, msg, email string)
tokenInfo := info.Token{} tokenInfo := info.Token{}
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenUpdateEmail); ok { if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenUpdateEmail); ok {
// 修改之后的邮箱 // 修改之后的邮箱
email = tokenInfo.Email email = strings.ToLower(tokenInfo.Email)
// 先验证该email是否被注册了 // 先验证该email是否被注册了
if userService.IsExistsUser(email) { if userService.IsExistsUser(email) {
ok = false ok = false
+13 -2
View File
@@ -99,16 +99,17 @@ func encodeValue(val string) string {
v.Set("", val) v.Set("", val)
return v.Encode()[1:] return v.Encode()[1:]
} }
// 添加笔记时通过title得到urlTitle // 添加笔记时通过title得到urlTitle
func fixUrlTitle(urlTitle string) string { func fixUrlTitle(urlTitle string) string {
if urlTitle != "" { if urlTitle != "" {
// 把特殊字段给替换掉 // 把特殊字段给替换掉
// str := `life "%&()+,/:;<>=?@\|` // str := `life "%&()+,/:;<>=?@\|`
reg, _ := regexp.Compile("/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\") reg, _ := regexp.Compile("/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\")
urlTitle = reg.ReplaceAllString(urlTitle, "-") urlTitle = reg.ReplaceAllString(urlTitle, "-")
urlTitle = strings.Trim(urlTitle, "-") // 左右单独的-去掉 urlTitle = strings.Trim(urlTitle, "-") // 左右单独的-去掉
// 把空格替换成- // 把空格替换成-
// urlTitle = strings.Replace(urlTitle, " ", "-", -1) // urlTitle = strings.Replace(urlTitle, " ", "-", -1)
for strings.Index(urlTitle, "--") >= 0 { // 防止出现连续的-- for strings.Index(urlTitle, "--") >= 0 { // 防止出现连续的--
urlTitle = strings.Replace(urlTitle, "--", "-", -1) urlTitle = strings.Replace(urlTitle, "--", "-", -1)
} }
@@ -119,6 +120,15 @@ func fixUrlTitle(urlTitle string) string {
func getUniqueUrlTitle(userId string, urlTitle string, types string, padding int) string { func getUniqueUrlTitle(userId string, urlTitle string, types string, padding int) string {
urlTitle2 := urlTitle urlTitle2 := urlTitle
// 判断urlTitle是不是过长, 过长则截断, 300
// 不然生成index有问题
// it will not index a single field with more than 1024 bytes.
// If you're indexing a field that is 2.5MB, it's not really indexing it, it's being skipped.
if len(urlTitle2) > 320 {
urlTitle2 = urlTitle2[:300] // 为什么要少些, 因为怕无限循环, 因为把padding截了
}
if padding > 1 { if padding > 1 {
urlTitle2 = urlTitle + "-" + strconv.Itoa(padding) urlTitle2 = urlTitle + "-" + strconv.Itoa(padding)
} }
@@ -139,6 +149,7 @@ func getUniqueUrlTitle(userId string, urlTitle string, types string, padding int
return urlTitle2 return urlTitle2
} }
// types == note,notebook,single // types == note,notebook,single
func GetUrTitle(userId string, title string, types string) string { func GetUrTitle(userId string, title string, types string) string {
urlTitle := strings.Trim(title, " ") urlTitle := strings.Trim(title, " ")
-237
View File
@@ -1,237 +0,0 @@
package main
import (
"github.com/robfig/revel"
"github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
// "github.com/leanote/leanote/app/lea/memcache"
// "github.com/leanote/leanote/app/lea/netutil"
"github.com/leanote/leanote/app/lea/html2image"
"time"
"fmt"
"gopkg.in/mgo.v2/bson"
// "gopkg.in/mgo.v2"
// "encoding/json"
// "strings"
)
var userId = "5295d4c95b1dd58edb4a7f4f"
var userIdO = bson.ObjectIdHex(userId)
var userId3 = "52b43ae4cfeeae33ef073b2b"
var userId3O = bson.ObjectIdHex(userId3)
var toUserId = "52b4376bcfeeae33ef073b21"
var toUserIdO = bson.ObjectIdHex(toUserId)
var notebookId = "52b2d051ea3ba3d3fb35910c"
var notebookIdO = bson.ObjectIdHex(notebookId)
var notebookId2O = bson.ObjectIdHex("52b2d051ea3ba3d3fb35910b")
var noteId = "52b2dd34ea3ba3d3fb35910d"
var noteIdO = bson.ObjectIdHex(noteId)
func testNoteService() {
println("testNoteService")
noteService := &service.NoteService{}
note := noteService.GetBlogNote("535f9e6b19807a4c8d000000")
LogJ(note)
/*
_, notes := noteService.SearchNote("go", "52d3e8ac99c37b7f0d000001", 1, 30, "", false, true);
LogJ(notes)
*/
return;
/*
noteService.AddNote(info.Note{UserId: userIdO,
NotebookId: notebookIdO,
Title: "life you", Tags: []string{"red", "yellow"}})
*/
noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
NoteId: bson.ObjectIdHex("52b4531dcfeeae33ef073b33"),
Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.UpdateNoteContent(userId, userId, "52b2dd34ea3ba3d3fb35910d", "life2---------")
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testNotebookService() {
service := &service.NotebookService{}
Log(service.IsBlog("52ccb959bcbf21610d000001"))
return;
notebooks := service.GetNotebooks(userId3)
LogJ(notebooks)
// b, _ := json.MarshalIndent(notebooks, "", " ")
// fmt.Println(string(b))
// service.UpdateNotebookTitle("52b2cf9eea3ba3d3fb359108", userId, "JS")
// service.SortNotebooks(userId, map[string]int{"52b2d051ea3ba3d3fb35910c": 4, "52b2d051ea3ba3d3fb35910b": 3})
/*
service.AddNotebook(info.Notebook{UserId: userId3O,
ParentNotebookId: bson.ObjectIdHex("52b43c1fcfeeae33ef073b2e"),
Title: "Mac-life",
Seq: 0})
*/
println("xxx")
}
func testShareService() {
service := &service.ShareService{}
// service.AddShareNote("52bd127dbcbf216d0b000000", 1, "5295d4c95b1dd58edb4a7f4f", "c@a.com")
LogJ(service.ListNoteShareUserInfo("52cd11a1bcbf215680000000", "5295d4c95b1dd58edb4a7f4f"))
return;
/*
service.AddShareNotebook(info.ShareNotebook{UserId: userId3O, ToUserId: userIdO,
NotebookId: bson.ObjectIdHex("52b43c38cfeeae33ef073b30")})
*/
notebooks,_ := service.GetShareNotebooks(userId)
LogJ(notebooks)
// noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
// NoteId: bson.ObjectIdHex("52b2dd34ea3ba3d3fb35910d"), Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testAuthService() {
userService := &service.UserService{}
LogJ(userService.GetUserInfo("52d26b4e99c37b609a000001"))
// userService.AddUser(info.User{UserId: bson.ObjectIdHex("52d26b4e99c37b609a000001"), Email: "leanote@leanote.com", Pwd:"abc"})
return;
authService := &service.AuthService{}
authService.Register("f@a.com", "abc")
// fmt.Println(authService.LogonGetUserInfo("a@a.com", "abc"))
// noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
// NoteId: bson.ObjectIdHex("52b2dd34ea3ba3d3fb35910d"), Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
// println(bson.IsObjectIdHex(id))
// note := noteService.Get(id)
// fmt.Println(note)
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
}
func testTagService() {
service := &service.TagService{}
// service.AddTags("5295d4c95b1dd58edb4a7f4f", []string{"life", "blue", "yellow"})
// service.AddTags("5295d4c95b1dd58edb4a7f4f", []string{"what", "can", "make"})
LogJ(service.GetTags("5295d4c95b1dd58edb4a7f4f"))
}
func testHtml2Image() {
start := time.Now()
Log("start...")
// TestFillString()
html2image.ToImage("uid", "username", "noteId", "开发一款属于自己的编程语言,开发一款属于自己的编程语言听起来是不是很酷?", `
<div class="each-post">
<p>
一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace,很喜欢那里的氛围,希望国内其他地方也能有更多这类空间供创客发挥。
假如你有一个比较成型的想法,想在硬件领域做点事情,核心团队也基本组好,硬件软件交互基本都有专人了。</p>
<p><a>这时候你的首要目标</a>,就是找个地方,按照你的计划,尽早做出一个可用的原型。Techspace可能是一个合适的地方。
一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace,很喜欢那里的氛围,希望国内其他地方也能有更多这类空间供创客发挥。
深圳 Techspace 位于工业区,园区内有奥迪、BMW 等企业的厂房,在大门口我停下来问保安,M10 栋在哪里?保安答,去 Techspace?最里面靠右手那栋。惊叹于保安的机智,我也在想,莫非有许多朋友都慕名来到这巨大园区寻访 Techspace?
穿过一片工业区里的高楼和各种建筑材料
</p>
<!--
<pre class="">cd jpeg-9a/<br>./configure --enable-shared --enable-static
make<br>make --- install</pre>
<p>
life you can, !!@kk
</p>
-->
</div>
`, "/Users/life/Desktop/a.png")
fmt.Printf("time cost %v\n", time.Now().Sub(start))
}
func testLea() {
names := ListDir("/Users/life/Documents/Go/package/src/leanote")
fmt.Println(names);
}
func main() {
revel.BasePath = "/Users/life/Documents/Go/package/src/leanote"
// testLea();
// a, b := SplitFilename("http://ab/c/a.gif#??")
// println(a)
// println(b)
// path, ok := netutil.WriteUrl("http://a.36krcnd.com/photo/2014/9bd1a07c0973d79ca05ad13c3c2e16b8.png!slider", "/tmp")
// println(path)
// testHtml2Image();
// println(IsObjectId("52d26b4e99c37b609a000001"))
// b := `请点击链接验证邮箱: <a href="">http://leanote.com/user/activeEmail?token=d8ca086cce5550a6227f9dc84dbac09d</a>. 48小时后过期.`
// SendEmail("lifephp@gmail.com", "leanote-验证邮箱", "验证邮", b)
//_, err := mgo.Dial("mongodb://leanote:nKFAkxKnWkEQy8Vv2LlM@115.28.133.226:27017/leanote")
// testNotebookService();
// testNoteService();
// testShareService()
// testAuthService()
// testTagService();
/*
filename := "你好59.26.png"
ext := SubstringByte(filename, strings.LastIndex(filename, "."))
ext = strings.ToLower(ext)
print(ext)
52d26ab199c37b5f80000001
Log(bson.NewObjectId())
Log(bson.NewObjectId())
Log(bson.NewObjectId())
*/
// Log(TransferExt("/你好a/b/a.gif", ".jpg"))
// TransToGif("/Users/life/Desktop/a2.png", 0, false)
// Log(IsUsername("xx**x"))
/*
memcache.Set("xx", map[string]string{"A":"you"}, 0)
a := memcache.Get("xx")
Log(a)
*/
}
+23
View File
@@ -0,0 +1,23 @@
package tests
import (
"testing"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/service"
// "gopkg.in/mgo.v2"
// "fmt"
)
func init() {
db.Init("mongodb://localhost:27017/leanote", "leanote")
service.InitService()
}
// 测试登录
func TestAuth(t *testing.T) {
_, err := service.AuthS.Login("admin", "abc123")
if err != nil {
t.Error("Admin User Auth Error")
}
}
+14
View File
@@ -0,0 +1,14 @@
package tests
import (
"testing"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
// "github.com/leanote/leanote/app/service"
// "gopkg.in/mgo.v2"
// "fmt"
)
func TestDBConnect(t *testing.T) {
db.Init("mongodb://localhost:27017/leanote", "leanote")
}
+3
View File
@@ -0,0 +1,3 @@
package tests
func a() {
}
-17
View File
@@ -11,20 +11,3 @@
<script src="/public/admin/js/artDialog/jquery.artDialog.js?skin=default"></script> <script src="/public/admin/js/artDialog/jquery.artDialog.js?skin=default"></script>
<script src="/public/js/common.js"></script> <script src="/public/js/common.js"></script>
<script src="/public/admin/js/admin.js"></script> <script src="/public/admin/js/admin.js"></script>
<script>
$(function() {
var pathname = location.pathname; // admin/t
var search = location.search; // ?t=xxx, 如果有?page呢
var fullPath = pathname;
if(search.indexOf("?t=") >= 0) {
var fullPath = pathname + search; // /admin/t?t=xxx
}
$("#nav > li").removeClass("active");
// 自己
var $thisLi = $('#nav a[href^="' + fullPath + '"]').parent();
$thisLi.addClass("active");
// 父也active
$thisLi.parent().parent().addClass('active');
});
</script>
+12 -2
View File
@@ -13,7 +13,6 @@
</a> </a>
</li> </li>
<li id="adminUserNav"> <li id="adminUserNav">
<a href="#"> <a href="#">
<i class="fa fa-users icon"> <i class="fa fa-users icon">
@@ -157,7 +156,7 @@
</li> </li>
<li> <li>
<a href="/admin/t?t=setting/shareNote"> <a href="/admin/t?t=setting/share_note">
<span> <span>
Register Share Note Register Share Note
</span> </span>
@@ -171,6 +170,15 @@
</span> </span>
</a> </a>
</li> </li>
<li>
<a href="/admin/t?t=setting/export_pdf">
<span>
Export PDF
</span>
</a>
</li>
</ul> </ul>
</li> </li>
@@ -232,6 +240,8 @@
v1.0-beta to v1.0-beta.2 v1.0-beta to v1.0-beta.2
</span> </span>
</a> </a>
</li>
<li>
<a href="/admin/t?t=upgrade/beta3"> <a href="/admin/t?t=upgrade/beta3">
<span> <span>
v1.0-beta.2/3 to v1.0-beta.4 v1.0-beta.2/3 to v1.0-beta.4
+52
View File
@@ -0,0 +1,52 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Export PDF</h3></div>
<div class="row">
<div class="col-sm-6">
<form id="add_user_form">
<section class="panel panel-default">
<div class="panel-body">
<div class="form-group">
<label>Wkhtmltopdf Binary Path</label>
<input type="text" class="form-control" placeholder="/usr/local/bin/wkhtmltopdf" name="path" value="{{.str.exportPdfBinPath}}">
Leanote use <a target="_blank" href="http://wkhtmltopdf.org">wkhtmltopdf</a> to export pdf. You should install it on your server.
<br />
Please input the path that wkhtmltopdf installed, e.g. <code>/usr/local/bin/wkhtmltopdf</code>
</div>
</div>
<footer class="panel-footer text-right bg-light lter">
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
</footer>
</section>
</form>
</div>
</div>
{{template "admin/footer.html" .}}
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
<script>
$(function() {
init_validator("#add_user_form");
$("#submit").click(function(e){
e.preventDefault();
var t = this;
if($("#add_user_form").valid()) {
$(t).button('loading');
ajaxPost("/adminSetting/exportPdf", getFormJsonData("add_user_form"), function(ret){
$(t).button('reset')
if(!ret.Ok) {
art.alert(ret.Msg)
} else {
art.tips("Success");
}
});
}
});
});
</script>
{{template "admin/end.html" .}}
+166
View File
@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>
Leanote Album Image Manager
</title>
<link href="/css/bootstrap-min.css" rel="stylesheet" />
<link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
<link href="/public/album/css/style-min.css" rel="stylesheet" />
</head>
<body class="md" id="body">
<div style="margin: 3px;">
<div class="holder"></div>
<div class="tabs">
<ul id="myTab" class="nav nav-tabs">
<li class="active"><a href="#images" data-toggle="tab">{{leaMsg . "Images"}}</a></li>
<li class=""><a href="#uploadTab" data-toggle="tab">{{leaMsg . "Upload"}}</a></li>
<li class=""><a href="#url" data-toggle="tab">{{leaMsg . "Image URL"}}</a></li>
</ul>
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="images">
<!-- tools -->
<div>
<form class="form-inline" role="form">
<div class="form-group">
<label class="control-label" for="albums">{{leaMsg . "Albums"}}:</label>
</div>
<div class="form-group">
<select class="form-control" id="albumsForList">
<option value="">{{leaMsg . "Default"}}</option>
</select>
</div>
<div class="form-group">
<input class="form-control" type="text" id="key" placeholder="{{leaMsg . "File title search"}}"/>
</div>
<div class="form-group">
&nbsp;&nbsp;<a href="javascript:;" title="refresh" id="refresh"><span class="fa fa-refresh"></span></a>
</div>
</form>
</div>
<div id="imagePage">
<ul id="imageList" class="clearfix">
</ul>
<!-- pagination -->
<div id="paginationContainer">
<ul class="pagination">
</ul>
</div>
<div id="imageMask">
<div id="noImages">
<p>{{msg . "No Images"}}!</p>
<btn class="btn btn-default" id="goAddImageBtn">{{leaMsg . "Go to upload images"}}</btn>
</div>
<div id="loading">
loading....
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="uploadTab">
<div>
<form class="form-inline" role="form">
<div class="form-group" id="albumSelect">
<label class="control-label" for="albums">{{leaMsg . "Albums"}}:</label>
<select class="form-control" id="albumsForUpload">
<option value="">{{leaMsg . "Default"}}</option>
</select>
<a href="javascript:;" id="deleteAlbumBtn">{{leaMsg . "Delete"}}</a> |
<a href="javascript:;" id="renameAlbumBtn">{{leaMsg . "Rename"}}</a> |
<a href="javascript:;" id="addAlbumBtn">{{leaMsg . "Add"}}</a>
</div>
<!-- rename or add album -->
<div class="form-group" style="display: none" id="addOrUpdateAlbumForm">
<input type="text" class="form-control" style="width: 150px" id="albumName">
<button class="btn btn-success" type="button" id="addOrUpdateAlbumBtn">{{leaMsg . "Add Album"}}</button>
<button class="btn btn-default" type="button" id="cancelAlbumBtn">{{leaMsg . "Cancel"}}</button>
</div>
<div class="form-group">
<span id="msg" class="alert alert-success" style="padding: 3px; display: none"></span>
</div>
</form>
</div>
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
<div id="drop">
<a class="btn btn-default">
{{leaMsg . "Click to upload images Or Drop images to here"}}
</a>
<input type="file" name="file" multiple />
</div>
<ul id="upload-msg">
</ul>
</form>
</div>
<div class="tab-pane fade" id="url">
<form class="form-inline" id="imageUrlForm" style="">
<div class="form-group">
<label class="control-label" for="imageUrl">{{leaMsg . "Image URL"}}:</label>
</div>
<div class="form-group">
<input type="text" class="form-control" id="imageUrl" size="51"/>
</div>
<div class="form-group">
<button class="btn btn-success" id="addImageUrlBtn">{{leaMsg . "Add Image"}}</button>
</div>
<div class="form-group">
<div class="alert alert-danger" id="msgForUrl"
style="display: none; padding: 5px; width: 150px;">
{{leaMsg . "Can't load this url"}}
</div>
</div>
</form>
</div>
</div>
</div>
<ul id="preview" class="clearfix">
</ul>
<!--
<div id="previewAttrs" style="margin-left: 10px">
<form class="form-inline" role="form">
<div class="form-group">
<input class="form-control" id="attrTitle" placeholder="title" size="20" disabled/>
<input class="form-control" id="attrWidth" placeholder="width" size="5" disabled/> X
<input class="form-control" id="attrHeight" placeholder="height" size="5" disabled/>
<label><input type="checkbox" value="1" id="attrConstrain" disabled/> Constrain proportions</label>
</div>
</form>
</div>
-->
</div>
</body>
<script>
// javascript global configration
var G = {};
G.imageSrcPrefix = 'upload';
G.perPageItems = 12;
G.maxSelected = 1;
var UrlPrefix = '{{.siteUrl}}';
</script>
<!--
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap-min.js"></script>
<script src="/js/plugins/libs-min/fileupload.js"></script>
<script src="/js/jquery.pagination.js"></script>
<script src="/public/album/js/main.js"></script>
-->
<script src="/public/album/js/main.all.js"></script>
</html>
+1 -1
View File
@@ -26,7 +26,7 @@
</section> </section>
<div id="boxFooter"> <div id="boxFooter">
<p> <p>
<a href="/index">leanote</a> © 2014 <a href="/index">leanote</a> © 2015
</p> </p>
</div> </div>
</body> </body>
+1 -1
View File
@@ -26,7 +26,7 @@
</section> </section>
<div id="boxFooter"> <div id="boxFooter">
<p> <p>
<a href="/index">leanote</a> © 2014 <a href="/index">leanote</a> © 2015
</p> </p>
</div> </div>
</body> </body>
+124
View File
@@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.title}}</title>
<link href="/css/bootstrap.css" rel="stylesheet">
<link id="styleLink" href="/css/pdf.css" rel="stylesheet">
<style>
body {
margin-top: 30px;
}
table {
margin-bottom: 16px;
}
table th, table td {
padding: 6px 13px;
border: 1px solid #ddd;
}
table th {
font-weight: bold;
}
table tr {
background-color: #fff;
border-top: 1px solid #ccc;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
.mce-item-table, .mce-item-table td, .mce-item-table th, .mce-item-table caption {
border: 1px solid #ddd;
border-collapse: collapse;
padding: 6px 13px;
}
</style>
</head>
<body>
<div id="content">
<h1 class="title tex2jax_ignore">
{{if .blog.Title}}
{{.blog.Title}}
{{else}}
Untitled
{{end}}
</h1>
<div class="created-time">
<!--
{{ if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" id="logo">
{{else}}
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
{{end}}
{{.userInfo.Username}}
-->
{{if .blog.Tags}}
<img src="{{$.siteUrl}}/images/blog/tag.png" id="tag">
{{blogTagsForExport $ .blog.Tags}}
{{end}}
</div>
<div class="desc">
{{if .blog.IsMarkdown }}
<div id="markdownContent" style="display: none">
<!-- 用textarea装html, 防止得到的值失真 -->
<textarea>
{{.content | raw}}
</textarea>
</div>
<div id="parsedContent">
</div>
{{else}}
{{.content | raw}}
{{end}}
</div>
</div>
<!--
<div id="footer">
<p class="split"></p>
<a href="http://leanote.com"><img src="{{.siteUrl}}/images/logo/leanote_icon_blue.png" id="leanote_logo"/></a>
<p>
<a href="http://leanote.com">Leanote</a>
<br />
<a href="http://leanote.com/service">Upgrade Account</a>
</p>
</div>
-->
<script src="{{.siteUrl}}/js/jquery-1.9.0.min.js"></script>
<link href="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script>
function ok() {
setTimeout(function() {
window.status = 'done';
}, 0);
}
</script>
{{if not .blog.IsMarkdown }}
<script>
$("pre").addClass("prettyprint");
prettyPrint();
ok();
</script>
{{end}}
{{if .blog.IsMarkdown }}
<script src="/public/libs/md2html/md2html_for_export.js"></script>
<script>
var content = $.trim($("#markdownContent textarea").val());
md2Html(content, $("#parsedContent"), function(html) {
$("pre").addClass("prettyprint");
prettyPrint();
ok();
});
</script>
{{end}}
</body>
</html>
+1 -1
View File
@@ -27,7 +27,7 @@
<a href="/index">{{msg . "home"}}</a> <a href="/index">{{msg . "home"}}</a>
</p> </p>
<p> <p>
<a href="/index">leanote</a> © 2014 <a href="/index">leanote</a> © 2014-2015
</p> </p>
</div> </div>
+1 -1
View File
@@ -36,7 +36,7 @@
<a href="/index">{{msg . "home"}}</a> <a href="/index">{{msg . "home"}}</a>
</p> </p>
<p> <p>
<a href="/index">leanote</a> © 2014 <a href="/index">leanote</a> © 2014-2015
</p> </p>
</div> </div>
+1 -1
View File
@@ -21,7 +21,7 @@
<a href="/index">{{msg . "home"}}</a> <a href="/index">{{msg . "home"}}</a>
</p> </p>
<p> <p>
<a href="/index">leanote</a> © 2014 <a href="/index">leanote</a> © 2014-2015
</p> </p>
</div> </div>
+6 -5
View File
@@ -7,18 +7,19 @@
<a href="http://weibo.com/leanotecom" target="_blank"><i class="fa fa-weibo"></i></a> <a href="http://weibo.com/leanotecom" target="_blank"><i class="fa fa-weibo"></i></a>
<br /> <br />
leanote@leanote.com leanote@leanote.com
<div id="beian"> <br />
<a href="http://www.miibeian.gov.cn" target="_blank">沪ICP备14006011号</a> Copyright © 2014-2015 <a href="https://leanote.com">Leanote</a>
</div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<i class="fa fa-globe fa-3x icon-muted"></i> <i class="fa fa-globe fa-3x icon-muted"></i>
<h2>Join Us</h2> <h2>Join Us</h2>
<a href="https://github.com/leanote/leanote">github leanote</a> <a href="https://github.com/leanote/leanote">Github Leanote</a>
<br /> <br />
QQ Group: 158716820 QQ Group: 158716820, 158716820
</div> </div>
</div> </div>
</div> </div>
<div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div>
</div> </div>
+26 -23
View File
@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com"> <meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, {{msg $ "moto"}}"> <meta name="description" content="Leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote"> <meta name="author" content="leanote">
<title>{{.title}}</title> <title>{{.title}}</title>
@@ -37,31 +37,33 @@ function log(o) {
</div> </div>
<div id="navbar" class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<div id="lang">
<a data-lang="en-us">English</a>
<a data-lang="fr-fr">Français</a>
<a data-lang="zh-cn">简体中文</a>
</div>
<ul class="nav navbar-nav navbar-left"> <ul class="nav navbar-nav navbar-left">
<li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li> <li><a href="/index#" data-target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<!--
<li><a href="/index#aboutLeanote" target="#aboutLeanote" class="smooth-scroll">{{msg . "aboutLeanote"}}</a> </li>
-->
<li><a href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
<li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
<li><a id="leanoteBlog" href="{{.leaUrl}}/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li>
<li style="position: relative; margin-right: 3px;"> <li style="position: relative; margin-right: 3px;">
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a> <a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
<div class="red-circle" style=""></div>
</li> </li>
<li id="loginBtns"> <li><a id="leanoteBlog" href="http://lea.leanote.com" target="_blank" title="Lea++, leanote blog platform" class="">Lea++</a></li>
{{if .userInfo.Email}} <li><a id="leanoteApp" href="http://app.leanote.com" target="_blank" title="Leanote App" class="">{{msg . "desktopApp"}}</a></li>
{{msg . "hi"}}, {{.userInfo.Username}} <li>
<a href="{{$.noteUrl}}">{{msg . "myNote"}}</a> <a href="http://leanote.org#donate" target="_blank" class="">{{msg . "donate"}}</a>
<a href="/logout">{{msg . "logout"}}</a>
{{else}}
<a href="/login">{{msg . "login"}}</a>
{{if .openRegister}}
<a href="/register" class="btn-register">{{msg . "register"}}</a>
{{end}}
{{end}}
</li> </li>
<li><a href="http://leanote.org" target="_blank" title="leanote.org" class="">leanote.org</a></li>
{{if .userInfo.Email}}
<li id="loginBtns">
{{msg . "hi"}}, {{.userInfo.Username}}
<a href="{{$.noteUrl}}">{{msg . "myNote"}}</a>
<a href="/logout">{{msg . "logout"}}</a>
</li>
{{end}}
</ul> </ul>
</div> </div>
</div> </div>
@@ -91,10 +93,11 @@ function log(o) {
</div> </div>
<ul id="blogNav" class="pull-right"> <ul id="blogNav" class="pull-right">
<li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li> <li><a href="#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<li><a href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li> <li><a href="#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
<li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li> <li><a href="#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
<li><a id="leanoteBlog" href="{{.leaUrl}}/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li> <li><a id="leanoteBlog" href="{{.leaUrl}}/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li>
<li style="position: relative; margin-right: 3px;"> <li style="position: relative; margin-right: 3px;">
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a> <a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
<div style="position: absolute; <div style="position: absolute;
+37 -81
View File
@@ -1,26 +1,22 @@
{{template "home/header.html" .}} {{template "home/header.html" .}}
<style>
</style> </style>
<section> <section>
<div class="header"> <div class="header">
<h2>leanote, {{msg . "moto"}}</h2> <h2>Leanote, {{msg . "moto"}}</h2>
<p>{{msg . "moto3"}}</p> <p>{{msg . "moto3"}}</p>
<p>{{msg . "moto2"}}</p> <p>{{msg . "moto2"}}</p>
<div> <div>
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a> <a class="btn btn-default btn-primary" href="/register">{{msg . "register"}}</a>
&nbsp; &nbsp;
&nbsp; &nbsp;
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a> {{if not .userInfo.UserId}}
{{if .openRegister}} <a class="btn btn-default" href="/login">{{msg . "login"}}</a>
&nbsp; &nbsp;
&nbsp; &nbsp;
OR
&nbsp;
&nbsp;
<a class="btn btn-default" href="/register">{{msg . "register"}}</a>
{{end}} {{end}}
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
</div> </div>
</div> </div>
@@ -29,13 +25,23 @@
<div class="img-header"> <div class="img-header">
<img src="/images/home/mac-btns.png"/> <img src="/images/home/mac-btns.png"/>
</div> </div>
<img src="/images/home/preview2.png" style="width: 750px;" /> <div id="webSliderContainer">
<img class="web-slider" data-text="Default theme - markdown" src="/images/slider/v2/default_markdown.png"/>
<img class="web-slider hide-img" data-text="Default theme - rich editor" src="/images/slider/v2/simple_tinymce.png"/>
<img class="web-slider hide-img" data-text="Simple theme - markdown" src="/images/slider/v2/simple_markdown.png"/>
<img class="web-slider hide-img" data-text="Writting mode" src="/images/slider/v2/writting.png"/>
</div>
</div> </div>
<div class="slider-desc"><span id="webText">Default theme - markdown</span></div>
<div class="mobile"> <div class="mobile">
<div class="mobile-header"> <div class="mobile-header">
<img src="/images/home/mac-dot.png" /> <img src="/images/home/mac-dot.png" />
</div> </div>
<img class="mobile-image" src="/images/home/mobile.png" /> <div id="mobileSliderContainer">
<img class="mobile-slider" data-text="Mobile" src="/images/slider/v2/mobile_simple_list.png" />
<img class="mobile-slider hide-img" data-text="Mobile markdown" src="/images/slider/v2/mobile_markdown.png" />
</div>
<div class="slider-desc mobile-text"><span id="mobileText">Default theme - markdown</span></div>
</div> </div>
</div> </div>
</section> </section>
@@ -46,6 +52,10 @@
<div class="col-md-3"> <div class="col-md-3">
<h3>{{msg . "knowledge"}}</h3> <h3>{{msg . "knowledge"}}</h3>
<p>{{msg . "knowledgeInfo"}}</p> <p>{{msg . "knowledgeInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<h3>{{msg . "share"}}</h3> <h3>{{msg . "share"}}</h3>
@@ -55,81 +65,27 @@
<h3>{{msg . "cooperation"}}</h3> <h3>{{msg . "cooperation"}}</h3>
<p>{{msg . "cooperationInfo"}}</p> <p>{{msg . "cooperationInfo"}}</p>
</div> </div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div>
</div> </div>
</div> </div>
<div class="container" id="download">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "download"}}</h2>
<div class="row">
<div style="width:300px; margin:auto; padding: 10px;">
Linux : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz">leanote-linux-v0.4.bin.tar.gz</a> <br />
MacOS X : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz">leanote-mac-v0.4.bin.tar.gz</a> <br />
<br />
<a href="https://github.com/leanote/leanote#3-how-to-install-leanote">{{msg . "howToInstallLeanote"}}</a>
</div>
</div>
</div>
<div class="container" id="donate">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "donate"}}</h2>
<div class="row">
<div style="width:500px; margin:auto; padding: 10px;">
<p>
您可以通过支付宝向leanote捐赠, 所捐赠的钱将用来leanote开发.
</p>
<p>
支付宝账号: <b>pay@leanote.com</b>
</p>
<p>
或使用支付宝扫以下二维码捐赠:
</p>
<p style="text-align: center">
<img src="/images/leanote/leanote_alipay.jpg" style="padding: 10px;
border: 2px solid #eee;
border-radius: 10px;"/>
</p>
<p>
我们会定期将捐赠名单发布在 <a href="http://leanote.com/blog/view/5417ecf81a910828fd000000">捐赠列表</a>
</p>
<p>
感谢您对leanote的支持!
</p>
<hr />
<p>
有任何疑问, 建议或需其它服务或支持, 欢迎联系 <code>leanote@leanote.com</code> 或加入官方QQ群: <code>158716820</code> 谢谢!
</p>
</div>
</div>
</div>
{{template "home/footer.html"}} {{template "home/footer.html"}}
<script src="/js/jquery-1.9.0.min.js"></script> <script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script> <script src="/js/bootstrap.js"></script>
<script>
var lang = '{{.locale}}';
</script>
<script src="/js/home/index.js"></script>
<script> <script>
$(function() { var _hmt = _hmt || [];
/* (function() {
var u = navigator.userAgent; var hm = document.createElement("script");
var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1; hm.src = "//hm.baidu.com/hm.js?d0c988e37b452b3bf1220d45b30f2de2";
if(isMobile || $("body").width() < 600) { var s = document.getElementsByTagName("script")[0];
location.href = "/mobile/index"; s.parentNode.insertBefore(hm, s);
} })();
*/
// 平滑滚动
$(".smooth-scroll").click(function(e) {
e.preventDefault();
var t = $(this).attr("target");
var targetOffset = $(t).offset().top - 80;
$('html,body').animate({scrollTop: targetOffset}, 300);
});
});
</script> </script>
</body> </body>
</html> </html>
-140
View File
@@ -1,140 +0,0 @@
{{template "home/header.html" .}}
<style>
</style>
<section>
<div class="header">
<h2>leanote, {{msg . "moto"}}</h2>
<p>{{msg . "moto3"}}</p>
<p>
Knowledge, Blog, Sharing, Cooperation... all in leanote
</p>
<div>
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
&nbsp;
&nbsp;
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
{{if .openRegister}}
&nbsp;
&nbsp;
OR
&nbsp;
&nbsp;
<a class="btn btn-default" href="/register">{{msg . "register"}}</a>
{{end}}
</div>
</div>
<div class="preview" style="position: relative;">
<div>
<div class="img-header">
<img src="/images/home/mac-btns.png"/>
</div>
<img src="/images/home/preview2.png" style="width: 750px;" />
</div>
<div class="mobile">
<div class="mobile-header">
<img src="/images/home/mac-dot.png" />
</div>
<img class="mobile-image" src="/images/home/mobile.png" />
</div>
</div>
</section>
<div class="container" id="aboutLeanote">
<h2>{{msg . "aboutLeanote"}}</h2>
<div class="row">
<div class="col-md-3">
<h3>{{msg . "knowledge"}}</h3>
<p>{{msg . "knowledgeInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "share"}}</h3>
<p>{{msg . "shareInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "cooperation"}}</h3>
<p>{{msg . "cooperationInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div>
</div>
</div>
<hr />
<div class="container" id="download">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "download"}}</h2>
<div class="row">
<div style="width:300px; margin:auto; padding: 10px;">
Linux : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz">leanote-linux-v0.4.bin.tar.gz</a> <br />
MacOS X : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz">leanote-mac-v0.4.bin.tar.gz</a> <br />
<br />
<a href="https://github.com/leanote/leanote#3-how-to-install-leanote">{{msg . "howToInstallLeanote"}}</a>
</div>
</div>
</div>
<hr />
<div class="container" id="donate">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "donate"}}</h2>
<div class="row">
<div style="width:500px; margin:auto; padding: 10px;">
<p>
You can use <a href="http://alipay.com">alipay</a> to donate us, The donated money will be used to develop leanote.
</p>
<p>
Alipay Account: <b>pay@leanote.com</b>
</p>
<p>
Or you can use alipay app to scan the code to donate us:
</p>
<p style="text-align: center">
<img src="/images/leanote/leanote_alipay.jpg" style="padding: 10px;
border: 2px solid #eee;
border-radius: 10px; width: 200px;"/>
</p>
<p>
The donation list will be published at <a href="http://leanote.com/blog/view/5417ecf81a910828fd000000">Donation List</a>
</p>
<p>
Thanks for your support!
</p>
<hr />
<p>
Any questions, suggestions or need more supports, you are welcomed to contact us via <code>leanote@leanote.com</code> or the QQ Group <code>158716820</code> Thanks!
</p>
</div>
</div>
</div>
{{template "home/footer.html"}}
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
$(function() {
/*
var u = navigator.userAgent;
var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1;
if(isMobile || $("body").width() < 600) {
location.href = "/mobile/index";
}
*/
// 平滑滚动
$(".smooth-scroll").click(function(e) {
e.preventDefault();
var t = $(this).attr("target");
var targetOffset = $(t).offset().top - 80;
$('html,body').animate({scrollTop: targetOffset}, 300);
});
});
</script>
</body>
</html>
-139
View File
@@ -1,139 +0,0 @@
{{template "home/header.html" .}}
<style>
</style>
<section>
<div class="header">
<h2>leanote, {{msg . "moto"}}</h2>
<p>{{msg . "moto3"}}</p>
<p>{{msg . "moto2"}}</p>
<div>
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
&nbsp;
&nbsp;
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
{{if .openRegister}}
&nbsp;
&nbsp;
OR
&nbsp;
&nbsp;
<a class="btn btn-default" href="/register">{{msg . "register"}}</a>
{{end}}
</div>
</div>
<div class="preview" style="position: relative;">
<div>
<div class="img-header">
<img src="/images/home/mac-btns.png"/>
</div>
<img src="/images/home/preview2.png" style="width: 750px;" />
</div>
<div class="mobile">
<div class="mobile-header">
<img src="/images/home/mac-dot.png" />
</div>
<img class="mobile-image" src="/images/home/mobile.png" />
</div>
</div>
</section>
<div class="container" id="aboutLeanote">
<h2>{{msg . "aboutLeanote"}}</h2>
<div class="row">
<div class="col-md-3">
<h3>{{msg . "knowledge"}}</h3>
<p>{{msg . "knowledgeInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "share"}}</h3>
<p>{{msg . "shareInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "cooperation"}}</h3>
<p>{{msg . "cooperationInfo"}}</p>
</div>
<div class="col-md-3">
<h3>{{msg . "blog"}}</h3>
<p>{{msg . "blogInfo"}}</p>
</div>
</div>
</div>
<hr />
<div class="container" id="download">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "download"}}</h2>
<div class="row">
<div style="width:300px; margin:auto; padding: 10px;">
Linux : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz">leanote-linux-v0.4.bin.tar.gz</a> <br />
MacOS X : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz">leanote-mac-v0.4.bin.tar.gz</a> <br />
<br />
<a href="https://github.com/leanote/leanote#3-how-to-install-leanote">{{msg . "howToInstallLeanote"}}</a>
</div>
</div>
</div>
<hr />
<div class="container" id="donate">
<h2 style="margin: 20px 0;text-align: center;">{{msg . "donate"}}</h2>
<div class="row">
<div style="width:500px; margin:auto; padding: 10px;">
<p>
您可以通过<a href="http://alipay.com">支付宝</a>向leanote捐赠, 所捐赠的款项将用于开发leanote.
</p>
<p>
支付宝账号: <b>pay@leanote.com</b>
</p>
<p>
或使用支付宝扫以下二维码捐赠:
</p>
<p style="text-align: center">
<img src="/images/leanote/leanote_alipay.jpg" style="padding: 10px;
border: 2px solid #eee;
border-radius: 10px; width: 200px;"/>
</p>
<p>
我们会定期将捐赠名单发布在 <a href="http://leanote.com/blog/view/5417ecf81a910828fd000000">捐赠列表</a>
</p>
<p>
感谢您对leanote的支持!
</p>
<hr />
<p>
有任何疑问, 建议或需其它服务或支持, 欢迎联系 <code>leanote@leanote.com</code> 或加入官方QQ群: <code>158716820</code> 谢谢!
</p>
</div>
</div>
</div>
{{template "home/footer.html"}}
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
$(function() {
/*
var u = navigator.userAgent;
var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1;
if(isMobile || $("body").width() < 600) {
location.href = "/mobile/index";
}
*/
// 平滑滚动
$(".smooth-scroll").click(function(e) {
e.preventDefault();
var t = $(this).attr("target");
var targetOffset = $(t).offset().top - 80;
$('html,body').animate({scrollTop: targetOffset}, 300);
});
});
</script>
</body>
</html>
+1 -1
View File
@@ -64,7 +64,7 @@
<a href="/index">{{msg . "home"}}</a> <a href="/index">{{msg . "home"}}</a>
</p> </p>
<p> <p>
<a href="/index">leanote</a> © 2014 <a href="/index">leanote</a> © 2014-2015
</p> </p>
</div> </div>
+2 -2
View File
@@ -43,7 +43,7 @@
<a href="/index">{{msg . "home"}}</a> <a href="/index">{{msg . "home"}}</a>
</p> </p>
<p> <p>
<a href="/index">leanote</a> © 2014 <a href="/index">leanote</a> © 2014-2015
</p> </p>
</div> </div>
@@ -72,7 +72,7 @@ $(function() {
showMsg("{{msg . "inputEmail"}}", "email"); showMsg("{{msg . "inputEmail"}}", "email");
return; return;
} else { } else {
var myreg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[0-9a-zA-Z]{2,3}$/; var myreg = /^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-_]+@([a-zA-Z0-9\-]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,6}$/;
if(!myreg.test(email)) { if(!myreg.test(email)) {
showMsg("{{msg . "wrongEmail"}}", "email"); showMsg("{{msg . "wrongEmail"}}", "email");
return; return;
-102
View File
@@ -1,102 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote">
<title>{{.title}}</title>
<link href="{{.siteUrl}}/css/bootstrap.css" rel="stylesheet">
<link id="styleLink" href="{{.siteUrl}}/css/toImage.css" rel="stylesheet">
</head>
<body>
<div id="content">
<h1 class="title">
{{.blog.Title}}
</h1>
<div class="created-time">
{{ if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" id="logo">
{{else}}
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
{{end}}
{{.userInfo.Username}}
{{if .blog.Tags}}
<img src="{{$.siteUrl}}/images/blog/tag.png" id="tag">
{{blogTags .blog.Tags}}
{{end}}
</div>
<div class="desc">
{{if .blog.IsMarkdown }}
<div id="markdownContent" style="display: none">
<!-- 用textarea装html, 防止得到的值失真 -->
<textarea>
{{.content | raw}}
</textarea>
</div>
<div id="parsedContent">
</div>
{{else}}
{{.content | raw}}
{{end}}
</div>
</div>
<div id="footer">
<p>
{{ if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" id="logo">
{{else}}
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
{{end}}
(<a href="#">http://blog.leanote.com/{{.userInfo.Username}}</a>)
</p>
<img src="{{.siteUrl}}/images/logo/leanote_white.png" id="leanote_logo"/>
<p>
leanote, {{msg $ "moto"}}
</p>
</div>
<script src="{{.siteUrl}}/js/jquery-1.9.0.min.js"></script>
<script src="{{.siteUrl}}/js/bootstrap-min.js"></script>
<link href="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script>
$("pre").addClass("prettyprint linenums");
prettyPrint();
</script>
{{if .blog.IsMarkdown }}
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Converter.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Sanitizer.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/Markdown.Editor.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/pagedown/local/Markdown.local.zh.js"></script>
<script src="{{.siteUrl}}/public/mdeditor/editor/Markdown.Extra.js"></script>
<!--mathjax-->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]], processEscapes: true }, messageStyle: "none"});
</script>
<script src="{{.siteUrl}}/public/mdeditor/editor/mathJax.js"></script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
var content = $.trim($("#markdownContent textarea").val());
var converter = Markdown.getSanitizingConverter();
Markdown.Extra.init(converter, {extensions: ["tables", "fenced_code_gfm", "def_list"]});
var html = converter.makeHtml(content);
$("#parsedContent").html(html);
$("pre").addClass("prettyprint linenums");
prettyPrint();
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"wmd-preview"]);
</script>
{{end}}
</body>
</html>
-4
View File
@@ -1,4 +0,0 @@
lif------------
e
you can
+14 -5
View File
@@ -40,23 +40,32 @@
</div> </div>
</div> </div>
<style>
.mce-btn.mce-active, .mce-btn.mce-active:hover {
background-color: #FFFFFF;
}
.mce-btn {
background-color: #FFFFFF;
}
</style>
{{template "member/footer.html" .}} {{template "member/footer.html" .}}
<script type="text/javascript" src="/tinymce/tinymce.js"></script> <script type="text/javascript" src="/tinymce/tinymce.js"></script>
<script> <script>
var urlPrefix = "{{.siteUrl}}"; var urlPrefix = "{{.siteUrl}}";
var LeaAce = {nowIsInAce: function() {return false;}};
$(function() { $(function() {
tinymce.init({ tinymce.init({
selector : "#content1", selector : "#content1",
content_css : [ "/css/bootstrap.css", "/css/editor/editor.css" ], content_css : ["/css/editor/editor.css" ],
skin : "custom", theme : "leanote",
language : "{{.locale}}", language : "{{.locale}}",
height : 300, height : 300,
width : "100%", width : "100%",
skin : "custom", // skin : "custom",
plugins : [ plugins : [
"advlist autolink link leanote_image lists charmap hr ", "advlist autolink link lists charmap hr ",
"searchreplace visualblocks visualchars tabfocus", "searchreplace visualblocks visualchars tabfocus",
"table contextmenu directionality textcolor paste fullpage textcolor"], "table contextmenu directionality textcolor fullpage textcolor"],
toolbar1 : "formatselect |fontselect fontsizeselect| forecolor backcolor | bold italic underline strikethrough | bullist numlist |", toolbar1 : "formatselect |fontselect fontsizeselect| forecolor backcolor | bold italic underline strikethrough | bullist numlist |",
menubar : false, menubar : false,
statusbar : false, statusbar : false,
+21 -7
View File
@@ -55,7 +55,7 @@
{{range .myTpls}} {{range .myTpls}}
<a href="#" class="list-group-item " data-file="{{.}}"> <a href="#" class="list-group-item " data-file="{{.}}">
<i class="fa fa-remove icon-muted remove-file" title="delete file" ></i> <i class="fa fa-remove icon-muted remove-file" title="delete file" ></i>
{{.}} <span class="filename" data-filename="{{.}}">{{.}}</span>
<span class="badge"></span> <span class="badge"></span>
</a> </a>
{{end}} {{end}}
@@ -123,7 +123,7 @@ var theme = {
saveBtnO: $("#saveBtn"), saveBtnO: $("#saveBtn"),
newFileO: $("#newFile"), newFileO: $("#newFile"),
curTpl: "", curTpl: "",
tplInfos: {"header.html": "{{msg . "header"}}", "footer.html": "{{msg . "footer"}}", "index.html": "{{msg . "header"}}", "cate.html": "{{msg . "cate"}}", "search.html": "{{msg . "search"}}", "single.html": "{{msg . "single"}}", "archive.html": "{{msg . "archive"}}", "post.html": "{{msg . "post"}}", "tags.html": "{{msg . "tags"}}", "tag_posts.html": "{{msg . "tag_posts"}}", "share_comment.html": "{{msg . "share_comment"}}", "404.html":"404", "theme.json": "{{msg . "themeJson"}}", "paging.html": "{{msg . "paging"}}", "highlight.html": "{{msg . "highlight"}}"}, tplInfos: {"header.html": "{{msg . "header"}}", "footer.html": "{{msg . "footer"}}", "index.html": "{{msg . "index"}}", "cate.html": "{{msg . "cate"}}", "search.html": "{{msg . "search"}}", "single.html": "{{msg . "single"}}", "archive.html": "{{msg . "archive"}}", "post.html": "{{msg . "post"}}", "tags.html": "{{msg . "tags"}}", "tag_posts.html": "{{msg . "tag_posts"}}", "share_comment.html": "{{msg . "share_comment"}}", "404.html":"404", "theme.json": "{{msg . "themeJson"}}", "paging.html": "{{msg . "paging"}}", "highlight.html": "{{msg . "highlight"}}"},
init: function() { init: function() {
var self = this; var self = this;
self.showTplInfo(); self.showTplInfo();
@@ -171,12 +171,22 @@ var theme = {
newFile: function() { newFile: function() {
var self = this; var self = this;
self.newFileO.click(function() { self.newFileO.click(function() {
art.prompt("文件名", function(file){ // 文件是否存在?
art.prompt("Filename", function(file) {
if(!file) { if(!file) {
return; return;
} }
var t = '<a href="#" class="list-group-item " data-file="' + file + '"> <i class="fa fa-remove icon-muted remove-file" title="delete file" ></i> ' + file + '<span class="badge"></span></a>'; if($('[data-filename="' + file + '"]').length > 0) {
art.alert('File exists');
return;
}
var t = $('<a href="#" class="list-group-item " data-file="' + file + '"> <i class="fa fa-remove icon-muted remove-file" title="delete file" ></i> <span class="filename" data-filename="' + file + '">' + file + '</span><span class="badge"></span></a>');
$(".file-list").append(t); $(".file-list").append(t);
self.cache[file] = "";
// trigger click, 选中之
t.click();
}); });
}); });
}, },
@@ -206,6 +216,9 @@ var theme = {
var content = editor.getValue() var content = editor.getValue()
var filename = self.curTpl; var filename = self.curTpl;
if(!filename) {
return;
}
// 之前的内容是否有改动 // 之前的内容是否有改动
if(self.cache[filename] == content) { if(self.cache[filename] == content) {
return; return;
@@ -234,7 +247,7 @@ var theme = {
renderContent: function(name, content) { renderContent: function(name, content) {
var self = this; var self = this;
self.curTplO.html(name); self.curTplO.html(name);
self.curTpl = name; // 这里设计 self.curTpl = name; // 这里才设置
var lang = "html"; var lang = "html";
if(name.indexOf(".js") > 0 || name.indexOf(".json") > 0) { if(name.indexOf(".js") > 0 || name.indexOf(".json") > 0) {
lang = "javascript"; lang = "javascript";
@@ -251,8 +264,8 @@ var theme = {
renderTpl: function(name) { renderTpl: function(name) {
var self = this; var self = this;
var content = ""; var content = "";
self.curTpl = name; // 这里设置 self.curTpl = ""; // 这里设置为空
if(self.cache[name]) { if(self.cache[name] != undefined) {
content = self.cache[name]; content = self.cache[name];
self.renderContent(name, content); self.renderContent(name, content);
return; return;
@@ -264,6 +277,7 @@ var theme = {
} }
}); });
}, },
//----------------- //-----------------
// theme images // theme images
imageListO: $("#imageList"), imageListO: $("#imageList"),
+3 -9
View File
@@ -36,18 +36,12 @@ var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2
require.config({ require.config({
baseUrl: '/public', baseUrl: '/public',
paths: { paths: {
// ajax upload image/attach 'avatar': 'member/js/avatar',
'attachment_upload': 'js/app/attachment_upload', 'fileupload': 'js/plugins/libs-min/fileupload',
'jquery.ui.widget': 'tinymce/plugins/leaui_image/public/js/jquery.ui.widget',
'fileupload': '/tinymce/plugins/leaui_image/public/js/jquery.fileupload',
'iframe-transport': '/tinymce/plugins/leaui_image/public/js/jquery.iframe-transport'
},
shim: {
'fileupload': {deps: ['jquery.ui.widget', 'iframe-transport']}
} }
}); });
$(function() { $(function() {
require(['attachment_upload'], function(attachment_upload) {}); require(['avatar'], function(avatar) {});
}); });
</script> </script>
+25 -4
View File
@@ -13,11 +13,15 @@
<span class="label label-green">{{msg . "verified"}}</span> <span class="label label-green">{{msg . "verified"}}</span>
{{else}} {{else}}
<span class="label label-red">{{msg . "unVerified"}}</span> <span class="label label-red">{{msg . "unVerified"}}</span>
<a class="raw nowToActive">{{msg . "verifiedNow"}}</a> <a class="a raw nowToActive">{{msg . "verifiedNow"}}</a>
{{msg . "or"}} {{msg . "or"}}
<a class="raw reSendActiveEmail">{{msg . "resendVerifiedEmail"}}</a> <a class="a raw reSendActiveEmail">{{msg . "resendVerifiedEmail"}}</a>
{{end}} {{end}}
<!--
<hr />
<div class="alert alert-danger" id="emailMsg" style="display: none"></div> <div class="alert alert-danger" id="emailMsg" style="display: none"></div>
<div class="form-group"> <div class="form-group">
<label>{{msg . "email"}}</label> <label>{{msg . "email"}}</label>
@@ -29,12 +33,28 @@
]' ]'
data-msg_target="#emailMsg" data-msg_target="#emailMsg"
/> />
{{msg . "updateEmailTips"}} <div class="form-tips">{{msg . "updateEmailTips"}}</div>
</div> </div>
<div class="form-group">
<label class="control-label" for="pwd">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd"
data-rules='[
{rule: "required", msg: "inputPassword"},
{rule: "password", msg: "errorPassword"}
]'
data-msg_target="#emailMsg"
>
<div class="form-tips">{{msg . "inputLoginPasswordTips"}}</div>
</div>
-->
</div> </div>
<!--
<footer class="panel-footer text-right bg-light lter"> <footer class="panel-footer text-right bg-light lter">
<button type="submit" id="emailBtn" class="btn btn-success">{{msg . "submit"}}</button> <button type="submit" id="emailBtn" class="btn btn-success">{{msg . "submit"}}</button>
</footer> </footer>
-->
</section> </section>
</form> </form>
</div> </div>
@@ -76,7 +96,8 @@ $("#emailBtn").click(function(e) {
return; return;
} }
var email = $("#email").val(); var email = $("#email").val();
post("/user/updateEmailSendActiveEmail", {email: email}, function(e) { var pwd = $("#pwd").val();
post("/user/updateEmailSendActiveEmail", {email: email, pwd: pwd}, function(e) {
if(e.Ok) { if(e.Ok) {
var url = getEmailLoginAddress(email); var url = getEmailLoginAddress(email);
showAlert("#emailMsg", getMsg("verifiedEmaiHasSent") +" <a href='" + url + "' target='_blank'>" + getMsg("checkEmail") + "</a>", "success"); showAlert("#emailMsg", getMsg("verifiedEmaiHasSent") +" <a href='" + url + "' target='_blank'>" + getMsg("checkEmail") + "</a>", "success");
+1 -1
View File
@@ -24,7 +24,7 @@
]' ]'
data-msg_target="#pwdMsg" data-msg_target="#pwdMsg"
> >
{{msg . "passwordTips"}} <div class="form-tips">{{msg . "passwordTips"}}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="pwd2">{{msg . "password2"}}</label> <label class="control-label" for="pwd2">{{msg . "password2"}}</label>
+173 -180
View File
@@ -7,24 +7,26 @@
<meta name="apple-touch-fullscreen" content="yes"> <meta name="apple-touch-fullscreen" content="yes">
<meta name=”apple-mobile-web-app-capable” content=”yes” /> <meta name=”apple-mobile-web-app-capable” content=”yes” />
<meta name="keywords" content="leanote,leanote.com"> <meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, Not Just A Notebook"> <meta name="description" content="Leanote, {{msg $ "moto"}}">
<title>leanote, Not Just A Notebook</title> <title>Leanote, {{msg $ "moto"}}</title>
<link href="/css/bootstrap.css" rel="stylesheet" /> <link href="/css/bootstrap-min.css" rel="stylesheet" />
<!-- 先加载, 没有样式, 宽度不定 --> <!-- 先加载, 没有样式, 宽度不定 -->
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" rel="stylesheet"/> <link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" rel="stylesheet"/>
<!-- leanote css --> <!-- leanote css -->
<link href="/css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet" /> <link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
<link href="/css/zTreeStyle/zTreeStyle.css" rel="stylesheet" /> <link href="/css/zTreeStyle/zTreeStyle-min.css" rel="stylesheet" />
<!-- mdeditor --> <!-- mdeditor -->
<link href="/public/dist/themes/default.css" rel="stylesheet" /> <link href="/public/dist/themes/default-min.css" rel="stylesheet" />
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu-min.css" type="text/css" />
<script> <script>
var hash = location.hash; var hash = location.hash;
if(hash.indexOf("writing") >= 0) { if(hash.indexOf("writing") >= 0) {
var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css" type="text/css" id="themeLink" />'; var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css" type="text/css" id="themeLink" />';
} else { } else {
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css" type="text/css" id="themeLink" />'; var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css?t=4" type="text/css" id="themeLink" />';
} }
document.write(files); document.write(files);
</script> </script>
@@ -60,7 +62,7 @@ function log(o) {
<!-- search --> <!-- search -->
<div class="pull-left" id="searchWrap"> <div class="pull-left" id="searchWrap">
<form class="navbar-form form-inline col-lg-2" id="searchNote"> <form class="navbar-form form-inline col-lg-2" id="searchNote">
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput"> <input class="form-control" placeholder="{{msg . "SearchNote"}}" type="text" id="searchNoteInput">
</form> </form>
</div> </div>
@@ -68,45 +70,52 @@ function log(o) {
<div class="pull-left" style="" id="newNoteWrap"> <div class="pull-left" style="" id="newNoteWrap">
<!-- 新建笔记 --> <!-- 新建笔记 -->
<div id="newMyNote"> <div id="newMyNote">
<a id="newNoteBtn" title="{{msg . "newNote"}}"> <a class="new-note" id="newNoteBtn" title="{{msg . "newNote"}}">
<i class="fa fa-file-o"></i> <!--<i class="fa fa-file-o"></i>-->
<span class="new-note-text">{{msg . "newNote"}}</span> <span class="new-note-text">{{msg . "newNote"}}</span>
<span class="new-note-text-abbr">{{msg . "new"}}</span> <span class="new-note-text-abbr">{{msg . "new"}}</span>
</a> </a>
<span class="new-split">|</span> <span class="new-split">|</span>
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}"> <a class="new-note new-markdown" id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">Markdown</span> <!-- <i class="lea-icon icon-markdown"></i> -->
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
<span class="new-markdown-text-abbr">Md</span> <span class="new-markdown-text-abbr">Md</span>
</a> </a>
<span class="for-split"> - </span> <span class="for-split"> - </span>
<span id="curNotebookForNewNote" notebookId=""></span> <span id="curNotebookForNewNote" notebookId=""></span>
<!--
<div class="dropdown" style="display: inline-block"> <div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle" <a class="ios7-a dropdown-toggle"
id="listNotebookDropdownMenu" data-toggle="dropdown"> id="listNotebookDropdownMenu" data-toggle="dropdown">
<i class="fa fa-angle-down"></i> <i class="fa fa-angle-down"></i>
</a> </a>
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList"> <div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
<input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/> <input type="text" placeholder="{{msg . "SearchNotebook"}}" class="form-control" id="searchNotebookForAdd"/>
<ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote"> <ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
</ul> </ul>
</div> </div>
</div> </div>
-->
</div> </div>
<!-- 只为新建别人的笔记 --> <!-- 只为新建别人的笔记 -->
<div id="newSharedNote" style="display: none"> <div id="newSharedNote" style="display: none">
<a id="newSharedNoteBtn"> <a id="newSharedNoteBtn" class="new-note">
<i class="fa fa-file-o"></i> <!--<i class="fa fa-file-o"></i> -->
<span class="new-note-text">{{msg . "newNote"}}</span> <span class="new-note-text">{{msg . "newNote"}}</span>
<span class="new-note-text-abbr">{{msg . "new"}}</span> <span class="new-note-text-abbr">{{msg . "new"}}</span>
</a> </a>
<span class="new-split">|</span> <span class="new-split">|</span>
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}"> <a id="newShareNoteMarkdownBtn" class="new-note new-markdown" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">Markdown</span> <span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
<span class="new-markdown-text-abbr">Md</span> <span class="new-markdown-text-abbr">Md</span>
</a> </a>
<span class="for-split"> - </span> <span class="for-split"> - </span>
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span> <span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<!--
<div class="dropdown" style="display: inline-block"> <div class="dropdown" style="display: inline-block">
<a id="listShareNotebookDropdownMenu" class="ios7-a dropdown-toggle" data-toggle="dropdown"> <a id="listShareNotebookDropdownMenu" class="ios7-a dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-angle-down"></i> <i class="fa fa-angle-down"></i>
@@ -115,6 +124,7 @@ function log(o) {
<ul id="notebookNavForNewSharedNote"></ul> <ul id="notebookNavForNewSharedNote"></ul>
</div> </div>
</div> </div>
-->
</div> </div>
</div> </div>
@@ -157,7 +167,7 @@ function log(o) {
</li> </li>
<li role="presentation" class="my-link" > <li role="presentation" class="my-link" >
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}"> <a target="_blank" href="{{$.userInfo.BlogUrl}}">
<i class="fa fa-bold"></i> <i class="fa fa-bold"></i>
{{msg . "myBlog"}}</a> {{msg . "myBlog"}}</a>
</li> </li>
@@ -186,13 +196,11 @@ function log(o) {
</div> </div>
</div> </div>
<!--
<div class="pull-right top-nav writting-hide lea-blog"> <div class="pull-right top-nav writting-hide lea-blog">
<a target="_blank" href="http://lea.leanote.com"> <a target="_blank" href="http://lea.leanote.com">
lea++ lea++
</a> </a>
</div> </div>
-->
<div class="pull-right top-nav writting-hide" id="myBlog"> <div class="pull-right top-nav writting-hide" id="myBlog">
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}"> <a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
@@ -269,7 +277,7 @@ function log(o) {
</div> </div>
<div class="folderBody"> <div class="folderBody">
<input type="text" class="form-control" id="searchNotebookForList" placeholder="Search notebook"/> <input type="text" class="form-control" id="searchNotebookForList" placeholder="{{msg . "SearchNotebook"}}"/>
<ul class="ztree" id="notebookList"></ul> <ul class="ztree" id="notebookList"></ul>
<ul class="ztree" id="notebookListForSearch"></ul> <ul class="ztree" id="notebookListForSearch"></ul>
</div> </div>
@@ -416,37 +424,16 @@ function log(o) {
<div class="noteSplit" id="noteSplitter"></div> <div class="noteSplit" id="noteSplitter"></div>
<div id="note"> <div id="note">
<!-- 只读模式 -->
<div id="noteRead">
<div id="noteReadContainer">
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix" id="noteReadInfo">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i>{{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i>{{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
<div id="noteReadContent">
</div>
</div>
</div>
<!-- 遮罩, 为了resize3Columns用 --> <!-- 遮罩, 为了resize3Columns用 -->
<div id="noteMask" class="note-mask"></div> <div id="noteMask" class="note-mask"></div>
<div id="noteMaskForLoading" class="note-mask"> <div id="noteMaskForLoading" class="note-mask"> <!-- z-index: 11 -->
<img src="/images/loading-24.gif"/> <img src="/images/loading-24.gif"/>
<br /> <br />
loading... loading...
</div> </div>
<div id="editorMask"> <!-- for tips -->
<div id="editorMask"> <!-- z-index: 10 -->
{{msg . "noNoteNewNoteTips"}} {{msg . "noNoteNewNoteTips"}}
<br /> <br />
<br /> <br />
@@ -458,6 +445,16 @@ function log(o) {
{{msg . "canntNewNoteTips"}} {{msg . "canntNewNoteTips"}}
</div> </div>
</div> </div>
<div id="batchMask" class="note-mask"> <!-- z-index: 99 -->
<div class="batch-ctn" id="batchCtn"></div>
<div class="batch-info">
{{leaMsg . "<span></span> notes selected"}}
<p><i class="fa fa-cog"></i></p>
</div>
</div>
<div id="noteTop"> <div id="noteTop">
<!-- 左侧放tag, 右侧放按钮, 比如save, info, move, delete --> <!-- 左侧放tag, 右侧放按钮, 比如save, info, move, delete -->
<div id="tool" class="clearfix"> <div id="tool" class="clearfix">
@@ -496,34 +493,36 @@ function log(o) {
</div> </div>
<ul class="pull-right" id="editorTool"> <ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s" <li><a class="ios7-a " id="saveBtn"
data-toggle="dropdown"> data-toggle="dropdown" title="ctrl+s {{msg . "save"}}">
<span class="fa fa-save"></span> <span class="fa fa-save"></span></a></li>
{{msg . "save"}}</a></li>
<li class="dropdown" id="noteInfoDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" title="{{msg . "Information"}}">
<span class="fa fa-info"></span>
</a>
<div class="dropdown-menu" id="noteInfo"></div>
</li>
<li class="dropdown" id="attachDropdown"> <li class="dropdown" id="attachDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach"> <a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach" title="{{msg . "attachments"}}">
<span class="fa fa-paperclip"></span> <span class="fa fa-paperclip"></span>
{{msg . "attachments"}}<span id="attachNum"></span> <span id="attachNum"></span>
</a> </a>
<div class="dropdown-menu" id="attachMenu"> <div class="dropdown-menu" id="attachMenu">
<ul id="attachList"> <ul id="attachList">
</ul> </ul>
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data"> <form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data">
<div id="dropAttach" class="dropzone"> <div id="dropAttach" class="dropzone">
<a class="btn btn-success btn-choose-file"> <a class="btn btn-success btn-choose-file">
<i class="fa fa-upload"></i> <i class="fa fa-upload"></i>
<span>Choose File</span> <span>{{msg . "Choose File"}}</span>
</a> </a>
<a class="btn btn-default" id="downloadAllBtn"> <a class="btn btn-default" id="downloadAllBtn">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
<span>Download All</span> <span>{{msg . "Download All"}}</span>
</a>
<a class="btn btn-default" id="linkAllBtn">
<i class="fa fa-link"></i>
<span>Link All</span>
</a> </a>
<input type="file" name="file" multiple/> <input type="file" name="file" multiple/>
</div> </div>
<div id="attachUploadMsg"> <div id="attachUploadMsg">
@@ -532,31 +531,50 @@ function log(o) {
</div> </div>
</li> </li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown">
<span class="fa fa-question"></span>
{{msg . "editorTips"}}</a></li>
<li><a class="ios7-a " id="contentHistory" <li><a class="ios7-a " id="contentHistory"
data-toggle="dropdown"> data-toggle="dropdown" title="{{msg . "history"}}">
<span class="fa fa-history"></span> <span class="fa fa-history"></span></a></li>
{{msg . "history"}}</a></li> <li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown" title="{{msg . "editorTips"}}">
<span class="fa fa-question"></span></a></li>
</ul> </ul>
</div> </div>
<div id="noteTitleDiv"> <div id="noteTitleDiv">
<input name="noteTitle" id="noteTitle" value="" placeholder="{{msg . "unTitled"}}" tabindex="1" /> <input name="noteTitle" id="noteTitle" value="" placeholder="{{msg . "unTitled"}}" tabindex="1" />
</div> </div>
<!-- 只读时 -->
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix" id="noteReadInfo">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i> {{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i> {{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
</div> </div>
<div id="editor"> <div id="editor" class="read-only">
<!-- 编辑器 --> <!-- 编辑器 -->
<div id="mceToolbar"> <div id="mceToolbar">
<div id="popularToolbar" <div id="mceToolbarContainer">
style="position: absolute; right: 30px; left: 0"></div> <div id="popularToolbar" style="position: absolute; right: 30px; left: 0"></div>
<a <a id="moreBtn"> <i class="fa more-fa"></i></a>
style="dispaly: block; position: absolute; cursor: pointer; right: 10px; padding: 3px 10px" </div>
id="moreBtn"> <i class="fa fa-angle-down"></i> <!-- 查看信息 -->
</a> <div id="infoToolbar" class="info-toolbar invisible">
<a class="toolbar-update"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
</div>
</div> </div>
<div class="editorBg"></div> <div class="editorBg"></div>
<div id="leanoteNav" class="leanoteNav"> <div id="leanoteNav" class="leanoteNav">
@@ -571,7 +589,7 @@ function log(o) {
<!-- leaui image drop image to editor--> <!-- leaui image drop image to editor-->
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;"> <form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
<div id="drop"> <div id="drop">
Drop images to here {{leaMsg . "Drop images to here"}}
<input type="file" name="file" multiple style="display: none"/> <input type="file" name="file" multiple style="display: none"/>
</div> </div>
<ul id="uploadMsg"> <ul id="uploadMsg">
@@ -584,50 +602,56 @@ function log(o) {
</div> </div>
</div> </div>
<div id="mdEditor"> <div id="mdEditor" class="read-only">
<div class="layout-wrapper-l1"> <div class="layout-wrapper-l1">
<div class="layout-wrapper-l2"> <div class="layout-wrapper-l2">
<div class="navbar navbar-default"> <div class="navbar navbar-default">
<div class="navbar-inner" id="wmd-button-bar"> <div class="navbar-inner" id="wmd-button-bar">
<ul class="nav left-buttons"> <div id="mdInfoToolbar" class="info-toolbar invisible">
<li class="wmd-button-group1 btn-group"></li> <a class="toolbar-update"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
</ul> <span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
<ul class="nav left-buttons"> <span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
<li class="wmd-button-group2 btn-group"></li> </div>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group3 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group5 btn-group"></li>
</ul>
<!-- 帮助 -->
<ul class="nav left-buttons">
<li class="wmd-button-group6 btn-group">
<li class="wmd-button btn btn-success" id="wmd-help-button" title="Markdown syntax" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
</li>
</ul>
<!-- <div class="wmd-button-bar-inner">
<ul class="nav pull-right right-buttons"> <ul class="nav left-buttons">
<li class="offline-status hide"> <li class="wmd-button-group1 btn-group"></li>
<div class="text-danger"> </ul>
<i class="icon-attention-circled"></i>offline <ul class="nav left-buttons">
</div> <li class="wmd-button-group2 btn-group"></li>
</li> </ul>
<li class="extension-buttons"></li> <ul class="nav left-buttons">
</ul> <li class="wmd-button-group3 btn-group"></li>
<ul class="nav pull-right title-container"> </ul>
<li><div class="working-indicator"></div></li> <ul class="nav left-buttons">
<li><a class="btn btn-success file-title-navbar" href="#" <li class="wmd-button-group5 btn-group"></li>
title="Rename document"> </a></li> </ul>
<li><div class="input-file-title-container"><input type="text" <!-- 帮助 -->
class="col-sm-4 form-control hide input-file-title" <ul class="nav left-buttons">
placeholder="Document title" /></div></li> <li class="wmd-button-group6 btn-group">
</ul> <li class="wmd-button btn btn-success" id="wmd-help-button" title="{{msg . "Markdown syntax"}}" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
--> </li>
</ul>
<!--
<ul class="nav pull-right right-buttons">
<li class="offline-status hide">
<div class="text-danger">
<i class="icon-attention-circled"></i>offline
</div>
</li>
<li class="extension-buttons"></li>
</ul>
<ul class="nav pull-right title-container">
<li><div class="working-indicator"></div></li>
<li><a class="btn btn-success file-title-navbar" href="#"
title="Rename document"> </a></li>
<li><div class="input-file-title-container"><input type="text"
class="col-sm-4 form-control hide input-file-title"
placeholder="Document title" /></div></li>
</ul>
-->
</div>
</div> </div>
<div class="editorBg"></div> <div class="editorBg"></div>
</div> </div>
@@ -685,32 +709,6 @@ function log(o) {
</div> </div>
<!-- mdEditor --> <!-- mdEditor -->
<!-- v2 -->
<div class="modal fade modal-insert-link">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h4 class="modal-title">Hyperlink</h4>
</div>
<div class="modal-body">
<p>Please provide the link URL and an optional title:</p>
<div class="input-group">
<span class="input-group-addon"><i class="icon-globe"></i></span><input
id="input-insert-link" type="text" class="col-sm-5 form-control"
placeholder='http://example.com/ "optional title"' />
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a>
<a href="#" class="btn btn-primary action-insert-link"
data-dismiss="modal">OK</a>
</div>
</div>
</div>
</div>
<!-- 插入图片 --> <!-- 插入图片 -->
<div class="modal fade modal-insert-image"> <div class="modal fade modal-insert-image">
<div class="modal-dialog" style="width: 840px;max-width:100%;"> <div class="modal-dialog" style="width: 840px;max-width:100%;">
@@ -718,15 +716,15 @@ function log(o) {
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" <button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button> aria-hidden="true">&times;</button>
<h4 class="modal-title">Image</h4> <h4 class="modal-title">{{msg . "Image"}}</h4>
</div> </div>
<div class="modal-body" style="padding-top: 0; padding-bottom: 0"> <div class="modal-body" style="padding-top: 0; padding-bottom: 0">
<iframe name="mdImageManager" style="width: 100%; height: 350px" scrolling="no" id="leauiIfrForMD" src="" frameborder="0"></iframe> <iframe name="mdImageManager" style="width: 100%; height: 350px" scrolling="no" id="leauiIfrForMD" src="" frameborder="0"></iframe>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<a href="#" class="btn btn-default" <a href="#" class="btn btn-default"
data-dismiss="modal">Cancel</a> <a href="#" data-dismiss="modal">{{msg . "Cancel"}}</a> <a href="#"
class="btn btn-primary action-insert-image" data-dismiss="modal">Insert Image</a> class="btn btn-primary action-insert-image" data-dismiss="modal">{{msg . "Insert Image"}}</a>
</div> </div>
</div> </div>
</div> </div>
@@ -851,24 +849,7 @@ function log(o) {
</div><!-- /.modal --> </div><!-- /.modal -->
<!-- 编辑器提示 --> <!-- 编辑器提示 -->
<div class="modal fade bs-modal-sm" id="tipsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "editorTips"}}</h4>
</div>
<div class="modal-body">
{{msg . "editorTipsInfo"}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- modal 静态区域 --> <!-- modal 静态区域 -->
<div class="display: hidden"> <div class="display: hidden">
@@ -876,12 +857,11 @@ function log(o) {
</div> </div>
</div> </div>
</div> </div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/jquery.ztree.all-3.5.js"></script>
<script src="/js/i18n/msg.{{.locale}}.js"></script>
<script src="/js/common.js"></script>
<script> <script>
var UrlPrefix = '{{.siteUrl}}'; // 为了发weibo var LEA = {};
LEA.locale = "{{.locale}}";
var UrlPrefix = '{{.siteUrl}}';
var UserInfo = {{.userInfo|jsonJs}}; var UserInfo = {{.userInfo|jsonJs}};
var notebooks = {{.notebooks|jsonJs}}; var notebooks = {{.notebooks|jsonJs}};
var shareNotebooks = {{.shareNotebooks|jsonJs}}; var shareNotebooks = {{.shareNotebooks|jsonJs}};
@@ -894,38 +874,51 @@ var notes = {{.notes|jsonJs}};
var latestNotes = {{if .latestNotes}}{{.latestNotes|jsonJs}}{{else}}[]{{end}}; var latestNotes = {{if .latestNotes}}{{.latestNotes|jsonJs}}{{else}}[]{{end}};
var noteContentJson = {{.noteContentJson|jsonJs}}; var noteContentJson = {{.noteContentJson|jsonJs}};
var tagsJson = {{.tags|jsonJs}}; var tagsJson = {{.tags|jsonJs}};
LEA.locale = "{{.locale}}"; var GlobalConfigs = {{.globalConfigs|jsonJs}};
var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2 <!-- pro_tinymce_init_js -->
</script> </script>
<!-- 渲染view --> <script src="/js/i18n/msg.{{.locale}}.js"></script>
<script src="/tinymce/tinymce.js"></script>
<!-- pro_dep_js -->
<!-- dev -->
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/jquery.ztree.all-3.5.js"></script>
<script src="/js/object_id-min.js"></script>
<script src="/public/tinymce/tinymce.js"></script>
<!-- /dev -->
<script src="/public/libs/ace/ace.js"></script> <script src="/public/libs/ace/ace.js"></script>
<script src="/js/app/page.js"></script>
<!-- dev -->
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script> <script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
<script src="/js/contextmenu/jquery.contextmenu.js"></script> <script src="/js/contextmenu/jquery.contextmenu.js"></script>
<script src="/js/common.js"></script>
<script src="/js/bootstrap-min.js"></script> <script src="/js/bootstrap-min.js"></script>
<script src="/js/app/note.js"></script> <script src="/js/app/note.js"></script>
<script src="/js/app/tag.js"></script> <script src="/js/app/tag.js"></script>
<script src="/js/app/notebook.js"></script> <script src="/js/app/notebook.js"></script>
<script src="/js/app/share.js"></script> <script src="/js/app/share.js"></script>
<script src="/js/object_id-min.js"></script> <script src="/js/app/page.js?t=1"></script>
<!-- /dev -->
<!-- pro_app_js -->
<script> <script>
initPage(); initPage();
</script> // 当tinymce.dev.js时, 请注释require
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
<!-- v2 use require.js, mdeditor -->
<script>
window.baseDir = '/public/dist';
window.require = { window.require = {
baseUrl: window.baseDir, baseUrl: '/public',
deps: ['main']
}; };
</script> </script>
<!-- pro_markdown_js -->
<!-- dev -->
<script src="/js/require.js"></script> <script src="/js/require.js"></script>
</script> <script src="/public/dist/main.min.js"></script>
<!-- /dev -->
<script src="/public/js/plugins/main.js"></script>
</body> </body>
</html> </html>
+163 -188
View File
@@ -7,24 +7,26 @@
<meta name="apple-touch-fullscreen" content="yes"> <meta name="apple-touch-fullscreen" content="yes">
<meta name=”apple-mobile-web-app-capable” content=”yes” /> <meta name=”apple-mobile-web-app-capable” content=”yes” />
<meta name="keywords" content="leanote,leanote.com"> <meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, Not Just A Notebook"> <meta name="description" content="Leanote, {{msg $ "moto"}}">
<title>leanote, Not Just A Notebook</title> <title>Leanote, {{msg $ "moto"}}</title>
<link href="/css/bootstrap.css" rel="stylesheet" /> <link href="/css/bootstrap-min.css" rel="stylesheet" />
<!-- 先加载, 没有样式, 宽度不定 --> <!-- 先加载, 没有样式, 宽度不定 -->
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" rel="stylesheet"/> <link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" rel="stylesheet"/>
<!-- leanote css --> <!-- leanote css -->
<link href="/css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet" /> <link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
<link href="/css/zTreeStyle/zTreeStyle.css" rel="stylesheet" /> <link href="/css/zTreeStyle/zTreeStyle-min.css" rel="stylesheet" />
<!-- mdeditor --> <!-- mdeditor -->
<link href="/public/dist/themes/default.css" rel="stylesheet" /> <link href="/public/dist/themes/default-min.css" rel="stylesheet" />
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu-min.css" type="text/css" />
<script> <script>
var hash = location.hash; var hash = location.hash;
if(hash.indexOf("writing") >= 0) { if(hash.indexOf("writing") >= 0) {
var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css" type="text/css" id="themeLink" />'; var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css" type="text/css" id="themeLink" />';
} else { } else {
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css" type="text/css" id="themeLink" />'; var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css?t=4" type="text/css" id="themeLink" />';
} }
document.write(files); document.write(files);
</script> </script>
@@ -60,7 +62,7 @@ function log(o) {
<!-- search --> <!-- search -->
<div class="pull-left" id="searchWrap"> <div class="pull-left" id="searchWrap">
<form class="navbar-form form-inline col-lg-2" id="searchNote"> <form class="navbar-form form-inline col-lg-2" id="searchNote">
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput"> <input class="form-control" placeholder="{{msg . "SearchNote"}}" type="text" id="searchNoteInput">
</form> </form>
</div> </div>
@@ -68,45 +70,52 @@ function log(o) {
<div class="pull-left" style="" id="newNoteWrap"> <div class="pull-left" style="" id="newNoteWrap">
<!-- 新建笔记 --> <!-- 新建笔记 -->
<div id="newMyNote"> <div id="newMyNote">
<a id="newNoteBtn" title="{{msg . "newNote"}}"> <a class="new-note" id="newNoteBtn" title="{{msg . "newNote"}}">
<i class="fa fa-file-o"></i> <!--<i class="fa fa-file-o"></i>-->
<span class="new-note-text">{{msg . "newNote"}}</span> <span class="new-note-text">{{msg . "newNote"}}</span>
<span class="new-note-text-abbr">{{msg . "new"}}</span> <span class="new-note-text-abbr">{{msg . "new"}}</span>
</a> </a>
<span class="new-split">|</span> <span class="new-split">|</span>
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}"> <a class="new-note new-markdown" id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">Markdown</span> <!-- <i class="lea-icon icon-markdown"></i> -->
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
<span class="new-markdown-text-abbr">Md</span> <span class="new-markdown-text-abbr">Md</span>
</a> </a>
<span class="for-split"> - </span> <span class="for-split"> - </span>
<span id="curNotebookForNewNote" notebookId=""></span> <span id="curNotebookForNewNote" notebookId=""></span>
<!--
<div class="dropdown" style="display: inline-block"> <div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle" <a class="ios7-a dropdown-toggle"
id="listNotebookDropdownMenu" data-toggle="dropdown"> id="listNotebookDropdownMenu" data-toggle="dropdown">
<i class="fa fa-angle-down"></i> <i class="fa fa-angle-down"></i>
</a> </a>
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList"> <div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
<input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/> <input type="text" placeholder="{{msg . "SearchNotebook"}}" class="form-control" id="searchNotebookForAdd"/>
<ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote"> <ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
</ul> </ul>
</div> </div>
</div> </div>
-->
</div> </div>
<!-- 只为新建别人的笔记 --> <!-- 只为新建别人的笔记 -->
<div id="newSharedNote" style="display: none"> <div id="newSharedNote" style="display: none">
<a id="newSharedNoteBtn"> <a id="newSharedNoteBtn" class="new-note">
<i class="fa fa-file-o"></i> <!--<i class="fa fa-file-o"></i> -->
<span class="new-note-text">{{msg . "newNote"}}</span> <span class="new-note-text">{{msg . "newNote"}}</span>
<span class="new-note-text-abbr">{{msg . "new"}}</span> <span class="new-note-text-abbr">{{msg . "new"}}</span>
</a> </a>
<span class="new-split">|</span> <span class="new-split">|</span>
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}"> <a id="newShareNoteMarkdownBtn" class="new-note new-markdown" title="{{msg . "newMarkdown"}}">
<span class="new-markdown-text">Markdown</span> <span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
<span class="new-markdown-text-abbr">Md</span> <span class="new-markdown-text-abbr">Md</span>
</a> </a>
<span class="for-split"> - </span> <span class="for-split"> - </span>
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span> <span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<!--
<div class="dropdown" style="display: inline-block"> <div class="dropdown" style="display: inline-block">
<a id="listShareNotebookDropdownMenu" class="ios7-a dropdown-toggle" data-toggle="dropdown"> <a id="listShareNotebookDropdownMenu" class="ios7-a dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-angle-down"></i> <i class="fa fa-angle-down"></i>
@@ -115,6 +124,7 @@ function log(o) {
<ul id="notebookNavForNewSharedNote"></ul> <ul id="notebookNavForNewSharedNote"></ul>
</div> </div>
</div> </div>
-->
</div> </div>
</div> </div>
@@ -157,7 +167,7 @@ function log(o) {
</li> </li>
<li role="presentation" class="my-link" > <li role="presentation" class="my-link" >
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}"> <a target="_blank" href="{{$.userInfo.BlogUrl}}">
<i class="fa fa-bold"></i> <i class="fa fa-bold"></i>
{{msg . "myBlog"}}</a> {{msg . "myBlog"}}</a>
</li> </li>
@@ -186,13 +196,11 @@ function log(o) {
</div> </div>
</div> </div>
<!--
<div class="pull-right top-nav writting-hide lea-blog"> <div class="pull-right top-nav writting-hide lea-blog">
<a target="_blank" href="http://lea.leanote.com"> <a target="_blank" href="http://lea.leanote.com">
lea++ lea++
</a> </a>
</div> </div>
-->
<div class="pull-right top-nav writting-hide" id="myBlog"> <div class="pull-right top-nav writting-hide" id="myBlog">
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}"> <a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
@@ -269,7 +277,7 @@ function log(o) {
</div> </div>
<div class="folderBody"> <div class="folderBody">
<input type="text" class="form-control" id="searchNotebookForList" placeholder="Search notebook"/> <input type="text" class="form-control" id="searchNotebookForList" placeholder="{{msg . "SearchNotebook"}}"/>
<ul class="ztree" id="notebookList"></ul> <ul class="ztree" id="notebookList"></ul>
<ul class="ztree" id="notebookListForSearch"></ul> <ul class="ztree" id="notebookListForSearch"></ul>
</div> </div>
@@ -416,37 +424,16 @@ function log(o) {
<div class="noteSplit" id="noteSplitter"></div> <div class="noteSplit" id="noteSplitter"></div>
<div id="note"> <div id="note">
<!-- 只读模式 -->
<div id="noteRead">
<div id="noteReadContainer">
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix" id="noteReadInfo">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i>{{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i>{{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
<div id="noteReadContent">
</div>
</div>
</div>
<!-- 遮罩, 为了resize3Columns用 --> <!-- 遮罩, 为了resize3Columns用 -->
<div id="noteMask" class="note-mask"></div> <div id="noteMask" class="note-mask"></div>
<div id="noteMaskForLoading" class="note-mask"> <div id="noteMaskForLoading" class="note-mask"> <!-- z-index: 11 -->
<img src="/images/loading-24.gif"/> <img src="/images/loading-24.gif"/>
<br /> <br />
loading... loading...
</div> </div>
<div id="editorMask"> <!-- for tips -->
<div id="editorMask"> <!-- z-index: 10 -->
{{msg . "noNoteNewNoteTips"}} {{msg . "noNoteNewNoteTips"}}
<br /> <br />
<br /> <br />
@@ -458,6 +445,16 @@ function log(o) {
{{msg . "canntNewNoteTips"}} {{msg . "canntNewNoteTips"}}
</div> </div>
</div> </div>
<div id="batchMask" class="note-mask"> <!-- z-index: 99 -->
<div class="batch-ctn" id="batchCtn"></div>
<div class="batch-info">
{{leaMsg . "<span></span> notes selected"}}
<p><i class="fa fa-cog"></i></p>
</div>
</div>
<div id="noteTop"> <div id="noteTop">
<!-- 左侧放tag, 右侧放按钮, 比如save, info, move, delete --> <!-- 左侧放tag, 右侧放按钮, 比如save, info, move, delete -->
<div id="tool" class="clearfix"> <div id="tool" class="clearfix">
@@ -496,34 +493,36 @@ function log(o) {
</div> </div>
<ul class="pull-right" id="editorTool"> <ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s" <li><a class="ios7-a " id="saveBtn"
data-toggle="dropdown"> data-toggle="dropdown" title="ctrl+s {{msg . "save"}}">
<span class="fa fa-save"></span> <span class="fa fa-save"></span></a></li>
{{msg . "save"}}</a></li>
<li class="dropdown" id="noteInfoDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" title="{{msg . "Information"}}">
<span class="fa fa-info"></span>
</a>
<div class="dropdown-menu" id="noteInfo"></div>
</li>
<li class="dropdown" id="attachDropdown"> <li class="dropdown" id="attachDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach"> <a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach" title="{{msg . "attachments"}}">
<span class="fa fa-paperclip"></span> <span class="fa fa-paperclip"></span>
{{msg . "attachments"}}<span id="attachNum"></span> <span id="attachNum"></span>
</a> </a>
<div class="dropdown-menu" id="attachMenu"> <div class="dropdown-menu" id="attachMenu">
<ul id="attachList"> <ul id="attachList">
</ul> </ul>
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data"> <form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data">
<div id="dropAttach" class="dropzone"> <div id="dropAttach" class="dropzone">
<a class="btn btn-success btn-choose-file"> <a class="btn btn-success btn-choose-file">
<i class="fa fa-upload"></i> <i class="fa fa-upload"></i>
<span>Choose File</span> <span>{{msg . "Choose File"}}</span>
</a> </a>
<a class="btn btn-default" id="downloadAllBtn"> <a class="btn btn-default" id="downloadAllBtn">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
<span>Download All</span> <span>{{msg . "Download All"}}</span>
</a>
<a class="btn btn-default" id="linkAllBtn">
<i class="fa fa-link"></i>
<span>Link All</span>
</a> </a>
<input type="file" name="file" multiple/> <input type="file" name="file" multiple/>
</div> </div>
<div id="attachUploadMsg"> <div id="attachUploadMsg">
@@ -532,31 +531,50 @@ function log(o) {
</div> </div>
</li> </li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown">
<span class="fa fa-question"></span>
{{msg . "editorTips"}}</a></li>
<li><a class="ios7-a " id="contentHistory" <li><a class="ios7-a " id="contentHistory"
data-toggle="dropdown"> data-toggle="dropdown" title="{{msg . "history"}}">
<span class="fa fa-history"></span> <span class="fa fa-history"></span></a></li>
{{msg . "history"}}</a></li> <li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown" title="{{msg . "editorTips"}}">
<span class="fa fa-question"></span></a></li>
</ul> </ul>
</div> </div>
<div id="noteTitleDiv"> <div id="noteTitleDiv">
<input name="noteTitle" id="noteTitle" value="" placeholder="{{msg . "unTitled"}}" tabindex="1" /> <input name="noteTitle" id="noteTitle" value="" placeholder="{{msg . "unTitled"}}" tabindex="1" />
</div> </div>
<!-- 只读时 -->
<div id="noteReadTop">
<h2 id="noteReadTitle"></h2>
<div class="clearfix" id="noteReadInfo">
<i class="fa fa-bookmark-o"></i>
<span id="noteReadTags"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i> {{msg . "update"}}
<span id="noteReadUpdatedTime"></span>
<!-- 修改时间 -->
<i class="fa fa-clock-o"></i> {{msg . "create"}}
<span id="noteReadCreatedTime"></span>
</div>
</div>
</div> </div>
<div id="editor"> <div id="editor" class="read-only">
<!-- 编辑器 --> <!-- 编辑器 -->
<div id="mceToolbar"> <div id="mceToolbar">
<div id="popularToolbar" <div id="mceToolbarContainer">
style="position: absolute; right: 30px; left: 0"></div> <div id="popularToolbar" style="position: absolute; right: 30px; left: 0"></div>
<a <a id="moreBtn"> <i class="fa more-fa"></i></a>
style="dispaly: block; position: absolute; cursor: pointer; right: 10px; padding: 3px 10px" </div>
id="moreBtn"> <i class="fa fa-angle-down"></i> <!-- 查看信息 -->
</a> <div id="infoToolbar" class="info-toolbar invisible">
<a class="toolbar-update"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
</div>
</div> </div>
<div class="editorBg"></div> <div class="editorBg"></div>
<div id="leanoteNav" class="leanoteNav"> <div id="leanoteNav" class="leanoteNav">
@@ -571,7 +589,7 @@ function log(o) {
<!-- leaui image drop image to editor--> <!-- leaui image drop image to editor-->
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;"> <form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
<div id="drop"> <div id="drop">
Drop images to here {{leaMsg . "Drop images to here"}}
<input type="file" name="file" multiple style="display: none"/> <input type="file" name="file" multiple style="display: none"/>
</div> </div>
<ul id="uploadMsg"> <ul id="uploadMsg">
@@ -584,50 +602,56 @@ function log(o) {
</div> </div>
</div> </div>
<div id="mdEditor"> <div id="mdEditor" class="read-only">
<div class="layout-wrapper-l1"> <div class="layout-wrapper-l1">
<div class="layout-wrapper-l2"> <div class="layout-wrapper-l2">
<div class="navbar navbar-default"> <div class="navbar navbar-default">
<div class="navbar-inner" id="wmd-button-bar"> <div class="navbar-inner" id="wmd-button-bar">
<ul class="nav left-buttons"> <div id="mdInfoToolbar" class="info-toolbar invisible">
<li class="wmd-button-group1 btn-group"></li> <a class="toolbar-update"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
</ul> <span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
<ul class="nav left-buttons"> <span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
<li class="wmd-button-group2 btn-group"></li> </div>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group3 btn-group"></li>
</ul>
<ul class="nav left-buttons">
<li class="wmd-button-group5 btn-group"></li>
</ul>
<!-- 帮助 -->
<ul class="nav left-buttons">
<li class="wmd-button-group6 btn-group">
<li class="wmd-button btn btn-success" id="wmd-help-button" title="Markdown syntax" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
</li>
</ul>
<!-- <div class="wmd-button-bar-inner">
<ul class="nav pull-right right-buttons"> <ul class="nav left-buttons">
<li class="offline-status hide"> <li class="wmd-button-group1 btn-group"></li>
<div class="text-danger"> </ul>
<i class="icon-attention-circled"></i>offline <ul class="nav left-buttons">
</div> <li class="wmd-button-group2 btn-group"></li>
</li> </ul>
<li class="extension-buttons"></li> <ul class="nav left-buttons">
</ul> <li class="wmd-button-group3 btn-group"></li>
<ul class="nav pull-right title-container"> </ul>
<li><div class="working-indicator"></div></li> <ul class="nav left-buttons">
<li><a class="btn btn-success file-title-navbar" href="#" <li class="wmd-button-group5 btn-group"></li>
title="Rename document"> </a></li> </ul>
<li><div class="input-file-title-container"><input type="text" <!-- 帮助 -->
class="col-sm-4 form-control hide input-file-title" <ul class="nav left-buttons">
placeholder="Document title" /></div></li> <li class="wmd-button-group6 btn-group">
</ul> <li class="wmd-button btn btn-success" id="wmd-help-button" title="{{msg . "Markdown syntax"}}" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
--> </li>
</ul>
<!--
<ul class="nav pull-right right-buttons">
<li class="offline-status hide">
<div class="text-danger">
<i class="icon-attention-circled"></i>offline
</div>
</li>
<li class="extension-buttons"></li>
</ul>
<ul class="nav pull-right title-container">
<li><div class="working-indicator"></div></li>
<li><a class="btn btn-success file-title-navbar" href="#"
title="Rename document"> </a></li>
<li><div class="input-file-title-container"><input type="text"
class="col-sm-4 form-control hide input-file-title"
placeholder="Document title" /></div></li>
</ul>
-->
</div>
</div> </div>
<div class="editorBg"></div> <div class="editorBg"></div>
</div> </div>
@@ -685,32 +709,6 @@ function log(o) {
</div> </div>
<!-- mdEditor --> <!-- mdEditor -->
<!-- v2 -->
<div class="modal fade modal-insert-link">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h4 class="modal-title">Hyperlink</h4>
</div>
<div class="modal-body">
<p>Please provide the link URL and an optional title:</p>
<div class="input-group">
<span class="input-group-addon"><i class="icon-globe"></i></span><input
id="input-insert-link" type="text" class="col-sm-5 form-control"
placeholder='http://example.com/ "optional title"' />
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a>
<a href="#" class="btn btn-primary action-insert-link"
data-dismiss="modal">OK</a>
</div>
</div>
</div>
</div>
<!-- 插入图片 --> <!-- 插入图片 -->
<div class="modal fade modal-insert-image"> <div class="modal fade modal-insert-image">
<div class="modal-dialog" style="width: 840px;max-width:100%;"> <div class="modal-dialog" style="width: 840px;max-width:100%;">
@@ -718,15 +716,15 @@ function log(o) {
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" <button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button> aria-hidden="true">&times;</button>
<h4 class="modal-title">Image</h4> <h4 class="modal-title">{{msg . "Image"}}</h4>
</div> </div>
<div class="modal-body" style="padding-top: 0; padding-bottom: 0"> <div class="modal-body" style="padding-top: 0; padding-bottom: 0">
<iframe name="mdImageManager" style="width: 100%; height: 350px" scrolling="no" id="leauiIfrForMD" src="" frameborder="0"></iframe> <iframe name="mdImageManager" style="width: 100%; height: 350px" scrolling="no" id="leauiIfrForMD" src="" frameborder="0"></iframe>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<a href="#" class="btn btn-default" <a href="#" class="btn btn-default"
data-dismiss="modal">Cancel</a> <a href="#" data-dismiss="modal">{{msg . "Cancel"}}</a> <a href="#"
class="btn btn-primary action-insert-image" data-dismiss="modal">Insert Image</a> class="btn btn-primary action-insert-image" data-dismiss="modal">{{msg . "Insert Image"}}</a>
</div> </div>
</div> </div>
</div> </div>
@@ -851,24 +849,7 @@ function log(o) {
</div><!-- /.modal --> </div><!-- /.modal -->
<!-- 编辑器提示 --> <!-- 编辑器提示 -->
<div class="modal fade bs-modal-sm" id="tipsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" class="modalTitle">{{msg . "editorTips"}}</h4>
</div>
<div class="modal-body">
{{msg . "editorTipsInfo"}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- modal 静态区域 --> <!-- modal 静态区域 -->
<div class="display: hidden"> <div class="display: hidden">
@@ -876,12 +857,11 @@ function log(o) {
</div> </div>
</div> </div>
</div> </div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/jquery.ztree.all-3.5-min.js"></script>
<script src="/js/i18n/msg.{{.locale}}.js"></script>
<script src="/js/common-min.js"></script>
<script> <script>
var UrlPrefix = '{{.siteUrl}}'; // 为了发weibo var LEA = {};
LEA.locale = "{{.locale}}";
var UrlPrefix = '{{.siteUrl}}';
var UserInfo = {{.userInfo|jsonJs}}; var UserInfo = {{.userInfo|jsonJs}};
var notebooks = {{.notebooks|jsonJs}}; var notebooks = {{.notebooks|jsonJs}};
var shareNotebooks = {{.shareNotebooks|jsonJs}}; var shareNotebooks = {{.shareNotebooks|jsonJs}};
@@ -894,38 +874,33 @@ var notes = {{.notes|jsonJs}};
var latestNotes = {{if .latestNotes}}{{.latestNotes|jsonJs}}{{else}}[]{{end}}; var latestNotes = {{if .latestNotes}}{{.latestNotes|jsonJs}}{{else}}[]{{end}};
var noteContentJson = {{.noteContentJson|jsonJs}}; var noteContentJson = {{.noteContentJson|jsonJs}};
var tagsJson = {{.tags|jsonJs}}; var tagsJson = {{.tags|jsonJs}};
LEA.locale = "{{.locale}}"; var GlobalConfigs = {{.globalConfigs|jsonJs}};
var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2 var tinyMCEPreInit = {base: '/public/tinymce', suffix: '.min'};
</script> </script>
<!-- 渲染view --> <script src="/js/i18n/msg.{{.locale}}.js"></script>
<script src="/tinymce/tinymce.min.js"></script>
<script src="/js/dep.min.js"></script>
<script src="/public/libs/ace/ace.js"></script> <script src="/public/libs/ace/ace.js"></script>
<script src="/js/app/page-min.js"></script>
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll-min.js"></script>
<script src="/js/contextmenu/jquery.contextmenu-min.js"></script>
<script src="/js/bootstrap-min.js"></script> <script src="/js/app.min.js"></script>
<script src="/js/app/note-min.js"></script>
<script src="/js/app/tag-min.js"></script>
<script src="/js/app/notebook-min.js"></script>
<script src="/js/app/share-min.js"></script>
<script src="/js/object_id-min.js"></script>
<script> <script>
initPage(); initPage();
</script> // 当tinymce.dev.js时, 请注释require
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
<!-- v2 use require.js, mdeditor -->
<script>
window.baseDir = '/public/dist';
window.require = { window.require = {
baseUrl: window.baseDir, baseUrl: '/public',
deps: ['main']
}; };
</script> </script>
<script src="/js/require.js"></script>
</script> <script src="/js/markdown.min.js"></script>
<script src="/public/js/plugins/main.min.js"></script>
</body> </body>
</html> </html>
-32
View File
@@ -1,32 +0,0 @@
{{template "home/header_box.html" .}}
<section id="box" class="animated fadeInUp">
<div>
<h1 id="logo">leanote</h1>
<div id="boxForm">
<div id="boxHeader">We got a error</div>
<form>
<div class="alert alert-danger" id="loginMsg" style="display: block">
Sorry, we can't get your infomation.
<br />
Please <a href="/login">{{msg . "login"}}</a> Or <a href="/register?email={{.email}}">{{msg . "register"}}</a>
</div>
</form>
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
</body>
</html>
+100 -38
View File
@@ -2,40 +2,85 @@
# release leanote # release leanote
# 当前路径
SP=$(cd "$(dirname "$0")"; pwd) SP=$(cd "$(dirname "$0")"; pwd)
cd $SP
cd ../
# tmp path to store leanote release files # tmp path to store leanote release files
tmp="/Users/life/Desktop/leanote_release" tmp="/Users/life/Desktop/leanote_release"
version=x86_64.v1.0-beta.2 # version
V="v1.2"
##=================================
# 1. 先build 成 3个平台, 2种bit = 6种
##=================================
# cd /Documents/Go/package2/src/github.com/leanote/leanote/bin
# GOOS=darwin GOARCH=amd64 go build -o leanote-darwin-amd64 ../app/tmp
cd $SP
# $1 = darwin, linux
# $2 = amd64
function build()
{
echo build-$1-$2
if [ $1 = "linux" -o $1 = "darwin" ]
then
suffix=""
else
suffix=".exe"
fi
GOOS=$1 GOARCH=$2 go build -o leanote-$1-$2$suffix ../app/tmp
}
build "linux" "386";
build "linux" "amd64";
build "windows" "386";
build "windows" "amd64";
# build "darwin" "386";
build "darwin" "amd64";
##======================
# 2. release目录准备工作
##======================
rm -rf $tmp/leanote rm -rf $tmp/leanote
mkdir -p $tmp/leanote/app mkdir -p $tmp/leanote/app
mkdir -p $tmp/leanote/conf mkdir -p $tmp/leanote/conf
mkdir -p $tmp/leanote/bin mkdir -p $tmp/leanote/bin
##==================
# 3. 复制
##==================
cd $SP
cd ../
# bin # bin
cp -r ./bin/src $tmp/leanote/bin/ cp -r ./bin/src $tmp/leanote/bin/
cp ./bin/run.sh $tmp/leanote/bin/ cp ./bin/run.sh $tmp/leanote/bin/
cp ./bin/run.bat $tmp/leanote/bin/
# LICENSE
cp ./LICENSE $tmp/leanote/
# views # views
cp -r ./app/views $tmp/leanote/app cp -r ./app/views $tmp/leanote/app
# 可不要 # 可不要, 源码
cp -r ./app/service $tmp/leanote/app/service #cp -r ./app/service $tmp/leanote/app/service
cp -r ./app/controllers $tmp/leanote/app/controllers #cp -r ./app/controllers $tmp/leanote/app/controllers
cp -r ./app/db $tmp/leanote/app/db #cp -r ./app/db $tmp/leanote/app/db
cp -r ./app/info $tmp/leanote/app/info #cp -r ./app/info $tmp/leanote/app/info
cp -r ./app/lea $tmp/leanote/app/lea #cp -r ./app/lea $tmp/leanote/app/lea
# conf # conf
cp ./conf/app.conf-default $tmp/leanote/conf/app.conf cp ./conf/app.conf $tmp/leanote/conf/app.conf
cp ./conf/routes-default $tmp/leanote/conf/routes cp ./conf/routes $tmp/leanote/conf/routes
# 处理app.conf, 还原配置
cat $tmp/leanote/conf/app.conf | sed 's/db.dbname=leanote.*#/db.dbname=leanote #/' > $tmp/leanote/conf/app.conf2 # 不能直接覆盖
rm $tmp/leanote/conf/app.conf
mv $tmp/leanote/conf/app.conf2 $tmp/leanote/conf/app.conf
# others # others
cp -r ./messages ./public ./mongodb_backup $tmp/leanote/ cp -r ./messages ./public ./mongodb_backup $tmp/leanote/
@@ -44,40 +89,57 @@ cp -r ./messages ./public ./mongodb_backup $tmp/leanote/
rm -r $tmp/leanote/public/tinymce/classes rm -r $tmp/leanote/public/tinymce/classes
rm -r $tmp/leanote/public/upload rm -r $tmp/leanote/public/upload
mkdir $tmp/leanote/public/upload mkdir $tmp/leanote/public/upload
mkdir $tmp/leanote/files
rm -r $tmp/leanote/public/.codekit-cache rm -r $tmp/leanote/public/.codekit-cache
rm $tmp/leanote/public/.DS_Store rm $tmp/leanote/public/.DS_Store
rm $tmp/leanote/public/config.codekit rm $tmp/leanote/public/config.codekit
# make link # make link
cd $tmp/leanote/bin # cd $tmp/leanote/bin
mkdir ./src/github.com/leanote
# ln -s ../../../../ ./src/github.com/leanote/leanote # ln -s ../../../../ ./src/github.com/leanote/leanote
# archieve # archieve
# << 'BLOCK # << 'BLOCK
# linux
cd $SP
cd ../
cp ./bin/leanote-linux $tmp/leanote/bin/
cd $tmp
tar -cvf $tmp/leanote-linux-$version.bin.tar leanote
gzip $tmp/leanote-linux-$version.bin.tar
# mac ##===========
rm $tmp/leanote/bin/leanote-linux # 4. 打包
cd $SP ##===========
cd ../ # $1 = linux
cp ./bin/leanote-mac $tmp/leanote/bin/ # $2 = 386, amd64
cd $tmp
tar -cvf $tmp/leanote-mac-$version.bin.tar leanote
gzip $tmp/leanote-mac-$version.bin.tar
cd $SP # 创建一个$V的目录存放之
cd ../ rm -rf $tmp/$V
cp ./bin/leanote-linux $tmp/leanote/bin/ mkdir $tmp/$V
rm $tmp/bin/src/github.com/leanote/leanote function tarRelease()
cp -r $tmp/leanote/* $tmp/leanote_release_github {
echo tar-$1-$2
cd $SP
cd ../
rm $tmp/leanote/bin/leanote-* # 删除之前的bin文件
rm $tmp/leanote/bin/run* # 删除之前的run.sh 或 run.bat
if [ $1 = "linux" -o $1 = "darwin" ]
then
suffix=""
cp ./bin/run.sh $tmp/leanote/bin/
else
cp ./bin/run.bat $tmp/leanote/bin/
suffix=".exe"
fi
cp ./bin/leanote-$1-$2$suffix $tmp/leanote/bin/
cd $tmp
tar -cf $tmp/$V/leanote-$1-$2-$V.bin.tar leanote
gzip $tmp/$V/leanote-$1-$2-$V.bin.tar
}
tarRelease "linux" "386";
tarRelease "linux" "amd64";
tarRelease "windows" "386";
tarRelease "windows" "amd64";
# tarRelease "darwin" "386";
tarRelease "darwin" "amd64";
# BLOCK' # BLOCK'
+23
View File
@@ -0,0 +1,23 @@
@echo off
cd..
set SCRIPTPATH=%cd%
: top src directory
set path="%SCRIPTPATH%\bin\src\github.com\leanote"
if not exist "%path%" mkdir "%path%"
: create software link
if exist "%path%\leanote" del /Q "%path%\leanote"
mklink /D "%path%\leanote" %SCRIPTPATH%
: set GOPATH
set GOPATH="%SCRIPTPATH%\bin"
: run
if %processor_architecture%==x86 (
"%SCRIPTPATH%\bin\leanote-windows-386.exe" -importPath github.com/leanote/leanote
) else (
"%SCRIPTPATH%\bin\leanote-windows-amd64.exe" -importPath github.com/leanote/leanote
)
+13 -7
View File
@@ -7,17 +7,23 @@ path="$SCRIPTPATH/src/github.com/leanote"
if [ ! -d "$path" ]; then if [ ! -d "$path" ]; then
mkdir -p "$path" mkdir -p "$path"
fi fi
rm -rf $SCRIPTPATH/src/github.com/leanote/leanote # 先删除
ln -s ../../../../ $SCRIPTPATH/src/github.com/leanote/leanote ln -s ../../../../ $SCRIPTPATH/src/github.com/leanote/leanote
# set GOPATH # set GOPATH
export GOPATH=$GOPATH:$SCRIPTPATH export GOPATH=$SCRIPTPATH
# run # run
osName=`uname` osName=`uname` # Darwin or Linux
if [ $osName == "Darwin" ]; then osName=`tr '[A-Z]' '[a-z]' <<<"$osName"` # toLowerCase
chmod 777 "$SCRIPTPATH/leanote-mac" bit=`getconf LONG_BIT` # 32, 64
"$SCRIPTPATH/leanote-mac" -importPath github.com/leanote/leanote if [ $bit = "64" ]
then
bit="amd64"
else else
chmod 777 "$SCRIPTPATH/leanote-linux" bit="386"
"$SCRIPTPATH/leanote-linux" -importPath github.com/leanote/leanote
fi fi
script="$SCRIPTPATH/leanote-$osName-$bit"
chmod 777 $script
$script -importPath github.com/leanote/leanote
@@ -1,100 +0,0 @@
package controllers
import (
"github.com/revel/revel"
"os"
fpath "path/filepath"
"strings"
"syscall"
)
type Static struct {
*revel.Controller
}
// This method handles requests for files. The supplied prefix may be absolute
// or relative. If the prefix is relative it is assumed to be relative to the
// application directory. The filepath may either be just a file or an
// additional filepath to search for the given file. This response may return
// the following responses in the event of an error or invalid request;
// 403(Forbidden): If the prefix filepath combination results in a directory.
// 404(Not found): If the prefix and filepath combination results in a non-existent file.
// 500(Internal Server Error): There are a few edge cases that would likely indicate some configuration error outside of revel.
//
// Note that when defining routes in routes/conf the parameters must not have
// spaces around the comma.
// Bad: Static.Serve("public/img", "favicon.png")
// Good: Static.Serve("public/img","favicon.png")
//
// Examples:
// Serving a directory
// Route (conf/routes):
// GET /public/{<.*>filepath} Static.Serve("public")
// Request:
// public/js/sessvars.js
// Calls
// Static.Serve("public","js/sessvars.js")
//
// Serving a file
// Route (conf/routes):
// GET /favicon.ico Static.Serve("public/img","favicon.png")
// Request:
// favicon.ico
// Calls:
// Static.Serve("public/img", "favicon.png")
func (c Static) Serve(prefix, filepath string) revel.Result {
var basePath string
if !fpath.IsAbs(prefix) {
basePath = revel.BasePath
}
basePathPrefix := fpath.Join(basePath, fpath.FromSlash(prefix))
fname := fpath.Join(basePathPrefix, fpath.FromSlash(filepath))
if !strings.HasPrefix(fname, basePathPrefix) {
revel.WARN.Printf("Attempted to read file outside of base path: %s", fname)
return c.NotFound("")
}
finfo, err := os.Stat(fname)
if err != nil {
if os.IsNotExist(err) || err.(*os.PathError).Err == syscall.ENOTDIR {
revel.WARN.Printf("File not found (%s): %s ", fname, err)
return c.NotFound("File not found")
}
revel.ERROR.Printf("Error trying to get fileinfo for '%s': %s", fname, err)
return c.RenderError(err)
}
if finfo.Mode().IsDir() {
revel.WARN.Printf("Attempted directory listing of %s", fname)
return c.Forbidden("Directory listing not allowed")
}
file, err := os.Open(fname)
if err != nil {
if os.IsNotExist(err) {
revel.WARN.Printf("File not found (%s): %s ", fname, err)
return c.NotFound("File not found")
}
revel.ERROR.Printf("Error opening '%s': %s", fname, err)
return c.RenderError(err)
}
return c.RenderFile(file, revel.Inline)
}
// This method allows modules to serve binary files. The parameters are the same
// as Static.Serve with the additional module name pre-pended to the list of
// arguments.
func (c Static) ServeModule(moduleName, prefix, filepath string) revel.Result {
var basePath string
for _, module := range revel.Modules {
if module.Name == moduleName {
basePath = module.Path
}
}
absPath := fpath.Join(basePath, fpath.FromSlash(prefix))
return c.Serve(absPath, filepath)
}
@@ -1,152 +0,0 @@
package controllers
import (
"bytes"
"fmt"
"github.com/revel/revel"
"html"
"html/template"
"reflect"
"strings"
)
type TestRunner struct {
*revel.Controller
}
type TestSuiteDesc struct {
Name string
Tests []TestDesc
}
type TestDesc struct {
Name string
}
type TestSuiteResult struct {
Name string
Passed bool
Results []TestResult
}
type TestResult struct {
Name string
Passed bool
ErrorHtml template.HTML
ErrorSummary string
}
var NONE = []reflect.Value{}
func (c TestRunner) Index() revel.Result {
var testSuites []TestSuiteDesc
for _, testSuite := range revel.TestSuites {
testSuites = append(testSuites, DescribeSuite(testSuite))
}
return c.Render(testSuites)
}
// Run runs a single test, given by the argument.
func (c TestRunner) Run(suite, test string) revel.Result {
result := TestResult{Name: test}
for _, testSuite := range revel.TestSuites {
t := reflect.TypeOf(testSuite).Elem()
if t.Name() != suite {
continue
}
// Found the suite, create a new instance and run the named method.
v := reflect.New(t)
func() {
defer func() {
if err := recover(); err != nil {
error := revel.NewErrorFromPanic(err)
if error == nil {
result.ErrorHtml = template.HTML(html.EscapeString(fmt.Sprint(err)))
} else {
var buffer bytes.Buffer
tmpl, _ := revel.MainTemplateLoader.Template("TestRunner/FailureDetail.html")
tmpl.Render(&buffer, error)
result.ErrorSummary = errorSummary(error)
result.ErrorHtml = template.HTML(buffer.String())
}
}
}()
// Initialize the test suite with a NewTestSuite()
testSuiteInstance := v.Elem().FieldByName("TestSuite")
testSuiteInstance.Set(reflect.ValueOf(revel.NewTestSuite()))
// Call Before(), call the test, and call After().
if m := v.MethodByName("Before"); m.IsValid() {
m.Call(NONE)
}
if m := v.MethodByName("After"); m.IsValid() {
defer m.Call(NONE)
}
v.MethodByName(test).Call(NONE)
// No panic means success.
result.Passed = true
}()
break
}
return c.RenderJson(result)
}
// List returns a JSON list of test suites and tests.
// Used by the "test" command line tool.
func (c TestRunner) List() revel.Result {
var testSuites []TestSuiteDesc
for _, testSuite := range revel.TestSuites {
testSuites = append(testSuites, DescribeSuite(testSuite))
}
return c.RenderJson(testSuites)
}
func DescribeSuite(testSuite interface{}) TestSuiteDesc {
t := reflect.TypeOf(testSuite)
// Get a list of methods of the embedded test type.
super := t.Elem().Field(0).Type
superMethodNameSet := map[string]struct{}{}
for i := 0; i < super.NumMethod(); i++ {
superMethodNameSet[super.Method(i).Name] = struct{}{}
}
// Get a list of methods on the test suite that take no parameters, return
// no results, and were not part of the embedded type's method set.
var tests []TestDesc
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
mt := m.Type
_, isSuperMethod := superMethodNameSet[m.Name]
if mt.NumIn() == 1 &&
mt.NumOut() == 0 &&
mt.In(0) == t &&
!isSuperMethod &&
strings.HasPrefix(m.Name, "Test") {
tests = append(tests, TestDesc{m.Name})
}
}
return TestSuiteDesc{
Name: t.Elem().Name(),
Tests: tests,
}
}
func errorSummary(error *revel.Error) string {
var message = fmt.Sprintf("%4sStatus: %s\n%4sIn %s", "", error.Description, "", error.Path)
if error.Line != 0 {
message += fmt.Sprintf(" (around line %d): ", error.Line)
for _, line := range error.ContextSource() {
if line.IsError {
message += line.Source
}
}
}
return message
}
@@ -1,12 +0,0 @@
package app
import (
"fmt"
"github.com/revel/revel"
)
func init() {
revel.OnAppStart(func() {
fmt.Println("Go to /@tests to run the tests.")
})
}
@@ -1,9 +0,0 @@
<b>{{.Description}}</b><br>
In {{.Path}}
{{if .Line}}
(around {{if .Line}}line {{.Line}}{{end}}{{if .Column}} column {{.Column}}{{end}})
{{end}}:
{{range .ContextSource}}{{if .IsError}}<code>{{.Source}}</code>{{end}}{{end}}<br>
<a style="cursor:pointer;"
onclick="x=this.nextSibling.style;if(!x.display)x.display='none';else x.display=''">
Show Stack</a><pre style="display:none;">{{.Stack}}</pre>
@@ -1,84 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Revel Test Runner</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link href="/@tests/public/css/bootstrap.css" type="text/css" rel="stylesheet"></link>
<script src="/@tests/public/js/jquery-1.9.1.min.js" type="text/javascript"></script>
<style>
header { padding:20px 0; background-color:#ADD8E6 }
.passed td { background-color: #90EE90 !important; }
.failed td { background-color: #FFB6C1 !important; }
.tests td.name, .tests td.result { padding-top: 13px; }
pre { font-size:10px; white-space: pre; }
</style>
</head>
<body>
<header>
<div class="container">
<table><tr><td>
<h1>Test Runner</h1>
<p class="lead">Run all of your application's tests from here.</p>
</td><td style="padding-left:150px;">
<button class="btn btn-large" all-tests="">Run All Tests</button>
</td></tr></table>
</div>
</header>
<div class="container">
{{range .testSuites}}
<p class="lead" style="margin-top:20px;">{{.Name}}</p>
<table class="table table-striped tests" suite="{{.Name}}">
{{range .Tests}}
<tr>
<td class="name">{{.Name}}</td>
<td class="result">
</td>
<td><button test="{{.Name}}" class="btn">Run</button></td>
</tr>
{{end}}
</table>
{{end}}
</div>
<script>
var running = 0;
$("button[test]").click(function() {
var button = $(this).addClass("disabled").text("Running");
running += 1;
runTest(button);
});
$("button[all-tests]").click(function() {
var button = $(this).addClass("disabled").text("Running");
$("button[test]").click();
});
function runTest(button) {
var suite = button.parents("table").attr("suite");
var test = button.attr("test");
var row = button.parents("tr");
var resultCell = row.children(".result");
$.ajax({
dataType: "json",
url: "/@tests/"+suite+"/"+test,
async: false,
success: function(result) {
row.attr("class", result.Passed ? "passed" : "failed");
if (result.Passed) {
resultCell.html("");
} else {
resultCell.html(result.ErrorHtml);
}
button.removeClass("disabled").text("Run");
running -= 1;
if (running == 0) {
$("button[all-tests]").removeClass("disabled").text("Run All Tests");
}
}});
}
</script>
</body>
</html>
@@ -1,47 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Revel Test Runner</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<style>
body {
margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
color: #333333;
background-color: #ffffff;
}
header { padding:20px 0; }
header.passed { background-color: #90EE90 !important; }
header.failed { background-color: #FFB6C1 !important; }
table { margin-top: 20px; padding: 8px; line-height: 20px; }
td { vertical-align: top; padding-right:20px; }
a { color: #0088cc; }
.container { margin-left: auto; margin-right: auto; width: 940px; }
.result.failed b { font-weight:bold; color: #C00; font-size: 14px; }
.result.failed span { color: #C00; font-size: 14px; }
</style>
</head>
<body>
<header class="{{if .Passed}}passed{{else}}failed{{end}}">
<div class="container">
<h1>{{.Name}}</h1>
<p>{{if .Passed}}PASSED{{else}}FAILED{{end}}</p>
</div>
</header>
<div class="container">
<table>
{{range .Results}}
<tr class="result {{if .Passed}}passed{{else}}failed{{end}}">
<td><span>{{.Name}}</span></td>
<td>{{if .ErrorHtml}}{{.ErrorHtml}}{{else}}PASSED{{end}}</td>
</tr>
{{end}}
</table>
</div>
</body>
</html>
@@ -1,4 +0,0 @@
GET /@tests TestRunner.Index
GET /@tests.list TestRunner.List
GET /@tests/public/*filepath Static.ServeModule(testrunner,public)
GET /@tests/:suite/:test TestRunner.Run
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

@@ -1,5 +0,0 @@
/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
//@ sourceMappingURL=jquery.min.map
*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(Line truncated
return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+Line truncated
}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}reLine truncated
+10 -6
View File
@@ -2,9 +2,12 @@
# leanote config # leanote config
#------------------------ #------------------------
http.port=80 http.port=9000
site.url=http://localhost # or http://x.com:8080, http://www.xx.com:9000 site.url=http://localhost:9000 # or http://x.com:8080, http://www.xx.com:9000
# admin username
adminUsername=admin
# mongdb # mongdb
db.host=localhost db.host=localhost
@@ -12,9 +15,10 @@ db.port=27017
db.dbname=leanote # required db.dbname=leanote # required
db.username= # if not exists, please leave it blank db.username= # if not exists, please leave it blank
db.password= # if not exists, please leave it blank db.password= # if not exists, please leave it blank
# or you can set the mongdb url for more complex needs the format is: # or you can set the mongodb url for more complex needs the format is:
# mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb # mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
# db.url=mongodb://root:root123@localhost:27017/leanote # db.url=mongodb://root:root123@localhost:27017/leanote
# db.urlEnv=${MONGODB_URL} # set url from env. eg. mongodb://root:root123@localhost:27017/leanote
# You Must Change It !! About Security!! # You Must Change It !! About Security!!
app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y
@@ -28,7 +32,7 @@ http.addr=
http.ssl=false http.ssl=false
cookie.httponly=false cookie.httponly=false
cookie.prefix=LEANOTE cookie.prefix=LEANOTE
cookie.domain= # for share cookie with subdomain, 默认为空, 即不设置 不能设置为localhost, 要么就为空 cookie.domain= # for share cookie with sub-domain
cookie.secure=false cookie.secure=false
format.date=01/02/2006 format.date=01/02/2006
format.datetime=01/02/2006 15:04 format.datetime=01/02/2006 15:04
@@ -42,14 +46,14 @@ log.error.prefix = "ERROR "
# The default language of this application. # The default language of this application.
i18n.default_language=en i18n.default_language=en
module.static=github.com/revel/revel/modules/static module.static=github.com/revel/modules/static
[dev] [dev]
mode.dev=true mode.dev=true
results.pretty=true results.pretty=true
watch=true watch=true
module.testrunner = github.com/revel/revel/modules/testrunner module.testrunner = # github.com/revel/modules/testrunner
log.trace.output = stderr log.trace.output = stderr
log.info.output = stderr log.info.output = stderr
+3 -1
View File
@@ -2,7 +2,7 @@
# This file defines all application routes (Higher priority routes first) # This file defines all application routes (Higher priority routes first)
# ~~~~ # ~~~~
module:testrunner # module:testrunner
# Home is My Blog # Home is My Blog
Get / Index.Default Get / Index.Default
@@ -38,6 +38,8 @@ POST /findPasswordUpdate Auth.FindPasswordUpdate
* /note/copySharedNote Note.CopySharedNote * /note/copySharedNote Note.CopySharedNote
* /note/searchNoteByTags Note.SearchNoteByTags * /note/searchNoteByTags Note.SearchNoteByTags
* /note/setNote2Blog Note.SetNote2Blog * /note/setNote2Blog Note.SetNote2Blog
* /note/exportPdf Note.ExportPDF
* /note/toPdf Note.ToPdf
# pjax # pjax
GET /note/:noteId Note.Index GET /note/:noteId Note.Index
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Loaded 100 of 1728 files, more files were not shown because too many files have changed in this diff. Show more