typecho基本安装配置

ubuntu 下安装 typecho: 基于nginx + SQLite

1.安装nginx

sudo apt-get install nginx 

2.安装 php7.4

sudo add-apt-repository ppa:ondrej/php &&  sudo apt-get update &&  sudo apt-get upgrade
sudo apt-get install   php7.4-curl php7.4-gd php7.4-mbstring php7.4-xml php7.4-xmlrpc php7.4-fpm  php7.4-sqlite3 php7.4-bcmath php7.4-imagick sqlite3 
sudo apt upgrade

3.创建数据库

参考:

cd 自己创建的博客数据目录/data
sqlite3 typecho.db
sqlite> 
sqlite> .databases
main: 自己创建的博客数据目录/data/typecho.db

进入sqlite3命令行后运行 .databases, 否则可能不会生成数据库.

数据库配置优化:参考 sqlite的配置文件, 在没有数据前可以考虑通过 SQLite命令行修改 page_size.

4.配置 nginx

自定义附件目录:假定自己规划并创建的附件目录路径为: 自己创建的博客数据目录/data/attachment

nginx的配置文件中尽量避免出现中文.

#root /var/www/html;
root typecho程序解压后的目录/build;
index index.php index.html index.htm;

# pass PHP scripts to FastCGI server
    location ~ .*\.php(\/.*)*$ {
               include snippets/fastcgi-php.conf;
               fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
                client_max_body_size 100M;
        }

    location /attachment/ {
               alias 自己创建的博客数据目录/data/attachment/;
        }

配置gzip压缩,编辑 nginx.conf

##
        # Gzip Settings
        ##

        gzip on;

        gzip_vary on;
        # gzip_proxied any;
        gzip_comp_level 9;
        gzip_buffers 500 8k;
        gzip_http_version 1.1;
        gzip_types image/jpeg image/png image/jpg text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

重启: sudo service nginx restart

5.配置 php7.4

/etc/php/7.4/fpm/php.ini,部分字段:

#upload_max_filesize = 2M
upload_max_filesize = 200M

重启: sudo service php7.4-fpm restart

6.安装 typecho

参考: http://docs.typecho.org/install

cd 自己期望安装typecho的目录/blog/
wget https://typecho.org/downloads/xxxx-release.tar.gz
tar -xzf xxxx-release.tar.gz 

访问 http://127.0.0.1/install.php 安装 typecho

数据库文件路径: 自己创建的博客数据目录/data/typecho.db

7.typecho登录404问题

参考:http://docs.typecho.org/faq

修改nginx配置中对应的php部分:

    #location ~ \.php$ {
    location ~ .*\.php(\/.*)*$ {

修改php配置(有可能不需要):

;cgi.fix_pathinfo=1
cgi.fix_pathinfo=1

8.sqlite客户端: sqlitebrowser

参考: https://sqlitebrowser.org/dl/

安装:

sudo  add-apt-repository -y ppa:linuxgndu/sqlitebrowser
sudo  apt-get update
sudo  apt-get install sqlitebrowser

使用: 在ubuntu应用中打开 DB Browser for SQLite

win10 下安装 typecho: 基于nginx + SQLite

1. 安装php7.4

下载: http://windows.php.net/download/

下载线程安全版本VC15 x64 Thread Safe :php-7.4.9-Win32-vc15-x64.zip

解压文件复制到: C:\software\php

复制 php.ini-development 并重命名为 php.ini

修改 php.ini, 扩展支持要求参考 http://docs.typecho.org/install:

  • Mysql, PostgreSQL, SQLite 任意一种数据库支持,并在PHP中安装了相关扩展
  • CURL或者Socket扩展支持:Socket
  • mbstring或者iconv扩展支持:mbstring
extension_dir = "C:/software/php/ext"
date.timezone = Asia/Shanghai

enable_dl = On
cgi.fix_pathinfo=1
cgi.force_redirect = 0
fastcgi.impersonate = 1
cgi.rfc2616_headers = 1
upload_max_filesize = 200M
max_execution_time = 300

extension=bz2
extension=curl
extension=fileinfo
extension=gd2
extension=gettext
extension=mbstring
extension=exif      ; Must be after mbstring as it depends on it
extension=pdo_sqlite
extension=sockets

[opcache]
;添加如下行开启opcache
zend_extension=php_opcache.dll
;开关打开
opcache.enable=1 
; 开启CLI
opcache.enable_cli=1 
; 可用内存, 酌情而定, 单位为:Mb
opcache.memory_consumption=512 
; Zend Optimizer + 暂存池中字符串的占内存总量.(单位:MB)
opcache.interned_strings_buffer=32
; 对多缓存文件限制, 命中率不到 100% 的话, 可以试着提高这个值
opcache.max_accelerated_files=1000000
opcache.validate_timestamps=1
; Opcache 会在一定时间内去检查文件的修改时间, 这里设置检查的时间周期, 默认为 2, 定位为秒
opcache.revalidate_freq=2
; 需要创建目录  C:/software/php/temp
;开启Opcache File Cache可以让Opcache把opcode缓存缓存到外部文件中
; 对于一些脚本, 会有很明显的性能提升
;目录下Cache的一些Opcode的二进制导出文件可以跨PHP生命周期存在.
opcache.file_cache="C:/software/php/temp"

2.安装SQLite3

从官网https://www.sqlite.org/download.html 下载 sqlite-dll-win64-x64-3330000.zip, sqlite-tools-win32-x86-3330000.zip, 解压到 C:\software\sqlite3,最新版本可能呢不是333:

目录 C:\software\sqlite3 的内容:

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2020/10/16     11:29         521728 sqldiff.exe
-a----        2020/10/16     11:29           6262 sqlite3.def
-a----        2020/10/16     11:29        1950208 sqlite3.dll
-a----        2020/10/16     11:29         987136 sqlite3.exe
-a----        2020/10/16     11:29        2037248 sqlite3_analyzer.exe

C:\software\sqlite3 添加到系统 path变量

3.创建数据库

参考:

  • https://www.runoob.com/sqlite/sqlite-create-database.html
  • https://www.jianshu.com/p/30e777609367

必须在sqlite命令行下执行 .databases 才会生成数据库:

d:
D:\>cd D:\博客\typecho_local\data

D:\博客\typecho_local\data>sqlite3 typecho.db
sqlite> .databases
main: D:\博客\typecho_local\data\typecho.db
sqlite>

4. 安装nginx

下载: http://nginx.org/en/download.html

下载 Stable version,如: nginx-1.18.0.zip

解压文件复制到: C:\software\nginx 下, 配置:

location / {
            #root   html;
            root D:/博客/typecho_local/typecho;
            #index  index.html index.htm;
            index index.php;
        }

        # for attachment files
        location ^~ /attachment/ {
               alias D:/博客/typecho_local/data/attachment/;
        }

常用命令:

# 启动
start  .\nginx.exe 

# 停止
.\nginx.exe -s stop

# 重载 nginx (修改配置文件后用该命令可重新加载配置文件)
.\nginx.exe -s reload

配置对php的支持:

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ .*\.php(\/.*)*$ {
        #    root           html;
        root D:/博客/typecho_local/typecho;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            #fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include        fastcgi_params;
            client_max_body_size 200M;
        }

开启 phpcgi

C:\software\php\php-cgi.exe -b 127.0.0.1:9000 -c C:\software\php\php.ini

重载nginx配置

5.安装 typecho

参考:http://docs.typecho.org/install

https://github.com/typecho/typecho/releases 下载zip,解压到 D:\博客\typecho_local\typecho

访问 http://127.0.0.1/install.php 安装 typecho

数据库文件路径: D:/博客/typecho_local/data/typecho.db

6.typecho登录404问题

参考:http://docs.typecho.org/faq

修改nginx配置:

    #location ~ \.php$ {
    location ~ .*\.php(\/.*)*$ {

7.博客启停脚本

需要将解压的 RunHiddenConsole添加到系统path路径.

启动脚本:blogstart.bat

@ECHO OFF
echo start nginx ......
c:
cd C:\software\nginx
start .\nginx.exe
echo start php-cgi ......
cd C:\software\
.\RunHiddenConsole.exe "C:\software\php\php-cgi.exe" -b 127.0.0.1:9000 -c C:\software\php\php.ini

停止脚本:blogstop.bat

@ECHO OFF
echo stop nginx ......
taskkill /F /IM nginx.exe
echo stop php-cgi ......
taskkill /F /IM php-cgi.exe

8.其他

登录页面报错: Notice: Trying to access array offset on value of type null in \admin\common.php on line 32

参考:https://github.com/typecho/typecho/issues/944

typecho其他问题

1.报错: Call to undefined function openssl_decrypt()

PHP扩展openssl没有开启或安装.

windows下PHP开启openssl扩展方法:

打开 php.ini,找到 ;extension=openssl ,直接去掉前面的分号即可。

2.对于管理员群组,支持搜索私密文章内容

需要修改主题的搜索接口,对于typecho提供的默认搜索接口,修改 Archive.php 第1177行前后的代码:

private function searchHandle(Typecho_Db_Query $select, &$hasPushed)
    {
        /** 增加自定义搜索引擎接口 */
        //~ fix issue 40
        $keywords = $this->request->filter('url', 'search')->keywords;
        $this->pluginHandle()->trigger($hasPushed)->search($keywords, $this);

        if (!$hasPushed) {
            $searchQuery = '%' . str_replace(' ', '%', $keywords) . '%';

            /** 搜索无法进入隐私项保护归档 */
            /* 支持管理员搜索所有文档 */
            /* 原始代码 ===> */
            /*
            $select->where('table.contents.password IS NULL')
            ->where('table.contents.title LIKE ? OR table.contents.text LIKE ?', $searchQuery, $searchQuery)
            ->where('table.contents.type = ?', 'post');
            */
            /* 原始代码 <=== */
            /* 修改后代码 ===> */
            $currGroup = get_object_vars($this->user)['row']['group'];
            if ($currGroup == "administrator"){
                $select->where('table.contents.title LIKE ? OR table.contents.text LIKE ?', $searchQuery, $searchQuery)
            ->where('table.contents.type = ?', 'post');
            }else{
                $select->where('table.contents.password IS NULL')
                ->where('table.contents.title LIKE ? OR table.contents.text LIKE ?', $searchQuery, $searchQuery)
                ->where('table.contents.type = ?', 'post');
            }
            /* 修改后代码 <=== */
        }

3.插入 html标签

在html标签的上一行和下一行插入三个感叹号. 这是 typecho的一个不兼容特性, 建议直接在 typecho主题中换掉默认的markdown解析器.

!!!
<center>test</center><br />
!!!

4.为nginx 添加伪静态规则则

伪静态规则参考 http://www.mabiji.com/typecho/typechorewrite.html

vi /etc/nginx/sites-enabled/typecho,

        location / {
                try_files $uri $uri/ =404;

                # rewrite rules
                if (-f $request_filename/index.html){
                         rewrite (.*) $1/index.html break;
                }
                if (-f $request_filename/index.php){
                        rewrite (.*) $1/index.php;
                }
                if (!-e $request_filename){
                        rewrite (.*) /index.php;
                }

        }

5. 去掉url中的index.php

  1. 为nginx 添加伪静态规则
  2. typecho 后台:永久链接设置

  3. 是否使用地址重写功能:启用

  4. 自定义文章路径: wordpress风格 /archives/{slug}.html 或其他自定义风格

6.前台首页文章按修改时间输出

修改文件 typecho/var/Widget/Archive.php, 搜索 仅输出文章,将其中的 table.contents.created 改为 table.contents.modified 即可。对查询时间的影响未知

        /** 仅输出文章 */
        $this->_countSql = clone $select;

        $select->order('table.contents.modified', Typecho_Db::SORT_DESC) /** 修改该行 */
        ->page($this->_currentPage, $this->parameter->pageSize);
        $this->query($select);
    }

typecho性能优化

TpCache

提速效果明显。

1.安装 Memcached

Memcached是一个免费的开源高性能内存中键值数据存储。它最常用于通过从数据库调用结果中缓存各种对象来加速应用程序。

apt install memcached libmemcached-tools  php7.4-memcached

libmemcached-tools软件包包含几个用于管理Memcached服务器的命令行工具

查看状态: service memcached status

配置文件: /etc/memcached.conf

2.安装TpCache

  • 下载: https://github.com/phpgao/TpCache
  • 将文件夹重命名为TpCache,再拷贝至 usr/plugins/下。
  • 后台启用后设置
  • 需要缓存的页面:所有
  • 是否对已登录用户失效:关闭
  • 是否支持SSL:开启
  • 缓存驱动:Memcached

PostgreSQL数据库优化

1.表索引,实测效果不大

以下针对 PostgreSQL数据库,添加了4条索引:

CREATE INDEX typecho_contents_modified ON public.typecho_contents USING btree (modified);
CREATE INDEX typecho_contents_status ON public.typecho_contents USING btree (status);
CREATE INDEX typecho_contents_type ON public.typecho_contents USING btree (type);
CREATE INDEX typecho_relationships_mid ON public.typecho_relationships USING btree (mid);

2.数据库配置

配置优化,vi /etc/postgresql/13/main/postgresql.conf, 并重启数据库

work_mem = 4MB
effective_cache_size = 1GB
maintenance_work_mem = 64MB 
wal_buffers = 768kB
  • effective_cache_size,优化器假设一个查询可以用的最大内存,和shared_buffers无关(推荐内存的1/2) , 设置稍大,优化器更倾向使用索引扫描而不是顺序扫描
  • maintenance_work_mem 16MB 这里定义的内存只是被VACUUM等耗费资源较多的命令调用时使用 , 把该值调大,能加快命令的执行
  • wal_buffer 768kB 日志缓存区的大小 可以降低IO,如果遇上比较多的并发短事务,应该和commit_delay一起用

typecho插件

1.sitemap插件

参考: https://github.com/yinheco/typecho_sitemap

对于百度, 最好 安装自动推送代码 替代sitemap.

2.Mac风格代码高亮插件

以下配置描述基于 handsome主题部署。

  • 下载插件CodePrettify 上传到插件目录 usr/plugins/, 文件夹名改为 CodePrettify;
  • 进入网站后台-控制台-插件-激活插件(请勿与其它同类插件同时启用,以免互相影响)
  • 修改(替换)/usr/themes/handsome/assets/css/ 下的 handsome.min.css文件, 可以从这里 下载 handsome主题对应的版本
  • 设置:选择主题风格,是否显示行号等
  • handsome主题后台 --> 设置外观 --> 主题增强-->关闭启用主题内置的代码高亮(支持19种常用语言)
  • handsome主题后台设置 Pjax, 参考https://www.xcnte.com/archives/523/. handsome主题后台搜索 PJAX回调函数 有两处地方,注意不要填错地方了.
if (typeof Prism !== 'undefined') {
var pres = document.getElementsByTagName('pre');
                for (var i = 0; i < pres.length; i++){
                    if (pres[i].getElementsByTagName('code').length > 0)
                        pres[i].className  = 'line-numbers';}
Prism.highlightAll(true,null);}

深色模式,浅色模式设置后, 可能需要重新设置代码高亮.

参考:

3.typecho-markdown插件

https://github.com/mrgeneralgoo/typecho-markdown

4.评论邮件通知插件:LoveKKComment

URL: https://github.com/ylqjgm/LoveKKComment

安装方法

  • 至releases中下载最新版本插件;
  • 将下载的压缩包进行解压并上传至Typecho插件目录中,注意目录名称更改为LoveKKComment;
  • 后台激活插件;
  • 根据自己的实际情况选择邮件发送接口方式;
  • 根据所选的邮件发送接口,配置相应接口参数。

给typecho添加随机引言(名人名言)功能

1.添加随机引言功能代码

可以定制引言内容:引言的内容,放在两个 eot 之间

在主题的 functions.php中添加如下随机引言代码:

/* ===> 随机引言 by https://yinhe.co , 修改自 wordpress 插件 RQuotes*/
function show_quotes() {
    global $show_quotes;
    // eot里面的就是随机显示的格言,可以自定义一句一行即可。
    $quotes = <<< eot
兴趣是最好的老师——爱因斯坦
想象力比知识更重要! 因为知识是有限的, 而想象力概括着世界的一切, 推动着进步, 并且是知识进化的源泉。——爱因斯坦
只有偏执狂才能生存!——Andy Grove 
控制风险的最好办法是深入思考, 而不是投资组合。——巴菲特
价值投资不能保证我们盈利, 但价值投资给我们提供了通向成功的唯一机会。——巴菲特
我从事投资时, 会观察一家公司的全貌; 而大多数投资人只盯着它的股价。——巴菲特
退潮时, 便可知道谁在裸泳。——巴菲特
在b进位制中,以数n起头的数出现的机率为logb(n + 1) − logb(n) —— 本福特定律
Don't misinform your Doctor nor your Lawyer. —— Benjamin Franklin
别向医生和律师提供错误的消息。—— 本杰明·富兰克林
知识上的投资总能得到最好的回报。——本杰明.富兰克林
640K对每一个人来说都已足够 —— 比尔盖茨
一个伟大的程序员, 其价值相当于普通程序员的1万倍!——比尔盖茨
计算机没什么用。他们只会告诉你答案。——毕加索
想想看吧,已经有一百万只猴子坐在一百万台打字机旁,可Usenet就是比不上莎士比。—— Blair Houghton
我向星星许了个愿。我并不是真的相信它,但是反正也是免费的,而且也没有证据证明它不灵。—— 加菲猫
要节约用水,尽量和女友一起洗澡——加菲猫
通往地狱的路,都是由善意铺成的——哈耶克
不管我们已经观察到多少只白天鹅,都不能确立“所有天鹅皆为白色”的理论。只要看见一只黑天鹅就可以驳倒它。——卡尔·波普尔
天地不仁,以万物为刍狗;圣人不仁,以百姓为刍狗。—— 老子
没有人足够完美,以至可以未经别人同意就支配别人。 ——林肯
你可以暂时地蒙骗所有人, 也可以永久地蒙骗部分人, 但不可能永久地蒙骗所有人。——林肯
实力永远意味着责任和危险。 —— 罗斯福. T.
大多数人宁愿死去, 也不愿思考。 -- 事实上他们也确实到死都没有思考。——罗素
如果你想走到高处,就要使用自己的两条腿!不要让别人把你抬到高处;不要坐在别人的背上和头上。—— 尼采
在认识一切事物之后,人才能认识自己,因为事物仅仅是人的界限。——尼采
我注意过,即便是那些声称一切都是命中注定的而且我们无力改变的人,在过马路之前都会左右看。——史提芬·霍金
Stay hungry. Stay foolish.——史蒂夫.乔布斯
这辈子没法做太多的事情, 所以每一件都要做到精彩绝伦!——史蒂夫.乔布斯
我每天都自问: '如果今天是我生命的最后一天, 我还会做今天打算做的事情吗?'——史蒂夫.乔布斯
领袖和跟风者的区别就在于创新!——史蒂夫.乔布斯
尊严不值钱,却是我唯一真正拥有的!—— V For Vendetta
你自己的代码如果超过6个月不看,再看的时候也一样像是别人写——伊格尔森定律
不要恐慌 ——《银河系漫游指南》
eot;

    // Here we split it into lines
    $quotes = explode("\n", $quotes);
    // 随机选择一行输出
    $show_quotes = wptexturize( $quotes[ mt_rand(0, count($quotes)-1 ) ] );
        //echo "<p class=\"blog-post\" style=\"text-align:center; \">".$show_quotes."</p>";
         echo $show_quotes;
}

// https://developer.wordpress.org/reference/functions/wptexturize/
function wptexturize( $text, $reset = false ) {
    global $wp_cockneyreplace, $shortcode_tags;
    static $static_characters            = null,
        $static_replacements             = null,
        $dynamic_characters              = null,
        $dynamic_replacements            = null,
        $default_no_texturize_tags       = null,
        $default_no_texturize_shortcodes = null,
        $run_texturize                   = true,
        $apos                            = null,
        $prime                           = null,
        $double_prime                    = null,
        $opening_quote                   = null,
        $closing_quote                   = null,
        $opening_single_quote            = null,
        $closing_single_quote            = null,
        $open_q_flag                     = '<!--oq-->',
        $open_sq_flag                    = '<!--osq-->',
        $apos_flag                       = '<!--apos-->';

    // If there's nothing to do, just stop.
    if ( empty( $text ) || false === $run_texturize ) {
        return $text;
    }

    // Set up static variables. Run once only.
    if ( $reset || ! isset( $static_characters ) ) {
        /**
         * Filters whether to skip running wptexturize().
         *
         * Returning false from the filter will effectively short-circuit wptexturize()
         * and return the original text passed to the function instead.
         *
         * The filter runs only once, the first time wptexturize() is called.
         *
         * @since 4.0.0
         *
         * @see wptexturize()
         *
         * @param bool $run_texturize Whether to short-circuit wptexturize().
         */
        $run_texturize = apply_filters( 'run_wptexturize', $run_texturize );
        if ( false === $run_texturize ) {
            return $text;
        }

        /* translators: Opening curly double quote. */
        $opening_quote = _x( '&#8220;', 'opening curly double quote' );
        /* translators: Closing curly double quote. */
        $closing_quote = _x( '&#8221;', 'closing curly double quote' );

        /* translators: Apostrophe, for example in 'cause or can't. */
        $apos = _x( '&#8217;', 'apostrophe' );

        /* translators: Prime, for example in 9' (nine feet). */
        $prime = _x( '&#8242;', 'prime' );
        /* translators: Double prime, for example in 9" (nine inches). */
        $double_prime = _x( '&#8243;', 'double prime' );

        /* translators: Opening curly single quote. */
        $opening_single_quote = _x( '&#8216;', 'opening curly single quote' );
        /* translators: Closing curly single quote. */
        $closing_single_quote = _x( '&#8217;', 'closing curly single quote' );

        /* translators: En dash. */
        $en_dash = _x( '&#8211;', 'en dash' );
        /* translators: Em dash. */
        $em_dash = _x( '&#8212;', 'em dash' );

        $default_no_texturize_tags       = array( 'pre', 'code', 'kbd', 'style', 'script', 'tt' );
        $default_no_texturize_shortcodes = array( 'code' );

        // If a plugin has provided an autocorrect array, use it.
        if ( isset( $wp_cockneyreplace ) ) {
            $cockney        = array_keys( $wp_cockneyreplace );
            $cockneyreplace = array_values( $wp_cockneyreplace );
        } else {
            /*
             * translators: This is a comma-separated list of words that defy the syntax of quotations in normal use,
             * for example... 'We do not have enough words yet'... is a typical quoted phrase. But when we write
             * lines of code 'til we have enough of 'em, then we need to insert apostrophes instead of quotes.
             */
            $cockney = explode(
                ',',
                _x(
                    "'tain't,'twere,'twas,'tis,'twill,'til,'bout,'nuff,'round,'cause,'em",
                    'Comma-separated list of words to texturize in your language'
                )
            );

            $cockneyreplace = explode(
                ',',
                _x(
                    '&#8217;tain&#8217;t,&#8217;twere,&#8217;twas,&#8217;tis,&#8217;twill,&#8217;til,&#8217;bout,&#8217;nuff,&#8217;round,&#8217;cause,&#8217;em',
                    'Comma-separated list of replacement words in your language'
                )
            );
        }

        $static_characters   = array_merge( array( '...', '``', '\'\'', ' (tm)' ), $cockney );
        $static_replacements = array_merge( array( '&#8230;', $opening_quote, $closing_quote, ' &#8482;' ), $cockneyreplace );

        // Pattern-based replacements of characters.
        // Sort the remaining patterns into several arrays for performance tuning.
        $dynamic_characters   = array(
            'apos'  => array(),
            'quote' => array(),
            'dash'  => array(),
        );
        $dynamic_replacements = array(
            'apos'  => array(),
            'quote' => array(),
            'dash'  => array(),
        );
        $dynamic              = array();
        $spaces               = wp_spaces_regexp();

        // '99' and '99" are ambiguous among other patterns; assume it's an abbreviated year at the end of a quotation.
        if ( "'" !== $apos || "'" !== $closing_single_quote ) {
            $dynamic[ '/\'(\d\d)\'(?=\Z|[.,:;!?)}\-\]]|&gt;|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_single_quote;
        }
        if ( "'" !== $apos || '"' !== $closing_quote ) {
            $dynamic[ '/\'(\d\d)"(?=\Z|[.,:;!?)}\-\]]|&gt;|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_quote;
        }

        // '99 '99s '99's (apostrophe)  But never '9 or '99% or '999 or '99.0.
        if ( "'" !== $apos ) {
            $dynamic['/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/'] = $apos_flag;
        }

        // Quoted numbers like '0.42'.
        if ( "'" !== $opening_single_quote && "'" !== $closing_single_quote ) {
            $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $open_sq_flag . '$1' . $closing_single_quote;
        }

        // Single quote at start, or preceded by (, {, <, [, ", -, or spaces.
        if ( "'" !== $opening_single_quote ) {
            $dynamic[ '/(?<=\A|[([{"\-]|&lt;|' . $spaces . ')\'/' ] = $open_sq_flag;
        }

        // Apostrophe in a word. No spaces, double apostrophes, or other punctuation.
        if ( "'" !== $apos ) {
            $dynamic[ '/(?<!' . $spaces . ')\'(?!\Z|[.,:;!?"\'(){}[\]\-]|&[lg]t;|' . $spaces . ')/' ] = $apos_flag;
        }

        $dynamic_characters['apos']   = array_keys( $dynamic );
        $dynamic_replacements['apos'] = array_values( $dynamic );
        $dynamic                      = array();

        // Quoted numbers like "42".
        if ( '"' !== $opening_quote && '"' !== $closing_quote ) {
            $dynamic[ '/(?<=\A|' . $spaces . ')"(\d[.,\d]*)"/' ] = $open_q_flag . '$1' . $closing_quote;
        }

        // Double quote at start, or preceded by (, {, <, [, -, or spaces, and not followed by spaces.
        if ( '"' !== $opening_quote ) {
            $dynamic[ '/(?<=\A|[([{\-]|&lt;|' . $spaces . ')"(?!' . $spaces . ')/' ] = $open_q_flag;
        }

        $dynamic_characters['quote']   = array_keys( $dynamic );
        $dynamic_replacements['quote'] = array_values( $dynamic );
        $dynamic                       = array();

        // Dashes and spaces.
        $dynamic['/---/'] = $em_dash;
        $dynamic[ '/(?<=^|' . $spaces . ')--(?=$|' . $spaces . ')/' ] = $em_dash;
        $dynamic['/(?<!xn)--/']                                       = $en_dash;
        $dynamic[ '/(?<=^|' . $spaces . ')-(?=$|' . $spaces . ')/' ]  = $en_dash;

        $dynamic_characters['dash']   = array_keys( $dynamic );
        $dynamic_replacements['dash'] = array_values( $dynamic );
    }

    // Must do this every time in case plugins use these filters in a context sensitive manner.
    /**
     * Filters the list of HTML elements not to texturize.
     *
     * @since 2.8.0
     *
     * @param string[] $default_no_texturize_tags An array of HTML element names.
     */
    $no_texturize_tags = apply_filters( 'no_texturize_tags', $default_no_texturize_tags );
    /**
     * Filters the list of shortcodes not to texturize.
     *
     * @since 2.8.0
     *
     * @param string[] $default_no_texturize_shortcodes An array of shortcode names.
     */
    $no_texturize_shortcodes = apply_filters( 'no_texturize_shortcodes', $default_no_texturize_shortcodes );

    $no_texturize_tags_stack       = array();
    $no_texturize_shortcodes_stack = array();

    // Look for shortcodes and HTML elements.

    preg_match_all( '@\[/?([^<>&/\[\]\x00-\x20=]++)@', $text, $matches );
    $tagnames         = array_intersect( array_keys( $shortcode_tags ), $matches[1] );
    $found_shortcodes = ! empty( $tagnames );
    $shortcode_regex  = $found_shortcodes ? _get_wptexturize_shortcode_regex( $tagnames ) : '';
    $regex            = _get_wptexturize_split_regex( $shortcode_regex );

    $textarr = preg_split( $regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );

    foreach ( $textarr as &$curl ) {
        // Only call _wptexturize_pushpop_element if $curl is a delimiter.
        $first = $curl[0];
        if ( '<' === $first ) {
            if ( '<!--' === substr( $curl, 0, 4 ) ) {
                // This is an HTML comment delimiter.
                continue;
            } else {
                // This is an HTML element delimiter.

                // Replace each & with &#038; unless it already looks like an entity.
                $curl = preg_replace( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', '&#038;', $curl );

                _wptexturize_pushpop_element( $curl, $no_texturize_tags_stack, $no_texturize_tags );
            }
        } elseif ( '' === trim( $curl ) ) {
            // This is a newline between delimiters. Performance improves when we check this.
            continue;

        } elseif ( '[' === $first && $found_shortcodes && 1 === preg_match( '/^' . $shortcode_regex . '$/', $curl ) ) {
            // This is a shortcode delimiter.

            if ( '[[' !== substr( $curl, 0, 2 ) && ']]' !== substr( $curl, -2 ) ) {
                // Looks like a normal shortcode.
                _wptexturize_pushpop_element( $curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes );
            } else {
                // Looks like an escaped shortcode.
                continue;
            }
        } elseif ( empty( $no_texturize_shortcodes_stack ) && empty( $no_texturize_tags_stack ) ) {
            // This is neither a delimiter, nor is this content inside of no_texturize pairs. Do texturize.

            $curl = str_replace( $static_characters, $static_replacements, $curl );

            if ( false !== strpos( $curl, "'" ) ) {
                $curl = preg_replace( $dynamic_characters['apos'], $dynamic_replacements['apos'], $curl );
                $curl = wptexturize_primes( $curl, "'", $prime, $open_sq_flag, $closing_single_quote );
                $curl = str_replace( $apos_flag, $apos, $curl );
                $curl = str_replace( $open_sq_flag, $opening_single_quote, $curl );
            }
            if ( false !== strpos( $curl, '"' ) ) {
                $curl = preg_replace( $dynamic_characters['quote'], $dynamic_replacements['quote'], $curl );
                $curl = wptexturize_primes( $curl, '"', $double_prime, $open_q_flag, $closing_quote );
                $curl = str_replace( $open_q_flag, $opening_quote, $curl );
            }
            if ( false !== strpos( $curl, '-' ) ) {
                $curl = preg_replace( $dynamic_characters['dash'], $dynamic_replacements['dash'], $curl );
            }

            // 9x9 (times), but never 0x9999.
            if ( 1 === preg_match( '/(?<=\d)x\d/', $curl ) ) {
                // Searching for a digit is 10 times more expensive than for the x, so we avoid doing this one!
                $curl = preg_replace( '/\b(\d(?(?<=0)[\d\.,]+|[\d\.,]*))x(\d[\d\.,]*)\b/', '$1&#215;$2', $curl );
            }

            // Replace each & with &#038; unless it already looks like an entity.
            $curl = preg_replace( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', '&#038;', $curl );
        }
    }

    return implode( '', $textarr );
}


// https://developer.wordpress.org/reference/functions/apply_filters/
function apply_filters( $tag, $value ) {
    global $wp_filter, $wp_current_filter;

    $args = func_get_args();

    // Do 'all' actions first.
    if ( isset( $wp_filter['all'] ) ) {
        $wp_current_filter[] = $tag;
        _wp_call_all_hook( $args );
    }

    if ( ! isset( $wp_filter[ $tag ] ) ) {
        if ( isset( $wp_filter['all'] ) ) {
            array_pop( $wp_current_filter );
        }
        return $value;
    }

    if ( ! isset( $wp_filter['all'] ) ) {
        $wp_current_filter[] = $tag;
    }

    // Don't pass the tag name to WP_Hook.
    array_shift( $args );

    $filtered = $wp_filter[ $tag ]->apply_filters( $value, $args );

    array_pop( $wp_current_filter );

    return $filtered;
}

// https://developer.wordpress.org/reference/functions/_x/
function _x( $text, $context, $domain = 'default' ) {
    return translate_with_gettext_context( $text, $context, $domain );
}

// https://developer.wordpress.org/reference/functions/translate_with_gettext_context/
function translate_with_gettext_context( $text, $context, $domain = 'default' ) {
    $translations = get_translations_for_domain( $domain );
    $translation  = $translations->translate( $text, $context );

    /**
     * Filters text with its translation based on context information.
     *
     * @since 2.8.0
     *
     * @param string $translation Translated text.
     * @param string $text        Text to translate.
     * @param string $context     Context information for the translators.
     * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
     */
    $translation = apply_filters( 'gettext_with_context', $translation, $text, $context, $domain );

    /**
     * Filters text with its translation based on context information for a domain.
     *
     * The dynamic portion of the hook, `$domain`, refers to the text domain.
     *
     * @since 5.5.0
     *
     * @param string $translation Translated text.
     * @param string $text        Text to translate.
     * @param string $context     Context information for the translators.
     * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
     */
    $translation = apply_filters( "gettext_with_context_{$domain}", $translation, $text, $context, $domain );

    return $translation;
}

// https://developer.wordpress.org/reference/functions/get_translations_for_domain/
function get_translations_for_domain( $domain ) {
    global $l10n;
    if ( isset( $l10n[ $domain ] ) || ( _load_textdomain_just_in_time( $domain ) && isset( $l10n[ $domain ] ) ) ) {
        return $l10n[ $domain ];
    }

    static $noop_translations = null;
    if ( null === $noop_translations ) {
        $noop_translations = new NOOP_Translations;
    }

    return $noop_translations;
}

// https://developer.wordpress.org/reference/functions/_load_textdomain_just_in_time/
function _load_textdomain_just_in_time( $domain ) {
    global $l10n_unloaded;

    $l10n_unloaded = (array) $l10n_unloaded;

    // Short-circuit if domain is 'default' which is reserved for core.
    if ( 'default' === $domain || isset( $l10n_unloaded[ $domain ] ) ) {
        return false;
    }

    $translation_path = _get_path_to_translation( $domain );
    if ( false === $translation_path ) {
        return false;
    }

    return load_textdomain( $domain, $translation_path );
}

// https://developer.wordpress.org/reference/classes/noop_translations/
class NOOP_Translations {
    var $entries = array();
    var $headers = array();

    function add_entry( $entry ) {
        return true;
    }

    /**
     * @param string $header
     * @param string $value
     */
    function set_header( $header, $value ) {
    }

    /**
     * @param array $headers
     */
    function set_headers( $headers ) {
    }

    /**
     * @param string $header
     * @return false
     */
    function get_header( $header ) {
        return false;
    }

    /**
     * @param Translation_Entry $entry
     * @return false
     */
    function translate_entry( &$entry ) {
        return false;
    }

    /**
     * @param string $singular
     * @param string $context
     */
    function translate( $singular, $context = null ) {
        return $singular;
    }

    /**
     * @param int $count
     * @return bool
     */
    function select_plural_form( $count ) {
        return 1 == $count ? 0 : 1;
    }

    /**
     * @return int
     */
    function get_plural_forms_count() {
        return 2;
    }

    /**
     * @param string $singular
     * @param string $plural
     * @param int    $count
     * @param string $context
     */
    function translate_plural( $singular, $plural, $count, $context = null ) {
        return 1 == $count ? $singular : $plural;
    }

    /**
     * @param object $other
     */
    function merge_with( &$other ) {
    }
}

// https://developer.wordpress.org/reference/functions/wp_spaces_regexp/
function wp_spaces_regexp() {
    static $spaces = '';

    if ( empty( $spaces ) ) {
        /**
         * Filters the regexp for common whitespace characters.
         *
         * This string is substituted for the \s sequence as needed in regular
         * expressions. For websites not written in English, different characters
         * may represent whitespace. For websites not encoded in UTF-8, the 0xC2 0xA0
         * sequence may not be in use.
         *
         * @since 4.0.0
         *
         * @param string $spaces Regexp pattern for matching common whitespace characters.
         */
        $spaces = apply_filters( 'wp_spaces_regexp', '[\r\n\t ]|\xC2\xA0|&nbsp;' );
    }

    return $spaces;
}

// https://xref.trepmal.com/wptrunk/wp-includes/formatting.php.source.txt
function _get_wptexturize_split_regex( $shortcode_regex = '' ) {
    static $html_regex;

    if ( ! isset( $html_regex ) ) {
        // phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation
        $comment_regex =
            '!'             // Start of comment, after the <.
            . '(?:'         // Unroll the loop: Consume everything until --> is found.
            .     '-(?!->)' // Dash not followed by end of comment.
            .     '[^\-]*+' // Consume non-dashes.
            . ')*+'         // Loop possessively.
            . '(?:-->)?';   // End of comment. If not found, match all input.

        $html_regex = // Needs replaced with wp_html_split() per Shortcode API Roadmap.
            '<'                  // Find start of element.
            . '(?(?=!--)'        // Is this a comment?
            .     $comment_regex // Find end of comment.
            . '|'
            .     '[^>]*>?'      // Find end of element. If not found, match all input.
            . ')';
        // phpcs:enable
    }

    if ( empty( $shortcode_regex ) ) {
        $regex = '/(' . $html_regex . ')/';
    } else {
        $regex = '/(' . $html_regex . '|' . $shortcode_regex . ')/';
    }

    return $regex;
}

// https://developer.wordpress.org/reference/functions/wptexturize_primes/
function wptexturize_primes( $haystack, $needle, $prime, $open_quote, $close_quote ) {
    $spaces           = wp_spaces_regexp();
    $flag             = '<!--wp-prime-or-quote-->';
    $quote_pattern    = "/$needle(?=\\Z|[.,:;!?)}\\-\\]]|&gt;|" . $spaces . ')/';
    $prime_pattern    = "/(?<=\\d)$needle/";
    $flag_after_digit = "/(?<=\\d)$flag/";
    $flag_no_digit    = "/(?<!\\d)$flag/";

    $sentences = explode( $open_quote, $haystack );

    foreach ( $sentences as $key => &$sentence ) {
        if ( false === strpos( $sentence, $needle ) ) {
            continue;
        } elseif ( 0 !== $key && 0 === substr_count( $sentence, $close_quote ) ) {
            $sentence = preg_replace( $quote_pattern, $flag, $sentence, -1, $count );
            if ( $count > 1 ) {
                // This sentence appears to have multiple closing quotes. Attempt Vulcan logic.
                $sentence = preg_replace( $flag_no_digit, $close_quote, $sentence, -1, $count2 );
                if ( 0 === $count2 ) {
                    // Try looking for a quote followed by a period.
                    $count2 = substr_count( $sentence, "$flag." );
                    if ( $count2 > 0 ) {
                        // Assume the rightmost quote-period match is the end of quotation.
                        $pos = strrpos( $sentence, "$flag." );
                    } else {
                        // When all else fails, make the rightmost candidate a closing quote.
                        // This is most likely to be problematic in the context of bug #18549.
                        $pos = strrpos( $sentence, $flag );
                    }
                    $sentence = substr_replace( $sentence, $close_quote, $pos, strlen( $flag ) );
                }
                // Use conventional replacement on any remaining primes and quotes.
                $sentence = preg_replace( $prime_pattern, $prime, $sentence );
                $sentence = preg_replace( $flag_after_digit, $prime, $sentence );
                $sentence = str_replace( $flag, $close_quote, $sentence );
            } elseif ( 1 == $count ) {
                // Found only one closing quote candidate, so give it priority over primes.
                $sentence = str_replace( $flag, $close_quote, $sentence );
                $sentence = preg_replace( $prime_pattern, $prime, $sentence );
            } else {
                // No closing quotes found. Just run primes pattern.
                $sentence = preg_replace( $prime_pattern, $prime, $sentence );
            }
        } else {
            $sentence = preg_replace( $prime_pattern, $prime, $sentence );
            $sentence = preg_replace( $quote_pattern, $close_quote, $sentence );
        }
        if ( '"' === $needle && false !== strpos( $sentence, '"' ) ) {
            $sentence = str_replace( '"', $close_quote, $sentence );
        }
    }

    return implode( $open_quote, $sentences );
}
/* <=== 随机引言 */

2.输出引言

在需要输出引言的位置添加代码 <?php show_quotes(); ?> .

如 handsome主题,

a.在首页标题处显示引言

修改主题的 index.php 文件 header 标签的内容:

            <header class="bg-light lter wrapper-md">
                <!--
                <h1 class="m-n font-thin text-black l-h"><?php $this->options->title(); ?></h1>
                -->
                <small class="text-muted letterspacing indexWords">
                        <?php show_quotes(); ?>
                    </small>
            </header>

b.在首页分页按钮下显示引言

修改主题的 index.php 文件:

<?php Content::echoPostList($this) ?>
                <!--分页首页按钮-->
                <nav class="text-center m-t-lg m-b-lg" role="navigation">
                    <?php $this->pageNav('<i class="fontello fontello-chevron-left"></i>', '<i class="fontello fontello-chevron-right"></i>'); ?>
                </nav>
                <?php show_quotes();  ?>

c.在post页文章下添加一条横线和引言

修改主题的 post.php 文件:

         <!--文章内容-->
         <div id="post-content" class="wrapper-lg">

             <?php Content::postContentHtml($this,
                 $this->user->hasLogin()); ?>

            <hr style="height:2px;border:none;border-top:2px groove skyblue;" /> 
             <?php show_quotes();  ?>   

3.部分引言来源

typecho更新脚本实现思路

脚本功能和要求

1.配置项

# =====> 配置信息
dir_mdfiles = r"D:/博客/typecho_local/markdown原始文件"    #需要手工创建相关目录用于编写原始文件,目录路径使用 '/'
dir_attachment = r"D:/博客/typecho_local/data/attachment"  # nginx 中定义的存放附件的路径
dir_attachment_webserver = '/attachment'                   # ningx中配置的附件相对目录
table_prefix = 'typecho_' # 表前缀
authorId = 1
allowComment='0'          # 默认是否允许对文章评论, 长度为1的字符型, 取值: '0','1'
file_extension_list = ['md','jpg','jpeg','png'] # 检查哪些后缀的文件的最后修改时间,'.md' 必须包含. .md 外的其他文件会作为附件复制到 dir_attachment
# <===== 配置信息

2.markdown文件头

在markdown文件头定义文件本身的字段,用于导入数据库

  • 文件头以---作为单独的行开始和结束
  • slug必须唯一,建议以日期开头以更好的保证唯一性,类似 jekyll的文件命名
  • created字段格式: 2020-10-21 09:00
  • categories,tags字段:以英文逗号分隔
  • status: private,publish

示例:

title:  typecho更新脚本实现思路
slug: 20201113_md2te
categories: 工具,编程
tags:  typecho,python脚本
date: 2020-11-13 17:00
status:publish

3.文件中使用的图片

在md文件当前目录存放图片, 方便按文件夹组织和移动md源文件,也方便在 md编辑器中预览 md文件

图片命名规则:20201021xxx.png, 会根据日期复制到对应的目录,并在导入数据库时替换掉图片的 URL

图片插入规则: ![](picFileName), 单独一行插入, []内为空

4.脚本运行

假设脚本名称为: md2te.py ,支持命令行参数 f 强制更新所有md文件,f必须为命令行第二个参数(第一个为python文件本身)(已使用gogs管理脚本的执行):

/path2python /data/md2te.py f

可以设置 crontab任务定时更新(已使用gogs管理脚本的执行):

# update typecho per xx minutes
*/20  * * * * username   /path2/python /path2/md2te.py

文件同步

使用 syncthing 或其他同步工具。脚本可以放在同步文件夹中, 这样修改了脚本后可以马上同步使用gogs非常方便

脚本已经实现的部分功能

  • 支持emoji表情和对应短代码
  • 支持jieba分词自定义字典,支持自定义分词时需要替换掉的字符串
  • 支持删除不存在markdown文件对应的post
  • 自动更新分类,标签以及对应的数量;自动清理count为0的cat 和tag
  • 支持自定义分类的层级和顺序
  • 支持自动维护在markdown文件目录下的图片附件路径(图片以固定格式的日期开头)
  • 支持post的分词维护
© Licensed under CC BY-NC-SA 4.0

天地不仁,以万物为刍狗;圣人不仁,以百姓为刍狗。—— 老子

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!