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
cadeyrn cadeyrn@b8457f37-d9ea-0310-8a92-e5e31aec5664
Tue, 19 Mar 2013 10:16:59 +0000
7 files changed,
2354 insertions(+),
0 deletions(-)
A
uninstall.php
@@ -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(); + +?>
A
wp-ffpc-abstract.php
@@ -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 + } + } +}
A
wp-ffpc-acache.php
@@ -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; +} + +?>
A
wp-ffpc-admin.css
@@ -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; + +}
A
wp-ffpc-backend.php
@@ -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 ***********************/ + + } + +} + +?>
A
wp-ffpc-class.php
@@ -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 ); + } + + } +} + +?>
A
wp-ffpc-nginx-sample.conf
@@ -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; + } + + ... + } +} +...