Typecho博客模板制作手册
文件结构说明
文件名 | 作用 | 必须 |
---|---|---|
style.css | 主题样式文件 | 否 |
screenshot.png | 主题缩略图,图片后缀支持 jpg,png,gif,bmp,jpeg | 否 |
index.php | 首页以及说明文件 | 是 |
404.php | 404 页面文件 | 否 |
archive.php | 通用(分类、搜索、标签、作者)页面文件 | 否 |
category.php | 分类页面文件 | 否 |
search.php | 搜索页面文件 | 否 |
tag.php | 标签页面文件 | 否 |
author.php | 作者页面文件 | 否 |
comments.php | 评论页面文件 | 否 |
footer.php | 底部页面文件 | 否 |
functions.php | 主题函数文件 | 否 |
header.php | 头部页面文件 | 否 |
page.php | 独立页面文件 | 否 |
post.php | 日志页面文件 | 否 |
sidebar.php | 侧边栏页面文件 | 否 |
index.php
模板信息
我们先从主文件说起,打开这个文件,首先看到的是注释:
/**
* 这是typecho系统的一套默认皮肤
* @package Typecho Default Theme
* @author typecho
* @version 1.0.0
* @link http://typecho.org
*/
这是模板信息存放的地方,格式是PHP 注释,这个备注会被显示在主题切换页面。前两行是简短的介绍,每个“*”表示一个段落开始。
- @package 表示模板名
- @author表示作者名
- @version是模板的版本号
- @link是作者的网站连接
你会看到这个文件里有三个调用$this->need()
方法的地方
<?php
...
$this->need('header.php');
...
$this->need('sidebar.php');
...
$this->need('footer.php');
这些语句用来调用模板的其它 PHP 文件,语法是need(PHP文件相对路径)
,相对于主题的路径。这个方法实际调用的是require
方法,意味着模块是可以重复调用的。
显示文章列表
<?php while ($this->next()): ?>
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
<h2 class="post-title" itemprop="name headline">
<a itemprop="url"
href="https://www.lvtao.net/url.html?t=Jmx0Oz9waHAgJHRoaXMtJmd0O3Blcm1hbGluaygpID8mZ3Q7" target="_blank"><?php $this->title() ?></a>
</h2>
<ul class="post-meta">
<li itemprop="author" itemscope itemtype="http://schema.org/Person"><?php _e('作者: '); ?><a
itemprop="name" href="<?php $this->author->permalink(); ?>"
rel="author"><?php $this->author(); ?></a></li>
<li><?php _e('时间: '); ?>
<time datetime="<?php $this->date('c'); ?>" itemprop="datePublished"><?php $this->date(); ?></time>
</li>
<li><?php _e('分类: '); ?><?php $this->category(','); ?></li>
<li itemprop="interactionCount">
<a itemprop="discussionUrl"
href="<?php $this->permalink() ?>#comments"><?php $this->commentsNum('评论', '1 条评论', '%d 条评论'); ?></a>
</li>
</ul>
<div class="post-content" itemprop="articleBody">
<?php $this->content('- 阅读剩余部分 -'); ?>
</div>
</article>
<?php endwhile; ?>
实际上在文章数据在$this
对象里是以数组存起来的,所以必须用循环语句遍历才可以输出所有文章。
代码 | 解释 |
---|---|
<?php if ($this->have()): ?> | 判断是否有文章 |
<?php while($this->next()): ?> | while 循环语句,PHP 语法,必须与<?php endwhile; ?> 对应,$this->next()是判断有没有下篇文章,有返回true ,并且让数据下标+1,没有返回false |
<?php $this->permalink() ?> 当前 | 文章所在的连接 |
<?php $this->title() ?> | 文章标题 |
<?php $this->author(); ?> | 文章作者 |
<?php $this->author->permalink(); ?> | 文章作者地址 |
<?php $this->date(); ?> | 文章的发布日期,日期格式可在 Typecho 后台设置->阅读中设置,或者使用参数 1 强制指定日期格式<?php $this->date('Y-m-d'); ?> |
<?php $this->category(','); ?> | 文章所在分类,参数 1 是分隔符 |
<?php $this->tags(','); ?> | 文章标签,参数 1 是分隔符 |
<?php $this->commentsNum('评论', '1 条评论', '%d 条评论'); ?> | 文章评论数及连接,参数可以指定多个,不指定直接输出数字,指定三个主要是某些语言 0,单数,复数的后缀不一样,有的可能甚至更复杂 |
<?php $this->content('- 阅读剩余部分 -'); ?> | 文章内容,其中的“- 阅读剩余部分 -”是显示摘要时隐藏部分的邀请链接,也可使用<?php $this->excerpt(140, '...'); ?> 来进行自动截取文字内容,“140”是截取字符数量 |
文章分页
<?php $this->pageNav('«前一页', '后一页»'); ?>
也可以这样分开写
<?php $this->pageLink('下一页','next'); ?>
<?php $this->pageLink('上一页'); ?>
其他说明
archive.php
代码码类似index.php
,区别就是index.php
是显示首页的,而archive.php
是用于显示归档页面(分类,标签,搜索,作者),如果主题中没有没有archive.php
则会自动调用index.php
作为归档页。
header.php
编码
打开这个文件,见到的第一个 php 代码就是:
<meta charset="<?php $this->options->charset(); ?>">
页面标题
<?php $this->archiveTitle(array(
'category' => _t('分类 %s 下的文章'),
'search' => _t('包含关键字 %s 的文章'),
'tag' => _t('标签 %s 下的文章'),
'author' => _t('%s 发布的文章')
), '', ' - '); ?><?php $this->options->title(); ?>
<?php $this->archiveTitle(); ?>
是当前页面的标题,<?php $this->options->title(); ?>
是网站的标题。archiveTitle()
接收第一个参数用于格式化标题,格式是键值对(PHP 数组就是键值对),键名表示归档页类型,值则是标题模板,Typecho 会自动替换%s
为归档关键字。
导入样式
<link rel="stylesheet" href="<?php $this->options->themeUrl('style.css'); ?>">
其中 style.css 是样式表文件相对模板目录的路径和文件名。
其它 HTML 头部信息
<?php $this->header(); ?>
这是 Typecho 的自有函数,会输出 HTML 头部信息;同时这个也是头部插件接口,有了它插件可以向网站头部插入 css 或者 js 代码。
网站名称与 logo
<?php if ($this->options->logoUrl): ?>
<a id="logo" href="<?php $this->options->siteUrl(); ?>">
<img
src="<?php $this->options->logoUrl() ?>"
alt="<?php $this->options->title() ?>"
/>
</a>
<?php else: ?>
<a id="logo" href="<?php $this->options->siteUrl(); ?>"
><?php $this->options->title() ?></a
>
<p class="description"><?php $this->options->description() ?></p>
<?php endif; ?>
第一句的 if 判断是判断模板是否通过模板设置设置了 logo 的地址,如果设置了就显示 logo 图片,否则就显示博客标题。<?php $this->options->siteUrl(); ?>
是网站地址<?php $this->options->title() ?>
是网站名字<?php $this->options->description() ?>
是网站描述。
Logo 部分的讲解将会在functions.php章节中详细讲解。
站内搜索
<form id="search" method="post" action="<?php $this->options->siteUrl(); ?>" role="search">
<label for="s" class="sr-only"><?php _e('搜索关键字'); ?></label>
<input type="text" id="s" name="s" class="text" placeholder="<?php _e('输入关键字搜索'); ?>"/>
<button type="submit" class="submit"><?php _e('搜索'); ?></button>
</form>
当你的文章很多很多,这个搜索就必不可少。美化搜索框不要动 form action 和 input name,action 和 name 是必须这么写的。
页面导航
<nav id="nav-menu" class="clearfix" role="navigation">
<a<?php if ($this->is('index')): ?> class="current"<?php endif; ?>
href="<?php $this->options->siteUrl(); ?>"><?php _e('首页'); ?></a>
<?php \Widget\Contents\Page\Rows::alloc()->to($pages); ?>
<?php while ($pages->next()): ?>
<a<?php if ($this->is('page', $pages->slug)): ?> class="current"<?php endif; ?>
href="<?php $pages->permalink(); ?>"
title="<?php $pages->title(); ?>"><?php $pages->title(); ?></a>
<?php endwhile; ?>
</nav>
其中<?php $this->options->siteUrl(); ?>
是网站地址,\Widget\Contents\Page\Rows::alloc()
是获取所有页面,然后下面的 while 循环是循环输出独立页面的,其中<?php $pages->permalink(); ?>
是独立页面的超链接,<?php $pages->title(); ?>
是独立页面的标题。
footer.php
页脚文件,推荐大家把一些较大的 js 放在这个文件中最后载入,不会影响阅读。看看我们的 footer 要讲解些什么?
版权声明等
© <?php echo date('Y'); ?> <a href="<?php $this->options->siteUrl(); ?>"><?php $this->options->title(); ?></a>.
<?php _e('由 <a href="http://www.typecho.org">Typecho</a> 强力驱动'); ?>
<?php echo date('Y'); ?>
是当前年份<?php $this->options->siteUrl(); ?>
是网站地址<?php $this->options->title(); ?>
是网站标题。
插件接口
<?php $this->footer(); ?>
用于插件向页脚插入 css,js 文件等。
sidebar.php
最新文章列表
<ul class="widget-list">
<?php \Widget\Contents\Post\Recent::alloc()
->parse('<li><a href="{permalink}">{title}</a></li>'); ?>
</ul>
获取最新的 N 篇文章标题,得到的 html 是
<ul class="widget-list">
<li>
<a href="http://example.com/2008/12/31/sample-post-one">文章1的标题</a>
</li>
<li>
<a href="http://example.com/2008/12/31/sample-post-two">文章2的标题</a>
</li>
<!-- 省略n个重复 -->
<li>
<a href="http://example.com/2008/12/31/sample-post-ten">文章N的标题</a>
</li>
</ul>
N 的值可以在后台 设置 → 阅读 → 文章列表数目 设置
最新回复列表
<ul class="widget-list">
<?php \Widget\Comments\Recent::alloc()->to($comments); ?>
<?php while ($comments->next()): ?>
<li>
<a href="<?php $comments->permalink(); ?>"><?php $comments->author(false); ?></a>: <?php $comments->excerpt(35, '...'); ?>
</li>
<?php endwhile; ?>
</ul>
获取最新的 N 个回复,得到的 html 是
<ul class="widget-list">
<li>
回复人名字:
<a href="http://example.com/2008/12/31/sample-post#comments-12"
>回复的内容...</a
>
</li>
<li>
回复人名字:
<a href="http://example.com/2008/12/31/sample-post#comments-11"
>回复的内容...</a
>
</li>
<!-- 省略n个重复 -->
</ul>
其中<?php $comments->excerpt(35, '...'); ?>
,“35”代表要回复内容截取的字的个数,“…”代表省略的意思,你可以自行修改。
N 的值可以在后台 设置 → 评论 → 评论列表数目 设置
文章分类列表
<?php \Widget\Metas\Category\Rows::alloc()->listCategories('wrapClass=widget-list'); ?>
效果如下
<ul class="widget-list">
<li class="category-level-0 category-parent">
<a href="分类1链接">分类1</a>
</li>
<li class="category-level-0 category-parent">
<a href="分类2链接">分类2</a>
</li>
<!-- 省略n个重复 -->
</ul>
如果有个分类 3,分类 4 是上述分类 2 的子分类,那么效果如下
<ul class="widget-list">
<li class="category-level-0 category-parent">
<a href="分类1链接">分类1</a>
</li>
<li class="category-level-0 category-parent">
<a href="分类2链接">分类2</a>
<ul class="widget-list">
<li class="category-level-1 category-child category-level-odd">
<a href="分类3链接">分类3</a>
</li>
<li class="category-level-1 category-child category-level-odd">
<a href="分类4链接">分类4</a>
</li>
</ul>
</li>
<!-- 省略n个重复 -->
</ul>
按月归档
<ul class="widget-list">
<?php \Widget\Contents\Post\Date::alloc('type=month&format=F Y')
->parse('<li><a href="{permalink}">{date}</a></li>'); ?>
</ul>
输出:
<ul class="widget-list">
<li><a href="http://example.com/2018/11">November 2018</a></li>
<li><a href="http://example.com/2018/10">October 2018</a></li>
</ul>
format
是日期格式,这是 PHP 日期 format。type
是归档类型,可选值有:year
、month
、day
登陆登出
<?php if($this->user->hasLogin()): ?>
<li class="last">
<a href="<?php $this->options->adminUrl(); ?>"
><?php _e('进入后台'); ?>
(<?php $this->user->screenName(); ?>)</a
>
</li>
<li>
<a href="<?php $this->options->logoutUrl(); ?>"><?php _e('退出'); ?></a>
</li>
<?php else: ?>
<li class="last">
<a href="<?php $this->options->adminUrl('login.php'); ?>"
><?php _e('登录'); ?></a
>
</li>
<?php endif; ?>
这些是可有可无的,只是为了方便登录登出。
<?php $this->options->adminUrl(); ?>
是后台地址<?php $this->user->screenName(); ?>
用户昵称<?php $this->options->logoutUrl(); ?>
登出链接<?php $this->options->adminUrl('login.php'); ?>
登陆链接。
RSS 地址
<a href="<?php $this->options->feedUrl(); ?>"><?php _e('文章 RSS'); ?></a>
<!-- 文章的RSS地址连接 -->
<a href="<?php $this->options->commentsFeedUrl(); ?>"
><?php _e('评论 RSS'); ?></a
><!-- 评论的RSS地址连接 -->
post.php
post 页和 index 是差不多的,下面解释下 post.php 里面存在的 php 代码。
代码与说明
代码 | 解释 |
---|---|
<?php $this->permalink() ?> | 文章地址 |
<?php $this->title() ?> | 文章标题 |
<?php $this->author->permalink(); ?> | 文章作者主页链接 |
<?php $this->author(); ?> | 文章作者昵称 |
<?php $this->date(); ?> | 文章发布时间 |
<?php $this->category(','); ?> | 文章分类,多个分类中间用逗号隔开 |
<?php $this->content(); ?> | 文章内容 |
<?php $this->tags(', ', true, 'none'); ?> | 文章标签,多个标签间用逗号隔开,标签以带超链接的形式显示,如果不存在标签则显示 none |
<?php $this->need('comments.php'); ?> | 调用评论页 |
<?php $this->thePrev('%s','没有了'); ?> | 带有超链接的上一篇文章的标题 |
<?php $this->theNext('%s','没有了'); ?> | 带有超链接的下一篇文章的标题 |
<?php $this->fields->fieldName(); ?> | 自定义字段fieldName 的值,替换fieldName 为对应自定义字段名即可 |
其他说明
page.php 代码同 post.php,区别就是 post 是用来显示文章的,而 page.php 是用来显示独立页面的。
comments.php
评论列表
<?php $this->comments()->to($comments); ?>
<?php if ($comments->have()): ?>
<h3><?php $this->commentsNum(_t('暂无评论'), _t('仅有一条评论'), _t('已有 %d 条评论')); ?></h3>
<?php $comments->listComments(); ?>
<?php $comments->pageNav('« 前一页', '后一页 »'); ?>
<?php endif; ?>
判断文章是否存在评论,如果存在就输出评论;其中<?php $comments->listComments(); ?>
是评论列表,<?php $comments->pageNav('« 前一页', '后一页 »'); ?>
是评论翻页按钮。
评论输入表单
<!-- 判断设置是否允许对当前文章进行评论 -->
<?php if($this->allow('comment')): ?>
<div id="<?php $this->respondId(); ?>" class="respond">
<div class="cancel-comment-reply"><?php $comments->cancelReply(); ?></div>
<h3 id="response"><?php _e('添加新评论'); ?></h3>
<!-- 输入表单开始 -->
<form
method="post"
action="<?php $this->commentUrl() ?>"
id="comment-form"
role="form"
>
<!-- 如果当前用户已经登录 -->
<?php if($this->user->hasLogin()): ?>
<!-- 显示当前登录用户的用户名以及登出连接 -->
<p>
<?php _e('登录身份: '); ?><a href="<?php $this->options->profileUrl(); ?>"
><?php $this->user->screenName(); ?></a
>.
<a href="<?php $this->options->logoutUrl(); ?>" title="Logout"
><?php _e('退出'); ?>
»</a
>
</p>
<!-- 若当前用户未登录 -->
<?php else: ?>
<!-- 要求输入名字、邮箱、网址 -->
<p>
<label for="author" class="required"><?php _e('称呼'); ?></label>
<input
type="text"
name="author"
id="author"
class="text"
value="<?php $this->remember('author'); ?>"
required
/>
</p>
<p>
<label for="mail" <?php if ($this-
>options->commentsRequireMail): ?> class="required"<?php endif; ?>><?php _e('Email'); ?></label
>
<input
type="email"
name="mail"
id="mail"
class="text"
value="<?php $this->remember('mail'); ?>"
<?php
if
($this-
/>options->commentsRequireMail): ?> required<?php endif; ?>
/>
</p>
<p>
<label for="url" <?php if ($this-
>options->commentsRequireURL): ?> class="required"<?php endif; ?>><?php _e('网站'); ?></label
>
<input
type="url"
name="url"
id="url"
class="text"
placeholder="<?php _e('http://'); ?>"
value="<?php $this->remember('url'); ?>"
<?php
if
($this-
/>options->commentsRequireURL): ?> required<?php endif; ?>
/>
</p>
<?php endif; ?>
<!-- 输入要回复的内容 -->
<p>
<label for="textarea" class="required"><?php _e('内容'); ?></label>
<textarea
rows="8"
cols="50"
name="text"
id="textarea"
class="textarea"
required
>
<?php $this->remember('text'); ?></textarea
>
</p>
<p>
<button type="submit" class="submit"><?php _e('提交评论'); ?></button>
</p>
</form>
</div>
<!-- 若当前文章不允许进行评论 -->
<?php else: ?>
<h3><?php _e('评论已关闭'); ?></h3>
<?php endif; ?>
具体请对应上述代码中注释自行理解。
functions.php
function themeConfig($form) 内的代码是模板设置功能
单行输入框选项:LOGO 设置
$logoUrl = new \Typecho\Widget\Helper\Form\Element\Text(
'logoUrl',
null,
null,
_t('站点 LOGO 地址'),
_t('在这里填入一个图片 URL 地址, 以在网站标题前加上一个 LOGO')
);
$form->addInput($logoUrl); // 添加选项到表单
\Typecho\Widget\Helper\Form\Element\Text
一共有5个参数,
- 第一个参数是选项key,只能是字符串,方便在模板中调用
- 第二个填
NULL
就行 - 第三个参数是默认值
- 第四个选项是选项标题
- 第五个参数是选项附加描述
_t()
是 Typecho 中多语言字符处理函数。
上边代码就是在模板设置处添加一个 Logo 设置,可以添加一个图片地址作为 Logo
添加好了通过$this->options->logoUrl
调用图片,不过习惯性调用如下:
<!--判断logo已被设置-->
<?php if ($this->options->logoUrl): ?>
<!--给logo图片加上本站超链接-->
<a id="logo" href="<?php $this->options->siteUrl(); ?>">
<!--显示logo-->
<img
src="<?php $this->options->logoUrl() ?>"
alt="<?php $this->options->title() ?>"
/>
</a>
<?php endif; ?>
此处对应header.php
中的 logo 显示。
CHECKBOX选项:显示开关
$sidebarBlock = new \Typecho\Widget\Helper\Form\Element\Checkbox(
'sidebarBlock',
[
'ShowRecentPosts' => _t('显示最新文章'),
'ShowRecentComments' => _t('显示最近回复'),
'ShowCategory' => _t('显示分类'),
'ShowArchive' => _t('显示归档'),
'ShowOther' => _t('显示其它杂项')
],
['ShowRecentPosts', 'ShowRecentComments', 'ShowCategory', 'ShowArchive', 'ShowOther'],
_t('侧边栏显示')
);
$form->addInput($sidebarBlock->multiMode()); // 添加选项到表单
\Typecho\Widget\Helper\Form\Element\Checkbox
同样也有5个参数
- 第一个参数是选项key,只能是字符串,方便在模板中调用
- 第二个参数是选项列表,必须是数组,key是值,value是显示文本
- 第三个参数是默认值,同样必须是数组,存放默认启用的key值数组
- 第四个选项是选项标题
- 第五个参数是选项附加描述
调用的话,这里拿 ShowCategory 举例,如果勾选它
<?php if (!empty($this->options->sidebarBlock) && in_array('ShowCategory', $this->options->sidebarBlock)): ?>
勾选了就会显示这里的文字
<?php endif; ?>
<!-- 因为 Checkbox 保存的是数组,所以必须使用 in_array 函数判断是否勾选了该选项 -->
这里对应的是sidebar.php
中的最新文章,最新评论,文章分类,归档等显示开关。
其他选项
默认主题只提供了两种选项,这里简单介绍其他选项类型。
多行输入框
顾名思义,可以输入多行文本的输入框
$header_html = new \Typecho\Widget\Helper\Form\Element\Text(
'header_html',
null,
null,
_t('输入头部附加 html 代码'),
_t('在这里输入的 html 会附加到 header 中,可以填入统计代码,Google 广告代码等')
);
$form->addInput($header_html); // 添加选项到表单
参数功能和单行输入框选项一样。
调用方式
<?php
if ($this->options->header_html)
$this->options->header_html();
?>
选项值如何获取?
在 default 主题里,你能搜索到$this
的地方,都可以通过$this->options->选项key
的方式获取,如果在没有$this
的地方,可以通过Helper::options()->选项key
这样来获取比如 logo 地址:
<!--判断logo已被设置-->
<?php if (Helper::options()->logoUrl): ?>
<!--给logo图片加上本站超链接-->
<a id="logo" href="<?php Helper::options()->siteUrl(); ?>">
<!--显示logo-->
<img
src="<?php Helper::options()->logoUrl() ?>"
alt="<?php Helper::options()->title() ?>"
/>
</a>
<?php endif; ?>
其他说明
参考以上代码,照葫芦画瓢,可以增加自己需要的模板设置。
神奇的 is 语法
在前端页面可通过$this-is('xxx')
的方式来判定所在页面,比如
$this->is('index'); // 判断首页
$this->is('archive'); //判断 archive
$this->is('single'); // 判断正文页面 page/post
$this->is('page'); // 判断独立页面 page
$this->is('post'); // 判断文章页面 post
$this->is('category'); // 判断分类页面
$this->is('tag'); // 判断标签页面
$this->is('front'); // 判断文章列表页面
$this->is('attachment'); //判断附件页面
以后如果有新增的,可以查看源码自行获取:https://github.com/typecho/typecho/blob/master/var/Widget/Archive.php(搜索$handles)。
warning 注意
当你在Typecho后台设置→阅读中设置将某个独立页面作为首页后,那么原来的首页(文章列表页)就不能用$this->is('index');
去判断了,而是使用$this->is('front');
进行判断。
分类,页面,文章还可以这样判断
$this->is('category', 'default'); // 判断分类缩略名等于 default
$this->is('page', 'start'); // 判断独立页面缩略名等于 start
$this->is('post', 1); // 判断文章cid等于1
需要注意的是,后面的参数是分类、页面的缩略名或者 id
完整使用实例
<?php if ($this->is('post')) : ?>
如果是文章页面就会显示这里的文字
<?php endif; ?>
文本输出和多语言
在 Typecho 中,输出文字和 PHP 是一样的。
<?php
echo 'Hello World';
echo sprintf('Hello %s', 'World');
?>
直接在<?php ?>标签外编写文字也是可以的
但是如果遇上多语言的话这样是不行的,可以使用 Typecho 内置的多语言函数。
<?php
_e('Hello World');
_e('Hello %s', 'World');
echo _t('Hello %s', 'World');
_t
和_e
的区别是_t
会返回一个字符串,而_e
会直接输出。
自定义标题
官方默认模板是这么自定义标题的。
<title><?php $this->archiveTitle(array(
'category' => _t('分类 %s 下的文章'),
'search' => _t('包含关键字 %s 的文章'),
'tag' => _t('标签 %s 下的文章'),
'author' => _t('%s 发布的文章')
), '', ' - '); ?><?php $this->options->title(); ?></title>
输出结果:页面标题 - 站点名称
让我们来分解一下其中的语句,后一句大家都很明白,显示站点名称嘛,那前一句呢?其实前一句的标题包含三个参数:
<?php $this->archiveTitle($split, $before, $end); ?>
参数名称 | 默认值 | 简介 |
---|---|---|
$split | » | 多级菜单间的分隔符,如:2009 » 12 |
$before | » | title 前显示的字符 |
$end | title 后显示的字符 |
建议
其实官方默认这样就已经很好了,但是如果有seo优化需要建议在后面再加上页码信息,如:
<?php $this->archiveTitle(array(
'category' => _t('分类 %s 下的文章'),
'search' => _t('包含关键字 %s 的文章'),
'tag' => _t('标签 %s 下的文章'),
'author' => _t('%s 发布的文章')
), '', ' - '); ?><?php $this->options->title(); ?><?php if($this->getCurrentPage()>1) _e("第 %d 页", $this->getCurrentPage()); ?>
注意
本页看到的_t
和_e
都是 Typecho 内置的函数,用于多语言输出文本。详见文本输出和多语言
<?php
$localized_text = _t($format, $arg1, $arg2, ..., $argN);
$localized_text = _e($format, $arg1, $arg2, ..., $argN);
自定义头部信息输出
默认输出
在默认的模板中,头部信息的输出的结果是这样的
<meta name="description" content="Just So So ..." />
<meta name="keywords" content="typecho,php,blog" />
<meta name="generator" content="Typecho 0.8/10.8.15" />
<meta name="template" content="default" />
<link rel="pingback" href=".../action/xmlrpc" />
<link rel="EditURI" type="application/rsd+xml" title="RSD" href=".../action/xmlrpc?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href=".../action/xmlrpc?wlw" />
<link rel="alternate" type="application/rss+xml" title="RSS 2.0" href=".../feed/" />
<link rel="alternate" type="application/rdf+xml" title="RSS 1.0" href=".../feed/rss/" />
<link rel="alternate" type="application/atom+xml" title="ATOM 1.0" href=".../feed/atom/" />
操作函数
打开模板中的 header.php 文件,找到下面这句
<?php $this->header(); ?>
加上你要设置的参数即可,比如:
<?php $this->header('keywords=&generator=&template=&pingback=&xmlrpc=&wlw='); ?>
以上代码即可过滤关键词、程序、模板名称、文章引用、离线写作等信息的输出
制作面包屑导航
什么是面包屑导航
面包屑导航是一种网站导航方式,通常在网站顶部显示,用于显示用户当前位置。
面包屑导航通常由导航元素组成,每个元素代表一个网站导航项,通常由链接组成。
tip 面包屑导航示例
首页 » 最新文章\
首页 » 分类名称 » 文章标题\
首页 » 归档年份 » 归档月份\
首页 » 页面名称\
首页 » 分类名称\
首页 » 标签名称\
首页 » 搜索关键词或其他信息
实际上 title 中的内容可以直接移植到我们的面包屑中,在看下面的代码之前,也许你需要先温习神奇的is语法
<div class="crumbs_patch">
<a href="<?php $this->options->siteUrl(); ?>">Home</a> »</li>
<?php if ($this->is('index')): ?><!-- 页面为首页时 -->
Latest Post
<?php elseif ($this->is('post')): ?><!-- 页面为文章单页时 -->
<?php $this->category(); ?> » <?php $this->title() ?>
<?php else: ?><!-- 页面为其他页时 -->
<?php $this->archiveTitle(' » ','',''); ?>
<?php endif; ?>
</div>
将上面的代码放在需要显示的页面,例如index.php
、post.php
,抑或是header.php
页面
自定义评论区域
一、自定义单条评论的 HTML 结构
在自定义评论前,我们得先设计好单条评论的 HTML 代码结构,如:
<li id="li-comment-520" class="comment-body comment-parent comment-odd">
<div id="comment-520">
<div class="comment-author">
<img class="avatar" src="avatar.png" alt="" width="40" height="40">
<cite class="fn"><a href="评论者主页">评论者名字</a></cite>
</div>
<div class="comment-meta">
<a href="评论地址">评论时间</a>
<span class="comment-reply">回复按钮</span>
</div>
<p>我是评论内容</p>
</div><!-- 单条评论者信息及内容 -->
<div class="comment-children">
<!-- 嵌套评论相关 -->
</div>
</li>
自定义好HTML代码后,将如何去实现呢?首先我们要打开模板文件夹里的comments.php
文件,做好修改准备。
二、使用自定义评论函数
打开 comments.php 文件后,我们需要在它的顶部,插入以下函数代码:
<?php function threadedComments($comments, $options) {
$commentClass = '';
if ($comments->authorId) {
if ($comments->authorId == $comments->ownerId) {
$commentClass .= ' comment-by-author'; //如果是文章作者的评论添加 .comment-by-author 样式
} else {
$commentClass .= ' comment-by-user'; //如果是评论作者的添加 .comment-by-user 样式
}
}
$commentLevelClass = $comments->levels > 0 ? ' comment-child' : ' comment-parent'; //评论层数大于0为子级,否则是父级
?>
/* 自定义评论的代码结构 */
<?php } ?>
其次,将我们刚才自定义好的单条评论的 HTML 代码,放在上面代码里注释的地方,如下:
<?php function threadedComments($comments, $options) {
$commentClass = '';
if ($comments->authorId) {
if ($comments->authorId == $comments->ownerId) {
$commentClass .= ' comment-by-author'; //如果是文章作者的评论添加 .comment-by-author 样式
} else {
$commentClass .= ' comment-by-user'; //如果是评论作者的添加 .comment-by-user 样式
}
}
$commentLevelClass = $comments->_levels > 0 ? ' comment-child' : ' comment-parent'; //评论层数大于0为子级,否则是父级
?>
/* 自定义评论的代码结构 */
<li id="li-comment-520" class="comment-body comment-parent comment-odd">
<div id="comment-520">
<div class="comment-author">
<img class="avatar" src="avatar.png" alt="" width="40" height="40">
<cite class="fn"><a href="评论者主页">评论者名字</a></cite>
</div>
<div class="comment-meta">
<a href="评论地址">评论时间</a>
<span class="comment-reply">回复按钮</span>
</div>
<p>我是评论内容</p>
</div><!-- 单条评论者信息及内容 -->
<div class="comment-children">
<!-- 嵌套评论相关 -->
</div>
</li>
<?php } ?>
三、用系统的评论变量替换HTML中相关属性
把 HTML 里相关的属性,替换成 Typecho 系统中的评论变量,变量的列表可以参考下面。下面的例子,是替换评论的 id:
替换前:
<li id="li-comment-520" class="comment-body comment-parent comment-odd">
替换后:
<li id="li-<?php $comments->theId(); ?>" class="comment-body<?php
if ($comments->_levels > 0) {
echo ' comment-child';
$comments->levelsAlt(' comment-level-odd', ' comment-level-even');
} else {
echo ' comment-parent';
}
$comments->alt(' comment-odd', ' comment-even');
echo $commentClass;
?>">
其中,替换ID这里,还需要判断判断当前评论是父级评论还是子级评论,且判断评论 ID 的奇偶数等。
四、添加嵌套评论(子评论)
替换前:
<div class="comment-children">
<!-- 嵌套评论相关 -->
</div>
替换后:
<?php if ($comments->children) { ?> //是否嵌套评论判断开始
<div class="comment-children">
<?php $comments->threadedComments($options); ?> //嵌套评论所有内容
</div>
<?php } ?> //是否嵌套评论判断结束
五、相关方法及说明
<?php $comments->gravatar('40', ''); ?> //头像,有两个参数,大小、默认头像?
<?php $comments->author(); ?> //评论作者
<?php $comments->permalink(); ?> //当前评论的连接地址
<?php $comments->date('Y-m-d H:i'); ?>//评论时间,可在括号里设置格式
<?php $comments->reply(); ?> //回复按钮,可在括号里自定义评论按钮的文字
<?php $comments->content(); ?> //评论内容
六、最终得到的代码
当我们把上面所有变量都替换完成之后,最终得到的代码如下:
<?php function threadedComments($comments, $options) {
$commentClass = '';
if ($comments->authorId) {
if ($comments->authorId == $comments->ownerId) {
$commentClass .= ' comment-by-author';
} else {
$commentClass .= ' comment-by-user';
}
}
$commentLevelClass = $comments->levels > 0 ? ' comment-child' : ' comment-parent';
?>
<li id="li-<?php $comments->theId(); ?>" class="comment-body<?php
if ($comments->levels > 0) {
echo ' comment-child';
$comments->levelsAlt(' comment-level-odd', ' comment-level-even');
} else {
echo ' comment-parent';
}
$comments->alt(' comment-odd', ' comment-even');
echo $commentClass;
?>">
<div id="<?php $comments->theId(); ?>">
<div class="comment-author">
<?php $comments->gravatar('40', ''); ?>
<cite class="fn"><?php $comments->author(); ?></cite>
</div>
<div class="comment-meta">
<a href="<?php $comments->permalink(); ?>"><?php $comments->date('Y-m-d H:i'); ?></a>
<span class="comment-reply"><?php $comments->reply(); ?></span>
</div>
<?php $comments->content(); ?>
</div>
<?php if ($comments->children) { ?>
<div class="comment-children">
<?php $comments->threadedComments($options); ?>
</div>
<?php } ?>
</li>
<?php } ?>
注意:上面的自定义评论代码输出的,就是本来评论页里的下面这段代码,所以你就不用对这段代码做任何更改了。
<?php $comments->listComments(); ?>
首次评论审核提示,在自定义评论代码的适当地方添加以下语句,否则将看不到审核提示语句。
<?php if ('waiting' == $comments->status) { ?><span class="text-muted">您的评论需管理员审核后才能显示
分离文章的评论和引用通告
打开模板的comments.php
文件,找到通篇的核心语句:
<?php $this->comments()->to($comments); ?>
这条语句控制着评论的类型,他的参数如下:
语句 | 注释 |
---|---|
<?php $this→comments()→to($comments); ?> | 显示全部(默认) |
<?php $this→comments('comment')→to($comments); ?> | 只显示 comment |
<?php $this→comments('trackback')→to($trackbacks); ?> | 只显示 trackback |
<?php $this→comments('pingback')→to($pingbacks); ?> | 只显示 pingback |
为了分开,我们开始对comments.php
做如下修改,首先只显示评论:
<?php $this->comments('comment')->to($comments); ?><!-- 关键 -->
<?php if ($comments->have()) : ?>
<ol>
<?php while ($comments->next()) : ?>
<li id="<?php $comments->theId() ?>">
<div class="comment_data">
<?php $comments->gravatar(32, '', '', 'avatar'); ?>
<span><?php $comments->author() ?></span> Says:<br />
<?php $comments->date('F jS, Y'); ?> at <?php $comments->date('h:i a'); ?>
</div>
<div class="comment_text"><?php $comments->content() ?></div>
</li>
<?php endwhile; ?>
</ol>
<?php endif; ?>
然后输出 pingback,pingback 并不需要那么多的展示内容,假设只展示标题和日期:
<?php $this->comments('pingback')->to($pingbacks); ?><!-- 关键 -->
<?php if ($pingbacks->have()) : ?>
<h3>Pingbacks</h3>
<ol>
<?php while ($pingbacks->next()) : ?>
<li id="<?php $pingbacks->theId() ?>">
<?php $pingbacks->author() ?> <?php $pingbacks->date('F jS, Y'); ?>
</li>
<?php endwhile; ?>
</ol>
<?php endif; ?>
如果你要显示 trackback,也可以按如上的修改。typecho 模板语法很多是通用的,所以当你遇到不清楚的问题时,可以自己试着拼凑一下
自定义字段
字段使用与调用
在新建/编辑文章界面时,下方点击添加字段,填写字段名与字段值
模板对应位置(index.php/post.php/page.php/archive.php等)加上如下代码
<!--判断say字段的字段值是否存在-->
<?php if ($this->fields->say): ?>
他说了:<?php $this->fields->say(); ?>
<?php endif;?>
::: tip 输出结果
他说了:你好世界
:::
提示用户输入自定义字段
打开主题的functions.php
,填入如下函数(如果函数存在,请自行合并),就可以为你的主题增加一个自动绑定的输入框。
function themeFields($layout) {
$say = new \Typecho\Widget\Helper\Form\Element\Text('say', NULL, NULL, _t('留言'), _t('输入想说的话)'));
$layout->addItem($say);
}
模板作者这样设置后,文章字段就不用用户手动增加了,而是默认就加好了,用户只需要提交 字段值(自己想说的话) 就可以了。
只在文章编辑页提示
if($_SERVER['SCRIPT_NAME']=="/admin/write-post.php"){
function themeFields($layout) {
$say = new \Typecho\Widget\Helper\Form\Element\Text('say', NULL, NULL, _t('留言'), _t('输入想说的话)'));
$layout->addItem($say);
}
}
只在独立页面编辑页显示
if($_SERVER['SCRIPT_NAME']=="/admin/write-page.php"){
function themeFields($layout) {
$say = new \Typecho\Widget\Helper\Form\Element\Text('say', NULL, NULL, _t('留言'), _t('输入想说的话)'));
$layout->addItem($say);
}
}
调用相关文章
文章页拓展阅读
修改主题的时候一般会在文章页正文下方显示相关文章,拓展阅读,这个功能 Typecho 内置了相关函数,调用即可。
修改 post.php
, 将以下内容粘贴至你想加入相关文章的位置(例如我放在文章结束的位置),最后保存即可。
<?php $this->related(5)->to($relatedPosts); ?>
<ul>
<?php while ($relatedPosts->next()): ?>
<li><a href="<?php $relatedPosts->permalink(); ?>" title="<?php $relatedPosts->title(); ?>"><?php $relatedPosts->title(); ?></a></li>
<?php endwhile; ?>
</ul>
调用方法说明
$this->related($limits, $type);
这个函数有两个参数:
序号 | 参数类型 | 参数 | 默认值 | 说明 |
---|---|---|---|---|
1 | int | $limits | 5 | 相关文章数量 |
2 | ?string | $type | NULL | 文章关联方式,可接受 tags/author。 |
独立页面拓展阅读
在 Typecho 中,独立页面是没有标签的,所以只可以调取同作者相关文章。
<?php $relatedPosts = \Widget\Contents\Related\Author::alloc(
['cid' => $this->cid, 'type' => 'post', 'author' => $this->author->uid, 'limit' => 5]
); ?>
<ul>
<?php while ($relatedPosts->next()): ?>
<li><a href="<?php $relatedPosts->permalink(); ?>" title="<?php $relatedPosts->title(); ?>"><?php $relatedPosts->title(); ?></a></li>
<?php endwhile; ?>
</ul>
调用指定分类的文章
根据分类 mid 获取某个分类下的文章列表
<?php \Widget\Archive::allocWithAlias('index', 'pageSize=6&type=category', 'mid=1')->to($new); ?>
<?php while ($new->next()): ?>
<a href="<?php $new->permalink(); ?>"><?php $new->title(); ?></a>
<?php endwhile; ?>
以上就是获取分类mid
等于1的最新6篇文章,pageSize=6
就是指定调用数量,mid=1
指定分类mid,也可以用缩略名方式替换如slug=name
其中name
就是mid
等于1的分类的缩略名。
因为 Typecho 中 Widget 默认是是单例的,你重复获取也只是之前的引用(就是你获取两次都会输出一样的数据,所以必须通过 alias 区分),所以这里需要用\Widget\Archive::allocWithAlias('index-2')
来获取新的实例,如下:
<?php \Widget\Archive::allocWithAlias('index-1', 'pageSize=6&type=category', 'mid=1')->to($new); ?>
<?php while ($new->next()): ?>
<a href="<?php $new->permalink(); ?>"><?php $new->title(); ?></a>
<?php endwhile; ?>
<?php \Widget\Archive::allocWithAlias('index-2', 'pageSize=6&type=category', 'mid=2')->to($new); ?>
<?php while ($new->next()): ?>
<a href="<?php $new->permalink(); ?>"><?php $new->title(); ?></a>
<?php endwhile; ?>
文章标签
文章页调用
调用本文标签
在文章页post.php
通过下面的代码调用本文的标签:
<?php $this->tags(',', true); ?>
序号 | 参数类型 | 参数 | 默认值 | 说明 |
---|---|---|---|---|
1 | string | $split | , | 分隔符,用于分隔标签 |
2 | bool | $link | true | 是否输出带链接的标签 |
3 | ?string | $default | null | 没有标签的时候的输出内容 |
::: tip 说明(',', true, 'none')
第一个逗号间的逗号代表标签与标签的间隔用逗号隔开,true
是标签以超链接形式输出,第三个参数没提供,说明没有标签不输出内容。
:::
给每个标签套上div
<div><?php $this->tags('</div><div>', true, 'none'); ?></div>
根据文章是否含有标签输出指定内容
比如给含有永久VIP标签的文章输出永久VIP免费提示
<?php if(in_array('永久VIP', $this->tags) ): ?>
永久VIP免费
<?php endif; ?>
输出标签云
<?php \Widget\Metas\Tag\Cloud::alloc('sort=count&desc=1&limit=200')->to($tags); ?>
<?php if($tags->have()): ?>
<ul class="tags-list">
<?php while ($tags->next()): ?>
<li><a href="<?php $tags->permalink(); ?>" rel="tag" class="size-<?php $tags->split(5, 10, 20, 30); ?>" title="<?php $tags->count(); ?> 个话题"><?php $tags->name(); ?></a></li>
<?php endwhile; ?>
<?php else: ?>
<li><?php _e('没有任何标签'); ?></li>
<?php endif; ?>
</ul>
::: tip 参数说明
- sort:排序方式为 mid;
- ignoreZeroCount:忽略文章数为 0 的;
- desc:是否降序输出;
- limit:输出数目。
:::
自定义分页
<style>.page-navigator { display: flex; justify-content: center; align-items: center; gap: 8px;} .page-navigator > li { list-style: none; margin: 0; padding: 0; } .page-navigator > li + li { margin-top: 0 }</style>
自定义分页样式
<?php $this->pageNav('«', '»', 1, '...', array('wrapTag' => 'ol', 'wrapClass' => 'page-navigator', 'itemTag' => 'li', 'textTag' => 'span', 'currentClass' => 'current', 'prevClass' => 'prev', 'nextClass' => 'next',)); ?>
渲染出来的效果是这样的
<ol class="page-navigator"><li class="current"><a href="http://example.com/page/1/">1</a></li><li><a href="http://example.com/page/2/">2</a></li><li><span>...</span></li><li><a href="http://example.com/page/5/">5</a></li><li class="next"><a href="http://example.com/page/2/">»</a></li></ol>
由此可知
- « 和 » 分别对应的是上一页按钮和下一页按钮
- 数字1是分割范围(分几页),是当前页码附近可现实的页码数量,举个例子,当前页码为1,一共页码为5,那么上述代码输出的效果就是1,2,...5,如果当前页码为2呢,效果就是1,2,3,...5。
- ... 是分割字符,就是2中提到的那个省略页码的东西
- wrapTag外层包裹标签名,默认ol,wrapClass外层包裹类名,itemTag内层标签名, 默认li,textTag直接输出文字的标签名,currentClass当前聚焦类名,prevClass上一页类名,nextClass下一页类名。
5,itemClass可以给其他页码的标签带上class。【注:第5条为补充内容,例子中没有提到】
页码
当前页码:<?php if($this->getCurrentPage()>1) echo $this->getCurrentPage(); else echo 1;?>
总页码:<?php echo ceil($this->getTotal() / $this->parameter->pageSize); ?>
前台个人资料页
Typecho 中形如/author/1
这样的链接会显示 UID 为 1 的用户的所有文章。
加入你的 UID 是 666 则 /author/666
是你的用户页。我们可以自定义author.php
在这个页面增加个人资料修改的功能。
<div id="profile">
<h3><?php _e('个人资料'); ?></h3>
<section class="card">
<div class="card-title"><?php _e("用户名"); ?></div>
<div class="card-body">
<?php \Widget\Users\Profile::alloc()->profileForm()->render(); ?>
</div>
</section>
<section id="change-password" class="card">
<div class="card-title"><?php _e('密码修改'); ?></div>
<div class="card-body">
<?php \Widget\Users\Profile::alloc()->passwordForm()->render(); ?>
</div>
</section>
<?php \Widget\Users\Profile::alloc()->personalFormList(); ?>
</div>
但是这样有一个问题,所有人点进/author/666
都能看见个人资料页面,可以加个判断处理只有当前用户访问所属用户页才显示。
<?php if($this->user->uid==$this->author->uid && $this->user->hasLogin()): ?>
这里填入上边的代码
<?php endif; ?>
强制显示 404 页面
有的页面比如【个人资料】页面,在用户没登录的时候一般显示为404,那么怎么强制不登录时显示404呢?
修改profile.php
(假设profile.php
是个人资料独立页面的模板),在最上方加入
if (!$this->user->hasLogin()) {
throw new Typecho\Router\Exception("Path '{$this->request->getPathInfo()}' not found", 404);
}
常用统计函数
一些例子
<?php Typecho_Widget::widget('Widget_Stat')->to($stat); ?>
文章总数:<?php $stat->publishedPostsNum() ?>篇
分类总数:<?php $stat->categoriesNum() ?>个
评论总数:<?php $stat->publishedCommentsNum() ?>条
页面总数:<?php $stat->publishedPagesNum() ?>个
当前作者的文章总数:<?php $stat->myPublishedPostsNum() ?>篇
其他可用函数
描述来自源码注释,不是特别清晰,有空我会重新梳理下
说明 | 函数名 |
---|---|
获取已发布的文章数目 | publishedPostsNum |
获取待审核的文章数目 | waitingPostsNum |
获取草稿文章数目 | draftPostsNum |
获取当前用户已发布的文章数目 | myPublishedPostsNum |
获取当前用户待审核文章数目 | myWaitingPostsNum |
获取当前用户草稿文章数目 | myDraftPostsNum |
获取当前用户已发布的文章数目 | currentPublishedPostsNum |
获取当前用户待审核文章数目 | currentWaitingPostsNum |
获取当前用户草稿文章数目 | currentDraftPostsNum |
获取已发布页面数目 | publishedPagesNum |
获取草稿页面数目 | draftPagesNum |
获取当前显示的评论数目 | publishedCommentsNum |
获取当前待审核的评论数目 | waitingCommentsNum |
获取当前垃圾评论数目 | spamCommentsNum |
获取当前用户显示的评论数目 | myPublishedCommentsNum |
获取当前用户显示的评论数目 | myWaitingCommentsNum |
获取当前用户显示的评论数目 | mySpamCommentsNum |
获取当前文章的评论数目 | currentCommentsNum |
获取当前文章显示的评论数目 | currentPublishedCommentsNum |
获取当前文章显示的评论数目 | currentWaitingCommentsNum |
获取当前文章显示的评论数目 | currentSpamCommentsNum |
获取分类数目 | categoriesNum |
该统计函数来自源码var/Widget/Stat.php
中
获取建站天数
有可能不准确,因为有人会删掉第一篇文章
<?php
class XStat extends Widget_Stat {
/**
* 建站天数(可能不准确,因为有的人把第一篇文章删掉了)
*
* @return int
*/
public static function ___sitePublishedDaysNum(): int
{
$db = Typecho_Db::get();
$count = $db->fetchRow($db->select('MIN(created) as created')->from('table.contents'));
$diff = time() - $count['created'];
return intval($diff / 86400);
}
}
调用方式
<?php Typecho_Widget::widget('XStat')->to($stat); ?>
<?php _t("建站天数:%s天", $stat->sitePublishedDaysNum); ?>
常用模板函数
这些函数都可以放在functions.php
方便调用
获取模板版本号
function get_theme_version()
{
$info = Typecho_Plugin::parseInfo(__DIR__ . '/index.php');
return $info['version'];
}
全局替换正文内容
themeInit
函数必须放在functions.php
或者functions.php
引用的php文件里
function themeInit($archive) {
... // 其他语句
if ($archive->is('index') || $archive->is('content')) {
$archive->content = replace_text($archive->content);
}
... // 其他语句
}
function replace_text($content) {
$content = preg_replace('#<a(.*?) href="([^"]*/)?(([^"/]*)\.[^"]*)"(.*?)>#',
'<a$1 href="$2$3"$5 target="_blank">', $content); // 强制文章中的链接为新标签打开
return $content;
}
文章阅读次数统计
使用 views
字段存储数据
放在functions.php
中
function themeInit($archive) {
... // 其他语句
$db = \Typecho\Db::get();
$_prefix = $db->getPrefix();
try {
if (!array_key_exists('views', $db->fetchRow($db->select()->from('table.contents')->page(1, 1)))) {
$db->query('ALTER TABLE `' . $_prefix . 'contents` ADD `views` INT DEFAULT 0;');
}
} catch (Exception $e) {}
if ($archive->is('content')) {
increase_views($archive);
}
... // 其他语句
}
/**
* 增加浏览次数
*/
function increase_views($archive) {
$cookie = trim(\Typecho\Cookie::get('__typecho_views') ?? "", " \t\n\r\0\x0B,");
$viewedIds = !empty($cookie) ? explode(',', $cookie) : [];
$cid = $archive->cid;
if (!in_array($cid, $cookie)) {
$db = \Typecho\Db::get();
$db->query($db->update('table.contents')
->expression('views', 'views + 1')
->where('cid = ?', $cid));
$viewedIds[] = $cid;
$cookie = implode(',', $viewedIds);
\Typecho\Cookie::set('__typecho_views', $cookie);
}
}
/**
* 获取浏览次数,调用方式 get_views($archive, "0 times", "1 time", "%d times")
*/
function get_views() {
$args = func_get_args();
if (count($args) === 0) {
throw new \InvalidArgumentException(_t("Parameter#1 \$archive cannot be null"));
}
$archive = array_shift($args);
$row = $db->fetchRow($db->select('views')->from('table.contents')->where('cid = ?', $archive->cid));
$num = (isset($row) && isset($row['views'])) ? $row['views'] : 0;
if (empty($args)) {
$args[] = '%d';
}
return sprintf(array_key_exists($num, $args) ? $args[$num] : array_pop($args), $num);
}
调用举例,在post.php
中
<li class="meta-item">
<?php echo get_views($this, _t("无人问津"), _t("1 次浏览"), _t("%d 次浏览")); ?>
</li>
使用自定义字段存储数据
这个方法无需给contents
表增加一个字段views
,放在functions.php
中
function themeInit($archive) {
... // 其他语句
if ($archive->is('content')) {
increase_views($archive);
}
... // 其他语句
}
/**
* 增加浏览次数
*/
function increase_views($archive) {
$cookie = trim(\Typecho\Cookie::get('__typecho_views') ?? "", " \t\n\r\0\x0B,");
$viewedIds = !empty($cookie) ? explode(',', $cookie) : [];
$cid = $archive->cid;
if (!in_array($cid, $cookie)) {
if (!isset($archive->fields->views)) {
$archive->setField('views', 'int', 1, $cid);
} else {
$archive->setField('views', 'int', intval($archive->fields->views) + 1, $cid);
}
$viewedIds[] = $cid;
$cookie = implode(',', $viewedIds);
\Typecho\Cookie::set('__typecho_views', $cookie);
}
}
/**
* 获取浏览次数,调用方式 get_views($archive, "0 times", "1 time", "%d times")
*/
function get_views() {
$args = func_get_args();
if (count($args) === 0) {
throw new \InvalidArgumentException(_t("Parameter#1 \$archive cannot be null"));
}
$archive = array_shift($args);
$num = intval($archive->fields->views ?? 0);
if (empty($args)) {
$args[] = '%d';
}
return sprintf(array_key_exists($num, $args) ? $args[$num] : array_pop($args), $num);
}
显示新帖子图标
放在functions.php
中
function isNewPost($archive){
$now = new \Typecho\Date(T\Typecho\Date::time());
return $now->timeStamp - $archive->date->timeStamp < 24*60*60 ? true : false;
}
调用举例,在列表循环里或者post.php
里调用
<?php if (isNewPost($this)): ?>
<li class="meta-item">
新帖子
</li>
<?php endif; ?>
获取文章缩略图
放在functions.php
中
/**
* 输出缩略图
*
* @param Typecho\Widget|\Widget\Base\Contents|\Widget\Archive $archive 文章对象
* @param int $quantity 图片数量
* @param bool $return 是否返回
* @param bool $parse 是否转换
* @param string $template 转换模板
* @return mixed
*/
public static function thumbs($archive, int $quantity = 3, bool $return = false, bool $parse = false, string $template = '<img alt="" src="%s" />')
{
$thumbs = [];
// 首先使用自定义字段 thumb
if (self::fieldExists($archive, 'thumb') && $quantity > 0) {
$f_thumb = $archive->fields->thumb;
if (!in_array($f_thumb, $thumbs)) {
$fieldThumbs = explode("\n", $f_thumb);
foreach ($fieldThumbs as $thumb) {
if ($quantity > 0 && !empty(trim($thumb))) {
$thumbs[] = $thumb;
$quantity -= 1;
}
}
}
}
// 然后是正文匹配
preg_match_all("/<img(?<images>[^>]*?)>/i", $archive->content, $matches);
foreach ($matches['images'] as $value) {
if ($quantity <= 0) {
break;
}
$match = '';
// 2021.09.07 修复下标 src 不存在
if (XCore::configStr('XLazyLoad', 'on') === 'on') {
preg_match('/data-src="(?<src>.*?)"/i', $value, $dataSrcMatch);
if (array_key_exists('src', $dataSrcMatch)) {
$match = $dataSrcMatch['src'];
}
}
if (empty($match)) {
preg_match('/src="(?<src>.*?)"/i', $value, $srcMatch);
if (array_key_exists('src', $srcMatch)) {
$match = $srcMatch['src'];
}
}
if (!empty($match)) {
// 2020.03.29 修正输出插件图标的BUG
if (strpos($match, __TYPECHO_PLUGIN_DIR__ . "/TePass") !== false) {
continue;
}
if (strpos($match, "//") === false) {
continue;
}
if (!in_array($match, $thumbs)) {
$thumbs[] = $match;
$quantity -= 1;
}
}
}
// 接着是附件匹配
/** @var \Widget\Contents\Attachment\Related $attachments */
\Widget\Contents\Attachment\Related::allocWithAlias('@content-' . $archive->cid, 'parentId=' . $archive->cid)->to($attachments);
while ($attachments->next()) {
if ($quantity <= 0) {
break;
}
if (isset($attachments->isImage) && $attachments->isImage == 1) {
if (!in_array($attachments->url, $thumbs)) {
$thumbs[] = $attachments->url;
$quantity -= 1;
}
}
}
// 最后是随机
while ($quantity-- > 0) {
$thumbs[] = getRandomCover();
}
// 转换
if ($parse && (!empty($template))) {
for ($i = 0; $i < count($thumbs); $i++) {
$thumbs[$i] = str_replace("%s", $thumbs[$i], $template);
}
}
// 输出或返回
if ($return) {
if (count($thumbs) == 1) {
return $thumbs[0];
}
return $thumbs;
} else {
foreach ($thumbs as $thumb) {
echo $thumb;
}
return true;
}
}
function getRandomCover() {
// 定义图片路径数组,请根据实际修改
$images = [
'path/to/image1.jpg',
'path/to/image2.jpg',
'path/to/image3.jpg',
'path/to/image4.jpg',
'path/to/image5.jpg',
];
// 从数组中随机选择一张图片
$randomIndex = array_rand($images);
// 返回随机选择的图片路径
return $images[$randomIndex];
}
调用举例,在列表循环里或者post.php
里调用
<div class="post-cover">
<?php thumbs($this, 1, false, true); ?>
</div>
版权声明:本文为原创文章,版权归 全栈开发技术博客 所有。
本文链接:https://www.lvtao.net/dev/typecho-template-code.html
转载时须注明出处及本声明