Merge branch 'develop'

admin [init ok]
lea++ blog platform [ok]
This commit is contained in:
life
2014-09-24 22:24:52 +08:00
parent 99956cfd72
commit 87269cc939
409 changed files with 28646 additions and 18 deletions

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

@@ -4,7 +4,7 @@ import (
"github.com/revel/revel"
// "encoding/json"
"gopkg.in/mgo.v2/bson"
// . "leanote/app/lea"
// . "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
@@ -90,7 +89,7 @@ func (c Blog) Index(userId string, notebookId string) revel.Result {
c.RenderArgs["page"] = page
c.RenderArgs["pageSize"] = blogPageSize
c.RenderArgs["count"] = count
// 当前notebook
c.RenderArgs["notebookId"] = notebookId
c.RenderArgs["notebook"] = notebook

View File

@@ -0,0 +1,52 @@
package controllers
import (
"github.com/revel/revel"
// "encoding/json"
. "github.com/leanote/leanote/app/lea"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "math"
// "os"
// "path"
)
// lea++博客平台
type Lea struct {
BaseController
}
// 进入某个用户的博客
func (c Lea) Index(tag, keywords string) revel.Result {
c.RenderArgs["nav"] = "recommend"
return c.p(tag, keywords, true)
}
func (c Lea) Latest(tag, keywords string) revel.Result {
c.RenderArgs["nav"] = "latest"
return c.p(tag, keywords, false);
}
func (c Lea) p(tag, keywords string, recommend bool) revel.Result {
var tags = []string{}
if recommend {
tags = configService.GetGlobalArrayConfig("recommendTags")
} else {
tags = configService.GetGlobalArrayConfig("newTags")
}
// 如果不在所在的tag就不能搜索
if !InArray(tags, tag) {
tag = ""
}
c.RenderArgs["tag"] = tag
page := c.GetPage()
pageInfo, blogs := blogService.ListAllBlogs(tag, keywords, recommend, page, 10, "UpdatedTime", false)
c.RenderArgs["pageInfo"] = pageInfo
c.RenderArgs["blogs"] = blogs
c.RenderArgs["tags"] = tags
c.RenderArgs["keywords"] = keywords
return c.RenderTemplate("lea/index.html");
}

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

@@ -24,6 +24,7 @@ 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"
@@ -116,6 +117,7 @@ func InitService() {
pwdService = service.PwdS
suggestionService = service.SuggestionS
authService = service.AuthS
configService = service.ConfigS
}
func init() {

View File

@@ -39,6 +39,7 @@ var Files *mgo.Collection
var Attachs *mgo.Collection
var NoteImages *mgo.Collection
var Configs *mgo.Collection
// 初始化时连接数据库
func Init() {
@@ -110,6 +111,8 @@ func Init() {
Attachs = Session.DB(dbname).C("attachs")
NoteImages = Session.DB(dbname).C("note_images")
Configs = Session.DB(dbname).C("configs")
}
func init() {

View File

@@ -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`
}

View File

@@ -17,9 +17,11 @@ 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

View File

@@ -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"
)
@@ -10,4 +11,12 @@ type Page struct {
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

@@ -5,11 +5,13 @@ import (
. "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"
)
@@ -38,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)
@@ -78,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 {
@@ -114,6 +190,51 @@ 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() {
@@ -121,5 +242,7 @@ func init() {
service.InitService()
controllers.InitService()
admin.InitService()
service.ConfigS.InitGlobalConfigs()
})
}

View File

@@ -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,85 @@ 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}
if tag != "" {
query["Tags"] = bson.M{"$in": []string{tag}}
}
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
}
//------------------------
// 博客设置
@@ -152,4 +226,12 @@ 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
}

View File

@@ -99,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 {
@@ -135,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 // 原先的, 可能是同一个, 但有大小写
@@ -296,4 +319,25 @@ 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

@@ -21,6 +21,7 @@ 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
@@ -40,6 +41,7 @@ func InitService() {
FileS = &FileService{}
AlbumS = &AlbumService{}
AttachS = &AttachService{}
ConfigS = &ConfigService{}
PwdS = &PwdService{}
SuggestionS = &SuggestionService{}
AuthS = &AuthService{}
@@ -57,4 +59,5 @@ func InitService() {
fileService = FileS
albumService = AlbumS
attachService = AttachS
configService = ConfigS
}

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, your own cloud note!">
<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

@@ -0,0 +1,7 @@
<div id="footer">
<a href="/lea/index">lea++</a>, leanote博客平台.
<a href="/index">leanote</a> © 2014
</div>
</body>
</html>

48
app/views/Lea/header.html Normal file
View File

@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="lea++, leanote,leanote.com">
<meta name="description" content="lea++, leanote blog platform. leanote, your own cloud note!">
<meta name="author" content="leanote">
<link href="/css/bootstrap.css" rel="stylesheet">
<link href="/css/font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet"/>
<link href="/css/blog/p.css" rel="stylesheet">
<title>lea++, leanote博客平台</title>
<script>
function log(o) {
}
</script>
<style>
</style>
</head>
<body>
<div id="header">
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/lea/index">
<span id="logo"></span>
<span id="subTitle" title="lea++, leanote博客平台">lea++</span>
</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<form class="navbar-form navbar-right">
<input type="text" id="keywords" name="keywords" value="{{raw .keywords}}" class="form-control" placeholder="Search...">
</form>
<ul class="nav navbar-nav navbar-left">
<li {{if eq .nav "recommend"}}class="active"{{end}}><a href="/lea/index">推荐</a></li>
<li {{if eq .nav "latest"}}class="active"{{end}}><a href="/lea/latest">最新</a></li>
</ul>
</div>
</div>
</nav>
</div>

68
app/views/Lea/index.html Normal file
View File

@@ -0,0 +1,68 @@
{{template "lea/header.html" .}}
{{if eq .nav "recommend"}}
{{set $ "baseUrl" "/lea/index" }}
{{else}}
{{set $ "baseUrl" "/lea/latest" }}
{{end}}
<div id="content">
<!-- 分类 -->
<ul class="clearfix sort-nav">
<li {{if not .tag}}class="active"{{end}}>
<a class="category" href="{{$.baseUrl}}">全部</a>
</li>
{{range $v := .tags}}
<li {{if eq $v $.tag}}class="active"{{end}}>
<a class="category" data-category-id="{{$v}}" href="{{$.baseUrl}}?tag={{$v}}">{{$v}}</a>
</li>
{{end}}
</ul>
<!-- 文章列表 -->
<ul class="thumbnails">
{{range .blogs}}
<li>
<div class="article">
<a class="title" href="/blog/view/{{.NoteId.Hex}}" title="{{.Title}} {{msg $ "fullBlog"}}" target="_blank">{{.Title}}</a>
<a class="avatar maleskine-author" href="/blog/{{.UserId.Hex}}" target="_blank" title="{{.User.Username}}">
{{ if .User.Logo}}
<img src="{{.User.Logo}}">
{{else}}
<img src="/images/blog/default_avatar.png">
{{end}}
</a>
<div class="content">
{{.Desc | raw}}
</div>
<div class="article-info">
<a class="author" href="/blog/{{.User.Username}}">
<span class="fa fa-user"></span>
{{.User.Username}}
</a>
<!--
<span class="fa fa-comments-o"></span>9
<span class="fa fa-heart-o"></span>6
-->
<span class="fa fa-bookmark-o"></span>
{{if .Tags}}
{{blogTags .Tags}}
{{else}}
{{msg $ "noTag"}}
{{end}}
</div>
</div>
</li>
{{end}}
</ul>
<div id="pagination" class="clearfix">
{{if eq .nav "recommend"}}
{{set . "url" (urlConcat "/lea/index" "keywords" .keywords)}}
{{else}}
{{set . "url" (urlConcat "/lea/latest" "keywords" .keywords)}}
{{end}}
{{template "lea/page.html" .}}
</div>
</div>
{{template "lea/footer.html" .}}

33
app/views/Lea/page.html Normal file
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}}