Compare commits

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

No files matched your search

+3 -3
View File
@@ -2,9 +2,9 @@
pkg pkg
bin2 bin2
bin/i18n bin/i18n
bin/leanote* bin/leanote-linux
bin/leanote-mac
bin/release bin/release
bin/test.sh
bin/tmp bin/tmp
bin/test bin/test
bin/src bin/src
@@ -16,4 +16,4 @@ app/tmp/main.go
.project .project
public/config.codekit public/config.codekit
files files
/node_modules conf/app.conf
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>leanote-public</name>
<comment>leanote, your own cloud note!</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.googlecode.goclipse.goBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>goclipse.goNature</nature>
</natures>
</projectDescription>
-37
View File
@@ -1,37 +0,0 @@
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
@@ -1,345 +0,0 @@
var gulp = require('gulp');
var clean = require('gulp-clean');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var minifyHtml = require("gulp-minify-html");
var concat = require('gulp-concat');
var replace = require('gulp-replace');
var inject = require('gulp-inject');
var gulpSequence = require('gulp-sequence');
var fs = require('fs');
var leanoteBase = './';
var base = leanoteBase + '/public'; // public base
var noteDev = leanoteBase + '/app/views/note/note-dev.html';
var noteProBase = leanoteBase + '/app/views/note';
// 合并Js, 这些js都是不怎么修改, 且是依赖
// 840kb, 非常耗时!!
gulp.task('concatDepJs', function() {
var jss = [
'js/jquery-1.9.0.min.js',
'js/jquery.ztree.all-3.5-min.js',
'tinymce/tinymce.full.min.js', // 使用打成的包, 加载速度快
// 'libs/ace/ace.js',
'js/jQuery-slimScroll-1.3.0/jquery.slimscroll-min.js',
'js/contextmenu/jquery.contextmenu-min.js',
'js/bootstrap-min.js',
'js/object_id-min.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
// .pipe(uglify()) // 压缩
.pipe(concat('dep.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// 合并app js 这些js会经常变化 90kb
gulp.task('concatAppJs', function() {
var jss = [
'js/common.js',
'js/app/note.js',
'js/app/page.js', // 写作模式下, page依赖note
'js/app/tag.js',
'js/app/notebook.js',
'js/app/share.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('app.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// 合并requirejs和markdown为一个文件
gulp.task('concatMarkdownJs', function() {
var jss = [
'js/require.js',
'dist/main.min.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('markdown.min.js'))
.pipe(gulp.dest(base + '/js'));
});
// note-dev.html -> note.html, 替换css, js
// TODO 加?t=2323232, 强制浏览器更新, 一般只需要把app.min.js上加
gulp.task('devToProHtml', function() {
return gulp
.src(noteDev)
.pipe(replace(/<!-- dev -->[.\s\S]+?<!-- \/dev -->/g, '')) // 把dev 去掉
.pipe(replace(/<!-- pro_dep_js -->/, '<script src="/js/dep.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_app_js -->/, '<script src="/js/app.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_markdown_js -->/, '<script src="/js/markdown.min.js"></script>')) // 替换
.pipe(replace(/<!-- pro_tinymce_init_js -->/, "var tinyMCEPreInit = {base: '/public/tinymce', suffix: '.min'};")) // 替换
.pipe(replace(/plugins\/main.js/, "plugins/main.min.js")) // 替换
// 连续两个空行换成一个空行, 没用
.pipe(replace(/\n\n/g, '\n'))
.pipe(replace(/\n\n/g, '\n'))
.pipe(replace('console.log(o);', ''))
// .pipe(minifyHtml()) // 不行, 压缩后golang报错
.pipe(rename('note.html'))
.pipe(gulp.dest(noteProBase));
});
// Get used keys
// 只获取需要js i18n的key
var path = require('path');
gulp.task('i18n', function() {
var keys = {};
var reg = /getMsg\(["']+(.+?)["']+/g;
function getKey(data) {
while(ret = reg.exec(data)) {
keys[ret[1]] = 1;
}
}
// 先获取需要的key
function ls(ff) {
var files = fs.readdirSync(ff);
for(fn in files) {
var fname = ff + path.sep + files[fn];
var stat = fs.lstatSync(fname);
if(stat.isDirectory() == true) {
ls(fname);
}
else {
if ((fname.indexOf('.html') > 0 || fname.indexOf('.js') > 0)) {
// console.log(fname);
// if (fname.indexOf('min.js') < 0) {
var data = fs.readFileSync(fname, "utf-8");
// 得到getMsg里的key
getKey(data);
// }
}
}
}
}
console.log('parsing used keys');
ls(base + '/admin');
ls(base + '/blog');
ls(base + '/dist');
ls(base + '/js');
ls(base + '/album');
ls(base + '/libs');
ls(base + '/member');
ls(base + '/tinymce');
console.log('parsed');
// msg.zh
function getAllMsgs(fname) {
var msg = {};
var data = fs.readFileSync(fname, "utf-8");
var lines = data.split('\n');
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];
// 忽略注释
if (line[0] == '#' || line[1] == '#') {
continue;
}
var lineArr = line.split('=');
if (lineArr.length >= 2) {
var key = lineArr[0];
lineArr.shift();
msg[key] = lineArr.join('=');
// msg[lineArr[0]] = lineArr[1];
}
}
return msg;
}
// msg.zh, msg.js
function genI18nJsFile(fromFilename, keys) {
var msgs = getAllMsgs(leanoteBase + '/messages/' + fromFilename);
var toFilename = fromFilename + '.js';
var toMsgs = {};
for (var i in msgs) {
// 只要需要的
if (i in keys) {
toMsgs[i] = msgs[i];
}
}
var str = 'var MSG=' + JSON.stringify(toMsgs) + ';';
str += 'function getMsg(key, data) {var msg = MSG[key];if(msg) {if(data) {if(!isArray(data)) {data = [data];}' +
'for(var i = 0; i < data.length; ++i) {' +
'msg = msg.replace("%s", data[i]);' +
'}' +
'}' +
'return msg;' +
'}' +
'return key;' +
'}';
// 写入到文件中
fs.writeFile(base + '/js/i18n/' + toFilename, str);
}
genI18nJsFile('msg.zh', keys);
genI18nJsFile('msg.en', keys);
genI18nJsFile('msg.fr', keys);
genI18nJsFile('blog.zh', keys);
genI18nJsFile('blog.en', keys);
genI18nJsFile('blog.fr', keys);
});
// 合并album需要的js
gulp.task('concatAlbumJs', function() {
/*
gulp.src(base + '/album/js/main.js')
.pipe(uglify()) // 压缩
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest(base + '/album/js/'));
*/
gulp.src(base + '/album/css/style.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/album/css'));
var jss = [
'js/jquery-1.9.0.min.js',
'js/bootstrap-min.js',
'js/plugins/libs-min/fileupload.js',
'js/jquery.pagination.js',
'album/js/main.js',
];
for(var i in jss) {
jss[i] = base + '/' + jss[i];
}
return gulp
.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('main.all.js'))
.pipe(gulp.dest(base + '/album/js'));
});
// plugins压缩
gulp.task('plugins', function() {
gulp.src(base + '/js/plugins/libs/*.js')
.pipe(uglify()) // 压缩
// .pipe(concat('main.min.js'))
.pipe(gulp.dest(base + '/js/plugins/libs-min'));
// 所有js合并成一个
var jss = [
'note_info',
'tips',
'history',
'attachment_upload',
'editor_drop_paste',
'main'
];
for(var i in jss) {
jss[i] = base + '/js/plugins/' + jss[i] + '.js';
}
gulp.src(jss)
.pipe(uglify()) // 压缩
.pipe(concat('main.min.js'))
.pipe(gulp.dest(base + '/js/plugins'));
});
// mincss
var minifycss = require('gulp-minify-css');
gulp.task('minifycss', function() {
gulp.src(base + '/css/bootstrap.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css'));
gulp.src(base + '/css/font-awesome-4.2.0/css/font-awesome.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/font-awesome-4.2.0/css'));
gulp.src(base + '/css/zTreeStyle/zTreeStyle.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/zTreeStyle'));
gulp.src(base + '/dist/themes/default.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/dist/themes'));
gulp.src(base + '/js/contextmenu/css/contextmenu.css')
.pipe(rename({suffix: '-min'}))
.pipe(minifycss())
.pipe(gulp.dest(base + '/js/contextmenu/css'));
// theme
// 用codekit
var as = ['default', 'simple', 'writting', /*'writting-overwrite', */ 'mobile'];
/*
for(var i = 0; i < as.length; ++i) {
gulp.src(base + '/css/theme/' + as[i] + '.css')
.pipe(minifycss())
.pipe(gulp.dest(base + '/css/theme'));
}
*/
});
// tinymce
// please set the right path on your own env
var tinymceBase = '/Users/life/leanote/leanote-tools/tinymce_4.1.9_leanote_public';
gulp.task('tinymce', function() {
// 先清理
fs.unlink(tinymceBase + '/js/tinymce/tinymce.dev.js');
fs.unlink(tinymceBase + '/js/tinymce/tinymce.jquery.dev.js');
fs.unlink(tinymceBase + '/js/tinymce/tinymce.full.js');
fs.unlink(tinymceBase + '/js/tinymce/tinymce.full.min.js');
var cp = require('child_process');
var bundleCmd = 'grunt bundle --themes leanote --plugins autolink,link,leaui_image,lists,hr,paste,searchreplace,leanote_nav,leanote_code,tabfocus,table,directionality,textcolor';
// build
cp.exec('grunt minify', {cwd: tinymceBase}, function(err, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
// 将所有都合并成一起
cp.exec(bundleCmd, {cwd: tinymceBase}, function(err, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});
});
});
// 合并css, 无用
// Deprecated
gulp.task('concatCss', function() {
return gulp
.src([markdownRaw + '/css/default.css', markdownRaw + '/css/md.css'])
.pipe(concat('all.css'))
.pipe(gulp.dest(markdownMin));
});
gulp.task('concat', ['concatDepJs', 'concatAppJs', 'concatMarkdownJs']);
gulp.task('html', ['devToProHtml']);
gulp.task('default', ['concat', 'plugins', 'minifycss', 'i18n', 'concatAlbumJs', 'html']);
+3 -3
View File
@@ -1,6 +1,6 @@
LEANOTE - NOT JUST A NOTEPAD! leanote - you own cloud note!
Copyright 2014-2015 by the contributors Copyright 2014 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)
+76 -130
View File
@@ -1,167 +1,113 @@
# Leanote
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) # Leanote
[![Build Status](https://travis-ci.org/leanote/leanote.svg)](https://travis-ci.org/leanote/leanote)
## 1. Introduction ## 1. Introduction
Leanote, Not Just A Notepad! Leanote, not just a notebook!
![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.
**Other Features** ## 2. Why we created leanote
* 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
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! More information about how to install leanote please see:
* [leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
You can install Leanote on your server, and use Leanote App (Desktop, iOs, Android) to sync notes with your self-hosted server. ### 3.1. Download leanote
Welcome to sign up on https://leanote.com, Leanote team provide a stable and reliable service for you. Leanote V1.0.2-beta has been released. Binaries:
More information about how to install Leanote please see: * Linux: [leanote-linux-x86_64.v1.0-beta.2.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-linux-x86_64.v1.0-beta.2.bin.tar.gz)
* MacOS X: [leanote-mac-x86_64.v1.0-beta.2.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-mac-x86_64.v1.0-beta.bin.2.tar.gz)
* [Leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial) Or you can clone [Leanote bin repository](https://github.com/leanote/leanote-bin) (Recommend)
* [Leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
## 4. How to develop Leanote ### 3.2. Install MongoDB
Leanote is written in go using [revel](https://revel.github.io/) and [MongoDB](https://www.mongodb.org). Thus, you need to first install MongoDB.
For more tips please have a look at [our wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
### 3.3. Import initial MongoDB data
The mongodb data is in `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
```
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
```
The initial database contains two users:
```
user2 username: admin, password: abc123 (administrator)
user3 username: demo, password: demo@leanote.com (this user is for demo)
```
### 3.4. Configuration
Modify `[PATH_TO_LEANOTE]/conf/app.conf`. Available configuration options are:
``mongodb`` **required**
```Shell
db.host=localhost
db.port=27017
db.dbname=leanote
db.username=
db.password=
```
``app.secret`` **required** **important**
The secret key used for cryptographic operations (revel.Sign).
FOR SECURITY, YOU MUST CHANGE IT!!
For more infomation please see `app/app.conf` and the [revel manuals](https://revel.github.io/)
### 3.5. Run leanote
```
$> cd PATH_TO_LEANOTE/bin
$> sudo sh run.sh
```
## 4. How to develop leanote
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)
More docs please see [wiki](https://github.com/leanote/leanote/wiki). ## 5. Contributors
## 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.
## 7.Join us ## 6. Contributing
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).
If you find some problems or has some good ideas, please submit [issues](https://github.com/leanote/leanote/issues). ## Docs
* [leanote binary distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-binary-distribution-installation-tutorial)
* [leanote develop distribution installation tutorial](https://github.com/leanote/leanote/wiki/leanote-develop-distribution-installation-tutorial)
* [leanote blog theme api](https://github.com/leanote/leanote/wiki/leanote-blog-theme-api_en)
You are always welcomed! ## Discussion
* [leanote bbs](http://bbs.leanote.com)
## 8. Donation * [leanote google group](https://groups.google.com/forum/#!forum/leanote)
Support us, [donate us](http://leanote.org/#donate). And thanks [donators](http://leanote.leanote.com/post/leanote-donation-list). * QQ Group: 158716820
## 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)
+88 -48
View File
@@ -1,73 +1,113 @@
# Leanote
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) # Leanote产品
[![Build Status](https://travis-ci.org/leanote/leanote.svg)](https://travis-ci.org/leanote/leanote)
## 1. 介绍
Leanote, 不只是笔记! ## 1. 介绍
Leanote, 不只是笔记!
![leanote.png](leanote.png "") ![leanote.png](leanote.png "")
**特性** **特性**
* 知识管理: 通过leanote来管理知识, leanote有易操作的界面, 包含两款编辑器tinymce和markdown. 在leanote, 你可以尽情享受写作. * 知识管理: 通过leanote来管理知识, leanote有易操作的界面, 包含两款编辑器tinymce和markdown. 在leanote, 你可以尽情享受写作.
* 分享: 你也可以通过分享知识给好友, 让好友拥有你的知识. * 分享: 你也可以通过分享知识给好友, 让好友拥有你的知识.
* 协作: 在分享的同时也可以与好友一起协作知识. * 协作: 在分享的同时也可以与好友一起协作知识.
* 博客: leanote也可以作为你的博客, 将知识公开成博客, 让leanote把你的知识传播的更远! * 博客: leanote也可以作为你的博客, 将知识公开成博客, 让leanote把你的知识传播的更远!
## 2. 为什么我们要创建leanote? ## 2. 为什么我们要创建leanote?
说实话, 我们曾是evernote的忠实粉丝, 但是我们也发现evernote的不足: 说实话, 我们曾是evernote的忠实粉丝, 但是我们也发现evernote的不足:
* evernote的编辑器不能满足我们的需求, 不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放. * evernote的编辑器不能满足我们的需求, 不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放.
* 我们是markdown的爱好者, 可是evernote竟然没有. * 我们是markdown的爱好者, 可是evernote竟然没有.
* 我们也想将知识公开, 所以我们有自己的博客, 如wordpress, 但为什么这两者不能合二为一呢? * 我们也想将知识公开, 所以我们有自己的博客, 如wordpress, 但为什么这两者不能合二为一呢?
* 还有... * 还有...
## 3.安装leanote ## 3.安装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%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%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) ### 3.1. 下载leanote
* [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进行二次开发 Leanote V1.0-beta.2 已发布, 二进制文件(暂时没有windows版的):
请查看 [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) * Linux: [leanote-linux-x86_64.v1.0-beta.2.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-linux-x86_64.v1.0-beta.2.bin.tar.gz)
* MacOS X: [leanote-mac-x86_64.v1.0-beta.2.bin.tar.gz](https://github.com/leanote/leanote/releases/download/1.0-beta/leanote-mac-x86_64.v1.0-beta.2.bin.tar.gz)
## 5 相关文档 或者直接检出[Leanote bin repository](https://github.com/leanote/leanote-bin) (推荐, 因为为最新版本)
* [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). ### 3.2. 安装 MongodbDB
## 6. 贡献者 Leanote是由golang(使用[revel](https://revel.github.io/)框架 和 [MongoDB](https://www.mongodb.org)数据库), 你需要先安装Mongodb.
多谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, leanote因有你们而更完美!
## 7. 加入我们 安装MongodbDB, 导入数据更多细节请查看: [wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到leanote. ### 3.3. 导入初始数据
有任何问题或建议, 欢迎提交[issue](https://github.com/leanote/leanote/issues). MongodbDB初始数据在 `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善leanote. ```
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
```
## 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) user2 username: admin, password: abc123 (管理员, 重要!)
* [Leanote IOS](https://github.com/leanote/leanote-ios), [从App Store下载](https://itunes.apple.com/zn/app/leanote/id1022302858?mt=8) user3 username: demo@leanote.com, password: demo@leanote.com (为体验使用)
* [Leanote Android](https://github.com/Dminter/leanote-android-client), 开发阶段 ```
欢迎加入我们! ### 3.4. 配置
## 联系&加入我们 修改 `[PATH_TO_LEANOTE]/conf/app.conf`. 有以下选项:
* Email: leanote@leanote.com
* [leanote 社区](http://bbs.leanote.com) ``mongodb`` **必须配置!**
* QQ群: 158716820, 256076853
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote) ```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进行二次开发
请查看 [How-to-develop-leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-%E5%A6%82%E4%BD%95%E5%BC%80%E5%8F%91leanote)
## 5. 贡献者
多谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, leanote因有你们而更完美!
## 6. 加入我们
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到leanote.
leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善leanote.
## 相关文档
* [leanote二进制版详细安装教程](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)
## 讨论
* [leanote 社区](http://bbs.leanote.com)
* QQ群: 158716820
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)
---------------------------------------------------------------- ----------------------------------------------------------------
[English](README.md) [English](README.md)
-19
View File
@@ -1,19 +0,0 @@
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")
}
+8 -13
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,17 +14,12 @@ 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})
@@ -34,12 +29,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)
+12 -3
View File
@@ -100,7 +100,7 @@ func (c Attach) uploadAttach(noteId string) (re info.Re) {
id := bson.NewObjectId(); id := bson.NewObjectId();
fileInfo.AttachId = id fileInfo.AttachId = id
fileId = id.Hex() fileId = id.Hex()
Ok, resultMsg = attachService.AddAttach(fileInfo, false) Ok, resultMsg = attachService.AddAttach(fileInfo)
if resultMsg != "" { if resultMsg != "" {
resultMsg = c.Message(resultMsg) resultMsg = c.Message(resultMsg)
} }
@@ -129,9 +129,18 @@ func (c Attach) GetAttachs(noteId string) revel.Result {
// 下载附件 // 下载附件
// 权限判断 // 权限判断
func (c Attach) Download(attachId string) revel.Result { func (c Attach) Download(attachId, token string) revel.Result {
attach := attachService.GetAttach(attachId, c.GetUserId()); // 得到路径 if c.GetUserId() == "" && token == "" {
return c.RenderText("你需要从分享页面下载附件!")
}
sessionId := c.Session.Id()
attach := attachService.GetAttach(attachId, c.GetUserId(), token, sessionId); // 得到路径
path := attach.Path path := attach.Path
if token != "" && attach.Path == "" {
return c.RenderText("该下载链接已经失效,请重新刷新原分享笔记页面下载")
}
if path == "" { if path == "" {
return c.RenderText("") return c.RenderText("")
} }
+16 -24
View File
@@ -5,6 +5,7 @@ import (
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
// "strconv" // "strconv"
"fmt"
) )
// 用户登录/注销/找回密码 // 用户登录/注销/找回密码
@@ -40,36 +41,36 @@ func (c Auth) Login(email, from string) revel.Result {
func (c Auth) doLogin(email, pwd string) revel.Result { func (c Auth) doLogin(email, pwd string) revel.Result {
sessionId := c.Session.Id() sessionId := c.Session.Id()
var msg = "" var msg = ""
fmt.Println(sessionId)
userInfo, err := authService.Login(email, pwd) userInfo := authService.Login(email, pwd)
if err != nil { if userInfo.Email != "" {
// 登录错误, 则错误次数++
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)})
} }
func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result { func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result {
sessionId := c.Session.Id() sessionId := c.Session.Id()
var msg = ""
var msg = ""
// > 5次需要验证码, 直到登录成功 // > 5次需要验证码, 直到登录成功
if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha { if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha {
msg = "captchaError" msg = "captchaError"
} else { } else {
userInfo, err := authService.Login(email, pwd) userInfo := authService.Login(email, pwd)
if err != nil { if userInfo.Email != "" {
// 登录错误, 则错误次数++
msg = "wrongUsernameOrPassword"
sessionService.IncrLoginTimes(sessionId)
} 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"
sessionService.IncrLoginTimes(sessionId)
} }
} }
@@ -85,17 +86,8 @@ func (c Auth) Logout() revel.Result {
// 体验一下 // 体验一下
func (c Auth) Demo() revel.Result { func (c Auth) Demo() revel.Result {
email := configService.GetGlobalStringConfig("demoUsername") c.doLogin(configService.GetGlobalStringConfig("demoUsername"), configService.GetGlobalStringConfig("demoPassword"))
pwd := configService.GetGlobalStringConfig("demoPassword") return c.Redirect("/note")
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,13 +84,6 @@ 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]
+15 -58
View File
@@ -200,73 +200,29 @@ func (c Blog) getCates(userBlog info.UserBlog) {
} }
var i = 0 var i = 0
cates := make([]*info.Cate, len(notebooks)) cates := make([]map[string]string, len(notebooks))
// 先要保证已有的是正确的排序 // 先要保证已有的是正确的排序
cateIds := userBlog.CateIds cateIds := userBlog.CateIds
has := map[string]bool{} // cateIds中有的 has := map[string]bool{} // cateIds中有的
cateMap := map[string]*info.Cate{}
if cateIds != nil && len(cateIds) > 0 { if cateIds != nil && len(cateIds) > 0 {
for _, cateId := range cateIds { for _, cateId := range cateIds {
if n, ok := notebooksMap[cateId]; ok { if n, ok := notebooksMap[cateId]; ok {
parentNotebookId := "" cates[i] = map[string]string{"Title": n.Title, "UrlTitle": c.getCateUrlTitle(&n), "CateId": n.NotebookId.Hex()}
if n.ParentNotebookId != "" {
parentNotebookId = n.ParentNotebookId.Hex()
}
cates[i] = &info.Cate{Title: n.Title, UrlTitle: c.getCateUrlTitle(&n), CateId: n.NotebookId.Hex(), ParentCateId: parentNotebookId}
cateMap[cates[i].CateId] = cates[i]
i++ i++
has[cateId] = true has[cateId] = true
} }
} }
} }
// 之后
// 之后添加没有排序的
for _, n := range notebooks { for _, n := range notebooks {
id := n.NotebookId.Hex() id := n.NotebookId.Hex()
if !has[id] { if !has[id] {
parentNotebookId := "" cates[i] = map[string]string{"Title": n.Title, "UrlTitle": c.getCateUrlTitle(&n), "CateId": id}
if n.ParentNotebookId != "" {
parentNotebookId = n.ParentNotebookId.Hex()
}
cates[i] = &info.Cate{Title: n.Title, UrlTitle: c.getCateUrlTitle(&n), CateId: id, ParentCateId: parentNotebookId}
cateMap[cates[i].CateId] = cates[i]
i++ i++
} }
} }
// LogJ(">>")
// LogJ(cates)
// 建立层级
hasParent := map[string]bool{} // 有父的cate
for _, cate := range cates {
parentCateId := cate.ParentCateId
if parentCateId != "" {
if parentCate, ok := cateMap[parentCateId]; ok {
// Log("________")
// LogJ(parentCate)
// LogJ(cate)
if parentCate.Children == nil {
parentCate.Children = []*info.Cate{cate}
} else {
parentCate.Children = append(parentCate.Children, cate)
}
hasParent[cate.CateId] = true
}
}
}
// 得到没有父的cate, 作为第一级cate
catesTree := []*info.Cate{}
for _, cate := range cates {
if !hasParent[cate.CateId] {
catesTree = append(catesTree, cate)
}
}
c.RenderArgs["cates"] = cates c.RenderArgs["cates"] = cates
c.RenderArgs["catesTree"] = catesTree
} }
// 单页 // 单页
@@ -348,10 +304,9 @@ 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(">>") Log(userBlog.Style)
// Log(userBlog.Style) Log(userBlog.ThemeId.Hex())
// Log(userBlog.ThemeId.Hex())
return true, userBlog return true, userBlog
} }
@@ -585,14 +540,19 @@ func (c Blog) Index(userIdOrEmail string) (re revel.Result) {
} }
userId, userInfo := c.userIdOrEmail(hasDomain, userBlog, userIdOrEmail) userId, userInfo := c.userIdOrEmail(hasDomain, userBlog, userIdOrEmail)
var ok = false var ok = false
fmt.Println("before test...");
if ok, userBlog = c.blogCommon(userId, userBlog, userInfo); !ok { if ok, userBlog = c.blogCommon(userId, userBlog, userInfo); !ok {
fmt.Println("404 occur");
return c.e404(userBlog.ThemePath) // 404 TODO 使用用户的404 return c.e404(userBlog.ThemePath) // 404 TODO 使用用户的404
} }
fmt.Println("after test0...");
// 分页的话, 需要分页信息, totalPage, curPage // 分页的话, 需要分页信息, totalPage, curPage
page := c.GetPage() page := c.GetPage()
pageInfo, blogs := blogService.ListBlogs(userId, "", page, userBlog.PerPageSize, userBlog.SortField, userBlog.IsAsc) pageInfo, blogs := blogService.ListBlogs(userId, "", page, userBlog.PerPageSize, userBlog.SortField, userBlog.IsAsc)
blogs2 := blogService.FixBlogs(blogs) blogs2 := blogService.FixBlogs(blogs)
c.RenderArgs["posts"] = blogs2 c.RenderArgs["posts"] = blogs2
c.setPaging(pageInfo) c.setPaging(pageInfo)
@@ -600,6 +560,7 @@ func (c Blog) Index(userIdOrEmail string) (re revel.Result) {
c.RenderArgs["curIsIndex"] = true c.RenderArgs["curIsIndex"] = true
return c.render("index.html", userBlog.ThemePath) return c.render("index.html", userBlog.ThemePath)
} }
@@ -821,7 +782,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, callback string) revel.Result { func (c Blog) GetComments(noteId string) revel.Result {
// 评论 // 评论
userId := c.GetUserId() userId := c.GetUserId()
page := c.GetPage() page := c.GetPage()
@@ -834,10 +795,6 @@ func (c Blog) GetComments(noteId string, callback 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)
} }
+31 -42
View File
@@ -9,9 +9,9 @@ import (
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
"io/ioutil" "io/ioutil"
"os" "os"
"fmt"
// "strconv" // "strconv"
"strings" "strings"
"fmt"
) )
// 首页 // 首页
@@ -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,19 +100,13 @@ 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/" + Digest3(userId) + "/" + userId + "/images/logo" fileUrlPath = "public/upload/" + c.GetUserId() + "/images/logo"
} else { } else {
fileUrlPath = "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images" fileUrlPath = "files/" + c.GetUserId() + "/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
@@ -120,18 +114,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)
@@ -139,25 +133,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)
@@ -174,21 +168,20 @@ 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 = fileUrlPath fileId = "public/upload/" + c.GetUserId() + "/images/logo/" + filename
} }
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
@@ -211,13 +204,13 @@ 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
// 权限判断 // 权限判断
func (c File) OutputImage(noteId, fileId string) revel.Result { func (c File) OutputImage(noteId, fileId , token string) revel.Result {
path := fileService.GetFile(c.GetUserId(), fileId); // 得到路径 sessionId := c.Session.Id()
path := fileService.GetFile(c.GetUserId(), fileId, sessionId, token); // 得到路径
if path == "" { if path == "" {
return c.RenderText("") return c.RenderText("")
} }
@@ -234,16 +227,12 @@ 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)
@@ -258,14 +247,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)
+3 -2
View File
@@ -21,12 +21,13 @@ 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"]
c.SetLocale() lang := c.SetLocale()
return c.RenderTemplate("home/index.html"); return c.RenderTemplate("home/index_" + lang + ".html");
} }
// 建议 // 建议
+126 -202
View File
@@ -2,20 +2,17 @@ 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/lea"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"os" . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
"os/exec" "os/exec"
"regexp" // "time"
"strings" // "github.com/leanote/leanote/app/types"
"time" // "io/ioutil"
"fmt" // "fmt"
// "github.com/leanote/leanote/app/types" // "bytes"
// "io/ioutil" // "os"
// "bytes"
// "os"
) )
type Note struct { type Note struct {
@@ -25,9 +22,9 @@ type Note struct {
// 笔记首页, 判断是否已登录 // 笔记首页, 判断是否已登录
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中 // 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
// 否则, 转向登录页面 // 否则, 转向登录页面
func (c Note) Index(noteId, online string) revel.Result { func (c Note) Index(noteId string) revel.Result {
c.SetLocale() c.SetLocale()
userInfo := c.GetUserAndBlogUrl() userInfo := c.GetUserInfo()
userId := userInfo.UserId.Hex() userId := userInfo.UserId.Hex()
@@ -52,11 +49,10 @@ func (c Note) Index(noteId, online 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()
@@ -128,9 +124,10 @@ func (c Note) Index(noteId, online string) revel.Result {
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser() c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
// return c.RenderTemplate("note/note.html")
if isDev, _ := revel.Config.Bool("mode.dev"); isDev && online == "" { // return c.RenderTemplate("note/note.html")
if isDev, _ := revel.Config.Bool("mode.dev"); isDev {
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")
@@ -141,13 +138,13 @@ func (c Note) Index(noteId, online 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)
} }
@@ -172,7 +169,7 @@ type NoteOrContent struct {
Title string Title string
Desc string Desc string
ImgSrc string ImgSrc string
Tags string Tags []string
Content string Content string
Abstract string Abstract string
IsNew bool IsNew bool
@@ -195,7 +192,7 @@ func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
NoteId: bson.ObjectIdHex(noteOrContent.NoteId), NoteId: bson.ObjectIdHex(noteOrContent.NoteId),
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId), NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
Title: noteOrContent.Title, Title: noteOrContent.Title,
Tags: strings.Split(noteOrContent.Tags, ","), Tags: noteOrContent.Tags,
Desc: noteOrContent.Desc, Desc: noteOrContent.Desc,
ImgSrc: noteOrContent.ImgSrc, ImgSrc: noteOrContent.ImgSrc,
IsBlog: noteOrContent.IsBlog, IsBlog: noteOrContent.IsBlog,
@@ -228,90 +225,50 @@ func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
noteUpdate["Title"] = noteOrContent.Title; noteUpdate["Title"] = noteOrContent.Title;
} }
if c.Has("Tags") { if c.Has("Tags[]") {
needUpdateNote = true needUpdateNote = true
noteUpdate["Tags"] = strings.Split(noteOrContent.Tags, ","); noteUpdate["Tags"] = noteOrContent.Tags;
} }
// web端不控制
if needUpdateNote { if needUpdateNote {
noteService.UpdateNote(c.GetUserId(), noteService.UpdateNote(noteOrContent.UserId, c.GetUserId(),
noteOrContent.NoteId, noteUpdate, -1) noteOrContent.NoteId, noteUpdate)
} }
//------------- //-------------
afterContentUsn := 0
contentOk := false
contentMsg := ""
if c.Has("Content") {
// noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(),
// noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract)
contentOk, contentMsg, afterContentUsn = noteService.UpdateNoteContent(c.GetUserId(),
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract, needUpdateNote, -1)
}
Log(afterContentUsn) if c.Has("Content") {
Log(contentOk) noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(),
Log(contentMsg) noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract)
}
return c.RenderJson(true) return c.RenderJson(true)
} }
// 删除note/ 删除别人共享给我的笔记 // 删除note/ 删除别人共享给我的笔记
// userId 是note.UserId // userId 是note.UserId
func (c Note) DeleteNote(noteIds []string, isShared bool) revel.Result { func (c Note) DeleteNote(noteId, userId string, isShared bool) revel.Result {
if !isShared { if(!isShared) {
for _, noteId := range noteIds { return c.RenderJson(trashService.DeleteNote(noteId, c.GetUserId()));
trashService.DeleteNote(noteId, c.GetUserId())
}
return c.RenderJson(true)
} }
for _, noteId := range noteIds { return c.RenderJson(trashService.DeleteSharedNote(noteId, userId, c.GetUserId()));
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(noteIds []string, notebookId string) revel.Result { func (c Note) MoveNote(noteId, notebookId string) revel.Result {
userId := c.GetUserId() return c.RenderJson(noteService.MoveNote(noteId, notebookId, c.GetUserId()));
for _, noteId := range noteIds {
noteService.MoveNote(noteId, notebookId, userId)
}
return c.RenderJson(true)
} }
// 复制note // 复制note
func (c Note) CopyNote(noteIds []string, notebookId string) revel.Result { func (c Note) CopyNote(noteId, notebookId string) revel.Result {
copyNotes := make([]info.Note, len(noteIds)) return c.RenderJson(noteService.CopyNote(noteId, notebookId, c.GetUserId()));
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(noteIds []string, notebookId, fromUserId string) revel.Result { func (c Note) CopySharedNote(noteId, notebookId, fromUserId string) revel.Result {
copyNotes := make([]info.Note, len(noteIds)) return c.RenderJson(noteService.CopySharedNote(noteId, notebookId, fromUserId, c.GetUserId()));
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)
} }
//------------ //------------
@@ -321,103 +278,50 @@ 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 { // html2image
// 判断是否有权限生成
// 博客也可以调用
// 这是脚本调用, 没有cookie, 不执行权限控制, 通过传来的appKey判断
func (c Note) ToImage(noteId, appKey string) revel.Result {
// 虽然传了cookie但是这里还是不能得到userId, 所以还是通过appKey来验证之 // 虽然传了cookie但是这里还是不能得到userId, 所以还是通过appKey来验证之
appKeyTrue, _ := revel.Config.String("app.secret") appKeyTrue, _ := revel.Config.String("app.secret")
if appKeyTrue != appKey { if appKeyTrue != appKey {
return c.RenderText("error") return c.RenderText("")
} }
note := noteService.GetNoteById(noteId) note := noteService.GetNoteById(noteId)
if note.NoteId == "" { if note.NoteId == "" {
return c.RenderText("error") return c.RenderText("")
} }
c.SetLocale()
noteUserId := note.UserId.Hex() noteUserId := note.UserId.Hex()
content := noteService.GetNoteContent(noteId, noteUserId) content := noteService.GetNoteContent(noteId, noteUserId)
userInfo := userService.GetUserInfo(noteUserId) userInfo := userService.GetUserInfo(noteUserId);
//------------------
// 将content的图片转换为base64
contentStr := content.Content
siteUrlPattern := configService.GetSiteUrl()
if strings.Contains(siteUrlPattern, "https") {
siteUrlPattern = strings.Replace(siteUrlPattern, "https", "https*", 1)
} else {
siteUrlPattern = strings.Replace(siteUrlPattern, "http", "https*", 1)
}
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["blog"] = note
c.RenderArgs["content"] = contentStr c.RenderArgs["content"] = content.Content
c.RenderArgs["userInfo"] = userInfo c.RenderArgs["userInfo"] = userInfo
userBlog := blogService.GetUserBlog(noteUserId) userBlog := blogService.GetUserBlog(noteUserId)
c.RenderArgs["userBlog"] = userBlog c.RenderArgs["userBlog"] = userBlog
return c.RenderTemplate("file/pdf.html") return c.RenderTemplate("html2Image/index.html")
} }
// 导出成PDF func (c Note) Html2Image(noteId string) revel.Result {
func (c Note) ExportPdf(noteId string) revel.Result {
re := info.NewRe() re := info.NewRe()
userId := c.GetUserId() userId := c.GetUserId()
note := noteService.GetNoteById(noteId) note := noteService.GetNoteById(noteId)
if note.NoteId == "" { if note.NoteId == "" {
re.Msg = "No Note" re.Msg = "No Note"
return c.RenderText("error") return c.RenderJson(re)
} }
noteUserId := note.UserId.Hex() noteUserId := note.UserId.Hex()
@@ -426,71 +330,91 @@ func (c Note) ExportPdf(noteId string) revel.Result {
// 是否是有权限协作的 // 是否是有权限协作的
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) { if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
re.Msg = "No Perm" re.Msg = "No Perm"
return c.RenderText("error") return c.RenderJson(re)
} }
} }
// path 判断是否需要重新生成之 // path 判断是否需要重新生成之
guid := NewGuid() fileUrlPath := "/upload/" + noteUserId + "/images/weibo"
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf" dir := revel.BasePath + "/public/" + fileUrlPath
dir := revel.BasePath + "/" + fileUrlPath if !ClearDir(dir) {
if !MkdirAll(dir) { re.Msg = "No Dir"
return c.RenderText("error, no dir") return c.RenderJson(re)
} }
filename := guid + ".pdf"
filename := note.NoteId.Hex() + ".png";
path := dir + "/" + filename path := dir + "/" + filename
// leanote.com的secret // cookie
appKey, _ := revel.Config.String("app.secretLeanote") cookieName := revel.CookiePrefix + "_SESSION"
if appKey == "" { cookie, err := c.Request.Cookie(cookieName)
appKey, _ = revel.Config.String("app.secret") cookieStr := cookie.String()
cookieValue := ""
if err == nil && len(cookieStr) > len(cookieName) {
cookieValue = cookieStr[len(cookieName)+1:]
} }
appKey, _ := revel.Config.String("app.secret")
cookieDomain, _ := revel.Config.String("cookie.domain")
// 生成之 // 生成之
binPath := configService.GetGlobalStringConfig("exportPdfBinPath") url := configService.GetSiteUrl() + "/note/toImage?noteId=" + noteId + "&appKey=" + appKey;
// 默认路径 // /Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/b.js
binPath := configService.GetGlobalStringConfig("toImageBinPath")
if binPath == "" { if binPath == "" {
binPath = "/usr/local/bin/wkhtmltopdf" return c.RenderJson(re);
} }
cc := binPath + " \"" + url + "\" \"" + path + "\" \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
// cc := binPath + " --no-stop-slow-scripts --javascript-delay 10000 \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// cc := binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// 等待--window-status为done的状态
// http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html
// wkhtmltopdf参数大全
var cc string
if note.IsMarkdown {
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
} else {
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
}
cmd := exec.Command("/bin/sh", "-c", cc) cmd := exec.Command("/bin/sh", "-c", cc)
_, err := cmd.Output() Log(cc);
if err != nil { b, err := cmd.Output()
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err)) if err == nil {
} re.Ok = true
file, err := os.Open(path) re.Id = fileUrlPath + "/" + filename
if err != nil { } else {
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err)) re.Msg = string(b)
} Log("error:......")
// http://stackoverflow.com/questions/8588818/chrome-pdf-display-duplicate-headers-received-from-the-server Log(string(b))
// filenameReturn = strings.Replace(filenameReturn, ",", "-", -1) }
filenameReturn := note.Title
filenameReturn = FixFilename(filenameReturn) return c.RenderJson(re)
if filenameReturn == "" {
filenameReturn = "Untitled.pdf" /*
} else { // 这里速度慢, 生成不完全(图片和内容都不全)
filenameReturn += ".pdf" content := noteService.GetNoteContent(noteId, noteUserId)
} userInfo := userService.GetUserInfo(noteUserId);
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
c.SetLocale()
c.RenderArgs["blog"] = note
c.RenderArgs["content"] = content.Content
c.RenderArgs["userInfo"] = userInfo
userBlog := blogService.GetUserBlog(noteUserId)
c.RenderArgs["userBlog"] = userBlog
html := c.RenderTemplateStr("html2Image/index.html") // Result类型的
contentFile := dir + "/html";
fout, err := os.Create(contentFile)
if err != nil {
return c.RenderJson(re)
}
fout.WriteString(html);
fout.Close()
cc := "/Users/life/Documents/bin/phantomjs/bin/phantomjs /Users/life/Desktop/test/c.js \"" + contentFile + "\" \"" + path + "\""
cmd := exec.Command("/bin/sh", "-c", cc)
b, err := cmd.Output()
if err == nil {
re.Ok = true
re.Id = fileUrlPath + "/" + filename
} else {
Log(string(b))
}
*/
} }
// 设置/取消Blog; 置顶 // 设置/取消Blog; 置顶
func (c Note) SetNote2Blog(noteIds []string, isBlog, isTop bool) revel.Result { func (c Note) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
for _, noteId := range noteIds { re := noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop) return c.RenderJson(re)
}
return c.RenderJson(true)
} }
+1 -2
View File
@@ -37,8 +37,7 @@ func (c Notebook) AddNotebook(notebookId, title, parentNotebookId string) revel.
if(parentNotebookId != "") { if(parentNotebookId != "") {
notebook.ParentNotebookId = bson.ObjectIdHex(parentNotebookId) notebook.ParentNotebookId = bson.ObjectIdHex(parentNotebookId)
} }
re := notebookService.AddNotebook(notebook)
re, notebook := notebookService.AddNotebook(notebook)
if(re) { if(re) {
return c.RenderJson(notebook) return c.RenderJson(notebook)
+70
View File
@@ -0,0 +1,70 @@
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")
}
+65
View File
@@ -9,12 +9,15 @@ import (
// "github.com/leanote/leanote/app/types" // "github.com/leanote/leanote/app/types"
// "io/ioutil" // "io/ioutil"
// "fmt" // "fmt"
// "time"
) )
type Share struct { type Share struct {
BaseController BaseController
} }
const AttachToken = 2
// 添加共享note // 添加共享note
func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Result { func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Result {
status := make(map[string]info.Re, len(emails)) status := make(map[string]info.Re, len(emails))
@@ -36,6 +39,8 @@ func (c Share) AddShareNote(noteId string, emails []string, perm int) revel.Resu
return c.RenderJson(status) return c.RenderJson(status)
} }
// 添加共享notebook // 添加共享notebook
func (c Share) AddShareNotebook(notebookId string, emails []string, perm int) revel.Result { func (c Share) AddShareNotebook(notebookId string, emails []string, perm int) revel.Result {
status := make(map[string]info.Re, len(emails)) status := make(map[string]info.Re, len(emails))
@@ -186,3 +191,63 @@ func (c Share) DeleteShareNotebookGroup(notebookId, groupId string) revel.Result
func (c Share) UpdateShareNotebookGroupPerm(notebookId, groupId string, perm int) revel.Result { func (c Share) UpdateShareNotebookGroupPerm(notebookId, groupId string, perm int) revel.Result {
return c.AddShareNotebookGroup(notebookId, groupId, perm) return c.AddShareNotebookGroup(notebookId, groupId, perm)
} }
//生成笔记分享密码及更新到db
func (c Share) GenShareLinkPass(noteId string) revel.Result {
pass, ok := shareService.GenSharePass(noteId)
re := info.Re{Ok : true, Item : pass, List: ok}
return c.RenderJson(re);
}
func (c Share) QuerySharePass(noteId string) revel.Result {
pass := shareService.QuerySharePass(noteId)
re := info.Re{Ok : true, Item : pass}
return c.RenderJson(re);
}
//展示分享笔记
func (c Share) ShowShareNote(noteId string) revel.Result {
note := noteService.GetNoteById(noteId)
//
c.RenderArgs["noteId"] = noteId
username := userService.GetUsernameById(note.UserId)
c.RenderArgs["userName"] = username
c.RenderArgs["isMarkDown"] = note.IsMarkdown
// c.RenderArgs["timestamp"] = time.Now().Unix()
c.SetLocale()
return c.RenderTemplate("share/show_share_note.html")
}
//验证分享密码
func (c Share) Verify4ShareNote(noteId string, sharePass int) revel.Result {
ok, note, noteContent := shareService.Verify4ShareNote(noteId, sharePass)
attaches := []info.Attach{}
if ok && note.AttachNum > 0 {
attaches = attachService.ListAttachs(noteId, "")
}
token := tokenService.NewToken(noteId, noteId, AttachToken)
//插入笔记作为链接中的附件
noteAttachIds := map[string]bool{}
noteContent.Content, noteAttachIds = shareService.AppendToken4URL(token, noteContent.Content)
//过滤掉插入笔记的附件
filteredAttaches := []info.Attach{}
if len(noteAttachIds) > 0 && len(attaches) > 0 {
for _, attach := range attaches {
if !noteAttachIds[attach.AttachId.Hex()] {
filteredAttaches = append(filteredAttaches, attach)
}
}
} else {
filteredAttaches = attaches
}
sessionId := c.Session.Id()
sessionService.SetToken(sessionId, token)
re := info.Re{Ok : ok, Item: note, List: filteredAttaches, Id: token, Msg: noteContent.Content}
return c.RenderJson(re)
}
-30
View File
@@ -1,30 +0,0 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
// "gopkg.in/mgo.v2/bson"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
// "os/exec"
)
type Tag struct {
BaseController
}
// 更新Tag
func (c Tag) UpdateTag(tag string) revel.Result {
ret := info.NewRe()
ret.Ok = true
ret.Item = tagService.AddOrUpdateTag(c.GetUserId(), tag)
return c.RenderJson(ret)
}
// 删除标签
func (c Tag) DeleteTag(tag string) revel.Result {
ret := info.Re{}
ret.Ok = true
ret.Item = tagService.DeleteTag(c.GetUserId(), tag)
return c.RenderJson(ret)
}
+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.GetUserId() == configService.GetGlobalStringConfig("demoUserId") { if(c.GetUsername() == "demo") {
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.GetUserId() == configService.GetGlobalStringConfig("demoUserId") { if(c.GetUsername() == "demo") {
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, pwd string) revel.Result { func (c User) UpdateEmailSendActiveEmail(email string) revel.Result {
re := info.NewRe() re := info.NewRe()
if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") { if(c.GetUsername() == "demo") {
re.Msg = "cannotUpdateDemo" re.Msg = "cannotUpdateDemo"
return c.RenderJson(re); return c.RenderJson(re);
} }
@@ -46,10 +46,7 @@ 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, err := authService.Login(demoUsername, demoPassword) userInfo := 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,11 +56,7 @@ 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, err := authService.Login(demoUsername, demoPassword) userInfo := 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)
@@ -73,9 +69,15 @@ func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
return c.RenderJson(re) return c.RenderJson(re)
} }
func (c AdminSetting) ExportPdf(path string) revel.Result { // ToImage
// 长微博的bin路径phantomJs
func (c AdminSetting) ToImage() revel.Result {
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
return c.RenderTemplate("admin/setting/toImage.html");
}
func (c AdminSetting) DoToImage(toImageBinPath string) revel.Result {
re := info.NewRe() re := info.NewRe()
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "exportPdfBinPath", path) re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "toImageBinPath", toImageBinPath)
return c.RenderJson(re) return c.RenderJson(re)
} }
@@ -22,9 +22,3 @@ func (c AdminUpgrade) UpgradeBetaToBeta2() revel.Result {
re.Ok, re.Msg = upgradeService.UpgradeBetaToBeta2(c.GetUserId()) re.Ok, re.Msg = upgradeService.UpgradeBetaToBeta2(c.GetUserId())
return c.RenderJson(re) return c.RenderJson(re)
} }
func (c AdminUpgrade) UpgradeBeta3ToBeta4() revel.Result {
re := info.NewRe()
re.Ok, re.Msg = upgradeService.Api(c.GetUserId())
return c.RenderJson(re)
}
-465
View File
@@ -1,465 +0,0 @@
# API 列表
## 前言
### api url
所有api的url前面带/api/, 如:
`/api/user/info?userId=xxxx&token=xxxx`
除了/auth/login, /auth/register外其它的都需要另外带参数token=xxxx
### 文件目录结构
* 所有API的Controller都在app/api文件夹下
* 文件命名: Api功能Controller.go, 如ApiUserController.go
* 结构体命名为 Api功能, 如ApiUser
* API公用Controller: ApiBaseController
* init.go 注入service和定义拦截器
### 流程
用户登录后返回一个token, 以后所有的请求都携带该token.
在init.go中的拦截器会得到token并调用sessionService判断是否登录了
## 返回值结构
* 全部返回JSON, JSON, 除二进制文件(图片, 附件外), 如果返回其它非JSON格式的值, 肯定是出错了
* 错误信息全部返回 {Ok: false, Msg: "相应的错误信息"}
* 正确信息返回分两种:
1. 一些操作型的api, 比如updateUsername, updateLogo之类的, 成功后返回 {Ok: true, Msg:""}
2. 一些获取型api, 如getNote, 全部返回真实的返回数据, 如返回笔记:
```
{
"NoteId": "54bdc7e305fcd13ea3000000",
"NotebookId": "54bdc65599c37b0da9000003",
"UserId": "54bdc65599c37b0da9000002",
"Title": "笔记标题",
"Desc": "",
"Tags": null,
"Abstract": "",
"Content": "",
"IsMarkdown": false,
"IsBlog": false,
"IsTrash": true,
"IsDeleted": false,
"Usn": 15,
"Files": [],
"CreatedTime": "2015-01-20T11:13:41.34+08:00",
"UpdatedTime": "2015-01-20T11:13:41.34+08:00",
"PublicTime": "0001-01-01T00:00:00Z"
}
```
* 时间类型全是返回如 "2015-01-20T11:13:41.34+08:00" 的格式, (因为golang的time转成Json就是这样, 历史原因)
-----------------
## Auth 登录与注册
### /auth/login 登录
```
参数: email, pwd
Method: GET
返回:
错误: {"Ok":false, "Msg":"用户名或密码有误"}
正确: 比如:
{
"Ok":true,
"Token":"5500830738f41138e90003232",
"UserId":"52d26b4e99c37b609a000001",
"Email":"leanote@leanote.com",
"Username":"leanote"
}
```
登录成功后将使用token作为之后的请求
### /auth/logout 注销
```
参数: token
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: {Ok: true, Msg: ""}
```
### /auth/register 注册
```
参数: email, pwd
Method: POST
返回:
错误: {Ok: false, Msg: ""}
成功: {Ok: true, Msg: ""}
```
## User 用户
### /user/info 获取用户信息
```
参数: userId
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: type.User
```
### /user/updateUsername 修改用户名
```
参数: username(新用户名)
Method: POST
返回:
错误: {Ok: false, Msg: ""}
成功: {Ok: true, Msg: ""}
```
### /user/updatePwd 修改密码
```
参数: oldPwd(旧密码), pwd(新密码)
Method: POST
返回:
错误: {Ok: false, Msg: ""}
成功: {Ok: true, Msg: ""}
```
### /user/updateLogo 修改头像
```
参数: file(文件)
Method: POST
返回:
错误: {Ok: false, Msg: ""}
成功: {Ok: true, Msg: ""}
```
### /user/getSyncState 获取最新同步状态
```
参数: 无
Method: POST
返回:
错误: {Ok: false, Msg: ""}
成功: {LastSyncUsn: 3232, LastSyncTime: "上次同步时间"(暂时无用)}
```
-----
## Notebook 笔记本
### /notebook/getSyncNotebooks 得到需要同步的笔记本
```
参数: afterUsn(int, 在此usn后的笔记本是需要同步的), maxEntry(int, 最大要同步的量)
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: [type.Notebook] 数组
```
### /notebook/getNotebooks 得到所有笔记本
```
无参数
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: [type.Notebook] 数组
```
### /notebook/addNotebook 添加笔记本
```
参数: title(string), parentNotebookId(string, 父notebookId, 可空), seq(int) 排列
Method: POST
返回:
错误: {Ok: false, Msg:""}
成功: type.Notebook
```
### /notebook/updateNotebook 修改笔记本
```
参数: notebookId, title, parentNotebookId, seq(int), usn(int)
Method: POST
返回:
错误: {Ok: false, msg: ""} msg == "conflict" 表示冲突
成功: type.Notebook
```
### /notebook/deleteNotebook 删除笔记本
```
参数: notebookId, usn(int)
Method: GET
返回:
错误: {Ok: false, msg: ""} msg == "conflict" 表示冲突
成功: {Ok: true}
```
----
## Note 笔记
### /note/getSyncNotes 获取需要同步的笔记
```
参数: afterUsn(int, 在此usn后的笔记是需要同步的), maxEntry(int, 最大要同步的量)
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: [type.Note] 数组, 笔记不包含Abstract和Content
```
### /note/getNotes 获得某笔记本下的笔记(无内容)
```
参数: notebookId
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: [type.Note] 数组, 笔记不包含Abstract和Content
```
### /note/getNoteAndContent 获得笔记与内容
```
参数: noteId
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: type.Note
```
### /note/getNoteContent 获得笔记内容
```
参数: noteId
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: type.NoteContent
```
### /note/addNote 添加笔记
```
参数: (注意首字大写)
NotebookId string 必传
Title string 必传
Tags []string 可选
Content string 必传
Abstract string 可选, 当是markdown笔记时必须传
IsMarkdown bool 可选
Files []type.NoteFiles 数组 可选
Method: POST
返回:
错误: {Ok: false, Msg: ""}
成功: type.Note, 不包含Abstract, Content
```
** 关于笔记中的图片/附件**
客户端应该添加一个"图片/附件表"来存元数据, 图片应该要缓存到本地, 附件可在需要的时候再调用相应api获取.
Content中的数据, 图片,附件在Leanote的链接是, 所以, 不管你在本地的笔记中是以什么形式来保存图片,附件的链接的,请addNote和updateNote时务必将链接修改成leanote服务器上的链接.
http://leanote.com/api/file/getImage?fileId=xx
单个附件:
http://leanote.com/api/file/getAttach?fileId=xx
所有附件:
http://leanote.com/api/file/getAllAttachs?noteId=xxx
**注意:**
addNote时必须要把Files, 和相关的图片/附件一起传到服务器中
其中Files(文件的元数据)和其它字段以POST方式传出, 而真正数据则以http的multipart传入, 每个文件的name为"FileDatas[LocalFileId]"
图片在笔记内的链接必须为: http://leanote.com/api/file/getImage?fileId=LocalFileId或FileId
附件如果插入到了笔记内容内, 其链接必须为: http://leanote.com/api/file/getAttach?fileId=LocalFileId或FileId
其中, fileId为文件在本地的LocalFileId或服务器上的FileId
服务器端会生成FileId传给Client. Client在本地必须要建立LocalFileId与FileId的关联.
如果笔记内容传了, 且笔记内有图片, 则必须要传Files 文件元数据, 因为Server端还要对内容内的图片, 附件链接进行修改, 可能你传过去的是LocalFileId, 服务器端会将LocalFileId替换成FileId存到数据库中.
同样适用于 updateNote
http://leanote.com 不绝对, 因为用户可以自建服务, 所以在开发时需要可配置
### /note/updateNote 更新笔记
当更新了笔记某个属性时, 只要传某个属性就行, 其它不用传, 比如把笔记拉入了trash, 那么就传IsTrash: true
```
参数: (注意首字大写)
NoteId string 必传
Usn int 必传
NotebookId string 可选
Title string 可选
Tags []string 可选
Content string 可选
Abstract string 可选, 当是markdown笔记时必须传
IsMarkdown bool 可选
IsTrash bool 是否是trash 可选
Files []type.NoteFiles 数组 可选
Method: POST
返回:
错误: {Ok: false, msg: ''} msg == 'conflict' 表示冲突
成功: type.Note, 不包含Abstract和Content
```
### /note/deleteTrash 彻底删除笔记
```
参数: noteId, usn
Method: GET
返回:
错误: {Ok: false, msg: ''} msg == 'conflict' 表示冲突
成功: type.UpdateRet
```
-------
## Tag 标签
### /tag/getSyncTags 获取需要同步的标签
```
参数: afterUsn(int, 在此usn后的标签是需要同步的), maxEntry(int, 最大要同步的量)
Method: GET
返回:
错误: {Ok: false, Msg: ""}
成功: [type.Tag] 数组
```
### /tag/addTag 添加标签
```
参数: tag(string)
Method: POST
返回:
错误: {Ok: false, Msg: ""}
成功: type.Tag
```
### /tag/deleteTag 删除标签
```
参数: tag(string)
Method: POST
返回:
错误: {Ok: false, Msg: ""}
成功: type.UpdateRet
```
### File 文件(获取图片, 附件)
### /file/getImage 获取图片
```
参数: fileId
Method: GET
返回:
错误: 非二进制文件数据
成功: 二进制文件
```
### /file/getAttach 获取附件
```
参数: fileId
Method: GET
返回:
错误: 非二进制文件数据
成功: 二进制文件
```
### /file/getAllAttachs 获取所有附件
```
参数: noteId
Method: GET
返回:
错误: 非二进制文件数据
成功: 二进制文件
```
--------
## 数据类型
### type.User 用户信息
```
User {
UserId string
Username string
Email string
Verified bool
Logo string
}
```
### type.Notebook 笔记本
```
Notebook {
NotebookId
UserId
ParentNotebookId // 上级
Seq int // 排序
Title string
IsBlog bool
IsDeleted bool
CreatedTime time.Time
UpdatedTime time.Time
// 更新序号
Usn int // UpdateSequenceNum
}
```
### type.Note 笔记
```
Note {
NoteId string
NotebookId string
UserId string
Title string
Tags []string
Content string
IsMarkdown bool
IsBlog bool
IsTrash bool
Files []NoteFile // 图片, 附件
CreatedTime time.Time
UpdatedTime time.Time
PublicTime time.Time
// 更新序号
Usn int
}
```
### type.NoteContent 笔记内容
```
NoteContent {
NoteId string
UserId string
Content string
}
```
### type.NoteFile 笔记文件(图片,附件)
```
NoteFile {
FileId string // 服务器端Id
LocalFileId string // 客户端Id
Type string // images/png, doc, xls, 根据fileName确定
Title string
HasBody bool // 传过来的值是否要更新内容, 如果有true, 则必须传文件
IsAttach bool // 是否是附件, 不是附件就是图片
}
```
### type.Tag 标签
```
Tag {
TagId string
UserId string
Tag string
CreatedTime
UpdatedTime
IsDeleted bool // 删除位
// 更新序号
Usn
}
```
### type.UpdateRe 更新后返回的值, 包含Usn
```
ReUpdate {
Ok bool
Msg string
// 更新序号
Usn int
}
```
-47
View File
@@ -1,47 +0,0 @@
# API设计
By life (life@leanote.com)
## api url
所有api的url前面带/api/, 如:
`/api/user/info?userId=xxxx&token=xxxx`
## 文件目录结构
* 所有API的Controller都在app/api文件夹下
* 文件命名: Api功能Controller.go, 如ApiUserController.go
* 结构体命名为 Api功能, 如ApiUser
* API公用Controller: ApiBaseController
* init.go 注入service和定义拦截器
## 流程
用户登录后返回一个token, 以后所有的请求都携带该token.
在init.go中的拦截器会得到token并调用sessionService判断是否登录了
## 返回值结构
* 全部返回JSON, JSON, 除二进制文件(图片, 附件外), 如果返回其它非JSON格式的值, 肯定是出错了
* 错误信息全部返回 {Ok: false, Msg: ""}
* 正确信息全部返回真实的返回数据, 如返回笔记:
```
{
"NoteId": "54bdc7e305fcd13ea3000000",
"NotebookId": "54bdc65599c37b0da9000003",
"UserId": "54bdc65599c37b0da9000002",
"Title": "asdfads",
"Desc": "",
"Tags": null,
"Abstract": "",
"Content": "",
"IsMarkdown": false,
"IsBlog": false,
"IsTrash": true,
"IsDeleted": false,
"Usn": 15,
"Files": [],
"CreatedTime": "2015-01-20T11:13:41.34+08:00",
"UpdatedTime": "2015-01-20T11:13:41.34+08:00",
"PublicTime": "0001-01-01T00:00:00Z"
}
```
* 时间类型全是返回如 "2015-01-20T11:13:41.34+08:00" 的格式, (因为golang的time转成Json就是这样, 历史原因)
-69
View File
@@ -1,69 +0,0 @@
package api
import (
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
"gopkg.in/mgo.v2/bson"
// "strconv"
)
// 用户登录后生成一个token, 将这个token保存到session中
// 以后每次的请求必须带这个token, 并从session中获取userId
// 用户登录/注销/找回密码
type ApiAuth struct {
ApiBaseContrller
}
// 登录
// [ok]
// 成功返回 {Ok: true, Item: token }
// 失败返回 {Ok: false, Msg: ""}
func (c ApiAuth) Login(email, pwd string) revel.Result {
var msg = ""
userInfo, err := authService.Login(email, pwd)
if err == nil {
token := bson.NewObjectId().Hex()
sessionService.SetUserId(token, userInfo.UserId.Hex())
return c.RenderJson(info.AuthOk{Ok: true, Token: token, UserId: userInfo.UserId, Email: userInfo.Email, Username: userInfo.Username})
} else {
// 登录错误, 则错误次数++
msg = "wrongUsernameOrPassword"
}
return c.RenderJson(info.ApiRe{Ok: false, Msg: c.Message(msg)})
}
// 注销
// [Ok]
func (c ApiAuth) Logout() revel.Result {
token := c.getToken()
sessionService.Clear(token)
re := info.NewApiRe()
re.Ok = true
return c.RenderJson(re)
}
// 注册
// [Ok]
// 成功后并不返回用户ID, 需要用户重新登录
func (c ApiAuth) Register(email, pwd string) revel.Result {
re := info.NewApiRe()
if !configService.IsOpenRegister() {
re.Msg = "notOpenRegister" // 未开放注册
return c.RenderJson(re)
}
if re.Ok, re.Msg = Vd("email", email); !re.Ok {
return c.RenderJson(re)
}
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
return c.RenderJson(re)
}
// 注册
re.Ok, re.Msg = authService.Register(email, pwd, "")
return c.RenderJson(re)
}
-187
View File
@@ -1,187 +0,0 @@
package api
import (
"github.com/revel/revel"
"gopkg.in/mgo.v2/bson"
// "encoding/json"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/controllers"
"github.com/leanote/leanote/app/info"
"os"
// "fmt"
"io/ioutil"
// "fmt"
// "math"
// "strconv"
"strings"
)
// 公用Controller, 其它Controller继承它
type ApiBaseContrller struct {
controllers.BaseController // 不能用*BaseController
}
// 得到token, 这个token是在AuthInterceptor设到Session中的
func (c ApiBaseContrller) getToken() string {
return c.Session["_token"]
}
// userId
// _userId是在AuthInterceptor设置的
func (c ApiBaseContrller) getUserId() string {
return c.Session["_userId"]
}
// 得到用户信息
func (c ApiBaseContrller) getUserInfo() info.User {
userId := c.Session["_userId"]
if userId == "" {
return info.User{}
}
return userService.GetUserInfo(userId)
}
// 上传附件
func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg string, id string) {
userId := c.getUserId();
// 判断是否有权限为笔记添加附件
// 如果笔记还没有添加是不是会有问题
/*
if !shareService.HasUpdateNotePerm(noteId, userId) {
return
}
*/
file, handel, err := c.Request.FormFile(name)
if err != nil {
return
}
defer file.Close()
data, err := ioutil.ReadAll(file)
if err != nil {
return
}
// > 5M?
maxFileSize := configService.GetUploadSize("uploadAttachSize");
if maxFileSize <= 0 {
maxFileSize = 1000
}
if(float64(len(data)) > maxFileSize * float64(1024*1024)) {
msg = "fileIsTooLarge"
return
}
// 生成上传路径
newGuid := NewGuid()
filePath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/attachs"
dir := revel.BasePath + "/" + filePath
err = os.MkdirAll(dir, 0755)
if err != nil {
return
}
// 生成新的文件名
filename := handel.Filename
_, ext := SplitFilename(filename) // .doc
filename = newGuid + ext
toPath := dir + "/" + filename;
err = ioutil.WriteFile(toPath, data, 0777)
if err != nil {
return
}
// add File to db
fileType := ""
if ext != "" {
fileType = strings.ToLower(ext[1:])
}
filesize := GetFilesize(toPath)
fileInfo := info.Attach{AttachId: bson.NewObjectId(),
Name: filename,
Title: handel.Filename,
NoteId: bson.ObjectIdHex(noteId),
UploadUserId: bson.ObjectIdHex(userId),
Path: filePath + "/" + filename,
Type: fileType,
Size: filesize}
ok, msg = attachService.AddAttach(fileInfo, true)
if !ok {
return
}
id = fileInfo.AttachId.Hex()
return
}
// 上传图片
func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok bool, msg string, id string) {
if isAttach {
return c.uploadAttach(name, noteId)
}
file, handel, err := c.Request.FormFile(name)
if err != nil {
return
}
defer file.Close()
newGuid := NewGuid()
// 生成上传路径
userId := c.getUserId()
fileUrlPath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755)
if err != nil {
return
}
// 生成新的文件名
filename := handel.Filename
_, ext := SplitFilename(filename)
if(ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg") {
msg = "notImage"
return
}
filename = newGuid + ext
data, err := ioutil.ReadAll(file)
if err != nil {
return
}
maxFileSize := configService.GetUploadSize("uploadImageSize");
if maxFileSize <= 0 {
maxFileSize = 1000
}
// > 2M?
if(float64(len(data)) > maxFileSize * float64(1024*1024)) {
msg = "fileIsTooLarge"
return
}
toPath := dir + "/" + filename;
err = ioutil.WriteFile(toPath, data, 0777)
if err != nil {
return
}
// 改变成gif图片
_, toPathGif := TransToGif(toPath, 0, true)
filename = GetFilename(toPathGif)
filesize := GetFilesize(toPathGif)
fileUrlPath += "/" + filename
// File
fileInfo := info.File{FileId: bson.NewObjectId(),
Name: filename,
Title: handel.Filename,
Path: fileUrlPath,
Size: filesize}
ok, msg = fileService.AddImage(fileInfo, "", c.getUserId(), true)
if ok {
id = fileInfo.FileId.Hex()
}
return
}
-164
View File
@@ -1,164 +0,0 @@
package api
import (
"github.com/revel/revel"
// "encoding/json"
// . "github.com/leanote/leanote/app/lea"
// "gopkg.in/mgo.v2/bson"
// "github.com/leanote/leanote/app/lea/netutil"
// "github.com/leanote/leanote/app/info"
// "io/ioutil"
"os"
// "strconv"
"io"
"time"
"strings"
"archive/tar"
"compress/gzip"
)
// 文件操作, 图片, 头像上传, 输出
type ApiFile struct {
ApiBaseContrller
}
/*
// 协作时复制图片到owner
func (c ApiFile) CopyImage(userId, fileId, toUserId string) revel.Result {
re := info.NewRe()
re.Ok, re.Id = fileService.CopyImage(userId, fileId, toUserId)
return c.RenderJson(re)
}
// get all images by userId with page
func (c ApiFile) GetImages(albumId, key string, page int) revel.Result {
imagesPage := fileService.ListImagesWithPage(c.getUserId(), albumId, key, page, 12)
re := info.NewRe()
re.Ok = true
re.Item = imagesPage
return c.RenderJson(re)
}
func (c ApiFile) UpdateImageTitle(fileId, title string) revel.Result {
re := info.NewRe()
re.Ok = fileService.UpdateImageTitle(c.getUserId(), fileId, title)
return c.RenderJson(re)
}
func (c ApiFile) DeleteImage(fileId string) revel.Result {
re := info.NewRe()
re.Ok, re.Msg = fileService.DeleteImage(c.getUserId(), fileId)
return c.RenderJson(re)
}
*/
//-----------
// 输出image
// [OK]
func (c ApiFile) GetImage(fileId string) revel.Result {
path := fileService.GetFile(c.getUserId(), fileId) // 得到路径
if path == "" {
return c.RenderText("")
}
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
file, _ := os.Open(fn)
return c.RenderFile(file, revel.Inline) // revel.Attachment
}
// 下载附件
// [OK]
func (c ApiFile) GetAttach(fileId string) revel.Result {
attach := attachService.GetAttach(fileId, c.getUserId()); // 得到路径
path := attach.Path
if path == "" {
return c.RenderText("No Such File")
}
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
file, _ := os.Open(fn)
return c.RenderBinary(file, attach.Title, revel.Attachment, time.Now()) // revel.Attachment
}
// 下载所有附件
// [OK]
func (c ApiFile) GetAllAttachs(noteId string) revel.Result {
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
return c.RenderText("")
}
// 得到文件列表
attachs := attachService.ListAttachs(noteId, c.getUserId())
if attachs == nil || len(attachs) == 0 {
return c.RenderText("")
}
/*
dir := revel.BasePath + "/files/tmp"
err := os.MkdirAll(dir, 0755)
if err != nil {
return c.RenderText("")
}
*/
filename := note.Title + ".tar.gz"
if note.Title == "" {
filename = "all.tar.gz"
}
// file write
fw, err := os.Create(revel.BasePath + "/files/" + filename)
if err != nil {
return c.RenderText("")
}
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
// gzip write
gw := gzip.NewWriter(fw)
defer gw.Close()
// tar write
tw := tar.NewWriter(gw)
defer tw.Close()
// 遍历文件列表
for _, attach := range attachs {
fn := revel.BasePath + "/" + strings.TrimLeft(attach.Path, "/")
fr, err := os.Open(fn)
fileInfo, _ := fr.Stat()
if err != nil {
return c.RenderText("")
}
defer fr.Close()
// 信息头
h := new(tar.Header)
h.Name = attach.Title
h.Size = fileInfo.Size()
h.Mode = int64(fileInfo.Mode())
h.ModTime = fileInfo.ModTime()
// 写信息头
err = tw.WriteHeader(h)
if err != nil {
panic(err)
}
// 写文件
_, err = io.Copy(tw, fr)
if err != nil {
panic(err)
}
} // for
// tw.Close()
// gw.Close()
// fw.Close()
// file, _ := os.Open(dir + "/" + filename)
// fw.Seek(0, 0)
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachment
}
-664
View File
@@ -1,664 +0,0 @@
package api
import (
"github.com/revel/revel"
// "encoding/json"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson"
"regexp"
"strings"
"time"
"os/exec"
"os"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
// "bytes"
// "os"
)
// 笔记API
type ApiNote struct {
ApiBaseContrller
}
// 获取同步的笔记
// > afterUsn的笔记
// 无Desc, Abstract, 有Files
/*
{
"NoteId": "55195fa199c37b79be000005",
"NotebookId": "55195fa199c37b79be000002",
"UserId": "55195fa199c37b79be000001",
"Title": "Leanote语法Leanote语法Leanote语法Leanote语法Leanote语法",
"Desc": "",
"Tags": null,
"Abstract": "",
"Content": "",
"IsMarkdown": true,
"IsBlog": false,
"IsTrash": false,
"Usn": 5,
"Files": [],
"CreatedTime": "2015-03-30T22:37:21.695+08:00",
"UpdatedTime": "2015-03-30T22:37:21.724+08:00",
"PublicTime": "2015-03-30T22:37:21.695+08:00"
}
*/
func (c ApiNote) GetSyncNotes(afterUsn, maxEntry int) revel.Result {
if maxEntry == 0 {
maxEntry = 100
}
notes := noteService.GetSyncNotes(c.getUserId(), afterUsn, maxEntry)
return c.RenderJson(notes)
}
// 得到笔记本下的笔记
// [OK]
func (c ApiNote) GetNotes(notebookId string) revel.Result {
if notebookId != "" && !bson.IsObjectIdHex(notebookId) {
re := info.NewApiRe()
re.Msg = "notebookIdInvalid"
return c.RenderJson(re)
}
_, notes := noteService.ListNotes(c.getUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false)
return c.RenderJson(noteService.ToApiNotes(notes))
}
// 得到trash
// [OK]
func (c ApiNote) GetTrashNotes() revel.Result {
_, notes := noteService.ListNotes(c.getUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
return c.RenderJson(noteService.ToApiNotes(notes))
}
// get Note
// [OK]
/*
{
"NoteId": "550c0bee2ec82a2eb5000000",
"NotebookId": "54a1676399c37b1c77000004",
"UserId": "54a1676399c37b1c77000002",
"Title": "asdfadsf--=",
"Desc": "",
"Tags": [
""
],
"Abstract": "",
"Content": "",
"IsMarkdown": false,
"IsBlog": false,
"IsTrash": false,
"Usn": 8,
"Files": [
{
"FileId": "551975d599c37b970f000002",
"LocalFileId": "",
"Type": "",
"Title": "",
"HasBody": false,
"IsAttach": false
},
{
"FileId": "551975de99c37b970f000003",
"LocalFileId": "",
"Type": "doc",
"Title": "李铁-简历-ali-print-en.doc",
"HasBody": false,
"IsAttach": true
},
{
"FileId": "551975de99c37b970f000004",
"LocalFileId": "",
"Type": "doc",
"Title": "李铁-简历-ali-print.doc",
"HasBody": false,
"IsAttach": true
}
],
"CreatedTime": "2015-03-20T20:00:52.463+08:00",
"UpdatedTime": "2015-03-31T00:12:44.967+08:00",
"PublicTime": "2015-03-20T20:00:52.463+08:00"
}
*/
func (c ApiNote) GetNote(noteId string) revel.Result {
if !bson.IsObjectIdHex(noteId) {
re := info.NewApiRe()
re.Msg = "noteIdInvalid"
return c.RenderJson(re)
}
note := noteService.GetNote(noteId, c.getUserId())
if note.NoteId == "" {
re := info.NewApiRe()
re.Msg = "notExists"
return c.RenderJson(re)
}
apiNotes := noteService.ToApiNotes([]info.Note{note})
return c.RenderJson(apiNotes[0])
}
// 得到note和内容
// [OK]
func (c ApiNote) GetNoteAndContent(noteId string) revel.Result {
noteAndContent := noteService.GetNoteAndContent(noteId, c.getUserId())
apiNotes := noteService.ToApiNotes([]info.Note{noteAndContent.Note})
apiNote := apiNotes[0]
apiNote.Content = noteAndContent.Content
return c.RenderJson(apiNote)
}
// 处理笔记内容数据 http://leanote.com/file/outputImage -> https://leanote.com/api/file/getImage
// 图片, 附件都替换
func (c ApiNote) fixContent(content string) string {
// TODO, 这个url需要从config中取
// baseUrl := "http://leanote.com"
baseUrl := configService.GetSiteUrl()
// baseUrl := "http://localhost:9000"
patterns := []map[string]string{
map[string]string{"src": "src", "middle": "/file/outputImage", "param": "fileId", "to": "getImage?fileId="},
map[string]string{"src": "href", "middle": "/attach/download", "param": "attachId", "to": "getAttach?fileId="},
map[string]string{"src": "href", "middle": "/attach/downloadAll", "param": "noteId", "to": "getAllAttachs?noteId="},
}
for _, eachPattern := range patterns {
// src="http://leanote.com/file/outputImage?fileId=5503537b38f4111dcb0000d1"
// href="http://leanote.com/attach/download?attachId=5504243a38f4111dcb00017d"
// href="http://leanote.com/attach/downloadAll?noteId=55041b6a38f4111dcb000159"
regImage, _ := regexp.Compile(eachPattern["src"] + `=('|")`+ baseUrl + eachPattern["middle"] + `\?` + eachPattern["param"] + `=([a-z0-9A-Z]{24})("|')`)
findsImage := regImage.FindAllStringSubmatch(content, -1) // 查找所有的
// [[src='http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" ' 54672e8d38f411286b000069 "] [src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " 54672e8d38f411286b000069 "] [src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " 54672e8d38f411286b000069 "] [src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " 54672e8d38f411286b000069 "]]
for _, eachFind := range findsImage {
// [src='http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" ' 54672e8d38f411286b000069 "]
if len(eachFind) == 4 {
content = strings.Replace(content,
eachFind[0],
eachPattern["src"] + "=\"" + baseUrl + "/api/file/" + eachPattern["to"] + eachFind[2] + "\"",
1);
}
}
// markdown处理
// ![](http://leanote.com/file/outputImage?fileId=5503537b38f4111dcb0000d1)
// [selection 2.html](http://leanote.com/attach/download?attachId=5504262638f4111dcb00017f)
// [all.tar.gz](http://leanote.com/attach/downloadAll?noteId=5503b57d59f81b4eb4000000)
pre := "!" // 默认图片
if eachPattern["src"] == "href" { // 是attach
pre = ""
}
regImageMarkdown, _ := regexp.Compile(pre + `\[(.*?)\]\(`+ baseUrl + eachPattern["middle"] + `\?` + eachPattern["param"] + `=([a-z0-9A-Z]{24})\)`)
findsImageMarkdown := regImageMarkdown.FindAllStringSubmatch(content, -1) // 查找所有的
// [[![](http://leanote.com/file/outputImage?fileId=5503537b38f4111dcb0000d1) 5503537b38f4111dcb0000d1] [![你好啊, 我很好, 为什么?](http://leanote.com/file/outputImage?fileId=5503537b38f4111dcb0000d1) 5503537b38f4111dcb0000d1]]
for _, eachFind := range findsImageMarkdown {
// [![你好啊, 我很好, 为什么?](http://leanote.com/file/outputImage?fileId=5503537b38f4111dcb0000d1) 你好啊, 我很好, 为什么? 5503537b38f4111dcb0000d1]
if len(eachFind) == 3 {
content = strings.Replace(content, eachFind[0], pre + "[" + eachFind[1] + "](" + baseUrl + "/api/file/" + eachPattern["to"] + eachFind[2] + ")", 1);
}
}
}
return content
}
// content里的image, attach链接是
// https://leanote.com/api/file/getImage?fileId=xx
// https://leanote.com/api/file/getAttach?fileId=xx
// 将fileId=映射成ServerFileId, 这里的fileId可能是本地的FileId
func (c ApiNote) fixPostNotecontent(noteOrContent *info.ApiNote) {
if noteOrContent.Content == "" {
return
}
files := noteOrContent.Files
if files != nil && len(files) > 0 {
for _, file := range files {
if file.LocalFileId != "" {
noteOrContent.Content = strings.Replace(noteOrContent.Content, "fileId=" + file.LocalFileId, "fileId=" + file.FileId, -1)
}
}
}
}
// 得到内容
// [OK]
func (c ApiNote) GetNoteContent(noteId string) revel.Result {
// re := info.NewRe()
noteContent := noteService.GetNoteContent(noteId, c.getUserId())
if noteContent.Content != "" {
noteContent.Content = c.fixContent(noteContent.Content)
}
apiNoteContent := info.ApiNoteContent{
NoteId: noteContent.NoteId,
UserId: noteContent.UserId,
Content: noteContent.Content,
}
// re.Item = noteContent
return c.RenderJson(apiNoteContent)
}
// 添加笔记
// [OK]
func (c ApiNote) AddNote(noteOrContent info.ApiNote) revel.Result {
userId := bson.ObjectIdHex(c.getUserId())
re := info.NewRe()
myUserId := userId
// 为共享新建?
/*
if noteOrContent.FromUserId != "" {
userId = bson.ObjectIdHex(noteOrContent.FromUserId)
}
*/
// Log(noteOrContent.Title)
// LogJ(noteOrContent)
/*
LogJ(c.Params)
for name, _ := range c.Params.Files {
Log(name)
file, _, _ := c.Request.FormFile(name)
LogJ(file)
}
*/
// return c.RenderJson(re)
if noteOrContent.NotebookId == "" || !bson.IsObjectIdHex(noteOrContent.NotebookId) {
re.Msg = "notebookIdNotExists"
return c.RenderJson(re)
}
noteId := bson.NewObjectId()
// TODO 先上传图片/附件, 如果不成功, 则返回false
//
attachNum := 0
if noteOrContent.Files != nil && len(noteOrContent.Files) > 0 {
for i, file := range noteOrContent.Files {
if file.HasBody {
if file.LocalFileId != "" {
// FileDatas[54c7ae27d98d0329dd000000]
ok, msg, fileId := c.upload("FileDatas["+file.LocalFileId+"]", noteId.Hex(), file.IsAttach)
if !ok {
re.Ok = false
if msg != "" {
Log(msg)
Log(file.LocalFileId)
re.Msg = "fileUploadError"
}
// 报不是图片的错误没关系, 证明客户端传来非图片的数据
if msg != "notImage" {
return c.RenderJson(re)
}
} else {
// 建立映射
file.FileId = fileId
noteOrContent.Files[i] = file
if file.IsAttach {
attachNum++
}
}
} else {
return c.RenderJson(re)
}
}
}
}
c.fixPostNotecontent(&noteOrContent)
// Log("Add")
// LogJ(noteOrContent)
// return c.RenderJson(re)
note := info.Note{UserId: userId,
NoteId: noteId,
NotebookId: bson.ObjectIdHex(noteOrContent.NotebookId),
Title: noteOrContent.Title,
Tags: noteOrContent.Tags,
Desc: noteOrContent.Desc,
// ImgSrc: noteOrContent.ImgSrc,
IsBlog: noteOrContent.IsBlog,
IsMarkdown: noteOrContent.IsMarkdown,
AttachNum: attachNum,
}
noteContent := info.NoteContent{NoteId: note.NoteId,
UserId: userId,
IsBlog: note.IsBlog,
Content: noteOrContent.Content,
Abstract: noteOrContent.Abstract}
// 通过内容得到Desc, abstract
if noteOrContent.Abstract == "" {
note.Desc = SubStringHTMLToRaw(noteContent.Content, 200)
noteContent.Abstract = SubStringHTML(noteContent.Content, 200, "")
} else {
note.Desc = SubStringHTMLToRaw(noteContent.Abstract, 200)
}
note = noteService.AddNoteAndContentApi(note, noteContent, myUserId)
if note.NoteId == "" {
re.Ok = false
return c.RenderJson(re)
}
// 添加需要返回的
noteOrContent.NoteId = note.NoteId.Hex()
noteOrContent.Usn = note.Usn
noteOrContent.CreatedTime = note.CreatedTime
noteOrContent.UpdatedTime = note.UpdatedTime
noteOrContent.UserId = c.getUserId()
noteOrContent.IsMarkdown = note.IsMarkdown
// 删除一些不要返回的, 删除Desc?
noteOrContent.Content = ""
noteOrContent.Abstract = ""
// apiNote := info.NoteToApiNote(note, noteOrContent.Files)
return c.RenderJson(noteOrContent)
}
// 更新笔记
// [OK]
func (c ApiNote) UpdateNote(noteOrContent info.ApiNote) revel.Result {
re := info.NewReUpdate()
noteUpdate := bson.M{}
needUpdateNote := false
noteId := noteOrContent.NoteId
if noteOrContent.NoteId == "" {
re.Msg = "noteIdNotExists"
return c.RenderJson(re)
}
if noteOrContent.Usn <= 0 {
re.Msg = "usnNotExists"
return c.RenderJson(re)
}
// Log("_____________")
// LogJ(noteOrContent)
/*
LogJ(c.Params.Files)
LogJ(c.Request.Header)
LogJ(c.Params.Values)
*/
// 先判断USN的问题, 因为很可能添加完附件后, 会有USN冲突, 这时附件就添错了
userId := c.getUserId()
note := noteService.GetNote(noteId, userId)
if note.NoteId == "" {
re.Msg = "notExists"
return c.RenderJson(re)
}
if note.Usn != noteOrContent.Usn {
re.Msg = "conflict"
Log("conflict")
return c.RenderJson(re)
}
// 如果传了files
// TODO 测试
/*
for key, v := range c.Params.Values {
Log(key)
Log(v)
}
*/
// Log(c.Has("Files[0]"))
if c.Has("Files[0][LocalFileId]") {
// LogJ(c.Params.Files)
if noteOrContent.Files != nil && len(noteOrContent.Files) > 0 {
for i, file := range noteOrContent.Files {
if file.HasBody {
if file.LocalFileId != "" {
// FileDatas[54c7ae27d98d0329dd000000]
ok, msg, fileId := c.upload("FileDatas["+file.LocalFileId+"]", noteId, file.IsAttach)
if !ok {
Log("upload file error")
re.Ok = false
if msg == "" {
re.Msg = "fileUploadError"
} else {
re.Msg = msg
}
return c.RenderJson(re)
} else {
// 建立映射
file.FileId = fileId
noteOrContent.Files[i] = file
}
} else {
return c.RenderJson(re)
}
}
}
}
// Log("after upload")
// LogJ(noteOrContent.Files)
}
// 移到外面来, 删除最后一个file时也要处理, 不然总删不掉
// 附件问题, 根据Files, 有些要删除的, 只留下这些
attachService.UpdateOrDeleteAttachApi(noteId, userId, noteOrContent.Files)
// Desc前台传来
if c.Has("Desc") {
needUpdateNote = true
noteUpdate["Desc"] = noteOrContent.Desc
}
/*
if c.Has("ImgSrc") {
needUpdateNote = true
noteUpdate["ImgSrc"] = noteOrContent.ImgSrc
}
*/
if c.Has("Title") {
needUpdateNote = true
noteUpdate["Title"] = noteOrContent.Title
}
if c.Has("IsTrash") {
needUpdateNote = true
noteUpdate["IsTrash"] = noteOrContent.IsTrash
}
// 是否是博客
if c.Has("IsBlog") {
needUpdateNote = true
noteUpdate["IsBlog"] = noteOrContent.IsBlog
}
/*
Log(c.Has("tags[0]"))
Log(c.Has("Tags[]"))
for key, v := range c.Params.Values {
Log(key)
Log(v)
}
*/
if c.Has("Tags[0]") {
needUpdateNote = true
noteUpdate["Tags"] = noteOrContent.Tags
}
if c.Has("NotebookId") {
if bson.IsObjectIdHex(noteOrContent.NotebookId) {
needUpdateNote = true
noteUpdate["NotebookId"] = bson.ObjectIdHex(noteOrContent.NotebookId)
}
}
if c.Has("Content") {
// 通过内容得到Desc, 如果有Abstract, 则用Abstract生成Desc
if noteOrContent.Abstract == "" {
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Content, 200)
} else {
noteUpdate["Desc"] = SubStringHTMLToRaw(noteOrContent.Abstract, 200)
}
}
afterNoteUsn := 0
noteOk := false
noteMsg := ""
if needUpdateNote {
noteOk, noteMsg, afterNoteUsn = noteService.UpdateNote(c.getUserId(), noteOrContent.NoteId, noteUpdate, noteOrContent.Usn)
if !noteOk {
re.Ok = false
re.Msg = noteMsg
return c.RenderJson(re)
}
}
//-------------
afterContentUsn := 0
contentOk := false
contentMsg := ""
if c.Has("Content") {
// 把fileId替换下
c.fixPostNotecontent(&noteOrContent)
// 如果传了Abstract就用之
if noteOrContent.Abstract == "" {
noteOrContent.Abstract = SubStringHTML(noteOrContent.Content, 200, "")
}
// Log("--------> afte fixed")
// Log(noteOrContent.Content)
contentOk, contentMsg, afterContentUsn = noteService.UpdateNoteContent(c.getUserId(),
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract, needUpdateNote, noteOrContent.Usn)
}
if needUpdateNote {
re.Ok = noteOk
re.Msg = noteMsg
re.Usn = afterNoteUsn
} else {
re.Ok = contentOk
re.Msg = contentMsg
re.Usn = afterContentUsn
}
if !re.Ok {
return c.RenderJson(re)
}
noteOrContent.Content = ""
noteOrContent.Usn = re.Usn
noteOrContent.UpdatedTime = time.Now()
// Log("after upload")
// LogJ(noteOrContent.Files)
noteOrContent.UserId = c.getUserId()
return c.RenderJson(noteOrContent)
}
// 删除trash
func (c ApiNote) DeleteTrash(noteId string, usn int) revel.Result {
re := info.NewReUpdate()
re.Ok, re.Msg, re.Usn = trashService.DeleteTrashApi(noteId, c.getUserId(), usn)
return c.RenderJson(re)
}
// 得到历史列表
/*
func (c ApiNote) GetHistories(noteId string) revel.Result {
re := info.NewRe()
histories := noteContentHistoryService.ListHistories(noteId, c.getUserId())
if len(histories) > 0 {
re.Ok = true
re.Item = histories
}
return c.RenderJson(re)
}
*/
// 0.2 新增
// 导出成PDF
// 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
}
@@ -1,106 +0,0 @@
package api
import (
"github.com/leanote/leanote/app/info"
"github.com/revel/revel"
"gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
// "io/ioutil"
)
// 笔记本API
type ApiNotebook struct {
ApiBaseContrller
}
// 从Notebook -> ApiNotebook
func (c ApiNotebook) fixNotebooks(notebooks []info.Notebook) []info.ApiNotebook {
if notebooks == nil {
return nil
}
apiNotebooks := make([]info.ApiNotebook, len(notebooks))
for i, notebook := range notebooks {
apiNotebooks[i] = c.fixNotebook(&notebook)
}
return apiNotebooks
}
func (c ApiNotebook) fixNotebook(notebook *info.Notebook) info.ApiNotebook {
if notebook == nil {
return info.ApiNotebook{}
}
return info.ApiNotebook{
NotebookId : notebook.NotebookId,
UserId : notebook.UserId,
ParentNotebookId : notebook.ParentNotebookId,
Seq : notebook.Seq,
Title : notebook.Title,
UrlTitle : notebook.UrlTitle,
IsBlog : notebook.IsBlog,
CreatedTime : notebook.CreatedTime,
UpdatedTime : notebook.UpdatedTime,
Usn: notebook.Usn,
IsDeleted: notebook.IsDeleted,
}
}
// 获取同步的笔记本
// [OK]
// > afterUsn的笔记
// 返回 {ChunkHighUsn: 本下最大的usn, 借此可以知道是否还有, Notebooks: []}
func (c ApiNotebook) GetSyncNotebooks(afterUsn, maxEntry int) revel.Result {
if maxEntry == 0 {
maxEntry = 100
}
notebooks := notebookService.GeSyncNotebooks(c.getUserId(), afterUsn, maxEntry)
return c.RenderJson(c.fixNotebooks(notebooks))
}
// 得到用户的所有笔记本
// [OK]
// info.SubNotebooks
func (c ApiNotebook) GetNotebooks() revel.Result {
notebooks := notebookService.GeSyncNotebooks(c.getUserId(), 0, 99999)
return c.RenderJson(c.fixNotebooks(notebooks))
}
// 添加notebook
// [OK]
func (c ApiNotebook) AddNotebook(title, parentNotebookId string, seq int) revel.Result {
notebook := info.Notebook{NotebookId: bson.NewObjectId(),
Title: title,
Seq: seq,
UserId: bson.ObjectIdHex(c.getUserId())}
if parentNotebookId != "" && bson.IsObjectIdHex(parentNotebookId) {
notebook.ParentNotebookId = bson.ObjectIdHex(parentNotebookId)
}
re := info.NewRe()
re.Ok, notebook = notebookService.AddNotebook(notebook)
if !re.Ok {
return c.RenderJson(re)
}
return c.RenderJson(c.fixNotebook(&notebook))
}
// 修改笔记
// [OK]
func (c ApiNotebook) UpdateNotebook(notebookId, title, parentNotebookId string, seq, usn int) revel.Result {
re := info.NewApiRe()
ok, msg, notebook := notebookService.UpdateNotebookApi(c.getUserId(), notebookId, title, parentNotebookId, seq, usn)
if !ok {
re.Ok = false
re.Msg = msg
return c.RenderJson(re)
}
LogJ(notebook)
return c.RenderJson(c.fixNotebook(&notebook))
}
// 删除笔记本
// [OK]
func (c ApiNotebook) DeleteNotebook(notebookId string, usn int) revel.Result {
re := info.NewApiRe()
re.Ok, re.Msg = notebookService.DeleteNotebookForce(c.getUserId(), notebookId, usn)
return c.RenderJson(re)
}
-56
View File
@@ -1,56 +0,0 @@
package api
import (
"github.com/leanote/leanote/app/info"
"github.com/revel/revel"
// "gopkg.in/mgo.v2/bson"
// . "github.com/leanote/leanote/app/lea"
// "io/ioutil"
)
// 标签API
type ApiTag struct {
ApiBaseContrller
}
// 获取同步的标签
// [OK]
// > afterUsn的笔记
// 返回 {ChunkHighUsn: 本下最大的usn, 借此可以知道是否还有, Notebooks: []}
func (c ApiTag) GetSyncTags(afterUsn, maxEntry int) revel.Result {
if maxEntry == 0 {
maxEntry = 100
}
tags := tagService.GeSyncTags(c.getUserId(), afterUsn, maxEntry)
return c.RenderJson(tags)
}
// 添加Tag
// [OK]
// 不会产生冲突, 即使里面有
// 返回
/*
{
"TagId": "551978dd99c37b9bc5000001",
"UserId": "54a1676399c37b1c77000002",
"Tag": "32",
"Usn": 25,
"Count": 1,
"CreatedTime": "2015-03-31T00:25:01.149312407+08:00",
"UpdatedTime": "2015-03-31T00:25:01.149312407+08:00",
"IsDeleted": false
}
*/
func (c ApiTag) AddTag(tag string) revel.Result {
ret := tagService.AddOrUpdateTag(c.getUserId(), tag)
return c.RenderJson(ret)
}
// 删除标签
// [OK]
func (c ApiTag) DeleteTag(tag string, usn int) revel.Result {
re := info.NewReUpdate()
re.Ok, re.Msg, re.Usn = tagService.DeleteTagApi(c.getUserId(), tag, usn)
return c.RenderJson(re)
}
-155
View File
@@ -1,155 +0,0 @@
package api
import (
"github.com/revel/revel"
// "encoding/json"
"gopkg.in/mgo.v2/bson"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"time"
// "github.com/leanote/leanote/app/types"
"io/ioutil"
// "fmt"
// "math"
"os"
// "path"
// "strconv"
)
type ApiUser struct {
ApiBaseContrller
}
// 获取用户信息
// [OK]
func (c ApiUser) Info() revel.Result {
re := info.NewApiRe()
userInfo := c.getUserInfo()
if userInfo.UserId == "" {
return c.RenderJson(re)
}
apiUser := info.ApiUser{
UserId: userInfo.UserId.Hex(),
Username: userInfo.Username,
Email: userInfo.Email,
Logo: userInfo.Logo,
Verified: userInfo.Verified,
}
return c.RenderJson(apiUser)
}
// 修改用户名
// [OK]
func (c ApiUser) UpdateUsername(username string) revel.Result {
re := info.NewApiRe()
if c.GetUsername() == "demo" {
re.Msg = "cannotUpdateDemo"
return c.RenderJson(re)
}
if re.Ok, re.Msg = Vd("username", username); !re.Ok {
return c.RenderJson(re)
}
re.Ok, re.Msg = userService.UpdateUsername(c.getUserId(), username)
return c.RenderJson(re)
}
// 修改密码
// [OK]
func (c ApiUser) UpdatePwd(oldPwd, pwd string) revel.Result {
re := info.NewApiRe()
if c.GetUsername() == "demo" {
re.Msg = "cannotUpdateDemo"
return c.RenderJson(re)
}
if re.Ok, re.Msg = Vd("password", oldPwd); !re.Ok {
return c.RenderJson(re)
}
if re.Ok, re.Msg = Vd("password", pwd); !re.Ok {
return c.RenderJson(re)
}
re.Ok, re.Msg = userService.UpdatePwd(c.getUserId(), oldPwd, pwd)
return c.RenderJson(re)
}
// 获得同步状态
// [OK]
func (c ApiUser) GetSyncState() revel.Result {
ret := bson.M{"LastSyncUsn": userService.GetUsn(c.getUserId()), "LastSyncTime": time.Now().Unix()}
return c.RenderJson(ret)
}
// 头像设置
// 参数file=文件
// 成功返回{Logo: url} 头像新url
// [OK]
func (c ApiUser) UpdateLogo() revel.Result {
ok, msg, url := c.uploadImage()
if ok {
ok = userService.UpdateAvatar(c.getUserId(), url)
return c.RenderJson(map[string]string{"Logo": url})
} else {
re := info.NewApiRe()
re.Msg = msg
return c.RenderJson(re)
}
}
// 上传图片
func (c ApiUser) uploadImage() (ok bool, msg, url string) {
var fileUrlPath = ""
ok = false
file, handel, err := c.Request.FormFile("file")
if err != nil {
return
}
defer file.Close()
// 生成上传路径
fileUrlPath = "public/upload/" + c.getUserId() + "/images/logo"
dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755)
if err != nil {
return
}
// 生成新的文件名
filename := handel.Filename
var ext string
_, ext = SplitFilename(filename)
if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
msg = "notImage"
return
}
filename = NewGuid() + ext
data, err := ioutil.ReadAll(file)
if err != nil {
LogJ(err)
return
}
// > 5M?
if len(data) > 5*1024*1024 {
msg = "fileIsTooLarge"
return
}
toPath := dir + "/" + filename
err = ioutil.WriteFile(toPath, data, 0777)
if err != nil {
LogJ(err)
return
}
ok = true
url = configService.GetSiteUrl() + "/" + fileUrlPath + "/" + filename
return
}
+27
View File
@@ -0,0 +1,27 @@
package api
import (
"github.com/revel/revel"
// "encoding/json"
// "gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
// "github.com/leanote/leanote/app/info"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
// "math"
// "os"
// "path"
// "strconv"
)
type ApiUser struct {
*revel.Controller
}
// 修改用户名, 需要重置session
func (c ApiUser) Info() revel.Result {
Log("APIUser");
return c.RenderTemplate("home/index.html");
// return nil;
}
-144
View File
@@ -1,144 +0,0 @@
package api
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/service"
// . "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
"strings"
)
var userService *service.UserService
var noteService *service.NoteService
var trashService *service.TrashService
var notebookService *service.NotebookService
var noteContentHistoryService *service.NoteContentHistoryService
var authService *service.AuthService
var shareService *service.ShareService
var blogService *service.BlogService
var tagService *service.TagService
var pwdService *service.PwdService
var tokenService *service.TokenService
var suggestionService *service.SuggestionService
var albumService *service.AlbumService
var noteImageService *service.NoteImageService
var fileService *service.FileService
var attachService *service.AttachService
var configService *service.ConfigService
var emailService *service.EmailService
var sessionService *service.SessionService
var pageSize = 1000
var defaultSortField = "UpdatedTime"
var leanoteUserId = "admin" // 不能更改
// 状态
const (
S_DEFAULT = iota // 0
S_NOT_LOGIN // 1
S_WRONG_USERNAME_PASSWORD // 2
S_WRONG_CAPTCHA // 3
S_NEED_CAPTCHA // 4
S_NOT_OPEN_REGISTER // 4
)
// 拦截器
// 不需要拦截的url
var commonUrl = map[string]map[string]bool{"ApiAuth": map[string]bool{"Login": true,
"Register": true,
},
// 文件的操作也不用登录, userId会从session中获取
"ApiFile": map[string]bool{"GetImage": true,
"GetAttach": true,
"GetAllAttachs": true,
},
}
func needValidate(controller, method string) bool {
// 在里面
if v, ok := commonUrl[controller]; ok {
// 在commonUrl里
if _, ok2 := v[method]; ok2 {
return false
}
return true
} else {
// controller不在这里的, 肯定要验证
return true
}
}
// 这里得到token, 若不是login, logout等公用操作, 必须验证是否已登录
func AuthInterceptor(c *revel.Controller) revel.Result {
// 得到token /api/user/info?userId=xxx&token=xxxxx
token := c.Params.Values.Get("token")
noToken := false
if token == "" {
// 若无, 则取sessionId
token = c.Session.Id()
noToken = true
}
c.Session["_token"] = token
// 全部变成首字大写
var controller = strings.Title(c.Name)
var method = strings.Title(c.MethodName)
// 验证是否已登录
// 通过sessionService判断该token下是否有userId, 并返回userId
userId := sessionService.GetUserId(token)
if noToken && userId == "" {
// 从session中获取, api/file/getImage, api/file/getAttach, api/file/getAllAttach
// 客户端
userId, _ = c.Session["UserId"];
}
c.Session["_userId"] = userId
// 是否需要验证?
if !needValidate(controller, method) {
return nil
}
if userId != "" {
return nil // 已登录
}
// 没有登录, 返回错误的信息, 需要登录
re := info.NewApiRe()
re.Msg = "NOTLOGIN"
return c.RenderJson(re)
}
func init() {
// interceptors
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiAuth{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiUser{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiFile{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiNote{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiTag{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &ApiNotebook{})
}
// 最外层init.go调用
// 获取service, 单例
func InitService() {
notebookService = service.NotebookS
noteService = service.NoteS
noteContentHistoryService = service.NoteContentHistoryS
trashService = service.TrashS
shareService = service.ShareS
userService = service.UserS
tagService = service.TagS
blogService = service.BlogS
tokenService = service.TokenS
noteImageService = service.NoteImageS
fileService = service.FileS
albumService = service.AlbumS
attachService = service.AttachS
pwdService = service.PwdS
suggestionService = service.SuggestionS
authService = service.AuthS
configService = service.ConfigS
emailService = service.EmailS
sessionService = service.SessionS
}
+4 -4
View File
@@ -48,7 +48,8 @@ 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{"ToPdf": true}, "Note": map[string]bool{"ToImage": true},
"Share": map[string]bool{"ShowShareNote": true, "Verify4ShareNote": true},
"Blog": map[string]bool{"Index": true, "Blog": map[string]bool{"Index": true,
"View": true, "View": true,
"AboutMe": true, "AboutMe": true,
@@ -68,7 +69,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,10 +147,9 @@ 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 = "Please upload zip file" re.Msg = "请上传zip文件"
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 = "File is big than 10M" re.Msg = "文件大于10M"
return c.RenderJson(re) return c.RenderJson(re)
} }
+2 -36
View File
@@ -6,7 +6,6 @@ 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
@@ -29,7 +28,7 @@ var Groups *mgo.Collection
var GroupUsers *mgo.Collection var GroupUsers *mgo.Collection
var Tags *mgo.Collection var Tags *mgo.Collection
var NoteTags *mgo.Collection //var TagNotes *mgo.Collection
var TagCounts *mgo.Collection var TagCounts *mgo.Collection
var UserBlogs *mgo.Collection var UserBlogs *mgo.Collection
@@ -63,26 +62,10 @@ 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")
@@ -133,7 +116,7 @@ func Init(url, dbname string) {
// tag // tag
Tags = Session.DB(dbname).C("tags") Tags = Session.DB(dbname).C("tags")
NoteTags = Session.DB(dbname).C("note_tags") // TagNotes = Session.DB(dbname).C("tag_notes")
TagCounts = Session.DB(dbname).C("tag_count") TagCounts = Session.DB(dbname).C("tag_count")
// blog // blog
@@ -355,20 +338,3 @@ 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("重连失败!!!! 警告")
}
}
}
+2 -4
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/package2/src/github.com/leanote/leanote/messages/" var msgBasePath = "/Users/life/Documents/Go/package1/src/github.com/leanote/leanote/messages/"
var targetBasePath = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/public/js/i18n/" var targetBasePath = "/Users/life/Documents/Go/package1/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,8 +84,6 @@ 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")
} }
-119
View File
@@ -1,119 +0,0 @@
package info
import (
"time"
"gopkg.in/mgo.v2/bson"
)
//---------
// 数据结构
//---------
type NoteFile struct {
FileId string // 服务器端Id
LocalFileId string // 客户端Id
Type string // images/png, doc, xls, 根据fileName确定
Title string
HasBody bool // 传过来的值是否要更新内容
IsAttach bool // 是否是附件, 不是附件就是图片
}
type ApiNote struct {
NoteId string
NotebookId string
UserId string
Title string
Desc string
// ImgSrc string
Tags []string
Abstract string
Content string
IsMarkdown bool
// FromUserId string // 为共享而新建
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
IsTrash bool
IsDeleted bool
Usn int
Files []NoteFile
CreatedTime time.Time
UpdatedTime time.Time
PublicTime time.Time
}
// 内容
type ApiNoteContent struct {
NoteId bson.ObjectId `bson:"_id,omitempty"`
UserId bson.ObjectId `bson:"UserId"`
Content string `Content`
// CreatedTime time.Time `CreatedTime`
// UpdatedTime time.Time `UpdatedTime`
}
// 转换
func NoteToApiNote(note Note, files []NoteFile) ApiNote {
apiNote := ApiNote{}
return apiNote
}
//----------
// 用户信息
//----------
type ApiUser struct {
UserId string
Username string
Email string
Verified bool
Logo string
}
//----------
// Notebook
//----------
type ApiNotebook struct {
NotebookId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
UserId bson.ObjectId `bson:"UserId"`
ParentNotebookId bson.ObjectId `bson:"ParentNotebookId,omitempty"` // 上级
Seq int `Seq` // 排序
Title string `Title` // 标题
UrlTitle string `UrlTitle` // Url标题 2014/11.11加
IsBlog bool `IsBlog,omitempty` // 是否是Blog 2013/12/29 新加
CreatedTime time.Time `CreatedTime,omitempty`
UpdatedTime time.Time `UpdatedTime,omitempty`
Usn int `Usn` // UpdateSequenceNum
IsDeleted bool `IsDeleted`
}
//---------
// api 返回
//---------
// 一般返回
type ApiRe struct {
Ok bool
Msg string
}
func NewApiRe() ApiRe {
return ApiRe{Ok: false}
}
// auth
type AuthOk struct {
Ok bool
Token string
UserId bson.ObjectId
Email string
Username string
}
// 供notebook, note, tag更新的返回数据用
type ReUpdate struct {
Ok bool
Msg string
Usn int
}
func NewReUpdate() ReUpdate {
return ReUpdate{Ok: false}
}
-8
View File
@@ -47,11 +47,3 @@ type Archive struct {
MonthAchives []ArchiveMonth MonthAchives []ArchiveMonth
Posts []*Post Posts []*Post
} }
type Cate struct {
CateId string
ParentCateId string
Title string
UrlTitle string
Children []*Cate
}
+1 -4
View File
@@ -41,10 +41,7 @@ type Note struct {
PublicTime time.Time `PublicTime,omitempty` // 发表时间, 公开为博客则设置 PublicTime time.Time `PublicTime,omitempty` // 发表时间, 公开为博客则设置
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了 UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了
// 2015/1/15, 更新序号 SharePass int `SharePass` //分享笔记的密码
Usn int `Usn` // UpdateSequenceNum
IsDeleted bool `IsDeleted` // 删除位
} }
// 内容 // 内容
-4
View File
@@ -19,10 +19,6 @@ type Notebook struct {
IsBlog bool `IsBlog,omitempty` // 是否是Blog 2013/12/29 新加 IsBlog bool `IsBlog,omitempty` // 是否是Blog 2013/12/29 新加
CreatedTime time.Time `CreatedTime,omitempty` CreatedTime time.Time `CreatedTime,omitempty`
UpdatedTime time.Time `UpdatedTime,omitempty` UpdatedTime time.Time `UpdatedTime,omitempty`
// 2015/1/15, 更新序号
Usn int `Usn` // UpdateSequenceNum
IsDeleted bool `IsDeleted`
} }
// 仅仅是为了返回前台 // 仅仅是为了返回前台
+2 -3
View File
@@ -13,9 +13,8 @@ type Session struct {
LoginTimes int `LoginTimes` // 登录错误时间 LoginTimes int `LoginTimes` // 登录错误时间
Captcha string `Captcha` // 验证码 Captcha string `Captcha` // 验证码
Token string `Token` //attach token
UserId string `UserId` // API时有值UserId TokenTime time.Time `TokenTime` //token生成时间
CreatedTime time.Time `CreatedTime` CreatedTime time.Time `CreatedTime`
UpdatedTime time.Time `UpdatedTime` // 更新时间, expire这个时间会自动清空 UpdatedTime time.Time `UpdatedTime` // 更新时间, expire这个时间会自动清空
} }
+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`
-14
View File
@@ -2,7 +2,6 @@ package info
import ( import (
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time"
) )
// 这里主要是为了统计每个tag的note数目 // 这里主要是为了统计每个tag的note数目
@@ -22,18 +21,6 @@ type Tag struct {
Tags []string `Tags` Tags []string `Tags`
} }
// v2 版标签
type NoteTag struct {
TagId bson.ObjectId `bson:"_id"`
UserId bson.ObjectId `UserId` // 谁的
Tag string `Tag` // UserId, Tag是唯一索引
Usn int `Usn` // Update Sequence Number
Count int `Count` // 笔记数
CreatedTime time.Time `CreatedTime`
UpdatedTime time.Time `UpdatedTime`
IsDeleted bool `IsDeleted` // 删除位
}
type TagCount struct { type TagCount struct {
TagCountId bson.ObjectId `bson:"_id,omitempty"` TagCountId bson.ObjectId `bson:"_id,omitempty"`
UserId bson.ObjectId `UserId` // 谁的 UserId bson.ObjectId `UserId` // 谁的
@@ -41,7 +28,6 @@ type TagCount struct {
IsBlog bool `IsBlog` // 是否是博客的tag统计 IsBlog bool `IsBlog` // 是否是博客的tag统计
Count int `Count` // 统计数量 Count int `Count` // 统计数量
} }
/* /*
type TagsCounts []TagCount type TagsCounts []TagCount
func (this TagsCounts) Len() int { func (this TagsCounts) Len() int {
+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` // 文件夹路径, public/upload/54d7620d99c37b030600002c/themes/54d867c799c37b533e000001 Path string `Path` // 文件夹路径
Info map[string]interface{} `Info` // 所有信息 Info map[string]interface{} `Info` // 所有信息
IsActive bool `IsActive` // 是否在用 IsActive bool `IsActive` // 是否在用
+15 -26
View File
@@ -37,25 +37,21 @@ type User struct {
// 用户的帐户类型 // 用户的帐户类型
ImageNum int `bson:"ImageNum" json:"-"` // 图片数量 ImageNum int `bson:"ImageNum" json:"-"` // 图片数量
ImageSize int `bson:"ImageSize" json:"-"` // 图片大小 ImageSize int `bson:"ImageSize" json:"-"` // 图片大小
AttachNum int `bson:"AttachNum" json:"-"` // 附件数量 AttachNum int `bson:"AttachNum" json:"-"` // 附件数量
AttachSize int `bson:"AttachSize" json:"-"` // 附件大小 AttachSize int `bson:"AttachSize" json:"-"` // 附件大小
FromUserId bson.ObjectId `FromUserId,omitempty` // 邀请的用户 FromUserId bson.ObjectId `FromUserId,omitempty` // 邀请的用户
AccountType string `bson:"AccountType" json:"-"` // normal(为空), premium AccountType string `bson:"AccountType" json:"-"` // normal(为空), premium
AccountStartTime time.Time `bson:"AccountStartTime" json:"-"` // 开始日期 AccountStartTime time.Time `bson:"AccountStartTime" json:"-"` // 开始日期
AccountEndTime time.Time `bson:"AccountEndTime" json:"-"` // 结束日期 AccountEndTime time.Time `bson:"AccountEndTime" json:"-"` // 结束日期
// 阈值 // 阈值
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量 MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小 MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量 MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小 MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小 MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
// 2015/1/15, 更新序号
Usn int `Usn` // UpdateSequenceNum , 全局的
FullSyncBefore time.Time `bson:"FullSyncBefore"` // 需要全量同步的时间, 如果 > 客户端的LastSyncTime, 则需要全量更新
} }
type UserAccount struct { type UserAccount struct {
@@ -63,18 +59,11 @@ type UserAccount struct {
AccountStartTime time.Time `bson:"AccountStartTime" json:"-"` // 开始日期 AccountStartTime time.Time `bson:"AccountStartTime" json:"-"` // 开始日期
AccountEndTime time.Time `bson:"AccountEndTime" json:"-"` // 结束日期 AccountEndTime time.Time `bson:"AccountEndTime" json:"-"` // 结束日期
// 阈值 // 阈值
MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量 MaxImageNum int `bson:"MaxImageNums" json:"-"` // 图片数量
MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小 MaxImageSize int `bson:"MaxImageSize" json:"-"` // 图片大小
MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量 MaxAttachNum int `bson:"MaxAttachNum" json:"-"` // 图片数量
MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小 MaxAttachSize int `bson:"MaxAttachSize" json:"-"` // 图片大小
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小 MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
}
// note主页需要
type UserAndBlogUrl struct {
User
BlogUrl string `BlogUrl`
PostUrl string `PostUrl`
} }
// 用户与博客信息结合, 公开 // 用户与博客信息结合, 公开
+9 -58
View File
@@ -6,10 +6,11 @@ import (
"github.com/leanote/leanote/app/service" "github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/controllers" "github.com/leanote/leanote/app/controllers"
"github.com/leanote/leanote/app/controllers/api"
"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"
@@ -31,11 +32,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.
@@ -49,18 +50,6 @@ 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)
@@ -155,43 +144,8 @@ 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, typeStr string) template.HTML { revel.TemplateFuncs["blogTagsLea"] = func(renderArgs map[string]interface{}, tags []string, isRecommend bool) template.HTML {
if tags == nil || len(tags) == 0 { if tags == nil || len(tags) == 0 {
return "" return ""
} }
@@ -200,12 +154,10 @@ func init() {
lenTags := len(tags) lenTags := len(tags)
tagPostUrl := "http://lea.leanote.com/" tagPostUrl := "http://lea.leanote.com/"
if typeStr == "recommend" { if isRecommend {
tagPostUrl += "?tag="; tagPostUrl += "?tag=";
} else if typeStr == "latest" {
tagPostUrl += "latest?tag=";
} else { } else {
tagPostUrl += "subscription?tag="; tagPostUrl += "latest?tag=";
} }
for i, tag := range tags { for i, tag := range tags {
@@ -414,13 +366,12 @@ func init() {
// email配置 // email配置
InitEmail() InitEmail()
InitVd() InitVd()
// memcache.InitMemcache() // session服务 memcache.InitMemcache() // session服务
// 其它service // 其它service
service.InitService() service.InitService()
controllers.InitService() controllers.InitService()
admin.InitService() admin.InitService()
member.InitService() member.InitService()
service.ConfigS.InitGlobalConfigs() service.ConfigS.InitGlobalConfigs()
api.InitService() });
})
} }
-8
View File
@@ -56,14 +56,6 @@ 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
@@ -1,22 +0,0 @@
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)
}
+23 -138
View File
@@ -10,9 +10,6 @@ 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"
) )
@@ -25,22 +22,6 @@ 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)
@@ -141,49 +122,39 @@ func ReplaceAll(oldStr, pattern, newStr string) string {
return string(s) return string(s)
} }
// 获取纯文本 func SubStringHTML(param string, length int, end string) string {
func SubStringHTMLToRaw(param string, length int) string { // 先取出<pre></pre>占位..
if param == "" {
return param result := ""
}
// 1
n := 0 n := 0
var temp rune // 中文问题, 用rune来解决 var temp rune // 中文问题, 用rune来解决
isCode := false //是不是HTML代码
isHTML := false //是不是HTML特殊字符,如&nbsp;
rStr := []rune(param) rStr := []rune(param)
lenStr := len(rStr) for i := 0; i < len(rStr); i++ {
isCode := false
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 } else if temp == '&' {
} else if temp == '>' { isHTML = true
} else if temp == '>' && isCode {
n = n - 1
isCode = false isCode = false
resultRune[n] = ' '; } else if temp == ';' && isHTML {
isHTML = false
n++
if n >= length {
break
}
continue
} }
if !isCode { if !isCode && !isHTML {
resultRune[n] = temp; n = n + 1
// s += string(temp) }
n++ result += string(temp)
if n >= length { if n >= length {
break break
}
} }
} }
result := string(resultRune[0:n]) result += end
return strings.Trim(result, " ")
}
// 自带方法补全html
func fixHtml(result string) string {
// 取出所有标签 // 取出所有标签
tempResult := ReplaceAll(result, "(>)[^<>]*(<?)", "$1$2") // 把标签中间的所有内容都去掉了 tempResult := ReplaceAll(result, "(>)[^<>]*(<?)", "$1$2") // 把标签中间的所有内容都去掉了
@@ -225,72 +196,6 @@ func fixHtml(result 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 == "" {
@@ -307,7 +212,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,6}$`, email) ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[0-9a-zA-Z]{2,3}$`, email)
return ok return ok
} }
@@ -374,23 +279,3 @@ 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
}
+3 -39
View File
@@ -4,7 +4,6 @@ import (
"github.com/revel/revel" "github.com/revel/revel"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/controllers" "github.com/leanote/leanote/app/controllers"
// "github.com/leanote/leanote/app/controllers/api"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
@@ -55,34 +54,15 @@ func nextKey(key string) string {
return key[:fieldLen] return key[:fieldLen]
} }
var leanoteStructBinder = revel.Binder{ var leanoteStructBinder = revel.Binder{
// name == "noteOrContent"
Bind: func(params *revel.Params, name string, typ reflect.Type) reflect.Value { Bind: func(params *revel.Params, name string, typ reflect.Type) reflect.Value {
result := reflect.New(typ).Elem() // 创建一个该类型的, 然后其field从所有的param去取 result := reflect.New(typ).Elem()
fieldValues := make(map[string]reflect.Value) fieldValues := make(map[string]reflect.Value)
// fmt.Println(name) for key, _ := range params.Values {
// fmt.Println(typ) // api.NoteFiles
// name = files[0], files[1], noteContent
// fmt.Println(params.Values)
/*
map[Title:[test1] METHOD:[POST] NotebookId:[54c4f51705fcd14031000002]
files[1][FileId]:[]
controller:[note]
files[1][LocalFileId]:[54c7ae27d98d0329dd000000] files[1][HasBody]:[true] files[0][FileId]:[] files[0][LocalFileId]:[54c7ae855e94ea2dba000000] action:[addNote] Content:[<p>lifedddddd</p><p><img src="app://leanote/data/54bdc65599c37b0da9000002/images/1422368307147_2.png" alt="" data-mce-src="app://leanote/data/54bdc65599c37b0da9000002/images/1422368307147_2.png" style="display: block; margin-left: auto; margin-right: auto;"></p><p><img src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae27d98d0329dd000000" alt="" data-mce-src="http://127.0.0.1:8008/api/file/getImg?fileId=54c7ae27d98d0329dd000000"></p><p><br></p><p><img src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae855e94ea2dba000000" alt="" data-mce-src="http://127.0.0.1:8008/api/file/getImage?fileId=54c7ae855e94ea2dba000000" style="display: block; margin-left: auto; margin-right: auto;"></p><p><br></p><p><br></p>] IsBlog:[false] token:[user1]
files[0][HasBody]:[true]]
*/
nameIsSlice := strings.Contains(name, "[")
// fmt.Println(params.Values["files[1]"])
// fmt.Println(params.Values["Title"])
for key, _ := range params.Values {// Title, Content, Files
// 这里, 如果没有点, 默认就是a. // 这里, 如果没有点, 默认就是a.
// life // life
// fmt.Println("key:" + key); // files[0][LocalFileId]
// fmt.Println("name:" + name); // files[0][LocalFileId]
var suffix string var suffix string
var noPrefix = false var noPrefix = false
if nameIsSlice && strings.HasPrefix(key, name) { if !strings.HasPrefix(key, name + ".") {
suffix = key[len(name)+1:len(key)-1] // files[0][LocalFileId] 去掉 => LocalFileId
} else if !strings.HasPrefix(key, name + ".") {
noPrefix = true noPrefix = true
suffix = key suffix = key
// continue // continue
@@ -91,18 +71,13 @@ files[0][HasBody]:[true]]
// Strip off the prefix. e.g. foo.bar.baz => bar.baz // Strip off the prefix. e.g. foo.bar.baz => bar.baz
suffix = key[len(name)+1:] suffix = key[len(name)+1:]
} }
// fmt.Println(suffix);
fieldName := nextKey(suffix) // e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar" fieldName := nextKey(suffix) // e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar"
// fmt.Println(fieldName);
fieldLen := len(fieldName) fieldLen := len(fieldName)
if _, ok := fieldValues[fieldName]; !ok { if _, ok := fieldValues[fieldName]; !ok {
// Time to bind this field. Get it and make sure we can set it. // Time to bind this field. Get it and make sure we can set it.
fieldName = strings.Title(fieldName) // 传过来title, 但struct是Title
// fmt.Println("xx: " + fieldName)
fieldValue := result.FieldByName(fieldName) fieldValue := result.FieldByName(fieldName)
// fmt.Println(fieldValue)
if !fieldValue.IsValid() { if !fieldValue.IsValid() {
continue continue
} }
@@ -112,16 +87,8 @@ files[0][HasBody]:[true]]
var boundVal reflect.Value var boundVal reflect.Value
// 没有name前缀 // 没有name前缀
if(noPrefix) { if(noPrefix) {
// life
// fmt.Println("<<")
// fmt.Println(strings.Title(key[:fieldLen]));
boundVal = revel.Bind(params, key[:fieldLen], fieldValue.Type()) boundVal = revel.Bind(params, key[:fieldLen], fieldValue.Type())
} else { } else {
// fmt.Println("final")
// fmt.Println(key[:len(name)+1+fieldLen]) // files[0][HasBody
if nameIsSlice {
fieldLen += 1
}
boundVal = revel.Bind(params, key[:len(name)+1+fieldLen], fieldValue.Type()) boundVal = revel.Bind(params, key[:len(name)+1+fieldLen], fieldValue.Type())
} }
fieldValue.Set(boundVal) fieldValue.Set(boundVal)
@@ -149,8 +116,5 @@ func init() {
revel.TypeBinders[reflect.TypeOf(info.UserBlogComment{})] = leanoteStructBinder revel.TypeBinders[reflect.TypeOf(info.UserBlogComment{})] = leanoteStructBinder
revel.TypeBinders[reflect.TypeOf(info.UserBlogStyle{})] = leanoteStructBinder revel.TypeBinders[reflect.TypeOf(info.UserBlogStyle{})] = leanoteStructBinder
revel.TypeBinders[reflect.TypeOf(info.Notebook{})] = leanoteStructBinder revel.TypeBinders[reflect.TypeOf(info.Notebook{})] = leanoteStructBinder
revel.TypeBinders[reflect.TypeOf(info.UserAccount{})] = leanoteStructBinder
revel.TypeBinders[reflect.TypeOf(controllers.NoteOrContent{})] = leanoteStructBinder revel.TypeBinders[reflect.TypeOf(controllers.NoteOrContent{})] = leanoteStructBinder
revel.TypeBinders[reflect.TypeOf(info.ApiNote{})] = leanoteStructBinder
revel.TypeBinders[reflect.TypeOf(info.NoteFile{})] = leanoteStructBinder
} }
+6 -4
View File
@@ -179,6 +179,8 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
// 将该basePath下的所有文件提出 // 将该basePath下的所有文件提出
files := ListDir(basePath) files := ListDir(basePath)
Log(basePath)
LogJ(files);
for _, t := range files { for _, t := range files {
if !strings.Contains(t, ".html") { if !strings.Contains(t, ".html") {
continue; continue;
@@ -213,8 +215,7 @@ func RenderTemplate(name string, args map[string]interface{}, basePath string, i
//////////////////// ////////////////////
// // 错误显示
type ErrorResult struct { type ErrorResult struct {
RenderArgs map[string]interface{} RenderArgs map[string]interface{}
@@ -289,12 +290,13 @@ func (r ErrorResult) Apply(req *revel.Request, resp *revel.Response) {
r.RenderArgs["Router"] = revel.MainRouter r.RenderArgs["Router"] = revel.MainRouter
// 不是preview就不要显示错误了 // 不是preview就不要显示错误了
if r.IsPreview { LogJ(revelError)
// if r.IsPreview {
var b bytes.Buffer var b bytes.Buffer
out := io.Writer(resp.Out) out := io.Writer(resp.Out)
// out = ioutil.Discard // out = ioutil.Discard
err = tmpl.Execute(&b, r.RenderArgs) err = tmpl.Execute(&b, r.RenderArgs)
resp.WriteHeader(http.StatusOK, "text/html; charset=utf-8") resp.WriteHeader(http.StatusOK, "text/html; charset=utf-8")
b.WriteTo(out) b.WriteTo(out)
} // }
} }
-26
View File
@@ -1,26 +0,0 @@
// 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
@@ -0,0 +1,66 @@
package memcache
import (
"github.com/robfig/gomemcache/memcache"
"encoding/json"
"strconv"
)
var client *memcache.Client
// onAppStart后调用
func InitMemcache() {
client = memcache.New("localhost:11211")
}
//------------
// map
func SetMap(key string, value map[string]string, expiration int32) {
// 把value转成byte
bytes, _ := json.Marshal(value)
if expiration == -1 {
expiration = 30 * 24 * 60 * 60 // 30天
}
client.Set(&memcache.Item{Key: key, Value: bytes, Expiration: expiration})
}
func GetMap(key string) map[string]string {
item, err := client.Get(key)
if err != nil {
return nil
}
m := map[string]string{}
json.Unmarshal(item.Value, &m)
return m
}
//------------
// string
func GetString(key string) string {
item, err := client.Get(key)
if err != nil {
return ""
}
return string(item.Value)
}
func SetString(key string, value string, expiration int32) {
if expiration == -1 {
expiration = 30 * 24 * 60 * 60 // 30天
}
client.Set(&memcache.Item{Key: key, Value: []byte(value), Expiration: expiration})
}
//-------------------------
// int, 是通过转成string来存的
func GetInt(key string) int {
str := GetString(key)
i, _ := strconv.Atoi(str)
return i
}
func SetInt(key string, value int, expiration int32) {
str := strconv.Itoa(value)
SetString(key, str, expiration)
}
+1 -5
View File
@@ -2,7 +2,7 @@ package route
import ( import (
"github.com/revel/revel" "github.com/revel/revel"
"github.com/leanote/leanote/app/db" // "github.com/leanote/leanote/app/service"
// . "github.com/leanote/leanote/app/lea" // . "github.com/leanote/leanote/app/lea"
"net/url" "net/url"
"strings" "strings"
@@ -42,10 +42,6 @@ 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
@@ -0,0 +1,38 @@
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}
}
+210
View File
@@ -0,0 +1,210 @@
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)
}
+20 -16
View File
@@ -6,7 +6,6 @@ import (
"os/exec" "os/exec"
"io/ioutil" "io/ioutil"
"strings" "strings"
// "time"
) )
/* /*
@@ -33,7 +32,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/ZeroClipboard/ZeroClipboard"} "js/object_id"}
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/"
@@ -92,6 +91,7 @@ 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,13 +107,6 @@ 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)
} }
@@ -121,10 +114,8 @@ 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("/bin/sh", "-c", "grunt minify"); cmd := exec.Command("/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master/node_modules/jake/bin/cli.js", "minify")
cmd.Dir = base + "/tinymce_4.1.9" cmd.Dir = "/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master"
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")
@@ -132,19 +123,32 @@ 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{"js/main", "js/app/page", "js/contextmenu/jquery.contextmenu", otherJss := []string{"tinymce/tinymce", "js/main", "js/app/page", "js/contextmenu/jquery.contextmenu",
"mdeditor/editor/scrollLink",
"mdeditor/editor/editor",
"mdeditor/editor/jquery.waitforimages",
"mdeditor/editor/pagedown/local/Markdown.local.zh",
"mdeditor/editor/pagedown/local/Markdown.local.en",
"mdeditor/editor/pagedown/Markdown.Editor",
"mdeditor/editor/pagedown/Markdown.Sanitizer",
"mdeditor/editor/pagedown/Markdown.Converter",
"mdeditor/editor/Markdown.Extra",
"mdeditor/editor/underscore",
"mdeditor/editor/mathJax",
"js/jQuery-slimScroll-1.3.0/jquery.slimscroll",
"js/app/editor_drop_paste",
"js/app/attachment_upload",
"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",
} }
+21 -81
View File
@@ -1,46 +1,29 @@
package service package service
import ( import (
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"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"
"os" "os"
"strings" "strings"
"time"
) )
type AttachService struct { type AttachService struct {
} }
// add attach // add attach
// api调用时, 添加attach之前是没有note的 func (this *AttachService) AddAttach(attach info.Attach) (ok bool, msg string) {
// fromApi表示是api添加的, updateNote传过来的, 此时不要incNote's usn, 因为updateNote会inc的
func (this *AttachService) AddAttach(attach info.Attach, fromApi bool) (ok bool, msg string) {
attach.CreatedTime = time.Now() attach.CreatedTime = time.Now()
ok = db.Insert(db.Attachs, attach) ok = db.Insert(db.Attachs, attach)
note := noteService.GetNoteById(attach.NoteId.Hex())
// api调用时, 添加attach之前是没有note的
var userId string
if note.NoteId != "" {
userId = note.UserId.Hex()
} else {
userId = attach.UploadUserId.Hex()
}
if ok { if ok {
// 更新笔记的attachs num // 更新笔记的attachs num
this.updateNoteAttachNum(attach.NoteId, 1) this.updateNoteAttachNum(attach.NoteId, 1)
} }
if !fromApi {
// 增长note's usn
noteService.IncrNoteUsn(attach.NoteId.Hex(), userId)
}
return return
} }
@@ -63,45 +46,22 @@ 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{}
// 判断是否有权限为笔记添加附件, userId为空时表示是分享笔记的附件 // 判断是否有权限为笔记添加附件, userId为空时表示是分享笔记的附件
if userId != "" && !shareService.HasUpdateNotePerm(noteId, 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
} }
// api调用, 通过noteIds得到note's attachs, 通过noteId归类返回
func (this *AttachService) getAttachsByNoteIds(noteIds []bson.ObjectId) map[string][]info.Attach {
attachs := []info.Attach{}
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.M{"$in": noteIds}}, &attachs)
noteAttchs := make(map[string][]info.Attach)
for _, attach := range attachs {
noteId := attach.NoteId.Hex()
if itAttachs, ok := noteAttchs[noteId]; ok {
noteAttchs[noteId] = append(itAttachs, attach)
} else {
noteAttchs[noteId] = []info.Attach{attach}
}
}
return noteAttchs
}
func (this *AttachService) UpdateImageTitle(userId, fileId, title string) bool { func (this *AttachService) UpdateImageTitle(userId, fileId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title) return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
} }
// Delete note to delete attas firstly // Delete note to delete attas firstly
func (this *AttachService) DeleteAllAttachs(noteId, userId string) bool { func (this *AttachService) DeleteAllAttachs(noteId, userId string) bool {
note := noteService.GetNoteById(noteId) note := noteService.GetNoteById(noteId)
@@ -134,11 +94,7 @@ func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string)
attach.Path = strings.TrimLeft(attach.Path, "/") attach.Path = strings.TrimLeft(attach.Path, "/")
err := os.Remove(revel.BasePath + "/" + attach.Path) err := os.Remove(revel.BasePath + "/" + attach.Path)
if err == nil { if err == nil {
// userService.UpdateAttachSize(note.UserId.Hex(), -attach.Size) return true, "delete file error"
// 修改note Usn
noteService.IncrNoteUsn(attach.NoteId.Hex(), userId)
return true, "delete file success"
} }
return false, "delete file error" return false, "delete file error"
} }
@@ -150,7 +106,7 @@ func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string)
// 获取文件路径 // 获取文件路径
// 要判断是否具有权限 // 要判断是否具有权限
// userId是否具有attach的访问权限 // userId是否具有attach的访问权限
func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attach) { func (this *AttachService) GetAttach(attachId, userId, token, sessionId string) (attach info.Attach) {
if attachId == "" { if attachId == "" {
return return
} }
@@ -181,6 +137,17 @@ func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attac
return return
} }
//userId为空则是分享笔记的附件
if userId == "" && sessionId != "" {
if token != "" {
realToken := sessionService.GetToken(sessionId)
if token == realToken {
Log("attach token is equal!")
return
}
}
}
attach = info.Attach{} attach = info.Attach{}
return return
} }
@@ -202,46 +169,19 @@ func (this *AttachService) CopyAttachs(noteId, toNoteId, toUserId string) bool {
newFilename := NewGuid() + ext newFilename := NewGuid() + ext
dir := "files/" + toUserId + "/attachs" dir := "files/" + toUserId + "/attachs"
filePath := dir + "/" + newFilename filePath := dir + "/" + newFilename
err := os.MkdirAll(revel.BasePath+"/"+dir, 0755) err := os.MkdirAll(revel.BasePath + "/" + dir, 0755)
if err != nil { if err != nil {
return false return false
} }
_, err = CopyFile(revel.BasePath+"/"+attach.Path, revel.BasePath+"/"+filePath) _, err = CopyFile(revel.BasePath + "/" + attach.Path, revel.BasePath + "/" + filePath)
if err != nil { if err != nil {
return false return false
} }
attach.Name = newFilename attach.Name = newFilename
attach.Path = filePath attach.Path = filePath
this.AddAttach(attach, false) this.AddAttach(attach)
} }
return true return true
} }
// 只留下files的数据, 其它的都删除
func (this *AttachService) UpdateOrDeleteAttachApi(noteId, userId string, files []info.NoteFile) bool {
// 现在数据库内的
attachs := this.ListAttachs(noteId, userId)
nowAttachs := map[string]bool{}
if files != nil {
for _, file := range files {
if file.IsAttach && file.FileId != "" {
nowAttachs[file.FileId] = true
}
}
}
for _, attach := range attachs {
fileId := attach.AttachId.Hex()
if !nowAttachs[fileId] {
// 需要删除的
// TODO 权限验证去掉
this.DeleteAttach(fileId, userId)
}
}
return false
}
+31 -41
View File
@@ -2,31 +2,26 @@ 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"
"errors"
"fmt"
. "github.com/leanote/leanote/app/lea"
"strconv"
"strings" "strings"
. "github.com/leanote/leanote/app/lea"
"fmt"
"strconv"
) )
// 登录与权限 Login & Register // 登录与权限
type AuthService struct { type AuthService struct {
} }
// 使用bcrypt认证或者Md5认证 // pwd已md5了
// Use bcrypt (Md5 depreciated) func (this *AuthService) Login(emailOrUsername, pwd string) info.User {
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.GetUserInfoByName(emailOrUsername) userInfo := userService.LoginGetUserInfo(emailOrUsername, Md5(pwd))
if userInfo.UserId == "" || !ComparePwd(pwd, userInfo.Pwd) { return userInfo
return userInfo, errors.New("wrong username or password")
}
return userInfo, nil
} }
// 注册 // 注册
@@ -45,35 +40,33 @@ 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
} }
passwd := GenPwd(pwd) user := info.User{UserId: bson.NewObjectId(), Email: email, Username: email, Pwd: Md5(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()
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);
} }
email := user.Email
// 添加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")
@@ -81,34 +74,31 @@ 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.AddShareNotebook(notebook["notebookId"], perm, registerSharedUserId, email);
} }
// 添加共享笔记 // 添加共享笔记
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.AddShareNote(note["noteId"], perm, registerSharedUserId, email);
} }
// 复制笔记 // 复制笔记
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) noteUpdate := bson.M{"IsBlog": true}
// Log("Copy") noteService.UpdateNote(user.UserId.Hex(), user.UserId.Hex(), note.NoteId.Hex(), noteUpdate)
// LogJ(note)
noteUpdate := bson.M{"IsBlog": false} // 不要是博客
noteService.UpdateNote(user.UserId.Hex(), note.NoteId.Hex(), noteUpdate, -1)
} }
} }
//--------------- //---------------
// 添加一条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 (^_^)")
} }
@@ -123,7 +113,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
} }
@@ -140,10 +130,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
} }
+24 -44
View File
@@ -45,9 +45,7 @@ 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), db.GetByQ(db.Notes, bson.M{"UserId": bson.ObjectIdHex(userId), "UrlTitle": encodeValue(noteIdOrUrlTitle), "IsBlog": true, "IsTrash": false}, &note)
"IsBlog": true,
"IsTrash": false, "IsDeleted": false}, &note)
return this.GetBlogItem(note) return this.GetBlogItem(note)
} }
@@ -58,7 +56,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 info.BlogItem{} return
} }
// 内容 // 内容
@@ -71,14 +69,9 @@ func (this *BlogService) GetBlogItem(note info.Note) (blog info.BlogItem) {
} }
// 得到用户共享的notebooks // 得到用户共享的notebooks
// 3/19 博客不是deleted
func (this *BlogService) ListBlogNotebooks(userId string) []info.Notebook { func (this *BlogService) ListBlogNotebooks(userId string) []info.Notebook {
notebooks := []info.Notebook{} notebooks := []info.Notebook{}
orQ := []bson.M{ db.ListByQ(db.Notebooks, bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true}, &notebooks)
bson.M{"IsDeleted": false},
bson.M{"IsDeleted": bson.M{"$exists": false}},
}
db.ListByQ(db.Notebooks, bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "$or": orQ}, &notebooks)
return notebooks return notebooks
} }
@@ -133,8 +126,7 @@ 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{}
// tag不能为空 query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true}
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
} }
@@ -145,7 +137,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, "IsDeleted": false, "IsBlog": true} query := bson.M{"UserId": userIdO, "IsTrash": 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})
@@ -187,7 +179,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, "IsDeleted": false} q := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "IsTrash": false}
if notebookId != "" { if notebookId != "" {
q["NotebookId"] = bson.ObjectIdHex(notebookId) q["NotebookId"] = bson.ObjectIdHex(notebookId)
} }
@@ -294,10 +286,9 @@ 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,
"IsDeleted": false, "IsBlog": true,
"IsBlog": true, "Tags": bson.M{"$all": tags}}
"Tags": bson.M{"$all": tags}}
q := db.Notes.Find(query) q := db.Notes.Find(query)
@@ -370,10 +361,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都一样
@@ -397,14 +388,12 @@ 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,
"IsDeleted": false, "IsBlog": true,
"IsBlog": true, "_id": bson.M{"$ne": bson.ObjectIdHex(noteId)},
"_id": bson.M{"$ne": bson.ObjectIdHex(noteId)},
sorterField: sortFieldT1, sorterField: sortFieldT1,
} }
q := db.Notes.Find(query) q := db.Notes.Find(query)
@@ -436,7 +425,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, "IsDeleted": false, "IsBlog": true, "Title": bson.M{"$ne": "欢迎来到leanote!"}} query := bson.M{"IsTrash": 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}}
} }
@@ -502,9 +491,6 @@ 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)
@@ -1108,15 +1094,13 @@ func (this *BlogService) SortSingles(userId string, singleIds []string) (ok bool
// 得到用户的博客url // 得到用户的博客url
func (this *BlogService) GetUserBlogUrl(userBlog *info.UserBlog, username string) string { func (this *BlogService) GetUserBlogUrl(userBlog *info.UserBlog, username string) string {
if userBlog != nil { if userBlog.Domain != "" && configService.AllowCustomDomain() {
if userBlog.Domain != "" && configService.AllowCustomDomain() { return configService.GetUserUrl(userBlog.Domain)
return configService.GetUserUrl(userBlog.Domain) } else if userBlog.SubDomain != "" {
} else if userBlog.SubDomain != "" { return configService.GetUserSubUrl(userBlog.SubDomain)
return configService.GetUserSubUrl(userBlog.SubDomain) }
} if username == "" {
if username == "" { username = userBlog.UserId.Hex()
username = userBlog.UserId.Hex()
}
} }
return configService.GetBlogUrl() + "/" + username return configService.GetBlogUrl() + "/" + username
} }
@@ -1206,10 +1190,6 @@ 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
} }
+4 -18
View File
@@ -305,7 +305,7 @@ func (this *ConfigService) Backup(remark string) (ok bool, msg string) {
username, _ := revel.Config.String("db.username") username, _ := revel.Config.String("db.username")
password, _ := revel.Config.String("db.password") password, _ := revel.Config.String("db.password")
// mongodump -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM // mongodump -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
binPath = binPath + " -h " + host + " -d " + dbname + " --port " + port binPath = binPath + " -h " + host + " -d " + dbname + " -port " + port
if username != "" { if username != "" {
binPath += " -u " + username + " -p " + password binPath += " -u " + username + " -p " + password
} }
@@ -361,7 +361,7 @@ func (this *ConfigService) Restore(createdTime string) (ok bool, msg string) {
username, _ := revel.Config.String("db.username") username, _ := revel.Config.String("db.username")
password, _ := revel.Config.String("db.password") password, _ := revel.Config.String("db.password")
// mongorestore -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM // mongorestore -h localhost -d leanote -o /root/mongodb_backup/leanote-9-22/ -u leanote -p nKFAkxKnWkEQy8Vv2LlM
binPath = binPath + " --drop -h " + host + " -d " + dbname + " --port " + port binPath = binPath + " --drop -h " + host + " -d " + dbname + " -port " + port
if username != "" { if username != "" {
binPath += " -u " + username + " -p " + password binPath += " -u " + username + " -p " + password
} }
@@ -372,7 +372,7 @@ func (this *ConfigService) Restore(createdTime string) (ok bool, msg string) {
return false, path + " Is Not Exists" return false, path + " Is Not Exists"
} }
binPath += " " + path binPath += " --directoryperdb " + path
cmd := exec.Command("/bin/sh", "-c", binPath) cmd := exec.Command("/bin/sh", "-c", binPath)
Log(binPath); Log(binPath);
@@ -455,15 +455,12 @@ 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://") {
@@ -472,17 +469,6 @@ 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
}
}) })
} }
@@ -594,5 +580,5 @@ func (this *ConfigService) HomePageIsAdminsBlog() bool {
} }
func (this *ConfigService) GetVersion() string { func (this *ConfigService) GetVersion() string {
return "1.3.1" return "1.0-beta2"
} }
+28 -61
View File
@@ -1,18 +1,15 @@
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"
"io/ioutil" "time"
"net/http"
"os" "os"
"strings" "strings"
"time" // "fmt"
) )
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001" const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
@@ -119,57 +116,11 @@ 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的访问权限
func (this *FileService) GetFile(userId, fileId string) string {
func (this *FileService) GetFile(userId, fileId , sessionId , token string) string {
if fileId == "" { if fileId == "" {
return "" return ""
} }
@@ -182,7 +133,8 @@ func (this *FileService) GetFile(userId, fileId string) string {
} }
// 1. 判断权限 // 1. 判断权限
//未登录用户,判断token
noteIds := noteImageService.GetNoteIds(fileId)
// 是否是我的文件 // 是否是我的文件
if userId != "" && file.UserId.Hex() == userId { if userId != "" && file.UserId.Hex() == userId {
return path return path
@@ -192,13 +144,30 @@ func (this *FileService) GetFile(userId, fileId string) string {
// 这些笔记是否有public的, 若有则ok // 这些笔记是否有public的, 若有则ok
// 这些笔记(笔记本)是否有共享给我的, 若有则ok // 这些笔记(笔记本)是否有共享给我的, 若有则ok
noteIds := noteImageService.GetNoteIds(fileId)
if noteIds != nil && len(noteIds) > 0 { if noteIds != nil && len(noteIds) > 0 {
// 这些笔记是否有public的 // 这些笔记是否有public的
if db.Has(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}, "IsBlog": true}) { if db.Has(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}, "IsBlog": true}) {
return path return path
} }
//分享给未注册用户
if userId == "" && sessionId != "" {
if token != "" {
realToken := sessionService.GetToken(sessionId)
if token == realToken {
Log("image token is equal!")
return path
} else {
Log("image token is different!")
return ""
}
} else {
return ""
}
}
// 2014/12/28 修复, 如果是分享给用户组, 那就不行, 这里可以实现 // 2014/12/28 修复, 如果是分享给用户组, 那就不行, 这里可以实现
for _, noteId := range noteIds { for _, noteId := range noteIds {
note := noteService.GetNoteById(noteId.Hex()) note := noteService.GetNoteById(noteId.Hex())
@@ -206,6 +175,7 @@ func (this *FileService) GetFile(userId, fileId string) string {
return path; return path;
} }
} }
/* /*
// 若有共享给我的笔记? // 若有共享给我的笔记?
// 对该笔记可读? // 对该笔记可读?
@@ -242,6 +212,7 @@ func (this *FileService) GetFile(userId, fileId string) string {
return "" return ""
} }
// 复制图片 // 复制图片
func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, string) { func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, string) {
// 是否已经复制过了 // 是否已经复制过了
@@ -294,9 +265,5 @@ func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, strin
// 是否是我的文件 // 是否是我的文件
func (this *FileService) IsMyFile(userId, fileId string) bool { func (this *FileService) IsMyFile(userId, fileId string) bool {
// 如果有问题会panic
if !bson.IsObjectIdHex(fileId) || !bson.IsObjectIdHex(userId) {
return false;
}
return db.Has(db.Files, bson.M{"UserId": bson.ObjectIdHex(userId), "_id": bson.ObjectIdHex(fileId)}) return db.Has(db.Files, bson.M{"UserId": bson.ObjectIdHex(userId), "_id": bson.ObjectIdHex(fileId)})
} }
+1 -91
View File
@@ -32,21 +32,11 @@ 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)
@@ -54,14 +44,9 @@ 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())
@@ -76,55 +61,6 @@ func (this *GroupService) GetGroups(userId string) ([]info.Group) {
db.ListByQ(db.Groups, bson.M{"UserId": bson.ObjectIdHex(userId)}, &groups) db.ListByQ(db.Groups, bson.M{"UserId": bson.ObjectIdHex(userId)}, &groups)
return groups return groups
} }
// 得到我的和我所属组的ids
func (this *GroupService) GetMineAndBelongToGroupIds(userId string) ([]bson.ObjectId) {
// 所属组
groupIds := this.GetBelongToGroupIds(userId)
m := map[bson.ObjectId]bool{}
for _, groupId := range groupIds {
m[groupId] = true
}
// 我的组
myGroups := this.GetGroups(userId)
for _, group := range myGroups {
if !m[group.GroupId] {
groupIds = append(groupIds, group.GroupId)
}
}
return groupIds
}
// 获取包含此用户的组对象数组
// 获取该用户所属组, 和我的组
func (this *GroupService) GetGroupsContainOf(userId string) []info.Group {
// 我的组
myGroups := this.GetGroups(userId)
myGroupMap := map[bson.ObjectId]bool{}
for _, group := range myGroups {
myGroupMap[group.GroupId] = true
}
// 所属组
groupIds := this.GetBelongToGroupIds(userId)
groups := []info.Group{}
db.ListByQ(db.Groups, bson.M{"_id": bson.M{"$in": groupIds}}, &groups)
for _, group := range groups {
if !myGroupMap[group.GroupId] {
myGroups = append(myGroups, group)
}
}
return myGroups
}
// 得到分组, shareService用 // 得到分组, shareService用
func (this *GroupService) GetGroup(userId, groupId string) (info.Group) { func (this *GroupService) GetGroup(userId, groupId string) (info.Group) {
// 得到分组s // 得到分组s
@@ -167,25 +103,10 @@ 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"
} }
@@ -202,22 +123,11 @@ 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)}), ""
} }
+18 -85
View File
@@ -29,7 +29,6 @@ func (this *NoteImageService) GetNoteIds(imageId string) ([]bson.ObjectId) {
return nil return nil
} }
// TODO 这个web可以用, 但api会传来, 不用用了
// 解析内容中的图片, 建立图片与note的关系 // 解析内容中的图片, 建立图片与note的关系
// <img src="/file/outputImage?fileId=12323232" /> // <img src="/file/outputImage?fileId=12323232" />
// 图片必须是我的, 不然不添加 // 图片必须是我的, 不然不添加
@@ -39,8 +38,7 @@ func (this *NoteImageService) UpdateNoteImages(userId, noteId, imgSrc, content s
if imgSrc != "" { if imgSrc != "" {
content = "<img src=\"" + imgSrc + "\" >" + content content = "<img src=\"" + imgSrc + "\" >" + content
} }
// life 添加getImage reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})")
reg, _ := regexp.Compile("(outputImage|getImage)\\?fileId=([a-z0-9A-Z]{24})")
find := reg.FindAllStringSubmatch(content, -1) // 查找所有的 find := reg.FindAllStringSubmatch(content, -1) // 查找所有的
// 删除旧的 // 删除旧的
@@ -52,8 +50,8 @@ func (this *NoteImageService) UpdateNoteImages(userId, noteId, imgSrc, content s
hasAdded := make(map[string]bool) hasAdded := make(map[string]bool)
if find != nil && len(find) > 0 { if find != nil && len(find) > 0 {
for _, each := range find { for _, each := range find {
if each != nil && len(each) == 3 { if each != nil && len(each) == 2 {
fileId = each[2] // 现在有两个子表达式了 fileId = each[1]
// 之前没能添加过的 // 之前没能添加过的
if _, ok := hasAdded[fileId]; !ok { if _, ok := hasAdded[fileId]; !ok {
Log(fileId) Log(fileId)
@@ -73,13 +71,17 @@ 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)
@@ -87,88 +89,19 @@ func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId,
replaceMap[imageId] = newImageId replaceMap[imageId] = newImageId
} }
} }
*/
// 因为很多图片上传就会删除, 所以直接从内容中查看图片id进行复制 if len(replaceMap) > 0 {
// 替换之
// <img src="/file/outputImage?fileId=12323232" /> reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})")
// 把fileId=1232替换成新的 content = reg.ReplaceAllStringFunc(content, func(each string) string {
replaceMap := map[string]string{} // each=outputImage?fileId=541bd2f599c37b4f3r000003
fileId := each[len(each)-24:] // 得到后24位, 也即id
reg, _ := regexp.Compile("(outputImage|getImage)\\?fileId=([a-z0-9A-Z]{24})") if replaceFileId, ok := replaceMap[fileId]; ok {
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 "getImage?fileId=" + replaceFileId return each
} });
return each }
});
return content; return content;
} }
//
func (this *NoteImageService) getImagesByNoteIds(noteIds []bson.ObjectId) map[string][]info.File {
noteNoteImages := []info.NoteImage{}
db.ListByQ(db.NoteImages, bson.M{"NoteId": bson.M{"$in": noteIds}}, &noteNoteImages)
// 得到imageId, 再去files表查所有的Files
imageIds := []bson.ObjectId{}
// 图片1 => N notes
imageIdNotes := map[string][]string{} // imageId => [noteId1, noteId2, ...]
for _, noteImage := range noteNoteImages {
imageId := noteImage.ImageId
imageIds = append(imageIds, imageId)
imageIdHex := imageId.Hex()
noteId := noteImage.NoteId.Hex()
if notes, ok := imageIdNotes[imageIdHex]; ok {
imageIdNotes[imageIdHex] = append(notes, noteId)
} else {
imageIdNotes[imageIdHex] = []string{noteId}
}
}
// 得到所有files
files := []info.File{}
db.ListByQ(db.Files, bson.M{"_id": bson.M{"$in": imageIds}}, &files)
// 建立note->file关联
noteImages := make(map[string][]info.File)
for _, file := range files {
fileIdHex := file.FileId.Hex() // == imageId
// 这个fileIdHex有哪些notes呢?
if notes, ok := imageIdNotes[fileIdHex]; ok {
for _, noteId := range notes {
if files, ok2 := noteImages[noteId]; ok2 {
noteImages[noteId] = append(files, file)
} else {
noteImages[noteId] = []info.File{file}
}
}
}
}
return noteImages
}
+42 -337
View File
@@ -17,30 +17,28 @@ func (this *NoteService) GetNote(noteId, userId string) (note info.Note) {
db.GetByIdAndUserId(db.Notes, noteId, userId, &note) db.GetByIdAndUserId(db.Notes, noteId, userId, &note)
return return
} }
// fileService调用
// 不能是已经删除了的, life bug, 客户端删除后, 竟然还能在web上打开 //通过附件id得到note
func (this *NoteService) GetNoteById(noteId string) (note info.Note) { func (this *NoteService) GetNoteByAttachId(attachId string) (note info.Note) {
attach := info.Attach{}
db.Get(db.Attachs, attachId, &attach)
note = info.Note{} note = info.Note{}
if noteId == "" { noteId := attach.NoteId
return db.Get(db.Notes, noteId.Hex(), &note)
}
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsDeleted": false}, &note)
return return
} }
func (this *NoteService) GetNoteByIdAndUserId(noteId, userId string) (note info.Note) {
// fileService调用
func (this *NoteService) GetNoteById(noteId string) (note info.Note) {
note = info.Note{} note = info.Note{}
if noteId == "" || userId == "" { db.Get(db.Notes, noteId, &note)
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), db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsBlog": true, "IsTrash": false}, &note)
"IsBlog": true, "IsTrash": false, "IsDeleted": false}, &note)
return return
} }
// 通过id, userId得到noteContent // 通过id, userId得到noteContent
@@ -57,115 +55,14 @@ func (this *NoteService) GetNoteAndContent(noteId, userId string) (noteAndConten
return info.NoteAndContent{note, noteContent} return info.NoteAndContent{note, noteContent}
} }
// 获取同步的笔记
// > afterUsn的笔记
func (this *NoteService) GetSyncNotes(userId string, afterUsn, maxEntry int) []info.ApiNote {
notes := []info.Note{}
q := db.Notes.Find(bson.M{
"UserId": bson.ObjectIdHex(userId),
"Usn": bson.M{"$gt": afterUsn},
});
q.Sort("Usn").Limit(maxEntry).All(&notes)
return this.ToApiNotes(notes)
}
// note与apiNote的转换
func (this *NoteService) ToApiNotes(notes []info.Note) []info.ApiNote {
// 2, 得到所有图片, 附件信息
// 查images表, attachs表
if len(notes) > 0 {
noteIds := make([]bson.ObjectId, len(notes));
for i, note := range notes {
noteIds[i] = note.NoteId
}
noteFilesMap := this.getFiles(noteIds)
// 生成info.ApiNote
apiNotes := make([]info.ApiNote, len(notes))
for i, note := range notes {
noteId := note.NoteId.Hex()
apiNotes[i] = this.ToApiNote(&note, noteFilesMap[noteId])
}
return apiNotes
}
// 返回空的
return []info.ApiNote{}
}
// note与apiNote的转换
func (this *NoteService) ToApiNote(note *info.Note, files []info.NoteFile) info.ApiNote {
apiNote := info.ApiNote{
NoteId: note.NoteId.Hex(),
NotebookId: note.NotebookId.Hex(),
UserId : note.UserId.Hex(),
Title : note.Title,
Tags : note.Tags,
IsMarkdown : note.IsMarkdown,
IsBlog : note.IsBlog,
IsTrash : note.IsTrash,
IsDeleted : note.IsDeleted,
Usn : note.Usn,
CreatedTime : note.CreatedTime,
UpdatedTime : note.UpdatedTime,
PublicTime : note.PublicTime,
Files: files,
}
return apiNote
}
// getDirtyNotes, 把note的图片, 附件信息都发送给客户端
// 客户端保存到本地, 再获取图片, 附件
// 得到所有图片, 附件信息
// 查images表, attachs表
// [待测]
func (this *NoteService) getFiles(noteIds []bson.ObjectId) map[string][]info.NoteFile {
noteImages := noteImageService.getImagesByNoteIds(noteIds);
noteAttachs := attachService.getAttachsByNoteIds(noteIds)
noteFilesMap := map[string][]info.NoteFile{}
for _, noteId := range noteIds {
noteIdHex := noteId.Hex()
noteFiles := []info.NoteFile{}
// images
if images, ok := noteImages[noteIdHex]; ok {
for _, image := range images {
noteFiles = append(noteFiles, info.NoteFile{
FileId: image.FileId.Hex(),
Type: image.Type,
});
}
}
// attach
if attachs, ok := noteAttachs[noteIdHex]; ok {
for _, attach := range attachs {
noteFiles = append(noteFiles, info.NoteFile{
FileId: attach.AttachId.Hex(),
Type: attach.Type,
Title: attach.Title,
IsAttach: true,
});
}
}
noteFilesMap[noteIdHex] = noteFiles
}
return noteFilesMap
}
// 列出note, 排序规则, 还有分页 // 列出note, 排序规则, 还有分页
// CreatedTime, UpdatedTime, title 来排序 // CreatedTime, UpdatedTime, title 来排序
func (this *NoteService) ListNotes(userId, notebookId string, func (this *NoteService) ListNotes(userId, notebookId string,
isTrash bool, pageNumber, pageSize int, sortField string, isAsc bool, isBlog bool) (count int, notes []info.Note) { isTrash bool, pageNumber, pageSize int, sortField string, isAsc bool, isBlog bool) (count int, notes []info.Note) {
notes = []info.Note{} notes = []info.Note{}
skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, sortField, isAsc) skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, sortField, isAsc)
// 不是trash的 // 不是trash的
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsTrash": isTrash, "IsDeleted": false} query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsTrash": isTrash}
if isBlog { if isBlog {
query["IsBlog"] = true query["IsBlog"] = true
} }
@@ -235,7 +132,7 @@ func (this *NoteService) ListNoteContentByNoteIds(noteIds []bson.ObjectId) (note
// 首先要判断Notebook是否是Blog, 是的话设为blog // 首先要判断Notebook是否是Blog, 是的话设为blog
// [ok] // [ok]
func (this *NoteService) AddNote(note info.Note, fromApi bool) info.Note { func (this *NoteService) AddNote(note info.Note) info.Note {
if(note.NoteId.Hex() == "") { if(note.NoteId.Hex() == "") {
noteId := bson.NewObjectId(); noteId := bson.NewObjectId();
note.NoteId = noteId; note.NoteId = noteId;
@@ -245,18 +142,14 @@ func (this *NoteService) AddNote(note info.Note, fromApi bool) info.Note {
note.IsTrash = false note.IsTrash = false
note.UpdatedUserId = note.UserId note.UpdatedUserId = note.UserId
note.UrlTitle = GetUrTitle(note.UserId.Hex(), note.Title, "note") note.UrlTitle = GetUrTitle(note.UserId.Hex(), note.Title, "note")
note.Usn = userService.IncrUsn(note.UserId.Hex())
// 设为blog
notebookId := note.NotebookId.Hex() notebookId := note.NotebookId.Hex()
note.IsBlog = notebookService.IsBlog(notebookId)
// api会传IsBlog, web不会传 if note.IsBlog {
if !fromApi { note.PublicTime = note.UpdatedTime
// 设为blog
note.IsBlog = notebookService.IsBlog(notebookId)
} }
// if note.IsBlog {
note.PublicTime = note.UpdatedTime
// }
db.Insert(db.Notes, note) db.Insert(db.Notes, note)
@@ -274,7 +167,7 @@ func (this *NoteService) AddSharedNote(note info.Note, myUserId bson.ObjectId) i
// 判断我是否有权限添加 // 判断我是否有权限添加
if shareService.HasUpdateNotebookPerm(note.UserId.Hex(), myUserId.Hex(), note.NotebookId.Hex()) { if shareService.HasUpdateNotebookPerm(note.UserId.Hex(), myUserId.Hex(), note.NotebookId.Hex()) {
note.CreatedUserId = myUserId // 是我给共享我的人创建的 note.CreatedUserId = myUserId // 是我给共享我的人创建的
return this.AddNote(note, false) return this.AddNote(note)
} }
return info.Note{} return info.Note{}
} }
@@ -294,49 +187,6 @@ func (this *NoteService) AddNoteContent(noteContent info.NoteContent) info.NoteC
return noteContent; return noteContent;
} }
// API, abstract, desc需要这里获取
// 不需要
/*
func (this *NoteService) AddNoteAndContentApi(note info.Note, noteContent info.NoteContent, myUserId bson.ObjectId) info.Note {
if(note.NoteId.Hex() == "") {
noteId := bson.NewObjectId();
note.NoteId = noteId;
}
note.CreatedTime = time.Now()
note.UpdatedTime = note.CreatedTime
note.IsTrash = false
note.UpdatedUserId = note.UserId
note.UrlTitle = GetUrTitle(note.UserId.Hex(), note.Title, "note")
note.Usn = userService.IncrUsn(note.UserId.Hex())
// desc这里获取
desc := SubStringHTMLToRaw(noteContent.Content, 50)
note.Desc = desc;
// 设为blog
notebookId := note.NotebookId.Hex()
note.IsBlog = notebookService.IsBlog(notebookId)
if note.IsBlog {
note.PublicTime = note.UpdatedTime
}
db.Insert(db.Notes, note)
// tag1, 不需要了
// tagService.AddTags(note.UserId.Hex(), note.Tags)
// recount notebooks' notes number
notebookService.ReCountNotebookNumberNotes(notebookId)
// 这里, 添加到内容中
abstract := SubStringHTML(noteContent.Content, 200, "")
noteContent.Abstract = abstract
this.AddNoteContent(noteContent)
return note
}*/
// 添加笔记和内容 // 添加笔记和内容
// 这里使用 info.NoteAndContent 接收? // 这里使用 info.NoteAndContent 接收?
func (this *NoteService) AddNoteAndContentForController(note info.Note, noteContent info.NoteContent, updatedUserId string) info.Note { func (this *NoteService) AddNoteAndContentForController(note info.Note, noteContent info.NoteContent, updatedUserId string) info.Note {
@@ -359,24 +209,7 @@ func (this *NoteService) AddNoteAndContent(note info.Note, noteContent info.Note
if note.UserId != myUserId { if note.UserId != myUserId {
note = this.AddSharedNote(note, myUserId) note = this.AddSharedNote(note, myUserId)
} else { } else {
note = this.AddNote(note, false) note = this.AddNote(note)
}
if note.NoteId != "" {
this.AddNoteContent(noteContent)
}
return note
}
func (this *NoteService) AddNoteAndContentApi(note info.Note, noteContent info.NoteContent, myUserId bson.ObjectId) info.Note {
if(note.NoteId.Hex() == "") {
noteId := bson.NewObjectId()
note.NoteId = noteId
}
noteContent.NoteId = note.NoteId
if note.UserId != myUserId {
note = this.AddSharedNote(note, myUserId)
} else {
note = this.AddNote(note, true)
} }
if note.NoteId != "" { if note.NoteId != "" {
this.AddNoteContent(noteContent) this.AddNoteContent(noteContent)
@@ -385,30 +218,19 @@ func (this *NoteService) AddNoteAndContentApi(note info.Note, noteContent info.N
} }
// 修改笔记 // 修改笔记
// 这里没有判断usn func (this *NoteService) UpdateNote(userId, updatedUserId, noteId string, needUpdate bson.M) bool {
func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bson.M, usn int) (bool, string, int) {
// 是否存在
note := this.GetNoteById(noteId)
if note.NoteId == "" {
return false, "notExists", 0
}
userId := note.UserId.Hex()
// updatedUserId 要修改userId的note, 此时需要判断是否有修改权限 // updatedUserId 要修改userId的note, 此时需要判断是否有修改权限
if userId != updatedUserId { if userId != updatedUserId {
if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) { if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) {
Log("NO AUTH2") Log("NO AUTH2")
return false, "noAuth", 0 return false
} else { } else {
Log("HAS AUTH -----------") Log("HAS AUTH -----------")
} }
} }
if usn > 0 && note.Usn != usn {
return false, "conflict", 0
}
// 是否已自定义 // 是否已自定义
note := this.GetNoteById(noteId)
if note.IsBlog && note.HasSelfDefined { if note.IsBlog && note.HasSelfDefined {
delete(needUpdate, "ImgSrc") delete(needUpdate, "ImgSrc")
delete(needUpdate, "Desc") delete(needUpdate, "Desc")
@@ -416,11 +238,8 @@ func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bso
needUpdate["UpdatedUserId"] = bson.ObjectIdHex(updatedUserId); needUpdate["UpdatedUserId"] = bson.ObjectIdHex(updatedUserId);
needUpdate["UpdatedTime"] = time.Now(); needUpdate["UpdatedTime"] = time.Now();
afterUsn := userService.IncrUsn(userId);
needUpdate["Usn"] = afterUsn
// 添加tag2 // 添加tag2
// TODO 这个tag去掉, 添加tag另外添加, 不要这个
if tags, ok := needUpdate["Tags"]; ok { if tags, ok := needUpdate["Tags"]; ok {
tagService.AddTagsI(userId, tags) tagService.AddTagsI(userId, tags)
} }
@@ -428,60 +247,10 @@ func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bso
// 是否修改了isBlog // 是否修改了isBlog
// 也要修改noteContents的IsBlog // 也要修改noteContents的IsBlog
if isBlog, ok := needUpdate["IsBlog"]; ok { if isBlog, ok := needUpdate["IsBlog"]; ok {
isBlog2 := isBlog.(bool) db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog})
if note.IsBlog != isBlog2 {
this.UpdateNoteContentIsBlog(noteId, userId, isBlog2);
// 重新发布成博客
if !note.IsBlog {
needUpdate["PublicTime"] = needUpdate["UpdatedTime"]
}
}
} }
ok := db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, needUpdate) return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, needUpdate)
if !ok {
return ok, "", 0
}
// 重新获取之
note = this.GetNoteById(noteId)
hasRecount := false
// 如果修改了notebookId, 则更新notebookId'count
// 两方的notebook也要修改
notebookIdI := needUpdate["NotebookId"]
if notebookIdI != nil {
notebookId := notebookIdI.(bson.ObjectId)
if notebookId != "" {
notebookService.ReCountNotebookNumberNotes(note.NotebookId.Hex())
hasRecount = true
notebookService.ReCountNotebookNumberNotes(notebookId.Hex())
}
}
// 不要多次更新, isTrash = false, = true都要重新统计
if !hasRecount {
if _, ok := needUpdate["IsTrash"]; ok {
notebookService.ReCountNotebookNumberNotes(note.NotebookId.Hex())
}
}
return true, "", afterUsn
}
// 当设置/取消了笔记为博客
func (this *NoteService) UpdateNoteContentIsBlog(noteId, userId string, isBlog bool) {
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog})
}
// 附件修改, 增加noteIncr
func (this *NoteService) IncrNoteUsn(noteId, userId string) int {
afterUsn := userService.IncrUsn(userId)
db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
bson.M{"UpdatedTime": time.Now(), "Usn": afterUsn})
return afterUsn
} }
// 这里要判断权限, 如果userId != updatedUserId, 那么需要判断权限 // 这里要判断权限, 如果userId != updatedUserId, 那么需要判断权限
@@ -496,50 +265,31 @@ func (this *NoteService) UpdateNoteTitle(userId, updatedUserId, noteId, title st
} }
return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId), "Title": title, "UpdatedTime": time.Now(), "Usn": userService.IncrUsn(userId)}) bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId), "Title": title, "UpdatedTime": time.Now()})
} }
// 修改笔记本内容 // 修改笔记本内容
// [ok] TODO perm未测 // [ok] TODO perm未测
// hasBeforeUpdateNote 之前是否更新过note其它信息, 如果有更新, usn不用更新 func (this *NoteService) UpdateNoteContent(userId, updatedUserId, noteId, content, abstract string) bool {
// TODO abstract这里生成
func (this *NoteService) UpdateNoteContent(updatedUserId, noteId, content, abstract string, hasBeforeUpdateNote bool, usn int) (bool, string, int) {
// 是否已自定义
note := this.GetNoteById(noteId)
if note.NoteId == "" {
return false, "notExists", 0
}
userId := note.UserId.Hex()
// updatedUserId 要修改userId的note, 此时需要判断是否有修改权限 // updatedUserId 要修改userId的note, 此时需要判断是否有修改权限
if userId != updatedUserId { if userId != updatedUserId {
if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) { if !shareService.HasUpdatePerm(userId, updatedUserId, noteId) {
Log("NO AUTH") Log("NO AUTH")
return false, "noAuth", 0 return false
} }
} }
// abstract重置
data := bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId), data := bson.M{"UpdatedUserId": bson.ObjectIdHex(updatedUserId),
"Content": content, "Content": content,
"Abstract": abstract, "Abstract": abstract,
"UpdatedTime": time.Now()} "UpdatedTime": time.Now()}
// 是否已自定义
note := this.GetNoteById(noteId)
if note.IsBlog && note.HasSelfDefined { if note.IsBlog && note.HasSelfDefined {
delete(data, "Abstract") delete(data, "Abstract")
} }
// usn, 修改笔记不可能单独修改内容
afterUsn := 0
// 如果之前没有修改note其它信息, 那么usn++
if !hasBeforeUpdateNote {
// 需要验证
if usn >= 0 && note.Usn != usn {
return false, "conflict", 0
}
afterUsn = userService.IncrUsn(userId)
db.UpdateByIdAndUserIdField(db.Notes, noteId, userId, "Usn", usn)
}
if db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, data) { if db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, data) {
// 这里, 添加历史记录 // 这里, 添加历史记录
noteContentHistoryService.AddHistory(noteId, userId, info.EachHistory{UpdatedUserId: bson.ObjectIdHex(updatedUserId), noteContentHistoryService.AddHistory(noteId, userId, info.EachHistory{UpdatedUserId: bson.ObjectIdHex(updatedUserId),
@@ -550,9 +300,9 @@ func (this *NoteService) UpdateNoteContent(updatedUserId, noteId, content, abstr
// 更新笔记图片 // 更新笔记图片
noteImageService.UpdateNoteImages(userId, noteId, note.ImgSrc, content) noteImageService.UpdateNoteImages(userId, noteId, note.ImgSrc, content)
return true, "", afterUsn return true
} }
return false, "", 0 return false
} }
// ????? // ?????
@@ -566,7 +316,7 @@ func (this *NoteService) updateNoteImages(noteId string, content string) bool {
// 更新tags // 更新tags
// [ok] [del] // [ok] [del]
func (this *NoteService) UpdateTags(noteId string, userId string, tags []string) bool { func (this *NoteService) UpdateTags(noteId string, userId string, tags []string) bool {
return db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, bson.M{"Tags": tags, "Usn": userService.IncrUsn(userId)}) return db.UpdateByIdAndUserIdField(db.Notes, noteId, userId, "Tags", tags)
} }
func (this *NoteService) ToBlog(userId, noteId string, isBlog, isTop bool) bool { func (this *NoteService) ToBlog(userId, noteId string, isBlog, isTop bool) bool {
@@ -584,13 +334,9 @@ func (this *NoteService) ToBlog(userId, noteId string, isBlog, isTop bool) bool
} else { } else {
noteUpdate["HasSelfDefined"] = false noteUpdate["HasSelfDefined"] = false
} }
noteUpdate["Usn"] = userService.IncrUsn(userId)
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
@@ -607,9 +353,7 @@ func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
re := db.UpdateByIdAndUserId(db.Notes, noteId, userId, re := db.UpdateByIdAndUserId(db.Notes, noteId, userId,
bson.M{"$set": bson.M{"IsTrash": false, bson.M{"$set": bson.M{"IsTrash": false,
"NotebookId": bson.ObjectIdHex(notebookId), "NotebookId": bson.ObjectIdHex(notebookId)}})
"Usn": userService.IncrUsn(userId),
}})
if re { if re {
// 更新blog状态 // 更新blog状态
@@ -631,14 +375,13 @@ func (this *NoteService) MoveNote(noteId, notebookId, userId string) info.Note {
// 如果自己的blog状态是true, 不用改变, // 如果自己的blog状态是true, 不用改变,
// 否则, 如果notebookId的blog是true, 则改为true之 // 否则, 如果notebookId的blog是true, 则改为true之
// 返回blog状态 // 返回blog状态
// move, copy时用
func (this *NoteService) updateToNotebookBlog(noteId, notebookId, userId string) bool { func (this *NoteService) updateToNotebookBlog(noteId, notebookId, userId string) bool {
if this.IsBlog(noteId) { if this.IsBlog(noteId) {
return true return true
} }
if notebookService.IsBlog(notebookId) { if notebookService.IsBlog(notebookId) {
db.UpdateByIdAndUserId(db.Notes, noteId, userId, db.UpdateByIdAndUserId(db.Notes, noteId, userId,
bson.M{"$set": bson.M{"IsBlog": true, "PublicTime": time.Now()}}) // life bson.M{"$set": bson.M{"IsBlog": true}})
return true return true
} }
return false return false
@@ -664,7 +407,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
note = this.AddNoteAndContent(note, noteContent, note.UserId); this.AddNoteAndContent(note, noteContent, note.UserId);
// 更新blog状态 // 更新blog状态
isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId) isBlog := this.updateToNotebookBlog(note.NoteId.Hex(), notebookId, userId)
@@ -683,9 +426,10 @@ 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))
// 判断是否共享了给我 // 判断是否共享了给我
// Log(notebookService.IsMyNotebook(notebookId, myUserId)) if notebookService.IsMyNotebook(notebookId, myUserId) &&
if notebookService.IsMyNotebook(notebookId, myUserId) && shareService.HasReadPerm(fromUserId, myUserId, noteId) { (shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) {
note := this.GetNote(noteId, fromUserId) note := this.GetNote(noteId, fromUserId)
if note.NoteId == "" { if note.NoteId == "" {
return info.Note{} return info.Note{}
@@ -752,7 +496,6 @@ 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 {
@@ -835,58 +578,20 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb
return return
} }
//------------ //------------
// 统计 // 统计
func (this *NoteService) CountNote(userId string) int { func (this *NoteService) CountNote(userId string) int {
q := bson.M{"IsTrash": false, "IsDeleted": false} q := bson.M{"IsTrash": 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, "IsDeleted": false} q := bson.M{"IsBlog": true, "IsTrash": 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) CountNoteByTag(userId string, tag string) int {
if tag == "" {
return 0
}
query := bson.M{"UserId": bson.ObjectIdHex(userId),
// "IsTrash": false,
"IsDeleted": false,
"Tags": bson.M{"$in": []string{tag}}}
return db.Count(db.Notes, query)
}
// 删除tag
// 返回所有note的Usn
func (this *NoteService) UpdateNoteToDeleteTag(userId string, targetTag string) map[string]int {
query := bson.M{"UserId": bson.ObjectIdHex(userId),
"Tags": bson.M{"$in": []string{targetTag}}}
notes := []info.Note{}
db.ListByQ(db.Notes, query, &notes)
ret := map[string]int{}
for _, note := range notes {
tags := note.Tags
if tags == nil {
continue
}
for i, tag := range tags {
if tag == targetTag {
tags = tags
tags = append(tags[:i], tags[i+1:]...)
break;
}
}
usn := userService.IncrUsn(userId)
db.UpdateByQMap(db.Notes, bson.M{"_id": note.NoteId}, bson.M{"Usn": usn, "Tags": tags})
ret[note.NoteId.Hex()] = usn
}
return ret
}
+18 -89
View File
@@ -8,8 +8,6 @@ import (
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"sort" "sort"
"time" "time"
"strings"
// "html"
) )
// 笔记本 // 笔记本
@@ -42,9 +40,7 @@ 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
@@ -115,24 +111,12 @@ func (this *NotebookService) GetNotebookByUserIdAndUrlTitle(userId, notebookIdOr
return notebook return notebook
} }
// 同步的方法
func (this *NotebookService) GeSyncNotebooks(userId string, afterUsn, maxEntry int) ([]info.Notebook) {
notebooks := []info.Notebook{}
q := db.Notebooks.Find(bson.M{"UserId": bson.ObjectIdHex(userId), "Usn": bson.M{"$gt": afterUsn}});
q.Sort("Usn").Limit(maxEntry).All(&notebooks)
return notebooks
}
// 得到用户下所有的notebook // 得到用户下所有的notebook
// 排序好之后返回 // 排序好之后返回
// [ok] // [ok]
func (this *NotebookService) GetNotebooks(userId string) info.SubNotebooks { func (this *NotebookService) GetNotebooks(userId string) info.SubNotebooks {
userNotebooks := []info.Notebook{} userNotebooks := []info.Notebook{}
orQ := []bson.M{ db.Notebooks.Find(bson.M{"UserId": bson.ObjectIdHex(userId)}).All(&userNotebooks)
bson.M{"IsDeleted": false},
bson.M{"IsDeleted": bson.M{"$exists": false}},
}
db.Notebooks.Find(bson.M{"UserId": bson.ObjectIdHex(userId), "$or": orQ}).All(&userNotebooks)
if len(userNotebooks) == 0 { if len(userNotebooks) == 0 {
return nil return nil
@@ -157,46 +141,14 @@ func (this *NotebookService) GetNotebooksByNotebookIds(notebookIds []bson.Object
// 添加 // 添加
// [ok] // [ok]
func (this *NotebookService) AddNotebook(notebook info.Notebook) (bool, info.Notebook) { func (this *NotebookService) AddNotebook(notebook info.Notebook) bool {
notebook.UrlTitle = GetUrTitle(notebook.UserId.Hex(), notebook.Title, "notebook") notebook.UrlTitle = GetUrTitle(notebook.UserId.Hex(), notebook.Title, "notebook")
notebook.Usn = userService.IncrUsn(notebook.UserId.Hex())
now := time.Now()
notebook.CreatedTime = now
notebook.UpdatedTime = now
err := db.Notebooks.Insert(notebook) err := db.Notebooks.Insert(notebook)
if err != nil { if err != nil {
return false, notebook panic(err)
}
return true, notebook
}
// 更新笔记, api
func (this *NotebookService) UpdateNotebookApi(userId, notebookId, title, parentNotebookId string, seq, usn int) (bool, string, info.Notebook) {
if notebookId == "" {
return false, "notebookIdNotExists", info.Notebook{}
}
// 先判断usn是否和数据库的一样, 如果不一样, 则冲突, 不保存
notebook := this.GetNotebookById(notebookId)
// 不存在
if notebook.NotebookId == "" {
return false, "notExists", notebook
} else if notebook.Usn != usn {
return false, "conflict", notebook
}
notebook.Usn = userService.IncrUsn(userId);
notebook.Title = title;
updates := bson.M{"Title": title, "Usn": notebook.Usn, "Seq": seq, "UpdatedTime": time.Now()};
if(parentNotebookId != "" && bson.IsObjectIdHex(parentNotebookId)) {
updates["ParentNotebookId"] = bson.ObjectIdHex(parentNotebookId);
} else { } else {
updates["ParentNotebookId"] = "";
} }
ok := db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, updates); return true
if ok {
return ok, "", this.GetNotebookById(notebookId)
}
return false, "", notebook
} }
// 判断是否是blog // 判断是否是blog
@@ -222,22 +174,19 @@ func (this *NotebookService) UpdateNotebook(notebook info.Notebook) bool {
// 更新笔记本标题 // 更新笔记本标题
// [ok] // [ok]
func (this *NotebookService) UpdateNotebookTitle(notebookId, userId, title string) bool { func (this *NotebookService) UpdateNotebookTitle(notebookId, userId, title string) bool {
usn := userService.IncrUsn(userId) return db.UpdateByIdAndUserIdField(db.Notebooks, notebookId, userId, "Title", title)
return db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, bson.M{"Title": title, "Usn": usn})
} }
// 更新notebook // 更新notebook
func (this *NotebookService) UpdateNotebook(userId, notebookId string, needUpdate bson.M) bool { func (this *NotebookService) UpdateNotebook(userId, notebookId string, needUpdate bson.M) bool {
needUpdate["UpdatedTime"] = time.Now(); needUpdate["UpdatedTime"] = time.Now();
needUpdate["Usn"] = userService.IncrUsn(userId)
return db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, needUpdate) return db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, needUpdate)
} }
// ToBlog or Not // ToBlog or Not
func (this *NotebookService) ToBlog(userId, notebookId string, isBlog bool) (bool) { func (this *NotebookService) ToBlog(userId, notebookId string, isBlog bool) (bool) {
updates := bson.M{"IsBlog": isBlog, "Usn": userService.IncrUsn(userId)}
// 笔记本 // 笔记本
db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, updates) db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, bson.M{"IsBlog": isBlog})
// 更新笔记 // 更新笔记
q := bson.M{"UserId": bson.ObjectIdHex(userId), q := bson.M{"UserId": bson.ObjectIdHex(userId),
@@ -248,8 +197,6 @@ func (this *NotebookService) ToBlog(userId, notebookId string, isBlog bool) (boo
} else { } else {
data["HasSelfDefined"] = false data["HasSelfDefined"] = false
} }
// usn
data["Usn"] = userService.IncrUsn(userId)
db.UpdateByQMap(db.Notes, q, data) db.UpdateByQMap(db.Notes, q, data)
// noteContents也更新, 这个就麻烦了, noteContents表没有NotebookId // noteContents也更新, 这个就麻烦了, noteContents表没有NotebookId
@@ -275,19 +222,12 @@ 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{ if db.Count(db.Notebooks, bson.M{"ParentNotebookId": bson.ObjectIdHex(notebookId),
"ParentNotebookId": bson.ObjectIdHex(notebookId), "UserId": bson.ObjectIdHex(userId)}) == 0 { // 无
"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, "IsTrash": false}) == 0 { // 不包含trash
"IsDeleted": false}) == 0 { // 不包含trash return db.DeleteByIdAndUserId(db.Notebooks, notebookId, userId), ""
// 不是真删除 1/20, 为了同步笔记本
ok := db.UpdateByQMap(db.Notebooks, bson.M{"_id": bson.ObjectIdHex(notebookId)}, bson.M{"IsDeleted": true, "Usn": userService.IncrUsn(userId)})
return ok, ""
// return db.DeleteByIdAndUserId(db.Notebooks, notebookId, userId), ""
} }
return false, "笔记本下有笔记" return false, "笔记本下有笔记"
} else { } else {
@@ -295,18 +235,6 @@ func (this *NotebookService) DeleteNotebook(userId, notebookId string) (bool, st
} }
} }
// API调用, 删除笔记本, 不作笔记控制
func (this *NotebookService) DeleteNotebookForce(userId, notebookId string, usn int) (bool, string) {
notebook := this.GetNotebookById(notebookId)
// 不存在
if notebook.NotebookId == "" {
return false, "notExists"
} else if notebook.Usn != usn {
return false, "conflict"
}
return db.DeleteByIdAndUserId(db.Notebooks, notebookId, userId), ""
}
// 排序 // 排序
// 传入 notebookId => Seq // 传入 notebookId => Seq
// 为什么要传入userId, 防止修改其它用户的信息 (恶意) // 为什么要传入userId, 防止修改其它用户的信息 (恶意)
@@ -317,7 +245,7 @@ func (this *NotebookService) SortNotebooks(userId string, notebookId2Seqs map[st
} }
for notebookId, seq := range notebookId2Seqs { for notebookId, seq := range notebookId2Seqs {
if !db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, bson.M{"Seq": seq, "Usn": userService.IncrUsn(userId)}) { if !db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(notebookId), bson.ObjectIdHex(userId), "Seq", seq) {
return false return false
} }
} }
@@ -325,14 +253,15 @@ func (this *NotebookService) SortNotebooks(userId string, notebookId2Seqs map[st
return true return true
} }
// 排序和设置父
func (this *NotebookService) DragNotebooks(userId string, curNotebookId string, parentNotebookId string, siblings []string) bool { func (this *NotebookService) DragNotebooks(userId string, curNotebookId string, parentNotebookId string, siblings []string) bool {
userIdO := bson.ObjectIdHex(userId)
ok := false ok := false
// 如果没parentNotebookId, 则parentNotebookId设空 // 如果没parentNotebookId, 则parentNotebookId设空
if(parentNotebookId == "") { if(parentNotebookId == "") {
ok = db.UpdateByIdAndUserIdMap(db.Notebooks, curNotebookId, userId, bson.M{"ParentNotebookId": "", "Usn": userService.IncrUsn(userId)}); ok = db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(curNotebookId), userIdO, "ParentNotebookId", "");
} else { } else {
ok = db.UpdateByIdAndUserIdMap(db.Notebooks, curNotebookId, userId, bson.M{"ParentNotebookId": bson.ObjectIdHex(parentNotebookId), "Usn": userService.IncrUsn(userId)}); ok = db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(curNotebookId), userIdO, "ParentNotebookId", bson.ObjectIdHex(parentNotebookId));
} }
if !ok { if !ok {
@@ -341,7 +270,7 @@ func (this *NotebookService) DragNotebooks(userId string, curNotebookId string,
// 排序 // 排序
for seq, notebookId := range siblings { for seq, notebookId := range siblings {
if !db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, bson.M{"Seq": seq, "Usn": userService.IncrUsn(userId)}) { if !db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(notebookId), userIdO, "Seq", seq) {
return false return false
} }
} }
@@ -354,7 +283,7 @@ func (this *NotebookService) DragNotebooks(userId string, curNotebookId string,
// trashService: DeleteNote (recove不用, 都统一在MoveNote里了) // trashService: DeleteNote (recove不用, 都统一在MoveNote里了)
func (this *NotebookService) ReCountNotebookNumberNotes(notebookId string) bool { func (this *NotebookService) ReCountNotebookNumberNotes(notebookId string) bool {
notebookIdO := bson.ObjectIdHex(notebookId) notebookIdO := bson.ObjectIdHex(notebookId)
count := db.Count(db.Notes, bson.M{"NotebookId": notebookIdO, "IsTrash": false, "IsDeleted": false}) count := db.Count(db.Notes, bson.M{"NotebookId": notebookIdO, "IsTrash": false})
Log(count) Log(count)
Log(notebookId) Log(notebookId)
return db.UpdateByQField(db.Notebooks, bson.M{"_id": notebookIdO}, "NumberNotes", count) return db.UpdateByQField(db.Notebooks, bson.M{"_id": notebookIdO}, "NumberNotes", count)
+2 -6
View File
@@ -45,13 +45,9 @@ 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", passwd) ok = db.UpdateByQField(db.Users, bson.M{"_id": tokenInfo.UserId}, "Pwd", Md5(pwd))
// 删除token // 删除token
tokenService.DeleteToken(tokenInfo.UserId.Hex(), info.TokenPwd) tokenService.DeleteToken(tokenInfo.UserId.Hex(), info.TokenPwd)
+16 -10
View File
@@ -17,6 +17,13 @@ func (this *SessionService) Update(sessionId, key string, value interface{}) boo
return db.UpdateByQMap(db.Sessions, bson.M{"SessionId": sessionId}, return db.UpdateByQMap(db.Sessions, bson.M{"SessionId": sessionId},
bson.M{key: value, "UpdatedTime": time.Now()}) bson.M{key: value, "UpdatedTime": time.Now()})
} }
func (this *SessionService) UpdateToken(sessionId, key string, value interface{}) bool {
return db.UpdateByQMap(db.Sessions, bson.M{"SessionId": sessionId},
bson.M{key: value, "UpdatedTime": time.Now(), "TokenTime": time.Now()})
}
// 注销时清空session // 注销时清空session
func (this *SessionService) Clear(sessionId string) bool { func (this *SessionService) Clear(sessionId string) bool {
return db.Delete(db.Sessions, bson.M{"SessionId": sessionId}) return db.Delete(db.Sessions, bson.M{"SessionId": sessionId})
@@ -70,19 +77,18 @@ func (this *SessionService) SetCaptcha(sessionId, captcha string) bool {
return ok return ok
} }
//-----------
// API // 附件token
func (this *SessionService) GetUserId(sessionId string) string { func (this *SessionService) GetToken(sessionId string) string {
session := this.Get(sessionId) session := this.Get(sessionId)
// 更新updateTime, 避免过期 return session.Token
db.UpdateByQMap(db.Sessions, bson.M{"SessionId": sessionId},
bson.M{"UpdatedTime": time.Now()})
return session.UserId
} }
// 登录成功后设置userId func (this *SessionService) SetToken(sessionId, token string) bool {
func (this *SessionService) SetUserId(sessionId, userId string) bool {
this.Get(sessionId) this.Get(sessionId)
ok := this.Update(sessionId, "UserId", userId) Log(sessionId)
Log(token)
ok := this.UpdateToken(sessionId, "Token", token)
Log(ok)
return ok return ok
} }
+65 -55
View File
@@ -7,6 +7,9 @@ import (
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time" "time"
"sort" "sort"
"math/rand"
"regexp"
"strings"
) )
// 共享Notebook, Note服务 // 共享Notebook, Note服务
@@ -33,9 +36,8 @@ type ShareService struct {
// 谁共享给了我的Query // 谁共享给了我的Query
func (this *ShareService) getOrQ(userId string) bson.M { func (this *ShareService) getOrQ(userId string) bson.M {
// 得到我和和我参与的组织 // 得到我参与的组织
groupIds := groupService.GetMineAndBelongToGroupIds(userId) groupIds := groupService.GetBelongToGroupIds(userId)
q := bson.M{} q := bson.M{}
if len(groupIds) > 0 { if len(groupIds) > 0 {
orQ := []bson.M{ orQ := []bson.M{
@@ -52,8 +54,6 @@ 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)
@@ -67,15 +67,7 @@ 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
@@ -113,11 +105,6 @@ 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 {
@@ -310,11 +297,7 @@ func (this *ShareService) AddShareNotebook(notebookId string, perm int, userId,
if toUserId == "" { if toUserId == "" {
return false, "无该用户", "" return false, "无该用户", ""
} }
return this.AddShareNotebookToUserId(notebookId, perm, userId, toUserId)
}
// 第三方注册时没有email
func (this *ShareService) AddShareNotebookToUserId(notebookId string, perm int, userId, toUserId string) (bool, string, string) {
// 添加一条记录说明两者存在关系 // 添加一条记录说明两者存在关系
this.AddHasShareNote(userId, toUserId); this.AddHasShareNote(userId, toUserId);
@@ -347,11 +330,7 @@ func (this *ShareService) AddShareNote(noteId string, perm int, userId, email st
if toUserId == "" { if toUserId == "" {
return false, "无该用户", "" return false, "无该用户", ""
} }
return this.AddShareNoteToUserId(noteId, perm, userId, toUserId)
}
// 第三方测试没有userId
func (this *ShareService) AddShareNoteToUserId(noteId string, perm int, userId, toUserId string) (bool, string, string) {
// 添加一条记录说明两者存在关系 // 添加一条记录说明两者存在关系
this.AddHasShareNote(userId, toUserId); this.AddHasShareNote(userId, toUserId);
@@ -361,12 +340,14 @@ func (this *ShareService) AddShareNoteToUserId(noteId string, perm int, userId,
"ToUserId": bson.ObjectIdHex(toUserId), "ToUserId": bson.ObjectIdHex(toUserId),
}); });
shareNote := info.ShareNote{NoteId: bson.ObjectIdHex(noteId), shareNote := info.ShareNote{NoteId: bson.ObjectIdHex(noteId),
UserId: bson.ObjectIdHex(userId), UserId: bson.ObjectIdHex(userId),
ToUserId: bson.ObjectIdHex(toUserId), ToUserId: bson.ObjectIdHex(toUserId),
Perm: perm, Perm: perm,
CreatedTime: time.Now(), CreatedTime: time.Now(),
} }
return db.Insert(db.ShareNotes, shareNote), "", toUserId return db.Insert(db.ShareNotes, shareNote), "", toUserId
} }
@@ -690,7 +671,8 @@ func (this *ShareService) HasReadNotePerm(noteId, userId string) bool {
// 得到笔记分享给的groups // 得到笔记分享给的groups
func (this *ShareService) GetNoteShareGroups(noteId, userId string) []info.ShareNote { func (this *ShareService) GetNoteShareGroups(noteId, userId string) []info.ShareNote {
groups := groupService.GetGroupsContainOf(userId) // 得到分组s
groups := groupService.GetGroups(userId)
// 得到有分享的分组 // 得到有分享的分组
shares := []info.ShareNote{} shares := []info.ShareNote{}
@@ -717,7 +699,9 @@ func (this *ShareService) GetNoteShareGroups(noteId, userId string) []info.Share
// 共享笔记给分组 // 共享笔记给分组
func (this *ShareService) AddShareNoteGroup(userId, noteId, groupId string, perm int) (bool) { func (this *ShareService) AddShareNoteGroup(userId, noteId, groupId string, perm int) (bool) {
if !groupService.IsExistsGroupUser(userId, groupId) { // 得到group, 是否是我的group
group := groupService.GetGroup(userId, groupId)
if group.GroupId == "" {
return false return false
} }
@@ -745,7 +729,8 @@ func (this *ShareService) DeleteShareNoteGroup(userId, noteId, groupId string) b
// 得到笔记本分享给的groups // 得到笔记本分享给的groups
func (this *ShareService) GetNotebookShareGroups(notebookId, userId string) []info.ShareNotebook { func (this *ShareService) GetNotebookShareGroups(notebookId, userId string) []info.ShareNotebook {
groups := groupService.GetGroupsContainOf(userId) // 得到分组s
groups := groupService.GetGroups(userId)
// 得到有分享的分组 // 得到有分享的分组
shares := []info.ShareNotebook{} shares := []info.ShareNotebook{}
@@ -772,7 +757,9 @@ func (this *ShareService) GetNotebookShareGroups(notebookId, userId string) []in
} }
// 共享笔记给分组 // 共享笔记给分组
func (this *ShareService) AddShareNotebookGroup(userId, notebookId, groupId string, perm int) (bool) { func (this *ShareService) AddShareNotebookGroup(userId, notebookId, groupId string, perm int) (bool) {
if !groupService.IsExistsGroupUser(userId, groupId) { // 得到group, 是否是我的group
group := groupService.GetGroup(userId, groupId)
if group.GroupId == "" {
return false return false
} }
@@ -796,35 +783,58 @@ func (this *ShareService) DeleteShareNotebookGroup(userId, notebookId, groupId s
}); });
} }
//--------------------
// 删除组时, 删除所有的
//--------------------
func (this *ShareService) DeleteAllShareNotebookGroup(groupId string) bool { func (this *ShareService) GenSharePass(noteId string) (int, bool) {
return db.Delete(db.ShareNotebooks, bson.M{ r := rand.New(rand.NewSource(time.Now().UnixNano()))
"ToGroupId": bson.ObjectIdHex(groupId), pass := 1000 + r.Intn(9000)
});
} ok := db.Update(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, bson.M{"$set": bson.M{"SharePass": pass}})
func (this *ShareService) DeleteAllShareNoteGroup(groupId string) bool { return pass, ok
return db.Delete(db.ShareNotes, bson.M{
"ToGroupId": bson.ObjectIdHex(groupId),
});
} }
//-------------------- func (this *ShareService) QuerySharePass(noteId string) int {
// 删除组内用户时, 删除其分享的 note := &info.Note{}
//-------------------- db.Get(db.Notes, noteId, note)
func (this *ShareService) DeleteShareNotebookGroupWhenDeleteGroupUser(userId, groupId string) bool { return note.SharePass
return db.Delete(db.ShareNotebooks, bson.M{
"UserId": bson.ObjectIdHex(userId),
"ToGroupId": bson.ObjectIdHex(groupId),
});
} }
func (this *ShareService) DeleteShareNoteGroupWhenDeleteGroupUser(userId, groupId string) bool { func (this *ShareService) Verify4ShareNote(noteId string, sharePass int) (flag bool, note info.Note, noteContent info.NoteContent) {
return db.Delete(db.ShareNotes, bson.M{ note = info.Note{}
"UserId": bson.ObjectIdHex(userId), db.Get(db.Notes, noteId, &note)
"ToGroupId": bson.ObjectIdHex(groupId), if note.SharePass == sharePass {
}); note = noteService.GetNoteById(noteId)
noteContent = noteService.GetNoteContent(noteId, note.UserId.Hex())
flag = true
} else {
note = info.Note{}
noteContent = info.NoteContent{}
flag = false
}
return
} }
//把笔记中含有attachID的url后加上token,返回笔记中含有的attachids
func (this *ShareService) AppendToken4URL(token, content string) (c string, attachIds map[string]bool) {
re := regexp.MustCompile(`href=\"(.*?attachId.*?)\".*?data-mce-href=\"(.*?attachId.*?)\"`)
re2 := regexp.MustCompile(`\"(.*?attachId.*?)\"`)
attachIds = map[string]bool{}
s := re.ReplaceAllStringFunc(content, func(m string) string {
idx := strings.Index(m, "attachId=") + len("attachId=")
var ss []byte = []byte(m)
attachId := string(ss[idx: idx + 24])
attachIds[attachId] = true
return re2.ReplaceAllString(m, `"${1}&token=` + token + `"`)
})
re3 := regexp.MustCompile(`<img\s+src=\"(.*?fileId.*?)\".*?data-mce-src=\"(.*?fileId.*?)\"`)
re4 := regexp.MustCompile(`\"(.*?fileId.*?)\"`)
c = re3.ReplaceAllStringFunc(s, func(m string) string {
return re4.ReplaceAllString(m, `"${1}&token=` + token + `"`)
})
return
}
+2 -100
View File
@@ -3,9 +3,9 @@ package service
import ( import (
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time" // "time"
) )
/* /*
@@ -14,14 +14,12 @@ import (
type TagService struct { type TagService struct {
} }
/*
func (this *TagService) GetTags(userId string) []string { func (this *TagService) GetTags(userId string) []string {
tag := info.Tag{} tag := info.Tag{}
db.Get(db.Tags, userId, &tag) db.Get(db.Tags, userId, &tag)
LogJ(tag) LogJ(tag)
return tag.Tags return tag.Tags
} }
*/
func (this *TagService) AddTagsI(userId string, tags interface{}) bool { func (this *TagService) AddTagsI(userId string, tags interface{}) bool {
if ts, ok2 := tags.([]string); ok2 { if ts, ok2 := tags.([]string); ok2 {
@@ -39,99 +37,3 @@ func (this *TagService) AddTags(userId string, tags []string) bool {
} }
return true return true
} }
//---------------------------
// v2
// 第二版标签, 单独一张表, 每一个tag一条记录
// 添加或更新标签, 先查下是否存在, 不存在则添加, 存在则更新
// 都要统计下tag的note数
// 什么时候调用? 笔记添加Tag, 删除Tag时
// 删除note时, 都可以调用
// 万能
func (this *TagService) AddOrUpdateTag(userId string, tag string) info.NoteTag {
userIdO := bson.ObjectIdHex(userId)
noteTag := info.NoteTag{}
db.GetByQ(db.NoteTags, bson.M{"UserId": userIdO, "Tag": tag}, &noteTag)
// 存在, 则更新之
if noteTag.TagId != "" {
// 统计note数
count := noteService.CountNoteByTag(userId, tag)
noteTag.Count = count
noteTag.UpdatedTime = time.Now()
// noteTag.Usn = userService.IncrUsn(userId), 更新count而已
db.UpdateByIdAndUserId(db.NoteTags, noteTag.TagId.Hex(), userId, noteTag)
return noteTag
}
// 不存在, 则创建之
noteTag.TagId = bson.NewObjectId()
noteTag.Count = 1
noteTag.Tag = tag
noteTag.UserId = bson.ObjectIdHex(userId)
noteTag.CreatedTime = time.Now()
noteTag.UpdatedTime = noteTag.CreatedTime
noteTag.Usn = userService.IncrUsn(userId)
noteTag.IsDeleted = false
db.Insert(db.NoteTags, noteTag)
return noteTag
}
// 得到标签, 按更新时间来排序
func (this *TagService) GetTags(userId string) []info.NoteTag {
tags := []info.NoteTag{}
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsDeleted": false}
q := db.NoteTags.Find(query);
sortFieldR := "-UpdatedTime"
q.Sort(sortFieldR).All(&tags)
return tags
}
// 删除标签
// 也删除所有的笔记含该标签的
// 返回noteId => usn
func (this *TagService) DeleteTag(userId string, tag string) map[string]int {
usn := userService.IncrUsn(userId)
if db.UpdateByQMap(db.NoteTags, bson.M{"UserId": bson.ObjectIdHex(userId), "Tag": tag}, bson.M{"Usn": usn, "IsDeleted": true}) {
return noteService.UpdateNoteToDeleteTag(userId, tag);
}
return map[string]int{}
}
// 删除标签, 供API调用
func (this *TagService) DeleteTagApi(userId string, tag string, usn int) (ok bool, msg string, toUsn int) {
noteTag := info.NoteTag{}
db.GetByQ(db.NoteTags, bson.M{"UserId": bson.ObjectIdHex(userId), "Tag": tag}, &noteTag)
if noteTag.TagId == "" {
return false, "notExists", 0
}
if noteTag.Usn > usn {
return false, "conflict", 0
}
toUsn = userService.IncrUsn(userId)
if db.UpdateByQMap(db.NoteTags, bson.M{"UserId": bson.ObjectIdHex(userId), "Tag": tag}, bson.M{"Usn": usn, "IsDeleted": true}) {
return true, "", toUsn
}
return false, "", 0
}
// 重新统计标签的count
func (this *TagService) reCountTagCount(userId string, tags []string) {
if tags == nil {
return
}
for _, tag := range tags {
this.AddOrUpdateTag(userId, tag);
}
}
// 同步用
func (this *TagService) GeSyncTags(userId string, afterUsn, maxEntry int) ([]info.NoteTag) {
noteTags := []info.NoteTag{}
q := db.NoteTags.Find(bson.M{"UserId": bson.ObjectIdHex(userId), "Usn": bson.M{"$gt": afterUsn}});
q.Sort("Usn").Limit(maxEntry).All(&noteTags)
return noteTags
}
+2 -10
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/" + Digest3(userId) + "/" + userId + "/themes" return revel.BasePath + "/public/upload/" + 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/" + Digest3(userId) + "/" + userId + "/themes/" + themeId return "public/upload/" + userId + "/themes/" + themeId
} }
// 新建主题 // 新建主题
@@ -412,18 +412,10 @@ 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
err := os.MkdirAll(targetPath, 0755)
if err != nil {
msg = "error"
return
}
if ok, msg = archive.Unzip(path, targetPath); !ok { if ok, msg = archive.Unzip(path, targetPath); !ok {
DeleteFile(targetPath) DeleteFile(targetPath)
Log("oh no")
return return
} }
// 主题验证 // 主题验证
if ok, msg = this.ValidateTheme(targetPath, "", ""); !ok { if ok, msg = this.ValidateTheme(targetPath, "", ""); !ok {
DeleteFile(targetPath) DeleteFile(targetPath)
+5 -53
View File
@@ -25,16 +25,10 @@ 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
if db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true, "Usn": userService.IncrUsn(userId)}}) { if db.UpdateByIdAndUserId(db.Notes, noteId, userId, bson.M{"$set": bson.M{"IsTrash": true}}) {
// recount notebooks' notes number // recount notebooks' notes number
notebookIdO := noteService.GetNotebookId(noteId) notebookIdO := noteService.GetNotebookId(noteId)
notebookId := notebookIdO.Hex() notebookId := notebookIdO.Hex()
@@ -42,17 +36,15 @@ func (this *TrashService) DeleteNote(noteId, userId string) bool {
return true return true
} }
} }
return false return false
} }
// 删除别人共享给我的笔记 // 删除别人共享给我的笔记
// 先判断我是否有权限, 笔记是否是我创建的 // 先判断我是否有权限, 笔记是否是我创建的
func (this *TrashService) DeleteSharedNote(noteId, myUserId string) bool { func (this *TrashService) DeleteSharedNote(noteId, userId, myUserId string) bool {
note := noteService.GetNoteById(noteId) note := noteService.GetNote(noteId, userId)
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}})
} }
return false return false
} }
@@ -61,63 +53,23 @@ func (this *TrashService) DeleteSharedNote(noteId, myUserId string) bool {
func (this *TrashService) recoverNote(noteId, notebookId, userId string) bool { func (this *TrashService) recoverNote(noteId, notebookId, userId string) bool {
re := db.UpdateByIdAndUserId(db.Notes, noteId, userId, re := db.UpdateByIdAndUserId(db.Notes, noteId, userId,
bson.M{"$set": bson.M{"IsTrash": false, bson.M{"$set": bson.M{"IsTrash": false,
"Usn": userService.IncrUsn(userId),
"NotebookId": bson.ObjectIdHex(notebookId)}}) "NotebookId": bson.ObjectIdHex(notebookId)}})
return re; return re;
} }
// 删除trash // 删除trash
func (this *TrashService) DeleteTrash(noteId, userId string) bool { func (this *TrashService) DeleteTrash(noteId, userId string) bool {
note := noteService.GetNote(noteId, userId);
if note.NoteId == "" {
return false
}
// delete note's attachs // delete note's attachs
ok := attachService.DeleteAllAttachs(noteId, userId) ok := attachService.DeleteAllAttachs(noteId, userId)
// 设置删除位
ok = db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
bson.M{"IsDeleted": true,
"Usn": userService.IncrUsn(userId)})
// delete note // delete note
// ok = db.DeleteByIdAndUserId(db.Notes, noteId, userId) ok = db.DeleteByIdAndUserId(db.Notes, noteId, userId)
// delete content // delete content
ok = db.DeleteByIdAndUserId(db.NoteContents, noteId, userId) ok = db.DeleteByIdAndUserId(db.NoteContents, noteId, userId)
// 重新统计tag's count
// TODO 这里会改变tag's Usn
tagService.reCountTagCount(userId, note.Tags)
return ok return ok
} }
func (this *TrashService) DeleteTrashApi(noteId, userId string, usn int) (bool, string, int) {
note := noteService.GetNote(noteId, userId)
if note.NoteId == "" || note.IsDeleted {
return false, "notExists", 0
}
if note.Usn != usn {
return false, "conflict", 0
}
// delete note's attachs
ok := attachService.DeleteAllAttachs(noteId, userId)
// 设置删除位
afterUsn := userService.IncrUsn(userId)
ok = db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId,
bson.M{"IsDeleted": true,
"Usn": afterUsn})
// delete content
ok = db.DeleteByIdAndUserId(db.NoteContents, noteId, userId)
return ok, "", afterUsn
}
// 列出note, 排序规则, 还有分页 // 列出note, 排序规则, 还有分页
// CreatedTime, UpdatedTime, title 来排序 // CreatedTime, UpdatedTime, title 来排序
func (this *TrashService) ListNotes(userId string, func (this *TrashService) ListNotes(userId string,
+2 -80
View File
@@ -5,7 +5,7 @@ import (
. "github.com/leanote/leanote/app/lea" . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time" // "time"
) )
@@ -38,7 +38,7 @@ func (this *UpgradeService) UpgradeBlog() bool {
*/ */
func (this *UpgradeService) UpgradeBetaToBeta2(userId string) (ok bool, msg string) { func (this *UpgradeService) UpgradeBetaToBeta2(userId string) (ok bool, msg string) {
if configService.GetGlobalStringConfig("UpgradeBetaToBeta2") != "" { if configService.GetGlobalStringConfig("UpgradeBetaToBeta2") != "" {
return false, "Leanote have been upgraded" return false, "已升级"
} }
// 1. aboutMe -> page // 1. aboutMe -> page
@@ -102,81 +102,3 @@ func (this *UpgradeService) UpgradeBetaToBeta2(userId string) (ok bool, msg stri
return return
} }
// Usn设置
// 客户端 api
func (this *UpgradeService) moveTag() {
usnI := 1
tags := []info.Tag{}
db.ListByQ(db.Tags, bson.M{}, &tags)
for _, eachTag := range tags {
tagTitles := eachTag.Tags
now := time.Now()
if tagTitles != nil && len(tagTitles) > 0 {
for _, tagTitle := range tagTitles {
noteTag := info.NoteTag{}
noteTag.TagId = bson.NewObjectId()
noteTag.Count = 1
noteTag.Tag = tagTitle
noteTag.UserId = eachTag.UserId
noteTag.CreatedTime = now
noteTag.UpdatedTime = now
noteTag.Usn = usnI
noteTag.IsDeleted = false
db.Insert(db.NoteTags, noteTag)
usnI++
}
}
}
}
func (this *UpgradeService) setNotebookUsn() {
usnI := 1
notebooks := []info.Notebook{}
db.ListByQWithFields(db.Notebooks, bson.M{}, []string{"UserId"}, &notebooks)
for _, notebook := range notebooks {
db.UpdateByQField(db.Notebooks, bson.M{"_id": notebook.NotebookId}, "Usn", usnI)
usnI++
}
}
func (this *UpgradeService) setNoteUsn() {
usnI := 1
notes := []info.Note{}
db.ListByQWithFields(db.Notes, bson.M{}, []string{"UserId"}, &notes)
for _, note := range notes {
db.UpdateByQField(db.Notes, bson.M{"_id": note.NoteId}, "Usn", usnI)
usnI++
}
}
// 升级为Api, beta.4
func (this *UpgradeService) Api(userId string) (ok bool, msg string) {
if configService.GetGlobalStringConfig("UpgradeBetaToBeta4") != "" {
return false, "Leanote have been upgraded"
}
// user
db.UpdateByQField(db.Users, bson.M{}, "Usn", 200000)
// notebook
db.UpdateByQField(db.Notebooks, bson.M{}, "IsDeleted", false)
this.setNotebookUsn();
// note
// 1-N
db.UpdateByQField(db.Notes, bson.M{}, "IsDeleted", false)
this.setNoteUsn();
// tag
// 1-N
/// tag, 要重新插入, 将之前的Tag表迁移到NoteTag中
this.moveTag();
configService.UpdateGlobalStringConfig(userId, "UpgradeBetaToBeta4", "1")
return true, ""
}
+16 -74
View File
@@ -12,27 +12,6 @@ import (
type UserService struct { type UserService struct {
} }
// 自增Usn
// 每次notebook,note添加, 修改, 删除, 都要修改
func (this *UserService) IncrUsn(userId string) int {
user := info.User{}
query := bson.M{"_id": bson.ObjectIdHex(userId)}
db.GetByQWithFields(db.Users, query, []string{"Usn"}, &user)
usn := user.Usn
usn += 1
Log("inc Usn")
db.UpdateByQField(db.Users, query, "Usn", usn)
return usn
// return db.Update(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, bson.M{"$inc": bson.M{"ReadNum": 1}})
}
func (this *UserService) GetUsn(userId string) int {
user := info.User{}
query := bson.M{"_id": bson.ObjectIdHex(userId)}
db.GetByQWithFields(db.Users, query, []string{"Usn"}, &user)
return user.Usn
}
// 添加用户 // 添加用户
func (this *UserService) AddUser(user info.User) bool { func (this *UserService) AddUser(user info.User) bool {
if user.UserId == "" { if user.UserId == "" {
@@ -114,13 +93,6 @@ 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{}
@@ -133,8 +105,6 @@ func (this *UserService) GetUserInfo(userId string) info.User {
func (this *UserService) GetUserInfoByEmail(email string) info.User { func (this *UserService) GetUserInfoByEmail(email string) info.User {
user := info.User{} user := info.User{}
db.GetByQ(db.Users, bson.M{"Email": email}, &user) db.GetByQ(db.Users, bson.M{"Email": email}, &user)
// Logo路径问题, 有些有http: 有些没有
this.setUserLogo(&user)
return user return user
} }
// 得到用户信息 username // 得到用户信息 username
@@ -142,15 +112,12 @@ func (this *UserService) GetUserInfoByUsername(username string) info.User {
user := info.User{} user := info.User{}
username = strings.ToLower(username) username = strings.ToLower(username)
db.GetByQ(db.Users, bson.M{"Username": username}, &user) db.GetByQ(db.Users, bson.M{"Username": username}, &user)
// Logo路径问题, 有些有http: 有些没有
this.setUserLogo(&user)
return user return user
} }
func (this *UserService) GetUserInfoByThirdUserId(thirdUserId string) info.User { func (this *UserService) GetUserInfoByThirdUserId(thirdUserId string) info.User {
user := info.User{} user := info.User{}
db.GetByQ(db.Users, bson.M{"ThirdUserId": thirdUserId}, &user) db.GetByQ(db.Users, bson.M{"ThirdUserId": thirdUserId}, &user)
this.setUserLogo(&user)
return user return user
} }
func (this *UserService) ListUserInfosByUserIds(userIds []bson.ObjectId) []info.User { func (this *UserService) ListUserInfosByUserIds(userIds []bson.ObjectId) []info.User {
@@ -216,33 +183,19 @@ 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),
} }
} }
@@ -267,17 +220,17 @@ func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.
return users2 return users2
} }
// 使用email(username), 得到用户信息 // 使用email(username), pwd得到用户信息
func (this *UserService) GetUserInfoByName(emailOrUsername string) info.User { func (this *UserService) LoginGetUserInfo(emailOrUsername, md5Pwd 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}, &user) db.GetByQ(db.Users, bson.M{"Email": emailOrUsername, "Pwd": md5Pwd}, &user)
} else { } else {
db.GetByQ(db.Users, bson.M{"Username": emailOrUsername}, &user) db.GetByQ(db.Users, bson.M{"Username": emailOrUsername, "Pwd": md5Pwd}, &user)
} }
this.setUserLogo(&user)
return user return user
} }
@@ -309,16 +262,10 @@ 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 !ComparePwd(oldPwd, userInfo.Pwd) { if userInfo.Pwd != Md5(oldPwd) {
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, ""
} }
@@ -327,12 +274,7 @@ 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
} }
@@ -390,7 +332,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 = strings.ToLower(tokenInfo.Email) email = tokenInfo.Email
// 先验证该email是否被注册了 // 先验证该email是否被注册了
if userService.IsExistsUser(email) { if userService.IsExistsUser(email) {
ok = false ok = false
+2 -13
View File
@@ -99,17 +99,16 @@ 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)
} }
@@ -120,15 +119,6 @@ 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)
} }
@@ -149,7 +139,6 @@ 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
@@ -0,0 +1,237 @@
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
@@ -1,23 +0,0 @@
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
@@ -1,14 +0,0 @@
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
@@ -1,3 +0,0 @@
package tests
func a() {
}
+17
View File
@@ -11,3 +11,20 @@
<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>
+3 -18
View File
@@ -13,6 +13,7 @@
</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">
@@ -156,7 +157,7 @@
</li> </li>
<li> <li>
<a href="/admin/t?t=setting/share_note"> <a href="/admin/t?t=setting/shareNote">
<span> <span>
Register Share Note Register Share Note
</span> </span>
@@ -170,15 +171,6 @@
</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>
@@ -237,14 +229,7 @@
<li> <li>
<a href="/admin/t?t=upgrade/beta2"> <a href="/admin/t?t=upgrade/beta2">
<span> <span>
v1.0-beta to v1.0-beta.2 v1.0-beta to v1.0-beta2
</span>
</a>
</li>
<li>
<a href="/admin/t?t=upgrade/beta3">
<span>
v1.0-beta.2/3 to v1.0-beta.4
</span> </span>
</a> </a>
</li> </li>
-52
View File
@@ -1,52 +0,0 @@
{{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" .}}
+1 -1
View File
@@ -46,7 +46,7 @@
</a> </a>
</li> </li>
<li class="hidden-xs"> <li class="hidden-xs">
<a href="/blog" class="dk" target="_blank"> <a href="/blog/admin" class="dk" target="_blank">
My Blog My Blog
</a> </a>
</li> </li>
+10 -12
View File
@@ -1,5 +1,5 @@
{{template "admin/top.html" .}} {{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Upgrade v1.0-beta to v1.0-beta.2</h3></div> <div class="m-b-md"> <h3 class="m-b-none">Upgrade v1.0-beta to v1.0-beta2</h3></div>
<div class="row"> <div class="row">
@@ -17,7 +17,7 @@
</ul> </ul>
</div> </div>
<footer class="panel-footer text-right bg-light lter"> <footer class="panel-footer text-right bg-light lter">
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Upgrade to v1.0-beta.2</button> <button type="submit" id="submit" class="btn btn-success btn-s-xs">Upgrade</button>
</footer> </footer>
</section> </section>
</form> </form>
@@ -31,16 +31,14 @@ $(function() {
$("#submit").click(function(e){ $("#submit").click(function(e){
e.preventDefault(); e.preventDefault();
var t = this; var t = this;
art.confirm("Are you sure to upgrade to v1.0-beta.2", function() { $(t).button('loading');
$(t).button('loading'); ajaxPost("/adminUpgrade/UpgradeBetaToBeta2", {}, function(ret){
ajaxPost("/adminUpgrade/UpgradeBetaToBeta2", {}, function(ret){ $(t).button('reset')
$(t).button('reset') if(!ret.Ok) {
if(!ret.Ok) { art.alert(ret.Msg)
art.alert(ret.Msg) } else {
} else { art.tips("Success");
art.tips("Success"); }
}
});
}); });
}); });
}); });
-46
View File
@@ -1,46 +0,0 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Upgrade v1.0-beta.2/3 to v1.0-beta.4</h3></div>
<div class="row">
<div class="col-sm-6">
<form id="add_user_form">
<section class="panel panel-default">
<div class="panel-body">
Current Version: <span class="label label-success">leanote v{{.version}}</span>
<ul>
<li>Api support (Enable Leanote Desktop App to connect server))</li>
<li>Update tags</li>
</ul>
</div>
<footer class="panel-footer text-right bg-light lter">
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Upgrade to v1.0-beta.4</button>
</footer>
</section>
</form>
</div>
</div>
{{template "admin/footer.html" .}}
<script>
$(function() {
$("#submit").click(function(e) {
e.preventDefault();
var t = this;
art.confirm("Are you sure to upgrade to v1.0-beta.4 ? Please don't try to upgrade twice.", function() {
$(t).button('loading');
ajaxPost("/adminUpgrade/UpgradeBeta3ToBeta4", {}, function(ret){
$(t).button('reset')
if(!ret.Ok) {
art.alert(ret.Msg)
} else {
art.tips("Success");
}
});
});
});
});
</script>
{{template "admin/end.html" .}}
+1 -1
View File
@@ -26,7 +26,7 @@
</section> </section>
<div id="boxFooter"> <div id="boxFooter">
<p> <p>
<a href="/index">leanote</a> © 2015 <a href="/index">leanote</a> © 2014
</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> © 2015 <a href="/index">leanote</a> © 2014
</p> </p>
</div> </div>
</body> </body>
-124
View File
@@ -1,124 +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">
<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-2015 <a href="/index">leanote</a> © 2014
</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-2015 <a href="/index">leanote</a> © 2014
</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-2015 <a href="/index">leanote</a> © 2014
</p> </p>
</div> </div>
+5 -6
View File
@@ -7,19 +7,18 @@
<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
<br /> <div id="beian">
Copyright © 2014-2015 <a href="https://leanote.com">Leanote</a> <a href="http://www.miibeian.gov.cn" target="_blank">沪ICP备14006011号</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, 158716820 QQ Group: 158716820
</div> </div>
</div> </div>
</div> </div>
<div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div>
</div> </div>
+21 -25
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,33 +37,30 @@ 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#" data-target="body" class="smooth-scroll">{{msg . "home"}}</a></li> <li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<!--
<li><a href="/index#aboutLeanote" target="#aboutLeanote" class="smooth-scroll">{{msg . "aboutLeanote"}}</a> </li>
-->
<li><a href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
<li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
<li 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><a id="leanoteBlog" href="http://lea.leanote.com" target="_blank" title="Lea++, leanote blog platform" class="">Lea++</a></li>
<li><a id="leanoteApp" href="http://app.leanote.com" target="_blank" title="Leanote App" class="">{{msg . "desktopApp"}}</a></li>
<li>
<a href="http://leanote.org#donate" target="_blank" class="">{{msg . "donate"}}</a>
</li>
<li><a href="http://leanote.org" target="_blank" title="leanote.org" class="">leanote.org</a></li>
{{if .userInfo.Email}}
<li id="loginBtns"> <li id="loginBtns">
{{msg . "hi"}}, {{.userInfo.Username}} {{if .userInfo.Email}}
<a href="{{$.noteUrl}}">{{msg . "myNote"}}</a> {{msg . "hi"}}, {{.userInfo.Username}}
<a href="/logout">{{msg . "logout"}}</a> <a href="{{$.noteUrl}}">{{msg . "myNote"}}</a>
<a href="/logout">{{msg . "logout"}}</a>
{{else}}
<a href="/login">{{msg . "login"}}</a>
{{if .openRegister}}
<a href="/register" class="btn-register">{{msg . "register"}}</a>
{{end}}
{{end}}
</li> </li>
{{end}}
</ul> </ul>
</div> </div>
</div> </div>
@@ -93,11 +90,10 @@ function log(o) {
</div> </div>
<ul id="blogNav" class="pull-right"> <ul id="blogNav" class="pull-right">
<li><a href="#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li> <li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<li><a href="#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li> <li><a href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
<li><a href="#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li> <li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
<li><a id="leanoteBlog" href="{{.leaUrl}}/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li> <li><a 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;
+81 -37
View File
@@ -1,22 +1,26 @@
{{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-default btn-primary" href="/register">{{msg . "register"}}</a> <a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
&nbsp; &nbsp;
&nbsp; &nbsp;
{{if not .userInfo.UserId}}
<a class="btn btn-default" href="/login">{{msg . "login"}}</a>
&nbsp;
&nbsp;
{{end}}
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a> <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> </div>
@@ -25,23 +29,13 @@
<div class="img-header"> <div class="img-header">
<img src="/images/home/mac-btns.png"/> <img src="/images/home/mac-btns.png"/>
</div> </div>
<div id="webSliderContainer"> <img src="/images/home/preview2.png" style="width: 750px;" />
<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>
<div id="mobileSliderContainer"> <img class="mobile-image" src="/images/home/mobile.png" />
<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>
@@ -52,10 +46,6 @@
<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>
@@ -65,27 +55,81 @@
<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>
var _hmt = _hmt || []; $(function() {
(function() { /*
var hm = document.createElement("script"); var u = navigator.userAgent;
hm.src = "//hm.baidu.com/hm.js?d0c988e37b452b3bf1220d45b30f2de2"; var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1;
var s = document.getElementsByTagName("script")[0]; if(isMobile || $("body").width() < 600) {
s.parentNode.insertBefore(hm, s); 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> </script>
</body> </body>
</html> </html>
+140
View File
@@ -0,0 +1,140 @@
{{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
@@ -0,0 +1,139 @@
{{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>
Loaded 100 of 2265 files, more files were not shown because too many files have changed in this diff. Show more