Typecho教程:一些typecho相关的教程

Typecho根据文章cid获取文章信息

如上图,就是基于这个功能,实现的轮播图,填写了文章cid,获取了文章标题,描述,缩略图和链接。其实这个功能,应该有很多人发过,不过大多都是需要写查询函数的,我呢因为懒,所以经过试验发现了这种不用自己写函数的写法。代码如下代码,其中cid=1就是获取cid为1的文章信息,可以改成你需要获取的文章cid。<?php $this->widget('Widget_Archive@indexxiu', 'pageSize=1&type=post', 'cid=1')->to($ji); ?> 标题:<?php $ji->title(); ?> 链接:<?php $ji->permalink(); ?> 描述:<?php $ji->description(); ?> ...获取多个上边的截图,就是获取了三个文章,来实现的,代码如下,其中166,163,160就是对应的三个文章的cid<?php $lunbo="166,163,160"; $hang = explode(",", $lunbo); $n=count($hang); $html=""; for($i=0;$i<$n;$i++){ $this->widget('Widget_Archive@lunbo'.$i, 'pageSize=1&type=post', 'cid='.$hang[$i])->to($ji); if($ji->fields->thumb){$img=$ji->fields->thumb;} if($i==0){$no=" sx_no";}else{$no="";} $html=$html.'<div class="sx_vt'.$no.'" name="'.$i.'" title="'.$ji->title.'" intro="'.$ji->description.'" hsrc="'.$ji->permalink.'"><img src="https://demo.lingyuji.cn/slideshow/images/load.jpg" bsrc="'.$img.'" alt="'.$ji->title.'"></div>'; } echo $html; ?>原理就是利用for循环,来实现的多次点播。提示代码中的Widget_Archive@后面的参数可以随便写,同一个页面多次使用时参数不能相同,否则会重复输出同一条数...

阅读文章
Typecho 无插件实现即时搜索

在functions.php最后面添加以下,通过每次访问判断文件间隔时间来达到更新缓存的目的。上方js里的search_a路径需要填写完整路径+/caches/cache.json/** * 静态缓存类 */ class cacheFile { private $_dir; const EXT = '.json'; public function __construct() { $this->_dir = dirname(__FILE__).'/caches/'; } public function cacheData($key, $value = '', $path = '') { $filePath = $this->_dir.$path.$key.self::EXT; if ($value !== '') { // 如果设置为 null,则删除缓存文件 if (is_null($value)) { return unlink($filePath); } $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0777); } // 该函数将返回写入到文件内数据的字节数,失败时返回FALSE return file_put_contents($filePath, $value); } // 如果已经存在该文件,直接返回文件里面的内容 if (!is_file($filePath)) { return false; } else { echo $filePath; // The function returns the read data 或者在失败时返回 FALSE. return json_decode(file_get_contents($filePath), true); } } } if(!file_exists($flag)) { touch($flag); $TheFile = dirname(__FILE__).'/caches/cache.json'; $cacheFile = new cacheFile(); $vowels = array("[", "{","]","}","<",">","\r\n", "\r", "\n","-","'",'"','`'," ",":",";",'\\'," "); Typecho_Widget::widget('Widget_Contents_Post_Recent','pageSize=10000')->to($archives); while($archives->next()): $output .= '{"this":"post","link":"'.$archives->permalink.'","title":"'.$archives->title.'","comments":"'.$archives->commentsNum0.'","text":"'.str_replace($vowels, "",$archives->text).'"},'; endwhile; Typecho_Widget::widget('Widget_Contents_Page_List')->to($pages); while($pages->next()): $output .= '{"this":"page","link":"'.$pages->permalink.'","title":"'.$pages->title.'","comments":"'.$pages->commentsNum0.'","text":"'.str_replace($vowels, "",$pages->text).'"},'; endwhile; Typecho_Widget::widget('Widget_Metas_Tag_Cloud','ignoreZeroCount=1&limit=10000')->to($tags); while ($tags->next()): $output .= '{"this":"tag","link":"'.$tags->permalink.'","title":"'.$tags->name.'","comments":"0","text":"'.str_replace($vowels, "",$tags->description).'"},'; endwhile; Typecho_Widget::widget('Widget_Metas_Category_List')->to($category); while ($category->next()): $output .= '{"this":"category","link":"'.$category->permalink.'","title":"'.$category->name.'","comments":"0","text":"'.str_replace($vowels, "",$category->description).'"},'; endwhile; $output = substr($output,0,strlen($output)-1); $data = '['.$output.']'; if (file_exists($TheFile)) { if ( time() - filemtime( $TheFile) > 1800){ $cacheFile->cacheData('cache', $data); }; //5分钟300秒,时间可以自己调整 } else { $cacheFile->cacheData('cache', $data); }; }HTML结构:<div class="ins-search" id="search"> <div class="ins-search-mask"> </div> <div class="ins-search-container"> <div class="ins-input-wrapper"> <form id="search-form" method="post" action="./" role="search"> <input id="search-input" type="text" name="s" class="ins-search-input" placeholder="想要查找什么..."> </form> <span id="close" class="ins-close ins-selectable">×</span> </div> <div class="ins-section-wrapper"> <a id="Ty" href="#"></a> <div class="ins-section-container" id="PostlistBox"> <section class="ins-section"><header class="ins-section-header">文章</header> <div class="ins-selectable ins-search-item" data-url="/"> <header>测试</header> <p class="ins-search-preview"> 及时搜索结果 </p> </div> </section><section class="ins-section"><header class="ins-section-header">页面</header> <div class="ins-selectable ins-search-item" data-url="/"> <header>测试</header> </div> </section><section class="ins-section"><header class="ins-section-header">分类</header> <div class="ins-selectable ins-search-item" data-url="/"> <header>测试<span class="ins-slug">测试</span></header> </div> </section><section class="ins-section"><header class="ins-section-header">标签</header> <div class="ins-selectable ins-search-item" data-url="/"> <header>测试<span class="ins-slug">测试</span></header> </div> </section> </div> </div> </div> </div>CSS:.ins-search{display:none;z-index:999} .ins-search a{-webkit-transition:none;transition:none} .ins-search header{position:unset;width:auto} .ins-selectable{cursor:pointer} .ins-search-container,.ins-search-mask{position:fixed} .ins-search-mask{top:0;left:0;width:100%;height:100%;z-index:100;background:rgba(0,0,0,.85)} .ins-input-wrapper{position:relative} .ins-search-input{width:100%;border:none;outline:0;font-size:16px;-webkit-box-shadow:none;box-shadow:none;font-weight:200;border-radius:0;background:#fff;line-height:20px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:12px 40px 12px 20px;border-bottom:1px solid #e2e2e2} .ins-close{top:50%;right:10px;width:20px;height:20px;font-size:16px;margin-top:-15px;position:absolute;text-align:center;display:inline-block;font:22px/30px Arial,Helvetica Neue,Helvetica,sans-serif;color:#888;font-weight:300} .ins-close:hover{color:#000} .ins-search-container{left:50%;top:100px;z-index:101;bottom:100px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:540px;margin-left:-270px;border:1px solid #ccc;border-radius:.28571429rem;background:#fff;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.08);box-shadow:0 1px 3px 0 rgba(0,0,0,.08)} @media screen and (max-width:559px),screen and (max-height:479px){.ins-search-container{top:0;left:0;margin:0;width:100%;height:100%;background:#f7f7f7} } .ins-section-wrapper{left:0;right:0;top:45px;bottom:0;overflow-y:auto;position:absolute} .ins-section-container{position:relative;background:#f7f7f7} .ins-section{font-size:14px;line-height:16px} .ins-section .ins-search-item,.ins-section .ins-section-header{padding:8px 15px} .ins-section .ins-section-header{color:#9a9a9a;border-bottom:1px solid #e2e2e2} .ins-section .ins-slug{margin-left:5px;color:#9a9a9a} .ins-section .ins-slug:before{content:'('} .ins-section .ins-slug:after{content:')'} .ins-section .ins-search-item .ins-search-preview,.ins-section .ins-search-item header{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:#616161} .ins-section .ins-search-item header i{margin-right:8px} .ins-section .ins-search-item .ins-search-preview{height:15px;font-size:12px;color:#9a9a9a;margin:5px 0 0 20px} .ins-section .ins-search-item.active,.ins-section .ins-search-item:hover{color:#fff;background:#006bde} .ins-section .ins-search-item.active .ins-search-preview,.ins-section .ins-search-item.active .ins-slug,.ins-section .ins-search-item:hover .ins-search-preview,.ins-section .ins-search-item:hover .ins-slug,.ins-section .ins-search-item:hover header{color:#fff}JS:var QueryStorage = []; search_a("这里填写生成的json链接"); var otxt = document.getElementById("search-input"), list = document.getElementById("PostlistBox"), Record = list.innerHTML; document.all ? otxt.onpropertychange = function() { query(QueryStorage, otxt.value, Record) } : otxt.oninput = function() { query(QueryStorage, otxt.value, Record) }; function search_a(val) { var _xhr = new XMLHttpRequest(); _xhr.open("GET", val, true); _xhr.setRequestHeader("Content-Type", "application/json"); _xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); _xhr.send(val); _xhr.onreadystatechange = function() { if (_xhr.readyState == 4 && _xhr.status == 200) { json = _xhr.responseText; if (json != "") { QueryStorage = JSON.parse(json) } } } } if (!Object.values) Object.values = function(obj) { if (obj !== Object(obj)) throw new TypeError('Object.values called on a non-object'); var val = [], key; for (key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { val.push(obj[key]) } } return val } function a(a) { document.getElementById("Ty").href = a.getAttribute("href"), document.getElementById("Ty").click() } function query(a, b, c) { var n, o, p, q, d = "", e = "", f = "", g = "", h = "", i = '<div class="ins-selectable ins-search-item" onclick="a(this)" href="', j = '<section class="ins-section"><header class="ins-section-header">', k = "</header>", l = "</section>", m = Cx(a, b); for (n = 0; n < Object.keys(m).length; n++) switch (o = m[n].this) { case "post": e = e + i + m[n].link + '"><header>' + m[n].title + '</header><p class="ins-search-preview">' + m[n].comments + m[n].text + "</p></div>"; break; case "tag": f = f + i + m[n].link + '"><header>' + m[n].title + '<span class="ins-slug">' + m[n].text + "</span></header></div>"; break; case "category": g = g + i + m[n].link + '"><header>' + m[n].title + '<span class="ins-slug">' + m[n].text + "</span></header></div>"; break; case "page": h = h + i + m[n].link + '"><header>' + m[n].title + '</header><p class="ins-search-preview">' + m[n].comments + m[n].text + "</p></div>" } e && (d = d + j + "文章" + k + e + l), h && (d = d + j + "页面" + k + h + l), g && (d = d + j + "分类" + k + g + l), f && (d = d + j + "标签" + k + f + l), p = document.getElementById("PostlistBox"), q = document.getElementById("search-input"), p.innerHTML = "" == q.value ? c : d } function Cx(arr, q) { i = arr.filter(v = > Object.values(v).some(v = > new RegExp(q + '').test(v))); return I...

阅读文章
Typecho自定义个人设置页面

之前写过《typecho前台修改个人设置》这么一篇文章,但是里面的html结构是写死的,不够灵活。由因为我现在的模板使用的前端框架,前端框架只要改改html结构就能作出很好看的样式,于是乎我就折腾了这个。核心代码个人资料页面<?php Typecho_Widget::widget('Widget_Security')->to($security); ?> <form action="<?php $security->index('/action/users-profile'); ?>" method="post" enctype="application/x-www-form-urlencoded"> <label>用户ID</label> <?php $this->user->uid() ?> <label>昵称</label> <input type="text" name="screenName" value="<?php $this->user->screenName(); ?>"/> <label>邮箱</label> <input type="text" name="mail" value="<?php $this->user->mail(); ?>"/> <label>网站</label> <input type="text" name="url" value="<?php $this->user->url(); ?>"/> <input name="do" type="hidden" value="profile"> <button type="submit">确定</button> </form> 密码修改页面<?php Typecho_Widget::widget('Widget_Security')->to($security); ?> <form action="<?php $security->index('/action/users-profile'); ?>" method="post" enctype="application/x-www-form-urlencoded"> <label><font style="vertical-align: inherit;">密码</label> <input type="password" name="password"/> <label>重复密码</label> <inputtype="password" name="confirm"/> <input name="do" type="hidden" value="password"> <button type="submit">确定</button> </form>html结构根据自己写的样式调整美化即...

阅读文章
Typecho 计算文章内图片标签数量

适用于图片型主题.仅计算文章内使用的img标签数量.在主题function.php里添加如下:function imgNum($content){ $output = preg_match_all('#<img(.*?) src="([^"]*/)?(([^"/]*)\.[^"]*)"(.*?)>#', $content,$s); $cnt = count( $s[1] ); return $cnt; }应用环境为:文章列表拓展:function imgNumCCC($content){ $output = preg_match_all('#<img(.*?) src="([^"]*/)?(([^"/]*)\.[^"]*)"(.*?)>#', $content,$s); $cnt = count( $s[1] ); if(intval($cnt)>=4){ //当图片大于或等于4张时,启用此方法 for ($i=1; $i < 5; $i++) { if($i==4){ //判断是否最后一张,如果为最后一张,则进行计算/增加className $n = 'ImgBOX NUM" title="'.(intval($cnt)-4).'+"'; //最后一张时的className }else{ $n = 'ImgBOX"'; //正常的className } $c .= '<div class=" '.$n.'><img data-src="'.$s[2][$i].$s[3][$i].'"></div>';//因为我加了图片缓加载,所以图片链接放在了data-src里. } $c = '<div class="i-img">'.$c.'</div>'; //输出 } echo $c; }拓展示例...

阅读文章
Typecho 仿Github回复

用过Github评论的都知道,回复会顺带主评论内容,这样相较于博客的@某某用户、或回复某某用户要直观许多,不需要上下文联系.图例:其实Typecho也可以实现这样的效果,深入研究也可达到多层嵌套的效果.图例:实现方法如下: 在主题的Function.php中加入如下,之后,在主题评论列表回复处增加<?php getCommentHF($comments->coid)?>,即可实现 function getCommentHF($coid){ $parser = new HyperDown(); //Typecho内置函数 将md转为html $db = Typecho_Db::get(); $prow = $db->fetchRow($db->select('parent') ->from('table.comments') ->where('coid = ? AND status = ?', $coid, 'approved')); $parent = $prow['parent']; if ($parent != "0") { $arow = $db->fetchRow($db->select('text','author','status') ->from('table.comments') ->where('coid = ?', $parent)); $text = $arow['text']; $author = $arow['author']; $status = $arow['status']; if($author){ if($status=='approved'){ $href = '<blockquote><a class="at" uid="'.$parent.'" onclick="scrollt(\'comment-'.$parent.'\'); return false;">@'.$author.'</a><br>'.$parser->makeHtml($text).'</blockquote>';; }else if($status=='waiting'){ $href = '<a>评论审核中···</a>'; } } echo $href; } else { echo ""; } }拓展:HyperDown() 是typecho的内置MD解析为html函数函数使用方法: $parser = new HyperDown(); $parser->makeHtml($text...

阅读文章
自定义Typecho加密文章的Html结构

为什么要自定义?我们先看下正常的typecho加密文章的html代码结构<form class="protected" action="一个链接地址" method="post"> <p class="word">请输入密码访问</p> <p> <input type="password" class="text" name="protectPassword"> <input type="hidden" name="protectCID" value="文章id"> <input type="submit" class="submit" value="提交"> </p> </form>那么我们作为模板的作者,如果要写这部分的样式,就必须根据他的class来写样式;但是如果我用的是前端框架,框架已经内置好了一些表单样式,我们为什么不用呢?因为typecho默认的结构,无法套用,所以就有了这篇文章。自定义加密文章的Html将模板post.php中的<?php $this->content(); ?>换为以下代码即可,其中html结构可根据自己模板架构自行调整。<!--判断文章是否加密--> <?php if($this->hidden||$this->titleshow): ?> <!--如果加密,输出自定义的表单格式--> <form action="<?php echo Typecho_Widget::widget('Widget_Security')->getTokenUrl($this->permalink); ?>" method="post"> <div class="form-group mb-3"> <label>请输入密码访问</label> <div class="input-group"> <input type="password" class="text" name="protectPassword" class="form-control" placeholder="请输入密码" aria-label="请输入密码"> <input type="hidden" name="protectCID" value="<?php $this->cid(); ?>" /> <div class="input-group-append"> <button class="btn btn-primary" type="submit">提交</button> </div> </div> </div> </form> <?php else: ?> <!--如果未加密,输出文章内容--> <?php $this->content(); ?> <?php endif;?>其中$this->titleshow是用来兼容titleshow插件的。关于Titleshow插件Typecho文章密码保护功能,默认会强制标题为”此内容被密码保护“,隐藏文章标签,强制文章评论数为0,隐藏文章内容,并且不允许文章进行评论,还会返回403。Titleshow插件,完美的解决了这些问题,加密文章可以显示标题,允许评论,不返回403状态等等,让文章加密功能只加密文章内容不影响其他。那么,这么好的插件去哪里下载呢?这里:https://github.com/jrotty/Titleshow题外话只是因为想偷懒不想写css,才有了本文,本身没啥技术含量,只是最开始不兼容Titleshow插件,所以就没有发文章出来,毕竟自己写的教程不兼容自己写的插件,岂不是很难堪233。所以今天给Titleshow插件升级了下,可以完美兼容了,嘿嘿...

阅读文章
Typecho主题前台实现删除文章功能

提示操作千万条,备份第一条,删文不谨慎,博主两行泪代码将下面的代码放到 post.php 中即可<?php Typecho_Widget::widget('Widget_Security')->to($security); ?> <a href="<?php $security->index('/action/contents-post-edit?do=delete&cid='.$this->cid); ?>">删除文章</a>点击按钮立即删除文章!完善上面的代码虽然实现了功能,但却泯灭了人性!下面我们完善下人性部分,代码改为<?php Typecho_Widget::widget('Widget_Security')->to($security); ?> <a href="<?php $security->index('/action/contents-post-edit?do=delete&cid='.$this->cid); ?>" onclick="javascript:return p_del()">删除文章</a> <script> function p_del() { var msg = "您真的确定要删除吗?"; if (confirm(msg)==true){ return true; }else{ return false; } } </script>这样弄好,点击按钮会弹出确认框,问其是否删除文章,用户确认后才会删除,比较符合操作习惯!后语post.php 页面成功删除文章后,因为文章不存在了,所以页面自动跳到了 404 页面,不知道怎么才能比较好的让它跳到首页Q...

阅读文章
Typecho评论回复按钮文字自定义

typecho评论的回复与回复取消按钮函数调用如下:回复按钮:<?php $comments->reply(); ?> 取消回复按钮:<?php $comments->cancelReply(); ?>输出的结果大概是这样:回复按钮:<a href="链接" rel="nofollow" onclick="代码">回复</a> 取消回复按钮:<a id="cancel-comment-reply-link" href="链接" rel="nofollow" onclick="代码">取消回复</a>那么我们如何自定义超链接中的文字呢?查了typecho源码发现,这两个函数是可以传参数的,传递的参数就是修改默认文字的。于是乎我就这样改了下<?php $comments->reply('<i class="mdi mdi-reply"></i>回复'); ?> <?php $comments->cancelReply('<i class="mdi mdi-reply"></i>取消'); ?>效果如下...

阅读文章
Typecho获取随机文章函数

function getRandomPosts($random=5){ $db = Typecho_Db::get(); $adapterName = $db->getAdapterName();//兼容非MySQL数据库 if($adapterName == 'pgsql' || $adapterName == 'Pdo_Pgsql' || $adapterName == 'Pdo_SQLite' || $adapterName == 'SQLite'){ $order_by = 'RANDOM()'; }else{ $order_by = 'RAND()'; } $sql = $db->select()->from('table.contents') ->where('status = ?','publish') ->where('table.contents.created <= ?', time()) ->where('type = ?', 'post') ->limit($random) ->order($order_by); $result = $db->fetchAll($sql); if($result){ foreach($result as $val){ $obj = Typecho_Widget::widget('Widget_Abstract_Contents'); $val = $obj->push($val); $post_title = htmlspecialchars($val['title']); $permalink = $val['permalink']; echo '<a href="'.$permalink.'" title="'.$post_title.'"><h5 class="card-title">'.$post_title.'</h5></a>'; } } }需要使用时在模板中调用<?php getRandomPosts(10);?>即可,这个随机文章函数的好处就是不光兼容mysql还兼容sqlite数据库。完整使用方法1.将上面完整的随机文章代码丢进主题文件夹的function.php里面,保存;2.在需要添加随机文章的地方加上代码:<?php getRandomPosts(10);?>,保存;3.刷新页面,搞定!文章转自:http://www.7tec.cn/246.ht...

阅读文章
Typecho不使用插件实现Ajax评论功能

原文出自绛木子博客:https://lixianhua.com/te_ajax_comment_without_pluign.html为了不使用插件实现Ajax评论功能需要实现:1,监听评论表单,改用ajax方式提交2,创建新的评论表单提交地址(用Typecho主题提供的系统方法themeInit实现)当访问文章加载主题时,themeInit方法首先被加载,可在此方法中判断是否为添加评论的操作,即新的评论表单地址为文章的链接(permalink).具体判断方法如下// 主题初始化 function themeInit($archive){ // 判断是否是添加评论的操作 // 为文章或页面、post操作,且包含参数`themeAction=comment`(自定义) if($archive->is('single') && $archive->request->isPost() && $archive->request->is('themeAction=comment')){ // 为添加评论的操作时 ajaxComment($archive); } }要实现ajax评论,则无法使用系统默认的feedback,这里我们将复制feedback功能并改造为我们需要的方法/** * ajaxComment * 实现Ajax评论的方法(实现feedback中的comment功能) * @param Widget_Archive $archive * @return void */ function ajaxComment($archive){ $options = Helper::options(); $user = Typecho_Widget::widget('Widget_User'); $db = Typecho_Db::get(); // Security 验证不通过时会直接跳转,所以需要自己进行判断 // 需要开启反垃圾保护,此时将不验证来源 if($archive->request->get('_') != Helper::security()->getToken($archive->request->getReferer())){ $archive->response->throwJson(array('status'=>0,'msg'=>_t('非法请求'))); } /** 评论关闭 */ if(!$archive->allow('comment')){ $archive->response->throwJson(array('status'=>0,'msg'=>_t('评论已关闭'))); } /** 检查ip评论间隔 */ if (!$user->pass('editor', true) && $archive->authorId != $user->uid && $options->commentsPostIntervalEnable){ $latestComment = $db->fetchRow($db->select('created')->from('table.comments') ->where('cid = ?', $archive->cid) ->where('ip = ?', $archive->request->getIp()) ->order('created', Typecho_Db::SORT_DESC) ->limit(1)); if ($latestComment && ($options->gmtTime - $latestComment['created'] > 0 && $options->gmtTime - $latestComment['created'] < $options->commentsPostInterval)) { $archive->response->throwJson(array('status'=>0,'msg'=>_t('对不起, 您的发言过于频繁, 请稍侯再次发布'))); } } $comment = array( 'cid' => $archive->cid, 'created' => $options->gmtTime, 'agent' => $archive->request->getAgent(), 'ip' => $archive->request->getIp(), 'ownerId' => $archive->author->uid, 'type' => 'comment', 'status' => !$archive->allow('edit') && $options->commentsRequireModeration ? 'waiting' : 'approved' ); /** 判断父节点 */ if ($parentId = $archive->request->filter('int')->get('parent')) { if ($options->commentsThreaded && ($parent = $db->fetchRow($db->select('coid', 'cid')->from('table.comments') ->where('coid = ?', $parentId))) && $archive->cid == $parent['cid']) { $comment['parent'] = $parentId; } else { $archive->response->throwJson(array('status'=>0,'msg'=>_t('父级评论不存在'))); } } $feedback = Typecho_Widget::widget('Widget_Feedback'); //检验格式 $validator = new Typecho_Validate(); $validator->addRule('author', 'required', _t('必须填写用户名')); $validator->addRule('author', 'xssCheck', _t('请不要在用户名中使用特殊字符')); $validator->addRule('author', array($feedback, 'requireUserLogin'), _t('您所使用的用户名已经被注册,请登录后再次提交')); $validator->addRule('author', 'maxLength', _t('用户名最多包含200个字符'), 200); if ($options->commentsRequireMail && !$user->hasLogin()) { $validator->addRule('mail', 'required', _t('必须填写电子邮箱地址')); } $validator->addRule('mail', 'email', _t('邮箱地址不合法')); $validator->addRule('mail', 'maxLength', _t('电子邮箱最多包含200个字符'), 200); if ($options->commentsRequireUrl && !$user->hasLogin()) { $validator->addRule('url', 'required', _t('必须填写个人主页')); } $validator->addRule('url', 'url', _t('个人主页地址格式错误')); $validator->addRule('url', 'maxLength', _t('个人主页地址最多包含200个字符'), 200); $validator->addRule('text', 'required', _t('必须填写评论内容')); $comment['text'] = $archive->request->text; /** 对一般匿名访问者,将用户数据保存一个月 */ if (!$user->hasLogin()) { /** Anti-XSS */ $comment['author'] = $archive->request->filter('trim')->author; $comment['mail'] = $archive->request->filter('trim')->mail; $comment['url'] = $archive->request->filter('trim')->url; /** 修正用户提交的url */ if (!empty($comment['url'])) { $urlParams = parse_url($comment['url']); if (!isset($urlParams['scheme'])) { $comment['url'] = 'http://' . $comment['url']; } } $expire = $options->gmtTime + $options->timezone + 30*24*3600; Typecho_Cookie::set('__typecho_remember_author', $comment['author'], $expire); Typecho_Cookie::set('__typecho_remember_mail', $comment['mail'], $expire); Typecho_Cookie::set('__typecho_remember_url', $comment['url'], $expire); } else { $comment['author'] = $user->screenName; $comment['mail'] = $user->mail; $comment['url'] = $user->url; /** 记录登录用户的id */ $comment['authorId'] = $user->uid; } /** 评论者之前须有评论通过了审核 */ if (!$options->commentsRequireModeration && $options->commentsWhitelist) { if ($feedback->size($feedback->select()->where('author = ? AND mail = ? AND status = ?', $comment['author'], $comment['mail'], 'approved'))) { $comment['status'] = 'approved'; } else { $comment['status'] = 'waiting'; } } if ($error = $validator->run($comment)) { $archive->response->throwJson(array('status'=>0,'msg'=> implode(';',$error))); } /** 添加评论 */ $commentId = $feedback->insert($comment); if(!$commentId){ $archive->response->throwJson(array('status'=>0,'msg'=>_t('评论失败'))); } Typecho_Cookie::delete('__typecho_remember_text'); $db->fetchRow($feedback->select()->where('coid = ?', $commentId) ->limit(1), array($feedback, 'push')); // 返回评论数据 $data = array( 'cid' => $feedback->cid, 'coid' => $feedback->coid, 'parent' => $feedback->parent, 'mail' => $feedback->mail, 'url' => $feedback->url, 'ip' => $feedback->ip, 'agent' => $feedback->agent, 'author' => $feedback->author, 'authorId' => $feedback->authorId, 'permalink' => $feedback->permalink, 'created' => $feedback->created, 'datetime' => $feedback->date->format('Y-m-d H:i:s'), 'status' => $feedback->status, ); // 评论内容 ob_start(); $feedback->content(); $data['content'] = ob_get_clean(); $data['avatar'] = Typecho_Common::gravatarUrl($data['mail'], 48, Helper::options()->commentsAvatarRating, NULL, $archive->request->isSecure()); $archive->response->throwJson(array('status'=>1,'comment'=>$data)); }当已经在functions.php文件中添加完上述方法时,就已经可以接收ajax评论了。此时我们需要修改评论表单添加的方式及提交的地址。在footer.php文件中添加方法// 依赖jquery,请自行加载 $(function(){ // 监听评论表单提交 $('#comment-form').submit(function(){ var form = $(this), params = form.serialize(); // 添加functions.php中定义的判断参数 params += '&themeAction=comment'; // 解析新评论并附加到评论列表 var appendComment = function(comment){ // 评论列表 var el = $('#comments > .comment-list'); if(0 != comment.parent){ // 子评论则重新定位评论列表 var el = $('#comment-'+comment.parent); // 父评论不存在子评论时 if(el.find('.comment-children').length < 1){ $('<div class="comment-children"><ol class="comment-list"></ol></div>').appendTo(el); }else if(el.find('.comment-children > .comment-list').length <1){ $('<ol class="comment-list"></ol>').appendTo(el.find('.comment-children')); } el = $('#comment-'+comment.parent).find('.comment-children').find('.comment-list'); } if(0 == el.length){ $('<ol class="comment-list"></ol>').appendTo($('#comments')); el = $('#comments > .comment-list'); } // 评论html模板,根据具体主题定制 var html = '<li id="comment-{coid}" class="comment-body comment-ajax"><div class="comment-author"><span><img class="avatar" src="{avatar}" alt="{author}" width="32" height="32"></span><cite class="fn">{author}</cite></div><div class="comment-meta"><a href="{permalink}"><time>{datetime}</time></a></div><div class="comment-content">{content}</div></li>'; $.each(comment,function(k,v){ regExp = new RegExp('{'+k+'}', 'g'); html = html.replace(regExp, v); }); $(html).appendTo(el); } // ajax提交评论 $.ajax({ url: '<?php $this->permalink();?>', type: 'POST', data: params, dataType: 'json', beforeSend: function() { form.find('.submit').addClass('loading').html('<i class="icon icon-loading icon-pulse"></i> 提交中...').attr('disabled','disabled');}, complete: function() { form.find('.submit').removeClass('loading').html('提交评论').removeAttr('disabled');}, success: function(result){ if(1 == result.status){ // 新评论附加到评论列表 appendComment(result.comment); form.find('textarea').val(''); }else{ // 提醒错误消息 alert(undefined === result.msg ? '<?php _e('评论出错!');?>' : result.msg); } }, error:function(xhr, ajaxOptions, thrownError){ alert('评论失败,请重试'); } }); return false; }); });ajax评论需要自定义评论模板,获取使用其他方式拼接评论html。评论form表单的提交按钮需要添加class="submit"或修改代码为其他自定义的class注:需开启评论的反垃圾保护以上为转载内容,在实际投入使用时遇到了两点问题一,通过ajax进行评论,邮件通知插件并不会发出通知!原因是因为重写了评论的函数,而函数中没写评论完成后触发插件接口,所以邮件通知插件不会发邮件给作者。解决方法也很简单在上述php代码$db->fetchRow($feedback->select()->where('coid = ?', $commentId)->limit(1), array($feedback, 'push'));后面加入$feedback->pluginHandle()->finishComment($feedback);即可。二,评论过滤插件也会失效!原因也是没有写入评论过滤的接口这个接口我试着往里面写,没有成功,所以换了个方式来解决,直接在里面写过滤,而不是用插件过滤评论。比如在$commentId = $feedback->insert($comment);前面加入if (preg_match("/[\x{4e00}-\x{9fa5}]/u", $comment['text']) == 0) { $archive->response->throwJson(array('status'=>0,'msg'=>_t('评论内容请不少于一个中文汉字'))); }即可屏蔽非中文评论,建议同时使用评论过滤插件,因为有些垃圾评论是通过评论机器人完成的,并不会经过ajax评论,所以还需要继续使用评论过滤插件,亦或者在模板层面上用上评论过滤接口(我是这么做的,目前来看大概是有效果...

阅读文章
Typecho无插件实现同分类文章上一篇下一篇

这个功能因为自己突然有需要,然后去论坛搜了下发现也有人问过,帖子里有人推荐了个插件可以实现,不过我突然想到个模板函数,就是自定义文章上下篇链接的,好像就可以直接实现,不需要插件。函数如下:/** * 显示下一篇 * * @access public * @param string $default 如果没有下一篇,显示的默认文字 * @return void */ function theNext($widget, $default = NULL) { $db = Typecho_Db::get(); $sql = $db->select()->from('table.contents') ->where('table.contents.created > ?', $widget->created) ->where('table.contents.status = ?', 'publish') ->where('table.contents.type = ?', $widget->type) ->where('table.contents.password IS NULL') ->order('table.contents.created', Typecho_Db::SORT_ASC) ->limit(1); $content = $db->fetchRow($sql); if ($content) { $content = $widget->filter($content); $link = '<a href="' . $content['permalink'] . '" title="' . $content['title'] . '">下一篇</a>'; echo $link; } else { echo $default; } } /** * 显示上一篇 * * @access public * @param string $default 如果没有下一篇,显示的默认文字 * @return void */ function thePrev($widget, $default = NULL) { $db = Typecho_Db::get(); $sql = $db->select()->from('table.contents') ->where('table.contents.created < ?', $widget->created) ->where('table.contents.status = ?', 'publish') ->where('table.contents.type = ?', $widget->type) ->where('table.contents.password IS NULL') ->order('table.contents.created', Typecho_Db::SORT_DESC) ->limit(1); $content = $db->fetchRow($sql); if ($content) { $content = $widget->filter($content); $link = '<a href="' . $content['permalink'] . '" title="' . $content['title'] . '">上一篇</a>'; echo $link; } else { echo $default; } }调用代码<?php thePrev($this); ?>和<?php theNext($this); ?>。可以看出里面用的是数据库语句,那么实现[Typecho无插件实现同分类文章上一篇下一篇],岂不是插入个where语句就行了,对,就是这么简单。首先在函数里开头部分加入如下代码,获取当前文章的分类mid@$mid=intval($widget->categories[0]['mid']);然后在数据库语句中合适位置插入一个where语句->where('table.relationships.mid = ?', $mid)好了,这就大功告成了,希望能帮得到各...

阅读文章
搞了个Typecho在线文档

其实在去年11月份左右的时候,我就把官方文档抄了份markdown格式的,并且修改了些小细节,添加了一些我的理解,并且打包发到几个Q群里面,感觉看到的人不多,而且不方便。于是用docsify撸了个在线版的,然后觉得在线版放github比较好,大家可以一起修改,一起提提意见,于是乎花了几分钟把他又布置到了github上。项目地址:https://github.com/jrotty/jrotty.github.io在线文档地址:https://docs.qqdie.com/其实github我用的次数不多,不是很熟悉,再加上英语渣,所以以后提交代码什么的可能会出现点疏漏,大家见谅!今天顺便把官方的插件文档也抄了过来,都改成markdown格式了,停费时间的,希望这个文档能帮助到大家。如果您所在的地区上不去github没办法访问在线文档,可以访问http://lab.qqdie.com/docs/ 进行浏览,这个是布置到我自己的主机的,国内应该可以流畅访问。问:都有官方文档了,为什么还要在重复搞个在线文档?答:主要是官方文档手机端访问浏览体验较差,官方文档无人维护,热心博友有心也没有权限维护文...

阅读文章
Typecho完美实现回复可见功能

之前转载过这么一篇文章《typecho非插件实现回复可见功能》,可以实现回复可见功能,但是有个问题,在文章列表页展示文章缩略内容时,如果回复可见内容刚好在缩略内容的位置上时,就会暴露出来,同时Feed里面也会暴露这个问题,那么如何解决呢,下面请看如何几近完美的实现回复可见功能:步骤一就是《typecho非插件实现回复可见功能》里面的内容将post.php中的<?php $this->content(); ?>换成<?php $db = Typecho_Db::get(); $sql = $db->select()->from('table.comments') ->where('cid = ?',$this->cid) ->where('mail = ?', $this->remember('mail',true)) ->where('status = ?', 'approved') //只有通过审核的评论才能看回复可见内容 ->limit(1); $result = $db->fetchAll($sql); if($this->user->hasLogin() || $result) { $content = preg_replace("/\[hide\](.*?)\[\/hide\]/sm",'<div class="reply2view">$1</div>',$this->content); } else{ $content = preg_replace("/\[hide\](.*?)\[\/hide\]/sm",'<div class="reply2view">此处内容需要评论回复后方可阅读。</div>',$this->content); } echo $content ?>步骤二解决缩略内容和feed暴露问题。在functions.php中加入如下代码即可Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('myyodux','one'); Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('myyodux','one'); class myyodux { public static function one($con,$obj,$text) { $text = empty($text)?$con:$text; if(!$obj->is('single')){ $text = preg_replace("/\[hide\](.*?)\[\/hide\]/sm",'',$text); } return $text; } }就是用插件接口,在缩略内容输出之前,隐藏掉或者替换掉回复可见内容,同时使用if判断,来针对非single页面进行隐藏。步骤三使用方法在写文章需要隐藏部分内容时用以下写法(去掉@)[@hide]要隐藏的内容[/hide]css参考样式.reply2view { background:#f8f8f8; padding:10px 10px 10px 40px; position:relative }心里话在写yodu模板的时候,因为网友需要,我就找了教程把回复可见功能加上了,当时也是知道这个东西的bug,但是自己不会修,最近写了个typecho的搜索插件,看了一些typecho的插件接口和源码,发现用插件接口很好的就能解决了,于是乎水文一下,希望能帮助到一些...

阅读文章
Typecho前台登录/注册

Typecho前台登录前言前台登录是个很方便的功能,无论是作为个人博客还是多人博客,前台登录都会节省用户时间。代码<form action="<?php $this->options->loginAction()?>" method="post" name="login" rold="form"> <input type="hidden" name="referer" value="<?php $this->options->siteUrl(); ?>"> <input type="text" name="name" autocomplete="username" placeholder="请输入用户名" required/> <input type="password" name="password" autocomplete="current-password" placeholder="请输入密码" required/> <button type="submit">登录</button> </form>其中 referer 这个input就指明了登录成功后的跳转位置,现在默认的首页,可以修改value的值来自行定义登录成功跳转得地址。Typecho前台注册代码<form action="<?php $this->options->registerAction();?>" method="post" name="register" role="form"> <input type="hidden" name="_" value="<?php echo $this->security->getToken($this->request->getRequestUrl());?>"> 用户名<input type="text" name="name"> 邮箱:<input type="email" id="mail" name="mail" > <button type="submit" name="loginsubmit" value="true">注册</button> </form>说明用户进入注册页面,只会要求用户填写用户名和邮箱,点击注册按钮后会跳转到程序后台,此时会提示被分配了个临时密码,同时提示用户修改默认密码,填写个人信息如昵称,个人主页等。扩展如果也想像前台登录一样,登陆后自定义跳转页面,需要修改/var/Widget/Register.php这个文件,倒数第三行左右的这个代码$this->response->redirect($this->options->adminUrl);换成如下代码if (NULL != $this->request->referer) { $this->response->redirect($this->request->referer); } else{ $this->response->redirect($this->options->adminUrl); }这样在form里插入<input type="hidden" name="referer" value="跳转地址">即...

阅读文章
typecho前台修改个人设置

typecho皮肤中的author.php就是用户的个人中心,那么如何用它实现用户在前台修改个人信息呢?出人意料的非常简单,只要把后台里面个人设置里面的这些代码搬进来就行了。<section> <h3><?php _e('个人资料'); ?></h3> <ul><li> <label class="typecho-label" for="screenName-0-1"> 用户名</label><?php $this->user->name() ?></li></ul> <?php Typecho_Widget::widget('Widget_Users_Profile')->profileForm()->render(); ?> </sction> <section id="change-password"> <h3><?php _e('密码修改'); ?></h3> <?php Typecho_Widget::widget('Widget_Users_Profile')->passwordForm()->render(); ?> </sction> <?php Typecho_Widget::widget('Widget_Users_Profile')->personalFormList(); ?>但是直接用的话,还要考虑一些事情,比如游客访问进来如果也这样显示岂不是很尴尬,A用户访问B用户时显示也会变尴尬,所以需要加入判断。<?php if($this->user->uid==$this->author->uid && $this->user->hasLogin()): ?> 这里填写上边的代码即可,效果就是只有用户本人访问自己的个人中心,才会显示修改设置 <?php endif; ?>代码中不含有样式,需要自己美化下,具体有什么效果,如下图所...

阅读文章
Typecho数据库常用API

表创建和删除在Typecho插件开发过程中,往往需要创建自己的表。上文提到Typecho_Db类中的query函数,可用于执行所有sql语句,因此我们使用query()来进行表的创建、修改或者删除。$db= Typecho_Db::get(); $prefix = $db->getPrefix(); $db->query('create table '.$prefix.'metas xxxxx');注意,使用query方式创建表的时候,需要在表明前手动添加$prefix前缀,否则在后面的使用过程中会造成困惑。还可以使用table.来代替$prefix,typecho会自动识别并替换成指定的前缀。同理,修改或者删除Typecho数据库中表,按照同样的方式调用query即可。数据查询select,查询表数据select语句是可以说Typecho插件开发中最常用的sql调用。$db = Typecho_Db::get(); $query= $db->select()->from('table.metas'); $result = $db->fetchAll($query);说明:typecho中,.号具有特定的意义,这里table.metas表示这是一个metas表。实际上,typecho是自动将table.的字符使用str_replace替换成了config.inc.php中设定的前缀。举例:a. $db->select()->from('table.metas');将生成SELECT * FROM typecho_metas WHERE (mid= '2' ),其中typecho_是表前缀;b. 而$db->select()->from('metas');将生成SELECT * FROM metas WHERE (mid= '2' ),注意这里没有了表前缀。指定表字段查询有时为了提高查询性能,需要指定查询表中特定的几个字段,那么可以使用下面的方式:$query= $db->select('mid','name')->from('table.metas'); echo $query; //SELECT `mid` , `name` FROM typecho_metas如果联合查询中,两个表存在相同的字段名,那么可以使用table.来指定表名:$query = $db->select('table.contents.cid')->from('table.contents')->join....指定查询条件指定SQL查询的where语句,是最常用的api调用。$query= $db->select('mid','name')->from('table.metas')->where('mid = ?', 2); echo $query; //SELECT * FROM typecho_metas WHERE (`mid` = '2' )如需要指定多个查询条件,直接多次调用where即可,将生成and关系的where条件$db->select('mid','name')->from('table.metas')->where('mid = ?', 2)->where('name like ? ', $name);使用OR关系的查询条件可以使用orWhere()函数来指定SQL查询的或条件。$db->select('mid','name')->from('table.metas')->where('mid = ?', 2)->orWhere('mid = ? ', 3); //SELECT `mid` , `name` FROM typecho_metas WHERE (`mid` = '2' ) OR (`mid` = '3' )指定查询范围在需要分页的场景下,分页是必需的操作。offset()和limit()分别用于指定起始位置和结束位置,即指定查询范围。$query = $db->select('mid','name')->from('table.metas')->offset(2)->limit(3); echo $query;//SELECT `mid` , `name` FROM typecho_metas LIMIT 3 OFFSET 2Typecho中,还提供了一种简写的方法,见page()函数。$query = $db->select('mid','name')->from('table.metas')->page(3,10); echo $query;//SELECT `mid` , `name` FROM typecho_metas LIMIT 10 OFFSET 20 //表示取第三页,并取10条记录。对查询结果进行排序在Typecho中,使用order()函数和Typecho_Db::SORT_DESC指定查询结果的排序方式。$query = $db->select('mid','name')->from('table.metas')->order('mid',Typecho_Db::SORT_DESC); echo $query;//SELECT `mid` , `name` FROM typecho_metas ORDER BY `mid` DESC Tips: Typecho_Db::SORT_ASC 表示升序排序,Typecho_Db::SORT_DESC表示降序排序联合查询联合查询是SQL的常用语法,在Typecho中,同样使用内置函数join()方便地进行联合查询。$query = $db->select() ->from('table.contents') ->join('table.comments', 'table.contents.cid = table.comments.cid',Typecho_Db::LEFT_JOIN) ->where('table.contents.type = ?', 'post'); echo $query; //SELECT * FROM typechocontents LEFT JOIN typecho_comments ON typecho_contents.`cid` = typecho_comments.`cid` WHERE (typecho_contents.`type` = 'post' )1. update,更新表数据Typecho中,使用update()函数来进行更新表操作。但注意,update操作,需要借助于query执行。$update = $db->update('table.metas')->rows(array('name'=>'case_in_cn'))->where('mid=?',6); echo $update;//UPDATE typecho_metas SET `name` = 'some_name' WHERE (`mid`='6' ) //执行后,返回收影响的行数。 $updateRows= $db->query($update);2. insert,插入数据Typecho中,使用insert()函数来进行表插入操作。同样,insert操作需要借助于query函数。$insert = $db->insert('table.metas') ->rows(array('mid' => '22', 'name' => 'hello world')); //将构建好的sql执行, 如果你的主键id是自增型的还会返回insert id $insertId = $db->query($insert);3. delete,删除数据Typecho中使用delete()函数来删除数据表中的行。delete操作用于删除数据表中指定的行,同样需要借助query函数执行。$delete = $db->delete('table.metas') ->where('mid = ?', 2); //将构建好的sql执行, 会自动返回已经删除的记录数 $deletedRows = $db->query($delete);数据库调试查看查询语句在Typecho调试过程中,打印sql语句往往是很有帮助的。对于大于5.2版本的php,直接echo $query即可,对于小于5.2版本,则需要显式调用__toString()函数$select = $db->select()->from('table.metas'); //如果版本大于php5.2 echo $select; //如果小于php5.2 echo $select->__toString();声明文章转自:https://www.typechodev.com/dev/17.ht...

阅读文章
Typecho使用AJAX实时获取评论头像

前言刚才在隔壁看到《WordPress使用AJAX实时获取评论头像》,我就想typecho是不是也能实现这个功能呢!看了《WordPress使用AJAX实时获取评论头像》这个文章后,我理解到,它实际就是给模板内置了个api,通过ajax请求这个api来实时获取邮箱头像地址。懂了原理就简单了PHP部分此代码添加到主题functions.php文件function themeInit($archive) { if(isset($_GET['action']) == 'ajax_avatar_get' && 'GET' == $_SERVER['REQUEST_METHOD'] ) { $host = 'https://secure.gravatar.com/avatar/'; $email = strtolower( $_GET['email']); $hash = md5($email); $sjtx = 'mm'; $avatar = $host . $hash . '?d='.$sjtx; echo $avatar; die(); }else { return; } }如果你的模板已经添加过了themeInit,那么只要向themeInit内部添加if部分即可。JS部分此代码添加到主题js文件$("input#email").blur(function() { var _email = $(this).val(); if (_email != '') { $.ajax({ type: 'GET', data: { action: 'ajax_avatar_get', form: ajaxurl, // 修改为你的Ajax路径 email: _email }, success: function(data) { $('.avatar').attr('src', data); // 修改为你自己的头像标签 } }); // end ajax } return false; });代码中的input#email和.avatar需要根据自己的模板进行适当修改,代码中的ajaxurl可以直接写自己博客地址,或者当前文章地址也行,可以用js获取地址,也可以直接写死。后语其实除了php部分和WordPress稍有不同外,其他没什么不同,js部分直接抄自《WordPress使用AJAX实时获取评论头像...

阅读文章
typecho附件页面可以评论,那么如何找到别人的附件页面地址呢?

前言typecho的附件其实和文章性质一样,附件也有单独的页面,也可以进行评论,附件页面的地址格式如https://qqdie.com/attachment/1293/,1293就是附件的id,其实文章id和附件id是同一个东西,所以有的时候发布文章时id,文章id并不连贯。typecho的附件页面除了管理员外,其他人不能直接找到附件页面,那么怎么做可以找到别人的附件页面,然后进行评论,皮一下呢?答案是用php让php访问从id为1的页面访问,如https://qqdie.com/attachment/1/,如果返回状态404,那么id+1继续访问,直到访问到非404的地址,然后输出该地址,跳出for循环。<?php header('content-type:text/html;charset=utf-8'); for($i=1;$i<300;$i++){ $url = 'https://域名/attachment/'.$i; $headers = get_headers($url); if (strpos($headers[0], '404')) {echo $i;}else{ echo '<br>他的附件页面地址为<br>'.$url;break; } } ?>上述代码中$i就是代表id的,可以看出来我写的是循环到300,就是怕超时,如果300还没出来,就改下i的默认值和,循环变量在跑一遍...[谁让我是渣渣呢!]【部分人开了debug模式,即使不存在的页面也不是404状态,这样的网站就没办法了!】模板作者如何关闭附件页面的评论功能呢答案是用if判断下,如果是附件页面就不输出评论框,并显示提示文字“附件页面禁止评论”<?php if ($this->is('attachment')) : ?><h4 class="comment-close">附件页面禁止评论</h4> <?php else: ?>你的评论框代码部分<?php endif; ?>嗯我已经去了https://get233.com/, https://i.chainwon.com/, https://www.helingqi.com/, https://blog.qwq.moe/, https://www.jimoe.cn/ 这些人的附件页面皮了下。ヽ(✿゚▽゚)ノ后续该文章发布后当晚我就想能不能纯前端的实现这个呢,于是弄了一晚上,然后刚刚做了个视频演示,欢迎大家前去投币:https://www.bilibili.com/video/av2572471...

阅读文章
typecho模板设置数据备份与恢复

typecho模板设置数据会在你换模板的时候被清空,这样的设置其实我还是蛮喜欢的,不会有数据残留。然而WP用户转到typecho后跟我抱怨,说模板换成别的然后再切换回来,之前设置好的数据都没了,希望我在yodu模板上动动手脚解决这个问题。然而我是有些迟疑的,东西不是你说做我就要做的,我必须试一下...否则答应太快又搞不出来,岂不是很难受!不过既然发了这篇文章,就说明已经搞定了代码在themeConfig($form)函数里添加$db = Typecho_Db::get(); $sjdq=$db->fetchRow($db->select()->from ('table.options')->where ('name = ?', 'theme:Yodu')); $ysj = $sjdq['value']; if(isset($_POST['type'])) { if($_POST["type"]=="备份模板数据"){ if($db->fetchRow($db->select()->from ('table.options')->where ('name = ?', 'theme:Yodubf'))){ $update = $db->update('table.options')->rows(array('value'=>$ysj))->where('name = ?', 'theme:Yodubf'); $updateRows= $db->query($update); echo '<div class="tongzhi">备份已更新,请等待自动刷新!如果等不到请点击'; ?> <a href="<?php Helper::options()->adminUrl('options-theme.php'); ?>">这里</a></div> <script language="JavaScript">window.setTimeout("location=\'<?php Helper::options()->adminUrl('options-theme.php'); ?>\'", 2500);</script> <?php }else{ if($ysj){ $insert = $db->insert('table.options') ->rows(array('name' => 'theme:Yodubf','user' => '0','value' => $ysj)); $insertId = $db->query($insert); echo '<div class="tongzhi">备份完成,请等待自动刷新!如果等不到请点击'; ?> <a href="<?php Helper::options()->adminUrl('options-theme.php'); ?>">这里</a></div> <script language="JavaScript">window.setTimeout("location=\'<?php Helper::options()->adminUrl('options-theme.php'); ?>\'", 2500);</script> <?php } } } if($_POST["type"]=="还原模板数据"){ if($db->fetchRow($db->select()->from ('table.options')->where ('name = ?', 'theme:Yodubf'))){ $sjdub=$db->fetchRow($db->select()->from ('table.options')->where ('name = ?', 'theme:Yodubf')); $bsj = $sjdub['value']; $update = $db->update('table.options')->rows(array('value'=>$bsj))->where('name = ?', 'theme:Yodu'); $updateRows= $db->query($update); echo '<div class="tongzhi">检测到模板备份数据,恢复完成,请等待自动刷新!如果等不到请点击'; ?> <a href="<?php Helper::options()->adminUrl('options-theme.php'); ?>">这里</a></div> <script language="JavaScript">window.setTimeout("location=\'<?php Helper::options()->adminUrl('options-theme.php'); ?>\'", 2000);</script> <?php }else{ echo '<div class="tongzhi">没有模板备份数据,恢复不了哦!</div>'; } } if($_POST["type"]=="删除备份数据"){ if($db->fetchRow($db->select()->from ('table.options')->where ('name = ?', 'theme:Yodubf'))){ $delete = $db->delete('table.options')->where ('name = ?', 'theme:Yodubf'); $deletedRows = $db->query($delete); echo '<div class="tongzhi">删除成功,请等待自动刷新,如果等不到请点击'; ?> <a href="<?php Helper::options()->adminUrl('options-theme.php'); ?>">这里</a></div> <script language="JavaScript">window.setTimeout("location=\'<?php Helper::options()->adminUrl('options-theme.php'); ?>\'", 2500);</script> <?php }else{ echo '<div class="tongzhi">不用删了!备份不存在!!!</div>'; } } } echo '<form class="protected" action="?yodubf" method="post"> <input type="submit" name="type" class="btn btn-s" value="备份模板数据" />&nbsp;&nbsp;<input type="submit" name="type" class="btn btn-s" value="还原模板数据" />&nbsp;&nbsp;<input type="submit" name="type" class="btn btn-s" value="删除备份数据" /></form>';然后将里面出现的所有“yodu”改成你的模板目录的名字,如果拿不准就去数据库里看看模板的值名字。备份当用户点击备份时,先判断是否已经存在备份,如果不存在就插入一条新的数据,数据name为yodubf,value为模板原本的数据。此时就存在了一条备份数据。如果再次点击备份按钮会发生什么呢?会触发更新数据的语句,就是读取模板的设置数据,然后将备份的模板数据更新。还原当用户点击还原按钮时,会判断是否存在备份,如果不存在就发出提示说不存在数据无法恢复;如果存在,就会进行一个反向的更新操作,将备份的数据更新到模板默认设置数据。这个操作完成后会触发个小问题,比较影响体验的。就是在点击还原按钮时网页是先刷新后执行php还原语句的,也就是说还原完成后,你看到的模板设置页面数据并没有还原,但是实际数据库里面已经还原好了的,这一点很影响体验。于是乎,我鸡贼的弄了个js自动刷新语句,并发出提示文字,这样一下子就友好多了,注意文章中代码方面我并未给出css样式,所以美观度上需要自行优化。删除删除就简单了,判断是否存在备份,不存在就告诉用户不用删了,你压根就没有备份数据,如果有备份就执行删除语句,发出提示。一些没用的说明1,其实这东西应该可以写成懒人版的,模板名字什么的用php获取下,就不用我这样写死了,但是当时我处于试一试的心态写的,所以就能简单就简单了,现在又懒得弄了,要不是为了水文,这个我都懒得贴出来。2,别看文章中代码这么乱,条例就不清晰,其实我当时找了张纸写的逻辑然后才按照顺序一步一步的写的,也测试了很多回。3,最开始想写自动还原模板数据来着,就是检测到模板启用就自动还原曾经的备份数据,然而当时想不通如果去判断模板启用。4,当你想将本文章代码投入使用时,最好再测试博客进行测试,以免伤害你的数据库,同时建议测试时打开数据库管理页面,观看数据库对应表的...

阅读文章
typecho同一个页面下调用不同分类的文章但是却只显示一个分类文章

问题描述:同页面调用分类下文章,只显示一第一个分类下的文章在一个页面中,反复调用下面这段代码,获取不同mid分类下的文章,只显示第一次调用此方法获得的数据,其他分类也是显示第一次调用的数据。<?php $this->widget('Widget_Archive@index', 'pageSize=6&type=category', 'mid=1')->to($new); ?> <?php while ($new->next()): ?> <a href="<?php $new->permalink(); ?>"><?php $new->title(); ?></a> <?php endwhile; ?>一些啰嗦的话这个问题其实我也遇到过,因为typecho文档真的很有限,也没找到方法,后来自己瞎折腾就搞明白了。然后这个问题不止一个两个人问过我,而且还有些不认识我的人在论坛提问,所以不如水篇文章,网络分享,全世界可见,这次是互联网意义哈。解决方法代码中@index就是关键。调用不同分类,这个@部分不同就行了,比如你调用两个不同的分类文章,mid分别为1和2,那么代码就这样写分类一 <?php $this->widget('Widget_Archive@index', 'pageSize=6&type=category', 'mid=1')->to($new); ?> <?php while ($new->next()): ?> <a href="<?php $new->permalink(); ?>"><?php $new->title(); ?></a> <?php endwhile; ?> 分类二 <?php $this->widget('Widget_Archive@qqdie', 'pageSize=6&type=category', 'mid=2')->to($new); ?> <?php while ($new->next()): ?> <a href="<?php $new->permalink(); ?>"><?php $new->title(); ?></a> <?php endwhile; ?&g...

阅读文章
typecho弹出find Input author error怎么回事

大多数报错的起因就是因为,有些模板归档页面的评论功能被阉割掉了于是导致一些评论验证功能的插件爆这个错误。解决方法有以下两种1,编辑这些没有评论功能的页面,高级设置,关闭评论,然后发布文章。2,找到你的评论验证的插件,关闭该项过滤功能,比如commentfilter插件设置第一项屏蔽机器人评论,选择无动作,保存设置即可。3,直接将代码里的 echo" find input author error" 删除即可。使用notepad++或者sublime 可以全目录检索,直接搜到这串代码弹出这个还有一种不常见的原因,那就是模板评论地方书写有问题。这个解决方法就是弹模板作者小下体【或者同上文方...

阅读文章
typecho统计当前分类和子分类文章总数

typecho发布一篇文章,然后只勾选子分类,然后发布。父分类输出分类的文章数量,并没有包含这个新发布的文章。于是乎,垃圾博主啊用了点时间(一个半小时)写出了个函数,来解决这个问题function fenleinum($id){ $db = Typecho_Db::get(); $po=$db->select('table.metas.count')->from ('table.metas')->where ('parent = ?', $id)->orWhere('mid = ? ', $id); $pom = $db->fetchAll($po); $num = count($pom); $shu = 0; for ($x=0; $x<$num; $x++) { $shu=$pom[$x]['count']+$shu; } echo $shu; ...

阅读文章
模板层面向typecho编辑器页面插入代码

上篇文章《模板层面向 typecho 编辑器页面插入 css js》中讲到一种向编辑器中插入css的奇葩方式,某些时候那么写确实能解决不少问题。但是但是,文章编辑界面源代码中是加载了jquery的,但是在页面偏底部,而上篇文章中讲到的方法是插在jquery代码之上的,也就是说那种方法如果插入js代码的话,就不能依赖jquery,除非在引用一个。基于以上原因,我又想到中方式,插件的那种方式,经过试验我发现插件上的方法在模板中也完全可以。在模板functions.php中加入以下代码即可Typecho_Plugin::factory('admin/write-post.php')->bottom = array('myyodu', 'one'); Typecho_Plugin::factory('admin/write-page.php')->bottom = array('myyodu', 'one'); class myyodu { public static function one() { ?> 你的css代码或者js代码,js可以依赖jquery来书写 <?php } }例如实现上篇文章《模板层面向 typecho 编辑器页面插入 css js》的代码Typecho_Plugin::factory('admin/write-post.php')->bottom = array('myyodu', 'one'); Typecho_Plugin::factory('admin/write-page.php')->bottom = array('myyodu', 'one'); class myyodu { public static function one() { ?> style>.wmd-button-row {height:auto;}.copyright p:after {content: "YoDu魔法优化中";margin-left: 6px;font-size: 12px;}</style> <?php } }当然这样可玩性就高了,下图展示个我的一个小成果,模板层面向编辑器中加入字数统计功...

阅读文章
判断typecho的版本号

前台用<?php $this->options->Version(); ?>就能输出typecho的版本号,而他的格式是这样的1.1/17.11.151.1是他的版本,而后面的该版本的日期,所以说即使大家都是1.1,后面日期不一样的话你们的程序代码上就会有区别。那么假设你的模板不兼容某个版本的typecho时,为何不做个温馨提示呢?<?php $tver = substr($this->options->->Version , 0 , 3); if($tver>1.0){ echo '该模板可能不兼容大于1.0版本的typecho'; } ?>也可以根据后面的日期进行判断,总之判断完就能搞各种有趣的事情哈。if,if就是博主最擅长的事情哈!在functions.php用$this->options->Version可能没有用,没用的话试试下面这个,应该可以:Typecho_Widget::widget('Widget_Options')->Version或者$options = Helper::options(); $options->version();想到这个的原因也是想给自己模板做个判断智能提示下,实现后就...

阅读文章
让Typecho无限滚动加载的方法

据统计,据媒体报道,据各种经验之谈:用户不喜欢点击,更爱滚动!所以,之前挺流行的文章分页没有了,沿用了几百年的“下一页”也被无限load取代。瀑布流和Twitter更是推动了无限load的普及。这里介绍一个jQuery插件:Infinite AJAX Scroll,通过这个插件能快速实现无限滚动翻页。因为是jQuery所以到处都能用,这里我们以Typecho作为例子。第一步接入Infinite AJAX Scroll下载Infinite AJAX Scroll,放到对应主题的js文件夹中。因为是jQuery插件,我们还要先连上jQuery。打开header.php页面,插入如下代码:<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <script src="<?php $this->options->themeUrl('js/jquery-ias.js'); ?>"></script>第二步为Typecho加上分页如果看官网文档的话,它会告诉你分页代码用带有数字列表的<?php $this->pageNav(); ?>。如果你用的是这个代码,请务必换成<?php $this->pageLink('下一页','next'); ?>。步骤三调整代码把下面这段代码插在步骤一代码的后面即可,然后我们要做一些调整。<script type="text/javascript"> var ias = jQuery.ias({ container: '#posts', //大容器 item: '.post', //循环容器 pagination: '#pagination', //分页容器 next: '.next' //下一页的class }); ias.extension(new IASTriggerExtension({ text: '加载更多', //此选项为需要点击时的文字 offset: 2, //load多少页后显示加载更多按钮 })); ias.extension(new IASSpinnerExtension()); //加载时的图片 ias.extension(new IASNoneLeftExtension({text: "已经没有文章了"})); //到底后显示的文字 </script>我们需要把容器对应的id和class填上,item指的是循环列表的容器,就是列表中的文章最外层div的id或者classcontainer是整个大容器,就是包裹文章列表的div的id或者classpagination是分页所在的容器,就是包裹分页按钮的div的id或者classnext是下一页对应的class,就是分页按钮超链接的class如果没有id或者class,就自己加一个。为了更方便理解,我偷了个图,基本就是这样的步骤四重载函数因为文章可能含有缩略图,而缩略图可能会用到惰性加载的js,所以ajax加载文章后,缩略图可能加载异常,这时我们需要在步骤三的代码最后加上一条 ias.on('rendered', function(items) { //你的重载函数 })常见的惰性加载需要重载的函数Lazyload $("img.lazy").lazyload({effect:"fadeIn"}); //这里是你调用Lazyload的代码blazy;(function() { // Initialize var bLazy = new Blazy(); })(); 参考:http://www.19871222.com/infinite-ajax-scroll.htmlhttps://salongweb.com/infinite-ajax-scroll.ht...

阅读文章
Typecho不修改源码不关闭反垃圾保护兼容pjax

众所周知,typecho使用pjax模板会导致评论失败。解决方法一是修改程序源码来兼容,二是在评论设置处关闭反垃圾保护来避免冲突。方法一呢,明显不适合小白用户,而且程序更新后可能会出现问题,或者需要再次修改。方法二,虽然简单直接了很多,但是呢作为一个写主题的博主,总会被小白重复问道为啥评论不好使【明明主题安装说明里都强调说明了要关闭反垃圾保护,可是小白就是看不到!所以对于主题作者来说这样也不太好】其实认真观察yodu模板更新记录的人应该发现了,自从yodu3.3.0版开始,我就不在提示关闭反垃圾保护来兼容模板了,因为想到了个有趣的方法在functions.php添加function themeInit($archive) { Helper::options()->commentsAntiSpam = false; }这个方法其实与方法二一样,就是关闭反垃圾保护,只不过不用手动去关闭而是模板强制关闭反垃圾保护,并且评论设置处看起来还是开启反垃圾保护的样子,忽悠小白妥妥的!其实themeInit还有些厉害的地方,例如:function themeInit($archive) { Helper::options()->commentsMaxNestingLevels = 999;//评论回复楼侧最高999层.这个正常设置最高只有7层 Helper::options()->commentsAntiSpam = false;//评论关闭反垃圾保护 if ($archive->is('author')) { $archive->parameter->pageSize = 50; // 作者页面每50篇文章分页一次 } if ($archive->is('category','av')) { $archive->parameter->pageSize = 9; // 分类缩略名为av的分类列表每9篇文章分页一次 } $archive->content = a_class_replace($archive->content);//文章内容,让a_class_replace函数处理 } function a_class_replace($content) { $content = preg_replace('#<a(.*?) href="([^"]*/)?(([^"/]*)\.[^"]*)"(.*?)>#', '<a$1 href="$2$3"$5 target="_blank">', $content);//给文章每个超链接点击后新窗口打开,原理就是用正则替换文章内容...

阅读文章
利用个小bug判断typecho当前分类页面是父级还是子级

bug描述分类A是一个父级分类,a1,a2,a3都是A的子分类,并且发布的文章只勾选子级分类。那么在分类A文章列表页面下,使用<?php echo $this->category; ?>输出分类缩略名,它不会输出A的缩略名,反而会输出a1,a2,a3的某个分类的缩略名。然后joyqi告诉我用<?php echo $this->getArchiveSlug(); ?>输出缩略名来解决这个问题。分类页面子父级分类判断然后我突发奇想,可以用这个判断分类页面是父级还是子级呀?我太天才了!!!<?php $a = $this->category; $b = $this->getArchiveSlug(); if($a==$b){echo '子级分类页面';}else{echo '父级分类页面';} ?>用途可以用来分别设置子分类父级分类的样式,还可以根据分类级别不同输出不同内容,总之用起来很爽就对了!...

阅读文章
typecho修改后台地址

官方的文档Typecho 安装好后,默认的后台路径是domain.com/admin/,为了提高安全性,我们允许以domain.com/xxxx/的方式访问,其中xxxx是你自定义的路径。自定义的方法如下:你只需要在系统根目录下的config.inc.php文件里,找到/* 后台路径(相对路径) /define('__TYPECHO_ADMIN_DIR__', '/admin/');把这个/admin/路径改成自己想要的路径就行了实际操作将admin文件夹改名,改成你需要的例如abc,然后找到config.inc.php文件,将里面的admin改成abc即可,以后要登录后台访问domain.com/abc/即可。做个假的后台地址做完上面的操作后,在网站根目录重新建立个admin文件夹,然后里面放个index.html,然后index.html你想写成什么样就写成什么样,甚至可以写个假的登录框,然后随便个密码就登录成功,然后在显示个嘲讽的页面23...

阅读文章
Typecho 自定义分页样式

typecho的这种分页样式设置最初我也是很迷茫的,所以我做的大部分模板都是只是用上一页和下一页,然而昨天翻出来看一下发现其实挺简单的,以前自己没有理解好。其实这类文章在吕滔博客《Typecho 自定义分页样式》有讲,但是没有很细致的说明,甚至给出的代码本身有些小错误,所以我在这里细说下这个。先上代码,然后对应的分析下代码如下:<?php $this->pageNav('«', '»', 1, '...', array('wrapTag' => 'ol', 'wrapClass' => 'page-navigator', 'itemTag' => 'li', 'textTag' => 'span', 'currentClass' => 'current', 'prevClass' => 'prev', 'nextClass' => 'next',)); ?>对应出来的的html如下:<ol class="page-navigator"><li class="current"><a href="http://dmgogogo.com/page/1/">1</a></li><li><a href="http://dmgogogo.com/page/2/">2</a></li><li><span>...</span></li><li><a href="http://dmgogogo.com/page/5/">5</a></li><li class="next"><a href="http://dmgogogo.com/page/2/">»</a></li></ol>由此可知1,«和»分别对应的是上一页按钮和下一页按钮,2,数字1是分割范围(分几页),是当前页码附近可现实的页码数量,举个例子,当前页码为1,一共页码为5,那么上述代码输出的效果就是1,2,...5,如果当前页码为2呢,效果就是1,2,3,...5。3,...是分割字符,就是2中提到的那个省略页码的东西4,wrapTag外层包裹标签名,默认ol,wrapClass外层包裹类名,itemTag内层标签名, 默认li,textTag直接输出文字的标签名,currentClass当前聚焦类名,prevClass上一页类名,nextClass下一页类名。5,itemClass可以给其他页码的标签带上class那么实战下目标就是下图,这个是我截取的wp某博客模板的样子html代码如下:<div class="page"> <a href="#">|<</a><span class="current">1</span><a href="h#">2</a><a href="#">3</a><a href="#">4</a><a href="#">5</a><a href="#">6</a><a href="#">7</a><a href="#">8</a><a href="#">9</a><a href="#">10</a><a href="#">>|</a> </div>那么typecho怎么做呢,首先分析到上面的代码外围包裹标签为div类名为page,然后又看到他的上一页下一页的符号分别为|<和>|,页码间隔大概是10,内层标签没有,当前页码类为current。那么整理后代码大概是这样子的 <?php $this->pageNav('|<', '>|',10,'',array('wrapTag' => 'div', 'wrapClass' => 'page','itemTag' => '','currentClass' => 'current',)); ?>最后将对应的css偷下来就行了,等等!我为什么要说偷Q...

阅读文章
typecho常用的functions

文章阅读次数见《typecho 非插件实现文章阅读次数统计 (cookie 版)》随机文章function theme_random_posts(){ $defaults = array( 'number' => 6, 'before' => '<ul class="archive-posts">', 'after' => '</ul>', 'xformat' => '<li class="archive-post"> <a class="archive-post-title" href="{permalink}">{title}</a> </li>' ); $db = Typecho_Db::get(); $sql = $db->select()->from('table.contents') ->where('status = ?','publish') ->where('type = ?', 'post') ->limit($defaults['number']) ->order('RAND()'); $result = $db->fetchAll($sql); echo $defaults['before']; foreach($result as $val){ $val = Typecho_Widget::widget('Widget_Abstract_Contents')->filter($val); echo str_replace(array('{permalink}', '{title}'),array($val['permalink'], $val['title']), $defaults['xformat']); } echo $defaults['after']; }前台调用<?php theme_random_posts();?>页面加载耗时代码function timer_start() { global $timestart; $mtime = explode( ' ', microtime() ); $timestart = $mtime[1] + $mtime[0]; return true; } timer_start(); function timer_stop( $display = 0, $precision = 3 ) { global $timestart, $timeend; $mtime = explode( ' ', microtime() ); $timeend = $mtime[1] + $mtime[0]; $timetotal = $timeend - $timestart; $r = number_format( $timetotal, $precision ); if ( $display ) echo $r; return $r; }然后,模板部分加入<?php timer_stop() ?>【转自http://t.160.me/65.html】文章缩略图获取见《typecho 缩略图加入根据标签缩略名输出缩略图》文章中文字数统计function art_count ($cid){ $db=Typecho_Db::get (); $rs=$db->fetchRow ($db->select ('table.contents.text')->from ('table.contents')->where ('table.contents.cid=?',$cid)->order ('table.contents.cid',Typecho_Db::SORT_ASC)->limit (1)); $text = preg_replace("/[^\x{4e00}-\x{9fa5}]/u", "", $rs['text']); echo mb_strlen($text,'UTF-8'); }模板引用<?php echo art_count($this->cid); ?>【转自https://i.chainwon.com/131.html】替换文章内容function themeInit($archive) { if ($archive->is('single')) { $archive->content = image_class_replace($archive->content); } } function image_class_replace($content) { $content = preg_replace('#<a(.*?) href="([^"]*/)?(([^"/]*)\.[^"]*)"(.*?)>#', '<a$1 href="$2$3"$5 target="_blank">', $content); return $content; }上述代码就是将不含blank属性的超链接,替换为加上了blank属性的超链接自定义上下篇链接/** * 显示下一篇 * * @access public * @param string $default 如果没有下一篇,显示的默认文字 * @return void */ function theNext($widget, $default = NULL) { $db = Typecho_Db::get(); $sql = $db->select()->from('table.contents') ->where('table.contents.created > ?', $widget->created) ->where('table.contents.status = ?', 'publish') ->where('table.contents.type = ?', $widget->type) ->where('table.contents.password IS NULL') ->order('table.contents.created', Typecho_Db::SORT_ASC) ->limit(1); $content = $db->fetchRow($sql); if ($content) { $content = $widget->filter($content); $link = '<a href="' . $content['permalink'] . '" title="' . $content['title'] . '">下一篇</a>'; echo $link; } else { echo $default; } } /** * 显示上一篇 * * @access public * @param string $default 如果没有下一篇,显示的默认文字 * @return void */ function thePrev($widget, $default = NULL) { $db = Typecho_Db::get(); $sql = $db->select()->from('table.contents') ->where('table.contents.created < ?', $widget->created) ->where('table.contents.status = ?', 'publish') ->where('table.contents.type = ?', $widget->type) ->where('table.contents.password IS NULL') ->order('table.contents.created', Typecho_Db::SORT_DESC) ->limit(1); $content = $db->fetchRow($sql); if ($content) { $content = $widget->filter($content); $link = '<a href="' . $content['permalink'] . '" title="' . $content['title'] . '">上一篇</a>'; echo $link; } else { echo $default; } }调用代码<?php thePrev($this); ?>和<?php theNext($this); ?>【转自http://t.160.me/33.html】自定义分类、标签、搜索、首页等文章分页数量function themeInit($archive) { if ($archive->is('index')) { $archive->parameter->pageSize = 10; // 自定义条数 } } 上边的是自定义首页文章数量,下面的是自定义default文章数量 function themeInit($archive) { if ($archive->is('category', 'default')) { $archive->parameter->pageSize = 10; // 自定义条数 } }【转自https://app.typecho.me/coder/10.html】判断最新帖子显示图标例如24小时内发布的贴,需要一个标志来完成。这里是用判断输入特殊字符,再用CSS判断完成的。/** * 判断时间区间 * * 使用方法 if(timeZone($this->date->timeStamp)) echo 'ok'; */ function timeZone($from){ $now = new Typecho_Date(Typecho_Date::gmtTime()); return $now->timeStamp - $from < 24*60*60 ? true : false; }在index.php中调用<?php if(timeZone($this->date->timeStamp)) echo ' new'; ?>【转自http://t.160.me/44.html】N天内评论最多的文章代码作用是N天内评论最多的N篇文章,这个N你可以自己定义,自己修改。<?php function rmcp($days = 30,$num = 5){ $defaults = array( 'before' => '<ul>', 'after' => '</ul>', 'xformat' => '<li><a href="{permalink}">{title}</a><span class="subcolor">已有评论数:{commentsNum}</span></li>' ); $time = time() - (24 * 60 * 60 * $days); $db = Typecho_Db::get(); $sql = $db->select()->from('table.contents') ->where('created >= ?', $time) ->where('type = ?', 'post') ->limit($num) ->order('commentsNum',Typecho_Db::SORT_DESC); $result = $db->fetchAll($sql); echo $defaults['before']; foreach($result as $val){ $val = Typecho_Widget::widget('Widget_Abstract_Contents')->filter($val); echo str_replace(array('{permalink}', '{title}', '{commentsNum}'), array($val['permalink'], $val['title'], $val['commentsNum']), $defaults['xformat']); } echo $defaults['after']; } ?>模板调用<?php rmcp(60,5);?>这个调用的意思是2个月内评论最多的前5篇文章。【转自http://t.160.me/85.html】百度是否收录//判断内容页是否百度收录 function baidu_record() { $url='http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; if(checkBaidu($url)==1) {echo "百度已收录"; } else {echo "<a style=\"color:red;\" rel=\"external nofollow\" title=\"点击提交收录!\" target=\"_blank\" href=\"http://zhanzhang.baidu.com/sitesubmit/index?sitename=$url\">百度未收录</a>";} } function checkBaidu($url) { $url = 'http://www.baidu.com/s?wd=' . urlencode($url); $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $rs = curl_exec($curl); curl_close($curl); if (!strpos($rs, '没有找到')) { //没有找到说明已被百度收录 return 1; } else { return -1; } }模板调用代码<?php echo baidu_record() ?>【转自http://www.ihewro.xyz/archives/322/】在文章编辑页面设置个自定义字段function themeFields($layout) { $thumb = new Typecho_Widget_Helper_Form_Element_Text('thumb', NULL, NULL, _t('自定义缩略图'), _t('输入缩略图地址(仅文章有效)')); $layout->addItem($thumb); }...

阅读文章