trunk replaced with branch v1.0; merge was impossible; files added

git-svn-id: http://plugins.svn.wordpress.org/wp-ffpc/trunk@684134 b8457f37-d9ea-0310-8a92-e5e31aec5664
This commit is contained in:
cadeyrn 2013-03-19 10:16:59 +00:00
parent f845f9f22b
commit fa71c7a216
7 changed files with 2354 additions and 0 deletions

12
uninstall.php Normal file
View file

@ -0,0 +1,12 @@
<?php
/**
* uninstall file for WP-FFPC; uninstall hook does not remove the databse options
*/
/* get the worker file */
include_once ( 'wp-ffpc.php' );
/* run uninstall function */
$wp_ffpc->plugin_uninstall();
?>

534
wp-ffpc-abstract.php Normal file
View file

@ -0,0 +1,534 @@
<?php
/**
* abstract base class of WP-* plugins from hello@petermolnar.eu
*/
if ( ! defined( 'WP_CONTENT_URL' ) ) define( 'WP_CONTENT_URL', WP_SITEURL . '/wp-content' );
if ( ! defined( 'WP_CONTENT_DIR' ) ) define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
if ( ! defined( 'WP_PLUGIN_URL' ) ) define( 'WP_PLUGIN_URL', WP_CONTENT_URL . '/plugins' );
if ( ! defined( 'WP_PLUGIN_DIR' ) ) define( 'WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins' );
if ( ! defined( 'WPMU_PLUGIN_URL' ) ) define( 'WPMU_PLUGIN_URL', WP_CONTENT_URL . '/mu-plugins' );
if ( ! defined( 'WPMU_PLUGIN_DIR' ) ) define( 'WPMU_PLUGIN_DIR', WP_CONTENT_DIR . '/mu-plugins' );
if (!class_exists('WP_Plugins_Abstract')) {
/**
* abstract class for common, required functionalities
*
* @var string $plugin_constant The name of the plugin, will be used with strings, names, etc.
* @var array $options Plugin options array
* @var array $defaults Default options array
* @var int $status Save, delete, neutral status storage
* @var boolean $network true if plugin is Network Active
* @var string $settings_link Link for settings page
* @var string $plugin_url URL of plugin directory to be used with url-like includes
* @var string $plugin_dir Directory of plugin to be used with standard includes
* @var string $plugin_file Filename of main plugin PHP file
* @var string $plugin_name Name of the plugin
* @var string $plugin_version Plugin version number
* @var string $setting_page Setting page URL name
* @var string $setting_slug Parent settings page slug
* @var string $donation_link Donation link URL
* @var string $button_save ID of save button in HTML form
* @var string $button_delete ID of delete button in HTML form
* @var int $capability Level of admin required to manage plugin settings
* @var string $slug_save URL slug to present saved state
* @var string $slug_delete URL slug to present delete state
* @var string $broadcast_url URL base of broadcast messages
* @var string donation_business_id Business ID for donation form
* @var string $donation_business_name Business name for donation form
* @var string $donation_item_name Donation item name for donation form
* @var string donation_business_id Business ID for donation form
* @var string $broadcast_message Name of the file to get broadcast message from the web
*
*/
abstract class WP_Plugins_Abstract {
protected $plugin_constant;
protected $options = array();
protected $defaults = array();
protected $status = 0;
protected $network = false;
protected $settings_link = '';
protected $settings_slug = '';
protected $plugin_url;
protected $plugin_dir;
protected $plugin_file;
protected $plugin_name;
protected $plugin_version;
protected $plugin_settings_page;
protected $donation_link;
protected $button_save;
protected $button_delete;
public $capability = 10;
const slug_save = '&saved=true';
const slug_delete = '&deleted=true';
const broadcast_url = 'http://petermolnar.eu/broadcast/';
const donation_business_id = 'FA3NT7XDVHPWU';
protected $donation_business_name;
protected $donation_item_name;
protected $broadcast_message;
/**
* constructor
*
* @param string $plugin_constant General plugin identifier, same as directory & base PHP file name
* @param string $plugin_version Version number of the parameter
* @param string $plugin_name Readable name of the plugin
* @param mixed $defaults Default value(s) for plugin option(s)
* @param string $donation_link Donation link of plugin
*
*/
public function __construct( $plugin_constant, $plugin_version, $plugin_name, $defaults, $donation_link ) {
$this->plugin_constant = $plugin_constant;
$this->plugin_url = $this->replace_if_ssl ( WP_PLUGIN_URL ) . '/' . $this->plugin_constant . '/';
$this->plugin_dir = WP_PLUGIN_DIR. '/' . $this->plugin_constant . '/';
$this->plugin_file = $this->plugin_constant . '/' . $this->plugin_constant . '.php';
$this->plugin_version = $plugin_version;
$this->plugin_name = $plugin_name;
$this->defaults = $defaults;
$this->plugin_settings_page = $this->plugin_constant .'-settings';
$this->donation_link = $donation_link;
$this->button_save = $this->plugin_constant . '-save';
$this->button_delete = $this->plugin_constant . '-delete';
$this->broadcast_message = self::broadcast_url . $this->plugin_constant . '.message';
$this->donation_business_name = 'PeterMolnar_WordPressPlugins_' . $this->plugin_constant . '_HU';
$this->donation_item_name = $this->plugin_name;
/* we need network wide plugin check functions */
if ( ! function_exists( 'is_plugin_active_for_network' ) )
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
/* check if plugin is network-activated */
if ( @is_plugin_active_for_network ( $this->plugin_file ) ) {
$this->network = true;
$this->settings_slug = 'settings.php';
}
else {
$this->settings_slug = 'options-general.php';
}
/* set the settings page link string */
$this->settings_link = $this->settings_slug . '?page=' . $this->plugin_settings_page;
/* initialize plugin, plugin specific init functions */
$this->plugin_init();
/* get the options */
$this->plugin_options_read();
/* setup plugin, plugin specific setup functions that need options */
$this->plugin_setup();
/* add admin styling */
if( is_admin() ) {
/* jquery ui tabs is provided by WordPress */
wp_enqueue_script ( "jquery-ui-tabs" );
wp_enqueue_script ( "jquery-ui-slider" );
/* additional admin styling */
$css_handle = $this->plugin_constant . '-admin-css';
$css_file = $this->plugin_constant . '-admin.css';
if ( @file_exists ( $this->plugin_dir . $css_file ) )
{
$css_src = $this->plugin_url . $css_file;
wp_register_style( $css_handle, $css_src, false, false, 'all' );
wp_enqueue_style( $css_handle );
}
}
register_activation_hook( $this->plugin_file , array( $this , 'plugin_activate') );
register_deactivation_hook( $this->plugin_file , array( $this , 'plugin_deactivate') );
register_uninstall_hook( $this->plugin_file , array( $this , 'plugin_uninstall') );
/* register settings pages */
if ( $this->network )
add_filter( "network_admin_plugin_action_links_" . $this->plugin_file, array( $this, 'plugin_settings_link' ) );
else
add_filter( "plugin_action_links_" . $this->plugin_file, array( $this, 'plugin_settings_link' ) );
/* register admin init, catches $_POST and adds submenu to admin menu */
if ( $this->network )
add_action('network_admin_menu', array( $this , 'plugin_admin_init') );
else
add_action('admin_menu', array( $this , 'plugin_admin_init') );
}
/**
* activation hook function, to be extended
*/
abstract function plugin_activate();
/**
* deactivation hook function, to be extended
*/
abstract function plugin_deactivate ();
/**
* uninstall hook function, to be extended
*/
abstract function plugin_uninstall();
/**
* first init hook function, to be extended, before options were read
*/
abstract function plugin_init();
/**
* second init hook function, to be extended, after options were read
*/
abstract function plugin_setup();
/**
* admin panel, the HTML usually
*/
abstract function plugin_admin_panel();
/**
* admin init: save/delete setting, add admin panel call hook
*/
public function plugin_admin_init() {
/* save parameter updates, if there are any */
if ( isset( $_POST[ $this->button_save ] ) ) {
$this->plugin_options_save();
$this->status = 1;
header( "Location: ". $this->settings_link . self::slug_save );
}
/* delete parameters if requested */
if ( isset( $_POST[ $this->button_delete ] ) ) {
$this->plugin_options_delete();
$this->status = 2;
header( "Location: ". $this->settings_link . self::slug_delete );
}
/* load additional moves */
$this->plugin_hook_admin_init();
/* get broadcast message, if available */
$this->broadcast_message = @file_get_contents( $this->broadcast_message );
/* add submenu to settings pages */
add_submenu_page( $this->settings_slug, $this->plugin_name . __( ' options' , $this->plugin_constant ), $this->plugin_name, $this->capability, $this->plugin_settings_page, array ( $this , 'plugin_admin_panel' ) );
}
/**
* to be extended
*
*/
abstract function plugin_hook_admin_init();
/**
* callback function to add settings link to plugins page
*
* @param array $links Current links to add ours to
*
*/
public function plugin_settings_link ( $links ) {
$settings_link = '<a href="' . $this->settings_link . '">' . __( 'Settings', $this->plugin_constant ) . '</a>';
array_unshift( $links, $settings_link );
return $links;
}
/**
* deletes saved options from database
*/
protected function plugin_options_delete () {
/* get the currently saved options */
if ( $this->network )
delete_site_option( $this->plugin_constant );
else
delete_option( $this->plugin_constant );
/* additional moves */
$this->plugin_hook_options_delete();
}
/**
* hook to add functionality into plugin_options_read
*/
abstract function plugin_hook_options_delete ();
/**
* reads options stored in database and reads merges them with default values
*/
protected function plugin_options_read () {
/* get the currently saved options */
if ( $this->network )
$options = get_site_option( $this->plugin_constant );
else
$options = get_option( $this->plugin_constant );
/* this is the point to make any migrations from previous versions */
$this->plugin_hook_options_migrate( $options );
/* map missing values from default */
foreach ( $this->defaults as $key => $default )
if ( !@array_key_exists ( $key, $options ) )
$options[$key] = $default;
/* removed unused keys, rare, but possible */
foreach ( array_keys ( $options ) as $key )
if ( !@array_key_exists( $key, $this->defaults ) )
unset ( $options[$key] );
/* any additional read hook */
$this->plugin_hook_options_read( $options );
$this->options = $options;
}
/**
* hook to add functionality into plugin_options_read
*/
abstract function plugin_hook_options_read ( &$options );
/**
* hook for parameter migration
*/
abstract function plugin_hook_options_migrate( &$options );
/**
* used on update and to save current options to database
*
* @param boolean $activating [optional] true on activation hook
*
*/
protected function plugin_options_save ( $activating = false ) {
/* only try to update defaults if it's not activation hook, $_POST is not empty and the post
is ours */
if ( !$activating && !empty ( $_POST ) && isset( $_POST[ $this->button_save ] ) ) {
/* we'll only update those that exist in the defaults array */
$options = $this->defaults;
foreach ( $options as $key => $default )
{
/* $_POST element is available */
if ( !empty( $_POST[$key] ) ) {
$update = $_POST[$key];
/* get rid of slashes in strings, just in case */
if ( is_string ( $update ) )
$update = stripslashes($update);
$options[$key] = $update;
}
/* empty $_POST element: when HTML form posted, empty checkboxes a 0 input
values will not be part of the $_POST array, thus we need to check
if this is the situation by checking the types of the elements,
since a missing value means update from an integer to 0
*/
elseif ( empty( $_POST[$key] ) && ( is_bool ( $default ) || is_int( $default ) ) ) {
$options[$key] = 0;
}
}
/* update the options array */
$this->options = $options;
}
/* set plugin version */
$this->options['version'] = $this->plugin_version;
/* call hook function for additional moves before saving the values */
$this->plugin_hook_options_save( $activating );
/* save options to database */
if ( $this->network )
update_site_option( $this->plugin_constant , $this->options );
else
update_option( $this->plugin_constant , $this->options );
}
/**
* hook to add functionality into plugin_options_save
*/
abstract function plugin_hook_options_save ( $activating );
/**
* sends message to sysog
*
* @param string $message message to add besides basic info
* @param int $log_level [optional] Level of log, info by default
*
*/
protected function log ( $message, $log_level = LOG_INFO ) {
if ( @is_array( $message ) || @is_object ( $message ) )
$message = serialize($message);
if (! $this->config['log'] )
return false;
switch ( $log_level ) {
case LOG_ERR :
if ( function_exists( 'syslog' ) )
syslog( $log_level , self::plugin_constant . $message );
/* error level is real problem, needs to be displayed on the admin panel */
throw new Exception ( $message );
break;
default:
if ( function_exists( 'syslog' ) && $this->config['debug'] )
syslog( $log_level , self::plugin_constant . $message );
break;
}
}
/**
* replaces http:// with https:// in an url if server is currently running on https
*
* @param string $url URL to check
*
* @return string URL with correct protocol
*
*/
protected function replace_if_ssl ( $url ) {
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )
$_SERVER['HTTPS'] = 'on';
if ( isset($_SERVER['HTTPS']) && (( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ))
$url = str_replace ( 'http://' , 'https://' , $url );
return $url;
}
/**
* function to easily print a variable
*
* @param mixed $var Variable to dump
* @param boolean $ret Return text instead of printing if true
*
*/
protected function print_var ( $var , $ret = false ) {
if ( @is_array ( $var ) || @is_object( $var ) || @is_bool( $var ) )
$var = var_export ( $var, true );
if ( $ret )
return $var;
else
echo $var;
}
/**
* print value of an element from defaults array
*
* @param mixed $e Element index of $this->defaults array
*
*/
protected function print_default ( $e ) {
_e('Default : ', $this->plugin_constant);
$select = 'select_' . $e;
if ( @is_array ( $this->$select ) ) {
$x = $this->$select;
$this->print_var ( $x[ $this->defaults[ $e ] ] );
}
else {
$this->print_var ( $this->defaults[ $e ] );
}
}
/**
* select options field processor
*
* @param elements
* array to build <option> values of
*
* @param $current
* the current active element
*
* @param $print
* boolean: is true, the options will be printed, otherwise the string will be returned
*
* @return
* prints or returns the options string
*
*/
protected function print_select_options ( $elements, $current, $valid = false, $print = true ) {
/*
foreach ($elements as $value => $name ) : ?>
<option value="<?php echo $value ?>" <?php selected( $value , $current ); ?>>
<?php echo $name ; ?>
</option>
<?php endforeach;
*/
if ( is_array ( $valid ) )
$check_disabled = true;
else
$check_disabled = false;
foreach ($elements as $value => $name ) {
//$disabled .= ( @array_key_exists( $valid[ $value ] ) && $valid[ $value ] == false ) ? ' disabled="disabled"' : '';
$opt .= '<option value="' . $value . '" ';
$opt .= selected( $value , $current );
// ugly tree level valid check to prevent array warning messages
if ( is_array( $valid ) && isset ( $valid [ $value ] ) && $valid [ $value ] == false )
$opt .= ' disabled="disabled"';
$opt .= '>';
$opt .= $name;
$opt .= "</option>\n";
}
if ( $print )
echo $opt;
else
return $opt;
}
protected function plugin_donation_form () {
?>
<script>
jQuery(document).ready(function($) {
jQuery(function() {
var select = $( "#amount" );
var slider = $( '<div id="donation-slider"></div>' ).insertAfter( select ).slider({
min: 1,
max: 8,
range: "min",
value: select[ 0 ].selectedIndex + 1,
slide: function( event, ui ) {
select[ 0 ].selectedIndex = ui.value - 1;
}
});
$( "#amount" ).change(function() {
slider.slider( "value", this.selectedIndex + 1 );
});
});
});
</script>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" class="<?php echo $this->plugin_constant ?>-donation">
<label for="amount"><?php _e( "This plugin helped your business? I'd appreciate a coffee in return :) Please!", $this->plugin_constant ); ?></label>
<select name="amount" id="amount">
<option value="3">3$</option>
<option value="5">5$</option>
<option value="10" selected="selected">10$</option>
<option value="15">15$</option>
<option value="30">30$</option>
<option value="42">42$</option>
<option value="75">75$</option>
<option value="100">100$</option>
</select>
<input type="hidden" id="cmd" name="cmd" value="_donations" />
<input type="hidden" id="tax" name="tax" value="0" />
<input type="hidden" id="business" name="business" value="<?php echo self::donation_business_id ?>" />
<input type="hidden" id="bn" name="bn" value="<?php echo $this->donation_business_name ?>" />
<input type="hidden" id="item_name" name="item_name" value="<?php _e('Donation for ', $this->plugin_constant ); echo $this->donation_item_name ?>" />
<input type="hidden" id="currency_code" name="currency_code" value="USD" />
<input type="submit" name="submit" value="<?php _e('Donate via PayPal', $this->plugin_constant ) ?>" class="button-secondary" />
</form>
<?php
}
}
}

281
wp-ffpc-acache.php Normal file
View file

@ -0,0 +1,281 @@
<?php
/**
* advanced cache worker of WordPress plugin WP-FFPC
*/
/* check for WP cache enabled*/
if ( !WP_CACHE )
return false;
/* check for config */
if (!isset($wp_ffpc_config))
return false;
/* no cache for post request (comments, plugins and so on) */
if ($_SERVER["REQUEST_METHOD"] == 'POST')
return false;
/**
* Try to avoid enabling the cache if sessions are managed
* with request parameters and a session is active
*/
if (defined('SID') && SID != '')
return false;
/* request uri */
$wp_ffpc_uri = $_SERVER['REQUEST_URI'];
/* no cache for uri with query strings, things usually go bad that way */
if ( stripos($wp_ffpc_uri, '?') !== false)
return false;
/* no cache for pages starting with /wp- like WP admin */
if (stripos($wp_ffpc_uri, '/wp-') !== false)
return false;
/* no cache for robots.txt */
if ( stripos($wp_ffpc_uri, 'robots.txt') )
return false;
/* multisite files can be too large for memcached */
if ( function_exists('is_multisite') && stripos($wp_ffpc_uri, '/files/') )
if ( is_multisite() )
return false;
/* check if config is network active: use network config */
if (!empty ( $wp_ffpc_config['network'] ) )
$wp_ffpc_config = $wp_ffpc_config['network'];
/* check if config is active for site : use site config */
elseif ( !empty ( $wp_ffpc_config[ $_SERVER['HTTP_HOST'] ] ) )
$wp_ffpc_config = $wp_ffpc_config[ $_SERVER['HTTP_HOST'] ];
/* plugin config not found :( */
else
return false;
/* no cache for for logged in users normally, only if enabled */
if ( $wp_ffpc_config['cache_loggedin'] == 0 ) {
foreach ($_COOKIE as $n=>$v) {
// test cookie makes to cache not work!!!
if ($n == 'wordpress_test_cookie') continue;
// wp 2.5 and wp 2.3 have different cookie prefix, skip cache if a post password cookie is present, also
if ( (substr($n, 0, 14) == 'wordpressuser_' || substr($n, 0, 10) == 'wordpress_' || substr($n, 0, 12) == 'wp-postpass_') && !$wp_ffpc_config['cache_loggedin'] ) {
return false;
}
}
}
$wp_ffpc_redirect = null;
$wp_ffpc_backend = new WP_FFPC_Backend( $wp_ffpc_config );
if ( $wp_ffpc_backend->status() === false )
return false;
$wp_ffpc_keys = array ( 'meta', 'data' );
$wp_ffpc_values = array();
foreach ( $wp_ffpc_keys as $key ) {
$value = $wp_ffpc_backend->get ( $wp_ffpc_backend->key ( $key ) );
if ( ! $value )
{
wp_ffpc_start();
return;
}
else
{
$wp_ffpc_values[ $key ] = $value;
}
}
/* serve cache 404 status */
if ( $wp_ffpc_values['meta']['status'] == 404 ) {
header("HTTP/1.1 404 Not Found");
flush();
die();
}
/* server redirect cache */
if ( $wp_ffpc_values['meta']['redirect'] ) {
header('Location: ' . $wp_ffpc_values['meta']['redirect'] );
flush();
die();
}
/* page is already cached on client side (chrome likes to do this, anyway, it's quite efficient) */
if ( array_key_exists( "HTTP_IF_MODIFIED_SINCE" , $_SERVER ) && !empty( $wp_ffpc_values['meta']['lastmodified'] ) ) {
$if_modified_since = strtotime(preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]));
/* check is cache is still valid */
if ( $if_modified_since >= $wp_ffpc_values['meta']['lastmodified'] ) {
header("HTTP/1.0 304 Not Modified");
flush();
die();
}
}
/* if we reach this point it means data was found & correct, serve it */
header('Content-Type: ' . $wp_ffpc_values['meta']['mime']);
/* don't allow browser caching of page */
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0');
header('Pragma: no-cache');
/* expire at this very moment */
header('Expires: ' . gmdate("D, d M Y H:i:s", time() ) . " GMT");
/* if shortlinks were set */
if (!empty ( $wp_ffpc_values['meta']['shortlink'] ) )
header( 'Link:<'. $wp_ffpc_values['meta']['shortlink'] .'>; rel=shortlink' );
/* if last modifications were set (for posts & pages) */
if ( !empty($wp_ffpc_values['meta']['lastmodified']) )
header( 'Last-Modified: ' . gmdate("D, d M Y H:i:s", $wp_ffpc_values['meta']['lastmodified'] ). " GMT" );
/* pingback urls, if existx */
if ( !empty( $wp_ffpc_values['meta']['pingback'] ) )
header( 'X-Pingback: ' . $wp_ffpc_values['meta']['pingback'] );
/* for debugging */
if ( $wp_ffpc_config['response_header'] )
header( 'X-Cache-Engine: WP-FFPC with ' . $wp_ffpc_config['cache_type'] );
/* HTML data */
echo $wp_ffpc_values['data'];
flush();
die();
/**
* starts caching function
*
*/
function wp_ffpc_start( ) {
/* start object "colleting" and pass it the the actual storer function */
ob_start('wp_ffpc_callback');
}
/**
* callback function for WordPress redirect urls
*
*/
function wp_ffpc_redirect_callback ($redirect_url, $requested_url) {
global $wp_ffpc_redirect;
$wp_ffpc_redirect = $redirect_url;
return $redirect_url;
}
/**
* write cache function, called when page generation ended
*/
function wp_ffpc_callback( $buffer ) {
global $wp_ffpc_config;
global $wp_ffpc_backend;
global $wp_ffpc_redirect;
/* no is_home = error */
if (!function_exists('is_home'))
return $buffer;
/* no <body> close tag = not HTML, don't cache */
if (stripos($buffer, '</body>') === false)
return $buffer;
/* reset meta to solve conflicts */
$meta = array();
/* trim unneeded whitespace from beginning / ending of buffer */
$buffer = trim( $buffer );
/* Can be a trackback or other things without a body.
We do not cache them, WP needs to get those calls. */
if (strlen($buffer) == 0)
return '';
if ( is_home() )
$meta['type'] = 'home';
elseif (is_feed() )
$meta['type'] = 'feed';
elseif ( is_archive() )
$meta['type'] = 'archive';
elseif ( is_single() )
$meta['type'] = 'single';
elseif ( is_page() )
$meta['type'] = 'page';
else
$meta['type'] = 'unknown';
/* check if caching is disabled for page type */
$nocache_key = 'nocache_'. $meta['type'];
/* don't cache if prevented by rule, also, log it */
if ( $wp_ffpc_config[ $nocache_key ] == 1 ) {
return $buffer;
}
if ( is_404() )
$meta['status'] = 404;
/* redirect page */
if ( $wp_ffpc_redirect != null)
$meta['redirect'] = $wp_ffpc_redirect;
/* feed is xml, all others forced to be HTML */
if ( is_feed() )
$meta['mime'] = 'text/xml;charset=';
else
$meta['mime'] = 'text/html;charset=';
/* set mimetype */
$meta['mime'] = $meta['mime'] . $wp_ffpc_config['charset'];
/* try if post is available
if made with archieve, last listed post can make this go bad
*/
global $post;
if ( !empty($post) && ( $meta['type'] == 'single' || $meta['type'] == 'page' ) && !empty ( $post->post_modified_gmt ) )
{
/* get last modification data */
$meta['lastmodified'] = strtotime ( $post->post_modified_gmt );
/* get shortlink, if possible */
if (function_exists('wp_get_shortlink'))
{
$shortlink = wp_get_shortlink( );
if (!empty ( $shortlink ) )
$meta['shortlink'] = $shortlink;
}
}
/* store pingback url if pingbacks are enabled */
if ( get_option ( 'default_ping_status' ) == 'open' )
$meta['pingback'] = get_bloginfo('pingback_url');
/* sync all http and https requests if enabled */
if ( $config['sync_protocols'] == '1' )
{
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )
$_SERVER['HTTPS'] = 'on';
if ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) )
{
$sync_from = 'http://' . $_SERVER['SERVER_NAME'];
$sync_to = 'https://' . $_SERVER['SERVER_NAME'];
}
else
{
$sync_from = 'https://' . $_SERVER['SERVER_NAME'];
$sync_to = 'http://' . $_SERVER['SERVER_NAME'];
}
$buffer = str_replace ( $sync_from, $sync_to, $buffer );
}
$wp_ffpc_backend->set ( $wp_ffpc_backend->key ( 'meta' ) , $meta );
$wp_ffpc_backend->set ( $wp_ffpc_backend->key ( 'data' ) , $buffer );
/* vital for nginx, make no problem at other places */
header("HTTP/1.1 200 OK");
/* echoes HTML out */
return $buffer;
}
?>

141
wp-ffpc-admin.css Normal file
View file

@ -0,0 +1,141 @@
.plugin-admin dt {
margin-top: 2em;
font-weight:bold;
}
.plugin-admin fieldset {
display:block;
margin: 0;
padding: 0 1em 0 1em;
border: 1px solid #ccc;
}
.plugin-admin legend {
display: none;
visibility: hidden;
}
.plugin-admin .default {
display:block;
padding-left: 1em;
font-size:90%;
color:#666;
}
.plugin-admin .description {
display:block;
}
.plugin-admin .tabs {
padding:0;
margin: 0;
list-style-type: none;
}
.plugin-admin .tabs li {
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color: #ccc;
border-bottom-color: #ccc;
line-height: 120%;
display: inline-block;
padding: 0.5em 2em 0.5em 2em;
text-decoration: none;
margin-bottom: -1px;
-webkit-border-top-left-radius: 3px;
-webkit-border-top-right-radius: 3px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
background: #f1f1f1;
background-image: -webkit-gradient(linear,left bottom,left top,from(#ececec),to(#f9f9f9));
background-image: -webkit-linear-gradient(bottom,#ececec,#f9f9f9);
background-image: -moz-linear-gradient(bottom,#ececec,#f9f9f9);
background-image: -o-linear-gradient(bottom,#ececec,#f9f9f9);
background-image: linear-gradient(to top,#ececec,#f9f9f9);
}
.plugin-admin .tabs li a {
outline: none;
text-decoration: none;
font-size: 120%;
}
.plugin-admin .tabs li.ui-state-active {
background: #fff;
border-bottom-color: #fff;
}
.error p {
font-weight: bold;
}
.error-msg {
color: #990000;
}
.ok-msg {
color: #009900;
}
.clear {
clear:both;
}
.wp-ffpc-donation {
padding: 0.8em;
border: 1px solid #ccc;
background-color: #f6f6f6;
-webkit-border-radius: 3px;
border-radius: 3px;
}
.wp-ffpc-donation * {
display: inline;
line-height: 2em;
}
.wp-ffpc-donation .currency {
padding: 0 0 0 0.3em;
}
.wp-ffpc-donation .submit {
display: inline-block;
width: 74px;
height: 21px;
}
.wp-ffpc-donation .ui-slider {
position: relative;
text-align: left;
display: inline-block;
width: 16em;
margin: 0 2em 0 2em;
height: 0.4em;
background-color: #111;
border: 1px solid #ccc;
-webkit-border-radius: 3px;
border-radius: 3px;
background: #f1f1f1;
}
.ui-slider .ui-slider-handle {
position: absolute;
z-index: 2;
width: 1.2em;
height: 1.2em;
margin-top: -0.4em;
cursor: default;
border: 1px solid #ccc;
-webkit-border-radius: 3px;
border-radius: 3px;
background: #f1f1f1;
background-image: -webkit-gradient(linear,left bottom,left top,from(#ececec),to(#f9f9f9));
background-image: -webkit-linear-gradient(bottom,#ececec,#f9f9f9);
background-image: -moz-linear-gradient(bottom,#ececec,#f9f9f9);
background-image: -o-linear-gradient(bottom,#ececec,#f9f9f9);
background-image: linear-gradient(to top,#ececec,#f9f9f9);
outline: none;
}

672
wp-ffpc-backend.php Normal file
View file

@ -0,0 +1,672 @@
<?php
/**
* backend driver for WordPress plugin WP-FFPC
*
* supported storages:
* - APC
* - Memcached
* - Memcache
*
*/
if (!class_exists('WP_FFPC_Backend')) {
/* __ only availabe if we're running from the inside of wordpress, not in advanced-cache.php phase */
if ( function_exists ( '__' ) ) {
function __translate__ ( $text, $domain ) { return __($text, $domain); }
}
else {
function __translate__ ( $text, $domain ) { return $text; }
}
/**
*
* @var array $key_prefixes Prefix keys for entries
* @var mixed $connection Backend object storage variable
* @var array $config Configuration settings array
* @var boolean $alive Backend aliveness indicator
* @var mixed $status Backend server status storage
*
*/
class WP_FFPC_Backend {
const plugin_constant = 'wp-ffpc';
const network_key = 'network';
const id_prefix = 'wp-ffpc-id-';
const prefix = 'prefix-';
const host_separator = ',';
const port_separator = ':';
private $key_prefixes = array ( 'meta', 'data' );
private $connection = NULL;
private $options;
private $alive = false;
public $status;
/**
* constructor
*
* @param mixed $config Configuration options
*
*/
public function __construct( $config ) {
$this->options = $config;
/* no config, nothing is going to work */
if ( empty ( $this->options ) ) {
return false;
}
/* split hosts entry to servers */
$this->set_servers();
/* call backend initiator based on cache type */
$init = $this->proxy( 'init' );
$this->log ( __translate__(' init starting', self::plugin_constant ));
$this->$init();
}
/*********************** PUBLIC FUNCTIONS ***********************/
/**
* build key to make requests with
*
* @param string $suffix suffix to add to prefix
*
*/
public function key ( $suffix = 'meta' ) {
/* use the full accessed URL string as key, same will be generated by nginx as well
we need a data and a meta key: data is string only with content, meta is not used in nginx */
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )
$_SERVER['HTTPS'] = 'on';
$protocol = ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) ) ? 'https://' : 'http://';
return $suffix . '-' . $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}
/**
* public get function, transparent proxy to internal function based on backend
*
* @param string $key Cache key to get value for
*
* @return mixed False when entry not found or entry value on success
*/
public function get ( &$key ) {
/* look for backend aliveness, exit on inactive backend */
if ( ! $this->is_alive() )
return false;
/* log the current action */
$this->log ( __translate__('get ', self::plugin_constant ). $key );
/* proxy to internal function */
$internal = $this->proxy( 'get' );
$result = $this->$internal( $key );
if ( $result === false )
$this->log ( __translate__( "failed to get entry: ", self::plugin_constant ) . $key );
return $result;
}
/**
* public set function, transparent proxy to internal function based on backend
*
* @param string $key Cache key to set with ( reference only, for speed )
* @param mixed $data Data to set ( reference only, for speed )
*
* @return mixed $result status of set function
*/
public function set ( &$key, &$data ) {
/* look for backend aliveness, exit on inactive backend */
if ( ! $this->is_alive() )
return false;
/* log the current action */
$this->log( __translate__('set ', self::plugin_constant ) . $key . __translate__(' expiration time: ', self::plugin_constant ) . $this->options['expire']);
/* proxy to internal function */
$internal = $this->options['cache_type'] . '_set';
$result = $this->$internal( $key, $data );
/* check result validity */
if ( $result === false )
$this->log ( __translate__('failed to set entry: ', self::plugin_constant ) . $key, LOG_WARNING );
return $result;
}
/**
* public get function, transparent proxy to internal function based on backend
*
* @param string $key Cache key to invalidate, false mean full flush
*/
public function clear ( $post_id = false ) {
/* look for backend aliveness, exit on inactive backend */
if ( ! $this->is_alive() )
return false;
/* exit if no post_id is specified */
if ( empty ( $post_id ) && $this->options['invalidation_method'] != 0) {
$this->log ( __translate__('not clearing unidentified post ', self::plugin_constant ), LOG_WARNING );
return false;
}
/* if invalidation method is set to full, flush cache */
if ( $this->options['invalidation_method'] === 0 || empty ( $post_id ) ) {
/* log action */
$this->log ( __translate__('flushing cache', self::plugin_constant ) );
/* proxy to internal function */
$internal = $this->proxy ( 'flush' );
$result = $this->$internal();
if ( $result === false )
$this->log ( __translate__('failed to flush cache', self::plugin_constant ), LOG_WARNING );
return $result;
}
/* need permalink functions */
if ( !function_exists('get_permalink') )
include_once ( ABSPATH . 'wp-includes/link-template.php' );
/* get path from permalink */
$path = substr ( get_permalink( $post_id ) , 7 );
/* no path, don't do anything */
if ( empty( $path ) ) {
$this->log ( __translate__('unable to determine path from Post Permalink, post ID: ', self::plugin_constant ) . $post_id , LOG_WARNING );
return false;
}
if ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) )
$protocol = 'https://';
else
$protocol = 'http://';
$to_clear = array (
$this->options['prefix-meta'] . $protocol . $path,
$this->options['prefix-data'] . $protocol . $path,
);
$internal = $this->proxy ( 'clear' );
$this->$internal ( $to_clear );
}
/**
* get backend aliveness
*
* @return array Array of configured servers with aliveness value
*
*/
public function status () {
/* look for backend aliveness, exit on inactive backend */
if ( ! $this->is_alive() )
return false;
$internal = $this->proxy ( 'status' );
return $this->status;
}
/**
* backend proxy function name generator
*
* @return string Name of internal function based on cache_type
*
*/
private function proxy ( $method ) {
return $this->options['cache_type'] . '_' . $method;
}
/**
* function to check backend aliveness
*
* @return boolean true if backend is alive, false if not
*
*/
private function is_alive() {
if ( ! $this->alive ) {
$this->log ( __translate__("backend is not active, exiting function ", self::plugin_constant ) . __FUNCTION__, LOG_WARNING );
return false;
}
return true;
}
/**
* split hosts string to backend servers
*
*
*/
private function set_servers () {
/* replace servers array in config according to hosts field */
$servers = explode( self::host_separator , $this->options['hosts']);
$options['servers'] = array();
foreach ( $servers as $snum => $sstring ) {
$separator = strpos( $sstring , self::port_separator );
$host = substr( $sstring, 0, $separator );
$port = substr( $sstring, $separator + 1 );
/* IP server */
if ( !empty ( $host ) && !empty($port) && is_numeric($port) ) {
$this->options['servers'][$sstring] = array (
'host' => $host,
'port' => $port
);
}
}
}
/**
* get current array of servers
*
* @return array Server list in current config
*
*/
public function get_servers () {
return $this->options['servers'];
}
/**
* sends message to sysog
*
* @param mixed $message message to add besides basic info
*
*/
public function log ( $message, $log_level = LOG_WARNING ) {
if ( @is_array( $message ) || @is_object ( $message ) )
$message = serialize($message);
if (! $this->options['log'] )
return false;
switch ( $log_level ) {
case LOG_ERR :
if ( function_exists( 'syslog' ) )
syslog( $log_level , self::plugin_constant . " with " . $this->options['cache_type'] . ' ' . $message );
/* error level is real problem, needs to be displayed on the admin panel */
throw new Exception ( $message );
break;
default:
if ( function_exists( 'syslog' ) && $this->options['debug'] )
syslog( $log_level , self::plugin_constant . " with " . $this->options['cache_type'] . ' ' . $message );
break;
}
}
/*********************** END PUBLIC FUNCTIONS ***********************/
/*********************** APC FUNCTIONS ***********************/
/**
* init apc backend: test APC availability and set alive status
*/
private function apc_init () {
/* verify apc functions exist, apc extension is loaded */
if ( ! function_exists( 'apc_sma_info' ) ) {
$this->log ( __translate__('APC extension missing', self::plugin_constant ) );
return false;
}
/* verify apc is working */
if ( apc_sma_info() ) {
$this->log ( __translate__('backend OK', self::plugin_constant ) );
$this->alive = true;
}
}
/**
* health checker for APC
*
* @return boolean Aliveness status
*
*/
private function apc_status () {
return $this->alive;
}
/**
* get function for APC backend
*
* @param string $key Key to get values for
*
* @return mixed Fetched data based on key
*
*/
private function apc_get ( &$key ) {
return apc_fetch( $key );
}
/**
* Set function for APC backend
*
* @param string $key Key to set with
* @param mixed $data Data to set
*
* @return boolean APC store outcome
*/
private function apc_set ( &$key, &$data ) {
return apc_store( $key , $data , $this->options['expire'] );
}
/**
* Flushes APC user entry storage
*
* @return boolean APC flush outcome status
*
*/
private function apc_flush ( ) {
return apc_clear_cache('user');
}
/**
* Removes entry from APC or flushes APC user entry storage
*
* @param mixed $keys Keys to clear, string or array
*/
private function apc_clear ( $keys ) {
/* make an array if only one string is present, easier processing */
if ( !is_array ( $keys ) )
$keys = array ( $keys );
foreach ( $keys as $key ) {
if ( ! apc_delete ( $key ) ) {
$this->log ( __translate__('Failed to delete APC entry: ', self::plugin_constant ) . $key, LOG_ERR );
//throw new Exception ( __translate__('Deleting APC entry failed with key ', self::plugin_constant ) . $key );
}
else {
$this->log ( __translate__( 'APC entry delete: ', self::plugin_constant ) . $key );
}
}
}
/*********************** END APC FUNCTIONS ***********************/
/*********************** MEMCACHED FUNCTIONS ***********************/
/**
* init memcached backend
*/
private function memcached_init () {
/* Memcached class does not exist, Memcached extension is not available */
if (!class_exists('Memcached')) {
$this->log ( __translate__(' Memcached extension missing', self::plugin_constant ), LOG_ERR );
return false;
}
/* check for existing server list, otherwise we cannot add backends */
if ( empty ( $this->options['servers'] ) && ! $this->alive ) {
$this->log ( __translate__("Memcached servers list is empty, init failed", self::plugin_constant ), LOG_WARNING );
return false;
}
/* check is there's no backend connection yet */
if ( $this->connection === NULL ) {
/* persistent backend needs an identifier */
if ( $this->options['persistent'] == '1' )
$this->connection = new Memcached( self::plugin_constant );
else
$this->connection = new Memcached();
/* use binary and not compressed format, good for nginx and still fast */
$this->connection->setOption( Memcached::OPT_COMPRESSION , false );
//$this->connection->setOption( Memcached::OPT_BINARY_PROTOCOL , true );
}
/* check if initialization was success or not */
if ( $this->connection === NULL ) {
$this->log ( __translate__( 'error initializing Memcached PHP extension, exiting', self::prefix ) );
return false;
}
/* check if we already have list of servers, only add server(s) if it's not already connected */
$servers_alive = array();
if ( !empty ( $this->status ) ) {
$servers_alive = $this->connection->getServerList();
/* create check array if backend servers are already connected */
if ( !empty ( $servers ) ) {
foreach ( $servers_alive as $skey => $server ) {
$skey = $server['host'] . ":" . $server['port'];
$servers_alive[ $skey ] = true;
}
}
}
/* adding servers */
foreach ( $this->options['servers'] as $server_id => $server ) {
/* reset server status to unknown */
$this->status[$server_id] = -1;
/* only add servers that does not exists already in connection pool */
if ( !@array_key_exists($server_id , $servers_alive ) ) {
$this->connection->addServer( $server['host'], $server['port'] );
$this->log ( $server_id . __translate__(" added, persistent mode: ", self::plugin_constant ) . $this->options['persistent'] );
}
}
/* backend is now alive */
$this->alive = true;
$this->memcached_status();
}
/**
* sets current backend alive status for Memcached servers
*
*/
private function memcached_status () {
/* server status will be calculated by getting server stats */
$this->log ( __translate__("checking server statuses", self::plugin_constant ));
/* get servers statistic from connection */
$report = $this->connection->getStats();
foreach ( $report as $server_id => $details ) {
/* reset server status to offline */
$this->status[$server_id] = 0;
/* if server uptime is not empty, it's most probably up & running */
if ( !empty($details['uptime']) ) {
$this->log ( $server_id . __translate__(" server is up & running", self::plugin_constant ));
$this->status[$server_id] = 1;
}
}
}
/**
* get function for Memcached backend
*
* @param string $key Key to get values for
*
*/
private function memcached_get ( &$key ) {
return $this->connection->get($key);
}
/**
* Set function for Memcached backend
*
* @param string $key Key to set with
* @param mixed $data Data to set
*
*/
private function memcached_set ( &$key, &$data ) {
$result = $this->connection->set ( $key, $data , $this->options['expire'] );
/* if storing failed, log the error code */
if ( $result === false ) {
$code = $this->connection->getResultCode();
$this->log ( __translate__('unable to set entry ', self::plugin_constant ) . $key . __translate__( ', Memcached error code: ', self::plugin_constant ) . $code );
//throw new Exception ( __translate__('Unable to store Memcached entry ', self::plugin_constant ) . $key . __translate__( ', error code: ', self::plugin_constant ) . $code );
}
return $result;
}
/**
*
* Flush memcached entries
*/
private function memcached_flush ( ) {
return $this->connection->flush();
}
/**
* Removes entry from Memcached or flushes Memcached storage
*
* @param mixed $keys String / array of string of keys to delete entries with
*/
private function memcached_clear ( $keys ) {
/* make an array if only one string is present, easier processing */
if ( !is_array ( $keys ) )
$keys = array ( $keys );
foreach ( $keys as $key ) {
$kresult = $this->connection->delete( $key );
if ( $kresult === false ) {
$code = $this->connection->getResultCode();
$this->log ( __translate__('unable to delete entry ', self::plugin_constant ) . $key . __translate__( ', Memcached error code: ', self::plugin_constant ) . $code );
}
else {
$this->log ( __translate__( 'entry deleted: ', self::plugin_constant ) . $key );
}
}
}
/*********************** END MEMCACHED FUNCTIONS ***********************/
/*********************** MEMCACHE FUNCTIONS ***********************/
/**
* init memcache backend
*/
private function memcache_init () {
/* Memcached class does not exist, Memcache extension is not available */
if (!class_exists('Memcache')) {
$this->log ( __translate__('PHP Memcache extension missing', self::plugin_constant ), LOG_ERR );
return false;
}
/* check for existing server list, otherwise we cannot add backends */
if ( empty ( $this->options['servers'] ) && ! $this->alive ) {
$this->log ( __translate__("servers list is empty, init failed", self::plugin_constant ), LOG_WARNING );
return false;
}
/* check is there's no backend connection yet */
if ( $this->connection === NULL )
$this->connection = new Memcache();
/* check if initialization was success or not */
if ( $this->connection === NULL ) {
$this->log ( __translate__( 'error initializing Memcache PHP extension, exiting', self::prefix ) );
return false;
}
/* adding servers */
foreach ( $this->options['servers'] as $server_id => $server ) {
/* reset server status to unknown */
if ( $this->options['persistent'] == '1' )
$this->status[$server_id] = $this->connection->pconnect ( $server['host'] , $server['port'] );
else
$this->status[$server_id] = $this->connection->connect ( $server['host'] , $server['port'] );
$this->log ( $server_id . __translate__(" added, persistent mode: ", self::plugin_constant ) . $this->options['persistent'] );
}
/* backend is now alive */
$this->alive = true;
$this->memcache_status();
}
/**
* check current backend alive status for Memcached
*
*/
private function memcache_status () {
/* server status will be calculated by getting server stats */
$this->log ( __translate__("checking server statuses", self::plugin_constant ));
/* get servers statistic from connection */
foreach ( $this->options['servers'] as $server_id => $server ) {
$this->status[$server_id] = $this->connection->getServerStatus( $server['host'], $server['port'] );
if ( $this->status[$server_id] == 0 )
$this->log ( $server_id . __translate__(" server is down", self::plugin_constant ));
else
$this->log ( $server_id . __translate__(" server is up & running", self::plugin_constant ));
}
}
/**
* get function for Memcached backend
*
* @param string $key Key to get values for
*
*/
private function memcache_get ( &$key ) {
return $this->connection->get($key);
}
/**
* Set function for Memcached backend
*
* @param string $key Key to set with
* @param mixed $data Data to set
*
*/
private function memcache_set ( &$key, &$data ) {
$result = $this->connection->set ( $key, $data , 0 , $this->options['expire'] );
return $result;
}
/**
*
* Flush memcached entries
*/
private function memcache_flush ( ) {
return $this->connection->flush();
}
/**
* Removes entry from Memcached or flushes Memcached storage
*
* @param mixed $keys String / array of string of keys to delete entries with
*/
private function memcache_clear ( $keys ) {
/* make an array if only one string is present, easier processing */
if ( !is_array ( $keys ) )
$keys = array ( $keys );
foreach ( $keys as $key ) {
$kresult = $this->connection->delete( $key );
if ( $kresult === false )
{
$this->log ( __translate__('unable to delete entry ', self::plugin_constant ) . $key );
}
else
{
$this->log ( __translate__( 'entry deleted: ', self::plugin_constant ) . $key );
}
}
}
/*********************** END MEMCACHE FUNCTIONS ***********************/
}
}
?>

663
wp-ffpc-class.php Normal file
View file

@ -0,0 +1,663 @@
<?php
/**
* main class for WordPress plugin WP-FFPC
*
* supported storages:
* - APC
* - Memcached
* - Memcache
*
*/
if ( ! class_exists( 'WP_FFPC' ) ) {
/* get the plugin abstract class*/
include_once ( 'wp-ffpc-abstract.php');
/* get the common functions class*/
include_once ( 'wp-ffpc-backend.php');
/**
* main wp-ffpc class
*
* @var string $acache_config Configuration storage file location
* @var string $acache_worker The advanced cache worker file location
* @var string $acache The WordPress standard advanced cache location
* @var array $select_cache_type Possible cache types array
* @var array $select_invalidation_method Possible invalidation methods array
* @var string $nginx_sample Nginx example config file location
* @var array $select_cache_type Cache types string array
* @var array $select_invalidation_method Invalidation methods string array
*
*/
class WP_FFPC extends WP_Plugins_Abstract {
const host_separator = ',';
const port_separator = ':';
const donation_id_key = 'hosted_button_id=';
const global_config_var = '$wp_ffpc_config';
const slug_flush = '&flushed=true';
private $global_option = '';
private $global_config_key = '';
private $global_config = array();
private $global_saved = false;
private $acache_worker = '';
private $acache = '';
private $nginx_sample = '';
private $acache_backend = '';
private $button_flush;
protected $select_cache_type = array ();
protected $select_invalidation_method = array ();
protected $valid_cache_type = array ();
/**
* init hook function runs before admin panel hook, themeing and options read
*/
public function plugin_init() {
/* advanced cache "worker" file */
$this->acache_worker = $this->plugin_dir . $this->plugin_constant . '-acache.php';
/* WordPress advanced-cache.php file location */
$this->acache = WP_CONTENT_DIR . '/advanced-cache.php';
/* nginx sample config file */
$this->nginx_sample = $this->plugin_dir . $this->plugin_constant . '-nginx-sample.conf';
/* backend driver file */
$this->acache_backend = $this->plugin_dir . $this->plugin_constant . '-backend.php';
/* flush button identifier */
$this->button_flush = $this->plugin_constant . '-flush';
/* global options identifier */
$this->global_option = $this->plugin_constant . '-global';
/* set global config key; here, because it's needed for migration */
if ( $this->network )
$this->global_config_key = 'network';
else
$this->global_config_key = $_SERVER['HTTP_HOST'];
/* cache type possible values array */
$this->select_cache_type = array (
'apc' => __( 'APC' , $this->plugin_constant ),
'memcache' => __( 'PHP Memcache' , $this->plugin_constant ),
'memcached' => __( 'PHP Memcached' , $this->plugin_constant ),
);
$this->valid_cache_type = array (
'apc' => function_exists( 'apc_sma_info' ) ? true : false,
'memcache' => class_exists ( 'Memcache') ? true : false,
'memcached' => class_exists ( 'Memcached') ? true : false,
);
/* invalidation method possible values array */
$this->select_invalidation_method = array (
0 => __( 'flush cache' , $this->plugin_constant ),
1 => __( 'only modified post' , $this->plugin_constant ),
);
}
/**
* additional init, steps that needs the plugin options
*
*/
public function plugin_setup () {
/* initiate backend */
$this->backend = new WP_FFPC_Backend ( $this->options );
/* get all available post types */
$post_types = get_post_types( );
/* cache invalidation hooks */
foreach ( $post_types as $post_type ) {
add_action( 'new_to_publish_' .$post_type , array( $this->backend , 'clear' ), 0 );
add_action( 'draft_to_publish' .$post_type , array( $this->backend , 'clear' ), 0 );
add_action( 'pending_to_publish' .$post_type , array( $this->backend , 'clear' ), 0 );
add_action( 'private_to_publish' .$post_type , array( $this->backend , 'clear' ), 0 );
add_action( 'publish_' . $post_type , array( $this->backend , 'clear' ), 0 );
}
/* invalidation on some other ocasions as well */
add_action( 'switch_theme', array( $this->backend , 'clear' ), 0 );
add_action( 'deleted_post', array( $this->backend , 'clear' ), 0 );
add_action( 'edit_post', array( $this->backend , 'clear' ), 0 );
/* add filter for catching canonical redirects */
add_filter('redirect_canonical', 'wp_ffpc_redirect_callback', 10, 2);
}
/**
* activation hook function, to be extended
*/
public function plugin_activate() {
/* we leave this empty to avoid not detecting WP network correctly */
}
/**
* deactivation hook function, to be extended
*/
public function plugin_deactivate () {
/* remove current site config from global config */
$this->update_global_config( true );
}
/**
* uninstall hook function, to be extended
*/
public function plugin_uninstall( $delete_options = true ) {
/* delete advanced-cache.php file */
unlink ( $this->acache );
/* delete site settings */
if ( $delete_options ) {
$this->plugin_options_delete ();
}
}
/**
* admin panel, the admin page displayed for plugin settings
*/
public function plugin_admin_panel() {
/**
* security, if somehow we're running without WordPress security functions
*/
if( ! function_exists( 'current_user_can' ) || ! current_user_can( 'manage_options' ) ){
die( );
}
?>
<div class="wrap">
<script>
jQuery(document).ready(function($) {
jQuery( "#<?php echo $this->plugin_constant ?>-settings" ).tabs();
});
</script>
<?php
$this->plugin_donation_form();
/**
* if options were saved, display saved message
*/
if ( ! empty( $this->broadcast_message ) ) : ?>
<div class="updated"><?php echo $this->broadcast_message; ?></div>
<?php endif;
/**
* if options were saved, display saved message
*/
if ($_GET['saved']=='true' || $this->status == 1) : ?>
<div class='updated settings-error'><p><strong><?php _e( 'Settings saved.' , $this->plugin_constant ) ?></strong></p></div>
<?php endif;
/**
* if options were delete, display delete message
*/
if ($_GET['deleted']=='true' || $this->status == 2) : ?>
<div class='error'><p><strong><?php _e( 'Plugin options deleted.' , $this->plugin_constant ) ?></strong></p></div>
<?php endif;
/**
* if options were saved
*/
if ($_GET['flushed']=='true' || $this->status == 3) : ?>
<div class='updated settings-error'><p><strong><?php _e( "Cache flushed." , $this->plugin_constant ); ?></strong></p></div>
<?php endif;
/**
* the admin panel itself
*/
?>
<h2><?php echo $this->plugin_name ; _e( ' settings', $this->plugin_constant ) ; ?></h2>
<?php if ( ! WP_CACHE ) : ?>
<div class="error"><p><?php _e("WP_CACHE is disabled, plugin will not work that way. Please add define `( 'WP_CACHE', true );` in wp-config.php", $this->plugin_constant ); ?></p></div>
<?php endif; ?>
<?php if ( ! $this->global_saved ) : ?>
<div class="error"><p><?php _e("WARNING: plugin settings are not yet saved for the site, please save settings!", $this->plugin_constant); ?></p><p><?php _e( "Technical information: the configuration array is not present in the global configuration." , $this->plugin_constant ) ?></p></div>
<?php endif; ?>
<?php if ( ! file_exists ( $this->acache ) ) : ?>
<div class="error"><p><?php _e("WARNING: advanced cache file is yet to be generated, please save settings!", $this->plugin_constant); ?></p><p><?php _e( "Technical information: please check if location is writable: " . $this->acache , $this->plugin_constant ) ?></p></div>
<?php endif; ?>
<?php if ( $this->options['cache_type'] == 'memcached' && !class_exists('Memcached') ) : ?>
<div class="error"><p><?php _e('ERROR: Memcached cache backend activated but no PHP memcached extension was found.', $this->plugin_constant); ?></p></div>
<?php endif; ?>
<?php if ( $this->options['cache_type'] == 'memcache' && !class_exists('Memcache') ) : ?>
<div class="error"><p><?php _e('ERROR: Memcache cache backend activated but no PHP memcache extension was found.', $this->plugin_constant); ?></p></div>
<?php endif; ?>
<?php
/* get the current runtime configuration for memcache in PHP because Memcache in binary mode is really problematic */
if ( extension_loaded ( 'memcache' ) )
{
$memcache_settings = ini_get_all( 'memcache' );
if ( !empty ( $memcache_settings ) && $this->options['cache_type'] == 'memcache' )
{
$memcache_protocol = strtolower($memcache_settings['memcache.protocol']['local_value']);
if ( $memcached_protocol == 'binary' ) :
?>
<div class="error"><p><?php _e('WARNING: Memcache extension is configured to use binary mode. This is very buggy and the plugin will most probably not work correctly. <br />Please consider to change either to ASCII mode or to Memcached extension.', $this->plugin_constant ); ?></p></div>
<?php
endif;
}
}
?>
<div class="updated">
<p><strong><?php _e ( 'Driver: ' , $this->plugin_constant); echo $this->options['cache_type']; ?></strong></p>
<?php
/* only display backend status if memcache-like extension is running */
if ( strstr ( $this->options['cache_type'], 'memcache') ) :
?><p><?php
_e( '<strong>Backend status:</strong><br />', $this->plugin_constant );
/* we need to go through all servers */
$servers = $this->backend->status();
foreach ( $servers as $server_string => $status ) {
echo $server_string ." => ";
if ( $status == 0 )
_e ( '<span class="error-msg">down</span><br />', $this->plugin_constant );
elseif ( $status == 1 )
_e ( '<span class="ok-msg">up & running</span><br />', $this->plugin_constant );
else
_e ( '<span class="error-msg">unknown, please try re-saving settings!</span><br />', $this->plugin_constant );
}
?></p><?php
endif;
?>
</div>
<form method="post" action="#" id="<?php echo $this->plugin_constant ?>-settings" class="plugin-admin">
<ul class="tabs">
<li><a href="#<?php echo $this->plugin_constant ?>-type" class="wp-switch-editor"><?php _e( 'Cache type', $this->plugin_constant ); ?></a></li>
<li><a href="#<?php echo $this->plugin_constant ?>-debug" class="wp-switch-editor"><?php _e( 'Debug & in-depth', $this->plugin_constant ); ?></a></li>
<li><a href="#<?php echo $this->plugin_constant ?>-exceptions" class="wp-switch-editor"><?php _e( 'Cache exceptions', $this->plugin_constant ); ?></a></li>
<li><a href="#<?php echo $this->plugin_constant ?>-memcached" class="wp-switch-editor"><?php _e( 'Memcache(d)', $this->plugin_constant ); ?></a></li>
<li><a href="#<?php echo $this->plugin_constant ?>-nginx" class="wp-switch-editor"><?php _e( 'nginx', $this->plugin_constant ); ?></a></li>
</ul>
<fieldset id="<?php echo $this->plugin_constant ?>-type">
<legend><?php _e( 'Set cache type', $this->plugin_constant ); ?></legend>
<dl>
<dt>
<label for="cache_type"><?php _e('Select backend', $this->plugin_constant); ?></label>
</dt>
<dd>
<select name="cache_type" id="cache_type">
<?php $this->print_select_options ( $this->select_cache_type , $this->options['cache_type'], $this->valid_cache_type ) ?>
</select>
<span class="description"><?php _e('Select backend storage driver', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'cache_type' ); ?></span>
</dd>
<dt>
<label for="expire"><?php _e('Expiration time (ms)', $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="number" name="expire" id="expire" value="<?php echo $this->options['expire']; ?>" />
<span class="description"><?php _e('Sets validity time of entry in milliseconds', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'expire' ); ?></span>
</dd>
<dt>
<label for="charset"><?php _e('Charset', $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="text" name="charset" id="charset" value="<?php echo $this->options['charset']; ?>" />
<span class="description"><?php _e('Charset of HTML and XML (pages and feeds) data.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'charset' ); ?></span>
</dd>
<dt>
<label for="invalidation_method"><?php _e('Cache invalidation method', $this->plugin_constant); ?></label>
</dt>
<dd>
<select name="invalidation_method" id="invalidation_method">
<?php $this->print_select_options ( $this->select_invalidation_method , $this->options['invalidation_method'] ) ?>
</select>
<span class="description"><?php _e('Select cache invalidation method. <p><strong>Be careful! Selecting "flush cache" will flush the whole cache, including elements that might have been set and used by other applications. Also, invalidating only the post will _not_ clear categories, archive and taxonomy pages, therefore only use this if refreshing after publish can wait until the entries expire on their own.</strong></p>', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'invalidation_method' ); ?></span>
</dd>
<dt>
<label for="prefix_data"><?php _e('Data prefix', $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="text" name="prefix_data" id="prefix_data" value="<?php echo $this->options['prefix_data']; ?>" />
<span class="description"><?php _e('Prefix for HTML content keys, can be used in nginx.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'prefix_data' ); ?></span>
</dd>
<dt>
<label for="prefix_meta"><?php _e('Meta prefix', $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="text" name="prefix_meta" id="prefix_meta" value="<?php echo $this->options['prefix_meta']; ?>" />
<span class="description"><?php _e('Prefix for meta content keys, used only with PHP processing.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'prefix_meta' ); ?></span>
</dd>
</dl>
</fieldset>
<fieldset id="<?php echo $this->plugin_constant ?>-debug">
<legend><?php _e( 'Debug & in-depth settings', $this->plugin_constant ); ?></legend>
<dl>
<dt>
<label for="log"><?php _e("Enable logging", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="log" id="log" value="1" <?php checked($this->options['log'],true); ?> />
<span class="description"><?php _e('Enables ERROR and WARNING level syslog messages. Requires PHP syslog function.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'log' ); ?></span>
</dd>
<dt>
<label for="log_info"><?php _e("Enable information log", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="log_info" id="log_info" value="1" <?php checked($this->options['log_info'],true); ?> />
<span class="description"><?php _e('Enables INFO level messages; carefull, plugin is really talkative. Requires PHP syslog function.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'log_info' ); ?></span>
</dd>
<dt>
<label for="response_header"><?php _e("Add X-Cache-Engine header", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="response_header" id="response_header" value="1" <?php checked($this->options['response_header'],true); ?> />
<span class="description"><?php _e('Add X-Cache-Engine HTTP header to HTTP responses.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'response_header' ); ?></span>
</dd>
<dt>
<label for="sync_protocols"><?php _e("Enable sync protocolls", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="sync_protocols" id="sync_protocols" value="1" <?php checked($this->options['sync_protocols'],true); ?> />
<span class="description"><?php _e('Enable to replace every protocol to the same as in the request for site\'s domain', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'sync_protocols' ); ?></span>
</dd>
</dl>
</fieldset>
<fieldset id="<?php echo $this->plugin_constant ?>-exceptions">
<legend><?php _e( 'Set cache additions/excepions', $this->plugin_constant ); ?></legend>
<dl>
<dt>
<label for="cache_loggedin"><?php _e('Enable cache for logged in users', $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="cache_loggedin" id="cache_loggedin" value="1" <?php checked($this->options['cache_loggedin'],true); ?> />
<span class="description"><?php _e('Cache pages even if user is logged in.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'cache_loggedin' ); ?></span>
</dd>
<dt>
<label for="nocache_home"><?php _e("Don't cache home", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_home" id="nocache_home" value="1" <?php checked($this->options['nocache_home'],true); ?> />
<span class="description"><?php _e('Exclude home page from caching', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'nocache_home' ); ?></span>
</dd>
<dt>
<label for="nocache_feed"><?php _e("Don't cache feeds", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_feed" id="nocache_feed" value="1" <?php checked($this->options['nocache_feed'],true); ?> />
<span class="description"><?php _e('Exclude feeds from caching.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'nocache_feed' ); ?></span>
</dd>
<dt>
<label for="nocache_archive"><?php _e("Don't cache archives", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_archive" id="nocache_archive" value="1" <?php checked($this->options['nocache_archive'],true); ?> />
<span class="description"><?php _e('Exclude archives from caching.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'nocache_archive' ); ?></span>
</dd>
<dt>
<label for="nocache_single"><?php _e("Don't cache posts (and single-type entries)", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_single" id="nocache_single" value="1" <?php checked($this->options['nocache_single'],true); ?> />
<span class="description"><?php _e('Exclude singles from caching.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'nocache_single' ); ?></span>
</dd>
<dt>
<label for="nocache_page"><?php _e("Don't cache pages", $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_page" id="nocache_page" value="1" <?php checked($this->options['nocache_page'],true); ?> />
<span class="description"><?php _e('Exclude pages from caching.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'nocache_page' ); ?></span>
</dd>
</dl>
</fieldset>
<fieldset id="<?php echo $this->plugin_constant ?>-memcached">
<legend><?php _e('Settings for memcached backend', $this->plugin_constant); ?></legend>
<dl>
<dt>
<label for="hosts"><?php _e('Hosts', $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="text" name="hosts" id="hosts" value="<?php echo $this->options['hosts']; ?>" />
<span class="description"><?php _e('List all valid like host:port,host:port,... <br />No spaces are allowed, please stick to use ":" for separating host and port and "," for separating entries. Do not add trailing ",".', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'hosts' ); ?></span>
</dd>
<dt>
<label for="persistent"><?php _e('Persistent memcache connections', $this->plugin_constant); ?></label>
</dt>
<dd>
<input type="checkbox" name="persistent" id="persistent" value="1" <?php checked($this->options['persistent'],true); ?> />
<span class="description"><?php _e('Make all memcache(d) connections persistent. Be carefull with this setting, always test the outcome.', $this->plugin_constant); ?></span>
<span class="default"><?php $this->print_default ( 'persistent' ); ?></span>
</dd>
</dl>
</fieldset>
<fieldset id="<?php echo $this->plugin_constant ?>-nginx">
<legend><?php _e('Sample config for nginx to utilize the data entries', $this->plugin_constant); ?></legend>
<pre><?php echo $this->nginx_example(); ?></pre>
</fieldset>
<p class="clear">
<input class="button-primary" type="submit" name="<?php echo $this->button_save ?>" id="<?php echo $this->button_save ?>" value="<?php _e('Save Changes', $this->plugin_constant ) ?>" />
<input class="button-secondary" style="float: right" type="submit" name="<?php echo $this->button_delete ?>" id="<?php echo $this->button_delete ?>" value="<?php _e('Delete options from DB', $this->plugin_constant ) ?>" />
<input class="button-secondary" style="float: right" type="submit" name="<?php echo $this->button_flush ?>" id="<?php echo $this->button_flush ?>" value="<?php _e('Clear cache', $this->plugin_constant ) ?>" />
</p>
</form>
</div>
<?php
}
/**
* extending admin init
*
*/
public function plugin_hook_admin_init () {
/* save parameter updates, if there are any */
if ( isset( $_POST[ $this->button_flush ] ) ) {
$this->backend->clear();
$this->status = 3;
header( "Location: ". $this->settings_link . self::slug_flush );
}
}
/**
* extending options_save
*
*/
public function plugin_hook_options_save( $activating ) {
/* flush the cache when news options are saved, not needed on activation */
if ( !$activating )
$this->backend->clear();
/* create the to-be-included configuration for advanced-cache.php */
$this->update_global_config();
/* create advanced cache file, needed only once or on activation, because there could be lefover advanced-cache.php from different plugins */
if ( !$activating )
$this->deploy_acache();
}
/**
* read hook; needs to be implemented
*/
public function plugin_hook_options_read( &$options ) {
/* read the global options, network compatibility */
$this->global_config = get_site_option( $this->global_option );
/* check if current site present in global config */
if ( !empty ( $this->global_config[ $this->global_config_key ] ) )
$this->global_saved = true;
$this->global_config[ $this->global_config_key ] = $options;
}
/**
* options delete hook; needs to be implemented
*/
public function plugin_hook_options_delete( ) {
delete_site_option ( $this->global_option );
}
/**
* need to do migrations from previous versions of the plugin
*
*/
public function plugin_hook_options_migrate( &$options ) {
if ( $options['version'] != $this->plugin_version || !isset ( $options['version'] ) ) {
/* cleanup possible leftover files from previous versions */
$check = array ( 'advanced-cache.php', 'nginx-sample.conf', 'wp-ffpc.admin.css', 'wp-ffpc-common.php' );
foreach ( $check as $fname ) {
$fname = $this->plugin_dir . $fname;
if ( file_exists ( $fname ) )
unlink ( $fname );
}
/* updating from version 0.4.x */
if ( !empty ( $options['host'] ) ) {
$options['hosts'] = $options['host'] . ':' . $options['port'];
}
/* migrating from version 0.6.x */
elseif ( is_array ( $options ) && array_key_exists ( $this->global_config_key , $options ) ) {
$options = $options[ $this->global_config_key ];
}
/* migrating from something, drop previous config */
else {
$options = array();
}
/* renamed options */
$options['log'] = $options['syslog'];
$options['response_header'] = $options['debug'];
}
}
/**
* advanced-cache.php creator function
*
*/
private function deploy_acache( ) {
/* in case advanced-cache.php was already there, remove it */
if ( @file_exists( $this->acache ))
unlink ($this->acache);
/* is deletion was unsuccessful, die, we have no rights to do that, fail */
if ( @file_exists( $this->acache ))
return false;
/* if no active site left no need for advanced cache :( */
if ( empty ( $this->global_config ) )
return false;
/* add the required includes and generate the needed code */
$string[] = "<?php";
$string[] = self::global_config_var . ' = ' . var_export ( $this->global_config, true ) . ';' ;
$string[] = "include_once ('" . $this->acache_backend . "');";
$string[] = "include_once ('" . $this->acache_worker . "');";
$string[] = "?>";
/* write the file and start caching from this point */
return file_put_contents( $this->acache, join( "\n" , $string ) );
}
/**
* function to generate working example from the nginx sample file
*
* @return string nginx config file
*
*/
private function nginx_example () {
/* read the sample file */
$nginx = file_get_contents ( $this->nginx_sample );
/* this part is not used when the cache is turned on for logged in users */
$loggedin = '# avoid cache for logged in users
if ($http_cookie ~* "comment_author_|wordpressuser_|wp-postpass_" ) {
set $memcached_request 0;
}';
/* replace the data prefix with the configured one */
$nginx = str_replace ( 'DATAPREFIX' , $this->options['prefix_data'] , $nginx );
/* set upstream servers from configured servers, best to get from the actual backend */
$servers = $this->backend->get_servers();
foreach ( array_keys( $servers ) as $server ) {
$nginx_servers .= " server ". $server .";\n";
}
$nginx = str_replace ( 'MEMCACHED_SERVERS' , $nginx_servers , $nginx );
/* add logged in cache, if valid */
if ( ! $this->options['cache_loggedin'])
$nginx = str_replace ( 'LOGGEDIN_EXCEPTION' , $loggedin , $nginx );
else
$nginx = str_replace ( 'LOGGEDIN_EXCEPTION' , '' , $nginx );
return $nginx;
}
/**
* function to update global configuration
*
* @param boolean $remove_site Bool to remove or add current config to global
*
*/
private function update_global_config ( $remove_site = false ) {
/* remove or add current config to global config */
if ( $remove_site ) {
unset ( $this->global_config[ $this->global_config_key ] );
}
else {
$this->global_config[ $this->global_config_key ] = $this->options;
}
/* deploy advanced-cache.php */
$this->deploy_acache ();
/* save options to database */
update_site_option( $this->global_option , $this->global_config );
}
}
}
?>

51
wp-ffpc-nginx-sample.conf Normal file
View file

@ -0,0 +1,51 @@
http {
...
upstream memcached-servers {
MEMCACHED_SERVERS
}
...
server {
...
# try to get result from memcached
location @memcached {
default_type text/html;
set $memcached_key DATAPREFIX$scheme://$host$request_uri;
set $memcached_request 1;
# exceptions
# avoid cache serve of POST requests
if ($request_method = POST ) {
set $memcached_request 0;
}
# avoid cache serve of wp-admin-like pages, starting with "wp-"
if ( $uri ~ "/wp-" ) {
set $memcached_request 0;
}
LOGGEDIN_EXCEPTION
if ( $memcached_request = 1) {
memcached_pass memcached-servers;
error_page 404 = @rewrites;
}
if ( $memcached_request = 0) {
rewrite ^ /index.php$request_uri last;
}
}
## rewrite rules
location @rewrites {
rewrite ^ /index.php$request_uri last;
}
location / {
try_files $uri $uri/ @memcached;
}
...
}
}
...