leanote ctaoist blog theme api - wiselike/leanote-of-unofficial GitHub Wiki
leanote 博客模板的语法是golang
模板语法, golang
模板语法简洁, 很快就会上手,更具体可参考golang 模板语法帮助;我修改了 leanote 的分类页面的逻辑,具体见 wiki。
所以 leanote 主题的 API 有所变化,重新归纳整理如下:
更具体的大家可以参考我的主题
标准的 leanote 主题模板组织结构如下, 其中header.html
, footer.html
, paging.html
, share_comment.html
, highlight.html
这些仅供其它模板引用, 可以不需要.
- theme.json 主题配置 [必须]
- header.html 头部模板, 供其它模板引用
- footer.html 底部模板, 供其它模板引用
- index.html 首页 [必须]
- post.html 文章详情页 [必须]
- archive.html 归档页 [必须]
- single.html 单页 [必须]
- share_comment.html 分享与评论页, 供 post.html 引用
- highlight.html 代码高亮页, 供其它页面引用, index, cate, search, tag_posts, post
- paging.html 分页, 供其它模板引用
- tags.html 标签列表页 [必须]
- tag_posts.html 标签文章页 [必须]
- cates.html 分类页 [必须]
- cate.html 分类文章页 [必须]
- 404.html 错误页 [必须]
- style.css 样式
- images/ 图片文件夹
- images/screenshot.png 主题预览图
公用变量表示在每个页面都可以使用的变量,也可以说是全局变量:
- 一些公用的 url
- 页面判断变量
-
$.singles
数据结构见单页面列表 -
$.themeInfo
主题信息,详见 ThemeInfo 主题信息 -
$.blogInfo
博客信息 -
$.tags
标签,详见 tags.html 标签列表页 -
$.recentPosts
最近发表的 5 篇文章
因为 leanote 支持二级域名和自定义域名, 所以 url 会根据用户的定义而不同, 这里假设用户设置了http://demo.leanote.com
二级域名, 和 http://demo.com
自定义域名, 而 leanote 默认的博客域名为http://blog.leanote.com
变量 | 描述 |
---|---|
$.siteUrl | 当前站点地址, 比如 http://leanote.com , http://localhost:9000
|
$.indexUrl | 我的博客首页地址, 比如 http://blog.leanote.com/用户名 , http://demo.leanote.com , http://demo.com , 优先级从低到高, 即如果你有自定义域名, 那么就是你的自定义域名 (下同) |
$.postUrl | 文章详情页 url, 如 http://blog.leanote.com/post/用户名 , 或 http://demo.leanote.com/post 或 http://demo.com/post
|
$.searchUrl | 搜索页 url, 如 http://blog.leanote.com/search/用户名 或 http://demo.leanote.com/search 或 http://demo.com/search
|
$.singleUrl | 单面面 url, 如 http://blog.leanote.com/single/用户名 或 http://demo.leanote.com/single 或 http://demo.com/single
|
|
归档页 url, 如 http://blog.leanote.com/archives/用户名 或 http://demo.leanote.com/archives 或 http://demo.com/archives
|
$.catesUrl | 分类页 url, 如 http://blog.leanote.com/cates/用户名
|
$.cateUrl | 分类文章页 url, 如 http://blog.leanote.com/cate/用户名 , 或 http://demo.leanote.com/cate 或 http://demo.com/cate
|
$.tagsUrl | 标签列表页 url, 如 http://blog.leanote.com/tags/用户名 或 http://demo.leanote.com/tags 或 http://demo.com/tags
|
$.tagPostsUrl | 标签文章页 url, 如 http://blog.leanote.com/tag/用户名 或 http://demo.leanote.com/tag 或 http://demo.com/tag
|
$.themeBaseUrl | 主题路径, 用于加载图片, css, js, 如 /public/upload/123232/themes/32323
|
注意
上面的 $.postUrl
, $.searchUrl
, $.singleUrl
, $.catesUrl
, $.cateUrl
, $.tagPostsUrl
, $.themeBaseUrl
是基地址, 还需要在后面加其它信息才能使用, 比如
- 查看一篇文章的链接为
{{$.postUrl}}/相应的文章Id
- 以某关键字进行搜索时
{{$.searchUrl}}?keywords=相应的关键字
- 查看某单页面时
{{$.singleUrl}}/某单页面id
- 查看某分类的文章列表时
{{$.cateUrl}}/某分类
- 查看某标签的文章列表时
{{$.tagPostsUrl}}/某tag
- 引用主题下 style.css 文件时
{{$.themeBaseUrl}}/style.css
leanote 还定义了一些公用的静态文件 url:
变量 | 描述 |
---|---|
$.blogCommonJsUrl | blog 公用 js 地址 |
$.jQueryUrl | jQuery 地址(1.9.0) |
$.fontAwesomeUrl | font awsome 地址 |
$.shareCommentCssUrl | 博客的分享与评论 css 地址 |
$.shareCommentJsUrl | 博客的分享与评论 js 地址 |
$.bootstrapCssUrl | bootstrap css 地址 |
$.bootstrapJsUrl | bootstrap js 地址 |
$.prettifyJsUrl | google code prettify js 地址 |
$.prettifyCssUrl | google code prettify css 地址 |
在 leanote 的 public/blog/js 目录下含有以下 js, 你可以使用/public/blog/js/xx.js
来加载, 如 <script src="/public/blog/js/jquery-cookie-min.js"></script>
- bootstrap-dialog.min.js
- bootstrap-hover-dropdown.js
- jquery-cookie-min.js
- jquery.qrcode.min.js
- jsrender.js
若你还需要其它的 js, css, 你可以在主题下新建 js, css, 到时只需使用 {{$.themeBaseUrl}}/你的.js或css
来加载就行
页面判断用于判断当前页是哪个页面, 比如是否在 index,或 cate 页?
变量 | 描述 |
---|---|
$.curIsIndex | 是否在博客主页 |
$.curIsPost | 否在文章详情页 |
$.curIsSearch | 是否在搜索页 |
$.curIsArchive | 是否在归档页 |
$.curIsSingle | 是否在单页 |
$.curIsCates | 是否在分类页 |
$.curIsCate | 是否在分类文章页 |
$.curIsTags | 是否在标签列表 |
$.curIsTagPosts | 是否在标签文章页 |
比如, 你可以在header.html
中通过判断页面来显示不同的标题:
<title>
{{if $.curIsIndex}}
{{$.blogInfo.Title}}
{{else if $.curIsCates}}
我的分类
{{else if $.curIsCate}}
分类-{{$.curCateTitle}}
{{else if $.curIsSearch}}
搜索-{{$.keywords}}
{{else if $.curIsTags}}
我的标签
{{else if $.curIsTagPosts}}
标签-{{$.curTag}}
{{else if $.curIsPost}}
{{$.post.Title}}
{{else if $.curIsSingle}}
{{$.single.Title}}
{{else if $.curIsArchive}}
归档
{{end}}
</title>
类型为 []Post
, Post
数据结构见 post.html 文章详情页
以下结构请注意首字大写!! 如你要在页面显示博客标题, 你需要这样: {{$.blogInfo.Title}}
全局变量为 $.blogInfo
,其类型是 BlogInfo
,数据结构如下:
{
"UserId" : "用户Id,即博客的作者的用户Id",
"Username" : "用户名, "
"UserLogo" : "用户的Logo, 包含http://",
"Title" : "博客标题"
"SubTitle" : "博客描述",
"Logo" : "博客Logo, 包含http://",
"OpenComment": true, // 是否开启评论 交给主题配置来判断会更好
"CommentType": "leanote" // 评论系统类型, 分为 leanote, or disqus
"DisqusId" : "leanote", // Disqus评论系统id
"ThemeId" : "xxxxxxxx", // 主题Id
"SubDomain" : "leanote的二级域名, 如demo",
"Domain" : "http://demo.com 自定义域名"
}
全局变量为 $.themeInfo
,类型为ThemeInfo
, ThemeInfo
数据结构请查看下章内容
以下包含最基本的, 是否还有其它字段, 这需要依据 theme.json 里的配置.
{
"Name" : "leanote-elegant-ing",
"Desc" : "一些描述, 或说明安装后还需要做的工作",
"Version" : "1.0",
"Author" : "leanote.com",
"AuthorUrl": "http://leanote.com"
// 可能还有其它配置...
|
总共支持两级分类,其中分类的文章数包含本身以及所有子分类的文章数。
{
"Year" : 2012 // 年
"Posts" : []Post, // 文章列表, 数组, 每个元素是Post类型
"MonthArchives": []MonthArchive // 按月的归档, MonthArchive类型见下
}
{
"Month": 12 // 月
"Posts": []Post, // 文章列表, 数组, 每个元素是Post类型
}
主题配置采用 JSON 格式
其中 Name, Desc, Version, Author, AuthorUrl 是必填项(注意首字大写)
你也可以定义其它的配置, 如 FriendLinks, 在模板文件使用 $.themeInfo.FriendLinks 来获取值
注意:
- JSON 语法严格, 键必须用双引号, 最后不得有空','来结尾
- 以下配置不能包含任何注释, 不然解析会出错!
{
"Name" : "leanote-elegant-ing",
"Desc" : "一些描述, 或说明安装后还需要做的的工作",
"Version" : "1.0",
"Author" : "leanote.com",
"AuthorUrl" : "http://leanote.com",
"FriendLinks": [
{"Title": "leanote", "Url": "http://leanote.com"}
]
}
下面分析每个页面的其它变量.
变量 | 描述 |
---|---|
$.posts | 类型是 []Post, 文章列表 |
$.pagingBaseUrl | 分页基本 url, 比如 http://demo.leanote.com , $.pagingBaseUrl 在 search.html 也有,只是值不一样 |
Post
的结构见post.html 文章详情页
变量 | 描述 |
---|---|
$.cates | 分类信息 |
$.cates
的数据结构:
{
"CateId" : "1232232",
"Title" : "分类标题",
"UrlTitle" : "友好的url",
"CateCount": "相应分类的文章数"
"SubCates" : "[]SubCate" // 子分类列表,包含子分类的 `CateId`, `Title`, `UrlTitle`, `CateCount`
}
变量 | 描述 |
---|---|
$.post | 类型是 Post, 当前文章信息 |
$.prePost | 类型是 Post, 上一篇文章, 无 Content 信息 |
$.nextPost | 类型是 Post, 下一篇文章, 无 Content 信息 |
Post
的数据结构:
{
"NoteId" : "1232323" // 一篇文件也是笔记, 所以使用NoteId作为主键
"Title" : "标题",
"UrlTitle" : "友好的Url",
"ImgSrc" : "文章主图url地址, 包含http",
"CreatedTime": time.Now(), // golang时间类型 创建时间
"UpdatedTime": time.Now(), // golang时间类型 更新时间
"PublicTime" : time.Now(), // golang时间类型 发布时间
"Desc" : "文章的描述, 纯Text",
"Abstract" : "文章的摘要, 有HTML",
"Content" : "文章内容",
"Tags" : []string{"标签1", "标签2"}, // 数组, 元素是标签名
"Cates: []string{"分类", "子分类", "子分类的子分类"} // 数组 笔记所属的分类列表
"CommentNum": 1232, // 评论数量
"ReadNum": 32, // 阅读量
"LikeNum": 33, // 赞的量
"IsMarkdown": false, // bool类型, 是否是markdown文章, 如果是, 那么Conent就是markdown内容, 不是html内容
"IsTop": false, // bool类型,是否置顶
}
变量 | 描述 |
---|---|
$.keywords | 搜索的关键字 |
$.pagingBaseUrl | 分页基本 url |
$.posts | 类型是 []Post, 文章列表 |
变量 | 描述 |
---|---|
$.archives | 类型是 []Archive |
$.curCateTitle | 当前分类标题, 如果有传 cateId, 那么可按分类来显示归档 |
$.curCateId | 当前分类 id, 如果有传 cateId, 那么可按分类来显示归档 |
可以按年来显示文章, 也可以按月
按年:
<ul>
{{range $.archives}}
<li><span class="archive-year">{{.Year}}</span>
<ul>
{{range .Posts}}
<li>
{{dateFormat .PublicTime "2006-01-02"}} <a href="{{$.postUrl}}/{{.NoteId}}">{{.Title}}</a>
</li>
{{end}}
</ul>
</li>
{{end}}
</ul>
按月:
<ul>
{{range $.archives}}
<li><span class="archive-year">{{.Year}}</span>
<ul>
{{range .MonthAchives}}
<li>
<span class="archive-month">{{.Month}}</span>
<ul>
{{range .Posts}}
<li>
{{dateFormat .PublicTime "2006-01-02"}} <a href="{{$.postUrl}}/{{.NoteId}}">{{.Title}}</a>
</li>
{{end}}
</ul>
</li>
{{end}}
</ul>
</li>
{{end}}
</ul>
归档页还可以按分类, 年, 月来查询, 比如 url: {{$.archiveUrl}}?year=2014&month=11&cateId=xxxxxx
表示查询分类为 xxxxxx, 2014 年 11 月的归档.
变量 | 描述 |
---|---|
$.curSingleId | 当前单页 id |
$.single | 类型是 Single, 单页信息 |
Single
数据结构: 注意 这里的 Single 没有 Content
{
"SingleId" : "xxxxxxx", // id
"Title" : "标签",
"UrlTitle" : "友好的url",
"Content" : "内容",
"CreatedTime": time.Now(), // golang时间类型 创建时间
"UpdatedTime": time.Now(), // golang时间类型 更新时间
}
$.tags
是全局变量,类型为 []TagCount
变量 | 描述 |
---|---|
$.tags | 类型是[]TagCount |
其中 TagCount
的类型: {"Tag": "标签名", "Count": 32} // 文章数
变量 | 描述 |
---|---|
$.curTag | 当前标签 |
$.curTagCount | 当前标签的 posts 数量 |
$.pagingBaseUrl | 分页基本 url |
$.posts | 类型是 []Post, 当前分类下的文章列表 |
无特殊变量
一些最基本的用法:
- 输出:
{{$.blogInfo.UserId}}
表示输出blogInfo.UserId
变量, 比如<span>{{$.blogInfo.UserId}}</span>
- 判断:
{{if $.blogInfo.OpendComment}} 为真的处理 {{else}} 为假为处理 {{end}}
- range 循环:
{{range $.posts}} {{.Title}} {{end}}
range 循环输出所有文章标题 - 调用函数:
{{$.post.CreatedTime|datetime}}
使用 datetime 函数来模式化时间, 会输出类似2014-11-5 12:33:22
的数据 - 注释:
{{/* 注释 */}}
$
表示根上下文, 推荐在每个变量前加$
, 这样当上下文改变时还可以正确获取想要的值, 如{{$.post.Title}}
.
而.
表示当前的上下文, 上下文会因 {{range}}, {{for}}而改变, 比如在index.html
页:
{{range $.posts}}
{{.Title}} <!-- 输出每个post的标题, 这里的.就表示当前的post -->
{{end}}
$
表示根上下文和.
表示当前上下文感觉都是相对于 template 文件来说的,最顶层就是当前 template 文件这一层。
使用 $var
来访问创建的变量,{{ $count := 1}}
这也可以创建变量,赋值:{{ $count = 2 }}
一个模板可以包含其它模板, 比如在 index.html
中
{{template "header.html" $}}
注:这里还需要传递上下文
$
, 不然在header.html
中不能引用变量.
有两种方法可以调用函数, 1) 管道 2) 函数调用
推荐使用函数调用的方式, 比管道更容易理解.
golang 官方文档上很详细:
{{printf "%q" "output"}} A function call. 函数调用
{{"output" | printf "%q"}}
A function call whose final argument comes from the previous command
printf有两个参数, 参数1为"%q", 参数2为"output". 相当于 {{printf "%q" "output"}}
{{printf "%q" (print "out" "put")}}
A parenthesized argument. 使用()来嵌套函数调用
下面是更复杂的例子
{{"put" | printf "%s%s" "out" | printf "%q"}}
A more elaborate call.
{{"output" | printf "%s" | printf "%q"}}
A longer chain.
- 管道
{{参数1| 函数名}}
{{$.post.CreatedTime | datetime}}
, 这里 datetime 是一个函数, $.post.CreatedTime
作为其参数
- 函数调用
{{函数名 参数1 参数2 参数3}}
上面的例子也可以像函数一样这样调用
{{datetime $.post.CreatedTime}}
if 是一个值, 这个值可以是变量, bool, string, object 都行, 字符串不空为值, object 不为 nil 为值, 值也可以是表达式.
如果值是变量, 在header.html
中有一段经典的 if else 条件判断:
<title>
{{if $.curIsIndex}}
{{$.blogInfo.Title}}
{{else if $.curIsCate}}
分类-{{$.curCateTitle}}
{{else if $.curIsPost}}
{{$.post.Title}}
{{else}}
我的博客
{{end}}
如果值是表达式, 在share_comment.html
中有一段复杂的例子:
{{if and $.blogInfo.OpenComment (eq $.blogInfo.CommentType "disqus")}}
{{end}}
golang 模板可以使用eq
, lt
, le
, gt
, ge
之类的来作比较, 也有and
, or
, 需要注意的是 eq
, lt
, gt
, and
, or
全是函数. 上面这个例子相当于伪代码:
if ($.blogInfo.OpenComment && $.blogInfo.CommentType == "disqus")
{{range $.posts}} {{.Title}} {{end}}
循环输出所有文章标题,也可以在循环中设置计数器:
{{range $index, $p := $.post}}
第 {{$index}} 篇文章:{{$p.Title}}
{{end}}
Leanote 使用的是 revel 框架,其本身添加了一些模板函数。抄录几个觉得有用的,更多详见 revel template 文档
-
append 把一个变量添加进一个数组,或者创建一个数组,如
{{append . "lst", 1}}
- checkbox
-
datetime, date, timeago 日期时间格式化, 如
{{$.post.UpdatedTime | datetime}}
{{$.post.UpdatedTime | date}}
-
even 判断是否是偶数,如
{{if even $index}}light-row{{else}}dark-row{{end}}
-
raw 显示原生 html, 如
{{$.post.Content | raw}}
-
set 给变量赋值,如
{{set . "title" "Basic Chat room"}}
- blogTags 输出标签, 如
{{blogTags $ .Tags}}
- dateFormat 自定义日期时间格式化, 如
{{dateFormat .PublicTime "2006-01-02"}}
- unixDatetime unix 时间戳格式化成日期时间,如
{{unixDatetime "1639198011"}}
,得到2021-12-11 12:46:51
- trim 去除前后的换行空格,如
{{trim .post.Content}}
- add, sub, incr 加减运算, 如
{{incr .curTagCount -1}}
- concat, concatStr 连接字符串,如
{{concat "Hello" " World"}}
,concatStr
的参数数量没有限制 - N 与 range 搭配可以实现循环固定次数,如
{{range $i := N 1 10}} {{$i}} {{end}}
,$i
的取值是从 1 到 10。