Compare commits
108 Commits
1.0
...
feature-pd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
716fbeae0c | ||
|
|
103891e0c0 | ||
|
|
638b5b51e0 | ||
|
|
90fea722aa | ||
|
|
90ce35327f | ||
|
|
60e3b9446a | ||
|
|
16a726223c | ||
|
|
dfe45f39bb | ||
|
|
413be4a6d0 | ||
|
|
6a35f98f89 | ||
|
|
ad295d97c8 | ||
|
|
cf8171c9cd | ||
|
|
c616a4e9a6 | ||
|
|
04d641a5ad | ||
|
|
ec68570a38 | ||
|
|
407b44382c | ||
|
|
71a2bcc7f0 | ||
|
|
a2dd09725c | ||
|
|
f757e00f03 | ||
|
|
b7652bb321 | ||
|
|
e6018d32ec | ||
|
|
cb7e272bbe | ||
|
|
a4d9d8e0d3 | ||
|
|
c844c8b188 | ||
|
|
320263eefa | ||
|
|
274875c6c4 | ||
|
|
1ea35324c1 | ||
|
|
3e20ad3cdd | ||
|
|
711e6b3d17 | ||
|
|
0438bbb414 | ||
|
|
70ee362cc8 | ||
|
|
0479f1a433 | ||
|
|
7e01cb8227 | ||
|
|
ae0eb3b918 | ||
|
|
097d2709e2 | ||
|
|
f1e56272ef | ||
|
|
db4cfbf605 | ||
|
|
da7d31fa00 | ||
|
|
62bc74d3c6 | ||
|
|
4467689ec3 | ||
|
|
69a874d90d | ||
|
|
b259a00c94 | ||
|
|
a0088ead9e | ||
|
|
228fd80abd | ||
|
|
8b3a1a646a | ||
|
|
c4954b94b8 | ||
|
|
2b56ebd620 | ||
|
|
a78d95ddad | ||
|
|
938362154a | ||
|
|
8bff2b7000 | ||
|
|
c56f228646 | ||
|
|
98f0313e3e | ||
|
|
bde2e891ef | ||
|
|
7acdaede0d | ||
|
|
45d1b4bee3 | ||
|
|
4d0a170f01 | ||
|
|
5e37e5a37d | ||
|
|
a60da3d592 | ||
|
|
9a88fab84e | ||
|
|
4981d6aad4 | ||
|
|
7193ac6833 | ||
|
|
3d1a32764d | ||
|
|
2b8df3ed76 | ||
|
|
e94b004a5a | ||
|
|
6cc6f26fa1 | ||
|
|
120cdd53c3 | ||
|
|
435aac4a9c | ||
|
|
f234cf285d | ||
|
|
54953b69c6 | ||
|
|
60878b7a65 | ||
|
|
dada88d8a2 | ||
|
|
7167705409 | ||
|
|
4217abca6e | ||
|
|
49a97f2285 | ||
|
|
4f2d7b8cd0 | ||
|
|
afbda7bfb2 | ||
|
|
c568756d16 | ||
|
|
5794f76b0a | ||
|
|
74a768c0fb | ||
|
|
4136060825 | ||
|
|
e249a953cd | ||
|
|
eddc2e6356 | ||
|
|
1604474d6e | ||
|
|
bbaf71481c | ||
|
|
952117818c | ||
|
|
a31432f1bf | ||
|
|
b0afe25730 | ||
|
|
024e0e9bae | ||
|
|
dbabc3a5a8 | ||
|
|
a30aec8254 | ||
|
|
7440218974 | ||
|
|
b3c771a87f | ||
|
|
8f8451cdff | ||
|
|
6d7fe83469 | ||
|
|
05a5280162 | ||
|
|
8dd55eb949 | ||
|
|
5838cc218f | ||
|
|
2e52c957e0 | ||
|
|
9486c58d78 | ||
|
|
6e7c1e0d41 | ||
|
|
6a0a60a644 | ||
|
|
94f9c2c3cb | ||
|
|
37e7ed8d14 | ||
|
|
4227c80ec7 | ||
|
|
6ec6ef698b | ||
|
|
0543dd015e | ||
|
|
b88f895acf | ||
|
|
fc517700cc |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,5 +16,4 @@ app/tmp/main.go
|
||||
.project
|
||||
public/config.codekit
|
||||
files
|
||||
/gulpfile.js
|
||||
/node_modules
|
||||
|
||||
37
.travis.yml
Normal file
37
.travis.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
language: go
|
||||
go: 1.3
|
||||
|
||||
services:
|
||||
- mongodb # 2.4.12
|
||||
|
||||
install:
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- go get -v github.com/leanote/leanote/app
|
||||
- go get -u github.com/revel/cmd/revel
|
||||
- ls $GOPATH/src/github.com/revel/
|
||||
# - go get github.com/revel/moudle/revel
|
||||
# - go install github.com/revel/cmd/revel
|
||||
- pwd
|
||||
- ls
|
||||
|
||||
script:
|
||||
- mongo --version
|
||||
- mongorestore -h localhost -d leanote --directoryperdb ./mongodb_backup/leanote_install_data/
|
||||
|
||||
- go test github.com/leanote/leanote/app/tests
|
||||
|
||||
# gen tmp/main.go, routes/routes.go
|
||||
- go run app/cmd/main.go
|
||||
# build
|
||||
- go build -o leanote github.com/leanote/leanote/app/tmp
|
||||
# run with port 9000
|
||||
- ./leanote -importPath=github.com/leanote/leanote -runMode=dev -port=9000 &
|
||||
- sleep 10s;
|
||||
# test
|
||||
- curl http://localhost:9000
|
||||
- curl http://localhost:9000/blog
|
||||
- curl http://localhost:9000/login
|
||||
- curl http://localhost:9000/demo
|
||||
|
||||
# - revel build github.com/leanote/leanote tmp
|
||||
# OK
|
||||
345
Gulpfile.js
Normal file
345
Gulpfile.js
Normal file
@@ -0,0 +1,345 @@
|
||||
var gulp = require('gulp');
|
||||
var clean = require('gulp-clean');
|
||||
var uglify = require('gulp-uglify');
|
||||
var rename = require('gulp-rename');
|
||||
var minifyHtml = require("gulp-minify-html");
|
||||
var concat = require('gulp-concat');
|
||||
var replace = require('gulp-replace');
|
||||
var inject = require('gulp-inject');
|
||||
var gulpSequence = require('gulp-sequence');
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
var leanoteBase = './';
|
||||
var base = leanoteBase + '/public'; // public base
|
||||
var noteDev = leanoteBase + '/app/views/note/note-dev.html';
|
||||
var noteProBase = leanoteBase + '/app/views/note';
|
||||
|
||||
// 合并Js, 这些js都是不怎么修改, 且是依赖
|
||||
// 840kb, 非常耗时!!
|
||||
gulp.task('concatDepJs', function() {
|
||||
var jss = [
|
||||
'js/jquery-1.9.0.min.js',
|
||||
'js/jquery.ztree.all-3.5-min.js',
|
||||
'tinymce/tinymce.full.min.js', // 使用打成的包, 加载速度快
|
||||
// 'libs/ace/ace.js',
|
||||
'js/jQuery-slimScroll-1.3.0/jquery.slimscroll-min.js',
|
||||
'js/contextmenu/jquery.contextmenu-min.js',
|
||||
'js/bootstrap-min.js',
|
||||
'js/object_id-min.js',
|
||||
];
|
||||
|
||||
for(var i in jss) {
|
||||
jss[i] = base + '/' + jss[i];
|
||||
}
|
||||
|
||||
return gulp
|
||||
.src(jss)
|
||||
// .pipe(uglify()) // 压缩
|
||||
.pipe(concat('dep.min.js'))
|
||||
.pipe(gulp.dest(base + '/js'));
|
||||
});
|
||||
|
||||
// 合并app js 这些js会经常变化 90kb
|
||||
gulp.task('concatAppJs', function() {
|
||||
var jss = [
|
||||
'js/common.js',
|
||||
'js/app/note.js',
|
||||
'js/app/page.js', // 写作模式下, page依赖note
|
||||
'js/app/tag.js',
|
||||
'js/app/notebook.js',
|
||||
'js/app/share.js',
|
||||
];
|
||||
|
||||
for(var i in jss) {
|
||||
jss[i] = base + '/' + jss[i];
|
||||
}
|
||||
|
||||
return gulp
|
||||
.src(jss)
|
||||
.pipe(uglify()) // 压缩
|
||||
.pipe(concat('app.min.js'))
|
||||
.pipe(gulp.dest(base + '/js'));
|
||||
});
|
||||
|
||||
// 合并requirejs和markdown为一个文件
|
||||
gulp.task('concatMarkdownJs', function() {
|
||||
var jss = [
|
||||
'js/require.js',
|
||||
'dist/main.min.js',
|
||||
];
|
||||
|
||||
for(var i in jss) {
|
||||
jss[i] = base + '/' + jss[i];
|
||||
}
|
||||
|
||||
return gulp
|
||||
.src(jss)
|
||||
.pipe(uglify()) // 压缩
|
||||
.pipe(concat('markdown.min.js'))
|
||||
.pipe(gulp.dest(base + '/js'));
|
||||
});
|
||||
|
||||
// note-dev.html -> note.html, 替换css, js
|
||||
// TODO 加?t=2323232, 强制浏览器更新, 一般只需要把app.min.js上加
|
||||
gulp.task('devToProHtml', function() {
|
||||
return gulp
|
||||
.src(noteDev)
|
||||
.pipe(replace(/<!-- dev -->[.\s\S]+?<!-- \/dev -->/g, '')) // 把dev 去掉
|
||||
.pipe(replace(/<!-- pro_dep_js -->/, '<script src="/js/dep.min.js"></script>')) // 替换
|
||||
.pipe(replace(/<!-- pro_app_js -->/, '<script src="/js/app.min.js"></script>')) // 替换
|
||||
.pipe(replace(/<!-- pro_markdown_js -->/, '<script src="/js/markdown.min.js"></script>')) // 替换
|
||||
.pipe(replace(/<!-- pro_tinymce_init_js -->/, "var tinyMCEPreInit = {base: '/public/tinymce', suffix: '.min'};")) // 替换
|
||||
.pipe(replace(/plugins\/main.js/, "plugins/main.min.js")) // 替换
|
||||
// 连续两个空行换成一个空行, 没用
|
||||
.pipe(replace(/\n\n/g, '\n'))
|
||||
.pipe(replace(/\n\n/g, '\n'))
|
||||
.pipe(replace('console.log(o);', ''))
|
||||
// .pipe(minifyHtml()) // 不行, 压缩后golang报错
|
||||
.pipe(rename('note.html'))
|
||||
.pipe(gulp.dest(noteProBase));
|
||||
});
|
||||
|
||||
// Get used keys
|
||||
// 只获取需要js i18n的key
|
||||
var path = require('path');
|
||||
gulp.task('i18n', function() {
|
||||
var keys = {};
|
||||
var reg = /getMsg\(["']+(.+?)["']+/g;
|
||||
function getKey(data) {
|
||||
while(ret = reg.exec(data)) {
|
||||
keys[ret[1]] = 1;
|
||||
}
|
||||
}
|
||||
// 先获取需要的key
|
||||
function ls(ff) {
|
||||
var files = fs.readdirSync(ff);
|
||||
for(fn in files) {
|
||||
var fname = ff + path.sep + files[fn];
|
||||
var stat = fs.lstatSync(fname);
|
||||
if(stat.isDirectory() == true) {
|
||||
ls(fname);
|
||||
}
|
||||
else {
|
||||
if ((fname.indexOf('.html') > 0 || fname.indexOf('.js') > 0)) {
|
||||
// console.log(fname);
|
||||
// if (fname.indexOf('min.js') < 0) {
|
||||
var data = fs.readFileSync(fname, "utf-8");
|
||||
// 得到getMsg里的key
|
||||
getKey(data);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('parsing used keys');
|
||||
|
||||
ls(base + '/admin');
|
||||
ls(base + '/blog');
|
||||
ls(base + '/dist');
|
||||
ls(base + '/js');
|
||||
ls(base + '/album');
|
||||
ls(base + '/libs');
|
||||
ls(base + '/member');
|
||||
ls(base + '/tinymce');
|
||||
|
||||
console.log('parsed');
|
||||
|
||||
// msg.zh
|
||||
function getAllMsgs(fname) {
|
||||
var msg = {};
|
||||
|
||||
var data = fs.readFileSync(fname, "utf-8");
|
||||
var lines = data.split('\n');
|
||||
for (var i = 0; i < lines.length; ++i) {
|
||||
var line = lines[i];
|
||||
// 忽略注释
|
||||
if (line[0] == '#' || line[1] == '#') {
|
||||
continue;
|
||||
}
|
||||
var lineArr = line.split('=');
|
||||
if (lineArr.length >= 2) {
|
||||
var key = lineArr[0];
|
||||
lineArr.shift();
|
||||
msg[key] = lineArr.join('=');
|
||||
// msg[lineArr[0]] = lineArr[1];
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
// msg.zh, msg.js
|
||||
function genI18nJsFile(fromFilename, keys) {
|
||||
var msgs = getAllMsgs(leanoteBase + '/messages/' + fromFilename);
|
||||
var toFilename = fromFilename + '.js';
|
||||
var toMsgs = {};
|
||||
for (var i in msgs) {
|
||||
// 只要需要的
|
||||
if (i in keys) {
|
||||
toMsgs[i] = msgs[i];
|
||||
}
|
||||
}
|
||||
var str = 'var MSG=' + JSON.stringify(toMsgs) + ';';
|
||||
str += 'function getMsg(key, data) {var msg = MSG[key];if(msg) {if(data) {if(!isArray(data)) {data = [data];}' +
|
||||
'for(var i = 0; i < data.length; ++i) {' +
|
||||
'msg = msg.replace("%s", data[i]);' +
|
||||
'}' +
|
||||
'}' +
|
||||
'return msg;' +
|
||||
'}' +
|
||||
'return key;' +
|
||||
'}';
|
||||
// 写入到文件中
|
||||
fs.writeFile(base + '/js/i18n/' + toFilename, str);
|
||||
}
|
||||
|
||||
genI18nJsFile('msg.zh', keys);
|
||||
genI18nJsFile('msg.en', keys);
|
||||
genI18nJsFile('msg.fr', keys);
|
||||
genI18nJsFile('blog.zh', keys);
|
||||
genI18nJsFile('blog.en', keys);
|
||||
genI18nJsFile('blog.fr', keys);
|
||||
});
|
||||
|
||||
|
||||
// 合并album需要的js
|
||||
gulp.task('concatAlbumJs', function() {
|
||||
/*
|
||||
gulp.src(base + '/album/js/main.js')
|
||||
.pipe(uglify()) // 压缩
|
||||
.pipe(rename({suffix: '.min'}))
|
||||
.pipe(gulp.dest(base + '/album/js/'));
|
||||
*/
|
||||
|
||||
gulp.src(base + '/album/css/style.css')
|
||||
.pipe(rename({suffix: '-min'}))
|
||||
.pipe(minifycss())
|
||||
.pipe(gulp.dest(base + '/album/css'));
|
||||
|
||||
var jss = [
|
||||
'js/jquery-1.9.0.min.js',
|
||||
'js/bootstrap-min.js',
|
||||
'js/plugins/libs-min/fileupload.js',
|
||||
'js/jquery.pagination.js',
|
||||
'album/js/main.js',
|
||||
];
|
||||
|
||||
for(var i in jss) {
|
||||
jss[i] = base + '/' + jss[i];
|
||||
}
|
||||
|
||||
return gulp
|
||||
.src(jss)
|
||||
.pipe(uglify()) // 压缩
|
||||
.pipe(concat('main.all.js'))
|
||||
.pipe(gulp.dest(base + '/album/js'));
|
||||
});
|
||||
|
||||
// plugins压缩
|
||||
gulp.task('plugins', function() {
|
||||
gulp.src(base + '/js/plugins/libs/*.js')
|
||||
.pipe(uglify()) // 压缩
|
||||
// .pipe(concat('main.min.js'))
|
||||
.pipe(gulp.dest(base + '/js/plugins/libs-min'));
|
||||
|
||||
|
||||
// 所有js合并成一个
|
||||
var jss = [
|
||||
'note_info',
|
||||
'tips',
|
||||
'history',
|
||||
'attachment_upload',
|
||||
'editor_drop_paste',
|
||||
'main'
|
||||
];
|
||||
|
||||
for(var i in jss) {
|
||||
jss[i] = base + '/js/plugins/' + jss[i] + '.js';
|
||||
}
|
||||
|
||||
gulp.src(jss)
|
||||
.pipe(uglify()) // 压缩
|
||||
.pipe(concat('main.min.js'))
|
||||
.pipe(gulp.dest(base + '/js/plugins'));
|
||||
});
|
||||
|
||||
|
||||
// mincss
|
||||
var minifycss = require('gulp-minify-css');
|
||||
gulp.task('minifycss', function() {
|
||||
gulp.src(base + '/css/bootstrap.css')
|
||||
.pipe(rename({suffix: '-min'}))
|
||||
.pipe(minifycss())
|
||||
.pipe(gulp.dest(base + '/css'));
|
||||
|
||||
gulp.src(base + '/css/font-awesome-4.2.0/css/font-awesome.css')
|
||||
.pipe(rename({suffix: '-min'}))
|
||||
.pipe(minifycss())
|
||||
.pipe(gulp.dest(base + '/css/font-awesome-4.2.0/css'));
|
||||
|
||||
gulp.src(base + '/css/zTreeStyle/zTreeStyle.css')
|
||||
.pipe(rename({suffix: '-min'}))
|
||||
.pipe(minifycss())
|
||||
.pipe(gulp.dest(base + '/css/zTreeStyle'));
|
||||
|
||||
gulp.src(base + '/dist/themes/default.css')
|
||||
.pipe(rename({suffix: '-min'}))
|
||||
.pipe(minifycss())
|
||||
.pipe(gulp.dest(base + '/dist/themes'));
|
||||
|
||||
gulp.src(base + '/js/contextmenu/css/contextmenu.css')
|
||||
.pipe(rename({suffix: '-min'}))
|
||||
.pipe(minifycss())
|
||||
.pipe(gulp.dest(base + '/js/contextmenu/css'));
|
||||
|
||||
// theme
|
||||
// 用codekit
|
||||
var as = ['default', 'simple', 'writting', /*'writting-overwrite', */ 'mobile'];
|
||||
/*
|
||||
for(var i = 0; i < as.length; ++i) {
|
||||
gulp.src(base + '/css/theme/' + as[i] + '.css')
|
||||
.pipe(minifycss())
|
||||
.pipe(gulp.dest(base + '/css/theme'));
|
||||
}
|
||||
*/
|
||||
});
|
||||
|
||||
// tinymce
|
||||
// !! You must has tinymce_dev on public/
|
||||
var tinymceBase = base + '/tinymce_dev';
|
||||
gulp.task('tinymce', function() {
|
||||
// 先清理
|
||||
fs.unlink(tinymceBase + '/js/tinymce/tinymce.dev.js');
|
||||
fs.unlink(tinymceBase + '/js/tinymce/tinymce.jquery.dev.js');
|
||||
fs.unlink(tinymceBase + '/js/tinymce/tinymce.full.js');
|
||||
fs.unlink(tinymceBase + '/js/tinymce/tinymce.full.min.js');
|
||||
|
||||
var cp = require('child_process');
|
||||
|
||||
var bundleCmd = 'grunt bundle --themes leanote --plugins autolink,link,leaui_image,leaui_mind,lists,hr,paste,searchreplace,leanote_nav,leanote_code,tabfocus,table,directionality,textcolor';
|
||||
// build
|
||||
cp.exec('grunt minify', {cwd: tinymceBase}, function(err, stdout, stderr) {
|
||||
console.log('stdout: ' + stdout);
|
||||
console.log('stderr: ' + stderr);
|
||||
|
||||
// 将所有都合并成一起
|
||||
cp.exec(bundleCmd, {cwd: tinymceBase}, function(err, stdout, stderr) {
|
||||
console.log('stdout: ' + stdout);
|
||||
console.log('stderr: ' + stderr);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 合并css, 无用
|
||||
// Deprecated
|
||||
gulp.task('concatCss', function() {
|
||||
return gulp
|
||||
.src([markdownRaw + '/css/default.css', markdownRaw + '/css/md.css'])
|
||||
.pipe(concat('all.css'))
|
||||
.pipe(gulp.dest(markdownMin));
|
||||
});
|
||||
|
||||
gulp.task('concat', ['concatDepJs', 'concatAppJs', 'concatMarkdownJs']);
|
||||
gulp.task('html', ['devToProHtml']);
|
||||
gulp.task('default', ['concat', 'plugins', 'minifycss', 'i18n', 'concatAlbumJs', 'html']);
|
||||
6
LICENSE
6
LICENSE
@@ -1,6 +1,6 @@
|
||||
leanote - you own cloud note!
|
||||
LEANOTE - NOT JUST A NOTEPAD!
|
||||
|
||||
Copyright 2014 by the contributors
|
||||
Copyright 2014-2015 by the contributors
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -12,7 +12,7 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
leanote is licensed under the GPL v2.
|
||||
Leanote is licensed under the GPL v2.
|
||||
|
||||
life(life@leanote.com, lifephp@gmail.com)
|
||||
|
||||
|
||||
16
README.md
16
README.md
@@ -1,6 +1,7 @@
|
||||
|
||||
# Leanote
|
||||
|
||||
[](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://travis-ci.org/leanote/leanote)
|
||||
## 1. Introduction
|
||||
|
||||
Leanote, not just a notepad!
|
||||
@@ -54,15 +55,18 @@ Support us, [donate us](http://leanote.org/#donate). And thanks [donators](http:
|
||||
|
||||
## 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), development phase
|
||||
* [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/Dminter/leanote-android-client), development phase
|
||||
|
||||
And also, you are welcome to join us.
|
||||
|
||||
## 9. Discussion
|
||||
* [leanote bbs](http://bbs.leanote.com)
|
||||
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)
|
||||
* QQ Group: 158716820
|
||||
## 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: 158716820, 256076853
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
[中文](README_zh.md)
|
||||
|
||||
15
README_zh.md
15
README_zh.md
@@ -1,5 +1,7 @@
|
||||
# Leanote产品
|
||||
# Leanote
|
||||
|
||||
[](https://gitter.im/leanote/leanote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://travis-ci.org/leanote/leanote)
|
||||
## 1. 介绍
|
||||
|
||||
Leanote, 不只是笔记!
|
||||
@@ -56,15 +58,16 @@ Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善lea
|
||||
|
||||
## 9. 其它相关项目
|
||||
* [Leanote Desktop App](https://github.com/leanote/desktop-app), [下载地址](http://app.leanote.com)
|
||||
* [Leanote IOS](https://github.com/leanote/leanote-ios), 开发阶段
|
||||
* [Leanote IOS](https://github.com/leanote/leanote-ios), [从App Store下载](https://itunes.apple.com/zn/app/leanote/id1022302858?mt=8)
|
||||
* [Leanote Android](https://github.com/Dminter/leanote-android-client), 开发阶段
|
||||
|
||||
同样, 欢迎加入我们!
|
||||
欢迎加入我们!
|
||||
|
||||
## 讨论
|
||||
## 联系&加入我们
|
||||
* Email: leanote@leanote.com
|
||||
* [leanote 社区](http://bbs.leanote.com)
|
||||
* QQ群: 158716820
|
||||
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)
|
||||
* QQ群: 158716820, 256076853
|
||||
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)
|
||||
|
||||
----------------------------------------------------------------
|
||||
[English](README.md)
|
||||
|
||||
19
app/cmd/main.go
Normal file
19
app/cmd/main.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/revel/cmd/harness"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// go run main.go
|
||||
// 生成routes.go, main.go
|
||||
revel.Init("", "github.com/leanote/leanote", "")
|
||||
_, err := harness.Build() // ok, err
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Ok")
|
||||
// panicOnError(reverr, "Failed to build")
|
||||
}
|
||||
@@ -2,11 +2,11 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "io/ioutil"
|
||||
)
|
||||
|
||||
// Album controller
|
||||
@@ -14,12 +14,17 @@ type Album struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// 图片管理, iframe
|
||||
func (c Album) Index() revel.Result {
|
||||
c.SetLocale();
|
||||
return c.RenderTemplate("album/index.html")
|
||||
}
|
||||
|
||||
// all albums by userId
|
||||
func (c Album) GetAlbums() revel.Result {
|
||||
re := albumService.GetAlbums(c.GetUserId())
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
func (c Album) DeleteAlbum(albumId string) revel.Result {
|
||||
re, msg := albumService.DeleteAlbum(c.GetUserId(), albumId)
|
||||
return c.RenderJson(info.Re{Ok: re, Msg: msg})
|
||||
@@ -29,12 +34,12 @@ func (c Album) DeleteAlbum(albumId string) revel.Result {
|
||||
func (c Album) AddAlbum(name string) revel.Result {
|
||||
album := info.Album{
|
||||
AlbumId: bson.NewObjectId(),
|
||||
Name: name,
|
||||
Seq: -1,
|
||||
UserId: c.GetObjectUserId()}
|
||||
Name: name,
|
||||
Seq: -1,
|
||||
UserId: c.GetObjectUserId()}
|
||||
re := albumService.AddAlbum(album)
|
||||
|
||||
if(re) {
|
||||
if re {
|
||||
return c.RenderJson(album)
|
||||
} else {
|
||||
return c.RenderJson(false)
|
||||
@@ -44,4 +49,4 @@ func (c Album) AddAlbum(name string) revel.Result {
|
||||
// update alnum name
|
||||
func (c Album) UpdateAlbum(albumId, name string) revel.Result {
|
||||
return c.RenderJson(albumService.UpdateAlbum(albumId, c.GetUserId(), name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,16 +40,16 @@ func (c Auth) Login(email, from string) revel.Result {
|
||||
func (c Auth) doLogin(email, pwd string) revel.Result {
|
||||
sessionId := c.Session.Id()
|
||||
var msg = ""
|
||||
|
||||
userInfo := authService.Login(email, pwd)
|
||||
if userInfo.Email != "" {
|
||||
|
||||
userInfo, err := authService.Login(email, pwd)
|
||||
if err != nil {
|
||||
// 登录错误, 则错误次数++
|
||||
msg = "wrongUsernameOrPassword"
|
||||
} else {
|
||||
c.SetSession(userInfo)
|
||||
sessionService.ClearLoginTimes(sessionId)
|
||||
return c.RenderJson(info.Re{Ok: true})
|
||||
} else {
|
||||
// 登录错误, 则错误次数++
|
||||
msg = "wrongUsernameOrPassword"
|
||||
}
|
||||
}
|
||||
|
||||
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId) , Msg: c.Message(msg)})
|
||||
}
|
||||
@@ -61,16 +61,16 @@ func (c Auth) DoLogin(email, pwd string, captcha string) revel.Result {
|
||||
if sessionService.LoginTimesIsOver(sessionId) && sessionService.GetCaptcha(sessionId) != captcha {
|
||||
msg = "captchaError"
|
||||
} else {
|
||||
userInfo := authService.Login(email, pwd)
|
||||
if userInfo.Email != "" {
|
||||
c.SetSession(userInfo)
|
||||
sessionService.ClearLoginTimes(sessionId)
|
||||
return c.RenderJson(info.Re{Ok: true})
|
||||
} else {
|
||||
userInfo, err := authService.Login(email, pwd)
|
||||
if err != nil {
|
||||
// 登录错误, 则错误次数++
|
||||
msg = "wrongUsernameOrPassword"
|
||||
sessionService.IncrLoginTimes(sessionId)
|
||||
}
|
||||
} else {
|
||||
c.SetSession(userInfo)
|
||||
sessionService.ClearLoginTimes(sessionId)
|
||||
return c.RenderJson(info.Re{Ok: true})
|
||||
}
|
||||
}
|
||||
|
||||
return c.RenderJson(info.Re{Ok: false, Item: sessionService.LoginTimesIsOver(sessionId) , Msg: c.Message(msg)})
|
||||
@@ -85,10 +85,13 @@ func (c Auth) Logout() revel.Result {
|
||||
|
||||
// 体验一下
|
||||
func (c Auth) Demo() revel.Result {
|
||||
email := configService.GetGlobalStringConfig("demoPassword")
|
||||
pwd := configService.GetGlobalStringConfig("demoPassword");
|
||||
userInfo := authService.Login(email, pwd)
|
||||
if userInfo.Email != "" {
|
||||
email := configService.GetGlobalStringConfig("demoUsername")
|
||||
pwd := configService.GetGlobalStringConfig("demoPassword")
|
||||
|
||||
userInfo, err := authService.Login(email, pwd)
|
||||
if err != nil {
|
||||
return c.RenderJson(info.Re{Ok: false})
|
||||
} else {
|
||||
c.SetSession(userInfo)
|
||||
return c.Redirect("/note")
|
||||
}
|
||||
|
||||
@@ -84,6 +84,13 @@ func (c BaseController) GetUserInfo() info.User {
|
||||
return info.User{}
|
||||
}
|
||||
|
||||
func (c BaseController) GetUserAndBlogUrl() info.UserAndBlogUrl {
|
||||
if userId, ok := c.Session["UserId"]; ok && userId != "" {
|
||||
return userService.GetUserAndBlogUrl(userId);
|
||||
}
|
||||
return info.UserAndBlogUrl{}
|
||||
}
|
||||
|
||||
// 这里的session都是cookie中的, 与数据库session无关
|
||||
func (c BaseController) GetSession(key string) string {
|
||||
v, ok := c.Session[key]
|
||||
|
||||
@@ -265,10 +265,6 @@ func (c Blog) getCates(userBlog info.UserBlog) {
|
||||
}
|
||||
}
|
||||
|
||||
Log("cates")
|
||||
LogJ(cates)
|
||||
LogJ(catesTree);
|
||||
|
||||
c.RenderArgs["cates"] = cates
|
||||
c.RenderArgs["catesTree"] = catesTree
|
||||
}
|
||||
@@ -352,9 +348,10 @@ func (c Blog) blogCommon(userId string, userBlog info.UserBlog, userInfo info.Us
|
||||
// 得到主题信息
|
||||
themeInfo := themeService.GetThemeInfo(userBlog.ThemeId.Hex(), userBlog.Style)
|
||||
c.RenderArgs["themeInfo"] = themeInfo
|
||||
Log(">>")
|
||||
Log(userBlog.Style)
|
||||
Log(userBlog.ThemeId.Hex())
|
||||
|
||||
// Log(">>")
|
||||
// Log(userBlog.Style)
|
||||
// Log(userBlog.ThemeId.Hex())
|
||||
|
||||
return true, userBlog
|
||||
}
|
||||
|
||||
@@ -69,13 +69,13 @@ func (c File) UploadAvatar() revel.Result {
|
||||
c.UpdateSession("Logo", re.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// leaui image plugin upload image
|
||||
func (c File) UploadImageLeaui(albumId string) revel.Result {
|
||||
re := c.uploadImage("", albumId);
|
||||
re := c.uploadImage("", albumId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
@@ -84,74 +84,80 @@ func (c File) UploadImageLeaui(albumId string) revel.Result {
|
||||
func (c File) uploadImage(from, albumId string) (re info.Re) {
|
||||
var fileUrlPath = ""
|
||||
var fileId = ""
|
||||
var resultCode = 0 // 1表示正常
|
||||
var resultCode = 0 // 1表示正常
|
||||
var resultMsg = "error" // 错误信息
|
||||
var Ok = false
|
||||
|
||||
|
||||
defer func() {
|
||||
re.Id = fileId // 只是id, 没有其它信息
|
||||
re.Code = resultCode
|
||||
re.Msg = resultMsg
|
||||
re.Ok = Ok
|
||||
}()
|
||||
|
||||
|
||||
file, handel, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
return re
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 生成上传路径
|
||||
newGuid := NewGuid()
|
||||
|
||||
userId := c.GetUserId()
|
||||
|
||||
if(from == "logo" || from == "blogLogo") {
|
||||
fileUrlPath = "public/upload/" + c.GetUserId() + "/images/logo"
|
||||
fileUrlPath = "public/upload/" + Digest3(userId) + "/" + userId + "/images/logo"
|
||||
} else {
|
||||
fileUrlPath = "files/" + c.GetUserId() + "/images"
|
||||
fileUrlPath = "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
|
||||
}
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return re
|
||||
}
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
|
||||
var ext string;
|
||||
|
||||
var ext string
|
||||
if from == "pasteImage" {
|
||||
ext = ".png"; // TODO 可能不是png类型
|
||||
ext = ".png" // TODO 可能不是png类型
|
||||
} else {
|
||||
_, ext = SplitFilename(filename)
|
||||
if(ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg") {
|
||||
if ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {
|
||||
resultMsg = "Please upload image"
|
||||
return re
|
||||
}
|
||||
}
|
||||
|
||||
filename = NewGuid() + ext
|
||||
filename = newGuid + ext
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
LogJ(err)
|
||||
return re
|
||||
}
|
||||
|
||||
|
||||
var maxFileSize float64
|
||||
if(from == "logo") {
|
||||
maxFileSize = configService.GetUploadSize("uploadAvatarSize");
|
||||
if from == "logo" {
|
||||
maxFileSize = configService.GetUploadSize("uploadAvatarSize")
|
||||
} else if from == "blogLogo" {
|
||||
maxFileSize = configService.GetUploadSize("uploadBlogLogoSize");
|
||||
maxFileSize = configService.GetUploadSize("uploadBlogLogoSize")
|
||||
} else {
|
||||
maxFileSize = configService.GetUploadSize("uploadImageSize");
|
||||
maxFileSize = configService.GetUploadSize("uploadImageSize")
|
||||
}
|
||||
if maxFileSize <= 0 {
|
||||
maxFileSize = 1000
|
||||
}
|
||||
|
||||
|
||||
// > 2M?
|
||||
if(float64(len(data)) > maxFileSize * float64(1024*1024)) {
|
||||
if float64(len(data)) > maxFileSize*float64(1024*1024) {
|
||||
resultCode = 0
|
||||
resultMsg = fmt.Sprintf("The file Size is bigger than %vM", maxFileSize)
|
||||
return re
|
||||
}
|
||||
|
||||
toPath := dir + "/" + filename;
|
||||
|
||||
toPath := dir + "/" + filename
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
LogJ(err)
|
||||
@@ -164,26 +170,27 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
|
||||
fileUrlPath += "/" + filename
|
||||
resultCode = 1
|
||||
resultMsg = "Upload Success!"
|
||||
|
||||
|
||||
// File
|
||||
fileInfo := info.File{Name: filename,
|
||||
Title: handel.Filename,
|
||||
Path: fileUrlPath,
|
||||
Size: filesize}
|
||||
|
||||
id := bson.NewObjectId();
|
||||
Path: fileUrlPath,
|
||||
Size: filesize}
|
||||
|
||||
id := bson.NewObjectId()
|
||||
fileInfo.FileId = id
|
||||
fileId = id.Hex()
|
||||
if(from == "logo" || from == "blogLogo") {
|
||||
fileId = "public/upload/" + c.GetUserId() + "/images/logo/" + filename
|
||||
}
|
||||
|
||||
if(from == "logo" || from == "blogLogo") {
|
||||
fileId = fileUrlPath
|
||||
}
|
||||
|
||||
Ok, resultMsg = fileService.AddImage(fileInfo, albumId, c.GetUserId(), from == "" || from == "pasteImage")
|
||||
resultMsg = c.Message(resultMsg)
|
||||
|
||||
fileInfo.Path = ""; // 不要返回
|
||||
|
||||
fileInfo.Path = "" // 不要返回
|
||||
re.Item = fileInfo
|
||||
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
@@ -204,6 +211,7 @@ func (c File) DeleteImage(fileId string) revel.Result {
|
||||
re.Ok, re.Msg = fileService.DeleteImage(c.GetUserId(), fileId)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
//-----------
|
||||
|
||||
// 输出image
|
||||
@@ -226,35 +234,39 @@ func (c File) CopyImage(userId, fileId, toUserId string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// 复制外网的图片, 成公共图片 放在/upload下
|
||||
// 复制外网的图片
|
||||
// 都要好好的计算大小
|
||||
func (c File) CopyHttpImage(src string) revel.Result {
|
||||
re := info.NewRe()
|
||||
fileUrlPath := "upload/" + c.GetUserId() + "/images"
|
||||
dir := revel.BasePath + "/public/" + fileUrlPath
|
||||
|
||||
// 生成上传路径
|
||||
newGuid := NewGuid()
|
||||
userId := c.GetUserId()
|
||||
fileUrlPath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
filesize, filename, _, ok := netutil.WriteUrl(src, dir)
|
||||
|
||||
|
||||
if !ok {
|
||||
re.Msg = "copy error"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
// File
|
||||
fileInfo := info.File{Name: filename,
|
||||
Title: filename,
|
||||
Path: fileUrlPath + "/" + filename,
|
||||
Size: filesize}
|
||||
|
||||
id := bson.NewObjectId();
|
||||
Path: fileUrlPath + "/" + filename,
|
||||
Size: filesize}
|
||||
|
||||
id := bson.NewObjectId()
|
||||
fileInfo.FileId = id
|
||||
|
||||
|
||||
re.Id = id.Hex()
|
||||
re.Item = fileInfo.Path
|
||||
// re.Item = fileInfo.Path
|
||||
re.Ok, re.Msg = fileService.AddImage(fileInfo, "", c.GetUserId(), true)
|
||||
|
||||
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,12 @@ func (c Index) Default() revel.Result {
|
||||
}
|
||||
// leanote展示页, 没有登录的, 或已登录明确要进该页的
|
||||
func (c Index) Index() revel.Result {
|
||||
|
||||
c.SetUserInfo()
|
||||
c.RenderArgs["title"] = "leanote"
|
||||
c.RenderArgs["openRegister"] = configService.GlobalStringConfigs["openRegister"]
|
||||
lang := c.SetLocale()
|
||||
c.SetLocale()
|
||||
|
||||
return c.RenderTemplate("home/index_" + lang + ".html");
|
||||
return c.RenderTemplate("home/index.html");
|
||||
}
|
||||
|
||||
// 建议
|
||||
|
||||
@@ -2,17 +2,20 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "encoding/json"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "encoding/json"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
// "time"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
// "bytes"
|
||||
// "os"
|
||||
"time"
|
||||
"fmt"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "bytes"
|
||||
// "os"
|
||||
)
|
||||
|
||||
type Note struct {
|
||||
@@ -22,10 +25,10 @@ type Note struct {
|
||||
// 笔记首页, 判断是否已登录
|
||||
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
|
||||
// 否则, 转向登录页面
|
||||
func (c Note) Index(noteId string) revel.Result {
|
||||
func (c Note) Index(noteId, online string) revel.Result {
|
||||
c.SetLocale()
|
||||
userInfo := c.GetUserInfo()
|
||||
|
||||
userInfo := c.GetUserAndBlogUrl()
|
||||
|
||||
userId := userInfo.UserId.Hex()
|
||||
|
||||
// 没有登录
|
||||
@@ -125,8 +128,9 @@ func (c Note) Index(noteId string) revel.Result {
|
||||
|
||||
c.RenderArgs["globalConfigs"] = configService.GetGlobalConfigForUser()
|
||||
|
||||
// return c.RenderTemplate("note/note.html")
|
||||
if isDev, _ := revel.Config.Bool("mode.dev"); isDev {
|
||||
// return c.RenderTemplate("note/note.html")
|
||||
|
||||
if isDev, _ := revel.Config.Bool("mode.dev"); isDev && online == "" {
|
||||
return c.RenderTemplate("note/note-dev.html")
|
||||
} else {
|
||||
return c.RenderTemplate("note/note.html")
|
||||
@@ -137,13 +141,13 @@ func (c Note) Index(noteId string) revel.Result {
|
||||
// 已登录, 得到用户基本信息(notebook, shareNotebook), 跳转到index.html中
|
||||
// 否则, 转向登录页面
|
||||
func (c Note) ListNotes(notebookId string) revel.Result {
|
||||
_, notes := noteService.ListNotes(c.GetUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false);
|
||||
_, notes := noteService.ListNotes(c.GetUserId(), notebookId, false, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(notes)
|
||||
}
|
||||
|
||||
// 得到trash
|
||||
func (c Note) ListTrashNotes() revel.Result {
|
||||
_, notes := noteService.ListNotes(c.GetUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false);
|
||||
_, notes := noteService.ListNotes(c.GetUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(notes)
|
||||
}
|
||||
|
||||
@@ -292,6 +296,165 @@ func (c Note) SearchNoteByTags(tags []string) revel.Result {
|
||||
return c.RenderJson(blogs)
|
||||
}
|
||||
|
||||
// 生成PDF
|
||||
func (c Note) ToPdf(noteId, appKey string) revel.Result {
|
||||
// 虽然传了cookie但是这里还是不能得到userId, 所以还是通过appKey来验证之
|
||||
appKeyTrue, _ := revel.Config.String("app.secret")
|
||||
if appKeyTrue != appKey {
|
||||
return c.RenderText("error")
|
||||
}
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
return c.RenderText("error")
|
||||
}
|
||||
|
||||
noteUserId := note.UserId.Hex()
|
||||
content := noteService.GetNoteContent(noteId, noteUserId)
|
||||
userInfo := userService.GetUserInfo(noteUserId)
|
||||
|
||||
//------------------
|
||||
// 将content的图片转换为base64
|
||||
contentStr := content.Content
|
||||
|
||||
siteUrlPattern := configService.GetSiteUrl()
|
||||
if strings.Contains(siteUrlPattern, "https") {
|
||||
siteUrlPattern = strings.Replace(siteUrlPattern, "https", "https*", 1)
|
||||
} else {
|
||||
siteUrlPattern = strings.Replace(siteUrlPattern, "http", "https*", 1)
|
||||
}
|
||||
|
||||
regImage, _ := regexp.Compile(`<img .*?(src=('|")` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})("|'))`)
|
||||
|
||||
findsImage := regImage.FindAllStringSubmatch(contentStr, -1) // 查找所有的
|
||||
// [<img src="http://leanote.com/api/getImage?fileId=3354672e8d38f411286b000069" alt="" width="692" height="302" data-mce-src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " file/outputImage 54672e8d38f411286b000069 "]
|
||||
for _, eachFind := range findsImage {
|
||||
if len(eachFind) == 6 {
|
||||
fileId := eachFind[4]
|
||||
// 得到base64编码文件
|
||||
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
|
||||
if fileBase64 == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 1
|
||||
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
|
||||
allFixed := strings.Replace(eachFind[0], eachFind[1], "src=\""+fileBase64+"\"", -1)
|
||||
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
|
||||
}
|
||||
}
|
||||
|
||||
// markdown
|
||||
if note.IsMarkdown {
|
||||
// 
|
||||
regImageMarkdown, _ := regexp.Compile(`!\[.*?\]\(` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})\)`)
|
||||
findsImageMarkdown := regImageMarkdown.FindAllStringSubmatch(contentStr, -1) // 查找所有的
|
||||
for _, eachFind := range findsImageMarkdown {
|
||||
if len(eachFind) == 3 {
|
||||
fileId := eachFind[2]
|
||||
// 得到base64编码文件
|
||||
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
|
||||
if fileBase64 == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 1
|
||||
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
|
||||
allFixed := ""
|
||||
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if note.Tags != nil && len(note.Tags) > 0 && note.Tags[0] != "" {
|
||||
} else {
|
||||
note.Tags = nil
|
||||
}
|
||||
c.RenderArgs["blog"] = note
|
||||
c.RenderArgs["content"] = contentStr
|
||||
c.RenderArgs["userInfo"] = userInfo
|
||||
userBlog := blogService.GetUserBlog(noteUserId)
|
||||
c.RenderArgs["userBlog"] = userBlog
|
||||
|
||||
return c.RenderTemplate("file/pdf.html")
|
||||
}
|
||||
|
||||
// 导出成PDF
|
||||
func (c Note) ExportPdf(noteId string) revel.Result {
|
||||
re := info.NewRe()
|
||||
userId := c.GetUserId()
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
re.Msg = "No Note"
|
||||
return c.RenderText("error")
|
||||
}
|
||||
|
||||
noteUserId := note.UserId.Hex()
|
||||
// 是否有权限
|
||||
if noteUserId != userId {
|
||||
// 是否是有权限协作的
|
||||
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
|
||||
re.Msg = "No Perm"
|
||||
return c.RenderText("error")
|
||||
}
|
||||
}
|
||||
|
||||
// path 判断是否需要重新生成之
|
||||
guid := NewGuid()
|
||||
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
if !MkdirAll(dir) {
|
||||
return c.RenderText("error, no dir")
|
||||
}
|
||||
filename := guid + ".pdf"
|
||||
path := dir + "/" + filename
|
||||
|
||||
// leanote.com的secret
|
||||
appKey, _ := revel.Config.String("app.secretLeanote")
|
||||
if appKey == "" {
|
||||
appKey, _ = revel.Config.String("app.secret")
|
||||
}
|
||||
|
||||
// 生成之
|
||||
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
|
||||
// 默认路径
|
||||
if binPath == "" {
|
||||
binPath = "/usr/local/bin/wkhtmltopdf"
|
||||
}
|
||||
|
||||
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
|
||||
// cc := binPath + " --no-stop-slow-scripts --javascript-delay 10000 \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
// cc := binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
// 等待--window-status为done的状态
|
||||
// http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html
|
||||
// wkhtmltopdf参数大全
|
||||
var cc string
|
||||
if note.IsMarkdown {
|
||||
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
} else {
|
||||
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
}
|
||||
|
||||
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))
|
||||
}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))
|
||||
}
|
||||
// http://stackoverflow.com/questions/8588818/chrome-pdf-display-duplicate-headers-received-from-the-server
|
||||
// filenameReturn = strings.Replace(filenameReturn, ",", "-", -1)
|
||||
filenameReturn := note.Title
|
||||
filenameReturn = FixFilename(filenameReturn)
|
||||
if filenameReturn == "" {
|
||||
filenameReturn = "Untitled.pdf"
|
||||
} else {
|
||||
filenameReturn += ".pdf"
|
||||
}
|
||||
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
|
||||
// 设置/取消Blog; 置顶
|
||||
func (c Note) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
|
||||
re := noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
|
||||
|
||||
@@ -29,8 +29,8 @@ func (c User) Account(tab int) revel.Result {
|
||||
|
||||
// 修改用户名, 需要重置session
|
||||
func (c User) UpdateUsername(username string) revel.Result {
|
||||
re := info.NewRe();
|
||||
if(c.GetUsername() == "demo") {
|
||||
re := info.NewRe()
|
||||
if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
@@ -48,8 +48,8 @@ func (c User) UpdateUsername(username string) revel.Result {
|
||||
|
||||
// 修改密码
|
||||
func (c User) UpdatePwd(oldPwd, pwd string) revel.Result {
|
||||
re := info.NewRe();
|
||||
if(c.GetUsername() == "demo") {
|
||||
re := info.NewRe()
|
||||
if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderRe(re);
|
||||
}
|
||||
@@ -94,9 +94,9 @@ func (c User) ReSendActiveEmail() revel.Result {
|
||||
}
|
||||
|
||||
// 修改Email发送激活邮箱
|
||||
func (c User) UpdateEmailSendActiveEmail(email string) revel.Result {
|
||||
func (c User) updateEmailSendActiveEmail(email, pwd string) revel.Result {
|
||||
re := info.NewRe()
|
||||
if(c.GetUsername() == "demo") {
|
||||
if c.GetUserId() == configService.GetGlobalStringConfig("demoUserId") {
|
||||
re.Msg = "cannotUpdateDemo"
|
||||
return c.RenderJson(re);
|
||||
}
|
||||
@@ -193,4 +193,4 @@ func (c User) UpdateLeftIsMin(leftIsMin bool) revel.Result {
|
||||
}
|
||||
}
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,10 @@ func (c AdminEmail) Demo() revel.Result {
|
||||
func (c AdminEmail) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
userInfo := authService.Login(demoUsername, demoPassword)
|
||||
userInfo, err := authService.Login(demoUsername, demoPassword)
|
||||
if err != nil {
|
||||
return c.RenderJson(info.Re{Ok: false})
|
||||
}
|
||||
if userInfo.UserId == "" {
|
||||
re.Msg = "The User is Not Exists";
|
||||
return c.RenderJson(re)
|
||||
|
||||
@@ -56,7 +56,11 @@ func (c AdminSetting) Demo() revel.Result {
|
||||
func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
re := info.NewRe()
|
||||
|
||||
userInfo := authService.Login(demoUsername, demoPassword)
|
||||
userInfo, err := authService.Login(demoUsername, demoPassword)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return c.RenderJson(info.Re{Ok: false})
|
||||
}
|
||||
if userInfo.UserId == "" {
|
||||
re.Msg = "The User is Not Exists";
|
||||
return c.RenderJson(re)
|
||||
@@ -69,15 +73,9 @@ func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
// ToImage
|
||||
// 长微博的bin路径phantomJs
|
||||
func (c AdminSetting) ToImage() revel.Result {
|
||||
c.RenderArgs["toImageBinPath"] = configService.GetGlobalStringConfig("toImageBinPath")
|
||||
return c.RenderTemplate("admin/setting/toImage.html");
|
||||
}
|
||||
func (c AdminSetting) DoToImage(toImageBinPath string) revel.Result {
|
||||
func (c AdminSetting) ExportPdf(path string) revel.Result {
|
||||
re := info.NewRe()
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "toImageBinPath", toImageBinPath)
|
||||
re.Ok = configService.UpdateGlobalStringConfig(c.GetUserId(), "exportPdfBinPath", path)
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ type ApiAuth struct {
|
||||
// 失败返回 {Ok: false, Msg: ""}
|
||||
func (c ApiAuth) Login(email, pwd string) revel.Result {
|
||||
var msg = ""
|
||||
|
||||
userInfo := authService.Login(email, pwd)
|
||||
if userInfo.Email != "" {
|
||||
|
||||
userInfo, err := authService.Login(email, pwd)
|
||||
if err == nil {
|
||||
token := bson.NewObjectId().Hex()
|
||||
sessionService.SetUserId(token, userInfo.UserId.Hex())
|
||||
return c.RenderJson(info.AuthOk{Ok: true, Token: token, UserId: userInfo.UserId, Email: userInfo.Email, Username: userInfo.Username})
|
||||
@@ -66,4 +66,4 @@ func (c ApiAuth) Register(email, pwd string) revel.Result {
|
||||
// 注册
|
||||
re.Ok, re.Msg = authService.Register(email, pwd, "")
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,9 @@ func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg
|
||||
}
|
||||
|
||||
// 生成上传路径
|
||||
filePath := "files/" + userId + "/attachs"
|
||||
newGuid := NewGuid()
|
||||
filePath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/attachs"
|
||||
|
||||
dir := revel.BasePath + "/" + filePath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
@@ -83,7 +85,7 @@ func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg
|
||||
// 生成新的文件名
|
||||
filename := handel.Filename
|
||||
_, ext := SplitFilename(filename) // .doc
|
||||
filename = NewGuid() + ext
|
||||
filename = newGuid + ext
|
||||
toPath := dir + "/" + filename;
|
||||
err = ioutil.WriteFile(toPath, data, 0777)
|
||||
if err != nil {
|
||||
@@ -124,8 +126,12 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
newGuid := NewGuid()
|
||||
// 生成上传路径
|
||||
fileUrlPath := "files/" + c.getUserId() + "/images"
|
||||
userId := c.getUserId()
|
||||
fileUrlPath := "files/" + Digest3(userId) + "/" + userId + "/" + Digest2(newGuid) + "/images"
|
||||
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
@@ -139,7 +145,7 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
|
||||
return
|
||||
}
|
||||
|
||||
filename = NewGuid() + ext
|
||||
filename = newGuid + ext
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"os/exec"
|
||||
"os"
|
||||
// "github.com/leanote/leanote/app/types"
|
||||
// "io/ioutil"
|
||||
// "fmt"
|
||||
@@ -70,6 +72,7 @@ func (c ApiNote) GetNotes(notebookId string) revel.Result {
|
||||
func (c ApiNote) GetTrashNotes() revel.Result {
|
||||
_, notes := noteService.ListNotes(c.getUserId(), "", true, c.GetPage(), pageSize, defaultSortField, false, false)
|
||||
return c.RenderJson(noteService.ToApiNotes(notes))
|
||||
|
||||
}
|
||||
|
||||
// get Note
|
||||
@@ -579,3 +582,83 @@ func (c ApiNote) GetHistories(noteId string) revel.Result {
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
*/
|
||||
|
||||
// 0.2 新增
|
||||
// 导出成PDF
|
||||
// test localhost:9000/api/note/exportPdf?noteId=554f07bf05fcd15fa9000000&token=562211dc99c37ba6a7000001
|
||||
func (c ApiNote) ExportPdf(noteId string) revel.Result {
|
||||
re := info.NewApiRe()
|
||||
userId := c.getUserId()
|
||||
if noteId == "" {
|
||||
re.Msg = "noteNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
note := noteService.GetNoteById(noteId)
|
||||
if note.NoteId == "" {
|
||||
re.Msg = "noteNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
noteUserId := note.UserId.Hex()
|
||||
// 是否有权限
|
||||
if noteUserId != userId {
|
||||
// 是否是有权限协作的
|
||||
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
|
||||
re.Msg = "noteNotExists"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
}
|
||||
|
||||
// path 判断是否需要重新生成之
|
||||
guid := NewGuid()
|
||||
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
|
||||
dir := revel.BasePath + "/" + fileUrlPath
|
||||
if !MkdirAll(dir) {
|
||||
re.Msg = "noDir"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
filename := guid + ".pdf"
|
||||
path := dir + "/" + filename
|
||||
|
||||
appKey, _ := revel.Config.String("app.secretLeanote")
|
||||
if appKey == "" {
|
||||
appKey, _ = revel.Config.String("app.secret")
|
||||
}
|
||||
|
||||
// 生成之
|
||||
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
|
||||
// 默认路径
|
||||
if binPath == "" {
|
||||
binPath = "/usr/local/bin/wkhtmltopdf"
|
||||
}
|
||||
|
||||
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
|
||||
var cc string
|
||||
if(note.IsMarkdown) {
|
||||
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
} else {
|
||||
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
|
||||
}
|
||||
|
||||
cmd := exec.Command("/bin/sh", "-c", cc)
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
re.Msg = "sysError"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
re.Msg = "sysError"
|
||||
return c.RenderJson(re)
|
||||
}
|
||||
|
||||
filenameReturn := note.Title
|
||||
filenameReturn = FixFilename(filenameReturn)
|
||||
if filenameReturn == "" {
|
||||
filenameReturn = "Untitled.pdf"
|
||||
} else {
|
||||
filenameReturn += ".pdf"
|
||||
}
|
||||
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
|
||||
"FindPasswordUpdate": true,
|
||||
"Suggestion": true,
|
||||
},
|
||||
"Note": map[string]bool{"ToImage": true},
|
||||
"Note": map[string]bool{"ToPdf": true},
|
||||
"Blog": map[string]bool{"Index": true,
|
||||
"View": true,
|
||||
"AboutMe": true,
|
||||
@@ -68,7 +68,7 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
|
||||
},
|
||||
"Oauth": map[string]bool{"GithubCallback": true},
|
||||
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
|
||||
"Attach": map[string]bool{"Download": true, "DownloadAll": true},
|
||||
"Attach": map[string]bool{"Download": true/*, "DownloadAll": true*/},
|
||||
}
|
||||
|
||||
func needValidate(controller, method string) bool {
|
||||
@@ -146,9 +146,10 @@ func init() {
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Note{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Share{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &User{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Album{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &File{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Attach{})
|
||||
// revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Blog{})
|
||||
// revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Blog{})
|
||||
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &NoteContentHistory{})
|
||||
|
||||
revel.OnAppStart(func() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"gopkg.in/mgo.v2"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Init mgo and the common DAO
|
||||
@@ -62,10 +63,26 @@ func Init(url, dbname string) {
|
||||
config := revel.Config
|
||||
if url == "" {
|
||||
url, ok = config.String("db.url")
|
||||
if !ok {
|
||||
url, ok = config.String("db.urlEnv")
|
||||
if ok {
|
||||
Log("get db conf from urlEnv: " + url)
|
||||
}
|
||||
} else {
|
||||
Log("get db conf from db.url: " + url)
|
||||
}
|
||||
|
||||
if ok {
|
||||
// get dbname from urlEnv
|
||||
urls := strings.Split(url, "/")
|
||||
dbname = urls[len(urls)-1]
|
||||
}
|
||||
}
|
||||
if dbname == "" {
|
||||
dbname, _ = config.String("db.dbname")
|
||||
}
|
||||
|
||||
// get db config from host, port, username, password
|
||||
if !ok {
|
||||
host, _ := revel.Config.String("db.host")
|
||||
port, _ := revel.Config.String("db.port")
|
||||
@@ -338,3 +355,20 @@ func Err(err error) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查mognodb是否lost connection
|
||||
// 每个请求之前都要检查!!
|
||||
func CheckMongoSessionLost() {
|
||||
// fmt.Println("检查CheckMongoSessionLostErr")
|
||||
err := Session.Ping()
|
||||
if err != nil {
|
||||
Log("Lost connection to db!")
|
||||
Session.Refresh()
|
||||
err = Session.Ping()
|
||||
if err == nil {
|
||||
Log("Reconnect to db successful.")
|
||||
} else {
|
||||
Log("重连失败!!!! 警告")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,13 @@ type UserAccount struct {
|
||||
MaxPerAttachSize int `bson:"MaxPerAttachSize" json:"-"` // 单个附件大小
|
||||
}
|
||||
|
||||
// note主页需要
|
||||
type UserAndBlogUrl struct {
|
||||
User
|
||||
BlogUrl string `BlogUrl`
|
||||
PostUrl string `PostUrl`
|
||||
}
|
||||
|
||||
// 用户与博客信息结合, 公开
|
||||
type UserAndBlog struct {
|
||||
UserId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
|
||||
|
||||
12
app/init.go
12
app/init.go
@@ -10,8 +10,6 @@ import (
|
||||
"github.com/leanote/leanote/app/controllers/admin"
|
||||
"github.com/leanote/leanote/app/controllers/member"
|
||||
_ "github.com/leanote/leanote/app/lea/binder"
|
||||
// "github.com/leanote/leanote/app/lea/session"
|
||||
// "github.com/leanote/leanote/app/lea/memcache"
|
||||
"github.com/leanote/leanote/app/lea/route"
|
||||
"reflect"
|
||||
"fmt"
|
||||
@@ -182,6 +180,16 @@ func init() {
|
||||
return template.HTML(tagStr)
|
||||
}
|
||||
|
||||
// 不用revel的msg
|
||||
revel.TemplateFuncs["leaMsg"] = func(renderArgs map[string]interface{}, key string) template.HTML {
|
||||
locale, _ := renderArgs[revel.CurrentLocaleRenderArg].(string)
|
||||
str := revel.Message(locale, key)
|
||||
if strings.HasPrefix(str, "???") {
|
||||
str = key
|
||||
}
|
||||
return template.HTML(str);
|
||||
}
|
||||
|
||||
// lea++
|
||||
revel.TemplateFuncs["blogTagsLea"] = func(renderArgs map[string]interface{}, tags []string, typeStr string) template.HTML {
|
||||
if tags == nil || len(tags) == 0 {
|
||||
|
||||
22
app/lea/Pwd.go
Normal file
22
app/lea/Pwd.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package lea
|
||||
|
||||
// 对比密码是否一致
|
||||
// 因为之前密码是用md5加密的, 所以通过密码长度来判断
|
||||
// rawPwd 原始, 用户输入的密码
|
||||
func ComparePwd(rawPwd, dbPwd string) bool {
|
||||
if len(dbPwd) == 32 {
|
||||
return Md5(rawPwd) == dbPwd
|
||||
}
|
||||
|
||||
hex := []byte(dbPwd)
|
||||
return CompareHash(hex, rawPwd)
|
||||
}
|
||||
|
||||
// 加密
|
||||
func GenPwd(rawPwd string) string {
|
||||
digest, err := GenerateHash(rawPwd)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(digest)
|
||||
}
|
||||
148
app/lea/Util.go
148
app/lea/Util.go
@@ -11,6 +11,8 @@ import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
"strings"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"bytes"
|
||||
math_rand "math/rand"
|
||||
)
|
||||
|
||||
@@ -23,6 +25,22 @@ func Md5(s string) string {
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// 3位数的转换, 为了用bson.id -> 3位数
|
||||
func Digest3(str string) string {
|
||||
var b rune = 0
|
||||
for _, k := range str {
|
||||
b += k
|
||||
}
|
||||
return fmt.Sprintf("%d", b % 1000)
|
||||
}
|
||||
func Digest2(str string) string {
|
||||
var b rune = 0
|
||||
for _, k := range str {
|
||||
b += k
|
||||
}
|
||||
return fmt.Sprintf("%d", b % 100)
|
||||
}
|
||||
|
||||
// Guid
|
||||
func NewGuid() string {
|
||||
b := make([]byte, 48)
|
||||
@@ -124,73 +142,48 @@ func ReplaceAll(oldStr, pattern, newStr string) string {
|
||||
}
|
||||
|
||||
// 获取纯文本
|
||||
func SubStringHTMLToRaw(param string, length int) (result string) {
|
||||
func SubStringHTMLToRaw(param string, length int) string {
|
||||
if param == "" {
|
||||
return ""
|
||||
return param
|
||||
}
|
||||
result = ""
|
||||
n := 0
|
||||
var temp rune // 中文问题, 用rune来解决
|
||||
rStr := []rune(param)
|
||||
lenStr := len(rStr)
|
||||
isCode := false
|
||||
for i := 0; i < len(rStr); i++ {
|
||||
|
||||
resultRune := make([]rune, length)
|
||||
// s := ""
|
||||
for i := 0; i < lenStr; i++ {
|
||||
temp = rStr[i]
|
||||
if temp == '<' {
|
||||
isCode = true
|
||||
continue
|
||||
} else if temp == '>' {
|
||||
isCode = false
|
||||
result += " "; // 空格
|
||||
resultRune[n] = ' ';
|
||||
|
||||
n++
|
||||
if n >= length {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !isCode {
|
||||
result += string(temp)
|
||||
resultRune[n] = temp;
|
||||
// s += string(temp)
|
||||
n++
|
||||
if n >= length {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
result := string(resultRune[0:n])
|
||||
return strings.Trim(result, " ")
|
||||
}
|
||||
|
||||
// 获取摘要, HTML
|
||||
func SubStringHTML(param string, length int, end string) string {
|
||||
if param == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 先取出<pre></pre>占位..
|
||||
result := ""
|
||||
|
||||
// 1
|
||||
n := 0
|
||||
var temp rune // 中文问题, 用rune来解决
|
||||
isCode := false //是不是HTML代码
|
||||
isHTML := false //是不是HTML特殊字符,如
|
||||
rStr := []rune(param)
|
||||
for i := 0; i < len(rStr); i++ {
|
||||
temp = rStr[i]
|
||||
if temp == '<' {
|
||||
isCode = true
|
||||
} else if temp == '&' {
|
||||
isHTML = true
|
||||
} else if temp == '>' && isCode {
|
||||
n = n - 1
|
||||
isCode = false
|
||||
} else if temp == ';' && isHTML {
|
||||
isHTML = false
|
||||
}
|
||||
if !isCode && !isHTML {
|
||||
n = n + 1
|
||||
}
|
||||
result += string(temp)
|
||||
if n >= length {
|
||||
break
|
||||
}
|
||||
}
|
||||
result += end
|
||||
|
||||
// 自带方法补全html
|
||||
func fixHtml(result string) string {
|
||||
// 取出所有标签
|
||||
tempResult := ReplaceAll(result, "(>)[^<>]*(<?)", "$1$2") // 把标签中间的所有内容都去掉了
|
||||
|
||||
@@ -233,6 +226,71 @@ func SubStringHTML(param string, length int, end string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// 获取摘要, HTML
|
||||
func SubStringHTML(param string, length int, end string) string {
|
||||
if param == "" {
|
||||
return param
|
||||
}
|
||||
result := ""
|
||||
|
||||
rStr := []rune(param)
|
||||
lenStr := len(rStr)
|
||||
|
||||
if lenStr <= length {
|
||||
result = param
|
||||
} else {
|
||||
// 1
|
||||
n := 0
|
||||
var temp rune // 中文问题, 用rune来解决
|
||||
isCode := false //是不是HTML代码
|
||||
isHTML := false //是不是HTML特殊字符,如
|
||||
var i = 0;
|
||||
for ; i < lenStr; i++ {
|
||||
temp = rStr[i]
|
||||
if temp == '<' {
|
||||
isCode = true
|
||||
} else if temp == '&' {
|
||||
isHTML = true
|
||||
} else if temp == '>' && isCode {
|
||||
// n = n - 1
|
||||
isCode = false
|
||||
} else if temp == ';' && isHTML {
|
||||
isHTML = false
|
||||
}
|
||||
if !isCode && !isHTML {
|
||||
n = n + 1
|
||||
}
|
||||
// 每一次都相加, 速度非常慢!, 重新分配内存, 7倍的差距
|
||||
// result += string(temp)
|
||||
if n >= length {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
result = string(rStr[0:i])
|
||||
|
||||
if end != "" {
|
||||
result += end
|
||||
}
|
||||
}
|
||||
|
||||
// 使用goquery来取出html, 为了补全html
|
||||
htmlReader := bytes.NewBufferString(result)
|
||||
dom, err1 := goquery.NewDocumentFromReader(htmlReader)
|
||||
if err1 == nil {
|
||||
html, _ := dom.Html()
|
||||
html = strings.Replace(html, "<html><head></head><body>", "", 1)
|
||||
html = strings.Replace(html, "</body></html>", "", 1)
|
||||
|
||||
// TODO 把style="float: left"去掉
|
||||
return html
|
||||
|
||||
// 如果有错误, 则使用自己的方法补全, 有风险
|
||||
} else {
|
||||
return fixHtml(result)
|
||||
}
|
||||
}
|
||||
|
||||
// 是否是合格的密码
|
||||
func IsGoodPwd(pwd string) (bool, string) {
|
||||
if pwd == "" {
|
||||
@@ -249,7 +307,7 @@ func IsEmail(email string) bool {
|
||||
if email == "" {
|
||||
return false;
|
||||
}
|
||||
ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-z\-A-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,3}$`, email)
|
||||
ok, _ := regexp.MatchString(`^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[_a-z\-A-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,6}$`, email)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
||||
26
app/lea/crypto.go
Normal file
26
app/lea/crypto.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// contains two cryptographic functions for both storing and comparing passwords.
|
||||
package lea
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// GenerateHash generates bcrypt hash from plaintext password
|
||||
func GenerateHash(password string) ([]byte, error) {
|
||||
hex := []byte(password)
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword(hex, 10)
|
||||
if err != nil {
|
||||
return hashedPassword, err
|
||||
}
|
||||
return hashedPassword, nil
|
||||
}
|
||||
|
||||
// CompareHash compares bcrypt password with a plaintext one. Returns true if passwords match
|
||||
// and false if they do not.
|
||||
func CompareHash(digest []byte, password string) bool {
|
||||
hex := []byte(password)
|
||||
if err := bcrypt.CompareHashAndPassword(digest, hex); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package memcache
|
||||
|
||||
import (
|
||||
"github.com/robfig/gomemcache/memcache"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var client *memcache.Client
|
||||
|
||||
// onAppStart后调用
|
||||
func InitMemcache() {
|
||||
client = memcache.New("localhost:11211")
|
||||
}
|
||||
|
||||
//------------
|
||||
// map
|
||||
|
||||
func SetMap(key string, value map[string]string, expiration int32) {
|
||||
// 把value转成byte
|
||||
bytes, _ := json.Marshal(value)
|
||||
if expiration == -1 {
|
||||
expiration = 30 * 24 * 60 * 60 // 30天
|
||||
}
|
||||
client.Set(&memcache.Item{Key: key, Value: bytes, Expiration: expiration})
|
||||
}
|
||||
|
||||
func GetMap(key string) map[string]string {
|
||||
item, err := client.Get(key)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[string]string{}
|
||||
json.Unmarshal(item.Value, &m)
|
||||
return m
|
||||
}
|
||||
|
||||
//------------
|
||||
// string
|
||||
func GetString(key string) string {
|
||||
item, err := client.Get(key)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(item.Value)
|
||||
}
|
||||
func SetString(key string, value string, expiration int32) {
|
||||
if expiration == -1 {
|
||||
expiration = 30 * 24 * 60 * 60 // 30天
|
||||
}
|
||||
client.Set(&memcache.Item{Key: key, Value: []byte(value), Expiration: expiration})
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// int, 是通过转成string来存的
|
||||
|
||||
func GetInt(key string) int {
|
||||
str := GetString(key)
|
||||
i, _ := strconv.Atoi(str)
|
||||
return i
|
||||
}
|
||||
func SetInt(key string, value int, expiration int32) {
|
||||
str := strconv.Itoa(value)
|
||||
SetString(key, str, expiration)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package route
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// "github.com/leanote/leanote/app/service"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -42,6 +42,10 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
}
|
||||
*/
|
||||
if route.ControllerName != "Static" {
|
||||
|
||||
// 检查mongodb 是否lost
|
||||
db.CheckMongoSessionLost()
|
||||
|
||||
// api设置
|
||||
// leanote.com/api/user/get => ApiUser::Get
|
||||
//* /api/login ApiAuth.Login, 这里的设置, 其实已经转成了ApiAuth了
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/lea/memcache"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
)
|
||||
|
||||
// 使用filter
|
||||
// 很巧妙就使用了memcache来处理session
|
||||
// revel的session(cookie)只存sessionId, 其它信息存在memcache中
|
||||
|
||||
func MSessionFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
sessionId := c.Session.Id()
|
||||
|
||||
// 从memcache中得到cache, 赋给session
|
||||
cache := revel.Session(memcache.GetMap(sessionId))
|
||||
|
||||
Log("memcache")
|
||||
LogJ(cache)
|
||||
if cache == nil {
|
||||
cache = revel.Session{}
|
||||
cache.Id()
|
||||
}
|
||||
c.Session = cache
|
||||
|
||||
// Make session vars available in templates as {{.session.xyz}}
|
||||
c.RenderArgs["session"] = c.Session
|
||||
|
||||
fc[0](c, fc[1:])
|
||||
|
||||
// 再把session保存之
|
||||
LogJ(c.Session)
|
||||
memcache.SetMap(sessionId, c.Session, -1)
|
||||
|
||||
// 只留下sessionId
|
||||
c.Session = revel.Session{revel.SESSION_ID_KEY: sessionId}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 主要修改revel的cookie, 设置Domain
|
||||
// 为了使sub domain共享cookie
|
||||
// cookie.domain = leanote.com
|
||||
|
||||
// A signed cookie (and thus limited to 4kb in size).
|
||||
// Restriction: Keys may not have a colon in them.
|
||||
type Session map[string]string
|
||||
|
||||
const (
|
||||
SESSION_ID_KEY = "_ID"
|
||||
TIMESTAMP_KEY = "_TS"
|
||||
)
|
||||
|
||||
// expireAfterDuration is the time to live, in seconds, of a session cookie.
|
||||
// It may be specified in config as "session.expires". Values greater than 0
|
||||
// set a persistent cookie with a time to live as specified, and the value 0
|
||||
// sets a session cookie.
|
||||
var expireAfterDuration time.Duration
|
||||
var cookieDomain = "" // life
|
||||
func init() {
|
||||
// Set expireAfterDuration, default to 30 days if no value in config
|
||||
revel.OnAppStart(func() {
|
||||
var err error
|
||||
if expiresString, ok := revel.Config.String("session.expires"); !ok {
|
||||
expireAfterDuration = 30 * 24 * time.Hour
|
||||
} else if expiresString == "session" {
|
||||
expireAfterDuration = 0
|
||||
} else if expireAfterDuration, err = time.ParseDuration(expiresString); err != nil {
|
||||
panic(fmt.Errorf("session.expires invalid: %s", err))
|
||||
}
|
||||
|
||||
cookieDomain, _ = revel.Config.String("cookie.domain")
|
||||
})
|
||||
}
|
||||
|
||||
// Id retrieves from the cookie or creates a time-based UUID identifying this
|
||||
// session.
|
||||
func (s Session) Id() string {
|
||||
if sessionIdStr, ok := s[SESSION_ID_KEY]; ok {
|
||||
return sessionIdStr
|
||||
}
|
||||
|
||||
buffer := make([]byte, 32)
|
||||
if _, err := rand.Read(buffer); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
s[SESSION_ID_KEY] = hex.EncodeToString(buffer)
|
||||
return s[SESSION_ID_KEY]
|
||||
}
|
||||
|
||||
// getExpiration return a time.Time with the session's expiration date.
|
||||
// If previous session has set to "session", remain it
|
||||
func (s Session) getExpiration() time.Time {
|
||||
if expireAfterDuration == 0 || s[TIMESTAMP_KEY] == "session" {
|
||||
// Expire after closing browser
|
||||
return time.Time{}
|
||||
}
|
||||
return time.Now().Add(expireAfterDuration)
|
||||
}
|
||||
|
||||
// cookie returns an http.Cookie containing the signed session.
|
||||
func (s Session) cookie() *http.Cookie {
|
||||
var sessionValue string
|
||||
ts := s.getExpiration()
|
||||
s[TIMESTAMP_KEY] = getSessionExpirationCookie(ts)
|
||||
for key, value := range s {
|
||||
if strings.ContainsAny(key, ":\x00") {
|
||||
panic("Session keys may not have colons or null bytes")
|
||||
}
|
||||
if strings.Contains(value, "\x00") {
|
||||
panic("Session values may not have null bytes")
|
||||
}
|
||||
sessionValue += "\x00" + key + ":" + value + "\x00"
|
||||
}
|
||||
|
||||
sessionData := url.QueryEscape(sessionValue)
|
||||
cookie := http.Cookie{
|
||||
Name: revel.CookiePrefix + "_SESSION",
|
||||
Value: revel.Sign(sessionData) + "-" + sessionData,
|
||||
Path: "/",
|
||||
HttpOnly: revel.CookieHttpOnly,
|
||||
Secure: revel.CookieSecure,
|
||||
Expires: ts.UTC(),
|
||||
}
|
||||
|
||||
if cookieDomain != "" {
|
||||
cookie.Domain = cookieDomain
|
||||
}
|
||||
|
||||
return &cookie
|
||||
}
|
||||
|
||||
// sessionTimeoutExpiredOrMissing returns a boolean of whether the session
|
||||
// cookie is either not present or present but beyond its time to live; i.e.,
|
||||
// whether there is not a valid session.
|
||||
func sessionTimeoutExpiredOrMissing(session Session) bool {
|
||||
if exp, present := session[TIMESTAMP_KEY]; !present {
|
||||
return true
|
||||
} else if exp == "session" {
|
||||
return false
|
||||
} else if expInt, _ := strconv.Atoi(exp); int64(expInt) < time.Now().Unix() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getSessionFromCookie returns a Session struct pulled from the signed
|
||||
// session cookie.
|
||||
func getSessionFromCookie(cookie *http.Cookie) Session {
|
||||
session := make(Session)
|
||||
|
||||
// Separate the data from the signature.
|
||||
hyphen := strings.Index(cookie.Value, "-")
|
||||
if hyphen == -1 || hyphen >= len(cookie.Value)-1 {
|
||||
return session
|
||||
}
|
||||
sig, data := cookie.Value[:hyphen], cookie.Value[hyphen+1:]
|
||||
|
||||
// Verify the signature.
|
||||
if !revel.Verify(data, sig) {
|
||||
revel.INFO.Println("Session cookie signature failed")
|
||||
return session
|
||||
}
|
||||
|
||||
revel.ParseKeyValueCookie(data, func(key, val string) {
|
||||
session[key] = val
|
||||
})
|
||||
|
||||
if sessionTimeoutExpiredOrMissing(session) {
|
||||
session = make(Session)
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
// SessionFilter is a Revel Filter that retrieves and sets the session cookie.
|
||||
// Within Revel, it is available as a Session attribute on Controller instances.
|
||||
// The name of the Session cookie is set as CookiePrefix + "_SESSION".
|
||||
func SessionFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
session := restoreSession(c.Request.Request)
|
||||
// c.Session, 重新生成一个revel.Session给controller!!!
|
||||
// Log("sessoin--------")
|
||||
// LogJ(session)
|
||||
revelSession := revel.Session(session) // 强制转换 还是同一个对象, 但有个问题, 这样Session.Id()方法是用revel的了
|
||||
c.Session = revelSession
|
||||
// 生成sessionId
|
||||
c.Session.Id()
|
||||
sessionWasEmpty := len(c.Session) == 0
|
||||
|
||||
// Make session vars available in templates as {{.session.xyz}}
|
||||
c.RenderArgs["session"] = c.Session
|
||||
|
||||
fc[0](c, fc[1:])
|
||||
|
||||
// Store the signed session if it could have changed.
|
||||
if len(c.Session) > 0 || !sessionWasEmpty {
|
||||
// 转换成lea.Session
|
||||
session = Session(c.Session)
|
||||
c.SetCookie(session.cookie())
|
||||
}
|
||||
}
|
||||
|
||||
// restoreSession returns either the current session, retrieved from the
|
||||
// session cookie, or a new session.
|
||||
func restoreSession(req *http.Request) Session {
|
||||
cookie, err := req.Cookie(revel.CookiePrefix + "_SESSION")
|
||||
if err != nil {
|
||||
return make(Session)
|
||||
} else {
|
||||
return getSessionFromCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// getSessionExpirationCookie retrieves the cookie's time to live as a
|
||||
// string of either the number of seconds, for a persistent cookie, or
|
||||
// "session".
|
||||
func getSessionExpirationCookie(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return "session"
|
||||
}
|
||||
return strconv.FormatInt(t.Unix(), 10)
|
||||
}
|
||||
|
||||
// SetNoExpiration sets session to expire when browser session ends
|
||||
func (s Session) SetNoExpiration() {
|
||||
s[TIMESTAMP_KEY] = "session"
|
||||
}
|
||||
|
||||
// SetDefaultExpiration sets session to expire after default duration
|
||||
func (s Session) SetDefaultExpiration() {
|
||||
delete(s, TIMESTAMP_KEY)
|
||||
}
|
||||
@@ -63,10 +63,19 @@ func (this *AttachService) updateNoteAttachNum(noteId bson.ObjectId, addNum int)
|
||||
// list attachs
|
||||
func (this *AttachService) ListAttachs(noteId, userId string) []info.Attach {
|
||||
attachs := []info.Attach{}
|
||||
// 判断是否有权限为笔记添加附件
|
||||
if !shareService.HasUpdateNotePerm(noteId, userId) {
|
||||
|
||||
// 判断是否有权限为笔记添加附件, userId为空时表示是分享笔记的附件
|
||||
if userId != "" && !shareService.HasUpdateNotePerm(noteId, userId) {
|
||||
return attachs
|
||||
}
|
||||
|
||||
// 笔记是否是自己的
|
||||
note := noteService.GetNoteByIdAndUserId(noteId, userId)
|
||||
if note.NoteId == "" {
|
||||
return attachs
|
||||
}
|
||||
|
||||
// TODO 这里, 优化权限控制
|
||||
|
||||
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)
|
||||
|
||||
|
||||
@@ -2,26 +2,31 @@ package service
|
||||
|
||||
import (
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "github.com/leanote/leanote/app/db"
|
||||
// "github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
// "github.com/revel/revel"
|
||||
"strings"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/revel/revel"
|
||||
"errors"
|
||||
"fmt"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 登录与权限
|
||||
// 登录与权限 Login & Register
|
||||
|
||||
type AuthService struct {
|
||||
}
|
||||
|
||||
// pwd已md5了
|
||||
func (this *AuthService) Login(emailOrUsername, pwd string) info.User {
|
||||
// 使用bcrypt认证或者Md5认证
|
||||
// Use bcrypt (Md5 depreciated)
|
||||
func (this *AuthService) Login(emailOrUsername, pwd string) (info.User, error) {
|
||||
emailOrUsername = strings.Trim(emailOrUsername, " ")
|
||||
// pwd = strings.Trim(pwd, " ")
|
||||
userInfo := userService.LoginGetUserInfo(emailOrUsername, Md5(pwd))
|
||||
return userInfo
|
||||
// pwd = strings.Trim(pwd, " ")
|
||||
userInfo := userService.GetUserInfoByName(emailOrUsername)
|
||||
if userInfo.UserId == "" || !ComparePwd(pwd, userInfo.Pwd) {
|
||||
return userInfo, errors.New("wrong username or password")
|
||||
}
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
// 注册
|
||||
@@ -40,71 +45,74 @@ func (this *AuthService) Register(email, pwd, fromUserId string) (bool, string)
|
||||
if userService.IsExistsUser(email) {
|
||||
return false, "userHasBeenRegistered-" + email
|
||||
}
|
||||
user := info.User{UserId: bson.NewObjectId(), Email: email, Username: email, Pwd: Md5(pwd)}
|
||||
passwd := GenPwd(pwd)
|
||||
if passwd == "" {
|
||||
return false, "GenerateHash error"
|
||||
}
|
||||
user := info.User{UserId: bson.NewObjectId(), Email: email, Username: email, Pwd: passwd}
|
||||
if fromUserId != "" && IsObjectId(fromUserId) {
|
||||
user.FromUserId = bson.ObjectIdHex(fromUserId)
|
||||
}
|
||||
LogJ(user)
|
||||
return this.register(user)
|
||||
}
|
||||
|
||||
func (this *AuthService) register(user info.User) (bool, string) {
|
||||
if userService.AddUser(user) {
|
||||
// 添加笔记本, 生活, 学习, 工作
|
||||
userId := user.UserId.Hex();
|
||||
userId := user.UserId.Hex()
|
||||
notebook := info.Notebook{
|
||||
Seq: -1,
|
||||
Seq: -1,
|
||||
UserId: user.UserId}
|
||||
title2Id := map[string]bson.ObjectId{"life": bson.NewObjectId(), "study": bson.NewObjectId(), "work": bson.NewObjectId()}
|
||||
for title, objectId := range title2Id {
|
||||
notebook.Title = title
|
||||
notebook.NotebookId = objectId
|
||||
notebook.UserId = user.UserId
|
||||
notebookService.AddNotebook(notebook);
|
||||
notebookService.AddNotebook(notebook)
|
||||
}
|
||||
|
||||
|
||||
// 添加leanote -> 该用户的共享
|
||||
registerSharedUserId := configService.GetGlobalStringConfig("registerSharedUserId")
|
||||
if(registerSharedUserId != "") {
|
||||
if registerSharedUserId != "" {
|
||||
registerSharedNotebooks := configService.GetGlobalArrMapConfig("registerSharedNotebooks")
|
||||
registerSharedNotes := configService.GetGlobalArrMapConfig("registerSharedNotes")
|
||||
registerCopyNoteIds := configService.GetGlobalArrayConfig("registerCopyNoteIds")
|
||||
|
||||
|
||||
// 添加共享笔记本
|
||||
for _, notebook := range registerSharedNotebooks {
|
||||
perm, _ := strconv.Atoi(notebook["perm"])
|
||||
shareService.AddShareNotebookToUserId(notebook["notebookId"], perm, registerSharedUserId, userId);
|
||||
shareService.AddShareNotebookToUserId(notebook["notebookId"], perm, registerSharedUserId, userId)
|
||||
}
|
||||
|
||||
|
||||
// 添加共享笔记
|
||||
for _, note := range registerSharedNotes {
|
||||
perm, _ := strconv.Atoi(note["perm"])
|
||||
shareService.AddShareNoteToUserId(note["noteId"], perm, registerSharedUserId, userId);
|
||||
shareService.AddShareNoteToUserId(note["noteId"], perm, registerSharedUserId, userId)
|
||||
}
|
||||
|
||||
|
||||
// 复制笔记
|
||||
for _, noteId := range registerCopyNoteIds {
|
||||
note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex());
|
||||
// Log(noteId)
|
||||
// Log("Copy")
|
||||
// LogJ(note)
|
||||
note := noteService.CopySharedNote(noteId, title2Id["life"].Hex(), registerSharedUserId, user.UserId.Hex())
|
||||
// Log(noteId)
|
||||
// Log("Copy")
|
||||
// LogJ(note)
|
||||
noteUpdate := bson.M{"IsBlog": false} // 不要是博客
|
||||
noteService.UpdateNote(user.UserId.Hex(), note.NoteId.Hex(), noteUpdate, -1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------
|
||||
// 添加一条userBlog
|
||||
blogService.UpdateUserBlog(info.UserBlog{UserId: user.UserId,
|
||||
Title: user.Username + " 's Blog",
|
||||
SubTitle: "Love Leanote!",
|
||||
AboutMe: "Hello, I am (^_^)",
|
||||
blogService.UpdateUserBlog(info.UserBlog{UserId: user.UserId,
|
||||
Title: user.Username + " 's Blog",
|
||||
SubTitle: "Love Leanote!",
|
||||
AboutMe: "Hello, I am (^_^)",
|
||||
CanComment: true,
|
||||
})
|
||||
})
|
||||
// 添加一个单页面
|
||||
blogService.AddOrUpdateSingle(user.UserId.Hex(), "", "About Me", "Hello, I am (^_^)")
|
||||
}
|
||||
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
@@ -115,7 +123,7 @@ func (this *AuthService) register(user info.User) (bool, string) {
|
||||
func (this *AuthService) getUsername(thirdType, thirdUsername string) (username string) {
|
||||
username = thirdType + "-" + thirdUsername
|
||||
i := 1
|
||||
for ;; {
|
||||
for {
|
||||
if !userService.IsExistsUserByUsername(username) {
|
||||
return
|
||||
}
|
||||
@@ -131,11 +139,11 @@ func (this *AuthService) ThirdRegister(thirdType, thirdUserId, thirdUsername str
|
||||
}
|
||||
|
||||
username := this.getUsername(thirdType, thirdUsername)
|
||||
userInfo = info.User{UserId: bson.NewObjectId(),
|
||||
Username: username,
|
||||
ThirdUserId: thirdUserId,
|
||||
userInfo = info.User{UserId: bson.NewObjectId(),
|
||||
Username: username,
|
||||
ThirdUserId: thirdUserId,
|
||||
ThirdUsername: thirdUsername,
|
||||
}
|
||||
}
|
||||
_, _ = this.register(userInfo)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@ func (this *BlogService) GetBlogByIdAndUrlTitle(userId string, noteIdOrUrlTitle
|
||||
return this.GetBlog(noteIdOrUrlTitle)
|
||||
}
|
||||
note := info.Note{}
|
||||
db.GetByQ(db.Notes, bson.M{"UserId": bson.ObjectIdHex(userId), "UrlTitle": encodeValue(noteIdOrUrlTitle), "IsBlog": true, "IsTrash": false}, ¬e)
|
||||
db.GetByQ(db.Notes, bson.M{"UserId": bson.ObjectIdHex(userId), "UrlTitle": encodeValue(noteIdOrUrlTitle),
|
||||
"IsBlog": true,
|
||||
"IsTrash": false, "IsDeleted": false}, ¬e)
|
||||
return this.GetBlogItem(note)
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ func (this *BlogService) GetBlog(noteId string) (blog info.BlogItem) {
|
||||
}
|
||||
func (this *BlogService) GetBlogItem(note info.Note) (blog info.BlogItem) {
|
||||
if note.NoteId == "" || !note.IsBlog {
|
||||
return
|
||||
return info.BlogItem{}
|
||||
}
|
||||
|
||||
// 内容
|
||||
@@ -131,7 +133,8 @@ func (this *BlogService) ListBlogs(userId, notebookId string, page, pageSize int
|
||||
func (this *BlogService) GetBlogTags(userId string) []info.TagCount {
|
||||
// 得到所有博客
|
||||
tagCounts := []info.TagCount{}
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true}
|
||||
// tag不能为空
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "Tag": bson.M{"$ne": ""}}
|
||||
db.TagCounts.Find(query).Sort("-Count").All(&tagCounts)
|
||||
return tagCounts
|
||||
}
|
||||
@@ -142,7 +145,7 @@ func (this *BlogService) ReCountBlogTags(userId string) bool {
|
||||
// 得到所有博客
|
||||
notes := []info.Note{}
|
||||
userIdO := bson.ObjectIdHex(userId)
|
||||
query := bson.M{"UserId": userIdO, "IsTrash": false, "IsBlog": true}
|
||||
query := bson.M{"UserId": userIdO, "IsTrash": false, "IsDeleted": false, "IsBlog": true}
|
||||
db.ListByQWithFields(db.Notes, query, []string{"Tags"}, ¬es)
|
||||
|
||||
db.DeleteAll(db.TagCounts, bson.M{"UserId": userIdO, "IsBlog": true})
|
||||
@@ -184,7 +187,7 @@ Posts: []
|
||||
*/
|
||||
func (this *BlogService) ListBlogsArchive(userId, notebookId string, year, month int, sortField string, isAsc bool) []info.Archive {
|
||||
// _, notes := noteService.ListNotes(userId, notebookId, false, 1, 99999, sortField, isAsc, true);
|
||||
q := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "IsTrash": false}
|
||||
q := bson.M{"UserId": bson.ObjectIdHex(userId), "IsBlog": true, "IsTrash": false, "IsDeleted": false}
|
||||
if notebookId != "" {
|
||||
q["NotebookId"] = bson.ObjectIdHex(notebookId)
|
||||
}
|
||||
@@ -291,9 +294,10 @@ func (this *BlogService) SearchBlogByTags(tags []string, userId string, pageNumb
|
||||
|
||||
// 不是trash的
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId),
|
||||
"IsTrash": false,
|
||||
"IsBlog": true,
|
||||
"Tags": bson.M{"$all": tags}}
|
||||
"IsTrash": false,
|
||||
"IsDeleted": false,
|
||||
"IsBlog": true,
|
||||
"Tags": bson.M{"$all": tags}}
|
||||
|
||||
q := db.Notes.Find(query)
|
||||
|
||||
@@ -366,10 +370,10 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
|
||||
if !isAsc {
|
||||
// 降序
|
||||
/*
|
||||
------- pre
|
||||
----- now
|
||||
--- next
|
||||
--
|
||||
------- pre
|
||||
----- now
|
||||
--- next
|
||||
--
|
||||
*/
|
||||
// 上一篇时间要比它大, 找最小的
|
||||
sortFieldT1 = bson.M{"$gte": baseTime} // 为什么要相等, 因为将notebook发布成博客, 会统一修改note的publicTime, 此时所有notes都一样
|
||||
@@ -393,12 +397,14 @@ func (this *BlogService) PreNextBlog(userId string, sorterField string, isAsc bo
|
||||
sortFieldR2 = sorterField
|
||||
}
|
||||
|
||||
// 1
|
||||
// 上一篇, 比基时间要小, 但是是最后一篇, 所以是降序
|
||||
note := info.Note{}
|
||||
query := bson.M{"UserId": userIdO,
|
||||
"IsTrash": false,
|
||||
"IsBlog": true,
|
||||
"_id": bson.M{"$ne": bson.ObjectIdHex(noteId)},
|
||||
query := bson.M{"UserId": userIdO,
|
||||
"IsTrash": false,
|
||||
"IsDeleted": false,
|
||||
"IsBlog": true,
|
||||
"_id": bson.M{"$ne": bson.ObjectIdHex(noteId)},
|
||||
sorterField: sortFieldT1,
|
||||
}
|
||||
q := db.Notes.Find(query)
|
||||
@@ -430,7 +436,7 @@ func (this *BlogService) ListAllBlogs(userId, tag string, keywords string, isRec
|
||||
skipNum, sortFieldR := parsePageAndSort(page, pageSize, sorterField, isAsc)
|
||||
|
||||
// 不是trash的
|
||||
query := bson.M{"IsTrash": false, "IsBlog": true, "Title": bson.M{"$ne": "欢迎来到leanote!"}}
|
||||
query := bson.M{"IsTrash": false, "IsDeleted": false, "IsBlog": true, "Title": bson.M{"$ne": "欢迎来到leanote!"}}
|
||||
if tag != "" {
|
||||
query["Tags"] = bson.M{"$in": []string{tag}}
|
||||
}
|
||||
@@ -496,6 +502,9 @@ func (this *BlogService) ListAllBlogs(userId, tag string, keywords string, isRec
|
||||
content = noteContent.Abstract
|
||||
}
|
||||
*/
|
||||
if len(note.Tags) == 1 && note.Tags[0] == "" {
|
||||
note.Tags = nil
|
||||
}
|
||||
blogs[i] = info.BlogItem{note, "", content, hasMore, userMap[note.UserId]}
|
||||
}
|
||||
pageInfo = info.NewPage(page, pageSize, count, nil)
|
||||
|
||||
@@ -594,5 +594,5 @@ func (this *ConfigService) HomePageIsAdminsBlog() bool {
|
||||
}
|
||||
|
||||
func (this *ConfigService) GetVersion() string {
|
||||
return "1.0-beta.4"
|
||||
return "1.1"
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
"github.com/revel/revel"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"time"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
|
||||
@@ -115,6 +119,53 @@ func (this *FileService) UpdateImage(userId, fileId, title string) bool {
|
||||
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
|
||||
}
|
||||
|
||||
func (this *FileService) GetFileBase64(userId, fileId string) (str string, mine string) {
|
||||
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
|
||||
if err := recover(); err != nil {
|
||||
fmt.Println(err) // 这里的err其实就是panic传入的内容,55
|
||||
}
|
||||
}()
|
||||
|
||||
path := this.GetFile(userId, fileId)
|
||||
|
||||
if path == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
path = revel.BasePath + "/" + strings.TrimLeft(path, "/")
|
||||
|
||||
ff, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
e64 := base64.StdEncoding
|
||||
maxEncLen := e64.EncodedLen(len(ff))
|
||||
encBuf := make([]byte, maxEncLen)
|
||||
|
||||
e64.Encode(encBuf, ff)
|
||||
|
||||
mime := http.DetectContentType(ff)
|
||||
|
||||
str = string(encBuf)
|
||||
return str, mime
|
||||
}
|
||||
|
||||
// 得到图片base64, 图片要在之前添加data:image/png;base64,
|
||||
func (this *FileService) GetImageBase64(userId, fileId string) string {
|
||||
|
||||
str, mime := this.GetFileBase64(userId, fileId)
|
||||
if str == "" {
|
||||
return ""
|
||||
}
|
||||
switch mime {
|
||||
case "image/gif", "image/jpeg", "image/pjpeg", "image/png", "image/tiff":
|
||||
return fmt.Sprintf("data:%s;base64,%s", mime, str)
|
||||
default:
|
||||
}
|
||||
return "data:image/png;base64," + str
|
||||
}
|
||||
|
||||
// 获取文件路径
|
||||
// 要判断是否具有权限
|
||||
// userId是否具有fileId的访问权限
|
||||
|
||||
@@ -21,14 +21,26 @@ func (this *NoteService) GetNote(noteId, userId string) (note info.Note) {
|
||||
// 不能是已经删除了的, life bug, 客户端删除后, 竟然还能在web上打开
|
||||
func (this *NoteService) GetNoteById(noteId string) (note info.Note) {
|
||||
note = info.Note{}
|
||||
if noteId == "" {
|
||||
return
|
||||
}
|
||||
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsDeleted": false}, ¬e)
|
||||
return
|
||||
}
|
||||
func (this *NoteService) GetNoteByIdAndUserId(noteId, userId string) (note info.Note) {
|
||||
note = info.Note{}
|
||||
if noteId == "" || userId == "" {
|
||||
return
|
||||
}
|
||||
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "UserId": bson.ObjectIdHex(userId), "IsDeleted": false}, ¬e)
|
||||
return
|
||||
}
|
||||
// 得到blog, blogService用
|
||||
// 不要传userId, 因为是公开的
|
||||
func (this *NoteService) GetBlogNote(noteId string) (note info.Note) {
|
||||
note = info.Note{}
|
||||
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsBlog": true, "IsTrash": false}, ¬e)
|
||||
db.GetByQ(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId),
|
||||
"IsBlog": true, "IsTrash": false, "IsDeleted": false}, ¬e)
|
||||
return
|
||||
}
|
||||
// 通过id, userId得到noteContent
|
||||
@@ -418,7 +430,7 @@ func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bso
|
||||
if isBlog, ok := needUpdate["IsBlog"]; ok {
|
||||
isBlog2 := isBlog.(bool)
|
||||
if note.IsBlog != isBlog2 {
|
||||
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog2})
|
||||
this.UpdateNoteContentIsBlog(noteId, userId, isBlog2);
|
||||
|
||||
// 重新发布成博客
|
||||
if !note.IsBlog {
|
||||
@@ -459,6 +471,11 @@ func (this *NoteService) UpdateNote(updatedUserId, noteId string, needUpdate bso
|
||||
return true, "", afterUsn
|
||||
}
|
||||
|
||||
// 当设置/取消了笔记为博客
|
||||
func (this *NoteService) UpdateNoteContentIsBlog(noteId, userId string, isBlog bool) {
|
||||
db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, bson.M{"IsBlog": isBlog})
|
||||
}
|
||||
|
||||
// 附件修改, 增加noteIncr
|
||||
func (this *NoteService) IncrNoteUsn(noteId, userId string) int {
|
||||
afterUsn := userService.IncrUsn(userId)
|
||||
@@ -572,6 +589,8 @@ func (this *NoteService) ToBlog(userId, noteId string, isBlog, isTop bool) bool
|
||||
ok := db.UpdateByIdAndUserIdMap(db.Notes, noteId, userId, noteUpdate)
|
||||
// 重新计算tags
|
||||
go (func() {
|
||||
this.UpdateNoteContentIsBlog(noteId, userId, isBlog);
|
||||
|
||||
blogService.ReCountBlogTags(userId)
|
||||
})()
|
||||
return ok
|
||||
@@ -734,6 +753,7 @@ func (this *NoteService) SearchNote(key, userId string, pageNumber, pageSize int
|
||||
// 不是trash的
|
||||
query := bson.M{"UserId": bson.ObjectIdHex(userId),
|
||||
"IsTrash": false,
|
||||
"IsDeleted": false, // 不能搜索已删除了的
|
||||
"$or": orQ,
|
||||
}
|
||||
if isBlog {
|
||||
@@ -819,14 +839,14 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb
|
||||
//------------
|
||||
// 统计
|
||||
func (this *NoteService) CountNote(userId string) int {
|
||||
q := bson.M{"IsTrash": false}
|
||||
q := bson.M{"IsTrash": false, "IsDeleted": false}
|
||||
if userId != "" {
|
||||
q["UserId"] = bson.ObjectIdHex(userId)
|
||||
}
|
||||
return db.Count(db.Notes, q)
|
||||
}
|
||||
func (this *NoteService) CountBlog(userId string) int {
|
||||
q := bson.M{"IsBlog": true, "IsTrash": false}
|
||||
q := bson.M{"IsBlog": true, "IsTrash": false, "IsDeleted": false}
|
||||
if userId != "" {
|
||||
q["UserId"] = bson.ObjectIdHex(userId)
|
||||
}
|
||||
|
||||
@@ -275,11 +275,15 @@ func (this *NotebookService) ToBlog(userId, notebookId string, isBlog bool) (boo
|
||||
// 查看是否有子notebook
|
||||
// 先查看该notebookId下是否有notes, 没有则删除
|
||||
func (this *NotebookService) DeleteNotebook(userId, notebookId string) (bool, string) {
|
||||
if db.Count(db.Notebooks, bson.M{"ParentNotebookId": bson.ObjectIdHex(notebookId),
|
||||
"UserId": bson.ObjectIdHex(userId)}) == 0 { // 无
|
||||
if db.Count(db.Notebooks, bson.M{
|
||||
"ParentNotebookId": bson.ObjectIdHex(notebookId),
|
||||
"UserId": bson.ObjectIdHex(userId),
|
||||
"IsDeleted": false,
|
||||
}) == 0 { // 无
|
||||
if db.Count(db.Notes, bson.M{"NotebookId": bson.ObjectIdHex(notebookId),
|
||||
"UserId": bson.ObjectIdHex(userId),
|
||||
"IsTrash": false}) == 0 { // 不包含trash
|
||||
"IsTrash": false,
|
||||
"IsDeleted": false}) == 0 { // 不包含trash
|
||||
// 不是真删除 1/20, 为了同步笔记本
|
||||
ok := db.UpdateByQMap(db.Notebooks, bson.M{"_id": bson.ObjectIdHex(notebookId)}, bson.M{"IsDeleted": true, "Usn": userService.IncrUsn(userId)})
|
||||
return ok, ""
|
||||
|
||||
@@ -45,9 +45,13 @@ func (this *PwdService) UpdatePwd(token, pwd string) (bool, string) {
|
||||
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenPwd); !ok {
|
||||
return ok, msg
|
||||
}
|
||||
|
||||
digest, err := GenerateHash(pwd)
|
||||
if err != nil {
|
||||
return false,"GenerateHash error"
|
||||
}
|
||||
passwd := string(digest)
|
||||
// 修改密码之
|
||||
ok = db.UpdateByQField(db.Users, bson.M{"_id": tokenInfo.UserId}, "Pwd", Md5(pwd))
|
||||
ok = db.UpdateByQField(db.Users, bson.M{"_id": tokenInfo.UserId}, "Pwd", passwd)
|
||||
|
||||
// 删除token
|
||||
tokenService.DeleteToken(tokenInfo.UserId.Hex(), info.TokenPwd)
|
||||
|
||||
@@ -85,13 +85,13 @@ func (this *ThemeService) getDefaultTheme(style string) info.Theme {
|
||||
|
||||
// 用户的主题路径设置
|
||||
func (this *ThemeService) getUserThemeBasePath(userId string) string {
|
||||
return revel.BasePath + "/public/upload/" + userId + "/themes"
|
||||
return revel.BasePath + "/public/upload/" + Digest3(userId) + "/" + userId + "/themes"
|
||||
}
|
||||
func (this *ThemeService) getUserThemePath(userId, themeId string) string {
|
||||
return this.getUserThemeBasePath(userId) + "/" + themeId
|
||||
}
|
||||
func (this *ThemeService) getUserThemePath2(userId, themeId string) string {
|
||||
return "public/upload/" + userId + "/themes/" + themeId
|
||||
return "public/upload/" + Digest3(userId) + "/" + userId + "/themes/" + themeId
|
||||
}
|
||||
|
||||
// 新建主题
|
||||
@@ -412,10 +412,18 @@ func (this *ThemeService) ImportTheme(userId, path string) (ok bool, msg string)
|
||||
themeIdO := bson.NewObjectId()
|
||||
themeId := themeIdO.Hex()
|
||||
targetPath := this.getUserThemePath(userId, themeId) // revel.BasePath + "/public/upload/" + userId + "/themes/" + themeId
|
||||
|
||||
err := os.MkdirAll(targetPath, 0755)
|
||||
if err != nil {
|
||||
msg = "error"
|
||||
return
|
||||
}
|
||||
if ok, msg = archive.Unzip(path, targetPath); !ok {
|
||||
DeleteFile(targetPath)
|
||||
Log("oh no")
|
||||
return
|
||||
}
|
||||
|
||||
// 主题验证
|
||||
if ok, msg = this.ValidateTheme(targetPath, "", ""); !ok {
|
||||
DeleteFile(targetPath)
|
||||
|
||||
@@ -69,6 +69,13 @@ func (this *UserService) GetUsername(userId string) string {
|
||||
return user.Username
|
||||
}
|
||||
|
||||
// 得到用户名
|
||||
func (this *UserService) GetUsernameById(userId bson.ObjectId) string {
|
||||
user := info.User{}
|
||||
db.GetByQWithFields(db.Users, bson.M{"_id": userId}, []string{"Username"}, &user)
|
||||
return user.Username
|
||||
}
|
||||
|
||||
// 是否存在该用户 email
|
||||
func (this *UserService) IsExistsUser(email string) bool {
|
||||
if this.GetUserId(email) == "" {
|
||||
@@ -107,6 +114,13 @@ func (this *UserService) setUserLogo(user *info.User) {
|
||||
}
|
||||
}
|
||||
|
||||
// 仅得到用户
|
||||
func (this *UserService) GetUser(userId string) info.User {
|
||||
user := info.User{}
|
||||
db.Get(db.Users, userId, &user)
|
||||
return user
|
||||
}
|
||||
|
||||
// 得到用户信息 userId
|
||||
func (this *UserService) GetUserInfo(userId string) info.User {
|
||||
user := info.User{}
|
||||
@@ -202,19 +216,33 @@ func (this *UserService) MapUserAndBlogByUserIds(userIds []bson.ObjectId) map[st
|
||||
return userAndBlogMap
|
||||
}
|
||||
|
||||
// 得到用户信息+博客主页
|
||||
func (this *UserService) GetUserAndBlogUrl(userId string) info.UserAndBlogUrl {
|
||||
user := this.GetUserInfo(userId)
|
||||
userBlog := blogService.GetUserBlog(userId)
|
||||
|
||||
blogUrls := blogService.GetBlogUrls(&userBlog, &user)
|
||||
|
||||
return info.UserAndBlogUrl{
|
||||
User: user,
|
||||
BlogUrl: blogUrls.IndexUrl,
|
||||
PostUrl: blogUrls.PostUrl,
|
||||
}
|
||||
}
|
||||
|
||||
// 得到userAndBlog公开信息
|
||||
func (this *UserService) GetUserAndBlog(userId string) info.UserAndBlog {
|
||||
user := this.GetUserInfo(userId)
|
||||
userBlog := blogService.GetUserBlog(userId)
|
||||
return info.UserAndBlog{
|
||||
UserId: user.UserId,
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
Logo: user.Logo,
|
||||
UserId: user.UserId,
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
Logo: user.Logo,
|
||||
BlogTitle: userBlog.Title,
|
||||
BlogLogo: userBlog.Logo,
|
||||
BlogUrl: blogService.GetUserBlogUrl(&userBlog, user.Username),
|
||||
BlogUrls: blogService.GetBlogUrls(&userBlog, &user),
|
||||
BlogLogo: userBlog.Logo,
|
||||
BlogUrl: blogService.GetUserBlogUrl(&userBlog, user.Username),
|
||||
BlogUrls: blogService.GetBlogUrls(&userBlog, &user),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,15 +267,15 @@ func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.
|
||||
return users2
|
||||
}
|
||||
|
||||
// 使用email(username), pwd得到用户信息
|
||||
func (this *UserService) LoginGetUserInfo(emailOrUsername, md5Pwd string) info.User {
|
||||
// 使用email(username), 得到用户信息
|
||||
func (this *UserService) GetUserInfoByName(emailOrUsername string) info.User {
|
||||
emailOrUsername = strings.ToLower(emailOrUsername)
|
||||
|
||||
user := info.User{}
|
||||
if strings.Contains(emailOrUsername, "@") {
|
||||
db.GetByQ(db.Users, bson.M{"Email": emailOrUsername, "Pwd": md5Pwd}, &user)
|
||||
db.GetByQ(db.Users, bson.M{"Email": emailOrUsername}, &user)
|
||||
} else {
|
||||
db.GetByQ(db.Users, bson.M{"Username": emailOrUsername, "Pwd": md5Pwd}, &user)
|
||||
db.GetByQ(db.Users, bson.M{"Username": emailOrUsername}, &user)
|
||||
}
|
||||
this.setUserLogo(&user)
|
||||
return user
|
||||
@@ -281,10 +309,16 @@ func (this *UserService) UpdateAvatar(userId, avatarPath string) (bool) {
|
||||
// 已经登录了的用户修改密码
|
||||
func (this *UserService) UpdatePwd(userId, oldPwd, pwd string) (bool, string) {
|
||||
userInfo := this.GetUserInfo(userId)
|
||||
if userInfo.Pwd != Md5(oldPwd) {
|
||||
if !ComparePwd(oldPwd, userInfo.Pwd) {
|
||||
return false, "oldPasswordError"
|
||||
}
|
||||
ok := db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", Md5(pwd))
|
||||
|
||||
passwd := GenPwd(pwd)
|
||||
if passwd == "" {
|
||||
return false, "GenerateHash error"
|
||||
}
|
||||
|
||||
ok := db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", passwd)
|
||||
return ok, ""
|
||||
}
|
||||
|
||||
@@ -293,7 +327,12 @@ func (this *UserService) ResetPwd(adminUserId, userId, pwd string) (ok bool, msg
|
||||
if configService.GetAdminUserId() != adminUserId {
|
||||
return
|
||||
}
|
||||
ok = db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", Md5(pwd))
|
||||
|
||||
passwd := GenPwd(pwd)
|
||||
if passwd == "" {
|
||||
return false, "GenerateHash error"
|
||||
}
|
||||
ok = db.UpdateByQField(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, "Pwd", passwd)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -351,7 +390,7 @@ func (this *UserService) UpdateEmail(token string) (ok bool, msg, email string)
|
||||
tokenInfo := info.Token{}
|
||||
if ok, msg, tokenInfo = tokenService.VerifyToken(token, info.TokenUpdateEmail); ok {
|
||||
// 修改之后的邮箱
|
||||
email = tokenInfo.Email
|
||||
email = strings.ToLower(tokenInfo.Email)
|
||||
// 先验证该email是否被注册了
|
||||
if userService.IsExistsUser(email) {
|
||||
ok = false
|
||||
|
||||
@@ -90,7 +90,7 @@ func decodeValue(val string) string {
|
||||
v, _ := url.ParseQuery("a=" + val)
|
||||
return v.Get("a")
|
||||
}
|
||||
|
||||
|
||||
func encodeValue(val string) string {
|
||||
if val == "" {
|
||||
return val
|
||||
@@ -99,16 +99,17 @@ func encodeValue(val string) string {
|
||||
v.Set("", val)
|
||||
return v.Encode()[1:]
|
||||
}
|
||||
|
||||
// 添加笔记时通过title得到urlTitle
|
||||
func fixUrlTitle(urlTitle string) string {
|
||||
if urlTitle != "" {
|
||||
// 把特殊字段给替换掉
|
||||
// str := `life "%&()+,/:;<>=?@\|`
|
||||
// str := `life "%&()+,/:;<>=?@\|`
|
||||
reg, _ := regexp.Compile("/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\")
|
||||
urlTitle = reg.ReplaceAllString(urlTitle, "-")
|
||||
urlTitle = strings.Trim(urlTitle, "-") // 左右单独的-去掉
|
||||
// 把空格替换成-
|
||||
// urlTitle = strings.Replace(urlTitle, " ", "-", -1)
|
||||
// urlTitle = strings.Replace(urlTitle, " ", "-", -1)
|
||||
for strings.Index(urlTitle, "--") >= 0 { // 防止出现连续的--
|
||||
urlTitle = strings.Replace(urlTitle, "--", "-", -1)
|
||||
}
|
||||
@@ -119,11 +120,20 @@ func fixUrlTitle(urlTitle string) string {
|
||||
|
||||
func getUniqueUrlTitle(userId string, urlTitle string, types string, padding int) string {
|
||||
urlTitle2 := urlTitle
|
||||
|
||||
// 判断urlTitle是不是过长, 过长则截断, 300
|
||||
// 不然生成index有问题
|
||||
// it will not index a single field with more than 1024 bytes.
|
||||
// If you're indexing a field that is 2.5MB, it's not really indexing it, it's being skipped.
|
||||
if len(urlTitle2) > 320 {
|
||||
urlTitle2 = urlTitle2[:300] // 为什么要少些, 因为怕无限循环, 因为把padding截了
|
||||
}
|
||||
|
||||
if padding > 1 {
|
||||
urlTitle2 = urlTitle + "-" + strconv.Itoa(padding)
|
||||
}
|
||||
userIdO := bson.ObjectIdHex(userId)
|
||||
|
||||
|
||||
var collection *mgo.Collection
|
||||
if types == "note" {
|
||||
collection = db.Notes
|
||||
@@ -136,9 +146,10 @@ func getUniqueUrlTitle(userId string, urlTitle string, types string, padding int
|
||||
padding++
|
||||
urlTitle2 = urlTitle + "-" + strconv.Itoa(padding)
|
||||
}
|
||||
|
||||
|
||||
return urlTitle2
|
||||
}
|
||||
|
||||
// types == note,notebook,single
|
||||
func GetUrTitle(userId string, title string, types string) string {
|
||||
urlTitle := strings.Trim(title, " ")
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/robfig/revel"
|
||||
|
||||
"github.com/leanote/leanote/app/service"
|
||||
"github.com/leanote/leanote/app/info"
|
||||
. "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/leanote/leanote/app/lea/memcache"
|
||||
// "github.com/leanote/leanote/app/lea/netutil"
|
||||
"github.com/leanote/leanote/app/lea/html2image"
|
||||
"time"
|
||||
"fmt"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
// "gopkg.in/mgo.v2"
|
||||
// "encoding/json"
|
||||
// "strings"
|
||||
)
|
||||
|
||||
var userId = "5295d4c95b1dd58edb4a7f4f"
|
||||
var userIdO = bson.ObjectIdHex(userId)
|
||||
|
||||
var userId3 = "52b43ae4cfeeae33ef073b2b"
|
||||
var userId3O = bson.ObjectIdHex(userId3)
|
||||
|
||||
var toUserId = "52b4376bcfeeae33ef073b21"
|
||||
var toUserIdO = bson.ObjectIdHex(toUserId)
|
||||
|
||||
var notebookId = "52b2d051ea3ba3d3fb35910c"
|
||||
var notebookIdO = bson.ObjectIdHex(notebookId)
|
||||
var notebookId2O = bson.ObjectIdHex("52b2d051ea3ba3d3fb35910b")
|
||||
|
||||
var noteId = "52b2dd34ea3ba3d3fb35910d"
|
||||
var noteIdO = bson.ObjectIdHex(noteId)
|
||||
|
||||
func testNoteService() {
|
||||
println("testNoteService")
|
||||
noteService := &service.NoteService{}
|
||||
note := noteService.GetBlogNote("535f9e6b19807a4c8d000000")
|
||||
LogJ(note)
|
||||
/*
|
||||
_, notes := noteService.SearchNote("go", "52d3e8ac99c37b7f0d000001", 1, 30, "", false, true);
|
||||
LogJ(notes)
|
||||
*/
|
||||
return;
|
||||
|
||||
/*
|
||||
noteService.AddNote(info.Note{UserId: userIdO,
|
||||
NotebookId: notebookIdO,
|
||||
Title: "life you", Tags: []string{"red", "yellow"}})
|
||||
*/
|
||||
|
||||
noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
|
||||
NoteId: bson.ObjectIdHex("52b4531dcfeeae33ef073b33"),
|
||||
Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
|
||||
|
||||
// noteService.UpdateNoteContent(userId, userId, "52b2dd34ea3ba3d3fb35910d", "life2---------")
|
||||
|
||||
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
|
||||
|
||||
// println(bson.IsObjectIdHex(id))
|
||||
|
||||
// note := noteService.Get(id)
|
||||
// fmt.Println(note)
|
||||
|
||||
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
|
||||
}
|
||||
|
||||
func testNotebookService() {
|
||||
service := &service.NotebookService{}
|
||||
Log(service.IsBlog("52ccb959bcbf21610d000001"))
|
||||
|
||||
return;
|
||||
|
||||
notebooks := service.GetNotebooks(userId3)
|
||||
LogJ(notebooks)
|
||||
|
||||
// b, _ := json.MarshalIndent(notebooks, "", " ")
|
||||
// fmt.Println(string(b))
|
||||
|
||||
// service.UpdateNotebookTitle("52b2cf9eea3ba3d3fb359108", userId, "JS")
|
||||
|
||||
// service.SortNotebooks(userId, map[string]int{"52b2d051ea3ba3d3fb35910c": 4, "52b2d051ea3ba3d3fb35910b": 3})
|
||||
|
||||
/*
|
||||
service.AddNotebook(info.Notebook{UserId: userId3O,
|
||||
ParentNotebookId: bson.ObjectIdHex("52b43c1fcfeeae33ef073b2e"),
|
||||
Title: "Mac-life",
|
||||
Seq: 0})
|
||||
*/
|
||||
|
||||
println("xxx")
|
||||
}
|
||||
|
||||
func testShareService() {
|
||||
service := &service.ShareService{}
|
||||
|
||||
// service.AddShareNote("52bd127dbcbf216d0b000000", 1, "5295d4c95b1dd58edb4a7f4f", "c@a.com")
|
||||
LogJ(service.ListNoteShareUserInfo("52cd11a1bcbf215680000000", "5295d4c95b1dd58edb4a7f4f"))
|
||||
return;
|
||||
|
||||
/*
|
||||
service.AddShareNotebook(info.ShareNotebook{UserId: userId3O, ToUserId: userIdO,
|
||||
NotebookId: bson.ObjectIdHex("52b43c38cfeeae33ef073b30")})
|
||||
*/
|
||||
|
||||
notebooks,_ := service.GetShareNotebooks(userId)
|
||||
LogJ(notebooks)
|
||||
|
||||
// noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
|
||||
// NoteId: bson.ObjectIdHex("52b2dd34ea3ba3d3fb35910d"), Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
|
||||
|
||||
|
||||
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
|
||||
|
||||
// println(bson.IsObjectIdHex(id))
|
||||
|
||||
// note := noteService.Get(id)
|
||||
// fmt.Println(note)
|
||||
|
||||
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
|
||||
}
|
||||
|
||||
|
||||
func testAuthService() {
|
||||
userService := &service.UserService{}
|
||||
LogJ(userService.GetUserInfo("52d26b4e99c37b609a000001"))
|
||||
// userService.AddUser(info.User{UserId: bson.ObjectIdHex("52d26b4e99c37b609a000001"), Email: "leanote@leanote.com", Pwd:"abc"})
|
||||
return;
|
||||
|
||||
authService := &service.AuthService{}
|
||||
authService.Register("f@a.com", "abc")
|
||||
|
||||
// fmt.Println(authService.LogonGetUserInfo("a@a.com", "abc"))
|
||||
|
||||
// noteService.AddNoteContent(info.NoteContent{UserId: userIdO,
|
||||
// NoteId: bson.ObjectIdHex("52b2dd34ea3ba3d3fb35910d"), Content:"xxxxxxxxxxxxxxxxxxxxlifeyou can m<div><p></p></div>"})
|
||||
|
||||
// noteService.AddNote(info.Note{Title: "life", Tags: []string{"life", "life2"}})
|
||||
|
||||
// println(bson.IsObjectIdHex(id))
|
||||
|
||||
// note := noteService.Get(id)
|
||||
// fmt.Println(note)
|
||||
|
||||
// noteService.UpdateTags(id, []string{"lifedd", "life2"});
|
||||
}
|
||||
|
||||
func testTagService() {
|
||||
service := &service.TagService{}
|
||||
// service.AddTags("5295d4c95b1dd58edb4a7f4f", []string{"life", "blue", "yellow"})
|
||||
// service.AddTags("5295d4c95b1dd58edb4a7f4f", []string{"what", "can", "make"})
|
||||
LogJ(service.GetTags("5295d4c95b1dd58edb4a7f4f"))
|
||||
}
|
||||
|
||||
func testHtml2Image() {
|
||||
start := time.Now()
|
||||
Log("start...")
|
||||
// TestFillString()
|
||||
|
||||
|
||||
html2image.ToImage("uid", "username", "noteId", "开发一款属于自己的编程语言,开发一款属于自己的编程语言听起来是不是很酷?", `
|
||||
<div class="each-post">
|
||||
<p>
|
||||
一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace,很喜欢那里的氛围,希望国内其他地方也能有更多这类空间供创客发挥。
|
||||
假如你有一个比较成型的想法,想在硬件领域做点事情,核心团队也基本组好,硬件软件交互基本都有专人了。</p>
|
||||
<p><a>这时候你的首要目标</a>,就是找个地方,按照你的计划,尽早做出一个可用的原型。Techspace可能是一个合适的地方。
|
||||
一个合格的 Techspace 需要有足够专业的器材、场地和资源,你可以和你的团队在里面进行激光切割、快速贴片甚至加工木材等操作,在相对独立的空间内又能同周围的同道友人互相激发切磋。国内现有的 Techspace 没几家,不久前我去深圳特地拜访了当地的 Techspace,很喜欢那里的氛围,希望国内其他地方也能有更多这类空间供创客发挥。
|
||||
深圳 Techspace 位于工业区,园区内有奥迪、BMW 等企业的厂房,在大门口我停下来问保安,M10 栋在哪里?保安答,去 Techspace?最里面靠右手那栋。惊叹于保安的机智,我也在想,莫非有许多朋友都慕名来到这巨大园区寻访 Techspace?
|
||||
穿过一片工业区里的高楼和各种建筑材料
|
||||
</p>
|
||||
<!--
|
||||
<pre class="">cd jpeg-9a/<br>./configure --enable-shared --enable-static
|
||||
make<br>make --- install</pre>
|
||||
<p>
|
||||
life you can, !!@kk
|
||||
</p>
|
||||
|
||||
-->
|
||||
</div>
|
||||
`, "/Users/life/Desktop/a.png")
|
||||
fmt.Printf("time cost %v\n", time.Now().Sub(start))
|
||||
}
|
||||
|
||||
func testLea() {
|
||||
names := ListDir("/Users/life/Documents/Go/package/src/leanote")
|
||||
fmt.Println(names);
|
||||
}
|
||||
|
||||
func main() {
|
||||
revel.BasePath = "/Users/life/Documents/Go/package/src/leanote"
|
||||
// testLea();
|
||||
|
||||
// a, b := SplitFilename("http://ab/c/a.gif#??")
|
||||
// println(a)
|
||||
// println(b)
|
||||
|
||||
// path, ok := netutil.WriteUrl("http://a.36krcnd.com/photo/2014/9bd1a07c0973d79ca05ad13c3c2e16b8.png!slider", "/tmp")
|
||||
// println(path)
|
||||
// testHtml2Image();
|
||||
|
||||
// println(IsObjectId("52d26b4e99c37b609a000001"))
|
||||
|
||||
// b := `请点击链接验证邮箱: <a href="">http://leanote.com/user/activeEmail?token=d8ca086cce5550a6227f9dc84dbac09d</a>. 48小时后过期.`
|
||||
// SendEmail("lifephp@gmail.com", "leanote-验证邮箱", "验证邮", b)
|
||||
|
||||
//_, err := mgo.Dial("mongodb://leanote:nKFAkxKnWkEQy8Vv2LlM@115.28.133.226:27017/leanote")
|
||||
|
||||
// testNotebookService();
|
||||
// testNoteService();
|
||||
// testShareService()
|
||||
// testAuthService()
|
||||
|
||||
// testTagService();
|
||||
/*
|
||||
filename := "你好59.26.png"
|
||||
ext := SubstringByte(filename, strings.LastIndex(filename, "."))
|
||||
ext = strings.ToLower(ext)
|
||||
print(ext)
|
||||
52d26ab199c37b5f80000001
|
||||
|
||||
Log(bson.NewObjectId())
|
||||
Log(bson.NewObjectId())
|
||||
Log(bson.NewObjectId())
|
||||
|
||||
*/
|
||||
// Log(TransferExt("/你好a/b/a.gif", ".jpg"))
|
||||
// TransToGif("/Users/life/Desktop/a2.png", 0, false)
|
||||
|
||||
// Log(IsUsername("xx**x"))
|
||||
|
||||
/*
|
||||
memcache.Set("xx", map[string]string{"A":"you"}, 0)
|
||||
a := memcache.Get("xx")
|
||||
Log(a)
|
||||
*/
|
||||
}
|
||||
23
app/tests/auth_test.go
Normal file
23
app/tests/auth_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
"github.com/leanote/leanote/app/service"
|
||||
// "gopkg.in/mgo.v2"
|
||||
// "fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
db.Init("mongodb://localhost:27017/leanote", "leanote")
|
||||
service.InitService()
|
||||
}
|
||||
|
||||
// 测试登录
|
||||
func TestAuth(t *testing.T) {
|
||||
_, err := service.AuthS.Login("admin", "abc123")
|
||||
if err != nil {
|
||||
t.Error("Admin User Auth Error")
|
||||
}
|
||||
}
|
||||
14
app/tests/db_test.go
Normal file
14
app/tests/db_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/leanote/leanote/app/db"
|
||||
// . "github.com/leanote/leanote/app/lea"
|
||||
// "github.com/leanote/leanote/app/service"
|
||||
// "gopkg.in/mgo.v2"
|
||||
// "fmt"
|
||||
)
|
||||
|
||||
func TestDBConnect(t *testing.T) {
|
||||
db.Init("mongodb://localhost:27017/leanote", "leanote")
|
||||
}
|
||||
3
app/tests/tmp.go
Normal file
3
app/tests/tmp.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package tests
|
||||
func a() {
|
||||
}
|
||||
@@ -11,20 +11,3 @@
|
||||
<script src="/public/admin/js/artDialog/jquery.artDialog.js?skin=default"></script>
|
||||
<script src="/public/js/common.js"></script>
|
||||
<script src="/public/admin/js/admin.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
var pathname = location.pathname; // admin/t
|
||||
var search = location.search; // ?t=xxx, 如果有?page呢
|
||||
var fullPath = pathname;
|
||||
if(search.indexOf("?t=") >= 0) {
|
||||
var fullPath = pathname + search; // /admin/t?t=xxx
|
||||
}
|
||||
|
||||
$("#nav > li").removeClass("active");
|
||||
// 自己
|
||||
var $thisLi = $('#nav a[href^="' + fullPath + '"]').parent();
|
||||
$thisLi.addClass("active");
|
||||
// 父也active
|
||||
$thisLi.parent().parent().addClass('active');
|
||||
});
|
||||
</script>
|
||||
@@ -13,7 +13,6 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="adminUserNav">
|
||||
<a href="#">
|
||||
<i class="fa fa-users icon">
|
||||
@@ -157,7 +156,7 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/admin/t?t=setting/shareNote">
|
||||
<a href="/admin/t?t=setting/share_note">
|
||||
<span>
|
||||
Register Share Note
|
||||
</span>
|
||||
@@ -171,6 +170,15 @@
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/admin/t?t=setting/export_pdf">
|
||||
<span>
|
||||
Export PDF
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -232,6 +240,8 @@
|
||||
v1.0-beta to v1.0-beta.2
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/t?t=upgrade/beta3">
|
||||
<span>
|
||||
v1.0-beta.2/3 to v1.0-beta.4
|
||||
@@ -241,4 +251,4 @@
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</nav>
|
||||
|
||||
52
app/views/admin/setting/export_pdf.html
Normal file
52
app/views/admin/setting/export_pdf.html
Normal file
@@ -0,0 +1,52 @@
|
||||
{{template "admin/top.html" .}}
|
||||
<div class="m-b-md"> <h3 class="m-b-none">Export PDF</h3></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-6">
|
||||
<form id="add_user_form">
|
||||
<section class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Wkhtmltopdf Binary Path</label>
|
||||
<input type="text" class="form-control" placeholder="/usr/local/bin/wkhtmltopdf" name="path" value="{{.str.exportPdfBinPath}}">
|
||||
Leanote use <a target="_blank" href="http://wkhtmltopdf.org">wkhtmltopdf</a> to export pdf. You should install it on your server.
|
||||
<br />
|
||||
Please input the path that wkhtmltopdf installed, e.g. <code>/usr/local/bin/wkhtmltopdf</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="submit" class="btn btn-success btn-s-xs">Submit</button>
|
||||
</footer>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{template "admin/footer.html" .}}
|
||||
<script src="/public/admin/js/jquery-validation-1.13.0/jquery.validate.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
init_validator("#add_user_form");
|
||||
|
||||
$("#submit").click(function(e){
|
||||
e.preventDefault();
|
||||
var t = this;
|
||||
if($("#add_user_form").valid()) {
|
||||
$(t).button('loading');
|
||||
ajaxPost("/adminSetting/exportPdf", getFormJsonData("add_user_form"), function(ret){
|
||||
$(t).button('reset')
|
||||
if(!ret.Ok) {
|
||||
art.alert(ret.Msg)
|
||||
} else {
|
||||
art.tips("Success");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{{template "admin/end.html" .}}
|
||||
166
app/views/album/index.html
Normal file
166
app/views/album/index.html
Normal file
@@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>
|
||||
Leanote Album Image Manager
|
||||
</title>
|
||||
|
||||
<link href="/css/bootstrap-min.css" rel="stylesheet" />
|
||||
<link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
|
||||
<link href="/public/album/css/style-min.css" rel="stylesheet" />
|
||||
|
||||
</head>
|
||||
<body class="md" id="body">
|
||||
<div style="margin: 3px;">
|
||||
<div class="holder"></div>
|
||||
|
||||
<div class="tabs">
|
||||
<ul id="myTab" class="nav nav-tabs">
|
||||
<li class="active"><a href="#images" data-toggle="tab">{{leaMsg . "Images"}}</a></li>
|
||||
<li class=""><a href="#uploadTab" data-toggle="tab">{{leaMsg . "Upload"}}</a></li>
|
||||
<li class=""><a href="#url" data-toggle="tab">{{leaMsg . "Image URL"}}</a></li>
|
||||
</ul>
|
||||
<div id="myTabContent" class="tab-content">
|
||||
<div class="tab-pane fade active in" id="images">
|
||||
<!-- tools -->
|
||||
<div>
|
||||
<form class="form-inline" role="form">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="albums">{{leaMsg . "Albums"}}:</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select class="form-control" id="albumsForList">
|
||||
<option value="">{{leaMsg . "Default"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input class="form-control" type="text" id="key" placeholder="{{leaMsg . "File title search"}}"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<a href="javascript:;" title="refresh" id="refresh"><span class="fa fa-refresh"></span></a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="imagePage">
|
||||
<ul id="imageList" class="clearfix">
|
||||
</ul>
|
||||
|
||||
<!-- pagination -->
|
||||
<div id="paginationContainer">
|
||||
<ul class="pagination">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="imageMask">
|
||||
<div id="noImages">
|
||||
<p>{{msg . "No Images"}}!</p>
|
||||
<btn class="btn btn-default" id="goAddImageBtn">{{leaMsg . "Go to upload images"}}</btn>
|
||||
</div>
|
||||
<div id="loading">
|
||||
loading....
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="uploadTab">
|
||||
<div>
|
||||
<form class="form-inline" role="form">
|
||||
<div class="form-group" id="albumSelect">
|
||||
<label class="control-label" for="albums">{{leaMsg . "Albums"}}:</label>
|
||||
|
||||
<select class="form-control" id="albumsForUpload">
|
||||
<option value="">{{leaMsg . "Default"}}</option>
|
||||
</select>
|
||||
|
||||
<a href="javascript:;" id="deleteAlbumBtn">{{leaMsg . "Delete"}}</a> |
|
||||
<a href="javascript:;" id="renameAlbumBtn">{{leaMsg . "Rename"}}</a> |
|
||||
<a href="javascript:;" id="addAlbumBtn">{{leaMsg . "Add"}}</a>
|
||||
</div>
|
||||
|
||||
<!-- rename or add album -->
|
||||
<div class="form-group" style="display: none" id="addOrUpdateAlbumForm">
|
||||
<input type="text" class="form-control" style="width: 150px" id="albumName">
|
||||
<button class="btn btn-success" type="button" id="addOrUpdateAlbumBtn">{{leaMsg . "Add Album"}}</button>
|
||||
<button class="btn btn-default" type="button" id="cancelAlbumBtn">{{leaMsg . "Cancel"}}</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<span id="msg" class="alert alert-success" style="padding: 3px; display: none"></span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
|
||||
<div id="drop">
|
||||
<a class="btn btn-default">
|
||||
{{leaMsg . "Click to upload images Or Drop images to here"}}
|
||||
</a>
|
||||
<input type="file" name="file" multiple />
|
||||
</div>
|
||||
<ul id="upload-msg">
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="url">
|
||||
<form class="form-inline" id="imageUrlForm" style="">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="imageUrl">{{leaMsg . "Image URL"}}:</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" id="imageUrl" size="51"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-success" id="addImageUrlBtn">{{leaMsg . "Add Image"}}</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="alert alert-danger" id="msgForUrl"
|
||||
style="display: none; padding: 5px; width: 150px;">
|
||||
{{leaMsg . "Can't load this url"}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul id="preview" class="clearfix">
|
||||
</ul>
|
||||
|
||||
<!--
|
||||
<div id="previewAttrs" style="margin-left: 10px">
|
||||
<form class="form-inline" role="form">
|
||||
<div class="form-group">
|
||||
<input class="form-control" id="attrTitle" placeholder="title" size="20" disabled/>
|
||||
<input class="form-control" id="attrWidth" placeholder="width" size="5" disabled/> X
|
||||
<input class="form-control" id="attrHeight" placeholder="height" size="5" disabled/>
|
||||
<label><input type="checkbox" value="1" id="attrConstrain" disabled/> Constrain proportions</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
// javascript global configration
|
||||
var G = {};
|
||||
G.imageSrcPrefix = 'upload';
|
||||
G.perPageItems = 12;
|
||||
G.maxSelected = 1;
|
||||
var UrlPrefix = '{{.siteUrl}}';
|
||||
</script>
|
||||
|
||||
<!--
|
||||
<script src="/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="/js/bootstrap-min.js"></script>
|
||||
<script src="/js/plugins/libs-min/fileupload.js"></script>
|
||||
<script src="/js/jquery.pagination.js"></script>
|
||||
<script src="/public/album/js/main.js"></script>
|
||||
-->
|
||||
|
||||
<script src="/public/album/js/main.all.js"></script>
|
||||
|
||||
</html>
|
||||
@@ -26,10 +26,10 @@
|
||||
</section>
|
||||
<div id="boxFooter">
|
||||
<p>
|
||||
<a href="/index">leanote</a> © 2014
|
||||
<a href="/index">leanote</a> © 2015
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
</section>
|
||||
<div id="boxFooter">
|
||||
<p>
|
||||
<a href="/index">leanote</a> © 2014
|
||||
<a href="/index">leanote</a> © 2015
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
124
app/views/file/pdf.html
Normal file
124
app/views/file/pdf.html
Normal file
@@ -0,0 +1,124 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.title}}</title>
|
||||
<link href="/css/bootstrap.css" rel="stylesheet">
|
||||
<link id="styleLink" href="/css/pdf.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin-top: 30px;
|
||||
}
|
||||
table {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
table th, table td {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
table th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table tr {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
table tr:nth-child(2n) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.mce-item-table, .mce-item-table td, .mce-item-table th, .mce-item-table caption {
|
||||
border: 1px solid #ddd;
|
||||
border-collapse: collapse;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<h1 class="title tex2jax_ignore">
|
||||
{{if .blog.Title}}
|
||||
{{.blog.Title}}
|
||||
{{else}}
|
||||
Untitled
|
||||
{{end}}
|
||||
</h1>
|
||||
<div class="created-time">
|
||||
<!--
|
||||
{{ if .userBlog.Logo}}
|
||||
<img src="{{.userBlog.Logo}}" id="logo">
|
||||
{{else}}
|
||||
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
|
||||
{{end}}
|
||||
{{.userInfo.Username}}
|
||||
-->
|
||||
{{if .blog.Tags}}
|
||||
<img src="{{$.siteUrl}}/images/blog/tag.png" id="tag">
|
||||
{{blogTagsForExport $ .blog.Tags}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="desc">
|
||||
{{if .blog.IsMarkdown }}
|
||||
<div id="markdownContent" style="display: none">
|
||||
<!-- 用textarea装html, 防止得到的值失真 -->
|
||||
<textarea>
|
||||
{{.content | raw}}
|
||||
</textarea>
|
||||
</div>
|
||||
<div id="parsedContent">
|
||||
</div>
|
||||
{{else}}
|
||||
{{.content | raw}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div id="footer">
|
||||
<p class="split"></p>
|
||||
<a href="http://leanote.com"><img src="{{.siteUrl}}/images/logo/leanote_icon_blue.png" id="leanote_logo"/></a>
|
||||
<p>
|
||||
<a href="http://leanote.com">Leanote</a>
|
||||
<br />
|
||||
<a href="http://leanote.com/service">Upgrade Account</a>
|
||||
</p>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<script src="{{.siteUrl}}/js/jquery-1.9.0.min.js"></script>
|
||||
|
||||
<link href="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
|
||||
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
|
||||
|
||||
<script>
|
||||
function ok() {
|
||||
setTimeout(function() {
|
||||
window.status = 'done';
|
||||
}, 0);
|
||||
}
|
||||
</script>
|
||||
|
||||
{{if not .blog.IsMarkdown }}
|
||||
<script>
|
||||
$("pre").addClass("prettyprint");
|
||||
prettyPrint();
|
||||
ok();
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
{{if .blog.IsMarkdown }}
|
||||
<script src="/public/libs/md2html/md2html_for_export.js"></script>
|
||||
<script>
|
||||
var content = $.trim($("#markdownContent textarea").val());
|
||||
md2Html(content, $("#parsedContent"), function(html) {
|
||||
$("pre").addClass("prettyprint");
|
||||
prettyPrint();
|
||||
ok();
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,16 +8,18 @@
|
||||
<br />
|
||||
leanote@leanote.com
|
||||
<br />
|
||||
Copyright © 2014-2015 <a href="http://leanote.com">Leanote</a>
|
||||
Copyright © 2014-2015 <a href="https://leanote.com">Leanote</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<i class="fa fa-globe fa-3x icon-muted"></i>
|
||||
<h2>Join Us</h2>
|
||||
<a href="https://github.com/leanote/leanote">github leanote</a>
|
||||
<a href="https://github.com/leanote/leanote">Github Leanote</a>
|
||||
<br />
|
||||
QQ Group: 158716820
|
||||
QQ Group: 158716820, 158716820
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="keywords" content="leanote,leanote.com">
|
||||
<meta name="description" content="Leanote, {{msg $ "moto"}}">
|
||||
<meta property="qc:admins" content="2451332115167456375" />
|
||||
<meta name="author" content="leanote">
|
||||
<title>{{.title}}</title>
|
||||
|
||||
@@ -46,18 +45,18 @@ function log(o) {
|
||||
|
||||
<ul class="nav navbar-nav navbar-left">
|
||||
<li><a href="/index#" data-target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
|
||||
<li style="position: relative;">
|
||||
<a id="" href="http://leanote.com/service" title="" class="">{{msg . "service"}}</a>
|
||||
<div class="red-circle" style=""></div>
|
||||
</li>
|
||||
|
||||
<li style="position: relative; margin-right: 3px;">
|
||||
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
|
||||
</li>
|
||||
<li><a id="leanoteBlog" href="http://lea.leanote.com" target="_blank" title="lea++, leanote blog platform" class="">lea++</a></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">
|
||||
{{msg . "hi"}}, {{.userInfo.Username}}
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
{{template "home/header.html" .}}
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<section>
|
||||
<div class="header">
|
||||
<h2>leanote, {{msg . "moto"}}</h2>
|
||||
<h2>Leanote, {{msg . "moto"}}</h2>
|
||||
<p>{{msg . "moto3"}}</p>
|
||||
<p>{{msg . "moto2"}}</p>
|
||||
|
||||
<div>
|
||||
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
|
||||
<a class="btn btn-default btn-primary" href="/register">{{msg . "register"}}</a>
|
||||
|
||||
|
||||
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
|
||||
{{if .openRegister}}
|
||||
|
||||
|
||||
OR
|
||||
|
||||
|
||||
<a class="btn btn-default" href="/register">{{msg . "register"}}</a>
|
||||
{{if not .userInfo.UserId}}
|
||||
<a class="btn btn-default" href="/login">{{msg . "login"}}</a>
|
||||
|
||||
|
||||
{{end}}
|
||||
|
||||
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,13 +25,23 @@
|
||||
<div class="img-header">
|
||||
<img src="/images/home/mac-btns.png"/>
|
||||
</div>
|
||||
<img src="/images/home/preview2.png" style="width: 750px;" />
|
||||
<div id="webSliderContainer">
|
||||
<img class="web-slider" data-text="Default theme - markdown" src="/images/slider/v2/default_markdown.png"/>
|
||||
<img class="web-slider hide-img" data-text="Default theme - rich editor" src="/images/slider/v2/simple_tinymce.png"/>
|
||||
<img class="web-slider hide-img" data-text="Simple theme - markdown" src="/images/slider/v2/simple_markdown.png"/>
|
||||
<img class="web-slider hide-img" data-text="Writting mode" src="/images/slider/v2/writting.png"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slider-desc"><span id="webText">Default theme - markdown</span></div>
|
||||
<div class="mobile">
|
||||
<div class="mobile-header">
|
||||
<img src="/images/home/mac-dot.png" />
|
||||
</div>
|
||||
<img class="mobile-image" src="/images/home/mobile.png" />
|
||||
<div id="mobileSliderContainer">
|
||||
<img class="mobile-slider" data-text="Mobile" src="/images/slider/v2/mobile_simple_list.png" />
|
||||
<img class="mobile-slider hide-img" data-text="Mobile markdown" src="/images/slider/v2/mobile_markdown.png" />
|
||||
</div>
|
||||
<div class="slider-desc mobile-text"><span id="mobileText">Default theme - markdown</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -46,6 +52,10 @@
|
||||
<div class="col-md-3">
|
||||
<h3>{{msg . "knowledge"}}</h3>
|
||||
<p>{{msg . "knowledgeInfo"}}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h3>{{msg . "blog"}}</h3>
|
||||
<p>{{msg . "blogInfo"}}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h3>{{msg . "share"}}</h3>
|
||||
@@ -55,81 +65,27 @@
|
||||
<h3>{{msg . "cooperation"}}</h3>
|
||||
<p>{{msg . "cooperationInfo"}}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h3>{{msg . "blog"}}</h3>
|
||||
<p>{{msg . "blogInfo"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" id="download">
|
||||
<h2 style="margin: 20px 0;text-align: center;">{{msg . "download"}}</h2>
|
||||
<div class="row">
|
||||
<div style="width:300px; margin:auto; padding: 10px;">
|
||||
Linux : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz">leanote-linux-v0.4.bin.tar.gz</a> <br />
|
||||
MacOS X : <a href="https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz">leanote-mac-v0.4.bin.tar.gz</a> <br />
|
||||
<br />
|
||||
<a href="https://github.com/leanote/leanote#3-how-to-install-leanote">{{msg . "howToInstallLeanote"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" id="donate">
|
||||
<h2 style="margin: 20px 0;text-align: center;">{{msg . "donate"}}</h2>
|
||||
<div class="row">
|
||||
<div style="width:500px; margin:auto; padding: 10px;">
|
||||
<p>
|
||||
您可以通过支付宝向leanote捐赠, 所捐赠的钱将用来leanote开发.
|
||||
</p>
|
||||
<p>
|
||||
支付宝账号: <b>pay@leanote.com</b>
|
||||
</p>
|
||||
<p>
|
||||
或使用支付宝扫以下二维码捐赠:
|
||||
</p>
|
||||
<p style="text-align: center">
|
||||
<img src="/images/leanote/leanote_alipay.jpg" style="padding: 10px;
|
||||
border: 2px solid #eee;
|
||||
border-radius: 10px;"/>
|
||||
</p>
|
||||
<p>
|
||||
我们会定期将捐赠名单发布在 <a href="http://leanote.com/blog/view/5417ecf81a910828fd000000">捐赠列表</a>
|
||||
</p>
|
||||
<p>
|
||||
感谢您对leanote的支持!
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
有任何疑问, 建议或需其它服务或支持, 欢迎联系 <code>leanote@leanote.com</code> 或加入官方QQ群: <code>158716820</code> 谢谢!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{template "home/footer.html"}}
|
||||
|
||||
<script src="/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="/js/bootstrap.js"></script>
|
||||
<script>
|
||||
var lang = '{{.locale}}';
|
||||
</script>
|
||||
<script src="/js/home/index.js"></script>
|
||||
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
/*
|
||||
var u = navigator.userAgent;
|
||||
var isMobile = u.indexOf('Android')>-1 || u.indexOf('Linux')>-1;
|
||||
if(isMobile || $("body").width() < 600) {
|
||||
location.href = "/mobile/index";
|
||||
}
|
||||
*/
|
||||
// 平滑滚动
|
||||
$(".smooth-scroll").click(function(e) {
|
||||
e.preventDefault();
|
||||
var t = $(this).attr("target");
|
||||
var targetOffset = $(t).offset().top - 80;
|
||||
$('html,body').animate({scrollTop: targetOffset}, 300);
|
||||
});
|
||||
});
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "//hm.baidu.com/hm.js?d0c988e37b452b3bf1220d45b30f2de2";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,138 +0,0 @@
|
||||
{{template "home/header.html" .}}
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<section>
|
||||
<div class="header">
|
||||
<h2>Leanote, {{msg . "moto"}}</h2>
|
||||
<p>{{msg . "moto3"}}</p>
|
||||
<p>
|
||||
Knowledge, Blog, Sharing, Cooperation... all in leanote
|
||||
</p>
|
||||
<div>
|
||||
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
|
||||
|
||||
|
||||
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
|
||||
|
||||
|
||||
<a class="btn btn-default" href="/login">{{msg . "login"}}</a>
|
||||
{{if .openRegister}}
|
||||
|
||||
|
||||
<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="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);
|
||||
});
|
||||
|
||||
function setCookie(name, value) {
|
||||
var Days = 10*365;
|
||||
var exp = new Date();
|
||||
exp.setTime(exp.getTime() + Days*24*60*60*1000);
|
||||
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
|
||||
}
|
||||
|
||||
$('#lang a').click(function() {
|
||||
var lang = $(this).data('lang');
|
||||
setCookie('LEANOTE_LANG', lang);
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,138 +0,0 @@
|
||||
{{template "home/header.html" .}}
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<section>
|
||||
<div class="header">
|
||||
<h2>Leanote, {{msg . "moto"}}</h2>
|
||||
<p>{{msg . "moto3"}}</p>
|
||||
<p>{{msg . "moto2"}}</p>
|
||||
|
||||
<div>
|
||||
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
|
||||
|
||||
|
||||
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
|
||||
|
||||
|
||||
<a class="btn btn-default" href="/login">{{msg . "login"}}</a>
|
||||
{{if .openRegister}}
|
||||
|
||||
|
||||
<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="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);
|
||||
});
|
||||
|
||||
function setCookie(name, value) {
|
||||
var Days = 10*365;
|
||||
var exp = new Date();
|
||||
exp.setTime(exp.getTime() + Days*24*60*60*1000);
|
||||
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
|
||||
}
|
||||
|
||||
$('#lang a').click(function() {
|
||||
var lang = $(this).data('lang');
|
||||
setCookie('LEANOTE_LANG', lang);
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -72,7 +72,7 @@ $(function() {
|
||||
showMsg("{{msg . "inputEmail"}}", "email");
|
||||
return;
|
||||
} else {
|
||||
var myreg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[0-9a-zA-Z]{2,3}$/;
|
||||
var myreg = /^([a-zA-Z0-9]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-_]+@([a-zA-Z0-9\-]+[_|\_|\.|\-]?)*[a-zA-Z0-9\-]+\.[0-9a-zA-Z]{2,6}$/;
|
||||
if(!myreg.test(email)) {
|
||||
showMsg("{{msg . "wrongEmail"}}", "email");
|
||||
return;
|
||||
@@ -117,4 +117,4 @@ $(function() {
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -36,18 +36,12 @@ var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2
|
||||
require.config({
|
||||
baseUrl: '/public',
|
||||
paths: {
|
||||
// ajax upload image/attach
|
||||
'attachment_upload': 'js/app/attachment_upload',
|
||||
'jquery.ui.widget': 'tinymce/plugins/leaui_image/public/js/jquery.ui.widget',
|
||||
'fileupload': '/tinymce/plugins/leaui_image/public/js/jquery.fileupload',
|
||||
'iframe-transport': '/tinymce/plugins/leaui_image/public/js/jquery.iframe-transport'
|
||||
},
|
||||
shim: {
|
||||
'fileupload': {deps: ['jquery.ui.widget', 'iframe-transport']}
|
||||
'avatar': 'member/js/avatar',
|
||||
'fileupload': 'js/plugins/libs-min/fileupload',
|
||||
}
|
||||
});
|
||||
$(function() {
|
||||
require(['attachment_upload'], function(attachment_upload) {});
|
||||
require(['avatar'], function(avatar) {});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -13,11 +13,15 @@
|
||||
<span class="label label-green">{{msg . "verified"}}</span>
|
||||
{{else}}
|
||||
<span class="label label-red">{{msg . "unVerified"}}</span>
|
||||
<a class="raw nowToActive">{{msg . "verifiedNow"}}</a>
|
||||
<a class="a raw nowToActive">{{msg . "verifiedNow"}}</a>
|
||||
{{msg . "or"}}
|
||||
<a class="raw reSendActiveEmail">{{msg . "resendVerifiedEmail"}}</a>
|
||||
<a class="a raw reSendActiveEmail">{{msg . "resendVerifiedEmail"}}</a>
|
||||
{{end}}
|
||||
|
||||
<!--
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="alert alert-danger" id="emailMsg" style="display: none"></div>
|
||||
<div class="form-group">
|
||||
<label>{{msg . "email"}}</label>
|
||||
@@ -29,12 +33,28 @@
|
||||
]'
|
||||
data-msg_target="#emailMsg"
|
||||
/>
|
||||
{{msg . "updateEmailTips"}}
|
||||
<div class="form-tips">{{msg . "updateEmailTips"}}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="pwd">{{msg . "password"}}</label>
|
||||
<input type="password" class="form-control" id="pwd" name="pwd"
|
||||
data-rules='[
|
||||
{rule: "required", msg: "inputPassword"},
|
||||
{rule: "password", msg: "errorPassword"}
|
||||
]'
|
||||
data-msg_target="#emailMsg"
|
||||
>
|
||||
<div class="form-tips">{{msg . "inputLoginPasswordTips"}}</div>
|
||||
</div>
|
||||
|
||||
-->
|
||||
</div>
|
||||
<!--
|
||||
<footer class="panel-footer text-right bg-light lter">
|
||||
<button type="submit" id="emailBtn" class="btn btn-success">{{msg . "submit"}}</button>
|
||||
</footer>
|
||||
-->
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
@@ -76,7 +96,8 @@ $("#emailBtn").click(function(e) {
|
||||
return;
|
||||
}
|
||||
var email = $("#email").val();
|
||||
post("/user/updateEmailSendActiveEmail", {email: email}, function(e) {
|
||||
var pwd = $("#pwd").val();
|
||||
post("/user/updateEmailSendActiveEmail", {email: email, pwd: pwd}, function(e) {
|
||||
if(e.Ok) {
|
||||
var url = getEmailLoginAddress(email);
|
||||
showAlert("#emailMsg", getMsg("verifiedEmaiHasSent") +" <a href='" + url + "' target='_blank'>" + getMsg("checkEmail") + "</a>", "success");
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
]'
|
||||
data-msg_target="#pwdMsg"
|
||||
>
|
||||
{{msg . "passwordTips"}}
|
||||
<div class="form-tips">{{msg . "passwordTips"}}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="pwd2">{{msg . "password2"}}</label>
|
||||
|
||||
@@ -10,21 +10,23 @@
|
||||
<meta name="description" content="Leanote, {{msg $ "moto"}}">
|
||||
<title>Leanote, {{msg $ "moto"}}</title>
|
||||
|
||||
<link href="/css/bootstrap.css" rel="stylesheet" />
|
||||
<link href="/css/bootstrap-min.css" rel="stylesheet" />
|
||||
<!-- 先加载, 没有样式, 宽度不定 -->
|
||||
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" rel="stylesheet"/>
|
||||
<!-- leanote css -->
|
||||
<link href="/css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet" />
|
||||
<link href="/css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
|
||||
<link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
|
||||
<link href="/css/zTreeStyle/zTreeStyle-min.css" rel="stylesheet" />
|
||||
<!-- mdeditor -->
|
||||
<link href="/public/dist/themes/default.css" rel="stylesheet" />
|
||||
<link href="/public/dist/themes/default-min.css" rel="stylesheet" />
|
||||
<!-- context-menu -->
|
||||
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu-min.css" type="text/css" />
|
||||
|
||||
<script>
|
||||
var hash = location.hash;
|
||||
if(hash.indexOf("writing") >= 0) {
|
||||
var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css" type="text/css" id="themeLink" />';
|
||||
} else {
|
||||
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css" type="text/css" id="themeLink" />';
|
||||
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css?t=4" type="text/css" id="themeLink" />';
|
||||
}
|
||||
document.write(files);
|
||||
</script>
|
||||
@@ -60,7 +62,7 @@ function log(o) {
|
||||
<!-- search -->
|
||||
<div class="pull-left" id="searchWrap">
|
||||
<form class="navbar-form form-inline col-lg-2" id="searchNote">
|
||||
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
|
||||
<input class="form-control" placeholder="{{msg . "SearchNote"}}" type="text" id="searchNoteInput">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -68,14 +70,15 @@ function log(o) {
|
||||
<div class="pull-left" style="" id="newNoteWrap">
|
||||
<!-- 新建笔记 -->
|
||||
<div id="newMyNote">
|
||||
<a id="newNoteBtn" title="{{msg . "newNote"}}">
|
||||
<i class="fa fa-file-o"></i>
|
||||
<a class="new-note" id="newNoteBtn" title="{{msg . "newNote"}}">
|
||||
<!--<i class="fa fa-file-o"></i>-->
|
||||
<span class="new-note-text">{{msg . "newNote"}}</span>
|
||||
<span class="new-note-text-abbr">{{msg . "new"}}</span>
|
||||
</a>
|
||||
<span class="new-split">|</span>
|
||||
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
<span class="new-markdown-text">Markdown</span>
|
||||
<a class="new-note new-markdown" id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
<!-- <i class="lea-icon icon-markdown"></i> -->
|
||||
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
|
||||
<span class="new-markdown-text-abbr">Md</span>
|
||||
</a>
|
||||
<span class="for-split"> - </span>
|
||||
@@ -86,7 +89,7 @@ function log(o) {
|
||||
<i class="fa fa-angle-down"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
|
||||
<input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/>
|
||||
<input type="text" placeholder="{{msg . "SearchNotebook"}}" class="form-control" id="searchNotebookForAdd"/>
|
||||
<ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
|
||||
</ul>
|
||||
</div>
|
||||
@@ -95,14 +98,14 @@ function log(o) {
|
||||
|
||||
<!-- 只为新建别人的笔记 -->
|
||||
<div id="newSharedNote" style="display: none">
|
||||
<a id="newSharedNoteBtn">
|
||||
<i class="fa fa-file-o"></i>
|
||||
<a id="newSharedNoteBtn" class="new-note">
|
||||
<!--<i class="fa fa-file-o"></i> -->
|
||||
<span class="new-note-text">{{msg . "newNote"}}</span>
|
||||
<span class="new-note-text-abbr">{{msg . "new"}}</span>
|
||||
</a>
|
||||
<span class="new-split">|</span>
|
||||
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
<span class="new-markdown-text">Markdown</span>
|
||||
<a id="newShareNoteMarkdownBtn" class="new-note" title="{{msg . "newMarkdown"}}">
|
||||
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
|
||||
<span class="new-markdown-text-abbr">Md</span>
|
||||
</a>
|
||||
<span class="for-split"> - </span>
|
||||
@@ -157,7 +160,7 @@ function log(o) {
|
||||
</li>
|
||||
|
||||
<li role="presentation" class="my-link" >
|
||||
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
|
||||
<a target="_blank" href="{{$.userInfo.BlogUrl}}">
|
||||
<i class="fa fa-bold"></i>
|
||||
{{msg . "myBlog"}}</a>
|
||||
</li>
|
||||
@@ -267,7 +270,7 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<div class="folderBody">
|
||||
<input type="text" class="form-control" id="searchNotebookForList" placeholder="Search notebook"/>
|
||||
<input type="text" class="form-control" id="searchNotebookForList" placeholder="{{msg . "SearchNotebook"}}"/>
|
||||
<ul class="ztree" id="notebookList"></ul>
|
||||
<ul class="ztree" id="notebookListForSearch"></ul>
|
||||
</div>
|
||||
@@ -472,34 +475,36 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<ul class="pull-right" id="editorTool">
|
||||
<li><a class="ios7-a " id="saveBtn" title="ctrl+s"
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-save"></span>
|
||||
{{msg . "save"}}</a></li>
|
||||
|
||||
<li><a class="ios7-a " id="saveBtn"
|
||||
data-toggle="dropdown" title="ctrl+s {{msg . "save"}}">
|
||||
<span class="fa fa-save"></span></a></li>
|
||||
|
||||
<li class="dropdown" id="noteInfoDropdown">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" title="{{msg . "Information"}}">
|
||||
<span class="fa fa-info"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu" id="noteInfo"></div>
|
||||
</li>
|
||||
|
||||
<li class="dropdown" id="attachDropdown">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach" title="{{msg . "attachments"}}">
|
||||
<span class="fa fa-paperclip"></span>
|
||||
{{msg . "attachments"}}<span id="attachNum"></span>
|
||||
<span id="attachNum"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu" id="attachMenu">
|
||||
<ul id="attachList">
|
||||
|
||||
</ul>
|
||||
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data">
|
||||
<div id="dropAttach" class="dropzone">
|
||||
<a class="btn btn-success btn-choose-file">
|
||||
<i class="fa fa-upload"></i>
|
||||
<span>Choose File</span>
|
||||
<span>{{msg . "Choose File"}}</span>
|
||||
</a>
|
||||
<a class="btn btn-default" id="downloadAllBtn">
|
||||
<i class="fa fa-download"></i>
|
||||
<span>Download All</span>
|
||||
</a>
|
||||
<a class="btn btn-default" id="linkAllBtn">
|
||||
<i class="fa fa-link"></i>
|
||||
<span>Link All</span>
|
||||
<span>{{msg . "Download All"}}</span>
|
||||
</a>
|
||||
|
||||
<input type="file" name="file" multiple/>
|
||||
</div>
|
||||
<div id="attachUploadMsg">
|
||||
@@ -508,14 +513,12 @@ function log(o) {
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li><a class="ios7-a " id="tipsBtn"
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-question"></span>
|
||||
{{msg . "editorTips"}}</a></li>
|
||||
<li><a class="ios7-a " id="contentHistory"
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-history"></span>
|
||||
{{msg . "history"}}</a></li>
|
||||
data-toggle="dropdown" title="{{msg . "history"}}">
|
||||
<span class="fa fa-history"></span></a></li>
|
||||
<li><a class="ios7-a " id="tipsBtn"
|
||||
data-toggle="dropdown" title="{{msg . "editorTips"}}">
|
||||
<span class="fa fa-question"></span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -527,7 +530,7 @@ function log(o) {
|
||||
<div id="noteReadTop">
|
||||
<h2 id="noteReadTitle"></h2>
|
||||
<div class="clearfix" id="noteReadInfo">
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
<span id="noteReadTags"></span>
|
||||
|
||||
<!-- 修改时间 -->
|
||||
@@ -535,13 +538,13 @@ function log(o) {
|
||||
<span id="noteReadUpdatedTime"></span>
|
||||
|
||||
<!-- 修改时间 -->
|
||||
<i class="fa fa-clock-o"></i> {{msg . "create"}}
|
||||
<i class="fa fa-clock-o"></i> {{msg . "create"}}
|
||||
<span id="noteReadCreatedTime"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editor">
|
||||
<div id="editor" class="read-only">
|
||||
<!-- 编辑器 -->
|
||||
<div id="mceToolbar">
|
||||
<div id="mceToolbarContainer">
|
||||
@@ -549,10 +552,10 @@ function log(o) {
|
||||
<a id="moreBtn"> <i class="fa more-fa"></i></a>
|
||||
</div>
|
||||
<!-- 查看信息 -->
|
||||
<div id="infoToolbar" class="info-toolbar">
|
||||
<div id="infoToolbar" class="info-toolbar invisible">
|
||||
<a class="toolbar-update"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
|
||||
<span class="lang">{{msg . "created"}}</span>: <span class="created-time">2015-04-03</span>
|
||||
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time">2015-05-01</span>
|
||||
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
|
||||
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editorBg"></div>
|
||||
@@ -568,7 +571,7 @@ function log(o) {
|
||||
<!-- leaui image drop image to editor-->
|
||||
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
|
||||
<div id="drop">
|
||||
Drop images to here
|
||||
{{leaMsg . "Drop images to here"}}
|
||||
<input type="file" name="file" multiple style="display: none"/>
|
||||
</div>
|
||||
<ul id="uploadMsg">
|
||||
@@ -581,16 +584,15 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdEditor">
|
||||
|
||||
<div id="mdEditor" class="read-only">
|
||||
<div class="layout-wrapper-l1">
|
||||
<div class="layout-wrapper-l2">
|
||||
<div class="navbar navbar-default">
|
||||
<div class="navbar-inner" id="wmd-button-bar">
|
||||
<div id="mdInfoToolbar" class="info-toolbar">
|
||||
<div id="mdInfoToolbar" class="info-toolbar invisible">
|
||||
<a class="toolbar-update"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
|
||||
<span class="lang">{{msg . "created"}}</span>: <span class="created-time">2015-04-03</span>
|
||||
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time">2015-05-01</span>
|
||||
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
|
||||
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
|
||||
</div>
|
||||
|
||||
<div class="wmd-button-bar-inner">
|
||||
@@ -609,7 +611,7 @@ function log(o) {
|
||||
<!-- 帮助 -->
|
||||
<ul class="nav left-buttons">
|
||||
<li class="wmd-button-group6 btn-group">
|
||||
<li class="wmd-button btn btn-success" id="wmd-help-button" title="Markdown syntax" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
|
||||
<li class="wmd-button btn btn-success" id="wmd-help-button" title="{{msg . "Markdown syntax"}}" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -689,32 +691,6 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<!-- mdEditor -->
|
||||
<!-- v2 -->
|
||||
<div class="modal fade modal-insert-link">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Hyperlink</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please provide the link URL and an optional title:</p>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="icon-globe"></i></span><input
|
||||
id="input-insert-link" type="text" class="col-sm-5 form-control"
|
||||
placeholder='http://example.com/ "optional title"' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a>
|
||||
<a href="#" class="btn btn-primary action-insert-link"
|
||||
data-dismiss="modal">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 插入图片 -->
|
||||
<div class="modal fade modal-insert-image">
|
||||
<div class="modal-dialog" style="width: 840px;max-width:100%;">
|
||||
@@ -722,15 +698,15 @@ function log(o) {
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Image</h4>
|
||||
<h4 class="modal-title">{{msg . "Image"}}</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="padding-top: 0; padding-bottom: 0">
|
||||
<iframe name="mdImageManager" style="width: 100%; height: 350px" scrolling="no" id="leauiIfrForMD" src="" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn btn-default"
|
||||
data-dismiss="modal">Cancel</a> <a href="#"
|
||||
class="btn btn-primary action-insert-image" data-dismiss="modal">Insert Image</a>
|
||||
data-dismiss="modal">{{msg . "Cancel"}}</a> <a href="#"
|
||||
class="btn btn-primary action-insert-image" data-dismiss="modal">{{msg . "Insert Image"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -745,7 +721,7 @@ function log(o) {
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="editorDialog-title"></h4>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
<div class="input-group">
|
||||
@@ -855,24 +831,7 @@ function log(o) {
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- 编辑器提示 -->
|
||||
<div class="modal fade bs-modal-sm" id="tipsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" class="modalTitle">{{msg . "editorTips"}}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{msg . "editorTipsInfo"}}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
|
||||
</div>
|
||||
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- modal 静态区域 -->
|
||||
<div class="display: hidden">
|
||||
@@ -880,12 +839,12 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="/js/jquery.ztree.all-3.5.js"></script>
|
||||
<script src="/js/i18n/msg.{{.locale}}.js"></script>
|
||||
<script src="/js/common.js"></script>
|
||||
|
||||
<script>
|
||||
var UrlPrefix = '{{.siteUrl}}'; // 为了发weibo
|
||||
var LEA = {};
|
||||
LEA.s1 = new Date();
|
||||
LEA.locale = "{{.locale}}";
|
||||
var UrlPrefix = '{{.siteUrl}}';
|
||||
var UserInfo = {{.userInfo|jsonJs}};
|
||||
var notebooks = {{.notebooks|jsonJs}};
|
||||
var shareNotebooks = {{.shareNotebooks|jsonJs}};
|
||||
@@ -898,40 +857,53 @@ var notes = {{.notes|jsonJs}};
|
||||
var latestNotes = {{if .latestNotes}}{{.latestNotes|jsonJs}}{{else}}[]{{end}};
|
||||
var noteContentJson = {{.noteContentJson|jsonJs}};
|
||||
var tagsJson = {{.tags|jsonJs}};
|
||||
LEA.locale = "{{.locale}}";
|
||||
var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2
|
||||
var GlobalConfigs = {{.globalConfigs|jsonJs}};
|
||||
<!-- pro_tinymce_init_js -->
|
||||
</script>
|
||||
|
||||
<!-- 渲染view -->
|
||||
<script src="/tinymce/tinymce.js"></script>
|
||||
<script src="/js/i18n/msg.{{.locale}}.js"></script>
|
||||
|
||||
<!-- pro_dep_js -->
|
||||
|
||||
<!-- dev -->
|
||||
<script src="/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="/js/jquery.ztree.all-3.5.js"></script>
|
||||
<script src="/js/object_id-min.js"></script>
|
||||
<script src="/public/tinymce/tinymce.js"></script>
|
||||
<!-- /dev -->
|
||||
|
||||
<script src="/public/libs/ace/ace.js"></script>
|
||||
<script src="/js/app/page.js"></script>
|
||||
|
||||
<!-- dev -->
|
||||
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
|
||||
<script src="/js/contextmenu/jquery.contextmenu.js"></script>
|
||||
<script src="/js/common.js"></script>
|
||||
<script src="/js/bootstrap-min.js"></script>
|
||||
<script src="/js/app/note.js"></script>
|
||||
<script src="/js/app/tag.js"></script>
|
||||
<script src="/js/app/notebook.js"></script>
|
||||
<script src="/js/app/share.js"></script>
|
||||
<script src="/js/object_id-min.js"></script>
|
||||
<script src="/js/app/page.js?t=1"></script>
|
||||
<!-- /dev -->
|
||||
|
||||
<!-- pro_app_js -->
|
||||
|
||||
<script>
|
||||
initPage();
|
||||
</script>
|
||||
|
||||
<!-- context-menu -->
|
||||
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
|
||||
|
||||
<!-- v2 use require.js, mdeditor -->
|
||||
<script>
|
||||
// 当tinymce.dev.js时, 请注释require
|
||||
window.require = {
|
||||
baseUrl: '/public',
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- pro_markdown_js -->
|
||||
|
||||
<!-- dev -->
|
||||
<script src="/js/require.js"></script>
|
||||
<script src="/public/dist/main.min.js"></script>
|
||||
<script src="/public/js/upload_paste/main.js"></script>
|
||||
</script>
|
||||
<!-- /dev -->
|
||||
|
||||
<script src="/public/js/plugins/main.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -10,21 +10,23 @@
|
||||
<meta name="description" content="Leanote, {{msg $ "moto"}}">
|
||||
<title>Leanote, {{msg $ "moto"}}</title>
|
||||
|
||||
<link href="/css/bootstrap.css" rel="stylesheet" />
|
||||
<link href="/css/bootstrap-min.css" rel="stylesheet" />
|
||||
<!-- 先加载, 没有样式, 宽度不定 -->
|
||||
<link rel="stylesheet" href="/tinymce/skins/custom/skin.min.css" rel="stylesheet"/>
|
||||
<!-- leanote css -->
|
||||
<link href="/css/font-awesome-4.2.0/css/font-awesome.css" rel="stylesheet" />
|
||||
<link href="/css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
|
||||
<link href="/css/font-awesome-4.2.0/css/font-awesome-min.css" rel="stylesheet" />
|
||||
<link href="/css/zTreeStyle/zTreeStyle-min.css" rel="stylesheet" />
|
||||
<!-- mdeditor -->
|
||||
<link href="/public/dist/themes/default.css" rel="stylesheet" />
|
||||
<link href="/public/dist/themes/default-min.css" rel="stylesheet" />
|
||||
<!-- context-menu -->
|
||||
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu-min.css" type="text/css" />
|
||||
|
||||
<script>
|
||||
var hash = location.hash;
|
||||
if(hash.indexOf("writing") >= 0) {
|
||||
var files = '<link rel="stylesheet" href="/css/theme/writting-overwrite.css" type="text/css" id="themeLink" />';
|
||||
} else {
|
||||
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css" type="text/css" id="themeLink" />';
|
||||
var files ='<link rel="stylesheet" href="/css/theme/{{if .userInfo.Theme}}{{.userInfo.Theme}}{{else}}default{{end}}.css?t=4" type="text/css" id="themeLink" />';
|
||||
}
|
||||
document.write(files);
|
||||
</script>
|
||||
@@ -60,7 +62,7 @@ function log(o) {
|
||||
<!-- search -->
|
||||
<div class="pull-left" id="searchWrap">
|
||||
<form class="navbar-form form-inline col-lg-2" id="searchNote">
|
||||
<input class="form-control" placeholder="Search" type="text" id="searchNoteInput">
|
||||
<input class="form-control" placeholder="{{msg . "SearchNote"}}" type="text" id="searchNoteInput">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -68,14 +70,15 @@ function log(o) {
|
||||
<div class="pull-left" style="" id="newNoteWrap">
|
||||
<!-- 新建笔记 -->
|
||||
<div id="newMyNote">
|
||||
<a id="newNoteBtn" title="{{msg . "newNote"}}">
|
||||
<i class="fa fa-file-o"></i>
|
||||
<a class="new-note" id="newNoteBtn" title="{{msg . "newNote"}}">
|
||||
<!--<i class="fa fa-file-o"></i>-->
|
||||
<span class="new-note-text">{{msg . "newNote"}}</span>
|
||||
<span class="new-note-text-abbr">{{msg . "new"}}</span>
|
||||
</a>
|
||||
<span class="new-split">|</span>
|
||||
<a id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
<span class="new-markdown-text">Markdown</span>
|
||||
<a class="new-note new-markdown" id="newNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
<!-- <i class="lea-icon icon-markdown"></i> -->
|
||||
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
|
||||
<span class="new-markdown-text-abbr">Md</span>
|
||||
</a>
|
||||
<span class="for-split"> - </span>
|
||||
@@ -86,7 +89,7 @@ function log(o) {
|
||||
<i class="fa fa-angle-down"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
|
||||
<input type="text" placeholder="Search notebook" class="form-control" id="searchNotebookForAdd"/>
|
||||
<input type="text" placeholder="{{msg . "SearchNotebook"}}" class="form-control" id="searchNotebookForAdd"/>
|
||||
<ul class="clearfix" role="menu" aria-labelledby="listNotebookDropdownMenu" id="notebookNavForNewNote">
|
||||
</ul>
|
||||
</div>
|
||||
@@ -95,14 +98,14 @@ function log(o) {
|
||||
|
||||
<!-- 只为新建别人的笔记 -->
|
||||
<div id="newSharedNote" style="display: none">
|
||||
<a id="newSharedNoteBtn">
|
||||
<i class="fa fa-file-o"></i>
|
||||
<a id="newSharedNoteBtn" class="new-note">
|
||||
<!--<i class="fa fa-file-o"></i> -->
|
||||
<span class="new-note-text">{{msg . "newNote"}}</span>
|
||||
<span class="new-note-text-abbr">{{msg . "new"}}</span>
|
||||
</a>
|
||||
<span class="new-split">|</span>
|
||||
<a id="newShareNoteMarkdownBtn" title="{{msg . "newMarkdown"}}">
|
||||
<span class="new-markdown-text">Markdown</span>
|
||||
<a id="newShareNoteMarkdownBtn" class="new-note" title="{{msg . "newMarkdown"}}">
|
||||
<span class="new-markdown-text">{{msg . "newMarkdown"}}</span>
|
||||
<span class="new-markdown-text-abbr">Md</span>
|
||||
</a>
|
||||
<span class="for-split"> - </span>
|
||||
@@ -157,7 +160,7 @@ function log(o) {
|
||||
</li>
|
||||
|
||||
<li role="presentation" class="my-link" >
|
||||
<a target="_blank" href="{{$.blogUrl}}/{{.userInfo.Username}}">
|
||||
<a target="_blank" href="{{$.userInfo.BlogUrl}}">
|
||||
<i class="fa fa-bold"></i>
|
||||
{{msg . "myBlog"}}</a>
|
||||
</li>
|
||||
@@ -267,7 +270,7 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<div class="folderBody">
|
||||
<input type="text" class="form-control" id="searchNotebookForList" placeholder="Search notebook"/>
|
||||
<input type="text" class="form-control" id="searchNotebookForList" placeholder="{{msg . "SearchNotebook"}}"/>
|
||||
<ul class="ztree" id="notebookList"></ul>
|
||||
<ul class="ztree" id="notebookListForSearch"></ul>
|
||||
</div>
|
||||
@@ -472,34 +475,36 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<ul class="pull-right" id="editorTool">
|
||||
<li><a class="ios7-a " id="saveBtn" title="ctrl+s"
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-save"></span>
|
||||
{{msg . "save"}}</a></li>
|
||||
|
||||
<li><a class="ios7-a " id="saveBtn"
|
||||
data-toggle="dropdown" title="ctrl+s {{msg . "save"}}">
|
||||
<span class="fa fa-save"></span></a></li>
|
||||
|
||||
<li class="dropdown" id="noteInfoDropdown">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" title="{{msg . "Information"}}">
|
||||
<span class="fa fa-info"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu" id="noteInfo"></div>
|
||||
</li>
|
||||
|
||||
<li class="dropdown" id="attachDropdown">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
|
||||
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach" title="{{msg . "attachments"}}">
|
||||
<span class="fa fa-paperclip"></span>
|
||||
{{msg . "attachments"}}<span id="attachNum"></span>
|
||||
<span id="attachNum"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu" id="attachMenu">
|
||||
<ul id="attachList">
|
||||
|
||||
</ul>
|
||||
<form id="uploadAttach" method="post" action="/attach/UploadAttach" enctype="multipart/form-data">
|
||||
<div id="dropAttach" class="dropzone">
|
||||
<a class="btn btn-success btn-choose-file">
|
||||
<i class="fa fa-upload"></i>
|
||||
<span>Choose File</span>
|
||||
<span>{{msg . "Choose File"}}</span>
|
||||
</a>
|
||||
<a class="btn btn-default" id="downloadAllBtn">
|
||||
<i class="fa fa-download"></i>
|
||||
<span>Download All</span>
|
||||
</a>
|
||||
<a class="btn btn-default" id="linkAllBtn">
|
||||
<i class="fa fa-link"></i>
|
||||
<span>Link All</span>
|
||||
<span>{{msg . "Download All"}}</span>
|
||||
</a>
|
||||
|
||||
<input type="file" name="file" multiple/>
|
||||
</div>
|
||||
<div id="attachUploadMsg">
|
||||
@@ -508,14 +513,12 @@ function log(o) {
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li><a class="ios7-a " id="tipsBtn"
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-question"></span>
|
||||
{{msg . "editorTips"}}</a></li>
|
||||
<li><a class="ios7-a " id="contentHistory"
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-history"></span>
|
||||
{{msg . "history"}}</a></li>
|
||||
data-toggle="dropdown" title="{{msg . "history"}}">
|
||||
<span class="fa fa-history"></span></a></li>
|
||||
<li><a class="ios7-a " id="tipsBtn"
|
||||
data-toggle="dropdown" title="{{msg . "editorTips"}}">
|
||||
<span class="fa fa-question"></span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -527,7 +530,7 @@ function log(o) {
|
||||
<div id="noteReadTop">
|
||||
<h2 id="noteReadTitle"></h2>
|
||||
<div class="clearfix" id="noteReadInfo">
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
<i class="fa fa-bookmark-o"></i>
|
||||
<span id="noteReadTags"></span>
|
||||
|
||||
<!-- 修改时间 -->
|
||||
@@ -535,13 +538,13 @@ function log(o) {
|
||||
<span id="noteReadUpdatedTime"></span>
|
||||
|
||||
<!-- 修改时间 -->
|
||||
<i class="fa fa-clock-o"></i> {{msg . "create"}}
|
||||
<i class="fa fa-clock-o"></i> {{msg . "create"}}
|
||||
<span id="noteReadCreatedTime"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editor">
|
||||
<div id="editor" class="read-only">
|
||||
<!-- 编辑器 -->
|
||||
<div id="mceToolbar">
|
||||
<div id="mceToolbarContainer">
|
||||
@@ -549,10 +552,10 @@ function log(o) {
|
||||
<a id="moreBtn"> <i class="fa more-fa"></i></a>
|
||||
</div>
|
||||
<!-- 查看信息 -->
|
||||
<div id="infoToolbar" class="info-toolbar">
|
||||
<div id="infoToolbar" class="info-toolbar invisible">
|
||||
<a class="toolbar-update"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
|
||||
<span class="lang">{{msg . "created"}}</span>: <span class="created-time">2015-04-03</span>
|
||||
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time">2015-05-01</span>
|
||||
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
|
||||
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editorBg"></div>
|
||||
@@ -568,7 +571,7 @@ function log(o) {
|
||||
<!-- leaui image drop image to editor-->
|
||||
<form id="upload" method="post" action="/file/uploadImageLeaui" enctype="multipart/form-data" style="margin-top: 5px;">
|
||||
<div id="drop">
|
||||
Drop images to here
|
||||
{{leaMsg . "Drop images to here"}}
|
||||
<input type="file" name="file" multiple style="display: none"/>
|
||||
</div>
|
||||
<ul id="uploadMsg">
|
||||
@@ -581,16 +584,15 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mdEditor">
|
||||
|
||||
<div id="mdEditor" class="read-only">
|
||||
<div class="layout-wrapper-l1">
|
||||
<div class="layout-wrapper-l2">
|
||||
<div class="navbar navbar-default">
|
||||
<div class="navbar-inner" id="wmd-button-bar">
|
||||
<div id="mdInfoToolbar" class="info-toolbar">
|
||||
<div id="mdInfoToolbar" class="info-toolbar invisible">
|
||||
<a class="toolbar-update"><i class="fa fa-pencil"></i> <span class="lang">{{msg . "modify"}}</span></a>
|
||||
<span class="lang">{{msg . "created"}}</span>: <span class="created-time">2015-04-03</span>
|
||||
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time">2015-05-01</span>
|
||||
<span class="lang">{{msg . "created"}}</span>: <span class="created-time"></span>
|
||||
<span class="lang">{{msg . "updated"}}</span>: <span class="updated-time"></span>
|
||||
</div>
|
||||
|
||||
<div class="wmd-button-bar-inner">
|
||||
@@ -609,7 +611,7 @@ function log(o) {
|
||||
<!-- 帮助 -->
|
||||
<ul class="nav left-buttons">
|
||||
<li class="wmd-button-group6 btn-group">
|
||||
<li class="wmd-button btn btn-success" id="wmd-help-button" title="Markdown syntax" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
|
||||
<li class="wmd-button btn btn-success" id="wmd-help-button" title="{{msg . "Markdown syntax"}}" style="left: 0px; display: none;"><span style="display: none; background-position: 0px 0px;"></span><i class="fa fa-question-circle"></i></li>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -689,32 +691,6 @@ function log(o) {
|
||||
</div>
|
||||
|
||||
<!-- mdEditor -->
|
||||
<!-- v2 -->
|
||||
<div class="modal fade modal-insert-link">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Hyperlink</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please provide the link URL and an optional title:</p>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="icon-globe"></i></span><input
|
||||
id="input-insert-link" type="text" class="col-sm-5 form-control"
|
||||
placeholder='http://example.com/ "optional title"' />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a>
|
||||
<a href="#" class="btn btn-primary action-insert-link"
|
||||
data-dismiss="modal">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 插入图片 -->
|
||||
<div class="modal fade modal-insert-image">
|
||||
<div class="modal-dialog" style="width: 840px;max-width:100%;">
|
||||
@@ -722,15 +698,15 @@ function log(o) {
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Image</h4>
|
||||
<h4 class="modal-title">{{msg . "Image"}}</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="padding-top: 0; padding-bottom: 0">
|
||||
<iframe name="mdImageManager" style="width: 100%; height: 350px" scrolling="no" id="leauiIfrForMD" src="" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn btn-default"
|
||||
data-dismiss="modal">Cancel</a> <a href="#"
|
||||
class="btn btn-primary action-insert-image" data-dismiss="modal">Insert Image</a>
|
||||
data-dismiss="modal">{{msg . "Cancel"}}</a> <a href="#"
|
||||
class="btn btn-primary action-insert-image" data-dismiss="modal">{{msg . "Insert Image"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -745,7 +721,7 @@ function log(o) {
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="editorDialog-title"></h4>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
<div class="input-group">
|
||||
@@ -855,24 +831,7 @@ function log(o) {
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- 编辑器提示 -->
|
||||
<div class="modal fade bs-modal-sm" id="tipsDialog" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" class="modalTitle">{{msg . "editorTips"}}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{msg . "editorTipsInfo"}}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{msg . "close"}}</button>
|
||||
</div>
|
||||
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- modal 静态区域 -->
|
||||
<div class="display: hidden">
|
||||
@@ -880,12 +839,12 @@ function log(o) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/jquery-1.9.0.min.js"></script>
|
||||
<script src="/js/jquery.ztree.all-3.5-min.js"></script>
|
||||
<script src="/js/i18n/msg.{{.locale}}.js"></script>
|
||||
<script src="/js/common-min.js"></script>
|
||||
|
||||
<script>
|
||||
var UrlPrefix = '{{.siteUrl}}'; // 为了发weibo
|
||||
var LEA = {};
|
||||
LEA.s1 = new Date();
|
||||
LEA.locale = "{{.locale}}";
|
||||
var UrlPrefix = '{{.siteUrl}}';
|
||||
var UserInfo = {{.userInfo|jsonJs}};
|
||||
var notebooks = {{.notebooks|jsonJs}};
|
||||
var shareNotebooks = {{.shareNotebooks|jsonJs}};
|
||||
@@ -898,40 +857,35 @@ var notes = {{.notes|jsonJs}};
|
||||
var latestNotes = {{if .latestNotes}}{{.latestNotes|jsonJs}}{{else}}[]{{end}};
|
||||
var noteContentJson = {{.noteContentJson|jsonJs}};
|
||||
var tagsJson = {{.tags|jsonJs}};
|
||||
LEA.locale = "{{.locale}}";
|
||||
var GlobalConfigs = {{.globalConfigs|jsonJs}}; // 2014/11/9 beta2
|
||||
var GlobalConfigs = {{.globalConfigs|jsonJs}};
|
||||
var tinyMCEPreInit = {base: '/public/tinymce', suffix: '.min'};
|
||||
</script>
|
||||
|
||||
<!-- 渲染view -->
|
||||
<script src="/tinymce/tinymce.min.js"></script>
|
||||
<script src="/js/i18n/msg.{{.locale}}.js"></script>
|
||||
|
||||
<script src="/js/dep.min.js"></script>
|
||||
|
||||
|
||||
|
||||
<script src="/public/libs/ace/ace.js"></script>
|
||||
<script src="/js/app/page-min.js"></script>
|
||||
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll-min.js"></script>
|
||||
<script src="/js/contextmenu/jquery.contextmenu-min.js"></script>
|
||||
<script src="/js/bootstrap-min.js"></script>
|
||||
<script src="/js/app/note-min.js"></script>
|
||||
<script src="/js/app/tag-min.js"></script>
|
||||
<script src="/js/app/notebook-min.js"></script>
|
||||
<script src="/js/app/share-min.js"></script>
|
||||
<script src="/js/object_id-min.js"></script>
|
||||
|
||||
|
||||
|
||||
<script src="/js/app.min.js"></script>
|
||||
|
||||
<script>
|
||||
initPage();
|
||||
</script>
|
||||
|
||||
<!-- context-menu -->
|
||||
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
|
||||
|
||||
<!-- v2 use require.js, mdeditor -->
|
||||
<script>
|
||||
// 当tinymce.min.js时, 请注释require
|
||||
// 当tinymce.dev.js时, 请注释require
|
||||
window.require = {
|
||||
baseUrl: '/public',
|
||||
};
|
||||
</script>
|
||||
<script src="/js/require.js"></script>
|
||||
<script src="/public/dist/main.min.js"></script>
|
||||
<script src="/public/js/upload_paste/main.js"></script>
|
||||
</script>
|
||||
|
||||
<script src="/js/markdown.min.js"></script>
|
||||
|
||||
|
||||
|
||||
<script src="/public/js/plugins/main.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -9,7 +9,7 @@ SP=$(cd "$(dirname "$0")"; pwd)
|
||||
tmp="/Users/life/Desktop/leanote_release"
|
||||
|
||||
# version
|
||||
V="v1.0-beta.4"
|
||||
V="v1.1"
|
||||
|
||||
##=================================
|
||||
# 1. 先build 成 3个平台, 2种bit = 6种
|
||||
@@ -37,11 +37,13 @@ function build()
|
||||
|
||||
build "linux" "386";
|
||||
build "linux" "amd64";
|
||||
build "darwin" "386";
|
||||
build "darwin" "amd64";
|
||||
|
||||
build "windows" "386";
|
||||
build "windows" "amd64";
|
||||
|
||||
# build "darwin" "386";
|
||||
build "darwin" "amd64";
|
||||
|
||||
|
||||
##======================
|
||||
# 2. release目录准备工作
|
||||
@@ -133,9 +135,11 @@ function tarRelease()
|
||||
|
||||
tarRelease "linux" "386";
|
||||
tarRelease "linux" "amd64";
|
||||
tarRelease "darwin" "386";
|
||||
tarRelease "darwin" "amd64";
|
||||
|
||||
tarRelease "windows" "386";
|
||||
tarRelease "windows" "amd64";
|
||||
|
||||
# BLOCK'
|
||||
# tarRelease "darwin" "386";
|
||||
tarRelease "darwin" "amd64";
|
||||
|
||||
# BLOCK'
|
||||
|
||||
@@ -13,7 +13,7 @@ if exist "%path%\leanote" del /Q "%path%\leanote"
|
||||
mklink /D "%path%\leanote" %SCRIPTPATH%
|
||||
|
||||
: set GOPATH
|
||||
set GOPATH=%GOPATH%;"%SCRIPTPATH%\bin"
|
||||
set GOPATH="%SCRIPTPATH%\bin"
|
||||
|
||||
: run
|
||||
if %processor_architecture%==x86 (
|
||||
|
||||
@@ -11,7 +11,7 @@ rm -rf $SCRIPTPATH/src/github.com/leanote/leanote # 先删除
|
||||
ln -s ../../../../ $SCRIPTPATH/src/github.com/leanote/leanote
|
||||
|
||||
# set GOPATH
|
||||
export GOPATH=$GOPATH:$SCRIPTPATH
|
||||
export GOPATH=$SCRIPTPATH
|
||||
|
||||
# run
|
||||
osName=`uname` # Darwin or Linux
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"os"
|
||||
fpath "path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type Static struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// This method handles requests for files. The supplied prefix may be absolute
|
||||
// or relative. If the prefix is relative it is assumed to be relative to the
|
||||
// application directory. The filepath may either be just a file or an
|
||||
// additional filepath to search for the given file. This response may return
|
||||
// the following responses in the event of an error or invalid request;
|
||||
// 403(Forbidden): If the prefix filepath combination results in a directory.
|
||||
// 404(Not found): If the prefix and filepath combination results in a non-existent file.
|
||||
// 500(Internal Server Error): There are a few edge cases that would likely indicate some configuration error outside of revel.
|
||||
//
|
||||
// Note that when defining routes in routes/conf the parameters must not have
|
||||
// spaces around the comma.
|
||||
// Bad: Static.Serve("public/img", "favicon.png")
|
||||
// Good: Static.Serve("public/img","favicon.png")
|
||||
//
|
||||
// Examples:
|
||||
// Serving a directory
|
||||
// Route (conf/routes):
|
||||
// GET /public/{<.*>filepath} Static.Serve("public")
|
||||
// Request:
|
||||
// public/js/sessvars.js
|
||||
// Calls
|
||||
// Static.Serve("public","js/sessvars.js")
|
||||
//
|
||||
// Serving a file
|
||||
// Route (conf/routes):
|
||||
// GET /favicon.ico Static.Serve("public/img","favicon.png")
|
||||
// Request:
|
||||
// favicon.ico
|
||||
// Calls:
|
||||
// Static.Serve("public/img", "favicon.png")
|
||||
func (c Static) Serve(prefix, filepath string) revel.Result {
|
||||
var basePath string
|
||||
|
||||
if !fpath.IsAbs(prefix) {
|
||||
basePath = revel.BasePath
|
||||
}
|
||||
|
||||
basePathPrefix := fpath.Join(basePath, fpath.FromSlash(prefix))
|
||||
fname := fpath.Join(basePathPrefix, fpath.FromSlash(filepath))
|
||||
if !strings.HasPrefix(fname, basePathPrefix) {
|
||||
revel.WARN.Printf("Attempted to read file outside of base path: %s", fname)
|
||||
return c.NotFound("")
|
||||
}
|
||||
|
||||
finfo, err := os.Stat(fname)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) || err.(*os.PathError).Err == syscall.ENOTDIR {
|
||||
revel.WARN.Printf("File not found (%s): %s ", fname, err)
|
||||
return c.NotFound("File not found")
|
||||
}
|
||||
revel.ERROR.Printf("Error trying to get fileinfo for '%s': %s", fname, err)
|
||||
return c.RenderError(err)
|
||||
}
|
||||
|
||||
if finfo.Mode().IsDir() {
|
||||
revel.WARN.Printf("Attempted directory listing of %s", fname)
|
||||
return c.Forbidden("Directory listing not allowed")
|
||||
}
|
||||
|
||||
file, err := os.Open(fname)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
revel.WARN.Printf("File not found (%s): %s ", fname, err)
|
||||
return c.NotFound("File not found")
|
||||
}
|
||||
revel.ERROR.Printf("Error opening '%s': %s", fname, err)
|
||||
return c.RenderError(err)
|
||||
}
|
||||
return c.RenderFile(file, revel.Inline)
|
||||
}
|
||||
|
||||
// This method allows modules to serve binary files. The parameters are the same
|
||||
// as Static.Serve with the additional module name pre-pended to the list of
|
||||
// arguments.
|
||||
func (c Static) ServeModule(moduleName, prefix, filepath string) revel.Result {
|
||||
var basePath string
|
||||
for _, module := range revel.Modules {
|
||||
if module.Name == moduleName {
|
||||
basePath = module.Path
|
||||
}
|
||||
}
|
||||
|
||||
absPath := fpath.Join(basePath, fpath.FromSlash(prefix))
|
||||
|
||||
return c.Serve(absPath, filepath)
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/revel/revel"
|
||||
"html"
|
||||
"html/template"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TestRunner struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
type TestSuiteDesc struct {
|
||||
Name string
|
||||
Tests []TestDesc
|
||||
}
|
||||
|
||||
type TestDesc struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type TestSuiteResult struct {
|
||||
Name string
|
||||
Passed bool
|
||||
Results []TestResult
|
||||
}
|
||||
|
||||
type TestResult struct {
|
||||
Name string
|
||||
Passed bool
|
||||
ErrorHtml template.HTML
|
||||
ErrorSummary string
|
||||
}
|
||||
|
||||
var NONE = []reflect.Value{}
|
||||
|
||||
func (c TestRunner) Index() revel.Result {
|
||||
var testSuites []TestSuiteDesc
|
||||
for _, testSuite := range revel.TestSuites {
|
||||
testSuites = append(testSuites, DescribeSuite(testSuite))
|
||||
}
|
||||
return c.Render(testSuites)
|
||||
}
|
||||
|
||||
// Run runs a single test, given by the argument.
|
||||
func (c TestRunner) Run(suite, test string) revel.Result {
|
||||
result := TestResult{Name: test}
|
||||
for _, testSuite := range revel.TestSuites {
|
||||
t := reflect.TypeOf(testSuite).Elem()
|
||||
if t.Name() != suite {
|
||||
continue
|
||||
}
|
||||
|
||||
// Found the suite, create a new instance and run the named method.
|
||||
v := reflect.New(t)
|
||||
func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
error := revel.NewErrorFromPanic(err)
|
||||
if error == nil {
|
||||
result.ErrorHtml = template.HTML(html.EscapeString(fmt.Sprint(err)))
|
||||
} else {
|
||||
var buffer bytes.Buffer
|
||||
tmpl, _ := revel.MainTemplateLoader.Template("TestRunner/FailureDetail.html")
|
||||
tmpl.Render(&buffer, error)
|
||||
result.ErrorSummary = errorSummary(error)
|
||||
result.ErrorHtml = template.HTML(buffer.String())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Initialize the test suite with a NewTestSuite()
|
||||
testSuiteInstance := v.Elem().FieldByName("TestSuite")
|
||||
testSuiteInstance.Set(reflect.ValueOf(revel.NewTestSuite()))
|
||||
|
||||
// Call Before(), call the test, and call After().
|
||||
if m := v.MethodByName("Before"); m.IsValid() {
|
||||
m.Call(NONE)
|
||||
}
|
||||
|
||||
if m := v.MethodByName("After"); m.IsValid() {
|
||||
defer m.Call(NONE)
|
||||
}
|
||||
|
||||
v.MethodByName(test).Call(NONE)
|
||||
|
||||
// No panic means success.
|
||||
result.Passed = true
|
||||
}()
|
||||
break
|
||||
}
|
||||
return c.RenderJson(result)
|
||||
}
|
||||
|
||||
// List returns a JSON list of test suites and tests.
|
||||
// Used by the "test" command line tool.
|
||||
func (c TestRunner) List() revel.Result {
|
||||
var testSuites []TestSuiteDesc
|
||||
for _, testSuite := range revel.TestSuites {
|
||||
testSuites = append(testSuites, DescribeSuite(testSuite))
|
||||
}
|
||||
return c.RenderJson(testSuites)
|
||||
}
|
||||
|
||||
func DescribeSuite(testSuite interface{}) TestSuiteDesc {
|
||||
t := reflect.TypeOf(testSuite)
|
||||
|
||||
// Get a list of methods of the embedded test type.
|
||||
super := t.Elem().Field(0).Type
|
||||
superMethodNameSet := map[string]struct{}{}
|
||||
for i := 0; i < super.NumMethod(); i++ {
|
||||
superMethodNameSet[super.Method(i).Name] = struct{}{}
|
||||
}
|
||||
|
||||
// Get a list of methods on the test suite that take no parameters, return
|
||||
// no results, and were not part of the embedded type's method set.
|
||||
var tests []TestDesc
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
m := t.Method(i)
|
||||
mt := m.Type
|
||||
_, isSuperMethod := superMethodNameSet[m.Name]
|
||||
if mt.NumIn() == 1 &&
|
||||
mt.NumOut() == 0 &&
|
||||
mt.In(0) == t &&
|
||||
!isSuperMethod &&
|
||||
strings.HasPrefix(m.Name, "Test") {
|
||||
tests = append(tests, TestDesc{m.Name})
|
||||
}
|
||||
}
|
||||
|
||||
return TestSuiteDesc{
|
||||
Name: t.Elem().Name(),
|
||||
Tests: tests,
|
||||
}
|
||||
}
|
||||
|
||||
func errorSummary(error *revel.Error) string {
|
||||
var message = fmt.Sprintf("%4sStatus: %s\n%4sIn %s", "", error.Description, "", error.Path)
|
||||
if error.Line != 0 {
|
||||
message += fmt.Sprintf(" (around line %d): ", error.Line)
|
||||
for _, line := range error.ContextSource() {
|
||||
if line.IsError {
|
||||
message += line.Source
|
||||
}
|
||||
}
|
||||
}
|
||||
return message
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
func init() {
|
||||
revel.OnAppStart(func() {
|
||||
fmt.Println("Go to /@tests to run the tests.")
|
||||
})
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<b>{{.Description}}</b><br>
|
||||
In {{.Path}}
|
||||
{{if .Line}}
|
||||
(around {{if .Line}}line {{.Line}}{{end}}{{if .Column}} column {{.Column}}{{end}})
|
||||
{{end}}:
|
||||
{{range .ContextSource}}{{if .IsError}}<code>{{.Source}}</code>{{end}}{{end}}<br>
|
||||
<a style="cursor:pointer;"
|
||||
onclick="x=this.nextSibling.style;if(!x.display)x.display='none';else x.display=''">
|
||||
Show Stack</a><pre style="display:none;">{{.Stack}}</pre>
|
||||
@@ -1,84 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Revel Test Runner</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<link href="/@tests/public/css/bootstrap.css" type="text/css" rel="stylesheet"></link>
|
||||
<script src="/@tests/public/js/jquery-1.9.1.min.js" type="text/javascript"></script>
|
||||
<style>
|
||||
header { padding:20px 0; background-color:#ADD8E6 }
|
||||
.passed td { background-color: #90EE90 !important; }
|
||||
.failed td { background-color: #FFB6C1 !important; }
|
||||
.tests td.name, .tests td.result { padding-top: 13px; }
|
||||
pre { font-size:10px; white-space: pre; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<table><tr><td>
|
||||
<h1>Test Runner</h1>
|
||||
<p class="lead">Run all of your application's tests from here.</p>
|
||||
</td><td style="padding-left:150px;">
|
||||
<button class="btn btn-large" all-tests="">Run All Tests</button>
|
||||
</td></tr></table>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
{{range .testSuites}}
|
||||
<p class="lead" style="margin-top:20px;">{{.Name}}</p>
|
||||
<table class="table table-striped tests" suite="{{.Name}}">
|
||||
{{range .Tests}}
|
||||
<tr>
|
||||
<td class="name">{{.Name}}</td>
|
||||
<td class="result">
|
||||
</td>
|
||||
<td><button test="{{.Name}}" class="btn">Run</button></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var running = 0;
|
||||
|
||||
$("button[test]").click(function() {
|
||||
var button = $(this).addClass("disabled").text("Running");
|
||||
running += 1;
|
||||
runTest(button);
|
||||
});
|
||||
|
||||
$("button[all-tests]").click(function() {
|
||||
var button = $(this).addClass("disabled").text("Running");
|
||||
$("button[test]").click();
|
||||
});
|
||||
|
||||
function runTest(button) {
|
||||
var suite = button.parents("table").attr("suite");
|
||||
var test = button.attr("test");
|
||||
var row = button.parents("tr");
|
||||
var resultCell = row.children(".result");
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: "/@tests/"+suite+"/"+test,
|
||||
async: false,
|
||||
success: function(result) {
|
||||
row.attr("class", result.Passed ? "passed" : "failed");
|
||||
if (result.Passed) {
|
||||
resultCell.html("");
|
||||
} else {
|
||||
resultCell.html(result.ErrorHtml);
|
||||
}
|
||||
button.removeClass("disabled").text("Run");
|
||||
running -= 1;
|
||||
if (running == 0) {
|
||||
$("button[all-tests]").removeClass("disabled").text("Run All Tests");
|
||||
}
|
||||
}});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,47 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Revel Test Runner</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #333333;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
header { padding:20px 0; }
|
||||
header.passed { background-color: #90EE90 !important; }
|
||||
header.failed { background-color: #FFB6C1 !important; }
|
||||
table { margin-top: 20px; padding: 8px; line-height: 20px; }
|
||||
td { vertical-align: top; padding-right:20px; }
|
||||
a { color: #0088cc; }
|
||||
.container { margin-left: auto; margin-right: auto; width: 940px; }
|
||||
.result.failed b { font-weight:bold; color: #C00; font-size: 14px; }
|
||||
.result.failed span { color: #C00; font-size: 14px; }
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="{{if .Passed}}passed{{else}}failed{{end}}">
|
||||
<div class="container">
|
||||
<h1>{{.Name}}</h1>
|
||||
<p>{{if .Passed}}PASSED{{else}}FAILED{{end}}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<table>
|
||||
{{range .Results}}
|
||||
<tr class="result {{if .Passed}}passed{{else}}failed{{end}}">
|
||||
<td><span>{{.Name}}</span></td>
|
||||
<td>{{if .ErrorHtml}}{{.ErrorHtml}}{{else}}PASSED{{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +0,0 @@
|
||||
GET /@tests TestRunner.Index
|
||||
GET /@tests.list TestRunner.List
|
||||
GET /@tests/public/*filepath Static.ServeModule(testrunner,public)
|
||||
GET /@tests/:suite/:test TestRunner.Run
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 5.5 KiB |
File diff suppressed because one or more lines are too long
@@ -6,15 +6,19 @@ http.port=9000
|
||||
|
||||
site.url=http://localhost:9000 # or http://x.com:8080, http://www.xx.com:9000
|
||||
|
||||
# admin username
|
||||
adminUsername=admin
|
||||
|
||||
# mongdb
|
||||
db.host=localhost
|
||||
db.port=27017
|
||||
db.dbname=leanote # required
|
||||
db.username= # if not exists, please leave it blank
|
||||
db.password= # if not exists, please leave it blank
|
||||
# or you can set the mongdb url for more complex needs the format is:
|
||||
# or you can set the mongodb url for more complex needs the format is:
|
||||
# mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb
|
||||
# db.url=mongodb://root:root123@localhost:27017/leanote
|
||||
# db.urlEnv=${MONGODB_URL} # set url from env. eg. mongodb://root:root123@localhost:27017/leanote
|
||||
|
||||
# You Must Change It !! About Security!!
|
||||
app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y
|
||||
@@ -28,7 +32,7 @@ http.addr=
|
||||
http.ssl=false
|
||||
cookie.httponly=false
|
||||
cookie.prefix=LEANOTE
|
||||
cookie.domain= # for share cookie with subdomain, 默认为空, 即不设置 不能设置为localhost, 要么就为空
|
||||
cookie.domain= # for share cookie with sub-domain
|
||||
cookie.secure=false
|
||||
format.date=01/02/2006
|
||||
format.datetime=01/02/2006 15:04
|
||||
@@ -49,7 +53,7 @@ mode.dev=true
|
||||
results.pretty=true
|
||||
watch=true
|
||||
|
||||
module.testrunner = github.com/revel/modules/testrunner
|
||||
module.testrunner = # github.com/revel/modules/testrunner
|
||||
|
||||
log.trace.output = stderr
|
||||
log.info.output = stderr
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# This file defines all application routes (Higher priority routes first)
|
||||
# ~~~~
|
||||
|
||||
module:testrunner
|
||||
# module:testrunner
|
||||
|
||||
# Home is My Blog
|
||||
Get / Index.Default
|
||||
@@ -38,6 +38,8 @@ POST /findPasswordUpdate Auth.FindPasswordUpdate
|
||||
* /note/copySharedNote Note.CopySharedNote
|
||||
* /note/searchNoteByTags Note.SearchNoteByTags
|
||||
* /note/setNote2Blog Note.SetNote2Blog
|
||||
* /note/exportPdf Note.ExportPDF
|
||||
* /note/toPdf Note.ToPdf
|
||||
# pjax
|
||||
GET /note/:noteId Note.Index
|
||||
|
||||
|
||||
@@ -42,8 +42,10 @@ curUser=Email
|
||||
|
||||
# form
|
||||
submit=submit
|
||||
Submit=Submit
|
||||
register=Sign up
|
||||
login=Sign in
|
||||
Password=Password
|
||||
password2=Confirm your password
|
||||
email=Email
|
||||
inputUsername=Username(email) is required
|
||||
@@ -51,6 +53,7 @@ inputEmail=Email is required
|
||||
wrongEmail=Wrong email
|
||||
wrongUsernameOrPassword=Wrong username or password
|
||||
inputPassword=Password is required
|
||||
inputLoginPasswordTips=Please input login password
|
||||
wrongPassword=Wrong password
|
||||
logining=Sign in
|
||||
loginSuccess=login success
|
||||
@@ -108,6 +111,11 @@ writingMode=Writing Mode
|
||||
normalMode=Normal Mode
|
||||
saving=Saving
|
||||
saveSuccess=Save success
|
||||
SearchNote=Search Note
|
||||
SearchNotebook=Search Notebook
|
||||
Choose File=Choose File
|
||||
Download All=Download All
|
||||
Information=Information
|
||||
|
||||
update=Update
|
||||
create=Create
|
||||
@@ -209,8 +217,6 @@ inviteEmailBody=Hi,I am %s, %s is awesome, come on!
|
||||
# 历史记录
|
||||
historiesNum=We have saved at most <b>10</b> latest histories with each note
|
||||
noHistories=No histories
|
||||
fold=Fold
|
||||
unfold=Unfold
|
||||
datetime=Datetime
|
||||
restoreFromThisVersion=Restore from this version
|
||||
confirmBackup=Are you sure to restore from this version? We will backup the current note.
|
||||
@@ -244,16 +250,11 @@ errorPerPageSize=Page size is error
|
||||
errorSortField=Sort Field is error
|
||||
themeValidHasRoundInclude=WARNING: Templates have circular references!
|
||||
|
||||
# lea++
|
||||
leaDesc=leanote blog platform
|
||||
recommend=Recommend
|
||||
latest=Latest
|
||||
subscription=Subscription
|
||||
|
||||
# 用户中心
|
||||
memberCenter=Member Center
|
||||
userNotExists=The user is not exists
|
||||
hasUsers=The user already exists
|
||||
Leanote Blog Theme Api=Leanote Blog Theme Api
|
||||
|
||||
# yu
|
||||
service=Service
|
||||
@@ -347,5 +348,23 @@ created=Created
|
||||
updated=Updated
|
||||
modify=Modify
|
||||
|
||||
# share
|
||||
tokenExpired=Token Expired
|
||||
Share=Share
|
||||
hasSharedThisNote=has shared this note
|
||||
inputSharePwd=Please input password
|
||||
shareToGroup=Share to group
|
||||
shareByPwd=Share by password
|
||||
groupName=Group Name
|
||||
operation=Operation
|
||||
groupAndMemberManage=Manage group/member
|
||||
shareToGroupTips=If you share your note to group, the member belongs to group will have the right to access the note
|
||||
shared=Shared
|
||||
notShared=Not shared
|
||||
shareLink=Share link
|
||||
sharePwd=Access password
|
||||
cancelShare=Cancel
|
||||
getShareLinkAndPwd=Get link and password
|
||||
|
||||
# error
|
||||
notFound=This page cann't found.
|
||||
notFound=This page can't be found.
|
||||
|
||||
@@ -10,10 +10,10 @@ forgetPassword = Mot de passe oublié?
|
||||
or=ou
|
||||
try=Essayer
|
||||
3th=Comptes tiers
|
||||
usernameOrEmail=Nom d'utilisateur ou courriel
|
||||
usernameOrEmail=Nom d'utilisateur ou adresse de courriel
|
||||
password=Mot de passe
|
||||
home=Mon bloc-note
|
||||
desktopApp=App
|
||||
home=Mes blocs-notes
|
||||
desktopApp=Application
|
||||
aboutLeanote=A propos de leanote
|
||||
suggestions=Suggestions
|
||||
yourSuggestions=Suggestions
|
||||
@@ -37,17 +37,18 @@ noTag=No Tags
|
||||
|
||||
hi=Coucou
|
||||
welcomeUseLeanote=Bienvenue!
|
||||
myNote=Ma note
|
||||
curUser=Courriel
|
||||
myNote=Mes notes
|
||||
curUser=Adresse courriel
|
||||
|
||||
# form
|
||||
submit=Soumettre
|
||||
Submit=Soumettre
|
||||
register=S'inscrire
|
||||
login=S'identifier
|
||||
password2=Confirmer votre mot de passe
|
||||
email=Courriel
|
||||
inputUsername=Le nom d'utilisateur(courriel) est requis
|
||||
inputEmail=Le courriel est requis
|
||||
email=Adresse de courriel
|
||||
inputUsername=Le nom d'utilisateur(adresse courriel) est requis
|
||||
inputEmail=L'adresse courriel est requis
|
||||
wrongEmail=Wrong email
|
||||
wrongUsernameOrPassword=Mauvais nom d'utilisateur ou mot de passe
|
||||
inputPassword=Le mot de passe est requis
|
||||
@@ -58,7 +59,7 @@ ing=En cours
|
||||
use = Utiliser
|
||||
hadAcount = Déjà inscrit?
|
||||
hasAcount = Pas encore inscrit?
|
||||
signInWithThird=Sign in with other account
|
||||
signInWithThird=S'identifier avec un compte tiers
|
||||
|
||||
# 注册
|
||||
registerSuccessAndRdirectToNote=inscription réussie, redirection...
|
||||
@@ -85,7 +86,7 @@ editorTips=Astuces
|
||||
editorTipsInfo=<h4>1. Raccourcis</h4>ctrl+maj+c Active/désactive le code<h4>2. maj+entrée Sortir du bloc courant</h4> ex. <img src="/images/outofcode.png" style="width: 90px"/> dans cette situation vous pouvez utiliser maj+entrée pour sortir du bloc de code courant.
|
||||
newNote=Nouvelle note
|
||||
newMarkdownNote=Nouvelle Note Markdown
|
||||
noNoteNewNoteTips=Le bloc-note est vide, pourquoi pas...
|
||||
noNoteNewNoteTips=Le bloc-note est vide, pourquoi ne pas...
|
||||
canntNewNoteTips=Désolé, impossible de créer une nouvelle note ici, veuillez d'abord choisir un bloc-note.
|
||||
new=Nouveau
|
||||
newMarkdown=Nouvelle note markdown
|
||||
@@ -108,6 +109,8 @@ writingMode=Mode écriture
|
||||
normalMode=Mode normal
|
||||
saving=Sauvegarde
|
||||
saveSuccess=Sauvegarde réussie
|
||||
SearchNote=Search Note
|
||||
SearchNotebook=Search Notebook
|
||||
|
||||
update=Mettre à jour
|
||||
create=Créer
|
||||
@@ -249,7 +252,7 @@ recommend=Recommandé
|
||||
latest=Plus récent.
|
||||
|
||||
# 用户中心
|
||||
memberCenter=Centre de Membre
|
||||
memberCenter=Espace Membre
|
||||
userNotExists=L'utilisateur n'existe pas
|
||||
hasUsers=L'utilisateur existe déjà
|
||||
|
||||
@@ -296,7 +299,7 @@ asc=Asc
|
||||
postList=Afficher la liste
|
||||
hasSelfDefined=Défini
|
||||
noSelfDefined=Non défini
|
||||
setAbstract=Réglages abstraits
|
||||
setAbstract=Autres réglages
|
||||
title=Titre
|
||||
content=Contenu
|
||||
|
||||
@@ -344,5 +347,24 @@ created=Created
|
||||
updated=Updated
|
||||
modify=Modify
|
||||
|
||||
# share
|
||||
tokenExpired=Token Expired
|
||||
Share=Share
|
||||
hasSharedThisNote=has shared this note
|
||||
inputSharePwd=Please input password
|
||||
shareToGroup=Share to group
|
||||
shareByPwd=Share by password
|
||||
groupName=Group Name
|
||||
operation=Operation
|
||||
groupAndMemberManage=Manage group/member
|
||||
shareToGroupTips=If you share your note to group, the member belongs to group will have the right to access the note
|
||||
shared=Shared
|
||||
notShared=Not shared
|
||||
shareLink=Share link
|
||||
sharePwd=Access password
|
||||
cancelShare=Cancel
|
||||
getShareLinkAndPwd=Get link and password
|
||||
|
||||
|
||||
# error
|
||||
notFound=Page introuvable.
|
||||
notFound=Page introuvable.
|
||||
138
messages/msg.zh
138
messages/msg.zh
@@ -41,19 +41,22 @@ myNote=My note
|
||||
curUser=Email
|
||||
|
||||
# form
|
||||
submit=submit
|
||||
register=Sign up
|
||||
login=Sign in
|
||||
password2=Confirm your password
|
||||
submit=提交
|
||||
Submit=提交
|
||||
register=注册
|
||||
login=登录
|
||||
Password=密码
|
||||
password2=请确认密码
|
||||
email=Email
|
||||
inputUsername=Username(email) is required
|
||||
inputEmail=Email is required
|
||||
wrongEmail=Wrong email
|
||||
wrongUsernameOrPassword=Wrong username or password
|
||||
inputPassword=Password is required
|
||||
wrongPassword=Wrong password
|
||||
logining=Sign in
|
||||
loginSuccess=login success
|
||||
inputUsername=请输入用户名或Email
|
||||
inputEmail=请输入Email
|
||||
wrongEmail=Email错误
|
||||
wrongUsernameOrPassword=用户名或密码错误
|
||||
inputPassword=请输入密码
|
||||
inputLoginPasswordTips=请输入登录密码
|
||||
wrongPassword=密码错误
|
||||
logining=登录
|
||||
loginSuccess=登录成功
|
||||
|
||||
hi=Hi
|
||||
welcomeUseLeanote=欢迎使用leanote
|
||||
@@ -104,7 +107,7 @@ history=历史记录
|
||||
save=保存
|
||||
editorTips=帮助
|
||||
editorTipsInfo=<h4>1. 快捷键</h4>ctrl+shift+c 代码块切换 <h4>2. shift+enter 跳出当前区域</h4>比如在代码块中<img src="/images/outofcode.png" style="width: 90px"/>按shift+enter可跳出当前代码块.
|
||||
newNote=新建笔记
|
||||
newNote=新建普通笔记
|
||||
newMarkdownNote=新建Markdown笔记
|
||||
noNoteNewNoteTips=该笔记本下空空如也...何不
|
||||
canntNewNoteTips=Sorry, 这里不能添加笔记的. 你需要先选择一个笔记本.
|
||||
@@ -121,7 +124,7 @@ all=最新
|
||||
trash=废纸篓
|
||||
delete=删除
|
||||
unTitled=无标题
|
||||
defaultShare=默认共享
|
||||
defaultShare=默认分享
|
||||
leftHidden=隐藏左侧
|
||||
leftShow=展开左侧
|
||||
nav=文档导航
|
||||
@@ -129,9 +132,22 @@ writingMode=写作模式
|
||||
normalMode=普通模式
|
||||
saving=正在保存
|
||||
saveSuccess=保存成功
|
||||
SearchNote=搜索笔记
|
||||
SearchNotebook=搜索笔记本
|
||||
Choose File=选择文件
|
||||
Download All=下载全部
|
||||
Information=信息
|
||||
Are you sure to delete it ?=确认删除?
|
||||
Insert link into content=将附件链接插入到内容中
|
||||
Download=下载
|
||||
Delete=删除
|
||||
Drop images to here=将图片拖动至此
|
||||
|
||||
update=更新
|
||||
create=创建
|
||||
Update Time=更新时间
|
||||
Create Time=创建时间
|
||||
Post Url=博文链接
|
||||
|
||||
demoRegister=<a href="/register">立即注册</a>
|
||||
|
||||
@@ -145,7 +161,7 @@ myTag=我的标签
|
||||
red=红色
|
||||
yellow=黄色
|
||||
blue=蓝色
|
||||
green=绿色
|
||||
green=绿色
|
||||
|
||||
# 设置
|
||||
accountSetting=帐户设置
|
||||
@@ -208,7 +224,7 @@ updateEmail=修改邮箱
|
||||
updateEmailTips=邮箱修改后, 验证之后才有效, 验证之后新的邮箱地址将会作为登录帐号使用.
|
||||
sendVerifiedEmail=发送验证邮箱
|
||||
sendSuccess=发送成功
|
||||
sendFailed=发送失败
|
||||
sendFailed=发送失败
|
||||
verified=已验证
|
||||
unVerified=未验证
|
||||
verifiedNow=现在去验证
|
||||
@@ -230,12 +246,10 @@ emailBodyRequired=邮件内容不能为空
|
||||
clickToCopy=点击复制
|
||||
sendSuccess=发送成功
|
||||
inviteEmailBody=Hi, 你好, 我是%s, %s非常好用, 快来注册吧!
|
||||
|
||||
|
||||
# 历史记录
|
||||
historiesNum=leanote会保存笔记的最近<b>10</b>份历史记录
|
||||
noHistories=无历史记录
|
||||
fold=折叠
|
||||
unfold=展开
|
||||
datetime=日期
|
||||
restoreFromThisVersion=从该版本还原
|
||||
confirmBackup=确定要从该版还原? 还原前leanote会备份当前版本到历史记录中.
|
||||
@@ -270,16 +284,11 @@ errorPerPageSize=每页记录数至少为1
|
||||
errorSortField=排序类型错误
|
||||
themeValidHasRoundInclude=警告: 模板存在循环引用问题!
|
||||
|
||||
# lea++
|
||||
leaDesc=leanote博客平台
|
||||
recommend=推荐文章
|
||||
latest=最新文章
|
||||
subscription=我的订阅
|
||||
|
||||
# 用户中心
|
||||
memberCenter=用户中心
|
||||
memberCenter=个人中心
|
||||
userNotExists=该成员沿未注册
|
||||
hasUsers=已存在该成员
|
||||
Leanote Blog Theme Api=Leanote博客主题API
|
||||
|
||||
# yu
|
||||
service=服务
|
||||
@@ -372,5 +381,82 @@ created=创建
|
||||
updated=更新
|
||||
modify=修改
|
||||
|
||||
#share
|
||||
tokenExpired = 会话已过期
|
||||
Share=分享
|
||||
hasSharedThisNote=分享了笔记
|
||||
inputSharePwd=您需要输入分享密码才能查看
|
||||
shareToGroup=分享给项目组
|
||||
shareByPwd=通过密码分享
|
||||
groupName=分组名
|
||||
operation=操作
|
||||
groupAndMemberManage=项目组/成员管理
|
||||
shareToGroupTips=将笔记/笔记本分享给项目组后, 该组下的用户便拥有了该笔记/笔记本
|
||||
shared=已分享
|
||||
notShared=未分享
|
||||
shareLink=分享链接
|
||||
sharePwd=查看密码
|
||||
cancelShare=取消分享
|
||||
getShareLinkAndPwd=生成该笔记的分享链接和密码
|
||||
|
||||
# markdown editor
|
||||
|
||||
Hyperlink=超链接
|
||||
Please provide the link URL and an optional title=请填写链接和一个可选的标题
|
||||
optional title=可选标题
|
||||
Ok=确认
|
||||
Cancel=取消
|
||||
Strong=粗体
|
||||
strong text=粗体
|
||||
Emphasis=斜体
|
||||
emphasized text=斜体
|
||||
Blockquote=引用
|
||||
Code Sample=代码
|
||||
enter code here=代码
|
||||
Image=图片
|
||||
Heading=标题
|
||||
Numbered List=有序列表
|
||||
Bulleted List=无序列表
|
||||
List item=项目
|
||||
Horizontal Rule=水平线
|
||||
Markdown syntax=Markdown 语法
|
||||
Undo=撤销
|
||||
Redo=重做
|
||||
enter image description here=图片标题
|
||||
enter link description here=链接标题
|
||||
|
||||
Insert Image=插入
|
||||
|
||||
# album image
|
||||
Images=图片
|
||||
Upload=上传
|
||||
Image URL=图片地址
|
||||
Albums=相册
|
||||
Default=默认
|
||||
File title search=通过标题搜索
|
||||
Go to upload images=去上传图片
|
||||
Rename=重命名
|
||||
Add=添加
|
||||
Add Album=添加相册
|
||||
Add Image=添加图片
|
||||
Can't load this url=不能载入该图片
|
||||
No Images=无图片
|
||||
Click to upload images Or Drop images to here=点击上传图片或将图片拖至此
|
||||
|
||||
Cannot delete default album=不能删除默认相册
|
||||
Cannot rename default album=不能重命名默认相册
|
||||
This album has images, please delete it's images at first.=相册内有图片, 不能删除
|
||||
|
||||
Rename Album=重命名
|
||||
Add Success!=添加成功!
|
||||
Rename Success!=重命名成功!
|
||||
Delete Success!=删除成功
|
||||
Are you sure to delete this image ?=确定删除该图片?
|
||||
click to remove this image=删除图片
|
||||
error=错误
|
||||
Error=错误
|
||||
Prev=上一页
|
||||
Next=下一页
|
||||
|
||||
# 必须要加这个, 奇怪
|
||||
[CN]
|
||||
|
||||
22
package.json
Normal file
22
package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "leanote-front-end-builder",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"gulp": "^3.8.7",
|
||||
"gulp-uglify": "^1.0.0",
|
||||
"gulp-clean": "^0.3.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"gulp-rename": "^1.0",
|
||||
"gulp-minify-html": "^1.0",
|
||||
"gulp-concat": "",
|
||||
"gulp-replace": "",
|
||||
"gulp-inject": "",
|
||||
"gulp-sequence": ""
|
||||
},
|
||||
"author": "life(http://life.leanote.com)",
|
||||
"license": "GPL v2",
|
||||
"bugs": {
|
||||
"url": "https://github.com/leanote/leanote/issues"
|
||||
}
|
||||
}
|
||||
@@ -907,7 +907,7 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
a {
|
||||
color: #2e3e4e;
|
||||
color: #346EA9;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover,
|
||||
|
||||
@@ -1088,7 +1088,7 @@ body {
|
||||
}
|
||||
|
||||
a {
|
||||
color: #2e3e4e;
|
||||
color: #346EA9;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -274,21 +274,56 @@ function goNowToDatetime(goNow) {
|
||||
!function ($) {
|
||||
$(function(){
|
||||
|
||||
// life
|
||||
$(".nav li > a").click(function(e) {
|
||||
$p = $(this).closest("ul");
|
||||
var $li = $(this).closest("li");
|
||||
if($li.find("ul").length == 0) {
|
||||
return true;
|
||||
}
|
||||
e.preventDefault();
|
||||
var hasClass = $li.hasClass("active");
|
||||
$p.find("li").removeClass("active");
|
||||
if(hasClass) {
|
||||
} else {
|
||||
$li.addClass("active");
|
||||
}
|
||||
});
|
||||
//navigation
|
||||
!function ($) {
|
||||
/**
|
||||
* description
|
||||
* every href must begin with "?t="
|
||||
*/
|
||||
//initial
|
||||
function getParameterByName(name, queryString) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); //address array [] condiion
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(queryString);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
var pathname = window.location.pathname; // admin/t
|
||||
var search = window.location.search; // ?t=xxx
|
||||
var fullPath = pathname;
|
||||
|
||||
//test case
|
||||
//http://localhost:9000/admin/t?p=0&t=email/sendToUsers
|
||||
//t=email/sendToUsers and t=email/send
|
||||
var paramId = getParameterByName("t",window.location.search)
|
||||
if(paramId !== "") {
|
||||
var fullPath = pathname + "?t=" + paramId; // /admin/t?t=xxx
|
||||
}
|
||||
|
||||
$("#nav > li").removeClass("active");
|
||||
|
||||
var $thisLi = $('#nav a[href="' + fullPath + '"]').parent();
|
||||
// 自己
|
||||
$thisLi.addClass("active");
|
||||
// 父也active
|
||||
$thisLi.parent().parent().addClass('active');
|
||||
|
||||
// event binding
|
||||
$(".nav li > a").click(function(e) {
|
||||
$p = $(this).closest("ul");
|
||||
var $li = $(this).closest("li");
|
||||
if($li.find("ul").length == 0) {
|
||||
return true;
|
||||
}
|
||||
e.preventDefault();
|
||||
var hasClass = $li.hasClass("active");
|
||||
$p.find("li").removeClass("active");
|
||||
if(hasClass) {
|
||||
} else {
|
||||
$li.addClass("active");
|
||||
}
|
||||
});
|
||||
}($);
|
||||
|
||||
// sort
|
||||
$(".th-sortable").click(function() {
|
||||
@@ -566,4 +601,4 @@ function goNowToDatetime(goNow) {
|
||||
|
||||
|
||||
});
|
||||
}(window.jQuery);
|
||||
}(window.jQuery);
|
||||
|
||||
1
public/album/css/style-min.css
vendored
Normal file
1
public/album/css/style-min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#drop,#imageList li,#upload{position:relative}#imageMask,#upload-msg{position:absolute;top:0}*{font-family:'Open Sans','Helvetica Neue',Arial,'Hiragino Sans GB','Microsoft YaHei','WenQuanYi Micro Hei',sans-serif}#drop{width:99%;min-height:260px;border:1px dotted #000;z-index:2;text-align:center}#drop.in{border:2px solid #000}#drop a{left:33%;top:105px;z-index:15;position:absolute}#drop input{display:none}#upload-msg{list-style-type:none;padding-left:0;margin-left:0;right:10px;width:300px;max-height:240px;overflow:auto;z-index:3}#imageList li,#preview li,.md{overflow:hidden}.tab-pane{padding:10px}#paginationContainer{text-align:right}#imageList{margin:10px 0 0;padding:0;height:196px}#imageList li{display:inline-block;float:left;padding:0;margin:0 3px 3px 0;height:90px;width:122px;border:1px solid #eee}#imageList li .a-img{line-height:80px;display:block;text-align:center}#imageList li:hover{border:1px solid #FF7300}#imageList li.selected{border:2px solid #FF7300}#imageList li img{max-width:100%;vertical-align:middle}#imageList li .tools{position:absolute;bottom:0;left:0;right:0;background-color:#fff;padding-right:2px;text-align:right;opacity:.7}.tools .file-title{width:100px;height:20px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;text-align:left}#imageList li:hover .tools{display:block;opacity:.9}.alert{padding:5px;margin-bottom:3px}#preview{margin:3px 0 3px 10px;padding:5px 0 0;border-top:1px solid #eee}#preview li{position:relative;display:inline-block;float:left;border:1px solid #E6E5E3;background-color:#FFF;width:64px;height:64px;margin-right:10px;line-height:64px;text-align:center;color:#E1E1E1;font-size:28px;cursor:pointer}#preview li:hover{border:1px solid #FF7300}#preview li.selected{border:2px solid #FF7300}#preview li .tools{position:absolute;bottom:0;right:0;left:0;height:15px;line-height:15px;font-size:14px;text-align:right}#preview li .tools a{color:red}#imagePage{position:relative}#imageMask{background:#fff;left:0;right:0;bottom:20px;font-size:18px;text-align:center;display:none;z-index:2}#previewAttrs .form-control{width:auto}#previewAttrs label{font-weight:400}@media screen and (max-width:700px){#previewAttrs{display:none}#drop{min-height:initial;padding:10px 0}#drop a{position:initial;margin:auto}#url form{margin:0!important}.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{width:auto;display:inline-block}#preview li:nth-child(10),#preview li:nth-child(3n),#preview li:nth-child(4n),#preview li:nth-child(5),#preview li:nth-child(7){display:none}#imageList li{width:90px}}#imageUrlForm{min-height:260px;text-align:center;padding-top:100px}.md{position:relative;height:350px}.md #previewAttrs{display:none}.md #preview{border:none;position:absolute;bottom:0;left:0;z-index:3}.md #drop{min-height:250px}#albumsForUpload{width:150px}
|
||||
243
public/album/css/style.css
Normal file
243
public/album/css/style.css
Normal file
@@ -0,0 +1,243 @@
|
||||
* {
|
||||
font-family: 'Open Sans', 'Helvetica Neue', Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif;
|
||||
}
|
||||
/* upload */
|
||||
#upload {
|
||||
position: relative;
|
||||
}
|
||||
#drop {
|
||||
position: relative;
|
||||
width: 99%;
|
||||
min-height: 260px;
|
||||
border: 1px dotted #000000;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
}
|
||||
#drop.in {
|
||||
border: 2px solid #000000;
|
||||
}
|
||||
#drop a {
|
||||
left: 33%;
|
||||
top: 105px;
|
||||
z-index: 15;
|
||||
position: absolute;
|
||||
}
|
||||
#drop input {
|
||||
display: none;
|
||||
}
|
||||
#upload-msg {
|
||||
list-style-type: none;
|
||||
padding-left: 0px;
|
||||
margin-left: 0px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
width: 300px;
|
||||
max-height: 240px;
|
||||
overflow: auto;
|
||||
z-index: 3;
|
||||
}
|
||||
/**/
|
||||
.tab-pane {
|
||||
padding: 10px;
|
||||
}
|
||||
#paginationContainer {
|
||||
text-align: right;
|
||||
}
|
||||
#imageList {
|
||||
margin: 0;
|
||||
margin-top: 10px;
|
||||
padding: 0;
|
||||
height: 196px;
|
||||
}
|
||||
#imageList li {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
padding: 0;
|
||||
margin: 0 3px 3px 0;
|
||||
position: relative;
|
||||
height: 90px;
|
||||
width: 122px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
#imageList li .a-img {
|
||||
line-height: 80px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
#imageList li:hover {
|
||||
border: 1px solid #FF7300;
|
||||
}
|
||||
#imageList li.selected {
|
||||
border: solid 2px #FF7300;
|
||||
}
|
||||
#imageList li img {
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#imageList li .tools {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
padding-right: 2px;
|
||||
text-align: right;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.tools .file-title {
|
||||
width: 100px;
|
||||
height: 20px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
}
|
||||
#imageList li:hover .tools {
|
||||
display: block;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.alert {
|
||||
padding: 5px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.tabs {
|
||||
/*height: 350px;*/
|
||||
}
|
||||
#preview {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 10px;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
padding-top: 5px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
#preview li {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
border: solid 1px #E6E5E3;
|
||||
background-color: #FFF;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
line-height: 64px;
|
||||
text-align: center;
|
||||
/*font-style: italic;*/
|
||||
color: #E1E1E1;
|
||||
font-size: 28px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#preview li:hover {
|
||||
border: 1px solid #FF7300;
|
||||
}
|
||||
#preview li.selected {
|
||||
border: solid 2px #FF7300;
|
||||
}
|
||||
#preview li .tools {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
font-size: 14px;
|
||||
text-align: right;
|
||||
}
|
||||
#preview li .tools a {
|
||||
color: red;
|
||||
}
|
||||
/**/
|
||||
#imagePage {
|
||||
position: relative;
|
||||
}
|
||||
#imageMask {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 20px;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
display: none;
|
||||
z-index: 2;
|
||||
}
|
||||
/**/
|
||||
#previewAttrs .form-control {
|
||||
width: auto;
|
||||
}
|
||||
#previewAttrs label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@media screen and (max-width:700px) {
|
||||
#previewAttrs {
|
||||
display: none;
|
||||
}
|
||||
#drop {
|
||||
min-height: initial;
|
||||
padding: 10px 0;
|
||||
}
|
||||
#drop a {
|
||||
position: initial;
|
||||
margin: auto;
|
||||
}
|
||||
#url form {
|
||||
margin: 0 !important;
|
||||
margin: auto;
|
||||
}
|
||||
.form-inline .form-group {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.form-inline .form-control {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
#preview li:nth-child(3n),
|
||||
#preview li:nth-child(4n),
|
||||
#preview li:nth-child(5),
|
||||
#preview li:nth-child(7),
|
||||
#preview li:nth-child(10)
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
#imageList li {
|
||||
width: 90px;
|
||||
}
|
||||
}
|
||||
#imageUrlForm {
|
||||
min-height: 260px;
|
||||
text-align: center;
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
.md {
|
||||
position: relative;
|
||||
height: 350px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.md #previewAttrs {
|
||||
display: none;
|
||||
|
||||
}
|
||||
.md #preview {
|
||||
border: none;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.md #drop {
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
#albumsForUpload {
|
||||
width: 150px;
|
||||
}
|
||||
BIN
public/album/images/ajax-loader.gif
Normal file
BIN
public/album/images/ajax-loader.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 723 B |
7
public/album/js/main.all.js
Normal file
7
public/album/js/main.all.js
Normal file
File diff suppressed because one or more lines are too long
865
public/album/js/main.js
Normal file
865
public/album/js/main.js
Normal file
@@ -0,0 +1,865 @@
|
||||
function log(o) {
|
||||
// console.log(o);
|
||||
}
|
||||
function retIsOk(ret) {
|
||||
if(ret && typeof ret == "object" && ret.Ok == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var urlPrefix = UrlPrefix;
|
||||
var getMsg = parent.getMsg;
|
||||
if (!getMsg) {
|
||||
getMsg = function(msg) {
|
||||
return msg;
|
||||
};
|
||||
}
|
||||
|
||||
// load image
|
||||
function getImageSize(url, callback) {
|
||||
var img = document.createElement('img');
|
||||
|
||||
function done(width, height) {
|
||||
img.parentNode.removeChild(img);
|
||||
callback({width: width, height: height});
|
||||
}
|
||||
|
||||
img.onload = function() {
|
||||
done(img.clientWidth, img.clientHeight);
|
||||
};
|
||||
|
||||
img.onerror = function() {
|
||||
done();
|
||||
};
|
||||
|
||||
img.src = url;
|
||||
|
||||
var style = img.style;
|
||||
style.visibility = 'hidden';
|
||||
style.position = 'fixed';
|
||||
style.bottom = style.left = 0;
|
||||
style.width = style.height = 'auto';
|
||||
|
||||
document.body.appendChild(img);
|
||||
}
|
||||
|
||||
var o = {
|
||||
maxSelected: G.maxSelected,
|
||||
selectedZoneO:$("#preview"),
|
||||
previewO: $("#preview"),
|
||||
selectedImages:[], // selected urls
|
||||
imageAttrs: {}, // src => {width, height, title}
|
||||
|
||||
pageNum: 1,
|
||||
|
||||
pagination: function(count) {
|
||||
var self = this;
|
||||
$(".pagination").pagination(count, {
|
||||
items_per_page: G.perPageItems,
|
||||
prev_text: getMsg('Prev'),
|
||||
next_text: getMsg('Next'),
|
||||
callback: function(pageNum) {
|
||||
self.pageNum = pageNum+1;
|
||||
self.renderImages($("#albumsForList").val(), self.pageNum, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
showMsg: function(msg) {
|
||||
$("#msg").html(msg).css("display", "inline");
|
||||
setTimeout(function() {
|
||||
$("#msg").fadeOut();
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
pageAddAlbum: function(ret) {
|
||||
var html = '<option value="' + ret.AlbumId + '">' + ret.Name + '</option>';
|
||||
$("#albumsForUpload").append(html).val(ret.AlbumId);
|
||||
$("#albumsForList").append(html);
|
||||
},
|
||||
pageUpdateAlbum: function(albumId, albumName) {
|
||||
$('option[value="' + albumId + '"]').html(albumName);
|
||||
},
|
||||
// add, delete, update album
|
||||
processAlbum: function() {
|
||||
var self = this;
|
||||
var isAddAlbum = true;
|
||||
var curAlbum = "";
|
||||
function toggleAddAlbum() {
|
||||
if($("#addOrUpdateAlbumForm").is(":hidden")) {
|
||||
$("#addOrUpdateAlbumForm").show();
|
||||
$("#albumSelect").hide();
|
||||
} else {
|
||||
$("#addOrUpdateAlbumForm").hide();
|
||||
$("#albumSelect").show();
|
||||
}
|
||||
}
|
||||
// rename
|
||||
$("#renameAlbumBtn").click(function(){
|
||||
curAlbum = $("#albumsForUpload").val();
|
||||
if(!curAlbum) {
|
||||
alert(getMsg("Cannot rename default album"));
|
||||
return;
|
||||
}
|
||||
toggleAddAlbum();
|
||||
$("#addOrUpdateAlbumBtn").html(getMsg("Rename Album"));
|
||||
$("#albumName").val($("#albumsForUpload option:selected").html()).focus();
|
||||
isAddAlbum = false;
|
||||
});
|
||||
// add album
|
||||
$("#addAlbumBtn").click(function() {
|
||||
toggleAddAlbum();
|
||||
$("#addOrUpdateAlbumBtn").html(getMsg("Add Album"));
|
||||
$("#albumName").val("").focus();
|
||||
isAddAlbum = true;
|
||||
});
|
||||
$("#cancelAlbumBtn").click(function() {
|
||||
toggleAddAlbum();
|
||||
});
|
||||
// add or update album
|
||||
$("#addOrUpdateAlbumBtn").click(function() {
|
||||
var albumName = $("#albumName").val();
|
||||
if(!albumName) {
|
||||
$("#albumName").focus();
|
||||
return;
|
||||
}
|
||||
if(isAddAlbum) {
|
||||
$.get("/album/addAlbum", {name: albumName}, function(ret) {
|
||||
if(typeof ret == "object" && ret.AlbumId != "") {
|
||||
$("#albumName").val("");
|
||||
self.showMsg(getMsg("Add Success!"));
|
||||
self.pageAddAlbum(ret);
|
||||
|
||||
setTimeout(function() {
|
||||
toggleAddAlbum();
|
||||
}, 200);
|
||||
} else {
|
||||
alert(getMsg("error"));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$.get("/album/updateAlbum", {albumId: curAlbum, name: albumName}, function(ret) {
|
||||
if(typeof ret == "boolean" && ret) {
|
||||
$("#albumName").val("");
|
||||
self.showMsg(getMsg("Rename Success!"));
|
||||
self.pageUpdateAlbum(curAlbum, albumName);
|
||||
|
||||
setTimeout(function() {
|
||||
toggleAddAlbum();
|
||||
}, 200);
|
||||
} else {
|
||||
alert(getMsg("error!"));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
// delete album
|
||||
$("#deleteAlbumBtn").click(function() {
|
||||
var albumId = $("#albumsForUpload").val();
|
||||
if(!albumId) {
|
||||
alert(getMsg("Cannot delete default album"));
|
||||
return;
|
||||
}
|
||||
$.get("/album/deleteAlbum", {albumId: albumId}, function(ret) {
|
||||
if(typeof ret == "object" && ret.Ok == true) {
|
||||
self.showMsg(getMsg("Delete Success!"));
|
||||
// delete this album from select
|
||||
$("#albumsForUpload option[value='" + albumId + "']").remove();
|
||||
|
||||
// if the albumsForList has select this album, it must refresh list after delete it;
|
||||
if($("#albumsForList").val() == albumId) {
|
||||
self.needRefresh = true;
|
||||
}
|
||||
|
||||
$("#albumsForList option[value='" + albumId + "']").remove();
|
||||
|
||||
} else {
|
||||
alert(getMsg("This album has images, please delete it's images at first."));
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
renderAlbums: function() {
|
||||
var self = this;
|
||||
$.get("/album/getAlbums", function(ret) {
|
||||
if(!ret) return;
|
||||
var html = "";
|
||||
for(var i in ret) {
|
||||
var each = ret[i];
|
||||
var option = '<option value="' + each.AlbumId + '">' + each.Name+ '</option>';
|
||||
html += option;
|
||||
}
|
||||
|
||||
$("#albumsForUpload").append(html);
|
||||
$("#albumsForList").append(html);
|
||||
|
||||
var albumId = $("#albumsForList").val();
|
||||
self.renderImages(albumId, 1, true);
|
||||
});
|
||||
},
|
||||
|
||||
imageMaskO: $("#imageMask"),
|
||||
noImagesO: $("#noImages"),
|
||||
loadingO: $("#loading"),
|
||||
|
||||
loadingStart: function() {
|
||||
if(this.imageMaskO.is(":hidden")) {
|
||||
this.imageMaskO.css("opacity", 0.8).show();
|
||||
}
|
||||
this.noImagesO.hide();
|
||||
this.loadingO.show();
|
||||
},
|
||||
loadingEnd: function() {
|
||||
this.imageMaskO.hide();
|
||||
},
|
||||
|
||||
noImages: function () {
|
||||
this.imageMaskO.show().css("opacity", 1);
|
||||
this.noImagesO.show();
|
||||
this.loadingO.hide();
|
||||
},
|
||||
|
||||
|
||||
search: function() {
|
||||
var self = this;
|
||||
var t1 = 1;
|
||||
$("#key").on("keyup", function() {
|
||||
var t2 = ++t1;
|
||||
var key = $(this).val();
|
||||
var albumId = $("#albumsForList").val();
|
||||
|
||||
self.renderImages(albumId, 1, true, key, function(){
|
||||
return t1 == t2;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
renderImages: function(albumId, page, needRenderPagination, key, needRender) {
|
||||
var self = this;
|
||||
|
||||
if(!page) {
|
||||
page = 1;
|
||||
}
|
||||
self.loadingStart();
|
||||
$.get("/file/getImages", {albumId: albumId, page: page, key: key}, function(ret) {
|
||||
if(!ret || !ret.Count) {
|
||||
self.noImages();
|
||||
return;
|
||||
}
|
||||
self.loadingEnd();
|
||||
var datas = ret.List;
|
||||
var selectedMap = {};
|
||||
|
||||
for(var i in self.selectedImages) {
|
||||
var src = self.selectedImages[i]; // src include G.imageSrcPrefix
|
||||
selectedMap[src] = true;
|
||||
}
|
||||
// log(self.selectedImages);
|
||||
|
||||
var html = "";
|
||||
for(var i in datas){
|
||||
var each = datas[i];
|
||||
var classes = "";
|
||||
// life edit
|
||||
// 之前的
|
||||
if(each.Path != "" && each.Path[0] == "/") {
|
||||
each.Path = each.Path.substr(1);
|
||||
}
|
||||
if(each.Path != "" && each.Path.substr(0, 7) == "upload/") {
|
||||
var src = urlPrefix + "/" + each.Path;
|
||||
} else {
|
||||
var src = urlPrefix + "/file/outputImage?fileId=" + each.FileId;
|
||||
}
|
||||
// log(src);
|
||||
if(selectedMap[src]) {
|
||||
classes = 'class="selected"';
|
||||
}
|
||||
html += '<li ' + classes + '>';
|
||||
html += '<a title="" href="javascript:;" class="a-img"><img alt="" src="' + src + '" data-original="' + src + '" ></a>';
|
||||
// html += '<div class="tools"><a href="javascript:;" class="del" data-id="' + each.FileId + '"><span class="fa fa-trash"></span></a></div>';
|
||||
html += '<div class="tools clearfix" data-id="' + each.FileId + '"><div class="file-title pull-left">' + each.Title + '</div><div class="pull-right"><a href="javascript:;" class="del" data-id="' + each.FileId + '"><span class="fa fa-trash"></span></a></div></div>';
|
||||
html += "</li>";
|
||||
}
|
||||
|
||||
// var html = $("#tImage").render(datas);
|
||||
$("#imageList").html(html);
|
||||
|
||||
if(needRenderPagination) {
|
||||
self.pagination(ret.Count);
|
||||
}
|
||||
|
||||
// $("#imageList img").lazyload({effect : "fadeIn"});
|
||||
// $("#imageList img").lazyload();
|
||||
});
|
||||
},
|
||||
|
||||
// 初始化已选择的图片区域
|
||||
initSelectedZones: function() {
|
||||
var self = this;
|
||||
num = this.maxSelected;
|
||||
self.previewO.html("");
|
||||
for(var i = 1; i <= num; ++i) {
|
||||
// self.previewO.append("<li>" + i + "</li>");
|
||||
self.previewO.append("<li>?</li>");
|
||||
}
|
||||
},
|
||||
|
||||
reRenderSelectedImages: function(isRemove, addSrc) {
|
||||
var self = this;
|
||||
|
||||
var lis = this.selectedZoneO.find("li");
|
||||
var size = this.selectedImages.length-1;
|
||||
for(var i = 0; i < this.maxSelected; ++i) {
|
||||
var target = lis.eq(i);
|
||||
if(i > size) {
|
||||
target.html('?');
|
||||
} else {
|
||||
src = this.selectedImages[i];
|
||||
|
||||
var data = self.imageAttrs[src];
|
||||
var attrs = "";
|
||||
if(data) {
|
||||
if(data.width) attrs += ' data-width="' + data.width + '"';
|
||||
if(data.height) attrs += ' data-height="' + data.height + '"';
|
||||
if(data.title) attrs += ' data-title="' + data.title + '"';
|
||||
}
|
||||
|
||||
target.html('<img ' + attrs + ' src="' + src + '" width="60"/><div class="tools"><a title="' + getMsg('click to remove this image') + '" href="javascript:;" class="del"><span class="fa fa-trash"></span></a></div>');
|
||||
}
|
||||
|
||||
// remove selected
|
||||
if(isRemove) {
|
||||
target.removeClass("selected");
|
||||
} else {
|
||||
// is add
|
||||
// trigger click and set attrs
|
||||
if(addSrc == src) {
|
||||
target.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
removeSelectedImage: function($li) {
|
||||
var self = this;
|
||||
|
||||
var src = $li.find("img").attr('src');
|
||||
for(var i in this.selectedImages) {
|
||||
if(this.selectedImages[i] == src) {
|
||||
this.selectedImages.splice(i, 1)
|
||||
}
|
||||
}
|
||||
this.reRenderSelectedImages(true);
|
||||
|
||||
// clear attrs and disable it
|
||||
self.clearAttrs();
|
||||
},
|
||||
addSelectedImage: function($li) {
|
||||
if(this.maxSelected > 1 && this.maxSelected <= this.selectedImages.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// life 为了图片安全
|
||||
if(typeof $li == "object") {
|
||||
var src = $li.find("img").attr('src');
|
||||
} else {
|
||||
// 也有可能来自url
|
||||
if($li.indexOf("http://") != -1 || $li.indexOf("https://") != -1) {
|
||||
src = $li;
|
||||
} else {
|
||||
// 来自内部
|
||||
src = urlPrefix + "/file/outputImage?fileId=" + $li;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果只允许选1个
|
||||
if(this.maxSelected == 1) {
|
||||
// 先把其它的去掉
|
||||
$("#imageList li").removeClass("selected");
|
||||
this.selectedImages = [src];
|
||||
} else {
|
||||
this.selectedImages.push(src);
|
||||
}
|
||||
|
||||
this.reRenderSelectedImages(false, src);
|
||||
|
||||
return true;
|
||||
},
|
||||
initDataFromTinymce: function() {
|
||||
var self = this;
|
||||
var datas = top.LEAUI_DATAS;
|
||||
var lastSrc = "";
|
||||
if(datas && datas.length > 0) {
|
||||
for(var i in datas) {
|
||||
var data = datas[i];
|
||||
data.constrain = true;
|
||||
lastSrc = data.src;
|
||||
self.selectedImages.push(data.src);
|
||||
self.imageAttrs[data.src] = data;
|
||||
}
|
||||
|
||||
self.reRenderSelectedImages(false, lastSrc);
|
||||
}
|
||||
},
|
||||
|
||||
init: function() {
|
||||
var self = this;
|
||||
|
||||
self.processAlbum();
|
||||
|
||||
$("#albumsForList").change(function() {
|
||||
var albumId = $(this).val();
|
||||
self.renderImages(albumId, 1, true);
|
||||
});
|
||||
|
||||
$("#imageList").on("click", 'li', function() {
|
||||
if($(this).hasClass("selected")) {
|
||||
$(this).removeClass("selected");
|
||||
self.removeSelectedImage($(this));
|
||||
} else {
|
||||
if(self.addSelectedImage($(this))){
|
||||
$(this).addClass("selected");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// delete file
|
||||
$("#imageList").on("click", '.del', function(e) {
|
||||
var t = this;
|
||||
e.stopPropagation();
|
||||
|
||||
if(confirm(getMsg("Are you sure to delete this image ?"))) {
|
||||
var fileId = $(this).data('id');
|
||||
$.get("/file/deleteImage", {fileId: fileId}, function(ret) {
|
||||
if(ret) {
|
||||
var $li = $(t).closest('li');
|
||||
if($li.hasClass("selected")) {
|
||||
self.removeSelectedImage($li);
|
||||
}
|
||||
$(t).closest('li').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// edit file title
|
||||
$("#imageList").on("click", '.file-title', function(e) {
|
||||
var p = this;
|
||||
var t = this;
|
||||
e.stopPropagation();
|
||||
if($(this).children().eq(0).is("input")){
|
||||
return;
|
||||
}
|
||||
var fileId = $(p).parent().data('id');
|
||||
var fileTitle = $(this).text();
|
||||
$(this).html('<input type="text" value="' + fileTitle + '" />');
|
||||
|
||||
var $input = $(this).find("input");
|
||||
$input.focus();
|
||||
$input.keydown(function(e){
|
||||
if(e.keyCode==13){
|
||||
$(this).trigger("blur");
|
||||
}
|
||||
});
|
||||
$input.blur(function() {
|
||||
var title = $(this).val();
|
||||
if(!title) {
|
||||
title = fileTitle;
|
||||
} else {
|
||||
$.post("/file/updateImageTitle", {fileId: fileId, title: title});
|
||||
}
|
||||
$(p).html(title);
|
||||
});
|
||||
});
|
||||
|
||||
// remove preview
|
||||
$("#preview").on("click", '.del', function(e) {
|
||||
e.stopPropagation();
|
||||
var $li = $(this).closest("li");
|
||||
var src = $li.find("img").attr("src");
|
||||
self.removeSelectedImage($li);
|
||||
|
||||
// 在当前的imagesList下看是否有
|
||||
$("#imageList img").each(function() {
|
||||
var src2 = $(this).attr('src');
|
||||
if(src == src2) {
|
||||
$(this).parent().parent().removeClass("selected");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
$("#goAddImageBtn").click(function() {
|
||||
$("#albumsForUpload").val($("#albumsForList").val());
|
||||
$('#myTab li:eq(1) a').tab('show');
|
||||
});
|
||||
|
||||
// toggle tab
|
||||
// refresh
|
||||
$('#myTab a').on('shown.bs.tab', function(e) {
|
||||
e.preventDefault()
|
||||
$(this).tab('show');
|
||||
var href = $(this).attr("href");
|
||||
|
||||
if(self.needRefresh && href == "#images") {
|
||||
setTimeout(function(){
|
||||
var albumId = $("#albumsForList").val();
|
||||
var key = $("#key").val();
|
||||
self.renderImages(albumId, self.pageNum, true, key);
|
||||
}, 200);
|
||||
self.needRefresh = false;
|
||||
}
|
||||
|
||||
if(href == "#url") {
|
||||
$("#imageUrl").focus();
|
||||
}
|
||||
});
|
||||
$("#refresh").click(function() {
|
||||
var albumId = $("#albumsForList").val();
|
||||
var key = $("#key").val();
|
||||
self.renderImages(albumId, self.pageNum, false, key);
|
||||
});
|
||||
|
||||
// add url
|
||||
$("#addImageUrlBtn").click(function(e) {
|
||||
e.preventDefault();
|
||||
var url = $.trim($("#imageUrl").val());
|
||||
if(!url) {
|
||||
$("#imageUrl").focus();
|
||||
return;
|
||||
}
|
||||
|
||||
getImageSize(url, function(ret) {
|
||||
if(!ret.width || !ret.height){
|
||||
$("#msgForUrl").show();
|
||||
return;
|
||||
}
|
||||
$("#msgForUrl").hide();
|
||||
$("#imageUrl").val("");
|
||||
self.addSelectedImage(url);
|
||||
});
|
||||
});
|
||||
|
||||
// 设置属性
|
||||
$("#preview").on("click", 'li', function() {
|
||||
if($(this).hasClass("selected")) {
|
||||
// $(this).removeClass("selected");
|
||||
} else {
|
||||
if($(this).find("img").length){
|
||||
$("#preview li").removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
|
||||
self.initAttr($(this));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#attrTitle, #attrWidth, #attrHeight").on("keyup", function(){
|
||||
self.modifyAttr($(this));
|
||||
});
|
||||
$("#attrConstrain").on("click", function(){
|
||||
self.modifyAttr($(this));
|
||||
});
|
||||
|
||||
// file search
|
||||
self.search();
|
||||
|
||||
self.initSelectedZones();
|
||||
|
||||
self.initDataFromTinymce();
|
||||
|
||||
self.renderAlbums();
|
||||
//...
|
||||
self.initUploader();
|
||||
},
|
||||
|
||||
// 设置
|
||||
curSrc: "",
|
||||
curLi: null,
|
||||
attrTitleO: $("#attrTitle"),
|
||||
attrWidthO: $("#attrWidth"),
|
||||
attrHeightO: $("#attrHeight"),
|
||||
attrConstrainO: $("#attrConstrain"),
|
||||
// clear attrs and disable it
|
||||
clearAttrs: function() {
|
||||
var self = this;
|
||||
self.attrTitleO.val("").attr("disabled", true);
|
||||
self.attrHeightO.val("").attr("disabled", true);
|
||||
self.attrWidthO.val("").attr("disabled", true);
|
||||
self.attrConstrainO.prop("checked", false).attr("disabled", true);
|
||||
},
|
||||
scale: function(isWidth) {
|
||||
var self = this;
|
||||
var autoScale = self.attrConstrainO.is(":checked");
|
||||
var width = +self.attrWidthO.val();
|
||||
var height = +self.attrHeightO.val();
|
||||
if(isNaN(width) || isNaN(height)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var curAttrs = self.getCurAttrs();
|
||||
var preWidth = curAttrs.preWidth || curAttrs.width;
|
||||
var preHeight = curAttrs.preHeight || curAttrs.height;
|
||||
|
||||
if(autoScale && preWidth && preHeight) {
|
||||
if(isWidth) {
|
||||
height = parseInt((width/preWidth) * preHeight);
|
||||
self.attrHeightO.val(height);
|
||||
} else {
|
||||
width = parseInt((height/preHeight) * preWidth);
|
||||
self.attrWidthO.val(width);
|
||||
}
|
||||
}
|
||||
var ret = {width: width, height: height};
|
||||
return ret;
|
||||
},
|
||||
getCurAttrs: function() {
|
||||
var self = this;
|
||||
return self.imageAttrs[self.curSrc];
|
||||
},
|
||||
setCurDataAttrs: function(attrs) {
|
||||
var self = this;
|
||||
var img = self.curLi.find("img");
|
||||
img.attr("data-width", attrs.width);
|
||||
img.attr("data-height", attrs.height);
|
||||
img.attr("data-title", attrs.title);
|
||||
|
||||
self.imageAttrs[self.curSrc] = attrs;
|
||||
},
|
||||
// 修改属性
|
||||
modifyAttr: function($target){
|
||||
var self = this;
|
||||
var id = $target.attr("id");
|
||||
var val = $target.val();
|
||||
var curAttrs = self.getCurAttrs();
|
||||
if(!curAttrs) {
|
||||
return;
|
||||
}
|
||||
switch(id) {
|
||||
case "attrConstrain":
|
||||
val = 0;
|
||||
if($target.is(":checked")) {
|
||||
val = 1;
|
||||
}
|
||||
curAttrs['constrain'] = val;
|
||||
break;
|
||||
case "attrTitle":
|
||||
curAttrs['title'] = val;
|
||||
break;
|
||||
case "attrWidth":
|
||||
$.extend(curAttrs, self.scale(true));
|
||||
break;
|
||||
case "attrHeight":
|
||||
$.extend(curAttrs, self.scale(false));
|
||||
break;
|
||||
}
|
||||
|
||||
self.setCurDataAttrs(curAttrs);
|
||||
},
|
||||
// when click preview li
|
||||
initAttr: function($li) {
|
||||
var self = this;
|
||||
if(typeof $li != "object") {
|
||||
$li = $("#preview").find('img[src="' + $li + '"]').parent();
|
||||
};
|
||||
|
||||
var src = $li.find("img").attr("src");
|
||||
self.curSrc = src;
|
||||
self.curLi = $li;
|
||||
|
||||
var attrs = self.imageAttrs[src];
|
||||
function setAttr(attrs) {
|
||||
attrs = attrs || {};
|
||||
self.attrTitleO.val(attrs.title).attr("disabled", false);
|
||||
self.attrWidthO.val(attrs.width).attr("disabled", false);
|
||||
self.attrHeightO.val(attrs.height).attr("disabled", false);
|
||||
self.attrConstrainO.attr("disabled", false);
|
||||
|
||||
if(attrs.constrain) {
|
||||
self.attrConstrainO.prop('checked', true);
|
||||
} else {
|
||||
self.attrConstrainO.prop('checked', false);
|
||||
}
|
||||
|
||||
self.setCurDataAttrs(attrs);
|
||||
}
|
||||
attrs = attrs || {};
|
||||
if(!attrs || !attrs.width || !attrs.height) {
|
||||
getImageSize(src, function(ret) {
|
||||
ret.title = attrs.title || "";
|
||||
ret.constrain = 1;
|
||||
ret.preWidth = ret.width;
|
||||
ret.preHeight = ret.height;
|
||||
if(src != self.curSrc) {
|
||||
self.imageAttrs[src] = ret;
|
||||
// in case user click fast
|
||||
self.setCurDataAttrs(attrs);
|
||||
return;
|
||||
}
|
||||
// set attrs
|
||||
setAttr(ret);
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
setAttr(attrs);
|
||||
}
|
||||
},
|
||||
|
||||
needRefresh: false,
|
||||
|
||||
uploadRefreshImageList: function() {
|
||||
var self = this;
|
||||
// check albumId
|
||||
var albumId = $("#albumsForList").val();
|
||||
if(albumId == $("#albumsForUpload").val()){
|
||||
self.needRefresh = true;
|
||||
}
|
||||
},
|
||||
|
||||
initUploader: function() {
|
||||
var self = this;
|
||||
var ul = $('#upload ul');
|
||||
|
||||
$('#drop a').click(function() {
|
||||
// trigger to show file select
|
||||
$(this).parent().find('input').click();
|
||||
});
|
||||
// Initialize the jQuery File Upload plugin
|
||||
$('#upload').fileupload({
|
||||
dataType: 'json',
|
||||
pasteZone: '',
|
||||
acceptFileTypes: /(\.|\/)(gif|jpg|jpeg|png|jpe)$/i,
|
||||
// maxFileSize: 210000,
|
||||
|
||||
// This element will accept file drag/drop uploading
|
||||
dropZone: $('#drop'),
|
||||
formData: function(form) {
|
||||
return [{name: 'albumId', value: $("#albumsForUpload").val()}]
|
||||
},
|
||||
/*
|
||||
urlFunc: function() {
|
||||
return 'server/index.php?action=file:uploadImage&album_id=' + $("#albumsForUpload").val();
|
||||
},
|
||||
*/
|
||||
|
||||
// This function is called when a file is added to the queue;
|
||||
// either via the browse button, or via drag/drop:
|
||||
add: function(e, data) {
|
||||
// 文件大小限制
|
||||
var size = data.files[0].size;
|
||||
var maxFileSize = +parent.GlobalConfigs["uploadImageSize"] || 100;
|
||||
if(typeof size == 'number' && size > 1024 * 1024 * maxFileSize) {
|
||||
var tpl = $('<li><div class="alert alert-danger"><a class="close" data-dismiss="alert">×</a></div></li>');
|
||||
tpl.find('div').append('<b>Warning:</b> ' + data.files[0].name + ' <small>[<i>' + formatFileSize(data.files[0].size) + '</i>] is bigger than ' + maxFileSize + 'M</small> ');
|
||||
tpl.appendTo(ul);
|
||||
return;
|
||||
}
|
||||
|
||||
var tpl = $('<li><div class="alert alert-info"><img class="loader" src="/public/album/images/ajax-loader.gif"> <a class="close" data-dismiss="alert">×</a></div></li>');
|
||||
// Append the file name and file size
|
||||
tpl.find('div').append(data.files[0].name + ' <small>[<i>' + formatFileSize(data.files[0].size) + '</i>]</small>');
|
||||
|
||||
// Add the HTML to the UL element
|
||||
data.context = tpl.appendTo(ul);
|
||||
|
||||
// data.form[0].action += "&album_id=" + $("#albumsForUpload").val();
|
||||
|
||||
// Automatically upload the file once it is added to the queue
|
||||
var jqXHR = data.submit();
|
||||
},
|
||||
|
||||
|
||||
done: function(e, data) {
|
||||
if (data.result.Ok == true) {
|
||||
data.context.remove();
|
||||
|
||||
// add image to preview
|
||||
self.addSelectedImage(data.result.Id);
|
||||
// reresh image list
|
||||
self.uploadRefreshImageList();
|
||||
} else {
|
||||
data.context.empty();
|
||||
var tpl = $('<li><div class="alert alert-danger"><a class="close" data-dismiss="alert">×</a></div></li>');
|
||||
tpl.find('div').append('<b>' + getMsg('Error') + ':</b> ' + data.files[0].name + ' <small>[<i>' + formatFileSize(data.files[0].size) + '</i>]</small> ' + data.result.Msg);
|
||||
data.context.append(tpl);
|
||||
setTimeout((function(tpl) {
|
||||
return function() {
|
||||
tpl.remove();
|
||||
}
|
||||
})(tpl), 3000);
|
||||
}
|
||||
$("#upload-msg").scrollTop(1000);
|
||||
},
|
||||
fail: function(e, data) {
|
||||
data.context.empty();
|
||||
var tpl = $('<li><div class="alert alert-danger"><a class="close" data-dismiss="alert">×</a></div></li>');
|
||||
tpl.find('div').append('<b>Error:</b> ' + data.files[0].name + ' <small>[<i>' + formatFileSize(data.files[0].size) + '</i>]</small> ' + data.errorThrown);
|
||||
data.context.append(tpl);
|
||||
|
||||
$("#upload-msg").scrollTop(1000);
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent the default action when a file is dropped on the window
|
||||
$(document).on('drop dragover', function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// Helper function that formats the file sizes
|
||||
function formatFileSize(bytes) {
|
||||
if (typeof bytes !== 'number') {
|
||||
return '';
|
||||
}
|
||||
if (bytes >= 1000000000) {
|
||||
return (bytes / 1000000000).toFixed(2) + ' GB';
|
||||
}
|
||||
if (bytes >= 1000000) {
|
||||
return (bytes / 1000000).toFixed(2) + ' MB';
|
||||
}
|
||||
return (bytes / 1000).toFixed(2) + ' KB';
|
||||
}
|
||||
|
||||
// drag css
|
||||
$(document).bind('dragover', function (e) {
|
||||
var dropZone = $('#drop'),
|
||||
timeout = window.dropZoneTimeout;
|
||||
if (!timeout) {
|
||||
dropZone.addClass('in');
|
||||
} else {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
var found = false,
|
||||
node = e.target;
|
||||
do {
|
||||
if (node === dropZone[0]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
node = node.parentNode;
|
||||
} while (node != null);
|
||||
if (found) {
|
||||
dropZone.addClass('hover');
|
||||
} else {
|
||||
dropZone.removeClass('hover');
|
||||
}
|
||||
window.dropZoneTimeout = setTimeout(function () {
|
||||
window.dropZoneTimeout = null;
|
||||
dropZone.removeClass('in hover');
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
o.init();
|
||||
});
|
||||
|
||||
// 为md得到图片链接
|
||||
function mdGetImgSrc() {
|
||||
if(o.selectedImages && o.selectedImages.length) {
|
||||
return o.selectedImages[0];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -51,6 +51,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div>
|
||||
</div>
|
||||
<script src="{{$.jQueryUrl}}"></script>
|
||||
<script src="{{$.bootstrapJsUrl}}"></script>
|
||||
|
||||
@@ -205,6 +205,11 @@ ul.dropdown-menu {
|
||||
padding-bottom: 10px;
|
||||
width: 700px;
|
||||
}
|
||||
.footer-leanote {
|
||||
text-align: center;
|
||||
padding: 3px 0;
|
||||
color: #ccc;
|
||||
}
|
||||
.navbar-brand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div>
|
||||
</div>
|
||||
<script src="{{$.jQueryUrl}}"></script>
|
||||
<script src="{{$.bootstrapJsUrl}}"></script>
|
||||
|
||||
@@ -357,6 +357,11 @@ a:hover {
|
||||
#search {
|
||||
width: 150px;
|
||||
}
|
||||
.footer-leanote {
|
||||
text-align: center;
|
||||
padding: 3px 0;
|
||||
color: #ccc;
|
||||
}
|
||||
#footerContainer {
|
||||
background-color: #ffffff;
|
||||
color: #666666;
|
||||
@@ -518,6 +523,7 @@ a:hover {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
table {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-leanote">Proudly powered by <a href="https://leanote.com">Leanote</a></div>
|
||||
</div>
|
||||
<script src="{{$.jQueryUrl}}"></script>
|
||||
<script src="{{$.bootstrapJsUrl}}"></script>
|
||||
|
||||
@@ -204,6 +204,11 @@ ul.dropdown-menu {
|
||||
#footer {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.footer-leanote {
|
||||
text-align: center;
|
||||
padding: 3px 0;
|
||||
color: #ccc;
|
||||
}
|
||||
.navbar-brand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,5 @@
|
||||
@bgColor: #fff;
|
||||
@headerBgColor: #fff;
|
||||
@fontFamily: 'Open Sans','Helvetica Neue',Arial,'Hiragino Sans GB','Microsoft YaHei','WenQuanYi Micro Hei',sans-serif;
|
||||
@fontFamily: "lucida grande", "lucida sans unicode", lucida, helvetica, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
|
||||
@aWhiteColor: #fff;
|
||||
@aBlackColor: #000;
|
||||
@@ -13,45 +12,20 @@
|
||||
@containerWidth: 845px;
|
||||
@titleColor: @hColor; // #5AD4A0;
|
||||
|
||||
@pwidth: 600px;
|
||||
@pwidth: 650px;
|
||||
@green: #65bd77;
|
||||
@gray: #ccc;
|
||||
|
||||
// font
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url('../../fonts/open-sans2/DXI1ORHCpsQm3Vp6mXoaTXhCUOGz7vYGh680lGh-uXM.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url('../../fonts/open-sans2/cJZKeOuBrn4kERxqtaUH3T8E0i7KZn-EPnyo3HZu7kw.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('../../fonts/open-sans2/k3k702ZOKiLJc3WVjuplzHhCUOGz7vYGh680lGh-uXM.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url('../../fonts/open-sans2/xjAJXh38I15wypJXxuGMBobN6UDyHWBl620a-IRfuBk.woff') format('woff');
|
||||
}
|
||||
/* leanote */
|
||||
@font-face {
|
||||
font-family: 'leanoteregular';
|
||||
src: url('../../fonts/leanote/leanote-regular-webfont.eot');
|
||||
src: url('../../fonts/leanote/leanote-regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../../fonts/leanote/leanote-regular-webfont.woff') format('woff'),
|
||||
url('../../fonts/leanote/leanote-regular-webfont.ttf') format('truetype'),
|
||||
url('../../fonts/leanote/leanote-regular-webfont.svg#leanoteregular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: 'leanote';
|
||||
src:url('../../fonts/leanote-font2/leanote.eot?-vcf23i');
|
||||
src:url('../../fonts/leanote-font2/leanote.eot?#iefix-vcf23i') format('embedded-opentype'),
|
||||
url('../../fonts/leanote-font2/leanote.ttf?-vcf23i') format('truetype'),
|
||||
url('../../fonts/leanote-font2/leanote.woff?-vcf23i') format('woff'),
|
||||
url('../../fonts/leanote-font2/leanote.svg?-vcf23i#leanote') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@selectionBg: @aBlackColor;
|
||||
@@ -92,11 +66,20 @@ a:hover {
|
||||
}
|
||||
}
|
||||
#logo {
|
||||
font-family: "leanoteregular";
|
||||
font-family: 'leanote';
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-size: 24px;
|
||||
}
|
||||
#logo:before {
|
||||
content: "b";
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
#navbar {
|
||||
@@ -246,161 +229,165 @@ a:hover {
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content {
|
||||
width: 310px;
|
||||
padding: 0;
|
||||
background: @cardBg;
|
||||
top: 35px;
|
||||
border-radius: 6px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.user-card-content {
|
||||
width: 310px;
|
||||
padding: 0;
|
||||
background: @cardBg;
|
||||
top: 35px;
|
||||
border-radius: 6px;
|
||||
word-break: break-all;
|
||||
|
||||
.loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
.loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
.loading {
|
||||
display: block;
|
||||
}
|
||||
.header,
|
||||
.content {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content .loading {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
&:before {
|
||||
content: "";
|
||||
width: 17px;
|
||||
height: 11px;
|
||||
background: url(http://7xj51o.com1.z0.glb.clouddn.com/bar-loading.gif);
|
||||
background-size: 17px 11px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content .header {
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
border-radius: 6px 6px 0 0;
|
||||
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 95px;
|
||||
}
|
||||
|
||||
.follow-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.not-followed {
|
||||
.follow-btn {
|
||||
display: block;
|
||||
}
|
||||
.following-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.following-btn {
|
||||
.unfollow {
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
.following {
|
||||
display: none;
|
||||
}
|
||||
.unfollow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content .header .follow-btn,
|
||||
.user-card-content .header .following-btn {
|
||||
float: right;
|
||||
padding: 4px 10px 2px;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.user-card-content .header .avatar {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.user-card-content .username {
|
||||
line-height: 32px;
|
||||
padding-left: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 100px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-card-content .header>span {
|
||||
position: relative;
|
||||
top: 6px;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.user-card-content .content {
|
||||
padding: 0 10px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.user-card-content .content p {
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.user-card-content .content .intro {
|
||||
padding: 10px 12px 7px;
|
||||
margin: 0 -10px 0;
|
||||
background: white;
|
||||
border-bottom: 1px solid #d9d9d9
|
||||
}
|
||||
|
||||
.user-card-content .content ul {
|
||||
padding: 1px 10px 2px 10px;
|
||||
margin: 0 -10px 10px;
|
||||
background: white
|
||||
}
|
||||
|
||||
.user-card-content .content ul li {
|
||||
border-bottom: 1px dashed #d9d9d9;
|
||||
a {
|
||||
margin: 5px 0;
|
||||
line-height: 20px;
|
||||
display: block;
|
||||
}
|
||||
.header,
|
||||
.content {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content .loading {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
&:before {
|
||||
content: "";
|
||||
width: 17px;
|
||||
height: 11px;
|
||||
background: url(http://7xj51o.com1.z0.glb.clouddn.com/bar-loading.gif);
|
||||
background-size: 17px 11px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content .header, .relation-btn {
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
border-radius: 6px 6px 0 0;
|
||||
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 95px;
|
||||
}
|
||||
|
||||
.follow-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.not-followed {
|
||||
.follow-btn {
|
||||
display: block;
|
||||
}
|
||||
.following-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content .content ul time {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.user-card-content .content h5 {
|
||||
margin: 0;
|
||||
padding-top: 3px;
|
||||
margin-right: 63px;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.user-card-content .content a {
|
||||
color: #999999
|
||||
}
|
||||
.following-btn {
|
||||
.unfollow {
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
.following {
|
||||
display: none;
|
||||
}
|
||||
.unfollow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content .header .follow-btn,
|
||||
.user-card-content .header .following-btn {
|
||||
float: right;
|
||||
padding: 4px 10px 2px;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.user-card-content .header .avatar {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.user-card-content .username {
|
||||
line-height: 32px;
|
||||
padding-left: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 100px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-card-content .header>span {
|
||||
position: relative;
|
||||
top: 6px;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.user-card-content .content {
|
||||
padding: 0 10px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.user-card-content .content p {
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.user-card-content .content .intro {
|
||||
padding: 10px 12px 7px;
|
||||
margin: 0 -10px 0;
|
||||
background: white;
|
||||
border-bottom: 1px solid #d9d9d9
|
||||
}
|
||||
|
||||
.user-card-content .content ul {
|
||||
padding: 1px 10px 2px 10px;
|
||||
margin: 0 -10px 10px;
|
||||
background: white
|
||||
}
|
||||
|
||||
.user-card-content .content ul li {
|
||||
border-bottom: 1px dashed #d9d9d9;
|
||||
a {
|
||||
margin: 5px 0;
|
||||
line-height: 20px;
|
||||
display: block;
|
||||
}
|
||||
&.no-post {
|
||||
border: none;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-content .content ul time {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.user-card-content .content h5 {
|
||||
margin: 0;
|
||||
padding-top: 3px;
|
||||
margin-right: 63px;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.user-card-content .content a {
|
||||
color: #999999
|
||||
}
|
||||
|
||||
|
||||
@@ -531,6 +518,11 @@ a:focus {
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
padding-right: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#searchInfo {
|
||||
@@ -571,4 +563,83 @@ a:focus {
|
||||
.label-red:hover {
|
||||
background: #d9534f;
|
||||
}
|
||||
}
|
||||
|
||||
.no-author {
|
||||
border: 2px dashed #ccc;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
height: 450px;
|
||||
margin-top: 80px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
// followers
|
||||
|
||||
.followers {
|
||||
margin-top: 70px !important;
|
||||
|
||||
.followers-nav {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.user-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.each-user {
|
||||
width: 215px;
|
||||
float: left;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.avatar-wrap {
|
||||
position: relative;
|
||||
.user-card {
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
.avatar-outer {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
line-height: 56px;
|
||||
border: 1px solid #eee;
|
||||
margin-right: 10px;
|
||||
img {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
.username-btn {
|
||||
margin-left: 70px;
|
||||
button {
|
||||
padding: 3px 4px;
|
||||
width: 85px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
}
|
||||
.username {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
.relation-btn {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.user-card .dropdown-menu {
|
||||
left: -125px;
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
.no-author {
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user