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 地址

因为 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/posthttp://demo.com/post
$.searchUrl 搜索页 url, 如 http://blog.leanote.com/search/用户名http://demo.leanote.com/searchhttp://demo.com/search
$.singleUrl 单面面 url, 如 http://blog.leanote.com/single/用户名http://demo.leanote.com/singlehttp://demo.com/single
$.archivesUrl ($.archiveUrl) 归档页 url, 如 http://blog.leanote.com/archives/用户名http://demo.leanote.com/archiveshttp://demo.com/archives
$.catesUrl 分类页 url, 如 http://blog.leanote.com/cates/用户名
$.cateUrl 分类文章页 url, 如 http://blog.leanote.com/cate/用户名, 或 http://demo.leanote.com/catehttp://demo.com/cate
$.tagsUrl 标签列表页 url, 如 http://blog.leanote.com/tags/用户名http://demo.leanote.com/tagshttp://demo.com/tags
$.tagPostsUrl 标签文章页 url, 如 http://blog.leanote.com/tag/用户名http://demo.leanote.com/taghttp://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>

$.recentPosts 最近发表的 5 篇文章

类型为 []Post, Post 数据结构见 post.html 文章详情页

数据结构

以下结构请注意首字大写!! 如你要在页面显示博客标题, 你需要这样: {{$.blogInfo.Title}}

BlogInfo 博客信息

全局变量为 $.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, ThemeInfo数据结构请查看下章内容 以下包含最基本的, 是否还有其它字段, 这需要依据 theme.json 里的配置.

{
    "Name"     : "leanote-elegant-ing",
    "Desc"     : "一些描述, 或说明安装后还需要做的工作",
    "Version"  : "1.0",
    "Author"   : "leanote.com",
    "AuthorUrl": "http://leanote.com"
    // 可能还有其它配置...
|

总共支持两级分类,其中分类的文章数包含本身以及所有子分类的文章数。

Archive 归档, 按年分类的所有文章

{
    "Year"         : 2012            // 年
    "Posts"        : []Post,         // 文章列表, 数组, 每个元素是Post类型
    "MonthArchives": []MonthArchive  // 按月的归档, MonthArchive类型见下
}

MonthArchive 按月归档, 用于在类型 Archive 内使用

{
    "Month": 12 // 月
    "Posts": []Post, // 文章列表, 数组, 每个元素是Post类型
}

theme.json 主题配置

主题配置采用 JSON 格式

其中 Name, Desc, Version, Author, AuthorUrl 是必填项(注意首字大写)

你也可以定义其它的配置, 如 FriendLinks, 在模板文件使用 $.themeInfo.FriendLinks 来获取值

注意:

  1. JSON 语法严格, 键必须用双引号, 最后不得有空','来结尾
  2. 以下配置不能包含任何注释, 不然解析会出错!
{
    "Name"       : "leanote-elegant-ing",
    "Desc"       : "一些描述, 或说明安装后还需要做的的工作",
    "Version"    : "1.0",
    "Author"     : "leanote.com",
    "AuthorUrl"  : "http://leanote.com",
    "FriendLinks": [
        {"Title": "leanote", "Url": "http://leanote.com"}
    ]
}

下面分析每个页面的其它变量.

index.html 首页

变量 描述
$.posts 类型是 []Post, 文章列表
$.pagingBaseUrl 分页基本 url, 比如 http://demo.leanote.com, $.pagingBaseUrl 在 search.html 也有,只是值不一样

Post 的结构见post.html 文章详情页

cates.html 分类页

变量 描述
$.cates 分类信息

$.cates 的数据结构:

{
    "CateId"   : "1232232",
    "Title"    : "分类标题",
    "UrlTitle" : "友好的url",
    "CateCount": "相应分类的文章数"
    "SubCates" : "[]SubCate"  // 子分类列表,包含子分类的 `CateId`, `Title`, `UrlTitle`, `CateCount`
}

post.html 文章详情页

变量 描述
$.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类型,是否置顶
}

search.html 搜索页

变量 描述
$.keywords 搜索的关键字
$.pagingBaseUrl 分页基本 url
$.posts 类型是 []Post, 文章列表

archive.html 归档页

变量 描述
$.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 月的归档.

single.html 单页面

变量 描述
$.curSingleId 当前单页 id
$.single 类型是 Single, 单页信息

Single数据结构: 注意 这里的 Single 没有 Content

{
    "SingleId"   : "xxxxxxx",    // id
    "Title"      : "标签",
    "UrlTitle"   : "友好的url",
    "Content"    : "内容",
    "CreatedTime": time.Now(),   // golang时间类型 创建时间
    "UpdatedTime": time.Now(),   // golang时间类型 更新时间
}

tags.html 标签列表页

$.tags 是全局变量,类型为 []TagCount

变量 描述
$.tags 类型是[]TagCount

其中 TagCount 的类型: {"Tag": "标签名", "Count": 32} // 文章数

tag_posts.html 标签文章页

变量 描述
$.curTag 当前标签
$.curTagCount 当前标签的 posts 数量
$.pagingBaseUrl 分页基本 url
$.posts 类型是 []Post, 当前分类下的文章列表

404.html 错误页

无特殊变量

golang 模板语法帮助

一些最基本的用法:

  • 输出: {{$.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. 管道

{{参数1| 函数名}}

{{$.post.CreatedTime | datetime}}, 这里 datetime 是一个函数, $.post.CreatedTime作为其参数

  1. 函数调用

{{函数名 参数1 参数2 参数3}}

上面的例子也可以像函数一样这样调用 {{datetime $.post.CreatedTime}}

if else 条件判断

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 循环

{{range $.posts}} {{.Title}} {{end}} 循环输出所有文章标题,也可以在循环中设置计数器:

{{range $index, $p := $.post}}
第 {{$index}} 篇文章:{{$p.Title}}
{{end}}

revel 的自定义函数

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"}}

leanote 的自定义函数

  • 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。
⚠️ **GitHub.com Fallback** ⚠️