目录
- 为typecho主题handsome添加文章分页功能
- typecho主题handsome自定义头图
- typecho主题handsome更换markdown解析器为 parsedown
- 基于PostgreSQL安装typecho(主题handsome)
- 为handsome主题部署PostgreSQL全文检索
为typecho主题handsome添加文章分页功能
功能思路来源于插件 SplitArchivePage,只需要修改handsome主题的文件 typecho/usr/themes/handsome/libs/Content.php
。
1. 在文件最后一个 }
前添加如下两个私有函数
请注意:添加代码前删掉如下代码中第4行 splitword变量的值中 page
前后的两个空格。
// 文章分页
private static function parse($text)
{
$pagebar = '';
$content = $text;
$splitword = '= page =';
if( strpos( $text , $splitword) !== false){
$contents = explode($splitword , $text );
$page = isset($_GET['page'])?intval($_GET['page']):1;
$content = $contents[$page-1];
$request = Typecho_Request::getInstance();
$_GET['page'] = '{page}';
$pagebar = self::setPageBar(count($contents),$page,$request->getPathinfo()."?". http_build_query($_GET));
}
$text = $content.$pagebar;
return $text;
}
private static function setPageBar($pageTotals,$page,$pageTemplate)
{
$isRewrite = Typecho_Widget::widget('Widget_Options')->rewrite;
$siteUrl = Typecho_Widget::widget('Widget_Options')->siteUrl;
$pageTemplate = ($isRewrite ? rtrim($siteUrl, '/') : $siteUrl."index.php") . $pageTemplate;
$splitPage = 3;
$pageHolder = array('{page}', '%7Bpage%7D');
if ($pageTotals < 1) { return; }
$pageBar .= '<nav class="text-center m-t-lg m-b-lg" role="navigation"><ol class="page-navigator">';
if ($page > 1) {
$pageBar .= '<li class="prev"><a href="' . str_replace($pageHolder, $page - 1, $pageTemplate) . '">' . '<i class="fontello fontello-chevron-left"></i></a></li>';
}
for ($i = 1; $i <= $pageTotals; $i ++) {
if($page==$i){
$pageBar .= '<li class="current"><a href="' . str_replace($pageHolder, $i, $pageTemplate) . '" ' . ($i != $page ? '' : '') . '>' . $i . '</a></li>';
}else
{if((($i==$page-3) and ($i!=1)) or (($i==$page+3) and ($i!=$pageTotals)))
{
$pageBar .= '<li><span>...</span></li>';
}else{
if((($i<$page-3) and ($i!=1)) or (($i>$page+3) and ($i!=$pageTotals)))
{}else{
$pageBar .= '<li><a href="' . str_replace($pageHolder, $i, $pageTemplate) . '" ' . ($i != $page ? '' : '') . '>' . $i . '</a></li>';
}
}
}
}
if ($page < $pageTotals) {
$pageBar .= '<li class="next"><a href="' . str_replace($pageHolder, $page + 1, $pageTemplate) . '">' . '<i class="fontello fontello-chevron-right"></i></a></li>';
}
$pageBar .='</ol></nav><style>.page-navigator>li>a, .page-navigator>li>span{ background: #EFEFEF; line-height: 1.42857143; padding: 6px 12px; border-bottom-style:none !important; }</style>';
return $pageBar;
}
2.修改 parseContentPublic函数的定义,添加一个参数 $need2pagination
并提供默认值
在文件 Content.php
的1987行前后。
默认值为 False
, 所以默认情况下不会调用内容分页函数避免对handsome原来的处理逻辑造成影响,比如评论者在评论内容中插入分页符就比较尴尬了 😈
public static function parseContentPublic($content,$need2pagination=False)
3. 在 parseContentPublic 函数中调用分页函数
在文件 Content.php
的2092行前后,语句 return $content;
之前增加一行代码,代码片段:
//文章分页
if ($need2pagination==True) { $content=self::parse($content);}
return $content;
}
4. 对于需要分页的内容,修改调用parseContentPublic 函数,添加 need2pagination参数
比如需要提供 post 的分页功能,仅需要修改如下函数 public static function postContent($obj, $status, $way = "origin")
, 搜索文本 该部分仅登录用户可见
, 在文件 Content.php
的2092行前后:
// $content = Content::parseContentPublic($content); //该行修改前语句,下一行为修改后语句
$content = Content::parseContentPublic($content,True);
}
return trim($content);
}
目前只测过对 post 分页,page等其他内容的分页没测试过。其他内容如果需要分页,同样可以用 ($content,True)
参数试试,风险自担。
5.在文章中分页
文章需要分页处添加行 = page =
(注意去掉page前后的空格)。 当然也可以修改代码换成其他标识。
效果:
👾 这里是第1页内容 👾
=page=
👾 这里是第2页内容 👾
=page=
👾 这里是第3页内容 👾
=page=
👾 这里是第4页内容 👾
=page=
👾 这里是第5页内容 👾
=page=
👾 这里是第6页内容 👾
=page=
👾 这里是第7页内容 👾
=page=
👾 这里是第8页内容 👾
=page=
👾 这里是第9页内容 👾
=page=
👾 这里是第10页内容 👾
=page=
👾 这里是第11页内容 👾
=page=
👾 这里是第12页内容 👾
左侧导航栏支持的其中2种图标和使用方式
使用:
{"name":"音乐","class":"glyphicon glyphicon-music","link":"xxx.com"},
使用:
{"name":"音乐2","feather":"music","link":"xxx.com"},
网站美化
参考 handsome主题美化记录 - 持续更新 修改的内容
以下代码的后台使用的是 PostgreSQL数据库
1. 访客总数统计
vi /typecho/usr/themes/handsome/functions.php
,文件中添加以下统计代码.
//总访问量
function theAllViews()
{
$db = Typecho_Db::get();
$row = $db->fetchAll('SELECT SUM(VIEWS) FROM typecho_contents');
$result= implode(",",$row[0]);
echo $result;
}
在 vi /typecho/usr/themes/handsome/component/sidebar.php
文件中插入以下调用代码, 在博客信息,评论数目的下方.
<li class="list-group-item"> <i class="glyphicon glyphicon-user text-muted"></i> <span class="badge
pull-right"><?php theAllViews();?></span><?php _me("访客总数") ?></li>
如果使用了静态缓存,显示的不是实时数据,可以考虑不用该功能。
2.去除顶部博客名称
vi /typecho/usr/themes/handsome/index.php
文件,位于公告位置下方,删除以下代码:
<h1 class="m-n font-thin h3 text-black l-h"><?php $this->options->title(); ?></h1>
3.网站加载耗时
vi /typecho/usr/themes/handsome/functions.php
,文件中添加以下统计代码
//加载耗时
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 = number_format( $timeend - $timestart, $precision );
$r = $timetotal < 1 ? $timetotal * 1000 . " ms" : $timetotal . " s";
if ( $display ) {
echo $r;
}
return $r;
}
在 vi /typecho/usr/themes/handsome/component/sidebar.php
文件中插入以下调用代码,在最后活动的后方:
<li class="list-group-item"> <i class="glyphicon glyphicon-time text-muted"></i> <span class="badge
pull-right"><?php echo timer_stop();?></span><?php _me("加载耗时") ?></li>
如果使用了静态缓存,显示的不是实时数据,可以考虑不用该功能。
4.彩色标签云
参考Typecho handsome主题自定义修改标签云颜色
更改方法:打开后台-更改外观-设置外观-开发者设置-选择好需要的颜色粘贴至自定义JavaScript即可.颜色也可以自行设定,修改代码中的十六进制颜色值即可
<!--纯黑标签云-->
let tags = document.querySelectorAll("#tag_cloud-2 a");
let colorArr = ["#000000", "#000000", "#000000", "#000000", "#000000", "#000000"];
tags.forEach(tag => {
tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)];
tag.style.backgroundColor = tagsColor;
});
<!--银白标签云-->
let tags = document.querySelectorAll("#tag_cloud-2 a");
let colorArr = ["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0"];
tags.forEach(tag => {
tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)];
tag.style.backgroundColor = tagsColor;
});
<!--淡蓝标签云-->
let tags = document.querySelectorAll("#tag_cloud-2 a");
let colorArr = ["#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6", "#ADD8E6"];
tags.forEach(tag => {
tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)];
tag.style.backgroundColor = tagsColor;
});
<!--彩色标签云-->
let tags = document.querySelectorAll("#tag_cloud-2 a");
let colorArr = ["#428BCA", "#AEDCAE", "#ECA9A7", "#DA99FF", "#FFB380", "#D9B999"];
tags.forEach(tag => {
tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)];
tag.style.backgroundColor = tagsColor;
});
<!--天蓝标签云-->
let tags = document.querySelectorAll("#tag_cloud-2 a");
let colorArr = ["#00BFFF", "#00BFFF", "#00BFFF", "#00BFFF", "#00BFFF", "#00BFFF"];
tags.forEach(tag => {
tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)];
tag.style.backgroundColor = tagsColor;
});
说明:如果主题中启用了pjax,还需要将上面代码添加到pjax-pjax回调函数中
5.页面尽量占满屏幕
- 方案一: handsome主题后台添加如下类似的自定义css,推荐该方案
- 方案二: 修改
handsome/assets/css/handsome.min.css
@media (min-width:1200px) {
.app.container {
width: 1170px
}
.app.container .app-aside,
.app.container .app-header {
/* max-width: 1170px */
max-width: 1920px
}
.app.container .app-footer-fixed {
/* max-width: 970px */
max-width: 1920px
}
.app.container.app-aside-folded .app-footer-fixed {
/* max-width: 1110px */
max-width: 1920px
}
.app.container.app-aside-dock .app-footer-fixed {
/* max-width: 1170px */
max-width: 1920px
}
}
@media (min-width:1800px) {
.app.container {
/* width: 1300px */
width: 1800px
}
.app.container .app-aside,
.app.container .app-header {
/* max-width: 1300px */
max-width: 3840px
}
.index-image,
.item-thumb {
min-height: 280px
}
}
6.不显示隐藏内容提示
对于用 [login] [/login]
隐藏的内容,不显示 该部分仅登录用户可见
编辑: /typecho/usr/themes/handsome/libs/Content.php
//仅登录用户可查看的内容
if (strpos($content, '[login') !== false) {//提高效率,避免每篇文章都要解析
$pattern = self::get_shortcode_regex(array('login'));
$isLogin = $status;
$content = Utils::handle_preg_replace_callback("/$pattern/", function ($matches) use ($isLogin) {
// 不解析类似 [[player]] 双重括号的代码
if ($matches[1] == '[' && $matches[6] == ']') {
return substr($matches[0], 1, -1);
}
if ($isLogin) {
return '<p>' . $matches[5] . '</p>';
// return '<div class="hideContent">' . $matches[5] . '</div>';
} //else {
// return '<div class="hideContent">' . _mt("该部分仅登录用户可见") . '</div>';
//}
}, $content);
}
7.删除 闲言碎语
vi /typecho/usr/themes/handsome/component/headnav.php
,删除 <?php echo $headerItemsOutput; ?>
下的如下语句:
<?php echo $headerItemsOutput; ?>
<?php if (!$hideTalkItem): ?>
<!--闲言碎语-->
......
<!--/闲言碎语-->
<?php endif; ?>
搜索不到结果,一直提示「无相关搜索结果」?
需要在Handsome插件里面构建一下搜索的索引.
构建索引时提示 Handsome插件下的cache文件夹没有写入权限,查看Handsome下是否有cache文件夹,并给777权限
chmod -R 777 /typecho/usr/plugins/Handsome/cache
百度自动推动代码
参考 百度帮助文档, 在handsome主题设置的 自定义 JavaScript
添加如下代码:
<script>
(function(){
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https'){
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
}
else{
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
</script>
统计代码
不要按百度,google或handsome主题的提示将js代码放head部分, 要放在 </body>
标签前,否则严重拖累页面加载速度。
1.百度统计代码
在handsome主题设置的 自定义 JavaScript
输入统计代码.
从 百度统计 获取代码, 安装位置不要参考百度说明。
2.google统计代码:类似,放在页面尾部.
备案信息
在主题设置的 博客底部左侧信息
插入类似如下的代码(不含 div):
<a target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=51019002002885" style="display:inline-block;text-decoration:none;"><img src="/attachment/2010/beian_ghs.png" style="float:left;"/>川公网安备 51019002002885号</a> <a href="http://www.beian.miit.gov.cn" rel="external nofollow" target="_blank">蜀ICP备20011690号</a>
删除版权信息: Powered by Typecho | Theme by handsome
vi /typecho/usr/themes/handsome/component/footer.php
<span class="pull-right hidden-xs text-ellipsis">
<?php $this->options->BottomInfo(); ?>
<!--
Powered by <a target="_blank" href="http://www.typecho.org">Typecho</a> | Theme by <a target="_blank"
href="https://www.ihewro.com/archives/489/">handsome</a>
-->
</span>
其他
- pjax回调函数不要加标签
<script>
,</script>
typecho主题handsome自定义头图
仅需要修改文件 usr/themes/handsome/libs/Content.php
1.修改函数 whenSwitchHeaderImgSrc
查找函数: 搜索文字 public static function whenSwitchHeaderImgSrc(
.
修改变量 $random
的生成方式,代码片段:
public static function whenSwitchHeaderImgSrc($widget, $index = 0, $howToThumb, $attach, $content, $thumbField)
{
// $randomNum = unserialize(INDEX_IMAGE_ARRAY);
// 随机缩略图路径
// $random = STATIC_PATH . 'img/sj/' . @$randomNum[$index] . '.jpg';//如果有文章置顶,这里可能会导致index not undefined
$num = mt_rand( 1, 23); // 23 为自定义的图片数量
switch( $num )
{
case 1: $image_file = "https://xxx.jpg";break; //自定义的图片地址
case 2: $image_file = "https://yyy.jpg";break;
......
case 23: $image_file = "https://zzz.jpg";break;
};
$random = $image_file;
2.修改函数 echoPostList
首页等位置的图片存在重复的情况,继续修改函数 exportHeaderImg,查找文字 public static function echoPostList(
.
在 while ($obj->next()) {
语句前定义可用图片列表, 可以和 函数 whenSwitchHeaderImgSrc 的图片列表完全不同,代码片段
// 可用头图列表
$imgSrcUnused = array("https://aaaa.jpg",
"bbb",
......
"https://ccc.jpg");
while ($obj->next()) {
替换语句 $parameterArray['imgSrc'] = Content::returnHeaderImgSrc($obj, "index", $index);
, 代码片段:
// $parameterArray['imgSrc'] = Content::returnHeaderImgSrc($obj, "index", $index);
$imgSrcID = array_rand($imgSrcUnused);
$parameterArray['imgSrc'] = $imgSrcUnused[$imgSrcID];
unset($imgSrcUnused[$imgSrcID]);
$parameterArray['linkUrl'] = $obj->permalink;
3.其他:图床的选择
typecho主题handsome更换markdown解析器为 parsedown
typecho的默认解析器有很多基本的功能支持的并不好,比如不支持行内html标签,必须用 !!!
标识.
方式一:直接使用插件,推荐
插件: https://github.com/mrgeneralgoo/typecho-markdown
方式二:使用erusev 提供的parsedown, 没有方式一魔改后支持的功能多
1.从 这里 下载 Parsedown.php
; 从 这里 下载ParsedownExtra.php
2.将 Parsedown.php
和 ParsedownExtra.php
两个文件放到 functions.php
同一目录下
3.修改主题的 functions.php
, 添加 class theme_plugin
:
/*表单组件*/
require("libs/admin/FormElements.php");
require('libs/admin/Checkbox.php');
require('libs/admin/Text.php');
require('libs/admin/Radio.php');
require('libs/admin/Select.php');
require('libs/admin/Textarea.php');
// 如下替换md解析器
class theme_plugin{
public static function markdown($text){
require_once 'Parsedown.php';
require_once 'ParsedownExtra.php';
return Parsedown::instance()->setBreaksEnabled(true)->text($text);
}
}
4.修改 functions_mine.php
,增加如下两行:
function themeInit($archive)
{
// 如下两行替换md解析器
Typecho_Plugin::factory('Widget_Abstract_Contents')->markdown = ['theme_plugin', 'markdown'];
Typecho_Plugin::factory('Widget_Abstract_Comments')->markdown = ['theme_plugin', 'markdown'];
基于PostgreSQL安装typecho(主题handsome)
ubuntu20.04 下安装 postgresql13
参考: https://www.postgresql.org/download/linux/ubuntu/
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
# Import the repository signing key:
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
# Update the package lists:
sudo apt-get update
# Install the latest version of PostgreSQL.
# If you want a specific version, use 'postgresql-12' or similar instead of 'postgresql':
sudo apt-get -y install postgresql-13 php-pgsql
安装完成后,默认会:
(1)创建名为"postgres"的Linux用户
(2)创建名为"postgres"、不带密码的默认数据库账号作为数据库管理员
(3)创建名为"postgres"的表
修改数据库密码:
Step1: 切换用户为postgres
sudo su postgres
Step2: 用postgres连接postgreSQL
psql -U postgres
Step3: 修改postgres密码
alter user postgres with password 'new password';
现在postgres用户就拥有了新密码
windows10下安装 postgresql13
安装版下载: https://www.enterprisedb.com/downloads/postgres-postgresql-downloads
安装目录: C:\PostgreSQL\13 ,数据目录: C:\PostgreSQL\13\data ,安装信息:
Installation Directory: C:\PostgreSQL\13
Server Installation Directory: C:\PostgreSQL\13
Data Directory: C:\PostgreSQL\13\data
Database Port: 5432
Database Superuser: postgres
Operating System Account: NT AUTHORITY\NetworkService
Database Service: postgresql-x64-13
Command Line Tools Installation Directory: C:\PostgreSQL\13
pgAdmin4 Installation Directory: C:\PostgreSQL\13\pgAdmin 4
Stack Builder Installation Directory: C:\PostgreSQL\13
安装 php,nginx
参考 https://yinhe.co/archives/20201022_typecho.html 安装
安装typecho
一定要从github下载master分支代码安装, typecho的稳定版本对postgresql支持有问题,很多bug是在master版本中修复的。
handsome主题500错误
错误提示:Database Query Error
主题官方文档帮助信息:
安装完主题后,页面中间部分空白/安装后首页报错,500错误
依次检查主题文件夹名称是否为handsome,插件文件夹名称为Handsome(首字母大写)
是否已经安装并且启用主题必要的插件Handsome
php版本5.5+,必须安装mbstring和openssl扩展,否则无法使用(正确检查方式是在你的服务器新建一个test.php,然后复制粘贴该代码<?php phpinfo(); ?>,最后在浏览器访问该文件可以查看服务器的php信息)
主题目录下面的lisence文件给777权限,包括递归子文件夹和子文件,也可以尝试给644或者755权限。因为有的服务器上传文件之后,默认给的权限php都没办法执行的
最后确保自己上传的文件没有缺失,比如handsome文件夹下有fucntions.php,Handsome文件夹下有Plugin.php 则是最起码的
初次使用主题必须保证博客有一篇文章,如果一篇文章都没有的话会导致向数据库中添加view(浏览次数字段)失败
PostgreSql数据库安装links插件时,需要手动新建typechos_links表。(会在下一个版本修复)
解决方式:
1.供创建和修改表参考的: sqlite数据库中contents,links 表创建信息
CREATE TABLE typecho_contents ( "cid" INTEGER NOT NULL PRIMARY KEY,
"title" varchar(200) default NULL ,
"slug" varchar(200) default NULL ,
"created" int(10) default '0' ,
"modified" int(10) default '0' ,
"text" text ,
"order" int(10) default '0' ,
"authorId" int(10) default '0' ,
"template" varchar(32) default NULL ,
"type" varchar(16) default 'post' ,
"status" varchar(16) default 'publish' ,
"password" varchar(32) default NULL ,
"commentsNum" int(10) default '0' ,
"allowComment" char(1) default '0' ,
"allowPing" char(1) default '0' ,
"allowFeed" char(1) default '0' ,
"parent" int(10) default '0' , `views` INT(10) DEFAULT 0, `agree` INT(10) NOT NULL DEFAULT 0)
CREATE TABLE `typecho_links` (
`lid` INTEGER NOT NULL PRIMARY KEY,
`name` varchar(200) default NULL,
`url` varchar(200) default NULL,
`sort` varchar(200) default NULL,
`image` varchar(200) default NULL,
`description` varchar(200) default NULL,
`user` varchar(200) default NULL,
`order` int(10) default '0'
)
2.供创建和修改表参考的:PostgreSQL中contents表创建信息
-- Table: public.typecho_contents
-- DROP TABLE public.typecho_contents;
CREATE TABLE public.typecho_contents
(
cid integer NOT NULL DEFAULT nextval('typecho_contents_seq'::regclass),
title character varying(150) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
slug character varying(150) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
created integer DEFAULT 0,
modified integer DEFAULT 0,
text text COLLATE pg_catalog."default",
"order" integer DEFAULT 0,
"authorId" integer DEFAULT 0,
template character varying(32) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
type character varying(16) COLLATE pg_catalog."default" DEFAULT 'post'::character varying,
status character varying(16) COLLATE pg_catalog."default" DEFAULT 'publish'::character varying,
password character varying(32) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
"commentsNum" integer DEFAULT 0,
"allowComment" character(1) COLLATE pg_catalog."default" DEFAULT '0'::bpchar,
"allowPing" character(1) COLLATE pg_catalog."default" DEFAULT '0'::bpchar,
"allowFeed" character(1) COLLATE pg_catalog."default" DEFAULT '0'::bpchar,
parent integer DEFAULT 0,
CONSTRAINT typecho_contents_pkey PRIMARY KEY (cid),
CONSTRAINT typecho_contents_slug_key UNIQUE (slug)
)
TABLESPACE pg_default;
ALTER TABLE public.typecho_contents
OWNER to postgres;
-- Index: typecho_contents_created
-- DROP INDEX public.typecho_contents_created;
CREATE INDEX typecho_contents_created
ON public.typecho_contents USING btree
(created ASC NULLS LAST)
TABLESPACE pg_default;
3.修改pg中的contents表
ALTER TABLE typecho_contents ADD COLUMN IF NOT EXISTS views integer DEFAULT 0;
4.创建表 typecho_links
CREATE SEQUENCE typecho_links_seq
INCREMENT 1
START 1
MINVALUE 1
MAXVALUE 9223372036854775807
CACHE 1;
CREATE TABLE typecho_links (
lid integer NOT NULL DEFAULT nextval('typecho_links_seq'::regclass),
name character varying(150) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
url character varying(150) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
sort character varying(150) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
image character varying(150) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
description character varying(150) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
"user" character varying(150) COLLATE pg_catalog."default" DEFAULT NULL::character varying,
"order" integer DEFAULT 0
);
5.handsome主题下加载首页继续报错
错误信息类似:
SQLSTATE[22003]: Numeric value out of range: 7 错误: 值 "-8639999998396321618" 超出类型 integer 的范围
LINE 1: ...ECT * FROM typecho_contents WHERE ("created" >= '-86399999...
^
Typecho_Db_Query_Exception: SQLSTATE[22003]: Numeric value out of range: 7 错误: 值 "-8639999998396321618" 超出类型 integer 的范围
LINE 1: ...ECT * FROM typecho_contents WHERE ("created" >= '-86399999...
^ in D:\博客\typecho_local\typecho\var\Typecho\Db\Adapter\Pdo.php:105
Stack trace:
#0 D:\博客\typecho_local\typecho\var\Typecho\Db\Adapter\Pdo\Pgsql.php(111): Typecho_Db_Adapter_Pdo->query('SELECT * FROM ...', Object(PDO), 1, 'SELECT', 'typecho_content...')
#1 D:\博客\typecho_local\typecho\var\Typecho\Db.php(367): Typecho_Db_Adapter_Pdo_Pgsql->query('SELECT * FROM ...', Object(PDO), 1, 'SELECT', 'typecho_content...')
#2 D:\博客\typecho_local\typecho\var\Typecho\Db.php(398): Typecho_Db->query(Object(Typecho_Db_Query), 1)
#3 D:\博客\typecho_local\typecho\usr\themes\handsome\libs\Content.php(3068): Typecho_Db->fetchAll(Object(Typecho_Db_Query))
#4 D:\博客\typecho_local\typecho\usr\themes\handsome\component\sidebar.php(24): Content::returnHotPosts(Object(Widget_Archive))
#5 D:\博客\typecho_local\typecho\var\Widget\Archive.php(1952): require('D:\\\xE5\x8D\x9A\xE5\xAE\xA2\\typec...')
#6 D:\博客\typecho_local\typecho\usr\themes\handsome\index.php(84): Widget_Archive->need('component/sideb...')
#7 D:\博客\typecho_local\typecho\var\Widget\Archive.php(2037): require_once('D:\\\xE5\x8D\x9A\xE5\xAE\xA2\\typec...')
#8 D:\博客\typecho_local\typecho\var\Typecho\Router.php(138): Widget_Archive->render()
#9 D:\博客\typecho_local\typecho\index.php(23): Typecho_Router::dispatch()
#10 {main}
handsome 7.3.1 主题bug, 修改 typecho\usr\themes\handsome\libs\Content.php
3043 行前后
public static function returnHotPosts($hot)
{
$options = mget();
//$days = 99999999999999;
// 365*40 = 14600
$days = 14600;
为handsome主题部署PostgreSQL全文检索
结巴分词
参考:https://github.com/fxsjy/jieba
安装: pip install jieba
支持四种分词模式,后续选择搜索引擎模式:
- 精确模式,试图将句子最精确地切开,适合文本分析;
- 全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;
- 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。
- paddle模式,利用PaddlePaddle深度学习框架,训练序列标注(双向GRU)网络模型实现分词。同时支持词性标注
示例:
# -*- coding: utf-8 -*-
"""
Spyder Editor
This is a temporary script file.
"""
import jieba
aStr = "小明硕士毕业于中国科学院计算所,后在日本京都大学深造"
# 搜索引擎模式
seg_list = jieba.cut_for_search(aStr)
print(','.join(seg_list))
'''
小明,硕士,毕业,于,中国,科学,学院,科学院,中国科学院,计算,计算所,,,后,在,日本,京都,大学,日本京都大学,深造
'''
为handsome主题部署PostgreSQL全文检索
参考:
这里为使用PostgreSQL部署的typecho添加全文检索功能, typecho主题为 handsome(主题返回的搜索字符串会合并,不会用到很复杂的搜索).
1.修改表结构
ALTER TABLE typecho_contents ADD COLUMN tsv tsvector;
CREATE INDEX tsv_idx ON typecho_contents USING gin(tsv);
2.填充 tsv 列
tsvector 数据类型的格式类似:
'a':1,6,10 'and':8 'ate':9 'cat':3 'fat':2,11 'mat':7 'on':5 'rat':12 'sat':4
- 'a':分词后的关键词
- 1,6,10: 一个位置通常表示源词在文档中的定位。位置信息可以被用于邻近排名。位置值可以从 1 到 16383,更大的数字会被 16383替换。对于相同的词位出现的重复位置将被丢弃。
- 关键词之间有空格:
'a':1,6,10
和'and':8
之间有空格 - 插入tsvector 数据类型数据时, 单引号要转义成双引号.
- 嵌入的引号和反斜线必须被双写
具有位置的词位可以进一步地被标注一个权重,它可以是A、 B、C或D。 D是默认值并且因此在输出中不会显示:
'a':1A 'cat':5 'fat':2B,4C
权重通常被用来反映文档结构,例如将主题词标记成与正文词不同。文本搜索排名函数可以为不同的权重标记器分配不同的优先级。
根据 typecho 的 title,tags,text生成 tsv,其中标题和tag对应分级A,内容对应分级B:
def segList2tsv(aStr,segList,weight):
# 根据字符串生成tsv字符串的函数,位置仅找第一个
# weight 取值 A,B,C
tsv_list = []
if len(segList)>0:
for word in segList:
if word not in ["'",'"','\\',' ']:
place = aStr.index(word)
if place > 16383:
place=16383
if place==0:
place=1
word=word.replace("'","''").replace(r"\\",r"\\\\")
tsv_list.append("''" + word + "'':" + str(place) + weight)
if len(tsv_list) >0:
tsv_str = " ".join(tsv_list)
return tsv_str
return ''
def get_seg(text):
# 获取分词列表
text = text.lower()
words = []
digitals = []
lines = text.split('\n')
for line in lines:
# 获取英文单词
temp_line = re.sub(r"[^A-Za-z]", " ", line.strip())
temp_words = temp_line.split()
if len(temp_words)>0:
for word in temp_words:
if len(word)>=2:
words.append(word)
# 获取数字
temp_line = re.sub(r"[^0-9]", " ", line.strip())
temp_digitals = temp_line.split()
if len(temp_digitals)>0:
for d in temp_digitals:
digitals.append(d)
seg_list = list(jieba.cut_for_search(text))
seg_list.extend(words)
seg_list.extend(digitals)
seg_list = list(set(seg_list))
list_to_return = []
for s in seg_list:
if len(s)>1:
list_to_return.append(s)
return list_to_return
def tsv4typecho(title,tags,text):
# 生成填充 typecho_contents 表的 tsv字段的值
text_title_tags = title + ' '
if len(tags)>0:
for t in tags:
text_title_tags += t
text_title_tags = text_title_tags.lower()
# 隐藏 contents表text字段开头的文字
text=text.replace('\n',' ').replace('<!--markdown-->','').lower()
segList_title_tags = get_seg(text_title_tags)
seg_list_text = get_seg(text)
segList_text = list(set(seg_list_text) - set(segList_title_tags))
# get tsv_list
tsv_str=''
tsv_str_title_tags = segList2tsv(text_title_tags,segList_title_tags,"A")
tsv_str_text = segList2tsv(text,segList_text,"B")
if (len(tsv_str_title_tags)) >0 :
tsv_str += tsv_str_title_tags
if (len(tsv_str_text)) >0 :
if (len(tsv_str_title_tags)) >0 :
tsv_str += ' ' + tsv_str_text
else:
tsv_str += tsv_str_text
return tsv_str
新建或更新 post时,调用该函数更新 tsv数据列。
3.查询语法
搜索语句:
SELECT * FROM typecho_contents WHERE tsv @@ '搜索字符串'
搜索字符串由布尔运算符 & (AND)
, | (OR)
和 ! (NOT)
分离的单个标记组成.
实际搜索时, 可以考虑空格分离的各个单词之间是用 &
连接的:
def SQLgenSearchStr(inputStr):
# 将输入的搜索字符串转换为 sql语句,用 &连接
keywords = inputStr.split(' ')
keywords_new = []
for k in keywords:
temp_str=k.strip().replace("'","")
if temp_str!='':
keywords_new.append(temp_str)
if len(keywords_new)>0:
keywordsStr = ' & '.join(keywords_new)
sql = "select * from typecho_contents WHERE tsv @@ '" + keywordsStr + "';"
return sql
return ''
SQL片段示例:
select * from typecho_contents WHERE tsv @@ 'a & b | c ';
在handsome主题部署时,目前仅支持一种复杂查询 &
。
4.在handsome主题上部署搜索
1.编辑文件 themes/handsome/libs/interface/Ajax.php
, 修改函数 searchGetResult
//增加了参数 $currGroup=''
function searchGetResult($thisText,$summaryNam =20,$currGroup=''){
// $filePath = __TYPECHO_ROOT_DIR__ . __TYPECHO_PLUGIN_DIR__ . DIRECTORY_SEPARATOR.'Handsome'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'search.json';
// $file = file_get_contents($filePath);
// $cache = json_decode($file,true);
// $html = "";
// $resultLength = 0;
$searchResultArray = [];//搜索结果
if (trim($thisText) !== ""){
// $searchArray = mb_split(" ",$thisText);
// $searchArray[] = $thisText;
// foreach ($searchArray as $thisText){
// if (trim($thisText) != ""){
// foreach ($cache as $item) {
// $content_ok = mb_stripos($item['content'], $thisText);
// if ($content_ok!==false){//内容中有匹配的结果
// //高亮内容
// $contentMatch = mb_substr($item['content'],max(0,$content_ok -$summaryNam/2),min($summaryNam,mb_strlen
// ($item['content']) -$content_ok));
// $contentMatch = str_ireplace($thisText,"<mark class='text_match'>".$thisText."</mark>",
// $contentMatch);
// $searchResultArray [] = array(
// "path" => $item["path"],
// "title" => $item["title"],
// "content" => $contentMatch
// );
// print_r(array(
// "path" => $item["path"],
// "title" => $item["title"],
// "content" => $contentMatch
// ));
// print_r('<br />');
// $resultLength ++;
// }else{
// //高亮标题
// $title_ok = mb_stripos($item['title'], $thisText);;
// if ($title_ok!== false){//标题中有匹配的结果
// $contentMatch = mb_substr($item['content'],0,min(30,mb_strlen($item['content']) - $title_ok));
// $contentMatch = str_ireplace($thisText,"<mark class='text_match'>".$thisText."</mark>",
// $contentMatch);
// $searchResultArray [] = array(
// "path" => $item["path"],
// "title" => $item["title"],
// "content" => $contentMatch
// );
// $resultLength ++;
// }else{
// //匹配不是
// continue;
// }
// }
// }
// }
// }
// ==> pg FTS
$db = Typecho_Db::get();
$strSearch=strtolower(trim($thisText));
// 多个连续空格只保留一个
$strSearch = preg_replace("/\s(?=\s)/","\\1",$strSearch);
// 支持多关键字查询
$strSearch=str_replace(" "," & ",$strSearch);
//判断当前用户角色是否为管理员,如果不是管理员,仅搜索publish文档
if ($currGroup == "administrator"){
$query= $db->select('slug','title')->from('table.contents')->where('tsv @@ ?', $strSearch);
}else{
$query= $db->select('slug','title')->from('table.contents')->where('tsv @@ ?', $strSearch)
->where('status = ?', 'publish');
}
$results = $db->fetchAll($query);
foreach ( $results as $result){
$searchResultArray [] = array(
"path" => "/archives/".$result["slug"].".html",
"title" => $result["title"],
"content" => ""
);
}
// <== pg FTS
$searchResultArray = Utils::array_unset_tt($searchResultArray,"path");
}
return $searchResultArray;
}
编辑文件 themes/handsome/search.php
, 计算 searchGetResult 函数需要的参数 currGroup 并传入参数:
//从自己的接口中获取搜索内容
//计算 searchGetResult 函数需要的参数 currGroup 并传入参数
$currGroup = get_object_vars($this->user) ['row']['group'];
$array = searchGetResult($this->request->keywords,100,$currGroup);
2.关闭 Ajax搜索请求
编辑 usr/themes/handsome/assets/js/core.min.js
,将如下代码:
{var a=$("#search_input").val();if(""!==a.trim()){$("#spin-search").addClass("show inline"),$("#icon-search").addClass("hide"),null!==b&&(b.abort(),b=null),b=$.getJSON("?action=ajax_search&content="+a,function(a){$("#search_tips_drop").text(""),$("#search_tips_drop").removeClass("hide"),$(a.results).appendTo("#search_tips_drop"),$("#spin-search").removeClass("show inline"),$("#icon-search").removeClass("hide"),b=null})}
替换为如下代码,关闭掉 action=ajax_search
:
{var a=$("#search_input").val();if(""!==a.trim()){$("#icon-search").addClass("show inline"),$("#icon-search").addClass("hide"),null!==b&&(b.abort(),b=null)}
3.支持多关键字查询
为了支持多关键字查询,修改 typecho/var/Widget/Archive.php
,将 $keywords = $this->request->filter('url', 'search')->keywords;
替换为 $keywords = $this->request->keywords;
, 并增加相关安全校验:
private function searchHandle(Typecho_Db_Query $select, &$hasPushed)
{
/** 增加自定义搜索引擎接口 */
//~ fix issue 40
// $keywords = $this->request->filter('url', 'search')->keywords;
// 空过滤
$keywords = trim($this->request->keywords);
// PHP、HTML标签过滤
$keywords = strip_tags($keywords);
// xss
$keywords = htmlspecialchars($keywords,ENT_QUOTES,'UTF-8');
$keywords = $this->clean_input($keywords);
同时在该文件中增加函数 clean_input()
的实现,在文件的最后一个 }
之前添加如下代码:
/**
* 修改自:xss_clean.php,参考: https://gist.github.com/mbijon/1098477
*
* @access public
* @param string $input
* @param int $safe_level
* @return string $output
*/
public function clean_input( $input, $safe_level = 0 ) {
$output = $input;
do {
// Treat $input as buffer on each loop, faster than new var
$input = $output;
// Remove unwanted tags
// $output = $this->strip_tags( $input );
$output = $this->strip_encoded_entities( $output );
// Use 2nd input param if not empty or '0'
if ( $safe_level !== 0 ) {
$output = $this->strip_base64( $output );
}
} while ( $output !== $input );
return $output;
}
/**
* @access private
* @param string $input
* @return string $input
*/
private function strip_encoded_entities( $input ) {
// Fix &entity\n;
$input = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $input);
$input = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $input);
$input = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $input);
$input = html_entity_decode($input, ENT_COMPAT, 'UTF-8');
// Remove any attribute starting with "on" or xmlns
$input = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+[>\b]?#iu', '$1>', $input);
// Remove javascript: and vbscript: protocols
$input = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $input);
$input = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $input);
$input = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $input);
// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$input = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $input);
$input = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $input);
$input = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $input);
return $input;
}
/**
* @access private
* @param string $input
* @return string $input
*/
private function strip_base64( $input ) {
$decoded = base64_decode( $input );
$decoded = $this->strip_tags( $decoded );
$decoded = $this->strip_encoded_entities( $decoded );
$output = base64_encode( $decoded );
return $output;
}
检索示例:
- 示例1:这里为单一关键词
typecho
: https://yinhe.co/search/typecho - 示例2:这里为三个关键词
词库 结巴 分词
, https://yinhe.co/search/%E8%AF%8D%E5%BA%93%20%E7%BB%93%E5%B7%B4%20%E5%88%86%E8%AF%8D
如果看不到效果,可能的原因:
- 没有刷新hansome插件中的离线缓存,js文件没更新
- nginx设置了js缓存
- 浏览器缓存了js,清除浏览器历史
4.其他
可以考虑关闭handsome主题的实时索引构建。
优化结巴分词
1.英文词库
推荐COCA词库:
链接: https://pan.baidu.com/s/1v2BjI-4Xbpyc-Ot8diE4MQ 提取码: dn2y
结巴分词对字典的要求:词典格式和 dict.txt 一样,一个词占一行;每一行分三部分:词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。file_name 若为路径或二进制方式打开的文件,则文件必须为 UTF-8 编码。
根据EXCEL生成字典脚本:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# coca.py
import xlrd # conda install xlrd
file= 'COCA60000.xlsx'
data = xlrd.open_workbook(file)
table = data.sheet_by_index(0)
nrows = table.nrows
# 列名:RANK # PoS WORD TOTAL SPOKEN FICTION MAGAZINE NEWSPAPER ACADEMIC
# 取 word 和 total
excel_list = []
for row in range(1, nrows):
excel_list.append((table.cell(row, 2).value).strip().lower() + ' ' + str(int(table.cell(row, 3).value)))
txt=('\n').join(excel_list)
dict_name='dict_coca60000.txt'
# 字典文件 820k左右,不到1MB
with open(dict_name, 'w',encoding='utf-8') as f:
f.write(txt)
使用:
jieba.load_userdict('dict_coca60000.txt')
2.中文词库
funNLP:https://github.com/fighting41love/funNLP
© Licensed under CC BY-NC-SA 4.0不管我们已经观察到多少只白天鹅,都不能确立“所有天鹅皆为白色”的理论。只要看见一只黑天鹅就可以驳倒它。——卡尔·波普尔