前言

使用过 WordPress 的都知道 WordPress 默认主题都有在线更新的功能,但是我们一般使用的主题却木有,在对欲思主题的二次开发的时候,博猪就用到了一个自动更新的功能,在这里和大家说说吧

程序简介

theme-update-check 是国外的牛人开发的一个终于 WordPress 主题更新的 PHP 类,类似于我们熟悉的缩略图裁图工具 timthumb.php,我们可以很方便的将他集成到我们的主题里面,实现主题的自动更新服务。

文件说明

首先在文末下载文件包,解压出来是三个文件,updates.php 和 info.json。
首先打开 info.json,你会发现是类似下面的代码

{
	"version" : "2.0",
	"details_url" : "http://git.oschina.net/yunluo/yusi",
	"download_url" : "http://git.oschina.net/yunluo/yusi/repository/archive?ref=master"
}

当然,这个是我自己主题的文件,应该都能看懂吧。
主要就三个参数
第一个是版本号
第二个是主题发布页
第三个是主题下载直链,就是输入这个链接,浏览器自动下载主题的那种。

再看一下 updates.php 文件,其他你不需要看,你只需要把主题名字(别名)和 info.json 文件的路径填写进去就好了,其他就不用管了。
然后在主题的 functions.php 写入下面代码

//主题自动更新服务
require 'updates.php';
$example_update_checker = new ThemeUpdateChecker(
    'yusi',
    'info.json 文件路径'//此路径可以开发者自己定义,保证 info.json 文件能够公开访问以及不能被除开发者之外的人修改即可
);

文件部署

首先将 updates.php 文件放在主题根目录,然后将 info.json 放在主题开发者自己的网站根目录里面,当然,其实并不一定是根目录,放在哪里,然后 updates.php 文件里面的路径就是这个,info.json 文件必须保证能够公开访问,然后 over 了!

推送更新

程序是将主题的开发者留下的 meta 文件和主题样式 style..css 里面的版本号进行比对,如果主题开发者的 info.json 文件里面的版本号比使用者的主题文件 style.css 里面的版本号大,那么就会在 WordPress 后台的更新工具进行更新提醒,和正常的 WordPress 更新,插件更新完全一样,如果你选择更新,就自动从主题直链在线安装主题。

注意事项

在使用中,博主发现了一些小问题,在这里也顺便说一下。
最重要的是保证一致性。保证自动更新里面的主题名字和你使用的主题名字一样,比如我目前使用的主题名字叫 yusi,对,这里说的是主题文件夹名字,而不是其他中文名字,需要保持一致的地方有 info.json,functions.php 中的初始化函数代码,updates.php 文件这四个地方,另外下载链接所下载的主题包文件名字也必须是一样的。
另外有很多朋友和博猪一样喜欢使用 github,gitcafe,oschina,coding 等等使用 GIT 版本控制系统的代码托管网站,如果采用这里提供的直链下载的话,你的项目名字也必须和主题名字一样,比如我之前的项目名字叫 yusi-theme,结果自动更新之后主题文件夹多了一个名字叫 yusi-theme 的主题文件夹。好吧,明白怎么回事了吧!

后语

使用这种自动更新功能可以给主题使用者更新鲜的体验以及免去了多次下载的麻烦,自动检查更新的功能最好弄一个开关,让用户选择是否启用,因为主题更新会全部覆盖主题文件,如果使用者在主题里面自己添加了什么代码,最后都可能被覆盖掉,所以,主题开发者尽量给主题多留一些框框,尽量让使用者在主题设置里面设置,而不是直接在主题里面。

源代码

<?php
/**
 * Theme Update Checker Library 1.2
 * http://w-shadow.com/
 *
 * Copyright 2012 Janis Elsts
 * Licensed under the GNU GPL license.
 * http://www.gnu.org/licenses/gpl.html
 */
 
if ( !class_exists('ThemeUpdateChecker') ):
 
class ThemeUpdateChecker {
	public $theme = 'Git-master';              //待检查更新的主题名
	public $metadataUrl = 'https://coding.net/u/googlo/p/File/git/raw/master/info.json';  //元数据文件的路径
	public $enableAutomaticChecking = true; //是否启用自动更新
	protected $optionName = '';      //更新时候的文字信息
	protected $automaticCheckDone = false;
	protected static $filterPrefix = 'tuc_request_update_';
 
	public function __construct($theme, $metadataUrl, $enableAutomaticChecking = true){
		$this->metadataUrl = $metadataUrl;
		$this->enableAutomaticChecking = $enableAutomaticChecking;
		$this->theme = $theme;
		$this->optionName = 'external_theme_updates-'.$this->theme;
		$this->installHooks();
	}
 
	public function installHooks(){
 
		if ( $this->enableAutomaticChecking ){
			add_filter('pre_set_site_transient_update_themes', array($this, 'onTransientUpdate'));
		}
 
		//Insert our update info into the update list maintained by WP.
		add_filter('site_transient_update_themes', array($this,'injectUpdate'));
 
		//Delete our update info when WP deletes its own.
		//This usually happens when a theme is installed, removed or upgraded.
		add_action('delete_site_transient_update_themes', array($this, 'deleteStoredData'));
	}
 
	public function requestUpdate($queryArgs = array()){
		//Query args to append to the URL. Themes can add their own by using a filter callback (see addQueryArgFilter()).
		$queryArgs['installed_version'] = $this->getInstalledVersion();
		$queryArgs = apply_filters(self::$filterPrefix.'query_args-'.$this->theme, $queryArgs);
 
		//Various options for the wp_remote_get() call. Themes can filter these, too.
		$options = array(
			'timeout' => 20, //seconds
		);
		$options = apply_filters(self::$filterPrefix.'options-'.$this->theme, $options);
 
		$url = $this->metadataUrl;
		if ( !empty($queryArgs) ){
			$url = add_query_arg($queryArgs, $url);
		}
 
		//Send the request.
		$result = wp_remote_get($url, $options);
 
		//Try to parse the response
		$themeUpdate = null;
		$code = wp_remote_retrieve_response_code($result);
		$body = wp_remote_retrieve_body($result);
		if ( ($code == 200) && !empty($body) ){
			$themeUpdate = ThemeUpdate::fromJson($body);
			//The update should be newer than the currently installed version.
			if ( ($themeUpdate != null) && version_compare($themeUpdate->version, $this->getInstalledVersion(), '<=') ){
				$themeUpdate = null;
			}
		}
 
		$themeUpdate = apply_filters(self::$filterPrefix.'result-'.$this->theme, $themeUpdate, $result);
		return $themeUpdate;
	}
 
	public function getInstalledVersion(){
		if ( function_exists('wp_get_theme') ) {
			$theme = wp_get_theme($this->theme);
			return $theme->get('Version');
		}
 
		foreach(get_themes() as $theme){
			if ( $theme['Stylesheet'] === $this->theme ){
				return $theme['Version'];
			}
		}
		return '';
	}
 
	public function checkForUpdates(){
		$state = get_option($this->optionName);
		if ( empty($state) ){
			$state = new StdClass;
			$state->lastCheck = 0;
			$state->checkedVersion = '';
			$state->update = null;
		}
 
		$state->lastCheck = time();
		$state->checkedVersion = $this->getInstalledVersion();
		update_option($this->optionName, $state); //Save before checking in case something goes wrong
 
		$state->update = $this->requestUpdate();
		update_option($this->optionName, $state);
	}
 
	public function onTransientUpdate($value){
		if ( !$this->automaticCheckDone ){
			$this->checkForUpdates();
			$this->automaticCheckDone = true;
		}
		return $value;
	}
 
	public function injectUpdate($updates){
		$state = get_option($this->optionName);
 
		//Is there an update to insert?
		if ( !empty($state) && isset($state->update) && !empty($state->update) ){
			$updates->response[$this->theme] = $state->update->toWpFormat();
		}
 
		return $updates;
	}
 
	public function deleteStoredData(){
		delete_option($this->optionName);
	}
 
	public function addQueryArgFilter($callback){
		add_filter(self::$filterPrefix.'query_args-'.$this->theme, $callback);
	}
 
	public function addHttpRequestArgFilter($callback){
		add_filter(self::$filterPrefix.'options-'.$this->theme, $callback);
	}
 
	public function addResultFilter($callback){
		add_filter(self::$filterPrefix.'result-'.$this->theme, $callback, 10, 2);
	}
}
 
endif;
 
if ( !class_exists('ThemeUpdate') ):
 
class ThemeUpdate {
	public $version;      //Version number.
	public $details_url;  //The URL where the user can learn more about this version.
	public $download_url; //The download URL for this version of the theme. Optional.
 
	public static function fromJson($json){
		$apiResponse = json_decode($json);
		if ( empty($apiResponse) || !is_object($apiResponse) ){
			return null;
		}
 
		//Very, very basic validation.
		$valid = isset($apiResponse->version) && !empty($apiResponse->version) && isset($apiResponse->details_url) && !empty($apiResponse->details_url);
		if ( !$valid ){
			return null;
		}
 
		$update = new self();
		foreach(get_object_vars($apiResponse) as $key => $value){
			$update->$key = $value;
		}
 
		return $update;
	}
 
	public function toWpFormat(){
		$update = array(
			'new_version' => $this->version,
			'url' => $this->details_url,
		);
 
		if ( !empty($this->download_url) ){
			$update['package'] = $this->download_url;
		}
 
		return $update;
	}
}
 
endif;