一些 Typecho 开发笔记

最近做了两个主题,一个是 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() 函数完成了主题初始化,创建表之类的操作可以在这个函数中完成。

主题开发

自定义渲染方式

分别创建 categorypagepost 文件夹,在其中创建 分类缩略名.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')->to($comments);
<?php while($comments->next()): ?>
    <?php $comments->gravatar(50,''); ?>
    <a href="<?php $comments->permalink(); ?>"><?php $comments->excerpt($num, '...'); ?></a>
<?php endwhile; ?>

其中 ignoreAuthor=true 可以过滤掉博主自己的评论。

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 boolean
 */
function isMobile(){
    $user_agent = $_SERVER['HTTP_USER_AGENT'];
    $mobile_browser = Array(
        "mqqbrowser", // 手机QQ浏览器
        "opera mobi", // 手机opera
        "juc","iuc", 'ucbrowser', // uc浏览器
        "fennec","ios","applewebKit/420","applewebkit/525","applewebkit/532","ipad","iphone","ipaq","ipod",
        "iemobile", "windows ce", // windows phone
        "240x320","480x640","acer","android","anywhereyougo.com","asus","audio","blackberry",
        "blazer","coolpad" ,"dopod", "etouch", "hitachi","htc","huawei", "jbrowser", "lenovo",
        "lg","lg-","lge-","lge", "mobi","moto","nokia","phone","samsung","sony",
        "symbian","tablet","tianyu","wap","xda","xde","zte"
    );
    $is_mobile = false;
    foreach ($mobile_browser as $device) {
        if (stristr($user_agent, $device)) {
            $is_mobile = true;
            break;
        }
    }
    return $is_mobile;
}

输出完备的文章标题

一般是处于 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')): ?><!-- 页面为首页时 -->
    文章列表 &raquo; 第&nbsp;<?php echo $this->_currentPage; ?>&nbsp;页
<?php elseif ($this->is('post')): ?><!-- 页面为文章单页时 -->
    文章 &raquo; <?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

暂时就这些,后续想到再添加。

Tags:none
上一篇
打赏
下一篇

添加新评论

已有 8 条评论

 援军 3 天前 • |

马克收藏,官方的文档看得我云里雾里

 熊猫小A 3 天前 • |
@援军

Typecho 就是缺完善的文档…开发起来太累了,好多地方都需要读源码

 山小炮 2 星期前 • |

整理的很好,太赞了~~

 熊猫小A 2 星期前 • |
@山小炮

谢谢~

 AoaBo's 4 星期前 • |

感谢分享,收藏了!

 熊猫小A 4 星期前 • |
@AoaBo's

能帮到你就太好啦

 mikusa 4 星期前 • |

以为进错博……

 熊猫小A 4 星期前 • |
@mikusa

噗,本来主题是仿的好奇心日报,做完后发现和广树的很像,干脆破罐子破摔了……