Skip to content

Commit 85d080a

Browse files
committed
addition of new auto-update solution code-- needs testing
1 parent bfea9db commit 85d080a

File tree

3 files changed

+315
-1
lines changed

3 files changed

+315
-1
lines changed

functions.php

+10
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,14 @@ function self_deprecating_sidebar_registration(){
192192
}
193193

194194
add_action( 'wp_loaded', 'self_deprecating_sidebar_registration' );
195+
196+
// THEME UPDATES
197+
198+
//Initialize the update checker.
199+
require 'theme-updates/theme-update-checker.php';
200+
$example_update_checker = new ThemeUpdateChecker(
201+
'oitflex',
202+
'http://resources.wordpress.ncsu.edu/oitflex/info.json'
203+
);
204+
195205
?>

style.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Github Theme URI: https://github.com/ncsuwebdev/WordPress-theme--OITFlex
55
Author: OIT Design, Jen Riehle
66
Description: A child of the Twenty-Eleven theme with a flexible layout, offering style and image options specific to NC State University and falling within the university brand book guidelines. Created by OIT Design.
77
Template: twentyeleven
8-
Version: v0.92
8+
Version: 0.93
99
License: GNU General Public License
1010
License URI: license.txt
1111
Tags: ncsu, dark, light, white, black, gray, one-column, two-columns, left-sidebar, right-sidebar, fixed-width, custom-background, custom-colors, custom-header, custom-menu, editor-style, featured-image-header, featured-images, full-width-template, microformats, post-formats, rtl-language-support, sticky-post, theme-options, translation-ready
+304
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
<?php
2+
/**
3+
* Theme Update Checker Library 1.0
4+
* http://w-shadow.com/
5+
*
6+
* Copyright 2011 Janis Elsts
7+
* Licensed under the GNU GPL license.
8+
* http://www.gnu.org/licenses/gpl.html
9+
*/
10+
11+
if ( !class_exists('ThemeUpdateChecker') ):
12+
13+
/**
14+
* A custom theme update checker.
15+
*
16+
* @author Janis Elsts
17+
* @copyright 2011
18+
* @version 1.0
19+
* @access public
20+
*/
21+
class ThemeUpdateChecker {
22+
public $theme = 'oitflex'; //The theme associated with this update checker instance.
23+
public $metadataUrl = 'http://resources.wordpress.ncsu.edu/oitflex/info.json'; //The URL of the theme's metadata file.
24+
public $enableAutomaticChecking = true; //Enable/disable automatic update checks.
25+
26+
27+
protected $optionName = ''; //Where to store update info.
28+
protected $automaticCheckDone = false;
29+
protected static $filterPrefix = 'tuc_request_update_';
30+
31+
/**
32+
* Class constructor.
33+
*
34+
* @param string $theme Theme slug, e.g. 'twentyten'.
35+
* @param string $metadataUrl The URL of the theme metadata file.
36+
* @param boolean $enableAutomaticChecking Enable/disable automatic udpate checking. If set to FALSE, you'll need to expclicitly call checkForUpdates() to, err, check for updates.
37+
* @return void
38+
*/
39+
public function __construct($theme, $metadataUrl, $enableAutomaticChecking = true){
40+
$this->metadataUrl = $metadataUrl;
41+
$this->enableAutomaticChecking = $enableAutomaticChecking;
42+
$this->theme = $theme;
43+
$this->optionName = 'external_theme_updates-'.$this->theme;
44+
45+
$this->installHooks();
46+
}
47+
48+
/**
49+
* Install the hooks required to run periodic update checks and inject update info
50+
* into WP data structures.
51+
*
52+
* @return void
53+
*/
54+
public function installHooks(){
55+
//Check for updates when WordPress does. We can detect when that happens by tracking
56+
//updates to the "update_themes" transient, which only happen in wp_update_themes().
57+
if ( $this->enableAutomaticChecking ){
58+
add_filter('pre_set_site_transient_update_themes', array($this, 'onTransientUpdate'));
59+
}
60+
61+
//Insert our update info into the update list maintained by WP.
62+
add_filter('site_transient_update_themes', array($this,'injectUpdate'));
63+
64+
//Delete our update info when WP deletes its own.
65+
//This usually happens when a theme is installed, removed or upgraded.
66+
add_action('delete_site_transient_update_themes', array($this, 'deleteStoredData'));
67+
}
68+
69+
/**
70+
* Retrieve update info from the configured metadata URL.
71+
*
72+
* Returns either an instance of ThemeUpdate, or NULL if there is
73+
* no newer version available or if there's an error.
74+
*
75+
* @uses wp_remote_get()
76+
*
77+
* @param array $queryArgs Additional query arguments to append to the request. Optional.
78+
* @return ThemeUpdate
79+
*/
80+
public function requestUpdate($queryArgs = array()){
81+
//Query args to append to the URL. Themes can add their own by using a filter callback (see addQueryArgFilter()).
82+
$queryArgs['installed_version'] = $this->getInstalledVersion();
83+
$queryArgs = apply_filters(self::$filterPrefix.'query_args-'.$this->theme, $queryArgs);
84+
85+
//Various options for the wp_remote_get() call. Themes can filter these, too.
86+
$options = array(
87+
'timeout' => 10, //seconds
88+
'headers' => array(
89+
'Accept' => 'application/json'
90+
),
91+
);
92+
$options = apply_filters(self::$filterPrefix.'options-'.$this->theme, array());
93+
94+
$url = $this->metadataUrl;
95+
if ( !empty($queryArgs) ){
96+
$url = add_query_arg($queryArgs, $url);
97+
}
98+
99+
//Send the request.
100+
$result = wp_remote_get($url, $options);
101+
102+
//Try to parse the response
103+
$themeUpdate = null;
104+
$code = wp_remote_retrieve_response_code($result);
105+
$body = wp_remote_retrieve_body($result);
106+
if ( ($code == 200) && !empty($body) ){
107+
$themeUpdate = ThemeUpdate::fromJson($body);
108+
//The update should be newer than the currently installed version.
109+
if ( ($themeUpdate != null) && version_compare($themeUpdate->version, $this->getInstalledVersion(), '<=') ){
110+
$themeUpdate = null;
111+
}
112+
}
113+
114+
$themeUpdate = apply_filters(self::$filterPrefix.'result-'.$this->theme, $themeUpdate, $result);
115+
return $themeUpdate;
116+
}
117+
118+
/**
119+
* Get the currently installed version of our theme.
120+
*
121+
* @return string Version number.
122+
*/
123+
public function getInstalledVersion(){
124+
foreach(get_themes() as $theme){
125+
if ( $theme['Stylesheet'] === $this->theme ){
126+
return $theme['Version'];
127+
}
128+
}
129+
return '';
130+
}
131+
132+
/**
133+
* Check for theme updates.
134+
*
135+
* @return void
136+
*/
137+
public function checkForUpdates(){
138+
$state = get_option($this->optionName);
139+
if ( empty($state) ){
140+
$state = new StdClass;
141+
$state->lastCheck = 0;
142+
$state->checkedVersion = '';
143+
$state->update = null;
144+
}
145+
146+
$state->lastCheck = time();
147+
$state->checkedVersion = $this->getInstalledVersion();
148+
update_option($this->optionName, $state); //Save before checking in case something goes wrong
149+
150+
$state->update = $this->requestUpdate();
151+
update_option($this->optionName, $state);
152+
}
153+
154+
/**
155+
* Run the automatic update check, but no more than once per pageload.
156+
* This is a callback for WP hooks. Do not call it directly.
157+
*
158+
* @param mixed $value
159+
* @return mixed
160+
*/
161+
public function onTransientUpdate($value){
162+
if ( !$this->automaticCheckDone ){
163+
$this->checkForUpdates();
164+
$this->automaticCheckDone = true;
165+
}
166+
return $value;
167+
}
168+
169+
/**
170+
* Insert the latest update (if any) into the update list maintained by WP.
171+
*
172+
* @param array $updates Update list.
173+
* @return array Modified update list.
174+
*/
175+
public function injectUpdate($updates){
176+
$state = get_option($this->optionName);
177+
178+
//Is there an update to insert?
179+
if ( !empty($state) && isset($state->update) && !empty($state->update) ){
180+
$updates->response[$this->theme] = $state->update->toWpFormat();
181+
}
182+
183+
return $updates;
184+
}
185+
186+
/**
187+
* Delete any stored book-keeping data.
188+
*
189+
* @return void
190+
*/
191+
public function deleteStoredData(){
192+
delete_option($this->optionName);
193+
}
194+
195+
/**
196+
* Register a callback for filtering query arguments.
197+
*
198+
* The callback function should take one argument - an associative array of query arguments.
199+
* It should return a modified array of query arguments.
200+
*
201+
* @param callback $callback
202+
* @return void
203+
*/
204+
public function addQueryArgFilter($callback){
205+
add_filter(self::$filterPrefix.'query_args-'.$this->themeName, $callback);
206+
}
207+
208+
/**
209+
* Register a callback for filtering arguments passed to wp_remote_get().
210+
*
211+
* The callback function should take one argument - an associative array of arguments -
212+
* and return a modified array or arguments. See the WP documentation on wp_remote_get()
213+
* for details on what arguments are available and how they work.
214+
*
215+
* @param callback $callback
216+
* @return void
217+
*/
218+
public function addHttpRequestArgFilter($callback){
219+
add_filter(self::$filterPrefix.'options-'.$this->themeName, $callback);
220+
}
221+
222+
/**
223+
* Register a callback for filtering the theme info retrieved from the external API.
224+
*
225+
* The callback function should take two arguments. If a theme update was retrieved
226+
* successfully, the first argument passed will be an instance of ThemeUpdate. Otherwise,
227+
* it will be NULL. The second argument will be the corresponding return value of
228+
* wp_remote_get (see WP docs for details).
229+
*
230+
* The callback function should return a new or modified instance of ThemeUpdate or NULL.
231+
*
232+
* @param callback $callback
233+
* @return void
234+
*/
235+
public function addResultFilter($callback){
236+
add_filter(self::$filterPrefix.'result-'.$this->themeName, $callback, 10, 2);
237+
}
238+
}
239+
240+
endif;
241+
242+
if ( !class_exists('ThemeUpdate') ):
243+
244+
/**
245+
* A simple container class for holding information about an available update.
246+
*
247+
* @author Janis Elsts
248+
* @copyright 2011
249+
* @version 1.0
250+
* @access public
251+
*/
252+
class ThemeUpdate {
253+
public $version; //Version number.
254+
public $details_url; //The URL where the user can learn more about this version.
255+
public $download_url; //The download URL for this version of the theme. Optional.
256+
257+
/**
258+
* Create a new instance of ThemeUpdate from its JSON-encoded representation.
259+
*
260+
* @param string $json Valid JSON string representing a theme information object.
261+
* @return ThemeUpdate New instance of ThemeUpdate, or NULL on error.
262+
*/
263+
public static function fromJson($json){
264+
$apiResponse = json_decode($json);
265+
if ( empty($apiResponse) || !is_object($apiResponse) ){
266+
return null;
267+
}
268+
269+
//Very, very basic validation.
270+
$valid = isset($apiResponse->version) && !empty($apiResponse->version) && isset($apiResponse->details_url) && !empty($apiResponse->details_url);
271+
if ( !$valid ){
272+
return null;
273+
}
274+
275+
$update = new self();
276+
foreach(get_object_vars($apiResponse) as $key => $value){
277+
$update->$key = $value;
278+
}
279+
280+
return $update;
281+
}
282+
283+
/**
284+
* Transform the update into the format expected by the WordPress core.
285+
*
286+
* @return array
287+
*/
288+
public function toWpFormat(){
289+
$update = array(
290+
'new_version' => $this->version,
291+
'url' => $this->details_url,
292+
);
293+
294+
if ( !empty($this->download_url) ){
295+
$update['package'] = $this->download_url;
296+
}
297+
298+
return $update;
299+
}
300+
}
301+
302+
endif;
303+
304+
?>

0 commit comments

Comments
 (0)