myfile=$my_file;
$this->myfolder=basename(dirname(__FILE__));
$this->mybasename=plugin_basename(__FILE__);
$this->debug_log = array();
$this->defaults = array(
'updater_module' => 'updater_plugin',
'enable_plugin_checks' => true,
'enable_wordpress_checks' => true,
'anonymize' => false,
'plugin_check_interval' => 43200,
'wordpress_check_interval' => 43200,
'global_notices' => $this->is_wp25(),
'new_file_permissions' => 0755,
'debug' => false,
'magic_key' => '',
'confirm_remote_installs' => true,
'show_miniguide' => false,
'oneclick_deactivated_notice' => false,
'mark_plugins_with_notifications' => true,
'hide_notifications_for_inactive' => false,
'hide_update_count_blurb' => false,
);
$this->options = get_option($this->options_name);
if(!is_array($this->options)){
$this->options = $this->defaults;
} else {
$this->options = array_merge($this->defaults, $this->options);
}
$this->debug = $this->options['debug'];
$this->update_enabled=get_option('update_enabled_plugins');
if (!is_object($this->update_enabled)){
$this->update_enabled = new stdClass();
$this->update_enabled->status = array();
$this->update_enabled->last_checked = 0;
update_option('update_enabled_plugins', $this->update_enabled);
}
add_action('activate_'.$this->myfile, array(&$this,'activation'));
add_action('admin_head', array(&$this,'admin_head'));
add_action('admin_menu', array(&$this,'admin_scripts')); //this seems to be the right hook for that
add_action('admin_print_scripts', array(&$this,'admin_print_scripts'));
add_action('admin_footer', array(&$this,'admin_foot'));
add_action('admin_menu', array(&$this, 'add_admin_menus'));
add_filter('ozh_adminmenu_icon', array(&$this, 'ozh_adminmenu_icon'));
add_filter('ozh_adminmenu_icon_install_theme', array(&$this, 'ozh_adminmenu_icon'));
add_filter('ozh_adminmenu_icon_install_plugin', array(&$this, 'ozh_adminmenu_icon'));
add_filter('ozh_adminmenu_icon_plugin_upgrade_options', array(&$this, 'ozh_adminmenu_icon'));
//This is used for marking plugins with enabled update notifications
add_action('load-plugins.php', array(&$this,'check_update_notifications'));
if ( ! $this->options['enable_wordpress_checks'])
remove_action('init', 'wp_version_check');
//Need to use a custom function if the interval is different.
if ( $this->options['enable_wordpress_checks'] &&
($this->options['wordpress_check_interval'] != 43200)
){
remove_action('init', 'wp_version_check');
add_action('init', array(&$this,'check_wordpress_version'));
}
if ($this->options['enable_plugin_checks']){
//See if the custom update checker should be used.
if ($this->options['global_notices']) {
//If global notices are enabled, check for updates on every page
add_action('admin_head', array(&$this,'check_plugin_updates'));
} elseif( $this->options['anonymize'] || ($this->options['plugin_check_interval'] != 43200) ){
//Otherwise, check at the usual time
add_action('load-plugins.php', array(&$this,'check_plugin_updates'));
}
}
//Hooks that only work in WP 2.5 and above
if ( $this->is_wp25() ) {
add_action('admin_init', array(&$this, 'admin_init'));
add_action('admin_notices', array(&$this, 'permanent_notices') );
if ($this->options['global_notices']) {
add_action( 'admin_notices', array(&$this, 'global_plugin_notices') );
}
} else {
//better than nothing
add_action('admin_head', array(&$this, 'admin_init'));
}
/*
if ($this->options['oneclick_deactivated_notice']){
add_action( 'admin_notices', array(&$this, 'oneclick_notice') );
}
*/
//*/
//AJAX handling
add_action('wp_ajax_hide_permanent_notice', array(&$this, 'ajax_hide_permanent_notice') );
}
/**
* dprint ($str, $level)
*
* Debugging output function. Also saves everything to a temporary internal log.
* $str - whatever you want to output. A and a newline are appended automatically.
* $level - priority. 0 - debug, 1 - information, 2 - warning, 3 - error.
*/
function dprint($str, $level=0) {
if ($this->debug) echo $str." \n" ;
$this->debug_log[] = array($str, $level);
}
function activation(){
//set default options
if(!is_array($this->options)){
$this->options = array();
};
$this->options = array_merge($this->defaults, $this->options);
if (empty($this->options['magic_key'])){
//generate a new "magic" key for installing plugins from the FF extension
$this->options['magic_key'] = md5(microtime());
}
update_option($this->options_name, $this->options);
$this->handleOneClick(); //Do something if OneClick is installed
}
function ajax_hide_permanent_notice(){
if (empty($_POST['key'])) return '';
$notices = get_option ('permanent_admin_notices');
unset($notices[$_POST['key']]);
update_option('permanent_admin_notices', $notices);
}
function add_permanent_notice($text, $key){
$notices = get_option ('permanent_admin_notices');
$notices['key'] = $text;
update_option('permanent_admin_notices', $notices);
}
/**
* Special processing if OneClick is installed/active
*/
function handleOneClick(){
if (!function_exists('get_plugins')) return false;
$plugins = get_plugins();
$active = get_option( 'active_plugins' );
$oneclick = 'oneclick/oneclick.php';
//Check if OneClick is installed
if (isset($plugins[$oneclick])){
//Enable the miniguide menu
$this->options['show_miniguide'] = true;
//Check if OneClick is active
if (in_array($oneclick, $active)){
//Deactivate OneClick
$this->deActivatePlugin($oneclick);
}
update_option($this->options_name, $this->options);
}
}
function format_debug_log($min_level=0){
$log = '';
$classes = array(0=>'ws_debug', 1=>'ws_notice', 2=>'ws_warning', 3=>'ws_error');
foreach ($this->debug_log as $entry){
if ($entry[1]<$min_level) continue;
$log .= "
$entry[0]
\n";
}
return $log;
}
function is_wp25(){
return file_exists( ABSPATH . 'wp-admin/update.php' );
}
function admin_init(){
//Hackety-hack! Unfortunately I can't do this earlier (AFAIK).
//Also, the admin_init hook only exists in WP 2.5
if ($this->options['enable_plugin_checks']){
if ($this->options['updater_module']=='updater_plugin'){
remove_action('after_plugin_row', 'wp_plugin_update_row'); //Muahahaha
if (function_exists('is_ssl')) {
add_action('after_plugin_row', array(&$this, 'plugin_update_row'), 1, 2);
} else {
add_action('after_plugin_row', array(&$this, 'plugin_update_row'));
}
}
if( $this->options['anonymize'] || ($this->options['plugin_check_interval'] != 43200)
|| $this->options['global_notices']
)
{
//Remove the original hook. My own hook was added in the constructor.
remove_action('load-plugins.php', 'wp_update_plugins');
}
} else {
remove_action('after_plugin_row', 'wp_plugin_update_row');
remove_action('load-plugins.php', 'wp_update_plugins');
}
if (!$this->options['enable_wordpress_checks']){
remove_filter( 'update_footer', 'core_update_footer' );
remove_action( 'admin_notices', 'update_nag', 3 );
}
}
function add_admin_menus(){
add_submenu_page('plugins.php', 'Upgrade Settings', 'Upgrade Settings', 'edit_plugins',
'plugin_upgrade_options', array(&$this, 'options_page'));
if (current_user_can('edit_plugins'))
add_filter('plugin_action_links', array(&$this, 'plugin_action_links'), 10, 2);
//only privileged users can install plugins
add_submenu_page('plugins.php', 'Install New', 'Install a Plugin', 'edit_plugins',
'install_plugin', array(&$this, 'installer_page'));
add_submenu_page('themes.php', 'Install New', 'Install a Theme', 'edit_themes',
'install_theme', array(&$this, 'installer_page'));
if ($this->options['show_miniguide']){
add_submenu_page('index.php', 'One Click Plugin Updater Miniguide', 'One Click Updater Miniguide',
'edit_plugins', 'one_click_miniguide', array(&$this, 'miniguide_page'));
}
}
function ozh_adminmenu_icon($hook){
$base = WP_PLUGIN_URL.'/'.$this->myfolder.'/images/';
if ($hook == 'install_theme')
return $base."theme_install.png";
elseif ($hook == 'install_plugin')
return $base."plugin_go.png";
elseif ($hook == 'plugin_upgrade_options')
return $base."plugin_upgrade_options.png";
return $hook;
}
function admin_head(){
echo "myfolder.'/single-click.css';
echo "' type='text/css' />";
if ($this->options['hide_update_count_blurb']){
echo "";
}
}
/**
* ws_oneclick_pup::admin_scripts()
* Enqueues the required JavaScript libraries
*
* @return void
*/
function admin_scripts(){
//The plugin needs JQuery for many of the UI modifications and confirmations
wp_enqueue_script('jquery');
}
/**
* ws_oneclick_pup::admin_print_scripts()
* Outputs any JavaScript that needs to go in the head section
*
* @return void
*/
function admin_print_scripts(){
//The function for hiding notices
?>
mybasename)
$links[] = "" . __('Settings') . "";
return $links;
}
function admin_foot(){
/*
echo '
';
//print_r($this->get_update_plugins());
echo '
';
//*/
if (!empty($_GET['page'])) return; //Don't run on plugin subpages
$do_update_url = get_option('siteurl').'/wp-content/plugins/'.$this->myfolder.'/do_update.php';
//Only execute on the plugin list itself.
if ( (stristr($_SERVER['REQUEST_URI'], 'plugins.php')!==false) ) {
$plugins=get_plugins();
$update = $this->get_update_plugins();
$active = get_option('active_plugins' );
//How many active plugins there are
$count_active = count($active);
//How many updates are available
if (is_array($update->response)){
if ($this->options['hide_notifications_for_inactive']){
//don't count updates for inactive plugins
$count_update = 0;
foreach($update->response as $plugin_file => $data){
if (in_array($plugin_file, $active))
$count_update++;
}
} else {
$count_update = count($update->response);
}
} else $count_update = 0;
$plugin_msg = "$count_active active plugins";
if ($count_update>0){
$s = ($count_update==1)?'':'s';
$plugin_msg .= ", $count_update upgrade$s available";
if (function_exists('activate_plugin')){
$link = $do_update_url.'?action=upgrade_all';
$link = wp_nonce_url($link, 'upgrade_all');
$plugin_msg .= " Upgrade All";
}
}
?>
get_update_plugins();
if ( !isset( $current->response[ $file ] ) ){
return false;
}
//Don't show the notification for inactive plugins (user-configured)
$active = get_option( 'active_plugins' );
if ($this->options['hide_notifications_for_inactive'] && !in_array($file, $active)){
return false;
}
$r = $current->response[ $file ];
//Workaround. Don't show the update notification if the new version is the same as the old one.
if ( function_exists('version_compare') ){
if ( version_compare($plugin_data['Version'], $r->new_version,'=') ){
return false;
}
} else {
if ( $plugin_data['Version'] == $r->new_version ){
return false;
}
}
$autoupdate_url=get_option('siteurl').'/wp-content/plugins/'.$this->myfolder.
'/do_update.php?action=update_plugin&plugin_file='.urlencode($file);
if(!empty($r->package)){
//Download URL is already known, so use that
$autoupdate_url .= '&download_url='.urlencode($r->package);
} else {
//OBSOLETE. do_update.php will use the plugin URL to autodetect the download URL.
$autoupdate_url .='&plugin_url='.urlencode($r->url);
}
//Add nonce verification for security
$autoupdate_url = wp_nonce_url($autoupdate_url, 'update_plugin-'.$file);
echo "
";
if ( !current_user_can('edit_plugins') ) {
printf( __('There is a new version of %1$s available. Download version %3$s here.'),
$plugin_data['Name'], $r->url, $r->new_version);
} else if ( empty($r->package) ) {
printf( __('There is a new version of %1$s available. Download version %3$s hereautomatic upgrade unavailable for this plugin.'),
$plugin_data['Name'], $r->url, $r->new_version);
} else {
printf( __('There is a new version of %1$s available. Download version %3$s here or upgrade automatically.'),
$plugin_data['Name'], $r->url, $r->new_version, $autoupdate_url );
}
echo "
";
}
/**
* Decrease a version number by a small fraction
*/
function version_decrease($version){
//Spaces? Kill anything that comes after a space.
$parts = explode(' ', trim($version));
$ver = $parts[0];
$numeric = array();
//Separate by dots
$parts = explode('.', $ver);
//Perform the arithmetics
$new_parts = array();
$carry = 1;
while(count($parts)>0){
$minor = intval(array_pop($parts));
$minor = $minor - $carry;
if ($minor < 0){
$minor = 10 - $carry;
//$minor =
} else {
$carry = 0;
}
array_unshift($new_parts, $minor);
}
//Add the dots again
$new_ver = implode('.', $new_parts);
return $new_ver;
}
function get_update_plugins(){
$plugins = get_plugins();
if ( function_exists('get_transient') ){
$current = get_transient( 'update_plugins' );
} else {
$current = get_option( 'update_plugins' );
}
$rez = $current;
//Remove missing plugins
if ( !isset($current->response) && is_array($current->response) ){
foreach ( $current->response as $plugin_file => $update_data ) {
if ( empty( $plugins[$plugin_file] ) ){
unset( $rez->response[$plugin_file] );
}
}
}
return $rez;
}
function set_update_plugins( $data ){
if ( function_exists('set_transient') ){
set_transient( 'update_plugins', $data );
}
//Just to be sure, set the option as well.
return update_option( 'update_plugins', $data );
}
function check_update_notifications(){
global $wp_version;
@set_time_limit(300);
if ( !function_exists('fsockopen') )
return false;
//Hack : make the plugin compatible with an unknown comment-related plugin
if ( !function_exists('get_plugins') )
return false;
$plugins = get_plugins();
$orig_plugins = $plugins;
$active = get_option( 'active_plugins' );
$current = $this->get_update_plugins();
$plugin_changed = empty($current);
foreach ( $plugins as $file => $p ) {
$plugins[$file]['Version']='0.0'; //fake zero version
//Use update info provided by WP, if available
if ( isset( $current->status->response[$file] ) ){
$this->update_enabled->status[$file] = true;
//New plugin?
} else if( !isset($this->update_enabled->status[$file]) ) {
$this->update_enabled->status[$file] = false; //Yep, assume no notifications
$plugin_changed = true;
}
}
//Remove information about deleted plugins
$remaining = $this->update_enabled->status;
foreach($remaining as $file => $status){
if (!isset($plugins[$file])){
unset($this->update_enabled->status[$file]);
/* Maybe comment this out not to waste time checking for updates every time a plugin is deleted. */
$plugin_changed = true;
}
}
update_option( 'update_enabled_plugins', $this->update_enabled);
//$plugin_changed=true; //debug - force status update
if (
isset( $this->update_enabled->last_checked ) &&
( ( time() - $this->update_enabled->last_checked ) < $this->options['plugin_check_interval']) &&
!$plugin_changed
)
return false;
$this->update_enabled->last_checked=time();
$to_send->plugins = $plugins;
$to_send->active = array();
$send = serialize( $to_send );
$request = 'plugins=' . urlencode( $send );
$http_request = "POST /plugins/update-check/1.0/ HTTP/1.0\r\n";
$http_request .= "Host: api.wordpress.org\r\n";
$http_request .= "Content-Type: application/x-www-form-urlencoded; charset=" . get_option('blog_charset') . "\r\n";
$http_request .= "Content-Length: " . strlen($request) . "\r\n";
$http_request .= 'User-Agent: WordPress/2.8; http://example.com/' . "\r\n";
$http_request .= "\r\n";
$http_request .= $request;
$response = '';
if( false != ( $fs = @fsockopen( 'api.wordpress.org', 80, $errno, $errstr, 3) ) && is_resource($fs) ) {
fwrite($fs, $http_request);
while ( !feof($fs) )
$response .= fgets($fs, 1160); // One TCP-IP packet
fclose($fs);
$response = explode("\r\n\r\n", $response, 2);
}
$response = unserialize( $response[1] );
if ( $response ) {
foreach($response as $file => $data) {
$this->update_enabled->status[$file]=true;
}
}
update_option( 'update_enabled_plugins', $this->update_enabled);
return true;
}
/**
* check_plugin_updates - check if there are new version of installed plugins.
*
* This is almost exactly like wp_update_plugins, but can give out less information about
* the blog.
*/
function check_plugin_updates() {
global $wp_version;
if ( !function_exists('fsockopen') )
return false;
//Hack : make compatible with an unknown comment-related plugin.
if ( !function_exists('get_plugins') )
return false;
$plugins = get_plugins();
$active = get_option( 'active_plugins' );
$current = $this->get_update_plugins();
$new_option = new stdClass;
$new_option->last_checked = time();
$new_option->checked = array();
$plugin_changed = false;
//Check for plugins that have a different ver. number than the last time.
foreach ( $plugins as $file => $p ) {
$new_option->checked[ $file ] = $p['Version'];
if ( !isset( $current->checked[ $file ] ) || strval($current->checked[ $file ]) !== strval($p['Version']) )
$plugin_changed = true;
}
//Check for deleted plugins
if ( isset ( $current->response ) && is_array( $current->response ) ) {
foreach ( $current->response as $plugin_file => $update_details ) {
if ( ! isset($plugins[ $plugin_file ]) ) {
$plugin_changed = true;
break;
}
}
}
//$plugin_changed = true; //debug
if (
isset( $current->last_checked ) &&
$this->options['plugin_check_interval'] > ( time() - $current->last_checked ) &&
!$plugin_changed
)
return false;
$to_send->plugins = $plugins;
$to_send->active = $active;
$send = serialize( $to_send );
$request = 'plugins=' . urlencode( $send );
$http_request = "POST /plugins/update-check/1.0/ HTTP/1.0\r\n";
$http_request .= "Host: api.wordpress.org\r\n";
if ($this->options['anonymize']) {
$http_request .= 'User-Agent: WordPress/2.8; http://example.com/' . "\r\n";
} else {
$http_request .= 'User-Agent: WordPress/' . $wp_version . '; ' . get_bloginfo('url') . "\r\n";
}
$http_request .= "Content-Type: application/x-www-form-urlencoded; charset=" . get_option('blog_charset') . "\r\n";
$http_request .= "Content-Length: " . strlen($request) . "\r\n";
$http_request .= "\r\n";
$http_request .= $request;
$response = '';
if( false != ( $fs = @fsockopen( 'api.wordpress.org', 80, $errno, $errstr, 3) ) && is_resource($fs) ) {
fwrite($fs, $http_request);
while ( !feof($fs) )
$response .= fgets($fs, 1160); // One TCP-IP packet
fclose($fs);
$response = explode("\r\n\r\n", $response, 2);
}
$response = unserialize( $response[1] );
if ( $response ) {
$new_option->response = $response;
//Plugins that have updates, also have notifications enabled (obviously)
if (is_array($response)){
foreach($response as $file => $data) {
$this->update_enabled->status[$file]=true;
}
update_option( 'update_enabled_plugins', $this->update_enabled);
}
} else {
$new_option->response = array();
}
$this->set_update_plugins ( $new_option );
}
/**
* Just like wp_version_check, but with a configurable interval.
*/
function check_wordpress_version(){
if ( !function_exists('fsockopen') || strpos($_SERVER['PHP_SELF'], 'install.php') !== false || defined('WP_INSTALLING') )
return;
global $wp_version;
$php_version = phpversion();
$current = get_option( 'update_core' );
$locale = get_locale();
if (
isset( $current->last_checked ) &&
$this->options['wordpress_check_interval'] > ( time() - $current->last_checked ) &&
$current->version_checked == $wp_version
)
return false;
$new_option = '';
$new_option->last_checked = time(); // this gets set whether we get a response or not, so if something is down or misconfigured it won't delay the page load for more than 3 seconds, twice a day
$new_option->version_checked = $wp_version;
$http_request = "GET /core/version-check/1.1/?version=$wp_version&php=$php_version&locale=$locale HTTP/1.0\r\n";
$http_request .= "Host: api.wordpress.org\r\n";
$http_request .= 'Content-Type: application/x-www-form-urlencoded; charset=' . get_option('blog_charset') . "\r\n";
$http_request .= 'User-Agent: WordPress/' . $wp_version . '; ' . get_bloginfo('url') . "\r\n";
$http_request .= "\r\n";
$response = '';
if ( false !== ( $fs = @fsockopen( 'api.wordpress.org', 80, $errno, $errstr, 3 ) ) && is_resource($fs) ) {
fwrite( $fs, $http_request );
while ( !feof( $fs ) )
$response .= fgets( $fs, 1160 ); // One TCP-IP packet
fclose( $fs );
$response = explode("\r\n\r\n", $response, 2);
if ( !preg_match( '|HTTP/.*? 200|', $response[0] ) )
return false;
$body = trim( $response[1] );
$body = str_replace(array("\r\n", "\r"), "\n", $body);
$returns = explode("\n", $body);
$new_option->response = attribute_escape( $returns[0] );
if ( isset( $returns[1] ) )
$new_option->url = clean_url( $returns[1] );
if ( isset( $returns[2] ) )
$new_option->current = attribute_escape( $returns[2] );
}
update_option( 'update_core', $new_option );
}
/**
* ws_oneclick_pup::download_page()
* Download and return the page/file from the provided URL. Tries three different techniques - cURL,
* Snoopy and fopen (in that order).
*
* @param string $url
* @param integer $timeout
* @return string or FALSE if the download fails.
*/
function download_page($url, $timeout=120){
$this->dprint("Downloading '$url'...", 1);
$parts=parse_url($url);
if(!$parts) return false;
if(!isset($parts['scheme'])) $url='http://'.$url;
$response = false;
if (function_exists('curl_init')) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
/* Currently redirection support is not absolutely necessary, so it's OK
if this line fails due to safemode restrictions */
@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
$response = curl_exec($ch);
curl_close($ch);
} else if (file_exists(ABSPATH . 'wp-includes/class-snoopy.php')){
require_once( ABSPATH . 'wp-includes/class-snoopy.php' );
$snoopy = new Snoopy();
$snoopy->fetch($url);
if( $snoopy->status == '200' ){
$response = $snoopy->results;
}
} else if (ini_get('allow_url_fopen') && (($rh = fopen($url, 'rb')) !== FALSE)) {
$response='';
while (!feof($rh)) {
$response.=fread($rh, 1024);
}
fclose($rh);
} else {
return false;
}
return $response;
}
function deActivatePlugin($plugin) {
if(!current_user_can('edit_plugins')) {
//echo 'Oops, sorry, you are not authorized to deactivate plugins!';
return false;
}
$current = get_option('active_plugins');
array_splice($current, array_search($plugin, $current), 1 ); // Array-fu!
do_action('deactivate_' . trim( $plugin ));
update_option('active_plugins', $current);
return true;
}
/**
* recursive_mkdir - recursively create a directory
*
* Note: The function expects $path to be an absolute path.
*/
function recursive_mkdir($path, $mode = 0755) {
//If the directory is inside the WP path we don't need to validate the whole tree.
//This also lets circumvent an open_basedir restriction problem with is_dir().
$okABSPATH = preg_replace('/[\\/]/', DIRECTORY_SEPARATOR, ABSPATH);
$path = preg_replace('/[\\/]/', DIRECTORY_SEPARATOR, $path);
if (strpos($path, $okABSPATH) === 0){
$tmppath = $okABSPATH;
$path = substr($path, strlen($tmppath));
//Remove the trailing slash from $tmppath (if present)
if (substr($tmppath, -1) == DIRECTORY_SEPARATOR) {
$tmppath = substr($tmppath, 0, strlen($tmppath)-1);
}
} else {
$tmppath = '';
}
$dirs = explode(DIRECTORY_SEPARATOR, $path);
foreach ($dirs as $directory){
if(empty($directory)) continue;
$tmppath = $tmppath . DIRECTORY_SEPARATOR . $directory;
if ( ! is_dir($tmppath) ){
$this->dprint("Creating directory '$tmppath'", 1);
if ( !mkdir($tmppath, $mode) ){
$this->dprint("Can't create directory '$tmppath'!", 3);
return new WP_Error('fs_mkdir', "Can't create directory '$tmppath'.");
}
}
}
return true;
}
/**
* extractFile() - extract the plugin or theme to the right folder
*
* zipfile - filename of the ZIP archive to extract.
* type - what does the archive contain? 'plugin', 'theme', 'autodetect' or 'none'.
* target - the destination directory to unzip to. Optional if $type is given.
* use_pclzip - whether to use PclZip (default : true).
*/
function extractFile($zipfile, $type='autodetect', $target = '', $use_pclzip=true){
$this->dprint("Extracting files from $zipfile...");
$magic_descriptor = array('type' => $type);
//Do some early autodetection
if(empty($target)){
if ($type == 'plugin'){
$target = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR;
} else if ($type == 'theme'){
if (function_exists('get_theme_root')){
$target = get_theme_root() . DIRECTORY_SEPARATOR;
} else {
$target = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR;
}
}
}
$this->dprint("So far, the type is set to '$type'.");
if (!$use_pclzip){
/* last-ditch attempt if PclZip didn't work - use the linux "unzip" executable */
if (empty($target)){
$this->dprint("Target directory not specified and can't autodetect in this mode!", 3);
return new WP_Error('ad_unsupported', "Can't use autodetection without PclZip support.");
}
if (function_exists('exec')) {
$this->dprint("Running the unzip command.", 1);
exec("unzip -uovd $target $zipfile", $ignored, $return_val);
$rez = $return_val == 0;
//Show the unzip output for debugging purposes
$this->dprint("unzip returned value '$return_val'. unzip log : ");
$unzip_log = explode("\n",$ignored);
if (is_array($unzip_log)){
foreach ($unzip_log as $log_line){
$this->dprint("\t$log_line");
}
}
if (!$rez){
return new WP_Error('zip_unzip_error', "exec('unzip') failed miserably.");
} else {
return $magic_descriptor;
}
}
return new WP_Error('zip_noexec', "Can't run unzip.");
}
if (!class_exists('PclZip'))
{
$this->dprint('Need to load PclZip.');
require_once ('pclzip.lib.php');
}
$archive = new PclZip($zipfile);
if (function_exists('gzopen')) {
$this->dprint("gzopen() found, will use PclZip.");
// Try to extract all of the file information in-memory. Note : hopefully unlikely to
// overrun memory limits.
if ( false == ($archive_files = $archive->listContent()) ){
// Nope.
$this->dprint("PclZip failed!", 3);
$this->dprint("PclZip says : '".$archive->errorInfo(true)."'", 3);
$error_msg = "PclZip Error : '".$archive->errorInfo(true)."'";
return new WP_Error('zip_unsupported', $error_msg);
} else {
//It worked! Woo-hoo!
$magic_descriptor['file_list'] = $archive_files;
//Let's see, where do we put the files?
if(empty($target)){
//Need to autodetect! Look at some PHP & CSS files for headers.
$this->dprint("Starting autodetection.", 1);
foreach($archive_files as $file_info){
$file_ext = strtolower(substr($file_info['filename'],-4));
if ( $file_info['folder'] ||
( substr_count($file_info['filename'],'/') > 1 ) ||
( ($file_ext != '.php') && ($file_ext != '.css') )
) continue;
$file = $archive->extract(PCLZIP_OPT_BY_NAME, $file_info['filename'],
PCLZIP_OPT_EXTRACT_AS_STRING);
$file = $file[0];
$this->dprint("\tChecking $file[filename]");
$plugin = $this->get_plugin_info($file['content']);
if (!empty($plugin['plugin name'])) {
$this->dprint("\tFound a plugin header! The plugin is : ".$plugin['plugin name'], 1);
$type = 'plugin';
$magic_descriptor['file_header'] = $plugin;
$magic_descriptor['type'] = $type;
$magic_descriptor['plugin_file'] = $file_info['filename'];
$target = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR;
break;
}
$theme = $this->get_theme_info($file['content']);
if (!empty($theme['theme name'])) {
$this->dprint("\tFound a theme header! It is '".$theme['theme name']."'", 1);
$type = 'theme';
$magic_descriptor['file_header'] = $theme;
$magic_descriptor['type'] = $type;
$target = get_theme_root() . DIRECTORY_SEPARATOR;
break;
}
}
if (empty($target)){
$this->dprint("Autodetection failed!", 3);
return new WP_Error("ad_failed", "Autodetection failed - this doesn't look like a plugin or a theme.");
}
}
//Finally, extract the files! Some of the code shamelessly stolen from WP core (file.php).
if (substr($target,-1) != DIRECTORY_SEPARATOR) $target .= DIRECTORY_SEPARATOR;
$to = preg_replace('/[\\/]/', DIRECTORY_SEPARATOR, $target);
$this->dprint("Starting extraction to folder '$to'.", 1);
//Make sure the target directory exists
$rez = $this->recursive_mkdir($to, $this->options['new_file_permissions']);
if (is_wp_error($rez)){
return $rez;
}
foreach ($archive_files as $file) {
$path = dirname($file['filename']);
//Create the file's directory if neccessary
$rez = $this->recursive_mkdir($to . $path, $this->options['new_file_permissions']);
if (is_wp_error($rez)){
return $rez;
}
// We've made sure the folders are there, so let's extract the file now:
if ( ! $file['folder'] ){
$this->dprint("Extracting $file[filename]", 1);
$file_data = $archive->extract(PCLZIP_OPT_BY_NAME, $file['filename'],
PCLZIP_OPT_EXTRACT_AS_STRING);
$file_data=$file_data[0];
//get additional info if we didn't earlier
if (empty($magic_descriptor['file_header'])){
if ('plugin' == $type){
if (strtolower(substr($file['filename'],-4))=='.php'){
$plugin = $this->get_plugin_info($file_data['content']);
if (!empty($plugin['plugin name'])) {
$this->dprint("\tFound a plugin header! The plugin is : ".$plugin['plugin name'], 1);
$magic_descriptor['file_header'] = $plugin;
$magic_descriptor['plugin_file'] = $file['filename'];
}
}
} else if ('theme' == $type){
if (strtolower(substr($file['filename'],-4))=='.css'){
$theme = $this->get_theme_info($file_data['content']);
if (!empty($theme['theme name'])) {
$this->dprint("\tFound a theme header! ".$theme['plugin name'], 1);
$magic_descriptor['file_header'] = $theme;
}
}
}
}
//Put the file where it belongs
if ( isset($file_data['content']) && (strlen($file_data['content'])>0) ) {
//$this->dprint("File $file[filename] = ".strlen($file_data['content']).' bytes', 0);
if ( !file_put_contents( $to . $file['filename'], $file_data['content']) ){
$this->dprint("Can't create file $file[filename] in $to!", 3);
return new WP_Error('fs_put_contents', "Can't create file '$file[filename]' in '$to'");
}
} else {
//special handling for zero-byte files (file_put_contents wouldn't work)
$fh = @fopen($to . $file['filename'], 'wb');
if(!$fh){
$this->dprint("Can't create a zero-byte file $file[filename] in $to!", 3);
return new WP_Error('fs_put_contents',
"Can't create a zero-byte file '$file[filename]' in '$to'");
}
fclose($fh);
}
@chmod($to . $file['filename'], $this->options['new_file_permissions']);
//^ I think this can be allowed to fail.
}
}
//Extraction succeeded! Yay.
$this->dprint("Extraction succeeded.", 1);
return $magic_descriptor;
}
} else {
$this->dprint("gzopen() not available, can't use PclZip.", 2);
return new WP_Error('zip_pclzip_unusable', "PclZip not supported - no gzopen().");
}
$this->dprint("extractFile() : you should never see this message.", 3);
return new WP_Error('impossible', "An impossible error!");
}
function get_plugin_info( $file_contents ) {
$plugin_data = $file_contents;
//Lets do it the simple way!
$names = array('plugin name'=>'', 'plugin uri'=>'', 'description'=>'', 'author uri'=> '',
'author' => 'Unknown', 'version' => '');
if ( preg_match_all('/('.implode('|', array_keys($names)).'):(.*)$/mi',
$plugin_data, $matches, PREG_SET_ORDER) )
{
foreach($matches as $match){
$key = strtolower($match[1]);
if (empty($names[$key]) && ($key != 'author')){
$names[strtolower($match[1])] = trim($match[2]);
}
}
}
$names['name'] = $names['plugin name'];
return $names;
}
function get_theme_info( $file_contents ) {
//Lets do this the simple way, too!
$theme_data = $file_contents;
$names = array('theme name'=>'', 'theme uri'=>'', 'description'=>'', 'author uri'=> '',
'template' => '', 'version' => '', 'status' => 'publish', 'tags' => '', 'author' => 'Anonymous');
if ( preg_match_all('/('.implode('|', array_keys($names)).'):(.*)$/mi',
$theme_data, $matches, PREG_SET_ORDER) )
{
foreach($matches as $match){
$names[strtolower($match[1])] = trim($match[2]);
}
}
$names['name'] = $names['theme name'];
return $names;
}
function is__writable($path)
{
//will work in despite of Windows ACLs bug
//NOTE: use a trailing slash for folders!!!
//see http://bugs.php.net/bug.php?id=27609
//see http://bugs.php.net/bug.php?id=30931
if ($path{strlen($path) - 1} == '/') // recursively return a temporary file path
return $this->is__writable($path . uniqid(mt_rand()) . '.tmp');
else
if (is_dir($path))
return $this->is__writable($path . '/' . uniqid(mt_rand()) . '.tmp');
// check tmp file for read/write capabilities
$rm = file_exists($path);
$f = @fopen($path, 'a');
if ($f === false)
return false;
fclose($f);
if (!$rm)
unlink($path);
return true;
}
function options_page(){
if (!empty($_POST['action']) && ($_POST['action']=='update') ){
$this->options['updater_module'] = $_POST['updater_module'];
$this->options['enable_plugin_checks'] = !empty($_POST['enable_plugin_checks']);
$this->options['enable_wordpress_checks'] = !empty($_POST['enable_wordpress_checks']);
$this->options['anonymize'] = !empty($_POST['anonymize']);
$this->options['mark_plugins_with_notifications'] = !empty($_POST['mark_plugins_with_notifications']);
$this->options['plugin_check_interval'] = intval($_POST['plugin_check_interval']);
$this->options['wordpress_check_interval'] = intval($_POST['wordpress_check_interval']);
$this->options['debug'] = !empty($_POST['debug']);
$this->options['confirm_remote_installs'] = !empty($_POST['confirm_remote_installs']);
$this->options['show_miniguide'] = !empty($_POST['show_miniguide']);
$this->debug = $this->options['debug'];
$this->options['global_notices'] = !empty($_POST['global_notices']);
$this->options['hide_notifications_for_inactive'] = !empty($_POST['hide_notifications_for_inactive']);
$this->options['hide_update_count_blurb'] = !empty($_POST['hide_update_count_blurb']);
if (!empty($_POST['new_file_permissions']))
$this->options['new_file_permissions'] = octdec(intval($_POST['new_file_permissions']));
update_option($this->options_name, $this->options);
echo '
Settings saved.
';
}
?>
Upgrade Settings
Here you can configure plugin update notifications, set how often WordPress checks for new versions,
and so on. This page was created by the
One Click Plugin Updater
plugin.
download_page($url, 300);
if ($contents){
$this->dprint("Downloaded ".strlen($contents)." bytes.", 1);
//First try : save in the plugin's own directory (and don't use tempnam() - buggy sometimes).
$filename=dirname(__FILE__)."/plg".md5(microtime().'|'.rand(0,1000000)).".zip";
$this->dprint("Will save the new version archive (zip) to a temporary file '$filename'.");
$handle = @fopen($filename, "wb");
if(!$handle) {
$this->dprint("Warning: couldn't create a temporary file at '$filename'.", 2);
//Second try : use the default (hopefully) system directory
$filename = tempnam("/tmp", "PLG");
$this->dprint("Using alternate temporary file '$filename'.", 1);
$handle = fopen($filename, "wb");
//That didn't work too, try one last time and don't use tempnam (buggy on some systems).
if(!$handle) {
$this->dprint("Warning: couldn't create a temporary file at '$filename'.", 2);
//Last try : the plugin's directory, but with tempnam()
$filename=tempnam(dirname(__FILE__), "PLG");
$this->dprint("Last attempt : using alternate temporary file '$filename'.", 1);
$handle = fopen($filename, "wb");
}
}
if(!$handle) {
$this->dprint("Error: couldn't create a temporary file '$filename'.", 3);
return new WP_Error('fs_tmp_failed', "Can't create a temporary file '$filename'.");
}
fwrite($handle, $contents);
fclose($handle);
unset($contents);
} else {
$this->dprint("Download failed.", 3);
return new WP_Error('download_failed', "Download failed.");
}
}
if(empty($filename)){
return new WP_Error('fs_no_file', "No file to extract. Weird.");
}
/**
* Extract the file
*/
$this->dprint("About to extract '$filename'.");
$rez = $this->extractFile($filename, $type);
if (is_wp_error($rez)){
if ( ($rez->get_error_code() == 'zip_pclzip_unusable') || ($rez->get_error_code() == 'zip_unsupported') ){
//Maybe we can try exec(unzip)...
if (!empty($type)){
$this->dprint("PclZip unavailable, using unzip.", 2);
$rez = $this->extractFile($filename, $type, '', false);
//Let the error code "fall through" to the end of the function.
} else {
$this->dprint("Error : PclZip error and can't use 'unzip' with autodetection.", 3);
$rez = new WP_Error('zip_error', "Can't unzip the file. PclZip failed and 'unzip'
can't be used with autodetection.");
}
}
}
/**
* Kill the temporary file no matter what
*/
@unlink($filename);
return $rez;
}
function installer_page(){
$type='autodetect';
if (!empty($_POST['installtype'])){
$type = $_POST['installtype'];
} else if (!empty($_GET['installtype'])){
$type = $_GET['installtype'];
} else {
$parts = explode('_', $_GET['page']);
$type = $parts[1];
}
if (!in_array($type, array('autodetect', 'plugin', 'theme')))
$type = 'autodetect';
//Some quick status-checks based on type
if ('autodetect' != $type){
if ($type == 'plugin'){
$target = WP_PLUGIN_DIR . '/';
} else if ($type == 'theme'){
if (function_exists('get_theme_root')){
$target = get_theme_root() . '/';
} else {
$target = WP_CONTENT_DIR . '/themes/';
}
}
if (!$this->is__writable($target)){
echo "
Warning : The folder $target
must be writable by PHP for the $type installer to work. See
Changing File Permissions
for a general guide on how to fix this.
";
if (!empty($url) || !empty($filename)){
//Looks like there's something to do! But lets verify the nonce first.
$arr = array_merge($_GET, $_POST);
$nonce = !empty($arr['_wpnonce'])?$arr['_wpnonce']:'';
$verified = false;
if (empty($nonce)){
//Missing nonce. Can only happen with external URL-based requests, legitimate or not.
//So check for the presence of the "magic" key
$magic = empty($_GET['magic'])?'':$_GET['magic'];
if ($magic != $this->options['magic_key']){
//It's debatable whether this looks good.
echo "
Insecure Request
\n
Looks like you're using the old OneClick FireFox extension, which is no
longer supported by this plugin.
Please install the new One-Click Installer for WP extension,
which is more secure and easier to use.
";
} else {
//Magic key OK. Do I need to ask for confirmation?
if ($this->options['confirm_remote_installs']){
//Let the user choose.
$install_url = trailingslashit(get_option('siteurl')).
'wp-admin/plugins.php?page=install_plugin&fileurl='.urlencode($url).
"&installtype=$type";
$install_url = wp_nonce_url($install_url, 'install_file');
$dontinstall_url = trailingslashit(get_option('siteurl')).
'wp-admin/plugins.php?page=install_plugin';
if (($type == 'plugin') || ($type == 'theme')){
$what = $type;
} else $what = 'plugin or theme';
//It's debatable whether this looks good.
echo "
Are you sure?
\n
Do you really want to install this $what on your blog?
Greetings, sentient. On this page you will find a short overview of the plugin's features and how to
use them. This will be especially useful if you have been using the old OneClick plugin before.
"One Click Plugin Updater" vs "OneClick"
These are two different plugins, created by two different developers and - initially - for different
goals. However, One Click Updater has now officially become OneClick's successor.
It includes all the main features of OneClick, and more.
Installing Plugins and Themes
You can do this by going to Plugins -> Install a Plugin or Design -> Install a Theme,
respectively. Plugins/themes can be installed either from an URL (e.g. "http://something.com/plugin.zip"),
or by uploading a ZIP archive from your computer. You will need to make sure the relevant
directories are writable by WordPress, as at this time the plugin only supports direct filesystem access.
FTP support may be added in a later version.
Firefox Extension
You can also use a Firefox add-on that will allow you to easily install plugins or themes by
right-clicking on a download link and selecting "Install on your blog". The plugin automatically
detects wheter the link represents a plugin or a theme.
Get the extension here.
Older versions of this plugin (up to version 2.1.2) support the
OneClick for WordPress add-on. This
backwards compatibility has been dropped in later versions in favor of the
aforementioned extension, which is more secure
and supports autodetection.
Deleting Plugins and Themes
You can delete an inactive plugin by clicking the appropriate "Delete" link in the Plugins
tab. You can also delete themes in a similar way in the Design -> Themes tab.
The plugin will ask for confirmation when you try to delete something, but if you say "Yes" the thing
you deleted will be gone for good, so use this feature wisely.
Automatic Upgrades
This plugin lets you upgrade other plugins with a single click in WP 2.3 and up. WP 2.5 introduced
it's own built-in plugin updater, but you can still use this plugin for that if the built-in version
doesn't work. If using the plugin, you can also perform all pending updates with a single click ;)
Select which one to use under Plugins -> Upgrade Settings.
Some other interesting stuff you can configure on that page :
Global update notifications (notifies you about available upgrades on all admin pages, not just
Plugins).
Whether to inform WordPress.org about your blog's URL and version when checking
(WP does this by default).
How often should WP check for plugin and core updates (you can even completely turn them off
if you want).
That's all for this short introduction :) If you like, you can hide this page by unchecking the
"Show Miniguide" box under Plugins -> Upgrade Settings.
";
}
}
/**
* Displays a message that plugin updates are available if they are
* Originally by Viper007Bond @ http://www.viper007bond.com/
*/
function global_plugin_notices() {
//Only administrators will see the notices
if (!current_user_can('edit_plugins')) return;
$current = $this->get_update_plugins();
if ( empty( $current->response ) ) return; // No plugin updates available
if (!function_exists('activate_plugin')) return; //Only in WP 2.5
$active = get_option('active_plugins');
//if ( empty($active_plugins) || !is_array($active_plugins) ) return;
$plugins = get_plugins();
$update_list = array();
$header = false;
foreach ( $current->response as $plugin_file => $update_data ) {
//Skip certain notifications
if (
//Plugin must be actually installed
empty( $plugins[$plugin_file] ) ||
//Plugin must be active (user-configurable)
(
$this->options['hide_notifications_for_inactive']
&& !in_array( $plugin_file, $active)
)
) continue;
// Make sure there is something to display
if ( empty($plugins[$plugin_file]['Name']) ) $plugins[$plugin_file]['Name'] = $plugin_file;
$update_list[] = "".$plugins[$plugin_file]['Name']."";
}
if (count($update_list)>0){
echo '
';
$link = get_option('siteurl').'/wp-content/plugins/'.$this->myfolder.
'/do_update.php?action=upgrade_all';
$link = wp_nonce_url($link, 'upgrade_all');
//$plugin_msg .= " Upgrade All";
if (count($update_list)==1){
$name = array_pop($update_list);
if ( !current_user_can('edit_plugins') )
printf( __('There is a new version of %1$s available.'), $name);
else
printf( __('There is a new version of %1$s available.
Upgrade Automatically'),
$name, $link);
} else{
//make a nice listing :)
$name = implode(', ', array_slice($update_list,0,count($update_list)-1));
$name .= ' and '.array_pop($update_list);
if ( !current_user_can('edit_plugins') )
printf( __('There are new versions available for %1$s.'), $name);
else
printf( __('There are new versions available for %1$s.
Upgrade All'),
$name, $link);
}
echo "
\n";
}
}
/**
* Recursively delete a directory and all files in it (ver 2)
*/
function deltree($directory){
if (substr($directory, -1) == '/')
{
$directory = substr($directory, 0, -1);
}
if (!file_exists($directory) || !is_dir($directory))
{
$this->dprint("'$directory' : Path doesn't exist or isn't a directory!", 3);
return false;
} else {
$this->dprint("Processing directory '$directory'...");
$handle = opendir($directory);
while (false !== ($item = readdir($handle)))
{
if ( ($item != '.') && ($item != '..'))
{
//$path = $directory . DIRECTORY_SEPARATOR . $item; //this just causes trouble
//Edit: Situation ambiguous. At this time the (non-)use of
//the DIRECTORY_SEPARATOR constant is inconsistent in the code.
$path = $directory . '/' . $item;
if (is_dir($path) && !is_link($path)) {
if (!$this->deltree($path)){
return false;
};
} else {
$this->dprint("Deleting file $path",1);
if (!unlink($path)){
$this->dprint("Can't delete file '$path'",3);
return false;
};
}
}
}
closedir($handle);
$this->dprint("Deleting directory $directory",1);
if (!rmdir($directory)) {
$this->dprint("Can't delete directory '$directory'",3);
return false;
}
return true;
}
}
}//class ends here
} // if !class_exists... ends here
$ws_pup = new ws_oneclick_pup();
//}
?>