Nginx For WordPress

##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";

gzip_vary on;
gzip_proxied any;
# 大于 4 之后,压缩大小几乎没有变化
gzip_comp_level 4;
gzip_http_version 1.1;
## 小于256B的不要压缩,默认是 20,太小了
gzip_min_length 256;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

server {
        listen      80;
        server_name docker.local;
        root        /var/www/html;
        index       index.php index.html;

        # 加上 * 不会去检查确切的 conf,如果文件不存在,一样可以通过 nginx 测试
        include /var/www/html/nginx.conf*;

        location = /favicon.ico {
                log_not_found off;
                access_log off;
        }

        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        }

        location = /xmlrpc.php {
                deny all;
                access_log off;
                log_not_found off;
        }

        location / {

                #禁止指定 user_agent 及 user_agent 为空的访问
                if ($http_user_agent ~ "FeedDemon|JikeSpider|Indy Library|Alexa Toolbar|AskTbFXTV|AhrefsBot|CrawlDaddy|CoolpadWebkit|Java|Feedly|UniversalFeedParser|YandexBot|Microsoft URL Control|Swiftbot|ZmEu|oBot|jaunty|Python-urllib|lightDeckReports Bot|YYSpider|DigExt|YisouSpider|HttpClient|MJ12bot|heritrix|EasouSpider|LinkpadBot|Ezooms|^$" )
                {
                        return 403;
                }

                # This is cool because no php is touched for static content.
                # include the "?$args" part so non-default permalinks doesn't break when using query string
                try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
                #include snippets/fastcgi-php.conf;
                #fastcgi_pass docker-php-fpm:9000;
                fastcgi_pass php-fpm:9000;
                include fastcgi_params;
                fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        }

        location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
                expires max;
                log_not_found off;
        }
}

在WordPress Feeds 里加入特色图片

编辑你的Feed模板基本与编辑你的主题模板一样。然而,Feed模板不集成到WordPress主题系统;如果你希望你的订阅的不同版本,你需要创建额外的模板。

自定义Feed模板

feed 模板位于/wp-includes/feed-{type}.php文件, 包括rdf格式、rss、rss和atom。他们是由feed重写规则使用一系列的行动中定义wp-includes /功能。使用 add_action wp-includes / default-filters.php php和附加。

为了覆盖需要清楚自己的模板默认行为叫load_template然后采取适当的步骤。使用一个模板为默认的一个示例rss2 feed 位于模板目录为自定义文章类型:

remove_all_actions( 'do_feed_rss2' );
add_action( 'do_feed_rss2', 'itc_feed_rss2', 10, 1 );

function itc_feed_rss2( $for_comments ) {
	$rss_template = get_template_directory() . '/feeds/itc_feed_rss2.php';
	if(get_query_var( 'post_type' ) == 'post' && file_exists( $rss_template ) )
		load_template( $rss_template );
	else
		do_feed_rss2( $for_comments ); // Call default function
}

然后在你的主题里建立一个feeds 目录,新建一个rss2 文件叫: itc_feed_rss2.php

复制 /wp-includes/feed-rss2.php 里的代码到 itc_feed_rss2.php

然后在<item> 里面加一段代码来插入特色图片,例如

	<item>
		<title><?php the_title_rss() ?></title>
		<link><?php the_permalink_rss() ?></link>
		<?php 
		if ( has_post_thumbnail() ) {
			$large_image_url = wp_get_attachment_image_src( get_post_thumbnail_id( get_the_ID() ), 'full' ); 
			echo '<image>' . $large_image_url[0] . '</image>'; 
		}
		?> 
		<comments><?php comments_link_feed(); ?></comments>

 

WordPress 获取相关文章

或许有点用,不过只适用与文章类型。

设想是:

  1. 首先获取拥有相同标贴的文章
  2. 如果数量不足,获取拥有部分相同标签的文章
  3. 如果数量还不足,获取拥有相同分类的文章
  4. 再不足,获取部分相同分类的文章

代码大致:

/*
 * class itcGetRelatedPosts
 * get related post by tags and categories
 * $related_posts = new itcGetRelatedPosts($post_id, 3);
 * */

class itcGetRelatedPosts {

	protected $_date;

	public function __construct($post_id, $showposts){
		$this->setDefault($post_id, $showposts);
		$this->_getPosts();
	}

	protected function setDefault($post_id, $showposts){
		$this->_date['post_id']			= $post_id;
		$this->_date['exclude_ids'][] 	= $post_id;
		$this->_date['showposts']		= $showposts;
		$this->_date['args']['post_type']	= 'post';
		$this->_date['posts']	= array();
		$this->_date['post_count']	= 0;
		$this->_getTagIds();
		$this->_updateArgs();
	}

	protected function _updateArgs(){
		$this->_date['args']['post__not_in']	= $this->_date['exclude_ids'];
		$this->_date['args']['posts_per_page']	= $this->_date['showposts'] - $this->_date['post_count'];
	}

	protected function _getTagIds(){
		$posttags = get_the_tags($this->_date['post_id']);
		if ($posttags) {
			foreach($posttags as $tag) {
				$this->_date['tag_ids'][] = $tag->term_id;
			}
		}else{
			$this->_date['tag_ids'] = NULL;
		}
	}

	protected function _getCategoryIds(){
		$categories = get_the_category($this->_date['post_id']);
		if($categories){
			foreach($categories as $category) {
				$this->_date['category_ids'][] = $category->term_id;
			}
		}else{ 
			$this->_date['category_ids'] = NULL;
		}
	}

	protected function _getPosts(){
		if($this->_date['tag_ids'] != NULL){
			//Display posts that are tagged with both tag id 37 and tag id 47
			$this->_date['args']['tag__and'] = $this->_date['tag_ids'];
			$this->_pushPost($this->_date['args']);
				
			//To display posts from either tag id 37 or 47
			if($this->_date['post_count'] < $this->_date['showposts']){
				unset($this->_date['args']['tag__and']);
				$this->_date['args']['tag__in'] = $this->_date['tag_ids'];
				$this->_pushPost($this->_date['args']);
			}
		}

		if($this->_date['post_count'] < $this->_date['showposts']){
			$this->_getCategoryIds();
			if(isset($this->_date['args']['tag__and'])) unset($this->_date['args']['tag__and']);
			if(isset($this->_date['args']['tag__in'])) unset($this->_date['args']['tag__in']);
			//Display posts that are in multiple categories. This shows posts that are in both categories 2 and 6:
			$this->_date['args']['category__and'] = $this->_date['category_ids'];
			$this->_pushPost($this->_date['args']);
		}
		//To display posts from either category 2 OR 6
		if($this->_date['post_count'] < $this->_date['showposts']){
			unset($this->_date['args']['category__and']);
			$this->_date['args']['category__in'] = $this->_date['category_ids'];
			$this->_pushPost($this->_date['args']);
		}
	}

	protected function _pushPost($the_args){
		$the_query = new WP_Query($the_args);
		if ( $the_query->have_posts() ) {
			$this->_date['post_count'] = $this->_date['post_count'] + $the_query->post_count;
			while ( $the_query->have_posts() ) { 
				$the_query->the_post();
				$this->_date['exclude_ids'][] = get_the_ID();
				$this->_date['posts'][get_the_ID()] = array(
						'ID' => get_the_ID(),
						'title' => get_the_title(),
						'link'	=> get_permalink(),
						//'format'	=> get_label_format(get_the_ID()),
						'excerpt'	=> get_the_excerpt()
				);
				$this->_date['posts'][get_the_ID()]['date'] = get_the_date(__('F j, Y'));
			}
			$this->_updateArgs();
		}
		wp_reset_postdata();
	}

	public function getPosts(){
		return $this->_date['posts'];
	}

	public function get_date(){
		print_r($this->_date);
	}
}

 

 

WordPress 如何在 Child Theme 重写 Parent 里的 shortcode

很多时候客户选择去购买商业主题来建站,很多商业主题都做得很不错,功能也很完善,但是肯定不是100%客户想要的,有些功能还是要修改的。

对于这种商业主题,作者会不定期更新,所以修改原主题就不是一个很好的解决办法,一般做法是新建一个 child theme 来重写一些功能。

那问题就来了,如何重写 shortcode 呢?方法如下

function itc_shortcodes() {
    remove_shortcode( 'portfolio_slider' );
    add_shortcode( 'portfolio_slider', 'itc_portfolio_slider' );
}

function itc_portfolio_slider(){
    return "YAO";
}
add_action( 'init', 'itc_shortcodes' );

注:对于应该使用哪一个 action,要去夫主题查看一下,如果夫主题是用的 action wp, 那么最后一行就应该替换为

add_action( 'wp', 'itc_shortcodes' );

这样你只需要将原来的代码负责过来放到新的 shortcode 的函数里,修改即可。

A simple way to schedule an Minutely event in WordPress

之前一直没有注意到要在执行任务计划之前先清除,以至于每隔1分钟就添加一个新的任务计划,最后内存枯竭。

add_filter( 'cron_schedules', '_cron_add_minutely' );

function _cron_add_minutely( $schedules ) {
    // Adds once weekly to the existing schedules.
    $schedules['minutely'] = array(
            'interval' => 60,
            'display' => __( 'Minutely' )
    );
    return $schedules;
}

add_action( 'wp', '_setup_schedule' );
function _setup_schedule() {
    //wp_clear_scheduled_hook( 'itc_daily_event' ); 
    if ( ! wp_next_scheduled( 'itc_daily_event' ) ) {
        wp_schedule_event(time(), 'minutely', 'itc_minutely_event' );
    }
}

add_action( '_minutely_event',   '_do_minutely_event' );
function _do_minutely_event() {
    error_log(date('Y-m-d H:i:s') ." check you php log file");
}

删除所有的计划人物by hook name

//Removes a cron based on a hook name
function delete_cron_hooks( $hooks = array(), $blog_id = 0 ) {
    if ( is_multisite() && $blog_id != 0 ) {               
        switch_to_blog( $blog_id );
    }
    $crons = get_option( 'cron' );
    if ( !$crons ) return false;
    foreach ( $crons as $timestamp => $cron ) {
        foreach ( $hooks as $hook ) {
            if ( isset( $cron[ $hook ] ) ) {
                unset( $crons[ $timestamp ] );
            }
        }
    }
 
    update_option( 'cron', $crons );
     
     
} //end delete_cron

怎么定制 Yoast breadcrumb

一般来说Yoast SEO 的 breadcrumb 功能已经很好了。

但是有的时候有的情况下还是需要一定的定制。

如果你有一个自定义文章 custom post type 叫 product,默认情况下, custom post type archive 页面会自动列出所有的 products,但是如果你想更容易的定制 product list 页面,我觉得最好还是创建一个页面模板, page-products.php, 然后创建一个页面来使用这个模板,这样你可以使用 custom fileds 来为这个页面定制更多可控制的内容。

为什么我不使用 custom post type archive page:

  1. archive page 的url 用的是 post-type, 也就是你的链接会是 domian.com/product/, 然而这个情况下的话我觉得用 domain.com/products/ 更有道理些。
  2. 如果 product archive 页面还有其他内容,banner 图片,描述,等等,这样你就很难去设置,而通过页面custom fileds 就很容易解决。
  3. 在使用 WPML 的时候更高的兼容性。

但是这样的设置在使用 Yoast breadcrumb 的时候会出现一个问题,就是你在 product 页面的时候,breadcrumb 会将你带回默认的custom post type archive 页面,这样,我们就需要更改这个链接。

breadcrumb

我的方法是通过 filter “ wpseo_breadcrumb_links ” 来实现。

if (class_exists('WPSEO_Breadcrumbs')) {
    //wpseo_breadcrumb_links
    add_filter('wpseo_breadcrumb_links', 'wpseo_breadcrumb_links_by_wpdevil');

    function wpseo_breadcrumb_links_by_wpdevil($links) {

        if(is_singular('product')){
            unset($links['1']); // 删除 post type archive
            array_splice($links, 1, 0, array(1 => array('id' => PRODUCTS_PAGE_ID))); //在 $links 数组第一位置插入 products 页面的 ID
        }

    	if(is_tax('product_category')){

			array_splice($links, 1, 0, array(1 => array('id' => PRODUCTS_PAGE_ID)));
		}

        if(isset($links[0]['text']) && empty($links[0]['text']))
            $links[0]['text'] = __('Home', WPDEVIL);


        return $links;
    }

     add_filter( 'wpseo_metabox_prio', function() { return 'low';});
}

主要用的一个数组函数

array_splice($links, 1, 0, array(1 => array('id' => PRODUCTS_PAGE_ID)));

在 $links 数组第一位置插入 products 页面的 ID。

PRODUCTS_PAGE_ID 是我预先定义的 静态变量,里面保存着 product page 的 ID。

将Google前端库和字体库替换成360前端公共库CDN服务

360网站卫士常用前端公共库CDN服务

这里提供了由360网站卫士CDN驱动的常用前端公共库以及和谐使用Google公共库&字体库的调用方法。

这里为您提供常用的JavaScript前端库,托管在360众多的全国CDN节点上,覆盖电信、联通、移动等主流运营商线路,您可以在自己的网页上直接通过script标记引用这些资源,让网站访问速度瞬间提速!

另外Google前端公共库的JS资源和Google免费字体库缓存在360网站卫士全国的CDN节点上。

如果你的主题里使用了google资源,可以把下面的代码粘贴到你的主题 functions.php 文件里。

class itcReplaceGoogles{
    /**
     * init Hook
     * 参考: http://www.soulteary.com/2014/06/08/replace-google-fonts.html
     */
    public function __construct()
    {
        add_filter('style_loader_tag', array($this, 'font'), 999);
        add_filter('script_loader_src', array($this, 'script'), 999);
    }
 
 
    /**
     * 将Google免费字体库的域名 fonts.googleapis.com 修改为:fonts.useso.com 
     *
     * @param $text
     * @return mixed
     */
    public function font($text)
    {
        return str_replace('//fonts.googleapis.com/', '//fonts.useso.com/', $text);
    }
    
    /**
     *  将Google前端库的域名 ajax.googleapis.com 修改为:ajax.useso.com 
     * @param type $text
     * @return type
     */
    
    public function script($text)
    {
        return str_replace('//ajax.googleapis.com/', '//ajax.useso.com/', $text);
    }
}

new itcReplaceGoogles();

 

怎么把自定义文章类型(Custom Post Types)加入到 WordPress 主RSS

你需要在你的主题函数里添加一个滤镜(filter),例如:

function _feed_request($qv) {
	if (isset($qv['feed']))
		$qv['post_type'] = get_post_types();
	return $qv;
}
add_filter('request', '_feed_request');

这个滤镜修改了Wordpress的查询,保留默认的文字类型同时加入所有的自定义文章类型(Custom Post Types)。

但是如果只想指定的几个类型在你的feed里,你可以这样:

function _feed_request($qv) {
	if (isset($qv['feed']) && !isset($qv['post_type']))
		$qv['post_type'] = array('post', 'event', 'product');
	return $qv;
}
add_filter('request', '_feed_request');

这样在主feed里就会同时加入 post,event 和 product。

String translation not working when do wordpress Ajax with WPML

  1. 正常的配置 WPML
  2. 使用 PO/MO 文件
  3. 使用 Codestyling Localisation 创建 PO/MO 文件

问题是在时候 Ajax 的时候,函数 __() 失效, 因为,首先你需要加载所有关于当前语言的翻译。很奇怪的 WPML 不能正确的使用翻译文件,这可能是 WORDPRESS 和 WPML 的一些限制。暂时的,下面的方法可能对大家有些帮助。

如果你做一个搜索的功能,首先,添加一个隐藏域到表单里:

if(defined('ICL_SITEPRESS_VERSION') && defined('ICL_LANGUAGE_CODE')){
    echo '<input type="hidden" id="lang" name="lang" value="'.ICL_LANGUAGE_CODE.'" />';
}

在 Ajax call:

if(!empty($_REQUEST['lang']))
{
    global $sitepress;
    $sitepress->switch_lang($_REQUEST['lang'], true);

    $lang = get_template_directory()  . '/language';
    $unload = unload_textdomain('textdomain');
    $load = load_textdomain('textdomain', $lang . '/'. get_locale() . '.mo');
}

参考:http://www.kriesi.at/support/topic/ajax-search-translation-handling-probs/

Move Excerpt After Title Field in WordPress Backend

Just feel better if the excerpt div appear after title field

function wpdevil_to_admin_head() {
    echo <<<EOF
<script>
    jQuery(document).ready(function(cash) {
        if($('#postexcerpt').length > 0){
            var preexcerpt = $('#postexcerpt');
            var curwysiwyg = $('#postdivrich');
            curwysiwyg.prepend(preexcerpt);
        }
    });    
</script>
EOF;
}
add_action('admin_head', 'wpdevil_to_admin_head');