Compare commits

...

125 Commits
v0.2 ... 0.5

Author SHA1 Message Date
life
8ae438272b change moto and delete lea 2014-10-15 17:31:27 +08:00
life
f99cca40c2 add qq group 2014-10-14 22:58:24 +08:00
life
3f1930723a lea pagination 2014-09-25 11:37:37 +08:00
life
9fbbde9849 lea++ style fixed 2014-09-24 23:13:37 +08:00
life
3bade30e1a lea++ 2014-09-24 23:01:57 +08:00
life
44c8f2a7e2 fix 2014-09-24 22:56:06 +08:00
life
8d820b069c blog css fixed image width 2014-09-24 22:43:33 +08:00
life
f16ba28a3b merge develop 2014-09-24 22:32:05 +08:00
life
9db1164fe0 merge develop 2014-09-24 22:31:53 +08:00
life
37563d1869 nav 2014-09-24 22:28:42 +08:00
life
87269cc939 Merge branch 'develop'
admin [init ok]
lea++ blog platform [ok]
2014-09-24 22:24:52 +08:00
life
99956cfd72 fix animation when toggle writing mod 2014-09-24 10:06:30 +08:00
life
cff6efde91 common page & animation 2014-09-23 18:56:04 +08:00
life
2221f146de fix TinyMCE Removes site base url 2014-09-22 22:45:57 +08:00
life
1fea36a7c1 fix 2014-09-22 22:43:33 +08:00
life
95af247cdc attach fix download url 2014-09-22 22:31:56 +08:00
life
5a2274328b attach fix download url 2014-09-22 22:26:50 +08:00
life
0e6f777402 update readme 2014-09-22 22:24:18 +08:00
life
84f5e9c969 slimscroll fixed js t() -> tt() 2014-09-22 20:21:58 +08:00
life
c216e3a1ea attach fixed 2014-09-22 20:09:25 +08:00
life
b4f0a08b9f attach fixed 2014-09-22 20:09:12 +08:00
life
02536f6de4 attach css fixed firefox 2014-09-22 20:04:23 +08:00
life
50b6af0446 attach css fixed firefox 2014-09-22 19:58:25 +08:00
life
416dd77717 init service 2014-09-22 19:15:28 +08:00
life
b89721a0f4 tagColor dropdown 2014-09-22 14:39:54 +08:00
life
b8ced2e1c3 donate 2014-09-22 14:06:46 +08:00
life
e240dfbafe dropdown triangle 2014-09-22 12:38:48 +08:00
life
b411302087 file size 2014-09-22 00:58:43 +08:00
life
e86bbb9b02 Merge branch 'develop-feature' 2014-09-22 00:43:50 +08:00
life
7bd5d66c55 #10, #14 2014-09-22 00:41:17 +08:00
life
6b194a63c6 Merge branch 'develop-feature' 2014-09-21 22:53:14 +08:00
life
5439c1b5fb #10 #14 2014-09-21 22:52:37 +08:00
life
ca9be9cd81 Merge branch 'develop-feature' 2014-09-21 22:20:29 +08:00
life
320c79e7a3 #10 #14 2014-09-21 22:20:00 +08:00
life
2ddbeb5b11 #10 #14 [ok]
add attachment feature,
1) upload, delete,
2) link attach into content (include tinymce & markdown)

markdown-editor.js add insertLink function to add link into markdown
content, usage:
MarkdownEditor.insertLink(link, title)

paste plugin edit for safety image
2014-09-21 22:09:54 +08:00
life
c556ab59b5 attachment feature #10 2014-09-21 22:05:04 +08:00
life
ab242b10f2 paste image when edit/add shared note 2014-09-21 00:29:46 +08:00
life
81c2254cfb copy shared note must copy images 2014-09-20 15:20:36 +08:00
life
9f89a3c717 #14 safety image 2014-09-19 17:29:24 +08:00
iiuazz
6f4ba8313c for safety image v1.0 #14 2014-09-19 17:18:53 +08:00
life
c142568a56 leanote's api init 2014-09-18 00:33:22 +08:00
life
65e84d30db new leanote router for leanote's api
/api/user/info => convert it to ApiUser.Info()
2014-09-18 00:28:23 +08:00
iiuazz
76a111c6b0 username lower 2014-09-15 19:37:24 +08:00
iiuazz
4d48ecad49 drop down animation 2014-09-15 16:13:27 +08:00
iiuazz
9bf3b4c2bc update readme 2014-09-15 14:03:06 +08:00
iiuazz
9bdce1a46c css firefox bug 2014-09-15 11:29:02 +08:00
iiuazz
b41101e073 release v1.0 2014-09-13 13:25:49 +08:00
iiuazz
cc9cb271c4 delete bin 2014-09-13 13:19:18 +08:00
iiuazz
b5661edd85 fix merge conflict 2014-09-13 13:16:05 +08:00
iiuazz
a60e49364b merge 2014-09-13 13:12:05 +08:00
iiuazz
cdd6aa035b Merge branch 'develop'
Conflicts:
	public/css/theme/default.css
	public/css/theme/simple.css
	public/css/theme/writting-overwrite.css
	public/css/theme/writting.css
2014-09-13 13:06:53 +08:00
iiuazz
522bbd9ed4 css 2014-09-13 13:02:04 +08:00
iiuazz
138dfa904c fix 2014-09-13 12:54:56 +08:00
leanote
94e3f543ab Update LICENSE 2014-09-13 10:52:13 +08:00
leanote
634ad35813 Update LICENSE 2014-09-13 10:44:01 +08:00
iiuazz
20a39d5128 fix 2014-09-13 00:41:28 +08:00
iiuazz
27dbd6552c contexmenu dynamic 2014-09-13 00:20:18 +08:00
iiuazz
d1f18b9476 v1.0-alpha release 2014-09-12 21:46:03 +08:00
iiuazz
8dd5239a8a default theme [ok] 2014-09-12 21:40:14 +08:00
iiuazz
e6fb6e3f09 all is ok, waitting to edit default theme 2014-09-12 20:42:40 +08:00
iiuazz
52010e4fc1 share notebook [ok] 2014-09-12 20:31:14 +08:00
iiuazz
adf59976ec email 2014-09-12 15:39:28 +08:00
iiuazz
f30430bc63 before sharenotebooks 2014-09-12 15:32:19 +08:00
iiuazz
a113b9b5e5 item setting [ok]
notebook icon [ok]
2014-09-12 11:50:24 +08:00
iiuazz
85fd63baa5 min left ok 2014-09-12 09:55:16 +08:00
iiuazz
4b723eb331 prepare to fix min 2014-09-11 22:46:26 +08:00
iiuazz
9b96f0fbd1 move, copy notebooks contextmenu ok 2014-09-11 21:49:15 +08:00
iiuazz
ca4eb3aef5 search notebook for list [ok] 2014-09-11 20:55:07 +08:00
iiuazz
883ea1da62 choose notebook to add note/markdown [ok] 2014-09-11 18:55:42 +08:00
iiuazz
dc2435a83d add notebook, add sub notebook, delete, rename [ok] 2014-09-11 14:31:25 +08:00
iiuazz
2bed5b31fa add ztree to drag and sort notebooks 2014-09-10 22:44:43 +08:00
iiuazz
bfcf8ec547 album 2014-09-09 19:34:59 +08:00
iiuazz
c30cb745f9 test 2014-09-09 19:31:43 +08:00
leanote
db2e78c5a5 Update .project 2014-09-09 18:51:44 +08:00
iiuazz
e4b003d063 update test 2014-09-09 18:50:33 +08:00
life
f3993519ea update ignore 2014-09-09 18:43:31 +08:00
life
02d0411d53 bbs 2014-09-05 16:16:51 +08:00
life
77f4b0f383 bbs 2014-09-05 13:56:28 +08:00
life
af9b5652ed leanote_icon_blue.png 2014-09-05 10:58:38 +08:00
life
fbad32e273 v0.4 released 2014-09-04 17:49:17 +08:00
life
3ffb10b923 mongodb initial data 2014-09-04 16:15:57 +08:00
life
ba9b35c46e logo changed 2014-09-04 14:45:12 +08:00
life
acc67754c3 logo changed 2014-09-04 14:23:01 +08:00
life
1ed9f0c96d favicon changed 2014-09-04 14:15:38 +08:00
life
2db2a55e81 favicon changed 2014-09-04 13:50:12 +08:00
life
634b17c6f7 leanote logo font 2014-09-04 13:27:36 +08:00
life
468ad4dec7 leanote logo font 2014-09-04 12:53:04 +08:00
life
7cbd38e8bf mgo package changed to 'gopkg.in/mgo.v2/bson' 2014-09-02 15:45:44 +08:00
life
1d8be4c012 golang template 2014-08-28 11:53:04 +08:00
life
7b6a5c0929 leaui image bug fix 2014-07-15 20:59:43 +08:00
life
fe10dbbd77 leaui image fix bugs 2014-07-15 20:55:28 +08:00
life
7db9d95108 leaui image 2014-07-14 22:59:00 +08:00
life
91eb893814 m 2014-07-14 22:58:09 +08:00
life
fcc86de37c leaui image 2014-07-08 22:09:48 +08:00
life
61a0b9caa0 leaui image 2014-07-08 22:05:33 +08:00
life
95fe0c5289 leaui image 2014-07-08 21:45:43 +08:00
life
bb0350fdac update leaui_image 2014-06-28 23:12:34 +08:00
life
1494b25129 add leaui_image plugin for replace leanote_image. on a train to ChangSha 2014-06-28 23:07:34 +08:00
life
06eb27faab loading css 2014-06-28 12:24:29 +08:00
life
121eab5d38 thumb css 2014-06-28 12:18:15 +08:00
life
37da418bdd thumb css 2014-06-28 12:17:09 +08:00
life
eea6ad07f8 thumb css 2014-06-28 12:15:54 +08:00
life
b0402f29dd markdown 2014-06-24 21:41:02 +08:00
life
8e05e433de blog 2014-06-24 21:14:17 +08:00
life
a0acd31fcf tinyce table 2014-06-17 19:46:34 +08:00
life
a5b525affd update readme 2014-06-15 13:31:08 +08:00
life
414639dc3d fix notebook 2014-06-14 17:44:04 +08:00
life
cf4accc110 slim scroll 2014-06-14 17:29:29 +08:00
life
8d4a757ee9 update readme 2014-06-12 15:12:08 +08:00
life
68bacdc3bf update readme 2014-06-12 15:11:12 +08:00
life
3df6fa2bb7 update readme 2014-06-12 11:35:14 +08:00
life
e8c0001721 update readme 2014-06-12 11:34:27 +08:00
life
4210d56a0d update readme 2014-06-12 11:31:37 +08:00
life
63b65920db paste image on chrome 2014-06-08 18:03:23 +08:00
life
c55f364153 paste image on chrome 2014-06-08 17:57:19 +08:00
life
8af64c7b74 toggle writing mode bookmark 2014-06-05 19:04:39 +08:00
life
7bb05bd9e7 index 2014-05-27 13:55:18 +08:00
life
abf3297639 release 0.3 2014-05-27 13:33:05 +08:00
life
ad00f36fd9 release 0.3 2014-05-27 13:32:30 +08:00
life
c549957cdc release 0.3 2014-05-27 13:30:07 +08:00
life
7f9356c5a7 release 0.3 2014-05-27 13:29:19 +08:00
life
910b079c55 resize editor 2014-05-27 12:59:14 +08:00
life
b1b36cec23 update email 2014-05-27 11:37:03 +08:00
life
8eab8c7310 fix updateEmail 2014-05-27 11:21:35 +08:00
life
ddd0c13d34 editor fix 2014-05-21 10:08:47 +08:00
600 changed files with 54029 additions and 1486 deletions

3
.gitignore vendored
View File

@@ -15,3 +15,6 @@ app/routes/routes.go
app/tmp/main.go
.DS_Store
.settings
.project
public/config.codekit
files

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>leanote-public</name>
<name>leanote2</name>
<comment>leanote, your own cloud note!</comment>
<projects>
</projects>

20
LICENSE
View File

@@ -1,3 +1,23 @@
leanote - you own cloud note!
Copyright 2014 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
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.
life(life@leanote.com, lifephp@gmail.com)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991

165
README.md
View File

@@ -1,6 +1,8 @@
[中文](https://github.com/leanote/leanote#1-介绍)
## 1. Introduction
Leanote, your own cloud note.
Leanote, note just a notebook!
**Some Features**
@@ -20,10 +22,10 @@ To be honest, our inspiration comes from Evernote. We use Evernote to manage our
### 3.1. Download leanote
Leanote v0.1 has been released. Binaries:
Leanote V0.4 has been released. Binaries:
* Linux: [leanote-linux-v0.1.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.1/leanote-linux-v0.1.bin.tar.gz)
* MacOS X: [leanote-mac-v0.1.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.1/leanote-mac-v0.1.bin.tar.gz)
* Linux: [leanote-linux-v0.4.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz)
* MacOS X: [leanote-mac-v0.4.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz)
### 3.2. Install MongoDB
@@ -39,11 +41,12 @@ The mongodb data is in `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
```
The initial database contains two users:
The initial database contains three users:
```
user1 username: leanote, password: abc123
user2 username: admin, password: abc123
user3 username: demo@leanote.com, password: demo@leanote.com (this user is for demo)
```
### 3.4. Configuration
@@ -87,40 +90,130 @@ $> sudo sh run.sh
## 4. How to develop leanote
For more tips please see [our wiki](https://github.com/leanote/leanote/wiki/How-to-develop-leanote)
Please see [How-to-develop-leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-%E5%A6%82%E4%BD%95%E5%BC%80%E5%8F%91leanote)
Leanote is a app based on [revel](https://revel.github.io/), so if you want to work on leanote, you should be familar with revel.
### 4.1 Install golang
## 5. Contributors
Thank you to all the [contributors](https://github.com/leanote/leanote/graphs/contributors) on
this project. Your help is much appreciated.
Install golang and set the `GOPATH` environment variable
## 6. Contributing
### 4.2 Install revel
```
$> go get github.com/revel/revel
$> go get github.com/revel/cmd/revel
```
### 4.3 Get leanote
```
$> go get github.com/leanote/leanote/app
```
### 4.4 Build/Run leanote via revel
```
$> cp conf/app.conf-default conf/app.conf
$> cp conf/routes-default conf/routes
```
Now you can modify the leanote source and build/run using `revel`
```
$> revel run github.com/leanote/leanote
```
You are welcome to join us and contribute code to leanote! Thanks.
Please fork this repository and contribute back using [pull requests](https://github.com/leanote/leanote/pulls).
## Discussion
[leanote google group](https://groups.google.com/forum/#!forum/leanote)
* [leanote bbs](http://bbs.leanote.com)
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)
-----------------------------------------------------------------------
## 1. 介绍
Leanote, 不只是笔记!
**特性**
* 知识管理: 通过leanote来管理知识, leanote有易操作的界面, 包含两款编辑器tinymce和markdown. 在leanote, 你可以尽情享受写作.
* 分享: 你也可以通过分享知识给好友, 让好友拥有你的知识.
* 协作: 在分享的同时也可以与好友一起协作知识.
* 博客: leanote也可以作为你的博客, 将知识公开成博客, 让leanote把你的知识传播的更远!
## 2. 为什么我们要创建leanote?
说实话, 我们曾是evernote的忠实粉丝, 但是我们也发现evernote的不足:
* evernote的编辑器不能满足我们的需求, 不能贴代码(格式会乱掉, 作为程序员, 代码是我们的基本需求啊), 图片不能缩放.
* 我们是markdown的爱好者, 可是evernote竟然没有.
* 我们也想将知识公开, 所以我们有自己的博客, 如wordpress, 但为什么这两者不能合二为一呢?
* 还有...
## 3.安装leanote
leanote是一款私有云笔记, 你可以下载它安装在自己的服务器上, 当然也可以在 http://leanote.com 上注册.
这里详细整理了leanote二进版和leanote开发版的安装教程, 请移步至:
* [leanote二进制详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
* [leanote开发版详细安装教程](https://github.com/leanote/leanote/wiki/leanote%E5%BC%80%E5%8F%91%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
### 3.1. 下载leanote
Leanote V0.4 已发布, 二进制文件(暂时没有windows版的):
* Linux: [leanote-linux-v0.4.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.4/leanote-linux-v0.4.bin.tar.gz)
* MacOS X: [leanote-mac-v0.4.bin.tar.gz](https://github.com/leanote/leanote/releases/download/0.4/leanote-mac-v0.4.bin.tar.gz)
### 3.2. 安装 MongodbDB
Leanote是由golang(使用[revel](https://revel.github.io/)框架 和 [MongoDB](https://www.mongodb.org)数据库), 你需要先安装Mongodb.
安装MongodbDB, 导入数据更多细节请查看: [wiki](https://github.com/leanote/leanote/wiki/Install-Mongodb)
### 3.3. 导入初始数据
MongodbDB初始数据在 `[PATH_TO_LEANOTE]/mongodb_backup/leanote_install_data`
```
$> mongorestore -h localhost -d leanote --directoryperdb PATH_TO_LEANOTE/mongodb_backup/leanote_install_data
```
初始数据包含三个用户:
```
user1 username: leanote, password: abc123
user2 username: admin, password: abc123
user3 username: demo@leanote.com, password: demo@leanote.com (为体验使用)
```
### 3.4. 配置
修改 `[PATH_TO_LEANOTE]/conf/app.conf`. 有以下选项:
``mongodb`` **必须配置!**
```Shell
db.host=localhost
db.port=27017
db.dbname=leanote
db.username=
db.password=
```
``http.port``
默认为 80
``site.url``
默认是 `http://localhost`, 这会在上传图片后的图片路径中用户, 还有发邮件, 找回密码验证邮箱时用到.
``email``
找回密码和验证邮箱时使用
``adminUsername``
默认是 `admin`. 首页即为该用户的博客
更多配置请查看 `app/app.conf` 和 [revel 手册](https://revel.github.io/)
### 3.5. 运行leanote
```
$> cd PATH_TO_LEANOTE/bin
$> sudo sh run.sh
```
## 4. 如何对leanote进行二次开发
请查看 [How-to-develop-leanote](https://github.com/leanote/leanote/wiki/How-to-develop-leanote-%E5%A6%82%E4%BD%95%E5%BC%80%E5%8F%91leanote)
## 5. 贡献者
多谢 [贡献者](https://github.com/leanote/leanote/graphs/contributors) 的贡献, leanote因有你们而更完美!
## 6. 加入我们
欢迎提交[pull requests](https://github.com/leanote/leanote/pulls) 到leanote.
leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善leanote.
## 讨论
* [leanote 社区](http://bbs.leanote.com)
* QQ群158716820
* [leanote google group](https://groups.google.com/forum/#!forum/leanote)

View File

@@ -0,0 +1,47 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"github.com/leanote/leanote/app/info"
"gopkg.in/mgo.v2/bson"
// . "github.com/leanote/leanote/app/lea"
// "io/ioutil"
)
// Album controller
type Album struct {
BaseController
}
// 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})
}
// add album
func (c Album) AddAlbum(name string) revel.Result {
album := info.Album{
AlbumId: bson.NewObjectId(),
Name: name,
Seq: -1,
UserId: c.GetObjectUserId()}
re := albumService.AddAlbum(album)
if(re) {
return c.RenderJson(album)
} else {
return c.RenderJson(false)
}
}
// update alnum name
func (c Album) UpdateAlbum(albumId, name string) revel.Result {
return c.RenderJson(albumService.UpdateAlbum(albumId, c.GetUserId(), name))
}

View File

@@ -0,0 +1,210 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
"io/ioutil"
"os"
"strings"
"time"
"io"
"archive/tar"
"compress/gzip"
)
// 附件
type Attach struct {
BaseController
}
// 上传附件
func (c Attach) UploadAttach(noteId string) revel.Result {
re := c.uploadAttach(noteId)
return c.RenderJson(re)
}
func (c Attach) uploadAttach(noteId string) (re info.Re) {
var fileId = ""
var resultMsg = "error" // 错误信息
var Ok = false
var fileInfo info.Attach
re = info.NewRe()
defer func() {
re.Id = fileId // 只是id, 没有其它信息
re.Msg = resultMsg
re.Ok = Ok
re.Item = fileInfo
}()
// 判断是否有权限为笔记添加附件
if !shareService.HasUpdateNotePerm(noteId, c.GetUserId()) {
return re
}
file, handel, err := c.Request.FormFile("file")
if err != nil {
return re
}
defer file.Close()
data, err := ioutil.ReadAll(file)
if err != nil {
return re
}
// > 5M?
if(len(data) > 5 * 1024 * 1024) {
resultMsg = "File is bigger than 5M"
return re
}
// 生成上传路径
filePath := "files/" + c.GetUserId() + "/attachs"
dir := revel.BasePath + "/" + filePath
err = os.MkdirAll(dir, 0755)
if err != nil {
return re
}
// 生成新的文件名
filename := handel.Filename
_, ext := SplitFilename(filename) // .doc
filename = NewGuid() + ext
toPath := dir + "/" + filename;
err = ioutil.WriteFile(toPath, data, 0777)
if err != nil {
return re
}
// add File to db
fileType := ""
if ext != "" {
fileType = strings.ToLower(ext[1:])
}
filesize := GetFilesize(toPath)
fileInfo = info.Attach{Name: filename,
Title: handel.Filename,
NoteId: bson.ObjectIdHex(noteId),
UploadUserId: c.GetObjectUserId(),
Path: filePath + "/" + filename,
Type: fileType,
Size: filesize}
id := bson.NewObjectId();
fileInfo.AttachId = id
fileId = id.Hex()
Ok = attachService.AddAttach(fileInfo)
fileInfo.Path = ""; // 不要返回
resultMsg = "success"
return re
}
// 删除附件
func (c Attach) DeleteAttach(attachId string) revel.Result {
re := info.NewRe()
re.Ok, re.Msg = attachService.DeleteAttach(attachId, c.GetUserId())
return c.RenderJson(re)
}
// get all attachs by noteId
func (c Attach) GetAttachs(noteId string) revel.Result {
re := info.NewRe()
re.Ok = true
re.List = attachService.ListAttachs(noteId, c.GetUserId())
return c.RenderJson(re)
}
// 下载附件
// 权限判断
func (c Attach) Download(attachId string) revel.Result {
attach := attachService.GetAttach(attachId, c.GetUserId()); // 得到路径
path := attach.Path
if path == "" {
return c.RenderText("")
}
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
file, _ := os.Open(fn)
return c.RenderBinary(file, attach.Title, revel.Attachment, time.Now()) // revel.Attachment
// return c.RenderFile(file, revel.Attachment) // revel.Attachment
}
func (c Attach) DownloadAll(noteId string) revel.Result {
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
return c.RenderText("")
}
// 得到文件列表
attachs := attachService.ListAttachs(noteId, c.GetUserId())
if attachs == nil || len(attachs) == 0 {
return c.RenderText("")
}
/*
dir := revel.BasePath + "/files/tmp"
err := os.MkdirAll(dir, 0755)
if err != nil {
return c.RenderText("")
}
*/
filename := note.Title + ".tar.gz"
if note.Title == "" {
filename = "all.tar.gz"
}
// file write
fw, err := os.Create(revel.BasePath + "/files/" + filename)
if err != nil {
return c.RenderText("")
}
// defer fw.Close() // 不需要关闭, 还要读取给用户下载
// gzip write
gw := gzip.NewWriter(fw)
defer gw.Close()
// tar write
tw := tar.NewWriter(gw)
defer tw.Close()
// 遍历文件列表
for _, attach := range attachs {
fn := revel.BasePath + "/" + strings.TrimLeft(attach.Path, "/")
fr, err := os.Open(fn)
fileInfo, _ := fr.Stat()
if err != nil {
return c.RenderText("")
}
defer fr.Close()
// 信息头
h := new(tar.Header)
h.Name = attach.Title
h.Size = fileInfo.Size()
h.Mode = int64(fileInfo.Mode())
h.ModTime = fileInfo.ModTime()
// 写信息头
err = tw.WriteHeader(h)
if err != nil {
panic(err)
}
// 写文件
_, err = io.Copy(tw, fr)
if err != nil {
panic(err)
}
} // for
// tw.Close()
// gw.Close()
// fw.Close()
// file, _ := os.Open(dir + "/" + filename)
// fw.Seek(0, 0)
return c.RenderBinary(fw, filename, revel.Attachment, time.Now()) // revel.Attachment
}

View File

@@ -32,6 +32,7 @@ func (c Auth) DoLogin(email, pwd string) revel.Result {
c.SetSession(userInfo)
// 必须要redirect, 不然用户刷新会重复提交登录信息
// return c.Redirect("/")
configService.InitUserConfigs(userInfo.UserId.Hex())
return c.RenderJson(info.Re{Ok: true})
}
// return c.RenderTemplate("login.html")

View File

@@ -2,7 +2,7 @@ package controllers
import (
"github.com/revel/revel"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"encoding/json"
"github.com/leanote/leanote/app/info"
// "io/ioutil"
@@ -154,7 +154,7 @@ func (c BaseController) E404() revel.Result {
}
// 设置本地
func (c BaseController) SetLocale() {
func (c BaseController) SetLocale() string {
locale := string(c.Request.Locale) // zh-CN
lang := locale
if strings.Contains(locale, "-") {
@@ -165,6 +165,7 @@ func (c BaseController) SetLocale() {
lang = "en";
}
c.RenderArgs["locale"] = lang;
return lang
}
// 设置userInfo

View File

@@ -3,8 +3,8 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"labix.org/v2/mgo/bson"
// . "leanote/app/lea"
"gopkg.in/mgo.v2/bson"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
@@ -45,15 +45,14 @@ func (c Blog) SetNotebook2Blog(notebookId string, isBlog bool) revel.Result {
//-----------------------------
// 前台
// 默认是admin用户的博客
// 列表
// 这里还需要得到其它博客配置信息...
// 配置信息可以放在users表中, 或添加一个user_options表(用户配置表)
// 进入某个用户的博客
var blogPageSize = 5
var searchBlogPageSize = 30
func (c Blog) Index(userId string, notebookId string) revel.Result {
// 用户id为空, 转至博客平台
if userId == "" {
userId = leanoteUserId
userId = leanoteUserId;
}
// userId可能是 username, email
@@ -173,6 +172,8 @@ func (c Blog) Set() revel.Result {
c.getRecentBlogs(userId)
c.SetLocale();
return c.RenderTemplate("blog/set.html")
}

View File

@@ -3,10 +3,13 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
"io/ioutil"
"os"
"strconv"
"strings"
)
// 首页
@@ -14,44 +17,62 @@ type File struct {
BaseController
}
// 上传图片 editor
func (c File) UploadImage(renderHtml string) revel.Result {
if renderHtml == "" {
renderHtml = "file/image.html"
}
// 上传的是博客logo
// TODO logo不要设置权限, 另外的目录
func (c File) UploadBlogLogo() revel.Result {
re := c.uploadImage("logo", "");
re := c.uploadImage();
c.RenderArgs["fileUrlPath"] = siteUrl + re.Id
c.RenderArgs["fileUrlPath"] = siteUrl + "/" + re.Id
c.RenderArgs["resultCode"] = re.Code
c.RenderArgs["resultMsg"] = re.Msg
return c.RenderTemplate(renderHtml)
return c.RenderTemplate("file/blog_logo.html")
}
// 上传的是博客logo
func (c File) UploadBlogLogo() revel.Result {
return c.UploadImage("file/blog_logo.html");
// 拖拉上传, pasteImage
// noteId 是为了判断是否是协作的note, 如果是则需要复制一份到note owner中
func (c File) PasteImage(noteId string) revel.Result {
re := c.uploadImage("pasteImage", "");
userId := c.GetUserId()
note := noteService.GetNoteById(noteId)
if note.UserId != "" {
noteUserId := note.UserId.Hex()
if noteUserId != userId {
// 是否是有权限协作的
if shareService.HasUpdatePerm(noteUserId, userId, noteId) {
// 复制图片之, 图片复制给noteUserId
_, re.Id = fileService.CopyImage(userId, re.Id, noteUserId)
} else {
// 怎么可能在这个笔记下paste图片呢?
// 正常情况下不会
}
}
}
// 拖拉上传
func (c File) UploadImageJson(renderHtml string) revel.Result {
re := c.uploadImage();
re.Id = siteUrl + re.Id
// re.Id = re.Id
return c.RenderJson(re)
}
// leaui image plugin upload image
func (c File) UploadImageLeaui(albumId string) revel.Result {
re := c.uploadImage("", albumId);
return c.RenderJson(re)
}
// 上传图片, 公用方法
func (c File) uploadImage() (re info.Re) {
// upload image common func
func (c File) uploadImage(from, albumId string) (re info.Re) {
var fileUrlPath = ""
var fileId = ""
var resultCode = 0 // 1表示正常
var resultMsg = "内部错误" // 错误信息
var Ok = false
defer func() {
re.Id = fileUrlPath
re.Id = fileId // 只是id, 没有其它信息
re.Code = resultCode
re.Msg = resultMsg
re.Ok = Ok
}()
file, handel, err := c.Request.FormFile("file")
@@ -60,20 +81,30 @@ func (c File) uploadImage() (re info.Re) {
}
defer file.Close()
// 生成上传路径
fileUrlPath = "/upload/" + c.GetUserId() + "/images"
dir := revel.BasePath + "/public/" + fileUrlPath
if(from == "logo") {
fileUrlPath = "public/upload/" + c.GetUserId() + "/images/logo"
} else {
fileUrlPath = "files/" + c.GetUserId() + "/images"
}
dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755)
if err != nil {
Log(err)
return re
}
// 生成新的文件名
filename := handel.Filename
_, ext := SplitFilename(filename)
var ext string;
if from == "pasteImage" {
ext = ".png"; // TODO 可能不是png类型
} else {
_, ext = SplitFilename(filename)
if(ext != ".gif" && ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg") {
resultMsg = "不是图片"
return re
}
}
filename = NewGuid() + ext
data, err := ioutil.ReadAll(file)
if err != nil {
@@ -96,10 +127,140 @@ func (c File) uploadImage() (re info.Re) {
}
// 改变成gif图片
_, toPathGif := TransToGif(toPath, 0, true)
fileUrlPath += "/" + GetFilename(toPathGif)
filename = GetFilename(toPathGif)
filesize := GetFilesize(toPathGif)
fileUrlPath += "/" + filename
resultCode = 1
resultMsg = "上传成功!"
// File
fileInfo := info.File{Name: filename,
Title: handel.Filename,
Path: fileUrlPath,
Size: filesize}
id := bson.NewObjectId();
fileInfo.FileId = id
fileId = id.Hex()
if(from == "logo") {
fileId = "public/upload/" + c.GetUserId() + "/images/logo/" + filename
}
Ok = fileService.AddImage(fileInfo, albumId, c.GetUserId())
fileInfo.Path = ""; // 不要返回
re.Item = fileInfo
return re
}
// get all images by userId with page
func (c File) GetImages(albumId, key string, page int) revel.Result {
re := fileService.ListImagesWithPage(c.GetUserId(), albumId, key, page, 12)
return c.RenderJson(re)
}
func (c File) UpdateImageTitle(fileId, title string) revel.Result {
re := info.NewRe()
re.Ok = fileService.UpdateImageTitle(c.GetUserId(), fileId, title)
return c.RenderJson(re)
}
func (c File) DeleteImage(fileId string) revel.Result {
re := info.NewRe()
re.Ok, re.Msg = fileService.DeleteImage(c.GetUserId(), fileId)
return c.RenderJson(re)
}
// update image uploader to leaui image,
// scan all user's images and insert into db
func (c File) UpgradeLeauiImage() revel.Result {
re := info.NewRe()
if ok, _ := revel.Config.Bool("upgradeLeauiImage"); !ok {
re.Msg = "Not allowed"
return c.RenderJson(re)
}
uploadPath := revel.BasePath + "/public/upload";
userIds := ListDir(uploadPath)
if userIds == nil {
re.Msg = "no user"
return c.RenderJson(re)
}
msg := "";
for _, userId := range userIds {
dirPath := uploadPath + "/" + userId + "/images"
images := ListDir(dirPath)
if images == nil {
msg += userId + " no images "
continue;
}
hadImages := fileService.GetAllImageNamesMap(userId)
i := 0
for _, filename := range images {
if _, ok := hadImages[filename]; !ok {
fileUrlPath := "/upload/" + userId + "/images/" + filename
fileInfo := info.File{Name: filename,
Title: filename,
Path: fileUrlPath,
Size: GetFilesize(dirPath + "/" + filename)}
fileService.AddImage(fileInfo, "", userId)
i++
}
}
msg += userId + ": " + strconv.Itoa(len(images)) + " -- " + strconv.Itoa(i) + " images "
}
re.Msg = msg
return c.RenderJson(re)
}
//-----------
// 输出image
// 权限判断
func (c File) OutputImage(noteId, fileId string) revel.Result {
path := fileService.GetFile(c.GetUserId(), fileId); // 得到路径
if path == "" {
return c.RenderText("")
}
fn := revel.BasePath + "/" + strings.TrimLeft(path, "/")
file, _ := os.Open(fn)
return c.RenderFile(file, revel.Inline) // revel.Attachment
}
// 协作时复制图片到owner
func (c File) CopyImage(userId, fileId, toUserId string) revel.Result {
re := info.NewRe()
re.Ok, re.Id = fileService.CopyImage(userId, fileId, toUserId)
return c.RenderJson(re)
}
//------------
// 过时 已弃用!
func (c File) UploadImage(renderHtml string) revel.Result {
if renderHtml == "" {
renderHtml = "file/image.html"
}
re := c.uploadImage("", "");
c.RenderArgs["fileUrlPath"] = siteUrl + re.Id
c.RenderArgs["resultCode"] = re.Code
c.RenderArgs["resultMsg"] = re.Msg
return c.RenderTemplate(renderHtml)
}
// 已弃用
func (c File) UploadImageJson(from, noteId string) revel.Result {
re := c.uploadImage(from, "");
return c.RenderJson(re)
}

View File

@@ -17,9 +17,9 @@ func (c Index) Index() revel.Result {
c.SetUserInfo()
c.RenderArgs["title"] = "leanote"
c.RenderArgs["openRegister"] = openRegister
c.SetLocale()
lang := c.SetLocale()
return c.RenderTemplate("home/index.html");
return c.RenderTemplate("home/index_" + lang + ".html");
}
// 建议

View File

@@ -3,7 +3,7 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/lea/html2image"
"github.com/leanote/leanote/app/info"
@@ -117,7 +117,6 @@ type NoteOrContent struct {
// 这里不能用json, 要用post
func (c Note) UpdateNoteOrContent(noteOrContent NoteOrContent) revel.Result {
// 新添加note
LogJ(noteOrContent)
if noteOrContent.IsNew {
userId := c.GetObjectUserId();
myUserId := userId

View File

@@ -2,9 +2,9 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"encoding/json"
"github.com/leanote/leanote/app/info"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
// . "github.com/leanote/leanote/app/lea"
// "io/ioutil"
)
@@ -29,11 +29,14 @@ func (c Notebook) DeleteNotebook(notebookId string) revel.Result {
}
// 添加notebook
func (c Notebook) AddNotebook(notebookId, title string) revel.Result {
func (c Notebook) AddNotebook(notebookId, title, parentNotebookId string) revel.Result {
notebook := info.Notebook{NotebookId: bson.ObjectIdHex(notebookId),
Title: title,
Seq: -1,
UserId: c.GetObjectUserId()}
if(parentNotebookId != "") {
notebook.ParentNotebookId = bson.ObjectIdHex(parentNotebookId)
}
re := notebookService.AddNotebook(notebook)
if(re) {
@@ -46,7 +49,23 @@ func (c Notebook) AddNotebook(notebookId, title string) revel.Result {
func (c Notebook) UpdateNotebookTitle(notebookId, title string) revel.Result {
return c.RenderJson(notebookService.UpdateNotebookTitle(notebookId, c.GetUserId(), title))
}
// 排序
// 无用
func (c Notebook) SortNotebooks(notebookId2Seqs map[string]int) revel.Result {
return c.RenderJson(notebookService.SortNotebooks(c.GetUserId(), notebookId2Seqs))
}
// 调整notebooks, 可能是排序, 可能是移动到其它笔记本下
type DragNotebooksInfo struct {
CurNotebookId string
ParentNotebookId string
Siblings []string
}
// 传过来的data是JSON.stringfy数据
func (c Notebook) DragNotebooks(data string) revel.Result {
info := DragNotebooksInfo{}
json.Unmarshal([]byte(data), &info)
return c.RenderJson(notebookService.DragNotebooks(c.GetUserId(), info.CurNotebookId, info.ParentNotebookId, info.Siblings))
}

View File

@@ -3,7 +3,7 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
// "labix.org/v2/mgo/bson"
// "gopkg.in/mgo.v2/bson"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
// "github.com/leanote/leanote/app/types"

View File

@@ -3,7 +3,7 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
// "labix.org/v2/mgo/bson"
// "gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
// "github.com/leanote/leanote/app/types"
@@ -77,7 +77,8 @@ func (c User) SendRegisterEmail(content, toEmail string) revel.Result {
// 发送邮件
var userInfo = c.GetUserInfo();
url := "http://leanote.com/register?from=" + userInfo.Username
siteUrl, _ := revel.Config.String("site.url")
url := siteUrl + "/register?from=" + userInfo.Username
body := fmt.Sprintf("点击链接注册leanote: <a href='%v'>%v</a>. ", url, url);
body = content + "<br />" + body
re.Ok = SendEmail(toEmail, userInfo.Username + "邀请您注册leanote", "邀请注册", body)

View File

@@ -0,0 +1,51 @@
package admin
import (
// "github.com/revel/revel"
// "gopkg.in/mgo.v2/bson"
// "encoding/json"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/controllers"
// "io/ioutil"
// "fmt"
// "math"
// "strconv"
"strings"
)
// 公用Controller, 其它Controller继承它
type AdminBaseController struct {
controllers.BaseController // 不能用*BaseController
}
// 得到sorterField 和 isAsc
// okSorter = ['email', 'username']
func (c AdminBaseController) getSorter(sorterField string, isAsc bool, okSorter []string) (string, bool){
sorter := ""
c.Params.Bind(&sorter, "sorter")
if sorter == "" {
return sorterField, isAsc;
}
// sorter形式 email-up, email-down
s2 := strings.Split(sorter, "-")
if len(s2) != 2 {
return sorterField, isAsc;
}
// 必须是可用的sorter
if okSorter != nil && len(okSorter) > 0 {
if !InArray(okSorter, s2[0]) {
return sorterField, isAsc;
}
}
sorterField = strings.Title(s2[0])
if s2[1] == "up" {
isAsc = true
} else {
isAsc = false
}
c.RenderArgs["sorter"] = sorter
return sorterField, isAsc;
}

View File

@@ -0,0 +1,30 @@
package admin
import (
"github.com/revel/revel"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
)
// admin 首页
type AdminBlog struct {
AdminBaseController
}
// admin 主页
func (c AdminBlog) Index(sorter, keywords string) revel.Result {
pageNumber := c.GetPage()
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"title", "userId", "isRecommed", "createdTime"});
pageInfo, blogs := blogService.ListAllBlogs("", keywords, false, pageNumber, userPageSize, sorterField, isAsc);
c.RenderArgs["pageInfo"] = pageInfo
c.RenderArgs["blogs"] = blogs
c.RenderArgs["keywords"] = keywords
return c.RenderTemplate("admin/blog/list.html");
}
func (c AdminBlog) SetRecommend(noteId string, recommend bool) revel.Result {
re := info.NewRe()
re.Ok = blogService.SetRecommend(noteId, recommend);
return c.RenderJson(re)
}

View File

@@ -0,0 +1,25 @@
package admin
import (
"github.com/revel/revel"
)
// admin 首页
type Admin struct {
AdminBaseController
}
// admin 主页
func (c Admin) Index() revel.Result {
c.SetUserInfo()
c.RenderArgs["title"] = "leanote"
c.SetLocale()
return c.RenderTemplate("admin/index.html");
}
func (c Admin) GetView(view string) revel.Result {
return c.RenderTemplate("admin/" + view);
}

View File

@@ -0,0 +1,62 @@
package admin
import (
"github.com/revel/revel"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
"strings"
)
// admin 首页
type AdminSetting struct {
AdminBaseController
}
// email配置
func (c AdminSetting) Email() revel.Result {
return nil
}
// blog标签设置
func (c AdminSetting) Blog() revel.Result {
recommendTags := configService.GetGlobalArrayConfig("recommendTags")
newTags := configService.GetGlobalArrayConfig("newTags")
c.RenderArgs["recommendTags"] = strings.Join(recommendTags, ",")
c.RenderArgs["newTags"] = strings.Join(newTags, ",")
return c.RenderTemplate("admin/setting/blog.html");
}
func (c AdminSetting) DoBlogTag(recommendTags, newTags string) revel.Result {
re := info.NewRe()
re.Ok = configService.UpdateUserArrayConfig(c.GetUserId(), "recommendTags", strings.Split(recommendTags, ","))
re.Ok = configService.UpdateUserArrayConfig(c.GetUserId(), "newTags", strings.Split(newTags, ","))
return c.RenderJson(re)
}
// demo
// blog标签设置
func (c AdminSetting) Demo() revel.Result {
c.RenderArgs["demoUsername"] = configService.GetGlobalStringConfig("demoUsername")
c.RenderArgs["demoPassword"] = configService.GetGlobalStringConfig("demoPassword")
return c.RenderTemplate("admin/setting/demo.html");
}
func (c AdminSetting) DoDemo(demoUsername, demoPassword string) revel.Result {
re := info.NewRe()
userInfo := authService.Login(demoUsername, demoPassword)
if userInfo.UserId == "" {
re.Msg = "The User is Not Exists";
return c.RenderJson(re)
}
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoUserId", userInfo.UserId.Hex())
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoUsername", demoUsername)
re.Ok = configService.UpdateUserStringConfig(c.GetUserId(), "demoPassword", demoPassword)
return c.RenderJson(re)
}

View File

@@ -0,0 +1,28 @@
package admin
import (
"github.com/revel/revel"
// . "github.com/leanote/leanote/app/lea"
)
// admin 首页
type AdminUser struct {
AdminBaseController
}
// admin 主页
var userPageSize = 10
func (c AdminUser) Index(sorter, keywords string) revel.Result {
pageNumber := c.GetPage()
sorterField, isAsc := c.getSorter("CreatedTime", false, []string{"email", "username", "verified", "createdTime"});
pageInfo, users := userService.ListUsers(pageNumber, userPageSize, sorterField, isAsc, keywords);
c.RenderArgs["pageInfo"] = pageInfo
c.RenderArgs["users"] = users
c.RenderArgs["keywords"] = keywords
return c.RenderTemplate("admin/user/list.html");
}
func (c AdminUser) Add() revel.Result {
return c.RenderTemplate("admin/user/add.html");
}

View File

@@ -0,0 +1,127 @@
package admin
import (
"github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
// "strings"
)
var userService *service.UserService
var noteService *service.NoteService
var trashService *service.TrashService
var notebookService *service.NotebookService
var noteContentHistoryService *service.NoteContentHistoryService
var authService *service.AuthService
var shareService *service.ShareService
var blogService *service.BlogService
var tagService *service.TagService
var pwdService *service.PwdService
var tokenService *service.TokenService
var suggestionService *service.SuggestionService
var albumService *service.AlbumService
var noteImageService *service.NoteImageService
var fileService *service.FileService
var attachService *service.AttachService
var configService *service.ConfigService
var adminUsername = "admin"
// 拦截器
// 不需要拦截的url
// Index 除了Note之外都不需要
var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": true,
"Login": true,
"DoLogin": true,
"Logout": true,
"Register": true,
"DoRegister": true,
"FindPasswword": true,
"DoFindPassword": true,
"FindPassword2": true,
"FindPasswordUpdate": true,
"Suggestion": true,
},
"Blog": map[string]bool{"Index": true,
"View": true,
"AboutMe": true,
"SearchBlog": true,
},
// 用户的激活与修改邮箱都不需要登录, 通过链接地址
"User": map[string]bool{"UpdateEmail": true,
"ActiveEmail":true,
},
"Oauth": map[string]bool{"GithubCallback": true},
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
"Attach": map[string]bool{"Download": true, "DownloadAll": true},
}
func needValidate(controller, method string) bool {
// 在里面
if v, ok := commonUrl[controller]; ok {
// 在commonUrl里
if _, ok2 := v[method]; ok2 {
return false
}
return true
} else {
// controller不在这里的, 肯定要验证
return true;
}
}
func AuthInterceptor(c *revel.Controller) revel.Result {
// 全部变成首字大写
/*
var controller = strings.Title(c.Name)
var method = strings.Title(c.MethodName)
// 是否需要验证?
if !needValidate(controller, method) {
return nil
}
*/
// 验证是否已登录
// 必须是管理员
if username, ok := c.Session["Username"]; ok && username == adminUsername {
return nil // 已登录
}
// 没有登录, 判断是否是ajax操作
if c.Request.Header.Get("X-Requested-With") == "XMLHttpRequest" {
re := info.NewRe()
re.Msg = "NOTLOGIN"
return c.RenderJson(re)
}
return c.Redirect("/login")
}
// 最外层init.go调用
// 获取service, 单例
func InitService() {
notebookService = service.NotebookS
noteService = service.NoteS
noteContentHistoryService = service.NoteContentHistoryS
trashService = service.TrashS
shareService = service.ShareS
userService = service.UserS
tagService = service.TagS
blogService = service.BlogS
tokenService = service.TokenS
noteImageService = service.NoteImageS
fileService = service.FileS
albumService= service.AlbumS
attachService = service.AttachS
pwdService = service.PwdS
suggestionService = service.SuggestionS
authService = service.AuthS
configService = service.ConfigS
}
func init() {
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Admin{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminSetting{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &AdminUser{})
revel.OnAppStart(func() {
adminUsername, _ = revel.Config.String("adminUsername")
})
}

View File

@@ -0,0 +1,27 @@
package api
import (
"github.com/revel/revel"
// "encoding/json"
// "gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
// "github.com/leanote/leanote/app/info"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
// "math"
// "os"
// "path"
// "strconv"
)
type ApiUser struct {
*revel.Controller
}
// 修改用户名, 需要重置session
func (c ApiUser) Info() revel.Result {
Log("APIUser");
return c.RenderTemplate("home/index.html");
// return nil;
}

View File

@@ -3,18 +3,16 @@ package controllers
import (
"github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
"strings"
)
// 该文件初始化所有service方法
var userService *service.UserService
var noteService *service.NoteService
var trashService *service.TrashService
var notebookService *service.NotebookService
var noteContentHistoryService *service.NoteContentHistoryService
var authService *service.AuthService
var shareService *service.ShareService
var blogService *service.BlogService
@@ -22,6 +20,11 @@ var tagService *service.TagService
var pwdService *service.PwdService
var tokenService *service.TokenService
var suggestionService *service.SuggestionService
var albumService *service.AlbumService
var noteImageService *service.NoteImageService
var fileService *service.FileService
var attachService *service.AttachService
var configService *service.ConfigService
var pageSize = 1000
var defaultSortField = "UpdatedTime"
@@ -53,12 +56,14 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
"User": map[string]bool{"UpdateEmail": true,
"ActiveEmail":true,
},
"oauth": map[string]bool{"githubCallback": true},
"Oauth": map[string]bool{"GithubCallback": true},
"File": map[string]bool{"OutputImage": true, "OutputFile": true},
"Attach": map[string]bool{"Download": true, "DownloadAll": true},
}
func needValidate(controller, method string) bool {
// 在里面
if v, ok := commonUrl[controller]; ok {
// 在commonUrl里
// 在commonUrl里
if _, ok2 := v[method]; ok2 {
return false
}
@@ -73,13 +78,11 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
var controller = strings.Title(c.Name)
var method = strings.Title(c.MethodName)
// 是否需要验证?
if !needValidate(controller, method) {
return nil
}
// 验证是否已登录
if userId, ok := c.Session["UserId"]; ok && userId != "" {
return nil // 已登录
@@ -95,6 +98,28 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
return c.Redirect("/login")
}
// 最外层init.go调用
// 获取service, 单例
func InitService() {
notebookService = service.NotebookS
noteService = service.NoteS
noteContentHistoryService = service.NoteContentHistoryS
trashService = service.TrashS
shareService = service.ShareS
userService = service.UserS
tagService = service.TagS
blogService = service.BlogS
tokenService = service.TokenS
noteImageService = service.NoteImageS
fileService = service.FileS
albumService= service.AlbumS
attachService = service.AttachS
pwdService = service.PwdS
suggestionService = service.SuggestionS
authService = service.AuthS
configService = service.ConfigS
}
func init() {
// interceptor
// revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Index{}) // Index.Note自己校验
@@ -103,24 +128,10 @@ func init() {
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Share{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &User{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &File{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Attach{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &Blog{})
revel.InterceptFunc(AuthInterceptor, revel.BEFORE, &NoteContentHistory{})
// service
userService = &service.UserService{}
noteService = &service.NoteService{}
trashService = &service.TrashService{}
notebookService = &service.NotebookService{}
noteContentHistoryService = &service.NoteContentHistoryService{}
authService = &service.AuthService{}
shareService = &service.ShareService{}
blogService = &service.BlogService{}
tagService = &service.TagService{}
pwdService = &service.PwdService{}
tokenService = &service.TokenService{}
suggestionService = &service.SuggestionService{}
revel.OnAppStart(func() {
leanoteUserId, _ = revel.Config.String("adminUsername")
siteUrl, _ = revel.Config.String("site.url")

View File

@@ -2,8 +2,8 @@ package db
import (
"fmt"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"github.com/revel/revel"
)
@@ -33,6 +33,14 @@ var Tokens *mgo.Collection
var Suggestions *mgo.Collection
// Album & file(image)
var Albums *mgo.Collection
var Files *mgo.Collection
var Attachs *mgo.Collection
var NoteImages *mgo.Collection
var Configs *mgo.Collection
// 初始化时连接数据库
func Init() {
var url string
@@ -94,8 +102,17 @@ func Init() {
// find password
Tokens = Session.DB(dbname).C("tokens")
//
// Suggestion
Suggestions = Session.DB(dbname).C("suggestions")
// Album & file
Albums = Session.DB(dbname).C("albums")
Files = Session.DB(dbname).C("files")
Attachs = Session.DB(dbname).C("attachs")
NoteImages = Session.DB(dbname).C("note_images")
Configs = Session.DB(dbname).C("configs")
}
func init() {

15
app/info/AlbumInfo.go Normal file
View File

@@ -0,0 +1,15 @@
package info
import (
"gopkg.in/mgo.v2/bson"
"time"
)
type Album struct {
AlbumId bson.ObjectId `bson:"_id,omitempty"` //
UserId bson.ObjectId `bson:"UserId"`
Name string `Name` // album name
Type int `Type` // type, the default is image: 0
Seq int `Seq`
CreatedTime time.Time `CreatedTime`
}

21
app/info/AttachInfo.go Normal file
View File

@@ -0,0 +1,21 @@
package info
import (
"gopkg.in/mgo.v2/bson"
"time"
)
// Attach belongs to note
type Attach struct {
AttachId bson.ObjectId `bson:"_id,omitempty"` //
NoteId bson.ObjectId `bson:"NoteId"` //
UploadUserId bson.ObjectId `bson:"UploadUserId"` // 可以不是note owner, 协作者userId
Name string `Name` // file name, md5, such as 13232312.doc
Title string `Title` // raw file name
Size int64 `Size` // file size (byte)
Type string `Type` // file type, "doc" = word
Path string `Path` // the file path such as: files/userId/attachs/adfadf.doc
CreatedTime time.Time `CreatedTime`
// FromFileId bson.ObjectId `bson:"FromFileId,omitempty"` // copy from fileId, for collaboration
}

View File

@@ -1,7 +1,7 @@
package info
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
)
// 只为blog, 不为note
@@ -10,6 +10,7 @@ type BlogItem struct {
Note
Content string // 可能是content的一部分, 截取. 点击more后就是整个信息了
HasMore bool // 是否是否还有
User User // 用户信息
}
type UserBlogBase struct {

15
app/info/Configinfo.go Normal file
View File

@@ -0,0 +1,15 @@
package info
import (
"gopkg.in/mgo.v2/bson"
"time"
)
// 配置
// 用户配置高于全局配置
type Config struct {
UserId bson.ObjectId `bson:"_id"`
StringConfigs map[string]string `StringConfigs` // key => value
ArrayConfigs map[string][]string `ArrayConfigs` // key => []value
UpdatedTime time.Time `UpdatedTime`
}

21
app/info/FileInfo.go Normal file
View File

@@ -0,0 +1,21 @@
package info
import (
"gopkg.in/mgo.v2/bson"
"time"
)
type File struct {
FileId bson.ObjectId `bson:"_id,omitempty"` //
UserId bson.ObjectId `bson:"UserId"`
AlbumId bson.ObjectId `bson:"AlbumId"`
Name string `Name` // file name
Title string `Title` // file name or user defined for search
Size int64 `Size` // file size (byte)
Type string `Type` // file type, "" = image, "doc" = word
Path string `Path` // the file path
IsDefaultAlbum bool `IsDefaultAlbum`
CreatedTime time.Time `CreatedTime`
FromFileId bson.ObjectId `bson:"FromFileId,omitempty"` // copy from fileId, for collaboration
}

12
app/info/NoteImage.go Normal file
View File

@@ -0,0 +1,12 @@
package info
import (
"gopkg.in/mgo.v2/bson"
)
// 笔记内部图片
type NoteImage struct {
NoteImageId bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
NoteId bson.ObjectId `bson:"NoteId"` // 笔记
ImageId bson.ObjectId `bson:"ImageId"` // 图片fileId
}

View File

@@ -1,7 +1,7 @@
package info
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"time"
)
@@ -17,13 +17,17 @@ type Note struct {
ImgSrc string `ImgSrc` // 图片, 第一张缩略图地址
Tags []string `Tags,omitempty`
IsTrash bool `IsTrash` // 是否是trash, 默认是false
IsBlog bool `IsBlog,omitempty` // 是否设置成了blog 2013/12/29 新加
IsRecommend bool `IsRecommend,omitempty` // 是否为推荐博客 2014/9/24新加
IsTop bool `IsTop,omitempty` // blog是否置顶
IsMarkdown bool `IsMarkdown` // 是否是markdown笔记, 默认是false
AttachNum int `AttachNum` // 2014/9/21, attachments num
CreatedTime time.Time `CreatedTime`
UpdatedTime time.Time `UpdatedTime`
UpdatedUserId bson.ObjectId `bson:"UpdatedUserId"` // 如果共享了, 并可写, 那么可能是其它他修改了

View File

@@ -1,7 +1,7 @@
package info
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"time"
)
@@ -21,7 +21,7 @@ type Notebook struct {
}
// 仅仅是为了返回前台
type SubNotebooks []Notebooks
type SubNotebooks []*Notebooks // 存地址, 为了生成tree
type Notebooks struct {
Notebook
Subs SubNotebooks // 子notebook 在数据库中是没有的
@@ -32,7 +32,7 @@ func (this SubNotebooks) Len() int {
return len(this)
}
func (this SubNotebooks) Less(i, j int) bool {
return this[i].Seq < this[j].Seq
return (*this[i]).Seq < (*this[j]).Seq
}
func (this SubNotebooks) Swap(i, j int) {
this[i], this[j] = this[j], this[i]

View File

@@ -1,7 +1,7 @@
package info
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"time"
)

View File

@@ -1,7 +1,7 @@
package info
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
)
// 建议

View File

@@ -1,7 +1,7 @@
package info
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
)
// 这里主要是为了统计每个tag的note数目

View File

@@ -1,7 +1,7 @@
package info
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"time"
)

View File

@@ -1,7 +1,7 @@
package info
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"time"
)
@@ -20,6 +20,7 @@ type User struct {
Pwd string `bson:"Pwd" json:"-"`
CreatedTime time.Time `CreatedTime`
Logo string `Logo` // 9-24
// 主题
Theme string `Theme`

View File

@@ -1,6 +1,7 @@
package info
import (
"math"
)
@@ -8,5 +9,14 @@ import (
type Page struct {
CurPage int // 当前页码
TotalPage int // 总页
Count int // 总记录数
List interface{}
}
func NewPage(page, perPageSize, count int, list interface{}) Page {
totalPage := 0
if count > 0 {
totalPage = int(math.Ceil(float64(count) / float64(perPageSize)))
}
return Page{page, totalPage, count, list}
}

View File

@@ -3,11 +3,15 @@ package app
import (
"github.com/revel/revel"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/service"
"github.com/leanote/leanote/app/controllers"
"github.com/leanote/leanote/app/controllers/admin"
_ "github.com/leanote/leanote/app/lea/binder"
"reflect"
"fmt"
"html/template"
"math"
"strings"
"strconv"
"time"
)
@@ -16,7 +20,8 @@ func init() {
// Filters is the default set of global filters.
revel.Filters = []revel.Filter{
revel.PanicFilter, // Recover from panics and display an error page instead.
revel.RouterFilter, // Use the routing table to select the right Action
RouterFilter,
// revel.RouterFilter, // Use the routing table to select the right Action
// AuthFilter, // Invoke the action.
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params.
@@ -35,9 +40,13 @@ func init() {
revel.TemplateFuncs["raw"] = func(str string) template.HTML {
return template.HTML(str)
}
revel.TemplateFuncs["add"] = func(i int) template.HTML {
revel.TemplateFuncs["add"] = func(i int) string {
i = i + 1;
return template.HTML(fmt.Sprintf("%v", i))
return fmt.Sprintf("%v", i)
}
revel.TemplateFuncs["sub"] = func(i int) int {
i = i - 1;
return i
}
revel.TemplateFuncs["concat"] = func(s1, s2 string) template.HTML {
return template.HTML(s1 + s2)
@@ -75,6 +84,76 @@ func init() {
return template.HTML(tagStr)
}
revel.TemplateFuncs["li"] = func(a string) string {
Log(a)
Log("life==")
return ""
}
// str连接
revel.TemplateFuncs["urlConcat"] = func(url string, v... interface{}) string {
html := ""
for i := 0; i < len(v); i = i + 2 {
item := v[i]
if i+1 == len(v) {
break;
}
value := v[i+1]
if item != nil && value != nil {
keyStr, _ := item.(string)
valueStr, err := value.(string)
if !err {
valueInt, _ := value.(int)
valueStr = strconv.Itoa(valueInt)
}
if keyStr != "" && valueStr != "" {
s := keyStr + "=" + valueStr
if html != "" {
html += "&" + s
} else {
html += s
}
}
}
}
if html != "" {
if strings.Index(url, "?") >= 0 {
return url + "&" + html
} else {
return url + "?" + html
}
}
return url
}
revel.TemplateFuncs["urlCond"] = func(url string, sorterI, keyords interface{}) template.HTML {
return ""
}
// 为后台管理sorter th使用
// 必须要返回HTMLAttr, 返回html, golang 会执行安全检查返回ZgotmplZ
// sorterI 可能是nil, 所以用interfalce{}来接收
/*
data-url="/adminUser/index"
data-sorter="email"
class="th-sortable {{if eq .sorter "email-up"}}th-sort-up{{else}}{{if eq .sorter "email-down"}}th-sort-down{{end}}{{end}}"
*/
revel.TemplateFuncs["sorterTh"] = func(url, sorterField string, sorterI interface{}) template.HTMLAttr {
sorter := ""
if sorterI != nil {
sorter, _ = sorterI.(string)
}
html := "data-url=\"" + url + "\" data-sorter=\"" + sorterField + "\"";
html += " class=\"th-sortable ";
if sorter == sorterField + "-up" {
html += "th-sort-up\"";
} else if(sorter == sorterField + "-down") {
html += "th-sort-down";
}
html += "\"";
return template.HTMLAttr(html)
}
// pagination
revel.TemplateFuncs["page"] = func(userId, notebookId string, page, pageSize, count int) template.HTML {
if count == 0 {
@@ -111,9 +190,59 @@ func init() {
}
return template.HTML("<li class='" + preClass + "'><a href='" + preUrl + "'>Previous</a></li> <li class='" + nextClass + "'><a href='" + nextUrl + "'>Next</a></li>")
}
// life
// https://groups.google.com/forum/#!topic/golang-nuts/OEdSDgEC7js
// http://play.golang.org/p/snygrVpQva
// http://grokbase.com/t/gg/golang-nuts/142a6dhfh3/go-nuts-text-template-using-comparison-operators-eq-gt-etc-on-non-existent-variable-causes-the-template-to-stop-outputting-but-with-no-error-correct-behaviour
revel.TemplateFuncs["gt"] = func(a1, a2 interface{}) bool {
switch a1.(type) {
case string:
switch a2.(type) {
case string:
return reflect.ValueOf(a1).String() > reflect.ValueOf(a2).String()
}
case int, int8, int16, int32, int64:
switch a2.(type) {
case int, int8, int16, int32, int64:
return reflect.ValueOf(a1).Int() > reflect.ValueOf(a2).Int()
}
case uint, uint8, uint16, uint32, uint64:
switch a2.(type) {
case uint, uint8, uint16, uint32, uint64:
return reflect.ValueOf(a1).Uint() > reflect.ValueOf(a2).Uint()
}
case float32, float64:
switch a2.(type) {
case float32, float64:
return reflect.ValueOf(a1).Float() > reflect.ValueOf(a2).Float()
}
}
return false
}
/*
{{range $i := N 1 10}}
<div>{{$i}}</div>
{{end}}
*/
revel.TemplateFuncs["N"] = func(start, end int) (stream chan int) {
stream = make(chan int)
go func() {
for i := start; i <= end; i++ {
stream <- i
}
close(stream)
}()
return
}
// init Email
revel.OnAppStart(func() {
InitEmail()
service.InitService()
controllers.InitService()
admin.InitService()
service.ConfigS.InitGlobalConfigs()
})
}

View File

@@ -28,8 +28,8 @@ var bodyTpl = `
<div style="float:left; height: 40px;">
<a href="http://leanote.com" style="font-size: 24px">leanote</a>
</div>
<div style="float:left; height:40px; line-height:16px;">
&nbsp;&nbsp;| &nbsp;<span style="font-size:24px">$title</span>
<div style="float:left; height:40px; line-height:40px;">
&nbsp;&nbsp;| &nbsp;<span style="font-size:14px">$title</span>
</div>
<div style="clear:both"></div>
</div>
@@ -50,7 +50,7 @@ var bodyTpl = `
font-size: 12px;
}
</style>
<a href="http://leanote.com">leanote</a>, your own cloud note
<a href="http://leanote.com">leanote</a>, your own cloud note!
</div>
</div>
</body>
@@ -78,6 +78,7 @@ func SendEmail(to, subject, title, body string) bool {
err := smtp.SendMail(host+":"+port, auth, username, send_to, msg)
if err != nil {
Log(err)
return false
}
return true

View File

@@ -4,6 +4,7 @@ import (
"strings"
"path/filepath"
"os"
"io"
)
// 分离文件名与扩展名(包含.)
@@ -30,6 +31,16 @@ func GetFilename(path string) string {
return filepath.Base(path)
}
// file size
// length in bytes
func GetFilesize(path string) int64 {
fileinfo, err := os.Stat(path)
if err == nil {
return fileinfo.Size()
}
return 0;
}
// 清空dir下所有的文件和文件夹
// RemoveAll会清空本文件夹, 所以还要创建之
func ClearDir(dir string) bool {
@@ -43,3 +54,27 @@ func ClearDir(dir string) bool {
}
return true
}
// list dir's all file, return filenames
func ListDir(dir string) []string {
f, err := os.Open(dir)
if err != nil {
return nil
}
names, _ := f.Readdirnames(0)
return names
}
func CopyFile(srcName, dstName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}

60
app/lea/Route.go Normal file
View File

@@ -0,0 +1,60 @@
package lea
import (
"github.com/revel/revel"
"net/url"
"strings"
)
// overwite revel RouterFilter
// /api/user/Info => ApiUser.Info()
func RouterFilter(c *revel.Controller, fc []revel.Filter) {
// Figure out the Controller/Action
var route *revel.RouteMatch = revel.MainRouter.Route(c.Request.Request)
if route == nil {
c.Result = c.NotFound("No matching route found: " + c.Request.RequestURI)
return
}
// The route may want to explicitly return a 404.
if route.Action == "404" {
c.Result = c.NotFound("(intentionally)")
return
}
//----------
// life start
path := c.Request.Request.URL.Path
// Log(c.Request.Request.URL.Host)
if strings.HasPrefix(path, "/api") || strings.HasPrefix(path, "api") {
route.ControllerName = "Api" + route.ControllerName
}
// end
// Set the action.
if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
c.Result = c.NotFound(err.Error())
return
}
// Add the route and fixed params to the Request Params.
c.Params.Route = route.Params
// Add the fixed parameters mapped by name.
// TODO: Pre-calculate this mapping.
for i, value := range route.FixedParams {
if c.Params.Fixed == nil {
c.Params.Fixed = make(url.Values)
}
if i < len(c.MethodType.Args) {
arg := c.MethodType.Args[i]
c.Params.Fixed.Set(arg.Name, value)
} else {
revel.WARN.Println("Too many parameters to", route.Action, "trying to add", value)
break
}
}
fc[0](c, fc[1:])
}

View File

@@ -8,7 +8,7 @@ import (
"encoding/base64"
"encoding/hex"
"io"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"time"
math_rand "math/rand"
)
@@ -267,3 +267,15 @@ func RandomPwd(num int) string {
return str
}
func InArray(arr []string, str string) bool {
if arr == nil {
return false
}
for _, v := range arr {
if v == str {
return true
}
}
return false
}

View File

@@ -2,7 +2,7 @@ package session
import (
"github.com/robfig/revel"
"leanote/app/lea/memcache"
"github.com/leanote/leanote/app/lea/memcache"
// . "leanote/app/lea"
)

View File

@@ -33,7 +33,8 @@ var jss = []string{"js/jquery-cookie", "js/bootstrap",
"js/common", "js/app/note", "js/app/tag", "js/app/notebook", "js/app/share",
"js/object_id", "js/ZeroClipboard/ZeroClipboard"}
var base = "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/public/"
var base1 = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/"
var base = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/public/"
var cmdPath = "/usr/local/bin/uglifyjs"
func cmdError(err error) {
@@ -89,8 +90,8 @@ func dev() {
"/public/mdeditor/editor/scrollLink.js": "/public/mdeditor/editor/scrollLink-min.js",
"console.log(o);": "",
}
path := "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/src/views/note/note-dev.html"
target := "/Users/life/Documents/Go/package/src/github.com/leanote/leanote/src/views/note/note.html"
path := base1 + "/src/views/note/note-dev.html"
target := base1 + "/src/views/note/note.html"
bs, _ := ioutil.ReadFile(path)
content := string(bs)

View File

@@ -0,0 +1,44 @@
package service
import (
"github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson"
"time"
)
const IMAGE_TYPE = 0
type AlbumService struct {
}
// add album
func (this *AlbumService) AddAlbum(album info.Album) bool {
album.CreatedTime = time.Now()
album.Type = IMAGE_TYPE
return db.Insert(db.Albums, album)
}
// get albums
func (this *AlbumService) GetAlbums(userId string) []info.Album {
albums := []info.Album{}
db.ListByQ(db.Albums, bson.M{"UserId": bson.ObjectIdHex(userId)}, &albums)
return albums
}
// delete album
// presupposition: has no images under this ablum
func (this *AlbumService) DeleteAlbum(userId, albumId string) (bool, string) {
if db.Count(db.Files, bson.M{"AlbumId": bson.ObjectIdHex(albumId),
"UserId": bson.ObjectIdHex(userId),
}) == 0 {
return db.DeleteByIdAndUserId(db.Albums, albumId, userId), ""
}
return false, "has images"
}
// update album name
func (this *AlbumService) UpdateAlbum(albumId, userId, name string) bool {
return db.UpdateByIdAndUserIdField(db.Albums, albumId, userId, "Name", name)
}

View File

@@ -0,0 +1,175 @@
package service
import (
. "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson"
"time"
"os"
"strings"
)
type AttachService struct {
}
// add attach
func (this *AttachService) AddAttach(attach info.Attach) bool {
attach.CreatedTime = time.Now()
ok := db.Insert(db.Attachs, attach)
if ok {
// 更新笔记的attachs num
this.updateNoteAttachNum(attach.NoteId, 1)
}
return ok
}
// 更新笔记的附件个数
// addNum 1或-1
func (this *AttachService) updateNoteAttachNum(noteId bson.ObjectId, addNum int) bool {
num := db.Count(db.Attachs, bson.M{"NoteId": noteId})
/*
note := info.Note{}
note = noteService.GetNoteById(noteId.Hex())
note.AttachNum += addNum
if note.AttachNum < 0 {
note.AttachNum = 0
}
Log(note.AttachNum)
*/
return db.UpdateByQField(db.Notes, bson.M{"_id": noteId}, "AttachNum", num)
}
// list attachs
func (this *AttachService) ListAttachs(noteId, userId string) []info.Attach {
attachs := []info.Attach{}
// 判断是否有权限为笔记添加附件
if !shareService.HasUpdateNotePerm(noteId, userId) {
return attachs
}
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)
return attachs
}
func (this *AttachService) UpdateImageTitle(userId, fileId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
}
// Delete note to delete attas firstly
func (this *AttachService) DeleteAllAttachs(noteId, userId string) bool {
note := noteService.GetNoteById(noteId)
if note.UserId.Hex() == userId {
attachs := []info.Attach{}
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)
for _, attach := range attachs {
attach.Path = strings.TrimLeft(attach.Path, "/")
os.Remove(revel.BasePath + "/" + attach.Path)
}
return true
}
return false
}
// delete attach
func (this *AttachService) DeleteAttach(attachId, userId string) (bool, string) {
attach := info.Attach{}
db.Get(db.Attachs, attachId, &attach)
if(attach.AttachId != "") {
// 判断是否有权限为笔记添加附件
if !shareService.HasUpdateNotePerm(attach.NoteId.Hex(), userId) {
return false, "No Perm"
}
if db.Delete(db.Attachs, bson.M{"_id": bson.ObjectIdHex(attachId)}) {
this.updateNoteAttachNum(attach.NoteId, -1)
attach.Path = strings.TrimLeft(attach.Path, "/")
err := os.Remove(revel.BasePath + "/" + attach.Path)
if err == nil {
return true, "delete file error"
}
return false, "delete file error"
}
return false, "db error"
}
return false, "no such item"
}
// 获取文件路径
// 要判断是否具有权限
// userId是否具有attach的访问权限
func (this *AttachService) GetAttach(attachId, userId string) (attach info.Attach) {
if attachId == "" {
return
}
attach = info.Attach{}
db.Get(db.Attachs, attachId, &attach)
path := attach.Path
if path == "" {
return
}
note := noteService.GetNoteById(attach.NoteId.Hex())
// 判断权限
// 笔记是否是公开的
if note.IsBlog {
return
}
// 笔记是否是我的
if note.UserId.Hex() == userId {
return
}
// 我是否有权限查看或协作
if shareService.HasReadNotePerm(attach.NoteId.Hex(), userId) {
return
}
attach = info.Attach{}
return
}
// 复制笔记时需要复制附件
// noteService调用, 权限已判断
func (this *AttachService) CopyAttachs(noteId, toNoteId, toUserId string) bool {
attachs := []info.Attach{}
db.ListByQ(db.Attachs, bson.M{"NoteId": bson.ObjectIdHex(noteId)}, &attachs)
// 复制之
toNoteIdO := bson.ObjectIdHex(toNoteId)
for _, attach := range attachs {
attach.AttachId = ""
attach.NoteId = toNoteIdO
// 文件复制一份
_, ext := SplitFilename(attach.Name)
newFilename := NewGuid() + ext
dir := "files/" + toUserId + "/attachs"
filePath := dir + "/" + newFilename
err := os.MkdirAll(revel.BasePath + "/" + dir, 0755)
if err != nil {
return false
}
_, err = CopyFile(revel.BasePath + "/" + attach.Path, revel.BasePath + "/" + filePath)
if err != nil {
return false
}
attach.Name = newFilename
attach.Path = filePath
this.AddAttach(attach)
}
return true
}

View File

@@ -1,7 +1,7 @@
package service
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
// "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
"github.com/revel/revel"

View File

@@ -4,7 +4,7 @@ import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
// "time"
// "sort"
)
@@ -40,7 +40,7 @@ func (this *BlogService) GetBlog(noteId string) (blog info.BlogItem) {
noteContent := noteService.GetNoteContent(note.NoteId.Hex(), note.UserId.Hex())
// 组装成blogItem
blog = info.BlogItem{note, noteContent.Content, false}
blog = info.BlogItem{note, noteContent.Content, false, info.User{}}
return
}
@@ -84,7 +84,7 @@ func (this *BlogService) ListBlogs(userId, notebookId string, page, pageSize int
if noteContent, ok := noteContentsMap[note.NoteId]; ok {
content = noteContent.Abstract
}
blogs[i] = info.BlogItem{note, content, hasMore}
blogs[i] = info.BlogItem{note, content, hasMore, info.User{}}
}
return count, blogs
}
@@ -119,11 +119,91 @@ func (this *BlogService) SearchBlog(key, userId string, page, pageSize int, sort
if noteContent, ok := noteContentsMap[note.NoteId]; ok {
content = noteContent.Abstract
}
blogs[i] = info.BlogItem{note, content, hasMore}
blogs[i] = info.BlogItem{note, content, hasMore, info.User{}}
}
return count, blogs
}
//-------
// p
// 平台 lea+
// 博客列表
func (this *BlogService) ListAllBlogs(tag string, keywords string, isRecommend bool, page, pageSize int, sorterField string, isAsc bool) (info.Page, []info.BlogItem) {
pageInfo := info.Page{CurPage: page}
notes := []info.Note{}
skipNum, sortFieldR := parsePageAndSort(page, pageSize, sorterField, isAsc)
// 不是trash的
query := bson.M{"IsTrash": false, "IsBlog": true, "Title": bson.M{"$ne":"欢迎来到leanote!"}}
if tag != "" {
query["Tags"] = bson.M{"$in": []string{tag}}
}
// 不是demo的博客
demoUserId := configService.GetGlobalStringConfig("demoUserId")
if demoUserId != "" {
query["UserId"] = bson.M{"$ne": bson.ObjectIdHex(demoUserId)}
}
if isRecommend {
query["IsRecommend"] = isRecommend
}
if keywords != "" {
query["Title"] = bson.M{"$regex": bson.RegEx{".*?" + keywords + ".*", "i"}}
}
q := db.Notes.Find(query);
// 总记录数
count, _ := q.Count()
q.Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).
All(&notes)
if(notes == nil || len(notes) == 0) {
return pageInfo, nil
}
// 得到content, 并且每个都要substring
noteIds := make([]bson.ObjectId, len(notes))
userIds := make([]bson.ObjectId, len(notes))
for i, note := range notes {
noteIds[i] = note.NoteId
userIds[i] = note.UserId
}
// 可以不要的
// 直接得到noteContents表的abstract
// 这里可能是乱序的
/*
noteContents := noteService.ListNoteAbstractsByNoteIds(noteIds) // 返回[info.NoteContent]
noteContentsMap := make(map[bson.ObjectId]info.NoteContent, len(noteContents))
for _, noteContent := range noteContents {
noteContentsMap[noteContent.NoteId] = noteContent
}
*/
// 得到用户信息
userMap := userService.MapUserInfoAndBlogInfosByUserIds(userIds)
// 组装成blogItem
// 按照notes的顺序
blogs := make([]info.BlogItem, len(noteIds))
for i, note := range notes {
hasMore := true
var content string
/*
if noteContent, ok := noteContentsMap[note.NoteId]; ok {
content = noteContent.Abstract
}
*/
blogs[i] = info.BlogItem{note, content, hasMore, userMap[note.UserId]}
}
pageInfo = info.NewPage(page, pageSize, count, nil)
return pageInfo, blogs
}
//------------------------
// 博客设置
@@ -153,3 +233,11 @@ func (this *BlogService) UpdateUserBlogComment(userId string, userBlog info.User
func (this *BlogService) UpdateUserBlogStyle(userId string, userBlog info.UserBlogStyle) bool {
return db.UpdateByQMap(db.UserBlogs, bson.M{"_id": bson.ObjectIdHex(userId)}, userBlog)
}
//------------
// 后台管理
// 推荐博客
func (this *BlogService) SetRecommend(noteId string, isRecommend bool) bool {
return db.UpdateByQField(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId), "IsBlog": true}, "IsRecommend", isRecommend)
}

View File

@@ -0,0 +1,141 @@
package service
import (
"github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson"
"github.com/revel/revel"
"time"
)
// 配置服务
type ConfigService struct {
// 全局的
GlobalStringConfigs map[string]string
GlobalArrayConfigs map[string][]string
// 两种配置, 用户自己的
UserStringConfigs map[string]string
UserArrayConfigs map[string][]string
// 合并之后的
AllStringConfigs map[string]string
AllArrayConfigs map[string][]string
}
var adminUserId = ""
// appStart时 将全局的配置从数据库中得到作为全局
func (this *ConfigService) InitGlobalConfigs() bool {
this.GlobalStringConfigs = map[string]string{}
this.GlobalArrayConfigs = map[string][]string{}
this.UserStringConfigs = map[string]string{}
this.UserArrayConfigs = map[string][]string{}
this.AllStringConfigs = map[string]string{}
this.AllArrayConfigs = map[string][]string{}
adminUsername, _ := revel.Config.String("adminUsername")
if adminUsername == "" {
adminUsername = "admin"
}
userInfo := userService.GetUserInfoByAny(adminUsername)
if userInfo.UserId == "" {
return false
}
adminUserId = userInfo.UserId.Hex()
configs := info.Config{}
db.Get2(db.Configs, userInfo.UserId, &configs)
if configs.UserId == "" {
db.Insert(db.Configs, info.Config{UserId: userInfo.UserId, StringConfigs: map[string]string{}, ArrayConfigs: map[string][]string{}})
}
this.GlobalStringConfigs = configs.StringConfigs;
this.GlobalArrayConfigs = configs.ArrayConfigs;
// 复制到所有配置上
for key, value := range this.GlobalStringConfigs {
this.AllStringConfigs[key] = value
}
for key, value := range this.GlobalArrayConfigs {
this.AllArrayConfigs[key] = value
}
return true
}
// 用户登录后获取用户自定义的配置, 并将所有的配置都用上
func (this *ConfigService) InitUserConfigs(userId string) bool {
configs := info.Config{}
db.Get(db.Configs, userId, &configs)
if configs.UserId == "" {
db.Insert(db.Configs, info.Config{UserId: bson.ObjectIdHex(userId), StringConfigs: map[string]string{}, ArrayConfigs: map[string][]string{}})
}
this.UserStringConfigs = configs.StringConfigs;
this.UserArrayConfigs = configs.ArrayConfigs;
// 合并配置
for key, value := range this.UserStringConfigs {
this.AllStringConfigs[key] = value
}
for key, value := range this.UserArrayConfigs {
this.AllArrayConfigs[key] = value
}
return true
}
// 获取配置
func (this *ConfigService) GetStringConfig(key string) string {
return this.AllStringConfigs[key]
}
func (this *ConfigService) GetArrayConfig(key string) []string {
arr := this.AllArrayConfigs[key]
if arr == nil {
return []string{}
}
return arr
}
// 更新用户配置
func (this *ConfigService) UpdateUserStringConfig(userId, key string, value string) bool {
this.UserStringConfigs[key] = value
this.AllStringConfigs[key] = value
if userId == adminUserId {
this.GlobalStringConfigs[key] = value
}
// 保存到数据库中
return db.UpdateByQMap(db.Configs, bson.M{"_id": bson.ObjectIdHex(userId)},
bson.M{"StringConfigs": this.UserStringConfigs, "UpdatedTime": time.Now()})
}
func (this *ConfigService) UpdateUserArrayConfig(userId, key string, value []string) bool {
this.UserArrayConfigs[key] = value
this.AllArrayConfigs[key] = value
if userId == adminUserId {
this.GlobalArrayConfigs[key] = value
}
// 保存到数据库中
return db.UpdateByQMap(db.Configs, bson.M{"_id": bson.ObjectIdHex(userId)},
bson.M{"ArrayConfigs": this.UserArrayConfigs, "UpdatedTime": time.Now()})
}
// 获取全局配置, 博客平台使用
func (this *ConfigService) GetGlobalStringConfig(key string) string {
return this.GlobalStringConfigs[key]
}
func (this *ConfigService) GetGlobalArrayConfig(key string) []string {
arr := this.GlobalArrayConfigs[key]
if arr == nil {
return []string{}
}
return arr
}

236
app/service/FileService.go Normal file
View File

@@ -0,0 +1,236 @@
package service
import (
. "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson"
"time"
"os"
"strings"
)
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
type FileService struct {
}
// add Image
func (this *FileService) AddImage(image info.File, albumId, userId string) bool {
image.CreatedTime = time.Now()
if albumId != "" {
image.AlbumId = bson.ObjectIdHex(albumId)
} else {
image.AlbumId = bson.ObjectIdHex(DEFAULT_ALBUM_ID)
image.IsDefaultAlbum = true
}
image.UserId = bson.ObjectIdHex(userId)
return db.Insert(db.Files, image)
}
// list images
// if albumId == "" get default album images
func (this *FileService) ListImagesWithPage(userId, albumId, key string, pageNumber, pageSize int) info.Page {
skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, "CreatedTime", false)
files := []info.File{}
q := bson.M{"UserId": bson.ObjectIdHex(userId), "Type": ""} // life
if albumId != "" {
q["AlbumId"] = bson.ObjectIdHex(albumId);
} else {
q["IsDefaultAlbum"] = true
}
if key != "" {
q["Title"] = bson.M{"$regex": bson.RegEx{".*?" + key + ".*", "i"}}
}
// LogJ(q)
count := db.Count(db.Files, q);
db.Files.
Find(q).
Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).
All(&files)
return info.Page{Count: count, List: files}
}
func (this *FileService) UpdateImageTitle(userId, fileId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
}
// get all images names
// for upgrade
func (this *FileService) GetAllImageNamesMap(userId string) (m map[string]bool) {
q := bson.M{"UserId": bson.ObjectIdHex(userId)}
files := []info.File{}
db.ListByQWithFields(db.Files, q, []string{"Name"}, &files)
m = make(map[string]bool)
if len(files) == 0 {
return
}
for _, file := range files {
m[file.Name] = true
}
return
}
// delete image
func (this *FileService) DeleteImage(userId, fileId string) (bool, string) {
file := info.File{}
db.GetByIdAndUserId(db.Files, fileId, userId, &file)
if(file.FileId != "") {
if db.DeleteByIdAndUserId(db.Files, fileId, userId) {
// delete image
// TODO
file.Path = strings.TrimLeft(file.Path, "/")
var err error
if strings.HasPrefix(file.Path, "upload") {
Log(file.Path)
err = os.Remove(revel.BasePath + "/public/" + file.Path)
} else {
err = os.Remove(revel.BasePath + "/" + file.Path)
}
if err == nil {
return true, ""
}
return false, "delete file error!"
}
return false, "db error"
}
return false, "no such item"
}
// update image title
func (this *FileService) UpdateImage(userId, fileId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
}
// 获取文件路径
// 要判断是否具有权限
// userId是否具有fileId的访问权限
func (this *FileService) GetFile(userId, fileId string) string {
if fileId == "" {
return ""
}
file := info.File{}
db.Get(db.Files, fileId, &file)
path := file.Path
if path == "" {
return ""
}
// 1. 判断权限
// 是否是我的文件
if userId != "" && file.UserId.Hex() == userId {
return path
}
// 得到使用过该fileId的所有笔记NoteId
// 这些笔记是否有public的, 若有则ok
// 这些笔记(笔记本)是否有共享给我的, 若有则ok
noteIds := noteImageService.GetNoteIds(fileId)
if noteIds != nil && len(noteIds) > 0 {
// 这些笔记是否有public的
if db.Has(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}, "IsBlog": true}) {
return path
}
// 若有共享给我的笔记?
if db.Has(db.ShareNotes, bson.M{"ToUserId": bson.ObjectIdHex(userId), "NoteId": bson.M{"$in": noteIds}}) {
return path
}
// 笔记本是否共享给我?
// 通过笔记得到笔记本
notes := []info.Note{}
db.ListByQWithFields(db.Notes, bson.M{"_id": bson.M{"$in": noteIds}}, []string{"NotebookId"}, &notes)
if notes != nil && len(notes) > 0 {
notebookIds := make([]bson.ObjectId, len(notes))
for i := 0; i < len(notes); i++ {
notebookIds[i] = notes[i].NotebookId
}
if db.Has(db.ShareNotebooks, bson.M{"ToUserId": bson.ObjectIdHex(userId), "NotebookId": bson.M{"$in": notebookIds}}) {
return path
}
}
}
// 可能是刚复制到owner上, 但内容又没有保存, 所以没有note->imageId的映射, 此时看是否有fromFileId
if file.FromFileId != "" {
fromFile := info.File{}
db.Get2(db.Files, file.FromFileId, &fromFile)
if fromFile.UserId.Hex() == userId {
return fromFile.Path
}
}
return ""
}
// 复制图片
func (this *FileService) CopyImage(userId, fileId, toUserId string) (bool, string) {
// 是否已经复制过了
file2 := info.File{}
db.GetByQ(db.Files, bson.M{"UserId": bson.ObjectIdHex(toUserId), "FromFileId": bson.ObjectIdHex(fileId)}, &file2)
if file2.FileId != "" {
return true, file2.FileId.Hex();
}
// 复制之
file := info.File{}
db.GetByIdAndUserId(db.Files, fileId, userId, &file)
if file.FileId == "" || file.UserId.Hex() != userId {
return false, ""
}
_, ext := SplitFilename(file.Name)
newFilename := NewGuid() + ext
dir := "files/" + toUserId + "/images"
filePath := dir + "/" + newFilename
err := os.MkdirAll(dir, 0755)
if err != nil {
return false, ""
}
_, err = CopyFile(revel.BasePath + "/" + file.Path, revel.BasePath + "/" + filePath)
if err != nil {
Log(err)
return false, ""
}
fileInfo := info.File{Name: newFilename,
Title: file.Title,
Path: filePath,
Size: file.Size,
FromFileId: file.FileId}
id := bson.NewObjectId();
fileInfo.FileId = id
fileId = id.Hex()
Ok := this.AddImage(fileInfo, "", toUserId)
if Ok {
return Ok, id.Hex()
}
return false, ""
}
// 是否是我的文件
func (this *FileService) IsMyFile(userId, fileId string) bool {
return db.Has(db.Files, bson.M{"UserId": bson.ObjectIdHex(userId), "_id": bson.ObjectIdHex(fileId)})
}

View File

@@ -4,7 +4,7 @@ import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
// "time"
)

View File

@@ -0,0 +1,102 @@
package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
. "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson"
"regexp"
// "time"
)
type NoteImageService struct {
}
// 通过id, userId得到noteIds
func (this *NoteImageService) GetNoteIds(imageId string) ([]bson.ObjectId) {
noteImages := []info.NoteImage{}
db.ListByQWithFields(db.NoteImages, bson.M{"ImageId": bson.ObjectIdHex(imageId)}, []string{"NoteId"}, &noteImages)
if noteImages != nil && len(noteImages) > 0 {
noteIds := make([]bson.ObjectId, len(noteImages))
cnt := len(noteImages)
for i := 0; i < cnt; i++ {
noteIds[i] = noteImages[i].NoteId
}
return noteIds
}
return nil
}
// 解析内容中的图片, 建立图片与note的关系
// <img src="/file/outputImage?fileId=12323232" />
// 图片必须是我的, 不然不添加
func (this *NoteImageService) UpdateNoteImages(userId, noteId, content string) bool {
reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})")
find := reg.FindAllStringSubmatch(content, -1) // 查找所有的
// 删除旧的
db.DeleteAll(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(noteId)})
// 添加新的
var fileId string
noteImage := info.NoteImage{NoteId: bson.ObjectIdHex(noteId)}
hasAdded := make(map[string]bool)
if find != nil && len(find) > 0 {
for _, each := range find {
if each != nil && len(each) == 2 {
fileId = each[1]
// 之前没能添加过的
if _, ok := hasAdded[fileId]; !ok {
Log(fileId)
// 判断是否是我的文件
if fileService.IsMyFile(userId, fileId) {
noteImage.ImageId = bson.ObjectIdHex(fileId)
db.Insert(db.NoteImages, noteImage)
}
hasAdded[fileId] = true
}
}
}
}
return true
}
// 复制图片, 把note的图片都copy给我, 且修改noteContent图片路径
func (this *NoteImageService) CopyNoteImages(fromNoteId, fromUserId, newNoteId, content, toUserId string) string {
// 得到fromNoteId的noteImages, 如果为空, 则直接返回content
noteImages := []info.NoteImage{}
db.ListByQWithFields(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(fromNoteId)}, []string{"ImageId"}, &noteImages)
if len(noteImages) == 0 {
return content;
}
// <img src="/file/outputImage?fileId=12323232" />
// 把fileId=1232替换成新的
replaceMap := map[string]string{}
for _, noteImage := range noteImages {
imageId := noteImage.ImageId.Hex()
ok, newImageId := fileService.CopyImage(fromUserId, imageId, toUserId)
if ok {
replaceMap[imageId] = newImageId
}
}
if len(replaceMap) > 0 {
// 替换之
reg, _ := regexp.Compile("outputImage\\?fileId=([a-z0-9A-Z]{24})")
content = reg.ReplaceAllStringFunc(content, func(each string) string {
// each=outputImage?fileId=541bd2f599c37b4f3r000003
fileId := each[len(each)-24:] // 得到后24位, 也即id
if replaceFileId, ok := replaceMap[fileId]; ok {
return "outputImage?fileId=" + replaceFileId
}
return each
});
}
return content;
}

View File

@@ -4,7 +4,7 @@ import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
. "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"time"
)
@@ -17,6 +17,12 @@ func (this *NoteService) GetNote(noteId, userId string) (note info.Note) {
db.GetByIdAndUserId(db.Notes, noteId, userId, &note)
return
}
// fileService调用
func (this *NoteService) GetNoteById(noteId string) (note info.Note) {
note = info.Note{}
db.Get(db.Notes, noteId, &note)
return
}
// 得到blog, blogService用
// 不要传userId, 因为是公开的
func (this *NoteService) GetBlogNote(noteId string) (note info.Note) {
@@ -148,6 +154,10 @@ func (this *NoteService) AddNoteContent(noteContent info.NoteContent) info.NoteC
noteContent.UpdatedTime = noteContent.CreatedTime
noteContent.UpdatedUserId = noteContent.UserId
db.Insert(db.NoteContents, noteContent)
// 更新笔记图片
noteImageService.UpdateNoteImages(noteContent.UserId.Hex(), noteContent.NoteId.Hex(), noteContent.Content)
return noteContent;
}
@@ -237,11 +247,23 @@ func (this *NoteService) UpdateNoteContent(userId, updatedUserId, noteId, conten
Content: content,
UpdatedTime: time.Now(),
})
// 更新笔记图片
noteImageService.UpdateNoteImages(userId, noteId, content)
return true
}
return false
}
// ?????
// 这种方式太恶心, 改动很大
// 通过content修改笔记的imageIds列表
// src="http://localhost:9000/file/outputImage?fileId=541ae75499c37b6b79000005&noteId=541ae63c19807a4bb9000000"
func (this *NoteService) updateNoteImages(noteId string, content string) bool {
return true
}
// 更新tags
// [ok] [del]
func (this *NoteService) UpdateTags(noteId string, userId string, tags []string) bool {
@@ -316,9 +338,12 @@ func (this *NoteService) CopyNote(noteId, notebookId, userId string) info.Note {
}
// 复制别人的共享笔记给我
// TODO 判断是否共享了给我
// 将别人可用的图片转为我的图片, 复制图片
func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId string) info.Note {
if notebookService.IsMyNotebook(notebookId, myUserId) {
Log(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId))
// 判断是否共享了给我
if notebookService.IsMyNotebook(notebookId, myUserId) &&
(shareService.HasSharedNote(noteId, myUserId) || shareService.HasSharedNotebook(noteId, myUserId, fromUserId)) {
note := this.GetNote(noteId, fromUserId)
if note.NoteId == "" {
return info.Note{}
@@ -332,10 +357,18 @@ func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId
note.IsTop = false
note.IsBlog = false // 别人的可能是blog
note.ImgSrc = "" // 为什么清空, 因为图片需要复制, 先清空
// content
noteContent.NoteId = note.NoteId
noteContent.UserId = note.UserId
// 复制图片, 把note的图片都copy给我, 且修改noteContent图片路径
noteContent.Content = noteImageService.CopyNoteImages(noteId, fromUserId, note.NoteId.Hex(), noteContent.Content, myUserId)
// 复制附件
attachService.CopyAttachs(noteId, note.NoteId.Hex(), myUserId)
// 添加之
note = this.AddNoteAndContent(note, noteContent, note.UserId);
@@ -353,8 +386,10 @@ func (this *NoteService) CopySharedNote(noteId, notebookId, fromUserId, myUserId
// shareService call
// [ok]
func (this *NoteService) GetNotebookId(noteId string) bson.ObjectId {
note := &info.Note{}
db.Get(db.Notes, noteId, note)
note := info.Note{}
// db.Get(db.Notes, noteId, &note)
// LogJ(note)
db.GetByQWithFields(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, []string{"NotebookId"}, &note)
return note.NotebookId
}
@@ -396,7 +431,6 @@ func (this *NoteService) searchNoteFromContent(notes []info.Note, userId, key st
for i, note := range notes {
noteIds[i] = note.NoteId
}
LogJ(noteIds)
noteContents := []info.NoteContent{}
query := bson.M{"_id": bson.M{"$nin": noteIds}, "UserId": bson.ObjectIdHex(userId), "Content": bson.M{"$regex": bson.RegEx{".*?" + key + ".*", "i"}}}
if isBlog {
@@ -419,9 +453,6 @@ func (this *NoteService) searchNoteFromContent(notes []info.Note, userId, key st
noteIds2[i] = content.NoteId
}
// Log(" content search ")
// Log(lenContent)
// 得到notes
notes2 := this.ListNotesByNoteIds(noteIds2)
@@ -446,8 +477,6 @@ func (this *NoteService) SearchNoteByTags(tags []string, userId string, pageNumb
// 总记录数
count, _ = q.Count()
Log(count)
q.Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).

View File

@@ -2,7 +2,7 @@ package service
import (
// "fmt"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
// . "github.com/leanote/leanote/app/lea"
@@ -47,32 +47,41 @@ func ParseAndSortNotebooks(userNotebooks []info.Notebook, noParentDelete, needSo
newNotebooks.NumberNotes = each.NumberNotes
newNotebooks.IsTrash = each.IsTrash
newNotebooks.IsBlog = each.IsBlog
// 存地址
userNotebooksMap[each.NotebookId] = &newNotebooks
}
// 第二遍, 追加到父下
// nilObjectId := bson.ObjectIdHex("")
// 需要删除的id
needDeleteNotebookId := map[bson.ObjectId]bool{}
for id, each := range userNotebooksMap {
// 如果有父, 那么追加到父下, 并剪掉当前, 那么最后就只有根的元素
if each.ParentNotebookId.Hex() != "" {
if userNotebooksMap[each.ParentNotebookId] != nil {
userNotebooksMap[each.ParentNotebookId].Subs = append(userNotebooksMap[each.ParentNotebookId].Subs, *each)
userNotebooksMap[each.ParentNotebookId].Subs = append(userNotebooksMap[each.ParentNotebookId].Subs, each) // Subs是存地址
// 并剪掉
delete(userNotebooksMap, id)
// bug
needDeleteNotebookId[id] = true
// delete(userNotebooksMap, id)
} else if noParentDelete {
// 没有父, 且设置了要删除
delete(userNotebooksMap, id)
needDeleteNotebookId[id] = true
// delete(userNotebooksMap, id)
}
}
}
// 第三遍, 得到所有根
final := make(info.SubNotebooks, len(userNotebooksMap))
final := make(info.SubNotebooks, len(userNotebooksMap)-len(needDeleteNotebookId))
i := 0
for _, each := range userNotebooksMap {
final[i] = *each
for id, each := range userNotebooksMap {
if !needDeleteNotebookId[id] {
final[i] = each
i++
}
}
// 最后排序
if needSort {
@@ -182,14 +191,20 @@ func (this *NotebookService) UpdateNotebook(userId, notebookId string, needUpdat
return db.UpdateByIdAndUserIdMap(db.Notebooks, notebookId, userId, needUpdate)
}
// 查看是否有子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.Notes, bson.M{"NotebookId": bson.ObjectIdHex(notebookId),
"UserId": bson.ObjectIdHex(userId),
"IsTrash": false}) == 0 { // 不包含trash
return db.DeleteByIdAndUserId(db.Notebooks, notebookId, userId), ""
}
return false, "笔记本下有笔记"
} else {
return false, "笔记本下有子笔记本"
}
}
// 排序
@@ -209,3 +224,28 @@ func (this *NotebookService) SortNotebooks(userId string, notebookId2Seqs map[st
return true
}
func (this *NotebookService) DragNotebooks(userId string, curNotebookId string, parentNotebookId string, siblings []string) bool {
userIdO := bson.ObjectIdHex(userId)
ok := false
// 如果没parentNotebookId, 则parentNotebookId设空
if(parentNotebookId == "") {
ok = db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(curNotebookId), userIdO, "ParentNotebookId", "");
} else {
ok = db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(curNotebookId), userIdO, "ParentNotebookId", bson.ObjectIdHex(parentNotebookId));
}
if !ok {
return false
}
// 排序
for seq, notebookId := range siblings {
if !db.UpdateByIdAndUserIdField2(db.Notebooks, bson.ObjectIdHex(notebookId), userIdO, "Seq", seq) {
return false
}
}
return true
}

View File

@@ -1,7 +1,8 @@
package service
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"github.com/revel/revel"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
@@ -31,7 +32,8 @@ func (this *PwdService) FindPwd(email string) (ok bool, msg string) {
}
// 发送邮件
url := "http://leanote.com/findPassword/" + token
siteUrl, _ := revel.Config.String("site.url")
url := siteUrl + "/findPassword/" + token
body := fmt.Sprintf("请点击链接修改密码: <a href='%v'>%v</a>. %v小时后过期.", url, url, int(overHours));
if !SendEmail(email, "leanote-找回密码", "找回密码", body) {
return false, "邮箱发送失败"

View File

@@ -3,8 +3,8 @@ package service
import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
. "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson"
"time"
"sort"
)
@@ -286,6 +286,28 @@ func (this *ShareService) AddShareNote(noteId string, perm int, userId, email st
return db.Insert(db.ShareNotes, shareNote), "", toUserId
}
// updatedUserId是否有查看userId noteId的权限?
func (this *ShareService) HasReadPerm(userId, updatedUserId, noteId string) bool {
if !db.Has(db.ShareNotes,
bson.M{"UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(updatedUserId), "NoteId": bson.ObjectIdHex(noteId)}) {
// noteId的notebookId是否被共享了?
notebookId := noteService.GetNotebookId(noteId)
if notebookId.Hex() == "" {
return false
}
// 判断notebook是否被共享
if !db.Has(db.ShareNotebooks,
bson.M{"UserId": bson.ObjectIdHex(userId), "ToUserId": bson.ObjectIdHex(updatedUserId), "NotebookId": notebookId}) {
return false
} else {
return true
}
} else {
return true
}
}
// updatedUserId是否有修改userId noteId的权限?
func (this *ShareService) HasUpdatePerm(userId, updatedUserId, noteId string) bool {
// 1. noteId是否被共享了?
@@ -334,14 +356,14 @@ func (this *ShareService) AddHasShareNote(userId, toUserId string) bool {
}
// userId是否被共享了noteId
func (this *ShareService) hasSharedNote(noteId, myUserId string) bool {
func (this *ShareService) HasSharedNote(noteId, myUserId string) bool {
return db.Has(db.ShareNotes, bson.M{"ToUserId": bson.ObjectIdHex(myUserId), "NoteId": bson.ObjectIdHex(noteId)})
}
// noteId的notebook是否共享了给我
func (this *ShareService) hasSharedNotebook(noteId, myUserId, sharedUserId string) bool {
note := noteService.GetNote(noteId, sharedUserId)
if note.NoteId != "" {
return db.Has(db.ShareNotebooks, bson.M{"NotebookId": note.NotebookId,
func (this *ShareService) HasSharedNotebook(noteId, myUserId, sharedUserId string) bool {
notebookId := noteService.GetNotebookId(noteId)
if notebookId != "" {
return db.Has(db.ShareNotebooks, bson.M{"NotebookId": notebookId,
"UserId": bson.ObjectIdHex(sharedUserId),
"ToUserId": bson.ObjectIdHex(myUserId),
})
@@ -355,7 +377,7 @@ func (this *ShareService) GetShareNoteContent(noteId, myUserId, sharedUserId str
noteContent = info.NoteContent{}
// 是否单独共享了该notebook
// 或者, 其notebook共享了我
if this.hasSharedNote(noteId, myUserId) || this.hasSharedNotebook(noteId, myUserId, sharedUserId) {
if this.HasSharedNote(noteId, myUserId) || this.HasSharedNotebook(noteId, myUserId, sharedUserId) {
db.Get(db.NoteContents, noteId, &noteContent)
} else {
}
@@ -508,3 +530,50 @@ func (this *ShareService) DeleteUserShareNoteAndNotebook(userId, toUserId string
return true
}
// 用户userId是否有修改noteId的权限
func (this *ShareService) HasUpdateNotePerm(noteId, userId string) bool {
if noteId == "" || userId == "" {
return false;
}
note := noteService.GetNoteById(noteId)
LogJ(note);
if note.UserId != "" {
noteUserId := note.UserId.Hex()
if noteUserId != userId {
// 是否是有权限协作的
if this.HasUpdatePerm(noteUserId, userId, noteId) {
return true
} else {
return false;
}
} else {
return true
}
} else {
return false;
}
}
// 用户userId是否有修改noteId的权限
func (this *ShareService) HasReadNotePerm(noteId, userId string) bool {
if noteId == "" || userId == "" {
return false;
}
note := noteService.GetNoteById(noteId)
if note.UserId != "" {
noteUserId := note.UserId.Hex()
if noteUserId != userId {
// 是否是有权限协作的
if this.HasReadPerm(noteUserId, userId, noteId) {
return true
} else {
return false;
}
} else {
return true
}
} else {
return false;
}
}

View File

@@ -4,7 +4,7 @@ import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
// . "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
// "time"
// "sort"
)

View File

@@ -4,7 +4,7 @@ import (
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
. "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
// "time"
)

View File

@@ -1,7 +1,7 @@
package service
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"

View File

@@ -1,7 +1,7 @@
package service
import (
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
)
@@ -53,7 +53,15 @@ func (this *TrashService) recoverNote(noteId, notebookId, userId string) bool {
// 删除trash
func (this *TrashService) DeleteTrash(noteId, userId string) bool {
return db.DeleteByIdAndUserId(db.Notes, noteId, userId)
// delete note's attachs
ok := attachService.DeleteAllAttachs(noteId, userId)
// delete note
ok = db.DeleteByIdAndUserId(db.Notes, noteId, userId)
// delete content
ok = db.DeleteByIdAndUserId(db.NoteContents, noteId, userId)
return ok
}
// 列出note, 排序规则, 还有分页

View File

@@ -1,10 +1,11 @@
package service
import (
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
. "github.com/leanote/leanote/app/lea"
"labix.org/v2/mgo/bson"
"gopkg.in/mgo.v2/bson"
"time"
"strings"
"fmt"
@@ -64,6 +65,7 @@ func (this *UserService) GetUserInfoByAny(idEmailUsername string) info.User {
return this.GetUserInfoByEmail(idEmailUsername)
}
// username
return this.GetUserInfoByUsername(idEmailUsername)
}
@@ -82,6 +84,7 @@ func (this *UserService) GetUserInfoByEmail(email string) info.User {
// 得到用户信息 username
func (this *UserService) GetUserInfoByUsername(username string) info.User {
user := info.User{}
username = strings.ToLower(username)
db.GetByQ(db.Users, bson.M{"Username": username}, &user)
return user
}
@@ -96,6 +99,29 @@ func (this *UserService) ListUserInfosByUserIds(userIds []bson.ObjectId) []info.
db.ListByQ(db.Users, bson.M{"_id": bson.M{"$in": userIds}}, &users)
return users
}
// 用户信息和博客设置信息
func (this *UserService) MapUserInfoAndBlogInfosByUserIds(userIds []bson.ObjectId) map[bson.ObjectId]info.User {
users := []info.User{}
db.ListByQ(db.Users, bson.M{"_id": bson.M{"$in": userIds}}, &users)
userBlogs := []info.UserBlog{}
db.ListByQWithFields(db.UserBlogs, bson.M{"_id": bson.M{"$in": userIds}}, []string{"Logo"}, &userBlogs)
userBlogMap := make(map[bson.ObjectId]info.UserBlog, len(userBlogs))
for _, user := range userBlogs {
userBlogMap[user.UserId] = user
}
userMap := make(map[bson.ObjectId]info.User, len(users))
for _, user := range users {
if userBlog, ok := userBlogMap[user.UserId]; ok {
user.Logo = userBlog.Logo
}
userMap[user.UserId] = user
}
return userMap
}
// 通过ids得到users, 按id的顺序组织users
func (this *UserService) GetUserInfosOrderBySeq(userIds []bson.ObjectId) []info.User {
@@ -132,7 +158,7 @@ func (this *UserService) LoginGetUserInfo(emailOrUsername, md5Pwd string) info.U
// 更新username
func (this *UserService) UpdateUsername(userId, username string) (bool, string) {
if userId == "" || username == "" {
if userId == "" || username == "" || username == "admin" { // admin用户是内置的, 不能设置
return false, "用户已存在"
}
usernameRaw := username // 原先的, 可能是同一个, 但有大小写
@@ -180,7 +206,8 @@ func (this *UserService) RegisterSendActiveEmail(userId string, email string) bo
}
// 发送邮件
url := "http://leanote.com/user/activeEmail?token=" + token
siteUrl, _ := revel.Config.String("site.url")
url := siteUrl + "/user/activeEmail?token=" + token
body := fmt.Sprintf("请点击链接验证邮箱: <a href='%v'>%v</a>. %v小时后过期.", url, url, tokenService.GetOverHours(info.TokenActiveEmail));
if !SendEmail(email, "leanote-验证邮箱", "验证邮箱", body) {
return false
@@ -208,11 +235,12 @@ func (this *UserService) UpdateEmailSendActiveEmail(userId, email string) (ok bo
}
// 发送邮件
url := "http://115.28.133.226/user/updateEmail?token=" + token
siteUrl, _ := revel.Config.String("site.url")
url := siteUrl + "/user/updateEmail?token=" + token
body := "邮箱验证后您的登录邮箱为: <b>" + email + "</b><br />";
body += fmt.Sprintf("请点击链接验证邮箱: <a href='%v'>%v</a>. %v小时后过期.", url, url, tokenService.GetOverHours(info.TokenUpdateEmail));
if !SendEmail(email, "leanote-验证邮箱", "验证邮箱", body) {
msg = "发送失败"
msg = "发送失败, 该邮箱存在?"
return
}
ok = true
@@ -292,3 +320,24 @@ func (this *UserService)UpdateColumnWidth(userId string, notebookWidth, noteList
func (this *UserService)UpdateLeftIsMin(userId string, leftIsMin bool) bool {
return db.UpdateByQMap(db.Users, bson.M{"_id": bson.ObjectIdHex(userId)}, bson.M{"LeftIsMin": leftIsMin})
}
//-------------
// user admin
func (this *UserService) ListUsers(pageNumber, pageSize int, sortField string, isAsc bool, email string) (page info.Page, users []info.User) {
users = []info.User{}
skipNum, sortFieldR := parsePageAndSort(pageNumber, pageSize, sortField, isAsc)
query := bson.M{}
if email != "" {
query["Email"] = bson.M{"$regex": bson.RegEx{".*?" + email + ".*", "i"}}
}
q := db.Users.Find(query);
// 总记录数
count, _ := q.Count()
// 列表
q.Sort(sortFieldR).
Skip(skipNum).
Limit(pageSize).
All(&users)
page = info.NewPage(pageNumber, pageSize, count, nil)
return
}

View File

@@ -8,24 +8,56 @@ import (
// 初始化, 实例service
// 为了共享service
var notebookService *NotebookService
var noteService *NoteService
var noteContentHistoryService *NoteContentHistoryService
var trashService *TrashService
var shareService *ShareService
var userService *UserService
var tagService *TagService
var blogService *BlogService
var tokenService *TokenService
var notebookService, NotebookS *NotebookService
var noteService, NoteS *NoteService
var noteContentHistoryService, NoteContentHistoryS *NoteContentHistoryService
var trashService, TrashS *TrashService
var shareService, ShareS *ShareService
var userService, UserS *UserService
var tagService, TagS *TagService
var blogService, BlogS *BlogService
var tokenService, TokenS *TokenService
var noteImageService, NoteImageS *NoteImageService
var fileService, FileS *FileService
var albumService, AlbumS *AlbumService
var attachService, AttachS *AttachService
var configService, ConfigS *ConfigService
var PwdS *PwdService
var SuggestionS *SuggestionService
var AuthS *AuthService
func init() {
notebookService = &NotebookService{}
noteService = &NoteService{}
noteContentHistoryService = &NoteContentHistoryService{}
trashService = &TrashService{}
shareService = &ShareService{}
userService = &UserService{}
tagService = &TagService{}
blogService = &BlogService{}
tokenService = &TokenService{}
// onAppStart调用
func InitService() {
NotebookS = &NotebookService{}
NoteS = &NoteService{}
NoteContentHistoryS = &NoteContentHistoryService{}
TrashS = &TrashService{}
ShareS = &ShareService{}
UserS = &UserService{}
TagS = &TagService{}
BlogS = &BlogService{}
TokenS = &TokenService{}
NoteImageS = &NoteImageService{}
FileS = &FileService{}
AlbumS = &AlbumService{}
AttachS = &AttachService{}
ConfigS = &ConfigService{}
PwdS = &PwdService{}
SuggestionS = &SuggestionService{}
AuthS = &AuthService{}
notebookService = NotebookS
noteService = NoteS
noteContentHistoryService = NoteContentHistoryS
trashService = TrashS
shareService = ShareS
userService = UserS
tagService = TagS
blogService = BlogS
tokenService = TokenS
noteImageService = NoteImageS
fileService = FileS
albumService = AlbumS
attachService = AttachS
configService = ConfigS
}

View File

@@ -11,8 +11,8 @@ import (
"github.com/leanote/leanote/app/lea/html2image"
"time"
"fmt"
"labix.org/v2/mgo/bson"
// "labix.org/v2/mgo"
"gopkg.in/mgo.v2/bson"
// "gopkg.in/mgo.v2"
// "encoding/json"
// "strings"
)
@@ -181,8 +181,15 @@ make<br>make --- install</pre>
`, "/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)
@@ -199,7 +206,7 @@ func main() {
//_, err := mgo.Dial("mongodb://leanote:nKFAkxKnWkEQy8Vv2LlM@115.28.133.226:27017/leanote")
// testNotebookService();
testNoteService();
// testNoteService();
// testShareService()
// testAuthService()

View File

@@ -0,0 +1,175 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Blog</h3></div>
<section class="panel panel-default">
<div class="row wrapper">
<div class="col-sm-5 m-b-xs">
<select class="input-sm form-control input-s-sm inline v-middle">
<option value="0">
Bulk action
</option>
<option value="1">
Delete selected
</option>
<option value="2">
Bulk edit
</option>
<option value="3">
Export
</option>
</select>
<button class="btn btn-sm btn-default">
Apply
</button>
</div>
<div class="col-sm-4 m-b-xs">
</div>
<div class="col-sm-3">
<div class="input-group search-group">
<input type="text" class="input-sm form-control" placeholder="Title" id="keywords" value="{{.keywords}}" />
<span class="input-group-btn">
<button class="btn btn-sm btn-default" type="button" data-url="/adminBlog/index">Search</button>
</span>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped b-t b-light">
<thead>
<tr>
<th width="20">
<input type="checkbox">
</th>
{{$url := urlConcat "/adminBlog/index" "keywords" .keywords}}
<th
{{sorterTh $url "title" .sorter}}
>
Title
<span class="th-sort">
<i class="fa fa-sort-down"></i>
<i class="fa fa-sort-up"></i>
<i class="fa fa-sort"></i>
</span>
</th>
<th
{{sorterTh $url "userId" .sorter}}
>
Username
<span class="th-sort">
<i class="fa fa-sort-down"></i>
<i class="fa fa-sort-up"></i>
<i class="fa fa-sort"></i>
</span>
</th>
<th
{{sorterTh $url "isRecommend" .sorter}}
>
isRecommend
<span class="th-sort">
<i class="fa fa-sort-down"></i>
<i class="fa fa-sort-up"></i>
<i class="fa fa-sort"></i>
</span>
</th>
<th
{{sorterTh $url "createdTime" .sorter}}
>
Create Date
<span class="th-sort">
<i class="fa fa-sort-down"></i>
<i class="fa fa-sort-up"></i>
<i class="fa fa-sort"></i>
</span>
</th>
<th width="30">
</th>
</tr>
</thead>
<tbody>
{{range .blogs}}
<tr>
<td>
<input type="checkbox" name="post[]" value="2">
</td>
<td>
<a href="/blog/view/{{.NoteId.Hex}}" target="_blank">{{.Title|raw}}</a>
</td>
<td>
<a href="/blog/{{.UserId.Hex}}" target="_blank">
{{.User.Username}}
</a>
</td>
<td>
<button data-loading-text="..." class="btn btn-default change-recommend" data-id="{{.NoteId.Hex}}" data-recommend="{{if .IsRecommend}}1{{else}}0{{end}}">
{{if .IsRecommend}}
Y
{{else}}
N
{{end}}
</button>
</td>
<td>
{{.CreatedTime|datetime}}
</td>
<td>
<a href="#" class="btn btn-default">Send Email</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<footer class="panel-footer">
<div class="row">
<div class="col-sm-4 hidden-xs">
<select class="input-sm form-control input-s-sm inline v-middle">
<option value="0">
Bulk action
</option>
<option value="1">
Delete selected
</option>
<option value="2">
Bulk edit
</option>
<option value="3">
Export
</option>
</select>
<button class="btn btn-sm btn-default">
Apply
</button>
</div>
<div class="col-sm-4 text-center">
<small class="text-muted inline m-t-sm m-b-sm">
showing 20-30 of 50 items
</small>
</div>
<div class="col-sm-4 text-right text-center-xs">
{{set . "url" (urlConcat "/adminBlog/index" "sorter" .sorter "keywords" .keywords)}}
{{template "admin/user/page.html" .}}
</div>
</div>
</footer>
</section>
{{template "admin/footer.html" .}}
<script>
$(function() {
$(".change-recommend").click(function() {
var isRecommend = +$(this).data("recommend");
var noteId = $(this).data("id");
var t = this;
$(t).button("loading");
ajaxGet("/adminBlog/setRecommend", {noteId: noteId, recommend: !isRecommend}, function() {
$(t).button("reset");
$(t).text(isRecommend ? "N" : "Y");
$(t).data("recommend", !isRecommend);
});
});
});
</script>
{{template "admin/end.html" .}}

View File

@@ -0,0 +1,33 @@
{{if gt .pageInfo.TotalPage 1}}
<ul class="pagination pagination-sm m-t-none m-b-none">
<li class="{{if eq $.pageInfo.CurPage 1}}disabled{{end}}" >
<a href="{{if eq $.pageInfo.CurPage 1}}javascript:;{{else}}{{sub $.pageInfo.CurPage | urlConcat $.url "page" }}{{end}}">
<i class="fa fa-chevron-left">
</i>
</a>
</li>
{{range $i := N 1 .pageInfo.TotalPage}}
{{if eq $i $.pageInfo.CurPage}}
<li class="active">
<a href="javascript:;">
{{$i}}
</a>
</li>
{{else}}
<li class="">
<a href="{{urlConcat $.url "page" $i}}">
{{$i}}
</a>
</li>
{{end}}
{{end}}
<li class="{{if eq .pageInfo.CurPage .pageInfo.TotalPage}}disabled{{end}}" >
<a href="{{if eq .pageInfo.CurPage .pageInfo.TotalPage}}javascript:;{{else}}{{add $.pageInfo.CurPage | urlConcat $.url "page" }}{{end}}">
<i class="fa fa-chevron-right">
</i>
</a>
</li>
</ul>
{{end}}

View File

@@ -0,0 +1,55 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Blog</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>Recommend Tags</label>
<input type="text" class="form-control" name="recommendTags" value="{{.recommendTags}}">
Split by ','
</div>
<div class="form-group">
<label>New Tags</label>
<input type="text" class="form-control" name="newTags" value="{{.newTags}}">
Split by ','
</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/doBlogTag", getFormJsonData("add_user_form"), function(ret){
$(t).button('reset')
if(!ret.Ok) {
art.alert(ret.Msg)
} else {
art.tips("Success");
}
});
}
});
});
</script>
{{template "admin/end.html" .}}

View File

@@ -0,0 +1,52 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Demo User</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>Demo Username</label>
<input type="text" class="form-control" name="demoUsername" value="{{.demoUsername}}">
</div>
<div class="form-group">
<label>Demo Password</label>
<input type="text" class="form-control" name="demoPassword" value="{{.demoPassword}}">
</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/doDemo", getFormJsonData("add_user_form"), function(ret){
$(t).button('reset')
if(!ret.Ok) {
art.alert(ret.Msg)
} else {
art.tips("Success");
}
});
}
});
});
</script>
{{template "admin/end.html" .}}

View File

@@ -0,0 +1,58 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Add User</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>Email</label>
<input type="text" class="form-control" name="email" data-rule-required="true" data-rule-email="true">
</div>
<div class="form-group pull-in clearfix">
<div class="col-sm-6">
<label>Enter password</label>
<input type="password" class="form-control" data-rule-required="true" id="pwd" name="pwd" data-rule-minlength="6">
</div>
<div class="col-sm-6">
<label>Confirm password</label>
<input type="password" class="form-control parsley-validated" data-rule-equalto="#pwd" data-rule-required="true" name="password2">
</div>
</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("/auth/doRegister", getFormJsonData("add_user_form"), function(ret){
$(t).button('reset')
if(!ret.Ok) {
art.alert(ret.Msg)
} else {
art.tips("Success");
}
});
}
});
});
</script>
{{template "admin/end.html" .}}

View File

@@ -0,0 +1,163 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">User</h3></div>
<section class="panel panel-default">
<div class="row wrapper">
<div class="col-sm-5 m-b-xs">
<select class="input-sm form-control input-s-sm inline v-middle">
<option value="0">
Bulk action
</option>
<option value="1">
Delete selected
</option>
<option value="2">
Bulk edit
</option>
<option value="3">
Export
</option>
</select>
<button class="btn btn-sm btn-default">
Apply
</button>
</div>
<div class="col-sm-4 m-b-xs">
</div>
<div class="col-sm-3">
<div class="input-group search-group">
<input type="text" class="input-sm form-control" placeholder="Email" id="keywords" value="{{.keywords}}" />
<span class="input-group-btn">
<button class="btn btn-sm btn-default" type="button" data-url="/adminUser/index">Search</button>
</span>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped b-t b-light">
<thead>
<tr>
<th width="20">
<input type="checkbox">
</th>
{{$url := urlConcat "/adminUser/index" "keywords" .keywords}}
<th
{{sorterTh $url "email" .sorter}}
>
Email
<span class="th-sort">
<i class="fa fa-sort-down"></i>
<i class="fa fa-sort-up"></i>
<i class="fa fa-sort"></i>
</span>
</th>
<th
{{sorterTh $url "username" .sorter}}
>
Username
<span class="th-sort">
<i class="fa fa-sort-down"></i>
<i class="fa fa-sort-up"></i>
<i class="fa fa-sort"></i>
</span>
</th>
<th
{{sorterTh $url "verified" .sorter}}
>
Verified
<span class="th-sort">
<i class="fa fa-sort-down"></i>
<i class="fa fa-sort-up"></i>
<i class="fa fa-sort"></i>
</span>
</th>
<th
{{sorterTh $url "createdTime" .sorter}}
>
Register Date
<span class="th-sort">
<i class="fa fa-sort-down"></i>
<i class="fa fa-sort-up"></i>
<i class="fa fa-sort"></i>
</span>
</th>
<th width="30">
</th>
</tr>
</thead>
<tbody>
{{range .users}}
<tr>
<td>
<input type="checkbox" name="post[]" value="2">
</td>
<td>
{{.Email}}
</td>
<td>
{{.Username}}
</td>
<td>
{{.Verified}}
</td>
<td>
{{.CreatedTime|datetime}}
<a href="#" class="active" data-toggle="class">
<i class="fa fa-check text-success text-active">
</i>
<i class="fa fa-times text-danger text">
</i>
</a>
</td>
<td>
<a href="#" class="btn btn-default">Send Email</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<footer class="panel-footer">
<div class="row">
<div class="col-sm-4 hidden-xs">
<select class="input-sm form-control input-s-sm inline v-middle">
<option value="0">
Bulk action
</option>
<option value="1">
Delete selected
</option>
<option value="2">
Bulk edit
</option>
<option value="3">
Export
</option>
</select>
<button class="btn btn-sm btn-default">
Apply
</button>
</div>
<div class="col-sm-4 text-center">
<small class="text-muted inline m-t-sm m-b-sm">
showing 20-30 of 50 items
</small>
</div>
<div class="col-sm-4 text-right text-center-xs">
{{set . "url" (urlConcat "/adminUser/index" "sorter" .sorter "keywords" .keywords)}}
{{template "admin/user/page.html" .}}
</div>
</div>
</footer>
</section>
{{template "admin/footer.html" .}}
<script>
$(function() {
});
</script>
{{template "admin/end.html" .}}

View File

@@ -0,0 +1,33 @@
{{if gt .pageInfo.TotalPage 1}}
<ul class="pagination pagination-sm m-t-none m-b-none">
<li class="{{if eq $.pageInfo.CurPage 1}}disabled{{end}}" >
<a href="{{if eq $.pageInfo.CurPage 1}}javascript:;{{else}}{{sub $.pageInfo.CurPage | urlConcat $.url "page" }}{{end}}">
<i class="fa fa-chevron-left">
</i>
</a>
</li>
{{range $i := N 1 .pageInfo.TotalPage}}
{{if eq $i $.pageInfo.CurPage}}
<li class="active">
<a href="javascript:;">
{{$i}}
</a>
</li>
{{else}}
<li class="">
<a href="{{urlConcat $.url "page" $i}}">
{{$i}}
</a>
</li>
{{end}}
{{end}}
<li class="{{if eq .pageInfo.CurPage .pageInfo.TotalPage}}disabled{{end}}" >
<a href="{{if eq .pageInfo.CurPage .pageInfo.TotalPage}}javascript:;{{else}}{{add $.pageInfo.CurPage | urlConcat $.url "page" }}{{end}}">
<i class="fa fa-chevron-right">
</i>
</a>
</li>
</ul>
{{end}}

639
app/views/Admin/button.html Normal file
View File

@@ -0,0 +1,639 @@
<div class="row">
<div class="col-md-6">
<h4 class="m-t-xs">
Button options
</h4>
<div class="doc-buttons">
<a href="#" class="btn btn-s-md btn-default">
Default
</a>
<a href="#" class="btn btn-s-md btn-primary">
Primary
</a>
<a href="#" class="btn btn-s-md btn-success">
Success
</a>
<a href="#" class="btn btn-s-md btn-info">
Info
</a>
<a href="#" class="btn btn-s-md btn-warning">
Warning
</a>
<a href="#" class="btn btn-s-md btn-danger">
Danger
</a>
<a href="#" class="btn btn-s-md btn-dark">
Dark
</a>
<a href="#" class="btn btn-s-md btn-default disabled">
Disabled
</a>
</div>
<h4 class="m-t">
Button size
</h4>
<p>
<a href="#" class="btn btn-default btn-lg">
Large button
</a>
</p>
<p>
<a href="#" class="btn btn-default">
Default button
</a>
</p>
<p>
<a href="#" class="btn btn-default btn-sm">
Small button
</a>
</p>
<p>
<a href="#" class="btn btn-default btn-xs">
Extra small button
</a>
</p>
<h4 class="m-t-lg">
Button dropdowns
</h4>
<p class="text-muted">
Single button dropdowns
</p>
<div class="m-b-sm">
<div class="btn-group">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
Action
<span class="caret">
</span>
</button>
<ul class="dropdown-menu">
<li>
<a href="#">
Action
</a>
</li>
<li>
<a href="#">
Another action
</a>
</li>
<li>
<a href="#">
Something else here
</a>
</li>
<li class="divider">
</li>
<li>
<a href="#">
Separated link
</a>
</li>
</ul>
</div>
<div class="btn-group">
<button class="btn btn-success dropdown-toggle" data-toggle="dropdown">
Action
<span class="caret">
</span>
</button>
<ul class="dropdown-menu">
<li>
<a href="#">
Action
</a>
</li>
<li>
<a href="#">
Another action
</a>
</li>
<li>
<a href="#">
Something else here
</a>
</li>
<li class="divider">
</li>
<li>
<a href="#">
Separated link
</a>
</li>
</ul>
</div>
</div>
<div class="m-b-sm">
<div class="btn-group">
<button class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
Action
<span class="caret">
</span>
</button>
<ul class="dropdown-menu">
<li>
<a href="#">
Action
</a>
</li>
<li>
<a href="#">
Another action
</a>
</li>
<li>
<a href="#">
Something else here
</a>
</li>
<li class="divider">
</li>
<li>
<a href="#">
Separated link
</a>
</li>
</ul>
</div>
</div>
<div class="m-b-sm">
<div class="btn-group">
<button class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
Action
<span class="caret">
</span>
</button>
<ul class="dropdown-menu">
<li>
<a href="#">
Action
</a>
</li>
<li>
<a href="#">
Another action
</a>
</li>
<li>
<a href="#">
Something else here
</a>
</li>
<li class="divider">
</li>
<li>
<a href="#">
Separated link
</a>
</li>
</ul>
</div>
</div>
<p class="text-muted">
Split button dropdowns & variation
</p>
<div class="m-b-sm">
<div class="btn-group">
<button class="btn btn-default">
Action
</button>
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret">
</span>
</button>
<ul class="dropdown-menu">
<li>
<a href="#">
Action
</a>
</li>
<li>
<a href="#">
Another action
</a>
</li>
<li>
<a href="#">
Something else here
</a>
</li>
<li class="divider">
</li>
<li>
<a href="#">
Separated link
</a>
</li>
</ul>
</div>
<div class="btn-group dropup">
<button class="btn btn-default">
Action
</button>
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret">
</span>
</button>
<ul class="dropdown-menu">
<li>
<a href="#">
Action
</a>
</li>
<li>
<a href="#">
Another action
</a>
</li>
<li>
<a href="#">
Something else here
</a>
</li>
<li class="divider">
</li>
<li>
<a href="#">
Separated link
</a>
</li>
</ul>
</div>
</div>
<h4 class="m-t-lg">
Button with icon
</h4>
<p>
<a href="#" class="btn btn-default">
<i class="fa fa-html5">
</i>
Html5
</a>
<a href="#" class="btn btn-info">
<i class="fa fa-css3">
</i>
CSS3
</a>
</p>
<p>
<a href="#" class="btn btn-default btn-lg btn-block">
<i class="fa fa-bars pull-right">
</i>
Block button with icon
</a>
</p>
<p>
<a href="#" class="btn btn-default btn-block">
<i class="fa fa-bars pull-left">
</i>
Block button with icon
</a>
</p>
<h4 class="m-t-lg">
Button icon
</h4>
<p id="social-buttons">
<a href="#" class="btn btn-sm btn-icon btn-info">
<i class="fa fa-twitter">
</i>
</a>
<a href="#" class="btn btn-sm btn-icon btn-success">
<i class="fa fa-facebook">
</i>
</a>
<a href="#" class="btn btn-sm btn-icon btn-danger">
<i class="fa fa-google-plus">
</i>
</a>
</p>
<h4 class="m-t-lg">
Button icon rounded
</h4>
<p id="social-buttons">
<a href="#" class="btn btn-rounded btn-sm btn-icon btn-default">
<i class="fa fa-twitter">
</i>
</a>
<a href="#" class="btn btn-rounded btn-sm btn-icon btn-default">
<i class="fa fa-facebook">
</i>
</a>
<a href="#" class="btn btn-rounded btn-sm btn-icon btn-default">
<i class="fa fa-google-plus">
</i>
</a>
</p>
</div>
<div class="col-md-6">
<h4 class="m-t-xs">
Rounded button
</h4>
<div class="doc-buttons">
<a href="#" class="btn btn-s-md btn-default btn-rounded">
Default
</a>
<a href="#" class="btn btn-s-md btn-primary btn-rounded">
Primary
</a>
<a href="#" class="btn btn-s-md btn-success btn-rounded">
Success
</a>
<a href="#" class="btn btn-s-md btn-info btn-rounded">
Info
</a>
<a href="#" class="btn btn-s-md btn-warning btn-rounded">
Warning
</a>
<a href="#" class="btn btn-s-md btn-danger btn-rounded">
Danger
</a>
<a href="#" class="btn btn-s-md btn-dark btn-rounded">
Dark
</a>
<a href="#" class="btn btn-s-md btn-default btn-rounded disabled">
Disabled
</a>
</div>
<h4 class="m-t-lg">
Button groups
</h4>
<div class="m-b-sm">
<div class="btn-group">
<button type="button" class="btn btn-default">
Left
</button>
<button type="button" class="btn btn-default">
Middle
</button>
<button type="button" class="btn btn-default">
Right
</button>
</div>
</div>
<p class="text-muted">
Vertical button groups
</p>
<div class="btn-group-vertical m-b-sm">
<button type="button" class="btn btn-default">
Top
</button>
<button type="button" class="btn btn-default">
Middle
</button>
<button type="button" class="btn btn-default">
Bottom
</button>
</div>
<p class="text-muted">
Nested button groups
</p>
<div class="btn-group m-b-sm">
<button type="button" class="btn btn-default">
1
</button>
<button type="button" class="btn btn-success">
2
</button>
<button type="button" class="btn btn-default">
3
</button>
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
Dropdown
<span class="caret">
</span>
</button>
<ul class="dropdown-menu">
<li>
<a href="#">
Dropdown link
</a>
</li>
<li>
<a href="#">
Dropdown link
</a>
</li>
<li>
<a href="#">
Dropdown link
</a>
</li>
</ul>
</div>
</div>
<p class="text-muted">
Justified button groups
</p>
<div class="m-b-sm">
<div class="btn-group btn-group-justified">
<a href="#" class="btn btn-primary">
Left
</a>
<a href="#" class="btn btn-info">
Middle
</a>
<a href="#" class="btn btn-success">
Right
</a>
</div>
</div>
<p class="text-muted">
Multiple button groups
</p>
<div class="btn-toolbar">
<div class="btn-group">
<button type="button" class="btn btn-default">
1
</button>
<button type="button" class="btn btn-default active">
2
</button>
<button type="button" class="btn btn-default">
3
</button>
<button type="button" class="btn btn-default">
4
</button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-default">
5
</button>
<button type="button" class="btn btn-default">
6
</button>
<button type="button" class="btn btn-default">
7
</button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-default">
8
</button>
</div>
</div>
<h4 class="m-t-lg">
Button components
</h4>
<p class="text-muted">
<span>
There are a few easy ways to quickly get started with Bootstrap, each
one ...
</span>
<span class="text-muted hide" id="moreless">
appealing to a different skill level and use case. Read through to see
what suits your particular needs.
</span>
</p>
<p>
<button href="#moreless" class="btn btn-sm btn-default" data-toggle="class:show">
<i class="fa fa-plus text">
</i>
<span class="text">
More
</span>
<i class="fa fa-minus text-active">
</i>
<span class="text-active">
Less
</span>
</button>
</p>
<p>
<button class="btn btn-default" id="btn-1" href="#btn-1" data-toggle="class:btn-success">
<i class="fa fa-cloud-upload text">
</i>
<span class="text">
Upload
</span>
<i class="fa fa-check text-active">
</i>
<span class="text-active">
Success
</span>
</button>
<button class="btn btn-default" data-toggle="button">
<i class="fa fa-heart-o text">
</i>
<i class="fa fa-heart text-active text-danger">
</i>
</button>
<button class="btn btn-default" data-toggle="button">
<span class="text">
<i class="fa fa-thumbs-up text-success">
</i>
25
</span>
<span class="text-active">
<i class="fa fa-thumbs-down text-danger">
</i>
10
</span>
</button>
<button class="btn btn-success" data-toggle="class:show inline" data-target="#spin"
data-loading-text="Saving...">
Save
</button>
<i class="fa fa-spin fa-spinner hide" id="spin">
</i>
</p>
<div class="m-b-sm">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-sm btn-info active">
<input type="radio" name="options" id="option1">
<i class="fa fa-check text-active">
</i>
Male
</label>
<label class="btn btn-sm btn-success">
<input type="radio" name="options" id="option2">
<i class="fa fa-check text-active">
</i>
Female
</label>
<label class="btn btn-sm btn-primary">
<input type="radio" name="options" id="option3">
<i class="fa fa-check text-active">
</i>
N/A
</label>
</div>
</div>
<div class="m-b-sm">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-sm btn-default">
<input type="checkbox" name="options" id="option1">
option1
</label>
<label class="btn btn-sm btn-default">
<input type="checkbox" name="options" id="option2">
option2
</label>
</div>
</div>
<h5 class="m-t-lg">
Select Button
</h5>
<div class="btn-group m-r">
<button data-toggle="dropdown" class="btn btn-sm btn-default dropdown-toggle">
<span class="dropdown-label">
Option1
</span>
<span class="caret">
</span>
</button>
<ul class="dropdown-menu dropdown-select">
<li class="active">
<a href="#">
<input type="radio" name="d-s-r" checked="">
Option1
</a>
</li>
<li>
<a href="#">
<input type="radio" name="d-s-r">
Option2
</a>
</li>
<li>
<a href="#">
<input type="radio" name="d-s-r">
Option3
</a>
</li>
<li class="disabled">
<a href="#">
<input type="radio" name="d-s-r" disabled="">
I'm disabled
</a>
</li>
</ul>
</div>
<h4 class="m-t-lg">
<a href="#" class="pull-right text-sm" data-toggle="class:btn-rounded"
data-target="#social-buttons a">
Toggle
</a>
Social buttons
</h4>
<p id="social-buttons">
<a href="#" class="btn btn-rounded btn-sm btn-twitter">
<i class="fa fa-fw fa-twitter">
</i>
Twitter
</a>
<a href="#" class="btn btn-rounded btn-sm btn-facebook">
<i class="fa fa-fw fa-facebook">
</i>
Facebook
</a>
<a href="#" class="btn btn-rounded btn-sm btn-gplus">
<i class="fa fa-fw fa-google-plus">
</i>
Google+
</a>
</p>
</div>
</div>

3
app/views/Admin/end.html Normal file
View File

@@ -0,0 +1,3 @@
</body>
</html>

View File

@@ -0,0 +1,36 @@
</div>
</section>
</section>
</section>
</section>
</section>
</section>
<!-- Bootstrap -->
<!-- App -->
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
<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;
var arr = pathname.split("/");
if(arr.length == 0){
return;
}
var controller = "";
var action = "";
if(arr[0] == "") {
arr = arr.slice(1);
}
controller = arr[0];
if(arr.length > 1) {
action = arr[1];
}
$("#nav > li").removeClass("active");
$("#" + controller + "Nav").addClass("active");
$('a[href="' + pathname + '"]').parent().addClass("active");
});
</script>

View File

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote">
<title>{{.title}}</title>
<link href="/css/bootstrap.css" rel="stylesheet">
<link href="/css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/public/admin/css/admin.css" rel="stylesheet">
<style>
</style>
<script>
function log(o) {
if(window.console) {
console.log(o);
}
}
</script>
</head>
<body>
<div id="headerContainer" class="navbar-fixed-top">
<div class="container" style="clearfix" id="header">
<div class="pull-left">
<h1 id="logo" class="clearfix">
<a href="/admin/index"></a>
<span>Admin</span>
</h1>
</div>
</div>
</div>

106
app/views/Admin/index.html Normal file
View File

@@ -0,0 +1,106 @@
{{template "admin/top.html" .}}
<div class="m-b-md"> <h3 class="m-b-none">Workset</h3> <small>Welcome you!</small> </div>
<section class="panel panel-default">
<div class="row m-l-none m-r-none bg-light lter">
<div class="col-sm-6 col-md-3 padder-v b-r b-light">
<span class="fa-stack fa-2x pull-left m-r-sm">
<i class="fa fa-circle fa-stack-2x text-info"></i>
<i class="fa fa-male fa-stack-1x text-white"></i>
</span>
<a class="clear" href="#">
<span class="h3 block m-t-xs"><strong>52,000</strong></span>
<small class="text-muted text-uc">users</small>
</a>
</div>
<div class="col-sm-6 col-md-3 padder-v b-r b-light lt">
<span class="fa-stack fa-2x pull-left m-r-sm">
<i class="fa fa-circle fa-stack-2x text-warning"></i>
<i class="fa fa-file-o fa-stack-1x text-white"></i>
</span>
<a class="clear" href="#">
<span class="h3 block m-t-xs"><strong>1312,000</strong></span>
<small class="text-muted text-uc">notes</small>
</a>
</div>
</div>
</section>
<!-- 最新动态 -->
<section class="panel panel-default">
<h4 class="font-thin padder">
Leanote Events
</h4>
<ul class="list-group">
<li class="list-group-item">
<p>
Wellcome
<a href="#" class="text-info">
@Drew Wllon
</a>
and play this web application template, have fun1
</p>
<small class="block text-muted">
<i class="fa fa-clock-o">
</i>
2 minuts ago
</small>
</li>
<li class="list-group-item">
<p>
Morbi nec
<a href="#" class="text-info">
@Jonathan George
</a>
nunc condimentum ipsum dolor sit amet, consectetur
</p>
<small class="block text-muted">
<i class="fa fa-clock-o">
</i>
1 hour ago
</small>
</li>
<li class="list-group-item">
<p>
<a href="#" class="text-info">
@Josh Long
</a>
Vestibulum ullamcorper sodales nisi nec adipiscing elit.
</p>
<small class="block text-muted">
<i class="fa fa-clock-o">
</i>
2 hours ago
</small>
</li>
</ul>
</section>
<section class="panel panel-default">
<form>
<textarea class="form-control no-border" rows="3" placeholder="Suggestions to leanote"></textarea>
</form>
<footer class="panel-footer bg-light lter">
<button class="btn btn-info pull-right btn-sm">
POST
</button>
<ul class="nav nav-pills nav-sm">
<!--
<li>
<a href="#">
<i class="fa fa-camera text-muted">
</i>
</a>
</li>
<li>
<a href="#">
<i class="fa fa-video-camera text-muted">
</i>
</a>
</li>
</ul>
-->
</footer>
</section>
{{template "admin/footer.html" .}}
{{template "admin/end.html" .}}

125
app/views/Admin/nav.html Normal file
View File

@@ -0,0 +1,125 @@
<nav class="nav-primary hidden-xs">
<ul class="nav" id="nav">
<li class="active" id="adminUserNav">
<a href="index.html">
<i class="fa fa-users icon">
<b class="bg-danger">
</b>
</i>
<span class="pull-right">
<i class="fa fa-angle-down text">
</i>
<i class="fa fa-angle-up text-active">
</i>
</span>
<span>
User
</span>
</a>
<!-- 导航列表 -->
<ul class="nav lt">
<li>
<a href="/adminUser/index">
<span>
List
</span>
</a>
</li>
<li>
<a href="/adminUser/add">
<span>
Add User
</span>
</a>
</li>
</ul>
</li>
<li>
<a href="/adminBlog/index">
<i class="fa fa-file icon">
<b class="bg-warning">
</b>
</i>
<span>
Blog
</span>
</a>
</li>
<li id="adminSettingNav">
<a href="#layout">
<i class="fa fa-cog icon">
<b class="bg-warning">
</b>
</i>
<span class="pull-right">
<i class="fa fa-angle-down text">
</i>
<i class="fa fa-angle-up text-active">
</i>
</span>
<span>
Setting
</span>
</a>
<ul class="nav lt">
<li>
<a href="layout-c.html">
<span>
Register
</span>
</a>
</li>
<li>
<a href="layout-c.html">
<span>
Login
</span>
</a>
</li>
<li>
<a href="layout-r.html">
<span>
Email
</span>
</a>
</li>
<li>
<a href="layout-h.html">
<span>
Share
</span>
</a>
</li>
<li>
<a href="/adminSetting/blog">
<span>
Blog
</span>
</a>
</li>
<li>
<a href="/adminSetting/demo">
<span>
Demo User
</span>
</a>
</li>
</ul>
</li>
<li>
<a href="#layout">
<i class="fa fa-columns icon">
<b class="bg-warning">
</b>
</i>
<span>
Others
</span>
</a>
</li>
</ul>
</nav>

106
app/views/Admin/top.html Normal file
View File

@@ -0,0 +1,106 @@
<!DOCTYPE html>
<html lang="en" class="app">
<head>
<meta charset="utf-8" />
<title>
leanote admin
</title>
<meta name="description" content="leanote admin"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<link href="/css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/public/admin/css/bootstrap.3.2.0.min.css" rel="stylesheet">
<link href="/public/admin/css/admin.css" rel="stylesheet">
<!--[if lt IE 9]>
<script src="/public/admin/js/ie/html5shiv.js"></script>
<script src="/public/admin/js/ie/respond.min.js"></script>
<script src="/public/admin/js/ie/excanvas.js"></script>
<![endif]-->
</head>
<body class="">
<section class="vbox">
<header class="bg-dark dk header navbar navbar-fixed-top-xs">
<div class="navbar-header aside-md clearfix" id="logo">
<a href="/admin/index" class="navbar-brand" data-toggle="fullscreen"></a>
<div>Admin</div>
</div>
<ul class="nav navbar-nav navbar-right m-n hidden-xs nav-user">
<li class="hidden-xs">
<a href="/index" class="dk">
Index
</a>
</li>
<li class="hidden-xs">
<a href="/note" class="dk">
My Note
</a>
</li>
<li class="hidden-xs">
<a href="/blog/admin" class="dk">
Blog
</a>
</li>
<li class="hidden-xs">
<a href="/blog/admin" class="dk">
Logout
</a>
</li>
</ul>
</header>
<section>
<section class="hbox stretch">
<!-- .aside -->
<aside class="bg-light lter b-r aside-md hidden-print hidden-xs" id="nav">
<section class="vbox">
<header class="header bg-primary lter text-center clearfix">
<div class="btn-group">
<div class="hidden-nav-xs">
<a class="btn btn-sm btn-primary">
Welcome, admin!
</a>
</div>
</div>
</header>
<section class="w-f scrollable">
<div class="slim-scroll" data-height="auto" data-disable-fade-out="true"
data-distance="0" data-size="5px" data-color="#333333">
<!-- nav -->
{{template "admin/nav.html" .}}
<!-- / nav -->
</div>
</section>
<footer class="footer lt hidden-xs b-t b-light">
<a href="#nav" data-toggle="class:nav-xs" class="pull-right btn btn-sm btn-default btn-icon">
<i class="fa fa-angle-left text">
</i>
<i class="fa fa-angle-right text-active">
</i>
</a>
</footer>
</section>
</aside>
<!-- /.aside -->
<section id="content">
<section class="vbox">
<section class="scrollable padder">
<!-- 导航 -->
<ul class="breadcrumb no-border no-radius b-b b-light pull-in">
<li>
<a href="index.html">
<i class="fa fa-home">
</i>
Home
</a>
</li>
<li>
<a href="#">
Elements
</a>
</li>
<li class="active">
Components
</li>
</ul>
<!-- 主要内容区 -->

View File

@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note!">
<meta name="description" content="leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote">
<title>{{.title}}</title>
@@ -61,7 +61,7 @@ function log(o) {
<li class="{{if .index}}active{{end}}"><a href="/blog/{{$username}}">{{msg . "home"}}</a></li>
{{range .notebooks}}
{{$notebookId := .NotebookId.Hex}}
<li class="{{if eq $navNotebookId $notebookId}}active{{else}}{{end}}">
<li class="{{if $navNotebookId}}{{if eq $navNotebookId $notebookId}}active{{else}}{{end}}{{end}}">
<a href="/blog/{{$username}}/{{$notebookId}}"
>{{.Title}}</a>
</li>

View File

@@ -126,7 +126,7 @@
<div class="col-sm-10">
<input type="text" class="form-control" id="SubTitle"
name="SubTitle" value="{{.userBlog.SubTitle}}"
placeholder="eg: leanote, your own cloud note">
placeholder="eg: leanote, {{msg $ "moto"}}">
</div>
</div>
@@ -166,19 +166,17 @@
$(function() {
tinymce.init({
selector : "#AboutMe",
content_css : [ "/css/bootstrap.css", "/css/editor.css" ],
content_css : [ "/css/bootstrap.css", "/css/editor/editor.css" ],
skin : "custom",
language : "zh_CN",
language : "{{.locale}}",
height : 300,
width : "100%",
skin : "custom",
plugins : [
"advlist autolink link image lists charmap hr ",
"searchreplace visualblocks visualchars code tabfocus",
"advlist autolink link leanote_image lists charmap hr ",
"searchreplace visualblocks visualchars leanote_code tabfocus",
"table contextmenu directionality textcolor paste fullpage textcolor"],
toolbar1 : "formatselect fontselect fontsizeselect | forecolor backcolor | bold italic underline strikethrough | image | bullist numlist | alignleft aligncenter alignright alignjustify",
toolbar2 : "outdent indent blockquote | link unlink | table | hr removeformat | subscript superscript | visualchars visualblocks | searchreplace | code",
toolbar1 : "formatselect |fontselect fontsizeselect| forecolor backcolor | bold italic underline strikethrough | bullist numlist |",
menubar : false,
toolbar_items_size : 'small',
statusbar : false,

View File

@@ -5,14 +5,31 @@
<section id="box">
<div>
<h1>error-404</h1>
<form class="form-inline" id="boxForm">
<div>
<h1 class="h text-white animated fadeInDownBig">404</h1>
</div>
<div id="errorBox">
<p class="error-info">
This page cann't found.
<br />
<a href="javascript:history.go(-1)">Back</a>
</form>
</p>
<div class="list-group m-b-sm bg-white m-b-lg">
<a href="javascript:history.go(-1);" class="list-group-item"><!-- <i class="fa fa-chevron-right icon-muted"> --></i> <i class="fa fa-fw fa-arrow-left icon-muted"></i> Back </a>
<a href="/index" class="list-group-item"><!-- <i class="fa fa-chevron-right icon-muted"> --></i> <i class="fa fa-fw fa-home icon-muted"></i> Goto homepage </a>
<a href="/note" class="list-group-item"><!-- <i class="fa fa-chevron-right icon-muted"> --></i> <i class="fa fa-fw fa-file-o icon-muted"></i> My note</a>
<a class="list-group-item" href="mailto:leanote@leanote.com">
<span class="badge">leanote@leanote.com</span>
<i class="fa fa-fw fa-envelope-o icon-muted"></i> Contact Us </a>
</div>
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
</body>
</html>
{{end}}

View File

@@ -5,14 +5,31 @@
<section id="box">
<div>
<h1>error-500</h1>
<form class="form-inline" id="boxForm">
Sorry, we got a error.
<br />
<a href="javascript:history.go(-1)">Back</a>
</form>
<div>
<h1 class="h text-white animated fadeInDownBig">404</h1>
</div>
<div id="errorBox">
<p class="error-info">
Sorry, we got an error.
</p>
<div class="list-group m-b-sm bg-white m-b-lg">
<a href="javascript:history.go(-1);" class="list-group-item"><!-- <i class="fa fa-chevron-right icon-muted"> --></i> <i class="fa fa-fw fa-arrow-left icon-muted"></i> Back </a>
<a href="/index" class="list-group-item"><!-- <i class="fa fa-chevron-right icon-muted"> --></i> <i class="fa fa-fw fa-home icon-muted"></i> Goto homepage </a>
<a href="/note" class="list-group-item"><!-- <i class="fa fa-chevron-right icon-muted"> --></i> <i class="fa fa-fw fa-file-o icon-muted"></i> My note</a>
<a class="list-group-item" href="mailto:leanote@leanote.com">
<span class="badge">leanote@leanote.com</span>
<i class="fa fa-fw fa-envelope-o icon-muted"></i> Contact Us </a>
</div>
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
</body>
</html>
{{end}}

View File

@@ -1,31 +1,35 @@
{{template "home/header_box.html" .}}
<style>
</style>
<section id="box">
<section id="box" class="animated fadeInUp">
<div>
<h1>leanote | {{msg . "findPassword"}}</h1>
<form class="form-inline" id="boxForm">
<h1 id="logo">leanote</h1>
<div id="boxForm">
<div id="boxHeader">{{msg . "findPassword"}}</div>
<form>
<div class="alert alert-danger" id="loginMsg"></div>
<table style="width: 100%">
<tr>
<td style="width: 110px"><label for="email">{{msg . "email"}}</label>
<input type="text" class="form-control" id="email" name="email" value="{{.email}}"> </td>
</tr>
<tr>
<td>
<button id="loginBtn" class="btn btn-success" style="width: 100%">{{msg . "findPassword"}}</button>
</td>
</tr>
</table>
<div class="form-group">
<label class="control-label" for="email">{{msg . "email"}}</label>
<input type="text" class="form-control" id="email" name="email" value="{{.email}}">
</div>
<button id="loginBtn" class="btn btn-success">{{msg . "findPassword"}}</button>
</form>
<div id="quickLinks">
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
</div>
</section>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>

View File

@@ -1,44 +1,45 @@
{{template "home/header_box.html" .}}
<section id="box">
<section id="box" class="animated fadeInUp">
<div>
<h1>leanote | {{msg . "updatePassword"}}</h1>
<form class="form-inline" id="boxForm">
<div class="form-group">
</div>
<h1 id="logo">leanote</h1>
<div id="boxForm">
<div id="boxHeader">{{msg . "updatePassword"}}</div>
<form>
<div class="alert alert-danger" id="loginMsg"> </div>
<table style="width: 100%">
<tr>
<td style="width: 350px"><label for="email">{{msg . "email"}}</label>
<div class="form-group">
<label class="control-label" for="email">{{msg . "email"}}</label>
<br />
{{.findPwd.Email}}
</td>
</tr>
<tr>
<td>
<label for="pwd">{{msg . "password"}}</label>
</div>
<div class="form-group">
<label class="control-label" for="pwd">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
{{msg . "passwordTips"}}
</td>
</tr>
<tr>
<td>
<label for="pwd2">{{msg . "password2"}}</label>
</div>
<div class="form-group">
<label class="control-label" for="pwd2">{{msg . "password2"}}</label>
<input type="password" class="form-control" id="pwd2" name="pwd2" >
</td>
</tr>
<tr>
<td>
<button id="loginBtn" class="btn btn-success" style="width: 100%">{{msg . "updatePassword"}}</button>
</td>
</tr>
</table>
</div>
<button id="loginBtn" class="btn btn-success">{{msg . "updatePassword"}}</button>
</form>
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>

View File

@@ -1,21 +1,30 @@
{{template "home/header_box.html" .}}
<section id="box">
<section id="box" class="animated fadeInUp">
<div>
<h1>leanote {{msg . "findPassword"}} - {{msg . "findPasswordTimeout"}}</h1>
<form class="form-inline" id="boxForm">
<h1 id="logo">leanote</h1>
<div id="boxForm">
<div id="boxHeader">{{msg . "findPasswordTimeout"}}</div>
<form>
<div class="alert alert-danger" id="loginMsg" style="display: block">
{{msg . "findPasswordTimeout"}}, <a href="/findPassword">{{msg . "reFindPassword"}}</a>
</div>
</form>
<div id="quickLinks">
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
</body>

View File

@@ -15,6 +15,8 @@
<i class="fa fa-globe fa-3x icon-muted"></i>
<h2>Join Us</h2>
<a href="https://github.com/leanote/leanote">github leanote</a>
<br />
QQ Group: 158716820
</div>
</div>
</div>

View File

@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note!">
<meta name="description" content="leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote">
<title>{{.title}}</title>
@@ -27,7 +27,7 @@ function log(o) {
<div class="pull-left">
<h1>
<a href="/index">
<img src="/images/logo.png" id="" style="height: 40px" title="leanote, Your own cloud note!"/>
<img src="/images/logo/leanote_black.png" id="" style="height: 50px" title="leanote, {{msg $ "moto"}}"/>
</a>
</h1>
</div>
@@ -47,8 +47,22 @@ function log(o) {
<ul id="blogNav" class="pull-right">
<li><a href="/index#" target="body" class="smooth-scroll">{{msg . "home"}}</a></li>
<!--
<li><a href="/index#aboutLeanote" target="#aboutLeanote" class="smooth-scroll">{{msg . "aboutLeanote"}}</a> </li>
<li><a id="leanoteBlog" href="http://leanote.com/blog/leanote" target="_blank" class="">{{msg . "leanoteBlog"}}</a></li>
-->
<li><a href="/index#download" target="#download" class="smooth-scroll">{{msg . "download"}}</a> </li>
<li><a href="/index#donate" target="#donate" class="smooth-scroll">{{msg . "donate"}}</a> </li>
<li><a id="leanoteBlog" href="http://leanote.com/lea/index" target="_blank" title="lea++, leanote博客平台" class="">lea++</a></li>
<li style="position: relative; margin-right: 3px;">
<a href="http://bbs.leanote.com" target="_blank" class="">{{msg . "discussion"}}</a>
<div style="position: absolute;
width: 8px;
height: 8px;
background: red;
top: 15px;
right: 5px;
border-radius: 9px;"></div>
</li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note!">
<meta name="description" content="leanote, {{msg $ "moto"}}">
<meta name="author" content="leanote">
<title>{{.title}}</title>
@@ -18,4 +18,4 @@ html, body {
}
</style>
</head>
<body>
<body id="boxBody">

View File

@@ -4,14 +4,15 @@
</style>
<section>
<div class="header">
<h2>leanote, your own cloud note!</h2>
<p>Knowledge, Sharing, Cooperation, Blog... all just in leanote</p>
<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">Fork leanote on Github</a>
<a class="btn btn-primary" href="https://github.com/leanote/leanote">{{msg . "fork github"}}</a>
&nbsp;
&nbsp;
<a class="btn btn-default" href="/demo">Try it</a>
<a class="btn btn-default" href="/demo">{{msg . "try"}}</a>
{{if .openRegister}}
&nbsp;
&nbsp;
@@ -61,6 +62,52 @@
</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>

View File

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

View File

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

View File

@@ -1,51 +1,57 @@
{{template "home/header_box.html" .}}
<section id="box">
<section id="box" class="animated fadeInUp">
<!--
<div>
<a class="back" href="javascript:history.go(-1);" tabindex="-1">←Back</a>
</div>
-->
<div>
<h1>leanote | {{msg . "login"}}</h1>
<form class="form-inline" id="boxForm" >
<h1 id="logo">leanote</h1>
<div id="boxForm">
<div id="boxHeader">{{msg . "login"}}</div>
<form>
<div class="alert alert-danger" id="loginMsg"></div>
<table>
<tr>
<td>
<label for="email">{{msg . "usernameOrEmail"}}</label>
<div class="form-group">
<label class="control-label">{{msg . "usernameOrEmail"}}</label>
<input type="text" class="form-control" id="email" name="email" value="{{.email}}">
</td>
</tr>
<tr>
<td>
<label for="pwd">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
</td>
</tr>
<tr>
<td>
<button id="loginBtn" class="btn btn-success" style="width: 100%">{{msg . "login"}}</button>
<br />
<a href="/findPassword">{{msg . "forgetPassword"}}</a>
{{if .openRegister}}
<br />
<a href="/register">{{msg . "register"}}</a>
{{msg . "or"}}
<a href="/demo">{{msg . "try"}}</a>
<div style="border-top: 1px dashed #666;margin:2px 0">
{{msg . "3th"}}: <a id="github">github<i class="fa fa-github"></i></a>
<span id="thirdLoginLoading" style="display: none"> <img src="/images/loading-a-20-2.gif" />正在登录...</span>
</div>
<div class="form-group">
<label class="control-label">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
</div>
<div class="clearfix">
<a href="/findPassword" class="pull-right m-t-xs"><small>{{msg . "forgetPassword"}}</small></a>
<button id="loginBtn" class="btn btn-success">{{msg . "login"}}</button>
</div>
<div class="line line-dashed"></div>
<a href="#" id="github" class="btn btn-github btn-block m-b-sm"><i class="fa fa-github pull-left"></i>{{msg . "use"}} Github</a>
<div class="line line-dashed"></div>
<p class="text-muted text-center"><small>{{msg . "hasAcount"}}</small></p>
{{if .openRegister}}
<a href="/register" class="btn btn-default btn-block">{{msg . "register"}}</a>
{{end}}
</td>
</tr>
</table>
{{msg . "or"}}
<a id="loginBtn" href="/demo" class="btn btn-default btn-block">{{msg . "try"}}</a>
</form>
<div id="quickLinks">
<a href="/index">{{msg . "home"}}</a>
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>
@@ -98,7 +104,7 @@ $(function() {
// github
$("#github").click(function() {
$("#thirdLoginLoading").show();
$(this).button("loading");
location.href="https://github.com/login/oauth/authorize?access_type=&approval_prompt=&client_id=3790fbf1fc14bc6c5d85&redirect_uri=http%3A%2F%2Fleanote.com%2Foauth%2FgithubCallback&response_type=code&scope=user&state=";
});
});

View File

@@ -1,50 +1,51 @@
{{template "home/header_box.html" .}}
<section id="box">
<section id="box" class="animated fadeInUp">
<!--
<div>
<a class="back" href="javascript:history.go(-1);" tabindex="-1">←Back</a>
</div>
-->
<div>
<h1>leanote | {{msg . "register"}}</h1>
<form class="form-inline" id="boxForm">
<h1 id="logo">leanote</h1>
<div id="boxForm">
<div id="boxHeader">{{msg . "register"}}</div>
<form>
<div class="alert alert-danger" id="loginMsg"></div>
<table>
<tr>
<td>
<label for="email">{{msg . "email"}}</label>
<div class="form-group">
<label class="control-label" for="email">{{msg . "email"}}</label>
<input type="text" class="form-control" id="email" name="email">
</td>
</tr>
<tr>
<td>
<label for="pwd">{{msg . "password"}}</label>
</div>
<div class="form-group">
<label class="control-label" for="pwd">{{msg . "password"}}</label>
<input type="password" class="form-control" id="pwd" name="pwd">
{{msg . "passwordTips"}}
</td>
</tr>
<tr>
<td>
<label for="pwd2">{{msg . "password2"}}</label>
</div>
<div class="form-group">
<label class="control-label" for="pwd2">{{msg . "password2"}}</label>
<input type="password" class="form-control" id="pwd2" name="pwd2" >
</td>
</tr>
</div>
<tr>
<td>
<button id="registerBtn" class="btn btn-success" style="width: 100%">{{msg . "register"}}</button>
</td>
</tr>
<button id="registerBtn" class="btn btn-success">{{msg . "register"}}</button>
</table>
<div class="line line-dashed"></div>
<p class="text-muted text-center"><small>{{msg . "hadAcount"}}</small></p>
<a id="loginBtn" href="/login" class="btn btn-default btn-block">{{msg . "login"}}</a>
</form>
<div id="quickLinks">
<a href="/login">{{msg . "login"}}</a>
&nbsp;
<a href="/index">{{msg . "home"}}</a>
</div>
</div>
</section>
<div id="boxFooter">
<p>
<a href="/index">{{msg . "home"}}</a>
</p>
<p>
<a href="/index">leanote</a> © 2014
</p>
</div>
<script src="/js/jquery-1.9.0.min.js"></script>
<script src="/js/bootstrap.js"></script>

View File

@@ -5,14 +5,16 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note">
<title>leanote, your own cloud note</title>
<meta name="description" content="leanote, {{msg $ "moto"}}">
<title>leanote, {{msg $ "moto"}}</title>
<link href="css/bootstrap.css" rel="stylesheet" />
<!-- 先加载, 没有样式, 宽度不定 -->
<link rel="stylesheet" href="tinymce/skins/custom/skin.min.css" type="text/css" />
<!-- leanote css -->
<link href="css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" />
<link href="css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
<script>
var hash = location.hash;
if(hash == "#writing") {
@@ -23,7 +25,6 @@ if(hash == "#writing") {
document.write(files);
</script>
<link href="css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" />
<!-- For Develop writting mod -->
@@ -41,17 +42,17 @@ function log(o) {
<!-- <div id="headerContainer"> -->
<div id="header">
<!-- <div id="headerLeft" class="pull-left">
<!--
<div id="headerLeft" class="pull-left">
</div>
-->
<div id="logo" class="pull-left">
<span>lea</span>note
<!-- <img style="height: 45px;" src="/images/logo.png" title="leanote, 不一样的笔记" alt="leanote">
-->
<!--<span>lea</span>note -->
<!--<img style="height: 45px;" src="/images/logo/leanote_white.png" title="leanote, 不一样的笔记" alt="leanote">-->
</div>
<div id="switcher" class="pull-left">
<i class="fa fa-align-justify" id="leftSwitcher" title="{{msg . "leftHidden"}}"></i>
<span id="leftSwitcher2" title="{{msg . "leftShow"}}">lea</span>
<span id="leftSwitcher2" title="{{msg . "leftShow"}}"></span>
</div>
<!-- search -->
<div class="pull-left" id="searchWrap">
@@ -80,10 +81,13 @@ function log(o) {
id="dropdownMenu2" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2" style="width: 180px;left: -5px;" id="notebookNavForNewNote">
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
<input type="text" placeholder="search notebook" class="form-control" id="searchNotebookForAdd"/>
<ul class="clearfix" role="menu" aria-labelledby="dropdownMenu2" id="notebookNavForNewNote">
</ul>
</div>
</div>
</div>
<!-- 只为新建别人的笔记 -->
<div id="newSharedNote" style="display: none">
@@ -98,12 +102,12 @@ function log(o) {
<span class="for-split"> - </span>
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle"
id="dropdownMenu2" data-toggle="dropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2" style="width: 180px;left: -5px;" id="notebookNavForNewSharedNote">
</ul>
<div class="dropdown-menu dropdown-list" style="left: -200px;" >
<ul id="notebookNavForNewSharedNote"></ul>
</div>
</div>
</div>
</div>
@@ -233,8 +237,12 @@ function log(o) {
<i class="fa fa-plus" title="{{msg . "addNotebook"}}"></i>
</div>
</div>
<ul class="folderBody" id="notebookList">
</ul>
<div class="folderBody">
<input type="text" class="form-control" id="searchNotebookForList" placeholder="search notebook"/>
<ul class="ztree" id="notebookList"></ul>
<ul class="ztree" id="notebookListForSearch"></ul>
</div>
</div>
<div class="folderNote closed" id="myTag">
@@ -245,7 +253,7 @@ function log(o) {
</span>
</div>
<ul class="folderBody" id="tagNav">
<ul class="folderBody clearfix" id="tagNav">
<li><a> <span class="label label-red">{{msg . "red"}}</span></a></li>
<li><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li>
<li><a> <span class="label label-yellow">{{msg . "yellow"}}</span></a></li>
@@ -253,7 +261,7 @@ function log(o) {
</ul>
</div>
<div class="folderNote closed" id="">
<div class="folderNote closed" id="myShareNotebooks">
<div class="folderHeader">
<i class="fa fa-user fa-left"></i>
<span>
@@ -268,7 +276,7 @@ function log(o) {
</div>
<!-- 缩小版 -->
<!-- 缩小版 todo 不要展示, 点击展开即可 -->
<div id="notebookMin">
<!-- 这里隐藏, 不要 -->
<div target="#notebookList" title="{{msg . "notebook"}}" class="minContainer">
@@ -282,13 +290,9 @@ function log(o) {
</ul>
</div>
<div id="minShareNotebooks">
<!--
<div class="minContainer" target="" title="">
<div class="minContainer" target="#friendContainer0" title="{{msg . "share"}}">
<i class="fa fa-user"></i>
<ul class="dropdown-menu">
</ul>
</div>
-->
</div>
</div>
@@ -312,19 +316,24 @@ function log(o) {
<div class="dropdown" id="myNotebookNavForListNav">
<a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">
<span id="curNotebookForListNote">{{msg . "all"}}</span>
<!--
<i class="fa fa-angle-down"></i>
-->
</a>
<!-- 如果notebook过多, 则会产生滚动 -->
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu1" style="left: 5px;" id="notebookNavForListNote">
</ul>
<!-- 如果notebook过多, 则会产生滚动 弃用
<div class="dropdown-menu dropdown-list" style="left: 5px;">
<ul role="menu" aria-labelledby="dropdownMenu1" id="notebookNavForListNote"></ul>
</div>
-->
</div>
<!-- 共享的笔记本 -->
<div class="dropdown" id="sharedNotebookNavForListNav" style="display: none">
<a class="ios7-a dropdown-toggle" id="dropdownMenu2" data-toggle="dropdown">
<span id="curSharedNotebookForListNote">{{msg . "all"}}</span>
<!--
<i class="fa fa-angle-down"></i>
-->
</a>
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu2" style="left: 5px;" id="sharedNotebookNavForListNote">
@@ -344,6 +353,7 @@ function log(o) {
</i>Sort <i class="fa fa-angle-down"></i>
-->
</a>
<!--
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu1"
style="right: 3px; ! important; left: -100px; min-width: 100px;">
@@ -357,6 +367,7 @@ function log(o) {
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">Separated </a></li>
</ul>
-->
</div>
</div>
@@ -450,12 +461,45 @@ function log(o) {
<li role="presentation"><span class="label label-green">{{msg . "green"}}</span></li>
</ul>
</div>
</div>
<ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s"
data-toggle="dropdown">{{msg . "save"}}</a></li>
<li class="dropdown" id="attachDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
<!--
<span class="fa fa-upload"></span>
-->
{{msg . "attachments"}}<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">
<a class="btn btn-success btn-choose-file">
Choose File to Upload
</a>
<a class="btn btn-default" id="downloadAllBtn">
<i class="fa fa-download"></i>
Download All
</a>
<a class="btn btn-default" id="linkAllBtn">
<i class="fa fa-link"></i>
Link All
</a>
<input type="file" name="file" multiple/>
</div>
<div id="attachUploadMsg">
</div>
</form>
</div>
</li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown">{{msg . "editorTips"}}</a></li>
<li><a class="ios7-a " id="contentHistory"
@@ -487,6 +531,17 @@ function log(o) {
<div id="leanoteNavContent">
</div>
</div>
<!-- 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
<input type="file" name="file" multiple style="display: none"/>
</div>
<ul id="uploadMsg">
</ul>
</form>
<!-- 由此可以算高度 -->
<div id="editorContent" name="editorContent" tabindex="2" />
{{.noteContent}}
@@ -512,7 +567,7 @@ function log(o) {
</div>
<textarea id="md-section-helper"></textarea>
</div>
<!-- for test -->
<!-- mdEditor -->
<!-- Hidden Popup Modal -->
@@ -834,7 +889,7 @@ function log(o) {
<tr>
<td>
<button id="pwdBtn" class="btn btn-success" style="width: 100%">{{msg . "submit"}}</button>
<button id="pwdBtn" class="btn btn-success">{{msg . "submit"}}</button>
</td>
</tr>
</table>
@@ -910,6 +965,7 @@ function log(o) {
</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>
@@ -926,9 +982,10 @@ LEA.locale = "{{.locale}}";
<!-- 渲染view -->
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
<script src="/js/contextmenu/jquery.contextmenu.js"></script>
<script src="js/app/page.js"></script>
<script src="tinymce/tinymce.min.js"></script>
<script src="tinymce/tinymce.js"></script>
<script src="js/jquery-cookie-min.js"></script>
<script src="js/bootstrap-min.js"></script>
<script src="js/app/note.js"></script>
@@ -950,6 +1007,9 @@ Note.setNoteCache(noteContentJson);
Note.renderNoteContent(noteContentJson)
Tag.renderTagNav(tagsJson);
// init notebook后才调用
initSlimScroll();
</script>
<!-- mdEditor -->
@@ -966,7 +1026,7 @@ Tag.renderTagNav(tagsJson);
MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]], processEscapes: true }, messageStyle: "none"});
</script>
<script src="/public/mdeditor/editor/mathJax-min.js"></script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script src="/public/mdeditor/editor/jquery.waitforimages-min.js"></script>
<script src="/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script src="/public/mdeditor/editor/editor.js"></script>
@@ -974,6 +1034,28 @@ Tag.renderTagNav(tagsJson);
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
<script src="/js/contextmenu/jquery.contextmenu.js"></script>
<!-- js version 2.0 use require.js -->
<script src="/js/require.js"></script>
<script>
require.config({
baseUrl: '/public',
paths: {
// 'jquery': 'js/jquery-1.9.0.min',
'leaui_image': 'tinymce/plugins/leaui_image/public/js/for_editor',
'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']}
}
});
require(['leaui_image'], function(leaui_image) {
});
require(['attachment_upload'], function(attachment_upload) {
});
</script>
</body>
</html>

View File

@@ -5,14 +5,16 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="leanote,leanote.com">
<meta name="description" content="leanote, your own cloud note">
<title>leanote, your own cloud note</title>
<meta name="description" content="leanote, {{msg $ "moto"}}">
<title>leanote, {{msg $ "moto"}}</title>
<link href="css/bootstrap.css" rel="stylesheet" />
<!-- 先加载, 没有样式, 宽度不定 -->
<link rel="stylesheet" href="tinymce/skins/custom/skin.min.css" type="text/css" />
<!-- leanote css -->
<link href="css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" />
<link href="css/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
<script>
var hash = location.hash;
if(hash == "#writing") {
@@ -23,7 +25,6 @@ if(hash == "#writing") {
document.write(files);
</script>
<link href="css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" />
<!-- For Develop writting mod -->
@@ -41,17 +42,17 @@ function log(o) {
<!-- <div id="headerContainer"> -->
<div id="header">
<!-- <div id="headerLeft" class="pull-left">
<!--
<div id="headerLeft" class="pull-left">
</div>
-->
<div id="logo" class="pull-left">
<span>lea</span>note
<!-- <img style="height: 45px;" src="/images/logo.png" title="leanote, 不一样的笔记" alt="leanote">
-->
<!--<span>lea</span>note -->
<!--<img style="height: 45px;" src="/images/logo/leanote_white.png" title="leanote, 不一样的笔记" alt="leanote">-->
</div>
<div id="switcher" class="pull-left">
<i class="fa fa-align-justify" id="leftSwitcher" title="{{msg . "leftHidden"}}"></i>
<span id="leftSwitcher2" title="{{msg . "leftShow"}}">lea</span>
<span id="leftSwitcher2" title="{{msg . "leftShow"}}"></span>
</div>
<!-- search -->
<div class="pull-left" id="searchWrap">
@@ -80,10 +81,13 @@ function log(o) {
id="dropdownMenu2" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2" style="width: 180px;left: -5px;" id="notebookNavForNewNote">
<div class="dropdown-menu dropdown-list" id="searchNotebookForAddDropdownList">
<input type="text" placeholder="search notebook" class="form-control" id="searchNotebookForAdd"/>
<ul class="clearfix" role="menu" aria-labelledby="dropdownMenu2" id="notebookNavForNewNote">
</ul>
</div>
</div>
</div>
<!-- 只为新建别人的笔记 -->
<div id="newSharedNote" style="display: none">
@@ -98,12 +102,12 @@ function log(o) {
<span class="for-split"> - </span>
<span id="curNotebookForNewSharedNote" notebookId="" userId=""></span>
<div class="dropdown" style="display: inline-block">
<a class="ios7-a dropdown-toggle"
id="dropdownMenu2" data-toggle="dropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2" style="width: 180px;left: -5px;" id="notebookNavForNewSharedNote">
</ul>
<div class="dropdown-menu dropdown-list" style="left: -200px;" >
<ul id="notebookNavForNewSharedNote"></ul>
</div>
</div>
</div>
</div>
@@ -160,6 +164,10 @@ function log(o) {
</div>
</div>
<div class="pull-right" style="line-height: 60px; margin:0 10px">
<a target="_blank" title="lea++, leanote blog platform" href="/lea/index">lea++</a>
</div>
<div class="pull-right" style="margin: 0 10px" id="topNav">
<a target="_blank" href="/blog/{{.userInfo.Username}}">
{{msg . "myBlog"}}
@@ -233,8 +241,12 @@ function log(o) {
<i class="fa fa-plus" title="{{msg . "addNotebook"}}"></i>
</div>
</div>
<ul class="folderBody" id="notebookList">
</ul>
<div class="folderBody">
<input type="text" class="form-control" id="searchNotebookForList" placeholder="search notebook"/>
<ul class="ztree" id="notebookList"></ul>
<ul class="ztree" id="notebookListForSearch"></ul>
</div>
</div>
<div class="folderNote closed" id="myTag">
@@ -245,7 +257,7 @@ function log(o) {
</span>
</div>
<ul class="folderBody" id="tagNav">
<ul class="folderBody clearfix" id="tagNav">
<li><a> <span class="label label-red">{{msg . "red"}}</span></a></li>
<li><a> <span class="label label-blue">{{msg . "blue"}}</span></a></li>
<li><a> <span class="label label-yellow">{{msg . "yellow"}}</span></a></li>
@@ -253,7 +265,7 @@ function log(o) {
</ul>
</div>
<div class="folderNote closed" id="">
<div class="folderNote closed" id="myShareNotebooks">
<div class="folderHeader">
<i class="fa fa-user fa-left"></i>
<span>
@@ -268,7 +280,7 @@ function log(o) {
</div>
<!-- 缩小版 -->
<!-- 缩小版 todo 不要展示, 点击展开即可 -->
<div id="notebookMin">
<!-- 这里隐藏, 不要 -->
<div target="#notebookList" title="{{msg . "notebook"}}" class="minContainer">
@@ -282,13 +294,9 @@ function log(o) {
</ul>
</div>
<div id="minShareNotebooks">
<!--
<div class="minContainer" target="" title="">
<div class="minContainer" target="#friendContainer0" title="{{msg . "share"}}">
<i class="fa fa-user"></i>
<ul class="dropdown-menu">
</ul>
</div>
-->
</div>
</div>
@@ -312,19 +320,24 @@ function log(o) {
<div class="dropdown" id="myNotebookNavForListNav">
<a class="ios7-a dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown">
<span id="curNotebookForListNote">{{msg . "all"}}</span>
<!--
<i class="fa fa-angle-down"></i>
-->
</a>
<!-- 如果notebook过多, 则会产生滚动 -->
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu1" style="left: 5px;" id="notebookNavForListNote">
</ul>
<!-- 如果notebook过多, 则会产生滚动 弃用
<div class="dropdown-menu dropdown-list" style="left: 5px;">
<ul role="menu" aria-labelledby="dropdownMenu1" id="notebookNavForListNote"></ul>
</div>
-->
</div>
<!-- 共享的笔记本 -->
<div class="dropdown" id="sharedNotebookNavForListNav" style="display: none">
<a class="ios7-a dropdown-toggle" id="dropdownMenu2" data-toggle="dropdown">
<span id="curSharedNotebookForListNote">{{msg . "all"}}</span>
<!--
<i class="fa fa-angle-down"></i>
-->
</a>
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu2" style="left: 5px;" id="sharedNotebookNavForListNote">
@@ -344,6 +357,7 @@ function log(o) {
</i>Sort <i class="fa fa-angle-down"></i>
-->
</a>
<!--
<ul class="dropdown-menu" role="menu"
aria-labelledby="dropdownMenu1"
style="right: 3px; ! important; left: -100px; min-width: 100px;">
@@ -357,6 +371,7 @@ function log(o) {
<li role="presentation"><a role="menuitem" tabindex="-1"
href="#">Separated </a></li>
</ul>
-->
</div>
</div>
@@ -450,12 +465,45 @@ function log(o) {
<li role="presentation"><span class="label label-green">{{msg . "green"}}</span></li>
</ul>
</div>
</div>
<ul class="pull-right" id="editorTool">
<li><a class="ios7-a " id="saveBtn" title="ctrl+s"
data-toggle="dropdown">{{msg . "save"}}</a></li>
<li class="dropdown" id="attachDropdown">
<a class="ios7-a dropdown-toggle" data-toggle="dropdown" id="showAttach">
<!--
<span class="fa fa-upload"></span>
-->
{{msg . "attachments"}}<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">
<a class="btn btn-success btn-choose-file">
Choose File to Upload
</a>
<a class="btn btn-default" id="downloadAllBtn">
<i class="fa fa-download"></i>
Download All
</a>
<a class="btn btn-default" id="linkAllBtn">
<i class="fa fa-link"></i>
Link All
</a>
<input type="file" name="file" multiple/>
</div>
<div id="attachUploadMsg">
</div>
</form>
</div>
</li>
<li><a class="ios7-a " id="tipsBtn"
data-toggle="dropdown">{{msg . "editorTips"}}</a></li>
<li><a class="ios7-a " id="contentHistory"
@@ -487,6 +535,17 @@ function log(o) {
<div id="leanoteNavContent">
</div>
</div>
<!-- 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
<input type="file" name="file" multiple style="display: none"/>
</div>
<ul id="uploadMsg">
</ul>
</form>
<!-- 由此可以算高度 -->
<div id="editorContent" name="editorContent" tabindex="2" />
{{.noteContent}}
@@ -512,7 +571,7 @@ function log(o) {
</div>
<textarea id="md-section-helper"></textarea>
</div>
<!-- for test -->
<!-- mdEditor -->
<!-- Hidden Popup Modal -->
@@ -834,7 +893,7 @@ function log(o) {
<tr>
<td>
<button id="pwdBtn" class="btn btn-success" style="width: 100%">{{msg . "submit"}}</button>
<button id="pwdBtn" class="btn btn-success">{{msg . "submit"}}</button>
</td>
</tr>
</table>
@@ -910,6 +969,7 @@ function log(o) {
</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-min.js"></script>
<script>
@@ -926,9 +986,10 @@ LEA.locale = "{{.locale}}";
<!-- 渲染view -->
<script src="/js/jQuery-slimScroll-1.3.0/jquery.slimscroll.js"></script>
<script src="/js/contextmenu/jquery.contextmenu-min.js"></script>
<script src="js/app/page-min.js"></script>
<script src="tinymce/tinymce.min.js"></script>
<script src="tinymce/tinymce.js"></script>
<script src="js/jquery-cookie-min.js"></script>
<script src="js/bootstrap-min.js"></script>
<script src="js/app/note-min.js"></script>
@@ -950,6 +1011,9 @@ Note.setNoteCache(noteContentJson);
Note.renderNoteContent(noteContentJson)
Tag.renderTagNav(tagsJson);
// init notebook后才调用
initSlimScroll();
</script>
<!-- mdEditor -->
@@ -966,7 +1030,7 @@ Tag.renderTagNav(tagsJson);
MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ["\\(","\\)"]], processEscapes: true }, messageStyle: "none"});
</script>
<script src="/public/mdeditor/editor/mathJax-min.js"></script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script src="/public/mdeditor/editor/jquery.waitforimages-min.js"></script>
<script src="/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script src="/public/mdeditor/editor/editor-min.js"></script>
@@ -974,6 +1038,28 @@ Tag.renderTagNav(tagsJson);
<!-- context-menu -->
<link rel="stylesheet" href="/js/contextmenu/css/contextmenu.css" type="text/css" />
<script src="/js/contextmenu/jquery.contextmenu-min.js"></script>
<!-- js version 2.0 use require.js -->
<script src="/js/require.js"></script>
<script>
require.config({
baseUrl: '/public',
paths: {
// 'jquery': 'js/jquery-1.9.0.min',
'leaui_image': 'tinymce/plugins/leaui_image/public/js/for_editor',
'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']}
}
});
require(['leaui_image'], function(leaui_image) {
});
require(['attachment_upload'], function(attachment_upload) {
});
</script>
</body>
</html>

View File

@@ -21,6 +21,13 @@ cp ./bin/run.sh $tmp/leanote/bin/
# views
cp -r ./app/views $tmp/leanote/app
# 可不要
cp -r ./app/service $tmp/leanote/app/service
cp -r ./app/controllers $tmp/leanote/app/controllers
cp -r ./app/db $tmp/leanote/app/db
cp -r ./app/info $tmp/leanote/app/info
cp -r ./app/lea $tmp/leanote/app/lea
# conf
cp ./conf/app.conf-default $tmp/leanote/conf/app.conf
cp ./conf/routes-default $tmp/leanote/conf/routes
@@ -30,6 +37,11 @@ cp -r ./messages ./public ./mongodb_backup $tmp/leanote/
# delete some files
rm -r $tmp/leanote/public/tinymce/classes
rm -r $tmp/leanote/public/upload
mkdir $tmp/leanote/public/upload
rm -r $tmp/leanote/public/.codekit-cache
rm $tmp/leanote/public/.DS_Store
rm $tmp/leanote/public/config.codekit
# make link
cd $tmp/leanote/bin
@@ -42,7 +54,8 @@ cd $SP
cd ../
cp ./bin/leanote-linux $tmp/leanote/bin/
cd $tmp
tar -cvf $tmp/leanote-linux.tar.gz leanote
tar -cvf $tmp/leanote-linux-v0.4.bin.tar leanote
gzip $tmp/leanote-linux-v0.4.bin.tar
# mac
rm $tmp/leanote/bin/leanote-linux
@@ -50,6 +63,7 @@ cd $SP
cd ../
cp ./bin/leanote-mac $tmp/leanote/bin/
cd $tmp
tar -cvf $tmp/leanote-mac.tar.gz leanote
tar -cvf $tmp/leanote-mac-v0.4.bin.tar leanote
gzip $tmp/leanote-mac-v0.4.bin.tar
# BLOCK'

View File

@@ -1,5 +1,8 @@
# leanote
moto=your own cloud note!
moto=Not Just A Notebook!
moto2=Knowledge, Sharing, Cooperation, Blog... all in leanote
moto3=Brief But Not Simple
fork github=Fork leanote on Github
# 首页
forgetPassword = Forget password?
@@ -45,6 +48,9 @@ wrongPassword=Wrong password
logining=Sign in
loginSuccess=login success
ing=processing
use = Use
hadAcount = Already have an account?
hasAcount = Do not have an account?
# 注册
registerSuccessAndRdirectToNote=register success, now redirect to my note...
@@ -76,6 +82,7 @@ myNotebook=My notebook
addNotebook=Add notebook
all=Newest
trash=Trash
delete=Delete
unTitled=UnTitled
defaultShare=Default sharing
leftHidden=Hidden slide bar
@@ -123,5 +130,14 @@ uploadImage=Upload image
aboutMe=About me
blogSet=Set blog
# index
discussion=Discussion
download=Download
howToInstallLeanote=How to install leanote
#
attachments = Attachments
donate = Donate
# error
notFound=This page cann't found.

View File

@@ -1,5 +1,8 @@
# leanote
moto=不一样的笔记!
moto=不只是笔记!
moto2=知识管理, 博客, 分享, 协作... 尽在leanote
moto3=简约而不简单
fork github=Github 源码
# 首页
forgetPassword = 忘记密码?
@@ -45,6 +48,9 @@ wrongPassword=密码有误
logining=正在登录
loginSuccess=登录成功, 正在跳转
ing=正在处理
use = 使用
hadAcount = 已有帐户?
hasAcount = 还无帐户?
# 注册
registerSuccessAndRdirectToNote=注册成功, 正在转至我的笔记...
@@ -77,6 +83,7 @@ myNotebook=我的笔记本
addNotebook=添加笔记本
all=最新
trash=废纸篓
delete=删除
unTitled=无标题
defaultShare=默认共享
leftHidden=隐藏左侧
@@ -127,6 +134,14 @@ blogSet=博客设置
# error
notFound=该页面不存在
# index
discussion=社区讨论
download=下载
howToInstallLeanote=leanote安装步骤
#
attachments = 附件
donate = 捐赠
# 必须要加这个, 奇怪

Binary file not shown.

View File

@@ -0,0 +1 @@
{ "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "leanote_public_3.files", "name" : "_id_" } ] }

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