最近做了两个主题,一个是 Typecho-Theme-RAW,另一个就是现在使用的这个,Typecho-Theme-Q。RAW 是我的 Typecho 主题第一作,象征意义比较大一点,主题质量各方面只能算差强人意吧。Q 的起源是因为我觉得 Bootstrap 有点意思,边看文档边写就写出来了,由于有写 RAW 时积累的经验加持,完成得很快。某些经验性质的东西不写下来很快就会忘,所以有了这一篇备忘。
以下部分内容来自 QQ 爹博客等大佬的博文,在此先行感谢。
Typecho 配置
TYPECHO_DEBUG
可在 Typecho 根目录下 config.inc.php
中添加以下代码来开启 DEBUG 模式。
define('__TYPECHO_DEBUG__', true);
这个模式下会打印更多的报错信息,某些普通情况直接 500 处理的错误在这里会打印出错误原因与调用堆栈等,很有用。
TYPECHO_GRAVATR_PREFIX
这个常量可以修改 GRAVATAR 头像链接的前缀,方便使用国内镜像来加快速度。
define('__TYPECHO_GRAVATR_PREFIX__', 'https://gravatar.cat.net/avatar');
需要注意的是要生效的话需要在主题中使用 Typecho 提供的方法来获取头像链接。
主题层面修改 Typecho 设置
在 PJAX 中评论失效的问题需要关闭主题的反垃圾保护,在主题中可以自动做到这一点。在主题目录下 functions.php
中创建如下代码即可。
function themeInit($archive){
Helper::options()->commentsAntiSpam = false;
}
Typecho 系统设置均可使用 Helper::options()
函数获取,权限蛮高的。另:themeInit()
函数完成了主题初始化,创建表之类的操作可以在这个函数中完成。
主题开发
自定义渲染方式
分别创建 category
,page
,post
文件夹,在其中创建 分类缩略名.php
、页面slug.php
、文章cid.php
即可覆盖默认的渲染方式来为某分类、页面、文章创建自定义渲染方式。
主题设置面板
在主题根目录下创建 functions.php
,在其中定义函数 themeConfig($form)
function themeConfig($form) {
//基本设置
$notice = new Typecho_Widget_Helper_Form_Element_Textarea('notice', NULL, NULL, _t('网站公告'), _t('网站公告'));
$form->addInput($notice);
$banner = new Typecho_Widget_Helper_Form_Element_Text('banner', NULL, NULL, _t('网站公告'), _t('网站公告'));
$form->addInput($banner);
}
类似这样就可以添加 Textarea 或者 Text 类型的输入框。其中的内容在主题中可以通过以下方式来访问。
$this->options->ValueName
文章自定义字段面板
在主题根目录下创建 functions.php
,在其中定义函数
function themeFields(Typecho_Widget_Helper_Layout $layout) {
$thumb = new Typecho_Widget_Helper_Form_Element_Textarea('banner', NULL, NULL, '文章主图', '输入图片URL,该图片会用于主页文章列表的显示');
$layout->addItem($thumb);
$showTOC=new Typecho_Widget_Helper_Form_Element_Select('showTOC',array('0'=>'不显示目录','1'=>'显示目录'),'0','文章目录','是否显示文章目录');
$layout->addItem($showTOC);
}
与上面相似,这样就可以为自定义字段创建友好的输入方式。这与手动添加自定义字段是等效的,但是可以提高使用体验。
自定义评论输出方式
创建 comments.php
文件,require 在 functions.php
中,然后就可以在其中重载输出评论的方式。
Typecho 内建函数
$this->header();
这个函数要在 head
标签中调用,负责输出 RSS、Pingback 等信息,还涉及输出插件要在 head
中输出的内容。与上面的类似,不过一般在 footer
标签后调用:
$this->footer();
获取所有设置:
Helper::options();
获取某插件设置:
Helper::options()->plugin($pluginName);
常用代码片段
博客内容统计
<?php Typecho_Widget::widget('Widget_Stat')->to($stat); ?>
<li>文章总数:<?php $stat->publishedPostsNum() ?>篇</li>
<li>分类总数:<?php $stat->categoriesNum() ?>个</li>
<li>评论总数:<?php $stat->publishedCommentsNum() ?>条</li>
<li>页面总数:<?php $stat->publishedPagesNum() ?>个</li>
输出当前文章为第几篇
$this->sequence
最近评论
$this->widget('Widget_Comments_Recent','ignoreAuthor=true&pageSize=10')->to($comments);
<?php while($comments->next()): ?>
<?php $comments->gravatar(50,''); ?>
<a href="<?php $comments->permalink(); ?>"><?php $comments->excerpt($num, '...'); ?></a>
<?php endwhile; ?>
其中 ignoreAuthor=true
可以过滤掉博主自己的评论。pageSize
为输出数量。
Gravatar 头像链接
Typecho_Common::gravatarUrl($mail, $size, '', '', true);
可以根据邮箱地址给出头像链接。
输出所有独立页面
$this->widget('Widget_Contents_Page_List')->parse('<li><a href="{permalink}" target="_self">{title}</a></li>');
parse
方法可以非常方便地格式化输出所有的独立页面,太帅了。
文章、评论分页
$this->pageNav('上一页', '下一页', 0, '', 'wrapClass=hidden&prevClass=prev&nextClass=next');
判断插件是否可用
这是一个小坑。只要启用过插件,即使已经禁用了插件,class_exsist()
仍然会返回 true
,所以应该如下检验:
/**
* 判断插件是否存在并启用
*
* @return boolean
*/
function isPluginAvailable($name) {
if (class_exists($name.'_Plugin')){
$plugins = Typecho_Plugin::export();
$plugins = $plugins['activated'];
return is_array($plugins) && array_key_exists($name, $plugins);
}
return false;
}
校验请求是否来自手机
/**
* 手机识别
*
* @return boolean
*/
function isPhone(){
$ua=strtolower($_SERVER["HTTP_USER_AGENT"]);
$devices=array("Android", 'iPhone', 'iPod', 'Phone');
foreach ($devices as $device) {
if(strpos($ua, strtolower($device))){
return true;
}
}
return false;
}
校验请求是否来自移动设备
/**
* 移动端判定
*
* @return bool
*/
public static function isMobile(){
if (isset ($_SERVER['HTTP_X_WAP_PROFILE'])){
return TRUE;
}
if (isset ($_SERVER['HTTP_USER_AGENT'])) {
$clientkeywords = array ('mobile','nokia','sony','ericsson','mot','samsung','htc','sgh','lg','sharp','sie-','philips','panasonic','alcatel','lenovo','iphone','ipod','blackberry','meizu','android','netfront','symbian','ucweb','windowsce','palm','operamini','operamobi','openwave','nexusone','cldc','midp','wap'
);
if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))){
return TRUE;
}
}
if (isset ($_SERVER['HTTP_ACCEPT'])){
if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== FALSE) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === FALSE || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))){
return TRUE;
}
}
return FALSE;
}
输出完备的文章标题
一般是处于 SEO 之用
/**
* 导出标题
*
* @return void
*/
function title(Widget_Archive $archive){
$archive->archiveTitle(array(
'category' => _t('分类 %s 下的文章'),
'search' => _t('包含关键字 %s 的文章'),
'tag' => _t('标签 %s 下的文章'),
'author' => _t('%s 发布的文章')
), '', ' - ');
Helper::options()->title();
}
面包屑导航
用以输出当前位置相对主页的路由。
<?php if ($this->is('index')): ?><!-- 页面为首页时 -->
文章列表 » 第 <?php echo $this->_currentPage; ?> 页
<?php elseif ($this->is('post')): ?><!-- 页面为文章单页时 -->
文章 » <?php $this->title() ?>
<?php else: ?><!-- 页面为其他页时 -->
<?php $this->archiveTitle(array(
'category' => _t('分类 "%s" 下的文章'),
'search' => _t('包含关键字 "%s" 的文章'),
'tag' => _t('标签 "%s" 下的文章'),
'author' => _t('%s 发布的文章')
), '', ''); ?>
<?php endif; ?>
某些可以简化工作量的函数
$this->options->themeUrl('/some/path/'); //输出相对主题目录的路径
Helper::options()->pluginUrl; //获取插件目录
Helper::options()->index('/some/path/'); //输出相对博客根目录的路径
数据库相关
根据 CID 调用文章
$db = Typecho_Db::get();
$row = $db->fetchRow($db->select()->from('table.contents')->where('cid = ?', $cid));
$row
为 Array,包括了文章本题的许多字段,包括 title
、创建日期等。
获得某文章的某自定义字段
$row = $db->fetchAll($db->select()->from('table.fields')->where('cid = ?', $cid)->where('name = ?', $fieldName));
$field=$row[0]['str_value'];
数据库相关属于比较高级的用法了,虽然在某些时候不得不用,但是最好还是使用 Typecho 提供的更原生的方法。更多:Typecho 数据库常用 API 。
暂时就这些,后续想到再添加。